UNPKG

@lamemind/react-agent-ts

Version:

Streaming ReAct agent in typescript with multiple LLM providers

155 lines (154 loc) 5.34 kB
export class AIMessageChunksCollector { constructor() { this.chunks = []; this.result = { additional_kwargs: { id: '', model: '', role: '', stop_reason: null }, messages: [], tool_calls: [], usage_metadata: { input_tokens: 0, output_tokens: 0, total_tokens: 0 } }; this.currentMessage = null; this.currentMessageIndex = -1; this.currentToolCallArgs = ''; this.askljdhaskjd = ''; } async consume(stream) { for await (const chunk of stream) { this.addChunk(chunk); } console.log(`Ricevuti ${this.chunks.length} chunk di risposta`); } formatMessage() { return { role: "assistant", content: this.result.messages }; } dumpResponse() { console.log("--------------------"); console.log("Risposta elaborata:", JSON.stringify(this.result, null, 2)); console.log("--------------------"); } addChunk(chunk) { // console.log(`Chunk ricevuto: ${this.chunks.length + 1}`); this.chunks.push(chunk); this.extractMainInfo(chunk); this.extractUsage(chunk); if (chunk.content && chunk.content.length > 0) { //@ts-ignore chunk.content.forEach(c => { this.processChunkContent(chunk, c); }); } if (chunk.additional_kwargs.stop_reason) { this.result.additional_kwargs.stop_reason = chunk.additional_kwargs.stop_reason; this.finalizeCurrentMessage(); } } extractMainInfo(chunk) { const result = this.result; if (chunk.additional_kwargs.id && !result.additional_kwargs.id) { result.additional_kwargs.id = chunk.additional_kwargs.id; result.additional_kwargs.role = (chunk.additional_kwargs.role || ''); result.additional_kwargs.model = (chunk.additional_kwargs.model || ''); } } extractUsage(chunk) { if (chunk.usage_metadata) { this.result.usage_metadata.input_tokens += chunk.usage_metadata.input_tokens; this.result.usage_metadata.output_tokens += chunk.usage_metadata.output_tokens; this.result.usage_metadata.total_tokens += chunk.usage_metadata.total_tokens; } } processChunkContent(chunk, c) { if (this.currentMessageIndex < c.index) { this.finalizeCurrentMessage(); this.createNewMessage(chunk, c); } else { this.appendChunkContent(c); } } createNewMessage(chunk, c) { this.currentMessageIndex = c.index; switch (c.type) { case 'tool_use': this.currentMessage = { type: 'tool_use', id: chunk.tool_call_chunks[0].id, name: chunk.tool_call_chunks[0].name, input: {} }; this.currentToolCallArgs = ''; break; case 'thinking': this.currentMessage = { type: 'thinking', // content: c.thinking, thinking: c.thinking, signature: c.signature }; break; case 'text': this.currentMessage = { type: 'text', // content: c.text, text: c.text }; break; default: throw new Error(`Unknown chunk type: ${c.type}`); } } finalizeCurrentMessage() { if (this.currentMessage) { if (this.currentMessage.type === 'tool_use') { if (this.currentToolCallArgs !== '') this.currentMessage.input = JSON.parse(this.currentToolCallArgs); this.currentToolCallArgs = ''; this.result.tool_calls.push(clone(this.currentMessage)); } this.result.messages.push(this.currentMessage); } } appendChunkContent(c) { const m = this.currentMessage; let append = ''; if (c.type === 'thinking') { append = c.thinking || ''; // m.content = (m.content || '') + append; m.thinking = (m.thinking || '') + append; m.signature = c.signature || m.signature; } else if (c.type === 'text') { append = c.text || ''; // m.content = (m.content || '') + append; m.text = (m.text || '') + append; } else if (c.type === 'tool_use') throw new Error('Tool use cannot append to existing message'); else if (c.type === 'input_json_delta') this.currentToolCallArgs += c.input || ''; if (append != '') this.logAppend(append); } logAppend(append) { this.askljdhaskjd += append; if (this.askljdhaskjd.length > 128) { console.log(this.askljdhaskjd); this.askljdhaskjd = ''; } } } function clone(obj) { return JSON.parse(JSON.stringify(obj)); }