@tanstack/ai
Version:
Core TanStack AI library - Open source AI SDK
149 lines (148 loc) • 5.15 kB
JavaScript
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