mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 14:03:49 +00:00
refactor(ai): Add completion signal to onText/onThinking callbacks
- Update LLMOptions interface to include completion boolean parameter - Modify all providers to signal when text/thinking blocks are complete - Update examples to handle the completion parameter - Move documentation files to docs/ directory
This commit is contained in:
parent
a42c54e6fe
commit
cb4c32faaa
11 changed files with 45 additions and 13 deletions
|
|
@ -114,15 +114,33 @@ export class AnthropicLLM implements LLM<AnthropicLLMOptions> {
|
|||
},
|
||||
);
|
||||
|
||||
let blockType: "text" | "thinking" | "other" = "other";
|
||||
for await (const event of stream) {
|
||||
if (event.type === "content_block_start") {
|
||||
if (event.content_block.type === "text") {
|
||||
blockType = "text";
|
||||
} else if (event.content_block.type === "thinking") {
|
||||
blockType = "thinking";
|
||||
} else {
|
||||
blockType = "other";
|
||||
}
|
||||
}
|
||||
if (event.type === "content_block_delta") {
|
||||
if (event.delta.type === "text_delta") {
|
||||
options?.onText?.(event.delta.text);
|
||||
options?.onText?.(event.delta.text, false);
|
||||
}
|
||||
if (event.delta.type === "thinking_delta") {
|
||||
options?.onThinking?.(event.delta.thinking);
|
||||
options?.onThinking?.(event.delta.thinking, false);
|
||||
}
|
||||
}
|
||||
if (event.type === "content_block_stop") {
|
||||
if (blockType === "text") {
|
||||
options?.onText?.("", true);
|
||||
} else if (blockType === "thinking") {
|
||||
options?.onThinking?.("", true);
|
||||
}
|
||||
blockType = "other";
|
||||
}
|
||||
}
|
||||
const msg = await stream.finalMessage();
|
||||
const thinking = msg.content.some((block) => block.type === "thinking")
|
||||
|
|
|
|||
|
|
@ -87,17 +87,24 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
};
|
||||
let finishReason: ChatCompletionChunk.Choice["finish_reason"] | null = null;
|
||||
|
||||
let inTextBlock = false;
|
||||
for await (const chunk of stream) {
|
||||
const choice = chunk.choices[0];
|
||||
|
||||
// Handle text content
|
||||
if (choice?.delta?.content) {
|
||||
content += choice.delta.content;
|
||||
options?.onText?.(choice.delta.content);
|
||||
options?.onText?.(choice.delta.content, false);
|
||||
inTextBlock = true;
|
||||
}
|
||||
|
||||
// Handle tool calls
|
||||
if (choice?.delta?.tool_calls) {
|
||||
if (inTextBlock) {
|
||||
// If we were in a text block, signal its end
|
||||
options?.onText?.("", true);
|
||||
inTextBlock = false;
|
||||
}
|
||||
for (const toolCall of choice.delta.tool_calls) {
|
||||
const index = toolCall.index;
|
||||
|
||||
|
|
@ -120,6 +127,11 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
|
||||
// Capture finish reason
|
||||
if (choice?.finish_reason) {
|
||||
if (inTextBlock) {
|
||||
// If we were in a text block, signal its end
|
||||
options?.onText?.("", true);
|
||||
inTextBlock = false;
|
||||
}
|
||||
finishReason = choice.finish_reason;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,21 +91,23 @@ export class OpenAIResponsesLLM implements LLM<OpenAIResponsesLLMOptions> {
|
|||
if (event.type === "response.reasoning_summary_text.delta") {
|
||||
const delta = event.delta;
|
||||
thinking += delta;
|
||||
options?.onThinking?.(delta);
|
||||
options?.onThinking?.(delta, false);
|
||||
} else if (event.type === "response.reasoning_summary_text.done") {
|
||||
if (event.text) {
|
||||
thinking = event.text;
|
||||
}
|
||||
options?.onThinking?.("", true);
|
||||
}
|
||||
// Handle main text output
|
||||
else if (event.type === "response.output_text.delta") {
|
||||
const delta = event.delta;
|
||||
content += delta;
|
||||
options?.onText?.(delta);
|
||||
options?.onText?.(delta, false);
|
||||
} else if (event.type === "response.output_text.done") {
|
||||
if (event.text) {
|
||||
content = event.text;
|
||||
}
|
||||
options?.onText?.("", true);
|
||||
}
|
||||
// Handle function calls
|
||||
else if (event.type === "response.output_item.done") {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
export interface LLMOptions {
|
||||
temperature?: number;
|
||||
maxTokens?: number;
|
||||
onText?: (text: string) => void;
|
||||
onThinking?: (thinking: string) => void;
|
||||
onText?: (text: string, complete: boolean) => void;
|
||||
onThinking?: (thinking: string, complete: boolean) => void;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ const tools: Tool[] = [
|
|||
];
|
||||
|
||||
const options: AnthropicLLMOptions = {
|
||||
onText: (t) => process.stdout.write(t),
|
||||
onThinking: (t) => process.stdout.write(chalk.dim(t)),
|
||||
onText: (t, complete) => process.stdout.write(t + (complete ? "\n" : "")),
|
||||
onThinking: (t, complete) => process.stdout.write(chalk.dim(t + (complete ? "\n" : ""))),
|
||||
thinking: { enabled: true }
|
||||
};
|
||||
const ai = new AnthropicLLM("claude-sonnet-4-0", process.env.ANTHROPIC_OAUTH_TOKEN ?? process.env.ANTHROPIC_API_KEY);
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ const tools: Tool[] = [
|
|||
];
|
||||
|
||||
const options: OpenAICompletionsLLMOptions = {
|
||||
onText: (t) => process.stdout.write(t),
|
||||
onThinking: (t) => process.stdout.write(chalk.dim(t)),
|
||||
onText: (t, complete) => process.stdout.write(t + (complete ? "\n" : "")),
|
||||
onThinking: (t, complete) => process.stdout.write(chalk.dim(t + (complete ? "\n" : ""))),
|
||||
reasoningEffort: "medium",
|
||||
toolChoice: "auto"
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ const context: Context = {
|
|||
}
|
||||
|
||||
const options: OpenAIResponsesLLMOptions = {
|
||||
onText: (t) => process.stdout.write(t),
|
||||
onThinking: (t) => process.stdout.write(chalk.dim(t)),
|
||||
onText: (t, complete) => process.stdout.write(t + (complete ? "\n" : "")),
|
||||
onThinking: (t, complete) => process.stdout.write(chalk.dim(t + (complete ? "\n" : ""))),
|
||||
reasoningEffort: "low",
|
||||
reasoningSummary: "auto"
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue