(model: string, readableStream: ReadableStream, tools?: any[])
| 752 | } |
| 753 | |
| 754 | async function receiveStream(model: string, readableStream: ReadableStream, tools?: any[]): Promise<any> { |
| 755 | return new Promise((resolve, reject) => { |
| 756 | const data = { |
| 757 | id: "", model, object: "chat.completion", |
| 758 | choices: [{ index: 0, message: { role: "assistant", content: "", reasoning_content: null as string | null }, finish_reason: "stop" }], |
| 759 | usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, |
| 760 | created: unixTimestamp(), |
| 761 | }; |
| 762 | const isSilentModel = model.includes('silent'); |
| 763 | const cachedParts: any[] = []; |
| 764 | const parser = createParser((event) => { |
| 765 | try { |
| 766 | const result = attempt(() => JSON.parse(event.data)); |
| 767 | if (isError(result)) throw new Error(`Stream response invalid: ${event.data}`); |
| 768 | if (!data.id && result.conversation_id) data.id = result.conversation_id; |
| 769 | if (result.status != "finish") { |
| 770 | if (result.parts) { cachedParts.length = 0; cachedParts.push(...result.parts); } |
| 771 | const searchMap = new Map<string, any>(); |
| 772 | cachedParts.forEach((part) => { |
| 773 | if (!part.content || !isArray(part.content)) return; |
| 774 | const { meta_data } = part; |
| 775 | part.content.forEach((item: any) => { |
| 776 | if (item.type == "tool_result" && meta_data?.tool_result_extra?.search_results) { |
| 777 | meta_data.tool_result_extra.search_results.forEach((res: any) => { if (res.match_key) searchMap.set(res.match_key, res); }); |
| 778 | } |
| 779 | }); |
| 780 | }); |
| 781 | const keyToIdMap = new Map<string, number>(); |
| 782 | let counter = 1; |
| 783 | let fullText = ""; |
| 784 | let fullReasoning = ""; |
| 785 | cachedParts.forEach((part: any) => { |
| 786 | const { content, meta_data } = part; |
| 787 | if (!isArray(content)) return; |
| 788 | let partText = ""; |
| 789 | let partReasoning = ""; |
| 790 | content.forEach((value: any) => { |
| 791 | const { type, text, think, image, code, content: innerContent } = value; |
| 792 | if (type == "text") { |
| 793 | let txt = text; |
| 794 | if (searchMap.size > 0) { |
| 795 | txt = txt.replace(/【?(turn\d+[a-zA-Z]+\d+)】?/g, (match: string, key: string) => { |
| 796 | const searchInfo = searchMap.get(key); |
| 797 | if (!searchInfo) return match; |
| 798 | if (!keyToIdMap.has(key)) keyToIdMap.set(key, counter++); |
| 799 | const newId = keyToIdMap.get(key); |
| 800 | return ` [${newId}](${searchInfo.url})`; |
| 801 | }); |
| 802 | } |
| 803 | partText += txt; |
| 804 | } else if (type == "think" && !isSilentModel) { |
| 805 | partReasoning += think; |
| 806 | } else if (type == "tool_result" && meta_data?.tool_result_extra?.search_results && isArray(meta_data.tool_result_extra.search_results) && !isSilentModel) { |
| 807 | partReasoning += meta_data.tool_result_extra.search_results.reduce((meta: string, v: any) => meta + `> 检索 ${v.title}(${v.url}) ...\n`, ""); |
| 808 | } else if (type == "quote_result" && part.status == "finish" && meta_data && isArray(meta_data.metadata_list) && !isSilentModel) { |
| 809 | partReasoning += meta_data.metadata_list.reduce((meta: string, v: any) => meta + `> 检索 ${v.title}(${v.url}) ...\n`, ""); |
| 810 | } else if (type == "image" && isArray(image) && part.status == "finish") { |
| 811 | partText += image.reduce((imgs: string, v: any) => imgs + (/^(http|https):\/\//.test(v.image_url) ? `` : ""), "") + "\n"; |
no test coverage detected