-
-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
316 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,48 @@ | ||
// no need to introduce a package to get the current time as this module is just a debug utility | ||
const getTime = () => { | ||
const date = new Date(); | ||
return `${date.getFullYear()}-${date.getDate()}-${date.getDay()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}`; | ||
}; | ||
|
||
export const debugStream = async (stream: ReadableStream) => { | ||
let done = false; | ||
let finished = false; | ||
let chunk = 0; | ||
let chunkValue: any; | ||
const decoder = new TextDecoder(); | ||
|
||
const reader = stream.getReader(); | ||
while (!done) { | ||
const { value, done: _done } = await reader.read(); | ||
const chunkValue = decoder.decode(value, { stream: true }); | ||
if (!_done) { | ||
console.log(`[chunk ${chunk}]`); | ||
|
||
console.log(`[stream start] ${getTime()}`); | ||
|
||
while (!finished) { | ||
try { | ||
const { value, done } = await reader.read(); | ||
|
||
if (done) { | ||
console.log(`[stream finished] total chunks: ${chunk}\n`); | ||
finished = true; | ||
break; | ||
} | ||
|
||
chunkValue = value; | ||
|
||
// if the value is ArrayBuffer, we need to decode it | ||
if ('byteLength' in value) { | ||
chunkValue = decoder.decode(value, { stream: true }); | ||
} else if (typeof value !== 'string') { | ||
chunkValue = JSON.stringify(value); | ||
} | ||
|
||
console.log(`[chunk ${chunk}] ${getTime()}`); | ||
console.log(chunkValue); | ||
} | ||
console.log(`\n`); | ||
|
||
done = _done; | ||
chunk++; | ||
finished = done; | ||
chunk++; | ||
} catch (e) { | ||
finished = true; | ||
console.error('[debugStream error]', e); | ||
console.error('[error chunk value:]', chunkValue); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { | ||
EnhancedGenerateContentResponse, | ||
GenerateContentStreamResult, | ||
} from '@google/generative-ai'; | ||
import { createCallbacksTransformer, readableFromAsyncIterable } from 'ai'; | ||
|
||
import { nanoid } from '@/utils/uuid'; | ||
|
||
import { ChatStreamCallbacks } from '../../types'; | ||
import { | ||
StreamProtocolChunk, | ||
StreamToolCallChunk, | ||
chatStreamable, | ||
generateToolCallId, | ||
} from './protocol'; | ||
|
||
const transformGoogleGenerativeAIStream = ( | ||
chunk: EnhancedGenerateContentResponse, | ||
): StreamProtocolChunk => { | ||
// maybe need another structure to add support for multiple choices | ||
const functionCalls = chunk.functionCalls(); | ||
|
||
if (functionCalls) { | ||
return { | ||
data: functionCalls.map( | ||
(value, index): StreamToolCallChunk => ({ | ||
function: { | ||
arguments: JSON.stringify(value.args), | ||
name: value.name, | ||
}, | ||
id: generateToolCallId(index, value.name), | ||
index: index, | ||
type: 'function', | ||
}), | ||
), | ||
id: nanoid(), | ||
type: 'tool_calls', | ||
}; | ||
} | ||
const text = chunk.text(); | ||
|
||
return { | ||
data: text, | ||
id: nanoid(), | ||
type: 'text', | ||
}; | ||
// if (typeof item.delta?.content === 'string') { | ||
// return { data: item.delta.content, id: chunk.id, type: 'text' }; | ||
// } | ||
// | ||
// if (item.delta?.tool_calls) { | ||
// return { | ||
// data: item.delta.tool_calls.map((value, index) => ({ | ||
// ...value, | ||
// | ||
// // mistral's tool calling don't have index and function field, it's data like: | ||
// // [{"id":"xbhnmTtY7","function":{"name":"lobe-image-designer____text2image____builtin","arguments":"{\"prompts\": [\"A photo of a small, fluffy dog with a playful expression and wagging tail.\", \"A watercolor painting of a small, energetic dog with a glossy coat and bright eyes.\", \"A vector illustration of a small, adorable dog with a short snout and perky ears.\", \"A drawing of a small, scruffy dog with a mischievous grin and a wagging tail.\"], \"quality\": \"standard\", \"seeds\": [123456, 654321, 111222, 333444], \"size\": \"1024x1024\", \"style\": \"vivid\"}"}}] | ||
// | ||
// // minimax's tool calling don't have index field, it's data like: | ||
// // [{"id":"call_function_4752059746","type":"function","function":{"name":"lobe-image-designer____text2image____builtin","arguments":"{\"prompts\": [\"一个流浪的地球,背景是浩瀚"}}] | ||
// | ||
// // so we need to add these default values | ||
// index: typeof value.index !== 'undefined' ? value.index : index, | ||
// type: value.type || 'function', | ||
// })), | ||
// id: chunk.id, | ||
// type: 'tool_calls', | ||
// }; | ||
// } | ||
// | ||
// // 给定结束原因 | ||
// if (item.finish_reason) { | ||
// return { data: item.finish_reason, id: chunk.id, type: 'stop' }; | ||
// } | ||
// | ||
// if (item.delta.content === null) { | ||
// return { data: item.delta, id: chunk.id, type: 'data' }; | ||
// } | ||
// | ||
// // 其余情况下,返回 delta 和 index | ||
// return { | ||
// data: { delta: item.delta, id: chunk.id, index: item.index }, | ||
// id: chunk.id, | ||
// type: 'data', | ||
// }; | ||
}; | ||
|
||
// only use for debug | ||
export const googleGenAIResultToStream = (stream: GenerateContentStreamResult) => { | ||
// make the response to the streamable format | ||
return readableFromAsyncIterable(chatStreamable(stream.stream)); | ||
}; | ||
|
||
export const GoogleGenerativeAIStream = ( | ||
rawStream: ReadableStream<EnhancedGenerateContentResponse>, | ||
callbacks?: ChatStreamCallbacks, | ||
) => | ||
rawStream | ||
.pipeThrough( | ||
new TransformStream({ | ||
transform: (chunk, controller) => { | ||
const { type, id, data } = transformGoogleGenerativeAIStream(chunk); | ||
|
||
controller.enqueue(`id: ${id}\n`); | ||
controller.enqueue(`event: ${type}\n`); | ||
controller.enqueue(`data: ${JSON.stringify(data)}\n\n`); | ||
}, | ||
}), | ||
) | ||
.pipeThrough(createCallbacksTransformer(callbacks)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './google-ai'; | ||
export * from './openai'; | ||
export * from './protocol'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.