UNPKG

@tanstack/ai

Version:

Core TanStack AI library - Open source AI SDK

149 lines (148 loc) 5.15 kB
import { EventType } from "@ag-ui/core"; import { toRunErrorPayload } from "../error-payload.js"; import { BaseSummarizeAdapter } from "./adapter.js"; class ChatStreamSummarizeAdapter extends BaseSummarizeAdapter { constructor(textAdapter, model, name = "chat-stream-summarize") { super({}, model); this.name = name; this.textAdapter = textAdapter; } async summarize(options) { const systemPrompt = this.buildSummarizationPrompt(options); let summary = ""; const id = this.generateId(); let model = options.model; let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }; options.logger.request( `activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? "unset"}`, { provider: this.name, model: options.model } ); try { for await (const chunk of this.textAdapter.chatStream( this.buildTextOptions(options, systemPrompt) )) { if (chunk.type === "TEXT_MESSAGE_CONTENT") { if (chunk.content) { summary = chunk.content; } else if (chunk.delta) { summary += chunk.delta; } model = chunk.model || model; } if (chunk.type === "RUN_FINISHED") { if (chunk.usage) { usage = chunk.usage; } } if (chunk.type === "RUN_ERROR") { const message = (chunk.error && typeof chunk.error.message === "string" ? chunk.error.message : null) ?? "Summarization failed"; const code = chunk.error && typeof chunk.error.code === "string" ? chunk.error.code : void 0; const err = new Error(message); if (code) { ; err.code = code; } throw err; } } } catch (error) { options.logger.errors(`${this.name}.summarize fatal`, { error: toRunErrorPayload(error, `${this.name}.summarize failed`), source: `${this.name}.summarize` }); throw error; } return { id, model, summary, usage }; } async *summarizeStream(options) { const systemPrompt = this.buildSummarizationPrompt(options); options.logger.request( `activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? "unset"}`, { provider: this.name, model: options.model } ); const id = this.generateId(); let summary = ""; let model = options.model; let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }; try { for await (const chunk of this.textAdapter.chatStream( this.buildTextOptions(options, systemPrompt) )) { if (chunk.type === "TEXT_MESSAGE_CONTENT") { if (chunk.content) { summary = chunk.content; } else if (chunk.delta) { summary += chunk.delta; } if (chunk.model) model = chunk.model; } if (chunk.type === "RUN_FINISHED") { if (chunk.usage) usage = chunk.usage; if (chunk.model) model = chunk.model; yield { type: EventType.CUSTOM, name: "generation:result", value: { id, model, summary, usage }, model, timestamp: Date.now() }; } yield chunk; } } catch (error) { options.logger.errors(`${this.name}.summarizeStream fatal`, { error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`), source: `${this.name}.summarizeStream` }); throw error; } } /** * Build the TextOptions passed to the underlying chatStream. Provider * `modelOptions` from the summarize call are forwarded as-is so knobs like * Anthropic cache headers, Gemini safety settings, or Ollama tuning params * still reach the wire layer. */ buildTextOptions(options, systemPrompt) { return { model: options.model, messages: [{ role: "user", content: options.text }], systemPrompts: [systemPrompt], maxTokens: options.maxLength, temperature: 0.3, modelOptions: options.modelOptions, logger: options.logger }; } buildSummarizationPrompt(options) { let prompt = "You are a professional summarizer. "; switch (options.style) { case "bullet-points": prompt += "Provide a summary in bullet point format. "; break; case "paragraph": prompt += "Provide a summary in paragraph format. "; break; case "concise": prompt += "Provide a very concise summary in 1-2 sentences. "; break; default: prompt += "Provide a clear and concise summary. "; } if (options.focus && options.focus.length > 0) { prompt += `Focus on the following aspects: ${options.focus.join(", ")}. `; } if (options.maxLength) { prompt += `Keep the summary under ${options.maxLength} tokens. `; } return prompt; } } export { ChatStreamSummarizeAdapter }; //# sourceMappingURL=chat-stream-summarize.js.map