feat(ai): Add OpenAI Completions and Responses API providers

- Implement OpenAICompletionsLLM for Chat Completions API with streaming
- Implement OpenAIResponsesLLM for Responses API with reasoning support
- Update types to use LLM/Context instead of AI/Request
- Add support for reasoning tokens, tool calls, and streaming
- Create test examples for both OpenAI providers
- Update Anthropic provider to match new interface
This commit is contained in:
Mario Zechner 2025-08-24 20:18:10 +02:00
parent e5aedfed29
commit 8364ecde4a
7 changed files with 722 additions and 39 deletions

View file

@ -1,10 +1,9 @@
import Anthropic from "@anthropic-ai/sdk";
import { MessageCreateParamsBase } from "@anthropic-ai/sdk/resources/messages.mjs";
import chalk from "chalk";
import { AnthropicAI } from "../../src/providers/anthropic";
import { Request, Message, Tool } from "../../src/types";
const anthropic = new Anthropic();
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import { AnthropicLLM, AnthropicLLMOptions } from "../../src/providers/anthropic";
import { Context, Tool } from "../../src/types";
// Define a simple calculator tool
const tools: Tool[] = [
@ -24,23 +23,27 @@ const tools: Tool[] = [
}
];
const ai = new AnthropicAI("claude-sonnet-4-0");
const context: Request = {
const options: AnthropicLLMOptions = {
onText: (t) => process.stdout.write(t),
onThinking: (t) => process.stdout.write(chalk.dim(t)),
thinking: { enabled: true }
};
const ai = new AnthropicLLM("claude-sonnet-4-0", process.env.ANTHROPIC_OAUTH_TOKEN ?? process.env.ANTHROPIC_API_KEY);
const context: Context = {
systemPrompt: "You are a helpful assistant that can use tools to answer questions.",
messages: [
{
role: "user",
content: "Think about birds briefly. Then give me a list of 10 birds. Finally, calculate 42 * 17 + 123 and 453 + 434 in parallel using the calculator tool.",
}
],
tools,
onText: (t) => process.stdout.write(t),
onThinking: (t) => process.stdout.write(chalk.dim(t))
tools
}
const options = {thinking: { enabled: true }};
let msg = await ai.complete(context, options)
context.messages.push(msg);
console.log(JSON.stringify(msg, null, 2));
console.log();
console.log(chalk.yellow(JSON.stringify(msg, null, 2)));
for (const toolCall of msg.toolCalls || []) {
if (toolCall.name === "calculate") {
@ -56,7 +59,8 @@ for (const toolCall of msg.toolCalls || []) {
}
msg = await ai.complete(context, options);
console.log(JSON.stringify(msg, null, 2));
console.log();
console.log(chalk.yellow(JSON.stringify(msg, null, 2)));

View file

@ -0,0 +1,65 @@
import chalk from "chalk";
import { Context, Tool } from "../../src/types";
import { OpenAICompletionsLLM, OpenAICompletionsLLMOptions } from "../../src/providers/openai-completions";
// Define a simple calculator tool
const tools: Tool[] = [
{
name: "calculate",
description: "Perform a mathematical calculation",
parameters: {
type: "object" as const,
properties: {
expression: {
type: "string",
description: "The mathematical expression to evaluate"
}
},
required: ["expression"]
}
}
];
const options: OpenAICompletionsLLMOptions = {
onText: (t) => process.stdout.write(t),
onThinking: (t) => process.stdout.write(chalk.dim(t)),
reasoningEffort: "medium",
toolChoice: "auto"
};
const ai = new OpenAICompletionsLLM("gpt-5-mini");
const context: Context = {
systemPrompt: "You are a helpful assistant that can use tools to answer questions.",
messages: [
{
role: "user",
content: "Think about birds briefly. Then give me a list of 10 birds. Finally, calculate 42 * 17 + 123 and 453 + 434 in parallel using the calculator tool.",
}
],
tools
}
let msg = await ai.complete(context, options)
context.messages.push(msg);
console.log();
console.log(chalk.yellow(JSON.stringify(msg, null, 2)));
for (const toolCall of msg.toolCalls || []) {
if (toolCall.name === "calculate") {
const expression = toolCall.arguments.expression;
const result = eval(expression);
context.messages.push({
role: "toolResult",
content: `The result of ${expression} is ${result}.`,
toolCallId: toolCall.id,
isError: false
});
}
}
msg = await ai.complete(context, options);
console.log();
console.log(chalk.yellow(JSON.stringify(msg, null, 2)));

View file

@ -0,0 +1,60 @@
import chalk from "chalk";
import { OpenAIResponsesLLMOptions, OpenAIResponsesLLM } from "../../src/providers/openai-responses.js";
import type { Context, Tool } from "../../src/types.js";
// Define a simple calculator tool
const tools: Tool[] = [
{
name: "calculate",
description: "Perform a mathematical calculation",
parameters: {
type: "object" as const,
properties: {
expression: {
type: "string",
description: "The mathematical expression to evaluate"
}
},
required: ["expression"]
}
}
];
const ai = new OpenAIResponsesLLM("gpt-5");
const context: Context = {
messages: [
{
role: "user",
content: "Think about birds briefly. Then give me a list of 10 birds. Finally, calculate 42 * 17 + 123 and 453 + 434 in parallel using the calculator tool.",
}
],
tools,
}
const options: OpenAIResponsesLLMOptions = {
onText: (t) => process.stdout.write(t),
onThinking: (t) => process.stdout.write(chalk.dim(t)),
reasoningEffort: "low",
reasoningSummary: "auto"
};
let msg = await ai.complete(context, options)
context.messages.push(msg);
console.log();
console.log(chalk.yellow(JSON.stringify(msg, null, 2)));
for (const toolCall of msg.toolCalls || []) {
if (toolCall.name === "calculate") {
const expression = toolCall.arguments.expression;
const result = eval(expression);
context.messages.push({
role: "toolResult",
content: `The result of ${expression} is ${result}.`,
toolCallId: toolCall.id,
isError: false
});
}
}
msg = await ai.complete(context, options);
console.log();
console.log(chalk.yellow(JSON.stringify(msg, null, 2)));