Compare commits

...

11 Commits

Author SHA1 Message Date
thucpn 6a279a391c Merge branch 'main' into feat/support-showing-image-on-chat-message-on-express 2024-01-17 16:04:01 +07:00
thucpn 4a4d13de8b fix: type conversion for model 2024-01-15 16:14:14 +07:00
thucpn 27f7653c0e fix: add expose header 2024-01-15 16:07:20 +07:00
thucpn c974dcdca3 feat(express): support showing image on chat message express backend 2024-01-15 15:32:08 +07:00
thucpn f037a3d857 fix: remove any 2024-01-15 15:25:37 +07:00
thucpn 7a75b48f35 fix: remove no need condition 2024-01-15 14:54:38 +07:00
Thuc Pham c4229f8714 Create spicy-colts-dream.md 2024-01-15 14:54:38 +07:00
thucpn 8389062271 fix: missing env file for frontend in fullstack templates 2024-01-15 14:54:38 +07:00
thucpn fda2a26874 fix: code review 2024-01-15 14:54:38 +07:00
thucpn 8cc02fe4d0 feat(nextjs): add chat message data component 2024-01-15 14:54:38 +07:00
thucpn bda44416f3 feat(nextjs): send experimental data in router handler 2024-01-15 14:54:38 +07:00
3 changed files with 63 additions and 12 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"create-llama": patch
---
feat: support showing image on chat message
@@ -35,7 +35,7 @@ export const chat = async (req: Request, res: Response) => {
}
const llm = new OpenAI({
model: process.env.MODEL || "gpt-3.5-turbo",
model: (process.env.MODEL as any) || "gpt-3.5-turbo",
});
const chatEngine = await createChatEngine(llm);
@@ -47,14 +47,29 @@ export const chat = async (req: Request, res: Response) => {
const response = await chatEngine.chat(
lastMessageContent as MessageContent,
messages,
messages as ChatMessage[],
true,
);
// Transform the response into a readable stream
const stream = LlamaIndexStream(response);
const { stream, data: streamData } = LlamaIndexStream(response, {
parserOptions: {
image_url: data?.imageUrl,
},
});
streamToResponse(stream, res);
// Pipe LlamaIndexStream to response
const processedStream = stream.pipeThrough(streamData.stream);
return streamToResponse(processedStream, res, {
headers: {
// MUST have the `X-Experimental-Stream-Data: 'true'` header
// in response so the client uses the correct parsing logic
// https://sdk.vercel.ai/docs/api-reference/stream-data#on-the-server
"X-Experimental-Stream-Data": "true",
"Content-Type": "text/plain; charset=utf-8",
"Access-Control-Expose-Headers": "X-Experimental-Stream-Data",
},
});
} catch (error) {
console.error("[LlamaIndex]", error);
return res.status(500).json({
@@ -1,17 +1,43 @@
import {
JSONValue,
createCallbacksTransformer,
createStreamDataTransformer,
experimental_StreamData,
trimStartOfStreamHelper,
type AIStreamCallbacksAndOptions,
} from "ai";
function createParser(res: AsyncGenerator<any>) {
type ParserOptions = {
image_url?: string;
};
function createParser(
res: AsyncGenerator<any>,
data: experimental_StreamData,
opts?: ParserOptions,
) {
const trimStartOfStream = trimStartOfStreamHelper();
return new ReadableStream<string>({
start() {
// if image_url is provided, send it via the data stream
if (opts?.image_url) {
const message: JSONValue = {
type: "image_url",
image_url: {
url: opts.image_url,
},
};
data.append(message);
} else {
data.append({}); // send an empty image response for the user's message
}
},
async pull(controller): Promise<void> {
const { value, done } = await res.next();
if (done) {
controller.close();
data.append({}); // send an empty image response for the assistant's message
data.close();
return;
}
@@ -25,11 +51,16 @@ function createParser(res: AsyncGenerator<any>) {
export function LlamaIndexStream(
res: AsyncGenerator<any>,
callbacks?: AIStreamCallbacksAndOptions,
): ReadableStream {
return createParser(res)
.pipeThrough(createCallbacksTransformer(callbacks))
.pipeThrough(
createStreamDataTransformer(callbacks?.experimental_streamData),
);
opts?: {
callbacks?: AIStreamCallbacksAndOptions;
parserOptions?: ParserOptions;
},
): { stream: ReadableStream; data: experimental_StreamData } {
const data = new experimental_StreamData();
return {
stream: createParser(res, data, opts?.parserOptions)
.pipeThrough(createCallbacksTransformer(opts?.callbacks))
.pipeThrough(createStreamDataTransformer(true)),
data,
};
}