mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 06:04:40 +00:00
fix(ai): Fix OpenAI Responses provider multi-turn conversation support
- Added contentSignature tracking for assistant messages - Fixed message format in convertToResponsesFormat (output_text instead of input_text) - Properly preserve message IDs for multi-turn conversations - Added proper ResponseOutputMessage type satisfaction - Updated tests to cover more providers and multi-turn scenarios
This commit is contained in:
parent
2e90f8f8bc
commit
cff766d3e2
12 changed files with 126 additions and 105 deletions
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -2716,10 +2716,10 @@
|
|||
},
|
||||
"packages/agent": {
|
||||
"name": "@mariozechner/pi-agent",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mariozechner/pi-tui": "^0.5.11",
|
||||
"@mariozechner/pi-tui": "^0.5.12",
|
||||
"@types/glob": "^8.1.0",
|
||||
"chalk": "^5.5.0",
|
||||
"glob": "^11.0.3",
|
||||
|
|
@ -3098,7 +3098,7 @@
|
|||
},
|
||||
"packages/ai": {
|
||||
"name": "@mariozechner/pi-ai",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.60.0",
|
||||
|
|
@ -3134,10 +3134,10 @@
|
|||
},
|
||||
"packages/pods": {
|
||||
"name": "@mariozechner/pi",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mariozechner/pi-agent": "^0.5.11",
|
||||
"@mariozechner/pi-agent": "^0.5.12",
|
||||
"chalk": "^5.5.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -3150,7 +3150,7 @@
|
|||
},
|
||||
"packages/tui": {
|
||||
"name": "@mariozechner/pi-tui",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime-types": "^2.1.4",
|
||||
|
|
|
|||
4
packages/agent/package-lock.json
generated
4
packages/agent/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@mariozechner/pi-agent",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@mariozechner/pi-agent",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mariozechner/tui": "^0.1.1",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@mariozechner/pi-agent",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"description": "General-purpose agent with tool calling and session persistence",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
"prepublishOnly": "npm run clean && npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mariozechner/pi-tui": "^0.5.12",
|
||||
"@mariozechner/pi-tui": "^0.5.13",
|
||||
"@types/glob": "^8.1.0",
|
||||
"chalk": "^5.5.0",
|
||||
"glob": "^11.0.3",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@mariozechner/pi-ai",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
|
|
|||
|
|
@ -1598,22 +1598,6 @@ export const PROVIDERS = {
|
|||
contextWindow: 131072,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"meta-llama/llama-3.1-405b-instruct": {
|
||||
id: "meta-llama/llama-3.1-405b-instruct",
|
||||
name: "Meta: Llama 3.1 405B Instruct",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.7999999999999999,
|
||||
output: 0.7999999999999999,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 32768,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"meta-llama/llama-3.1-70b-instruct": {
|
||||
id: "meta-llama/llama-3.1-70b-instruct",
|
||||
name: "Meta: Llama 3.1 70B Instruct",
|
||||
|
|
@ -1630,6 +1614,22 @@ export const PROVIDERS = {
|
|||
contextWindow: 131072,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"meta-llama/llama-3.1-405b-instruct": {
|
||||
id: "meta-llama/llama-3.1-405b-instruct",
|
||||
name: "Meta: Llama 3.1 405B Instruct",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.7999999999999999,
|
||||
output: 0.7999999999999999,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 32768,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"mistralai/mistral-nemo": {
|
||||
id: "mistralai/mistral-nemo",
|
||||
name: "Mistral: Mistral Nemo",
|
||||
|
|
@ -1646,6 +1646,22 @@ export const PROVIDERS = {
|
|||
contextWindow: 32000,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"mistralai/mistral-7b-instruct-v0.3": {
|
||||
id: "mistralai/mistral-7b-instruct-v0.3",
|
||||
name: "Mistral: Mistral 7B Instruct v0.3",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.028,
|
||||
output: 0.054,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 32768,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"mistralai/mistral-7b-instruct:free": {
|
||||
id: "mistralai/mistral-7b-instruct:free",
|
||||
name: "Mistral: Mistral 7B Instruct (free)",
|
||||
|
|
@ -1678,22 +1694,6 @@ export const PROVIDERS = {
|
|||
contextWindow: 32768,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"mistralai/mistral-7b-instruct-v0.3": {
|
||||
id: "mistralai/mistral-7b-instruct-v0.3",
|
||||
name: "Mistral: Mistral 7B Instruct v0.3",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.028,
|
||||
output: 0.054,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 32768,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"microsoft/phi-3-mini-128k-instruct": {
|
||||
id: "microsoft/phi-3-mini-128k-instruct",
|
||||
name: "Microsoft: Phi-3 Mini 128K Instruct",
|
||||
|
|
@ -1726,22 +1726,6 @@ export const PROVIDERS = {
|
|||
contextWindow: 128000,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"meta-llama/llama-3-8b-instruct": {
|
||||
id: "meta-llama/llama-3-8b-instruct",
|
||||
name: "Meta: Llama 3 8B Instruct",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.03,
|
||||
output: 0.06,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 8192,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"meta-llama/llama-3-70b-instruct": {
|
||||
id: "meta-llama/llama-3-70b-instruct",
|
||||
name: "Meta: Llama 3 70B Instruct",
|
||||
|
|
@ -1758,6 +1742,22 @@ export const PROVIDERS = {
|
|||
contextWindow: 8192,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"meta-llama/llama-3-8b-instruct": {
|
||||
id: "meta-llama/llama-3-8b-instruct",
|
||||
name: "Meta: Llama 3 8B Instruct",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.03,
|
||||
output: 0.06,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 8192,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model,
|
||||
"mistralai/mixtral-8x22b-instruct": {
|
||||
id: "mistralai/mixtral-8x22b-instruct",
|
||||
name: "Mistral: Mixtral 8x22B Instruct",
|
||||
|
|
@ -1854,22 +1854,6 @@ export const PROVIDERS = {
|
|||
contextWindow: 128000,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"mistralai/mistral-small": {
|
||||
id: "mistralai/mistral-small",
|
||||
name: "Mistral Small",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.19999999999999998,
|
||||
output: 0.6,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 32768,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"mistralai/mistral-tiny": {
|
||||
id: "mistralai/mistral-tiny",
|
||||
name: "Mistral Tiny",
|
||||
|
|
@ -1886,6 +1870,22 @@ export const PROVIDERS = {
|
|||
contextWindow: 32768,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"mistralai/mistral-small": {
|
||||
id: "mistralai/mistral-small",
|
||||
name: "Mistral Small",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.19999999999999998,
|
||||
output: 0.6,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 32768,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"mistralai/mixtral-8x7b-instruct": {
|
||||
id: "mistralai/mixtral-8x7b-instruct",
|
||||
name: "Mistral: Mixtral 8x7B Instruct",
|
||||
|
|
@ -2473,21 +2473,6 @@ export const PROVIDERS = {
|
|||
contextWindow: 16385,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"gpt-3.5-turbo": {
|
||||
id: "gpt-3.5-turbo",
|
||||
name: "OpenAI: GPT-3.5 Turbo",
|
||||
provider: "openai",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.5,
|
||||
output: 1.5,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 16385,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"gpt-4": {
|
||||
id: "gpt-4",
|
||||
name: "OpenAI: GPT-4",
|
||||
|
|
@ -2518,6 +2503,21 @@ export const PROVIDERS = {
|
|||
contextWindow: 8191,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
"gpt-3.5-turbo": {
|
||||
id: "gpt-3.5-turbo",
|
||||
name: "OpenAI: GPT-3.5 Turbo",
|
||||
provider: "openai",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.5,
|
||||
output: 1.5,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 16385,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model,
|
||||
},
|
||||
},
|
||||
anthropic: {
|
||||
|
|
@ -2597,9 +2597,9 @@ export const PROVIDERS = {
|
|||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model,
|
||||
"claude-3-5-haiku-latest": {
|
||||
id: "claude-3-5-haiku-latest",
|
||||
name: "Anthropic: Claude 3.5 Haiku",
|
||||
"claude-3-5-haiku-20241022": {
|
||||
id: "claude-3-5-haiku-20241022",
|
||||
name: "Anthropic: Claude 3.5 Haiku (2024-10-22)",
|
||||
provider: "anthropic",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
|
|
@ -2612,9 +2612,9 @@ export const PROVIDERS = {
|
|||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
} satisfies Model,
|
||||
"claude-3-5-haiku-20241022": {
|
||||
id: "claude-3-5-haiku-20241022",
|
||||
name: "Anthropic: Claude 3.5 Haiku (2024-10-22)",
|
||||
"claude-3-5-haiku-latest": {
|
||||
id: "claude-3-5-haiku-latest",
|
||||
name: "Anthropic: Claude 3.5 Haiku",
|
||||
provider: "anthropic",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type {
|
|||
ResponseInputText,
|
||||
ResponseReasoningItem,
|
||||
} from "openai/resources/responses/responses.js";
|
||||
import type { ResponseOutputMessage } from "openai/resources/responses/responses.mjs";
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Context,
|
||||
|
|
@ -83,6 +84,7 @@ export class OpenAIResponsesLLM implements LLM<OpenAIResponsesLLMOptions> {
|
|||
});
|
||||
|
||||
let content = "";
|
||||
let contentSignature = "";
|
||||
let thinking = "";
|
||||
const toolCalls: ToolCall[] = [];
|
||||
const reasoningItems: ResponseReasoningItem[] = [];
|
||||
|
|
@ -117,6 +119,7 @@ export class OpenAIResponsesLLM implements LLM<OpenAIResponsesLLMOptions> {
|
|||
content = event.text;
|
||||
}
|
||||
options?.onText?.("", true);
|
||||
contentSignature = event.item_id;
|
||||
}
|
||||
// Handle function calls
|
||||
else if (event.type === "response.output_item.done") {
|
||||
|
|
@ -167,6 +170,7 @@ export class OpenAIResponsesLLM implements LLM<OpenAIResponsesLLMOptions> {
|
|||
return {
|
||||
role: "assistant",
|
||||
content: content || undefined,
|
||||
contentSignature: contentSignature || undefined,
|
||||
thinking: thinking || undefined,
|
||||
thinkingSignature: JSON.stringify(reasoningItems) || undefined,
|
||||
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
||||
|
|
@ -257,8 +261,10 @@ export class OpenAIResponsesLLM implements LLM<OpenAIResponsesLLMOptions> {
|
|||
output.push({
|
||||
type: "message",
|
||||
role: "assistant",
|
||||
content: [{ type: "input_text", text: msg.content }],
|
||||
});
|
||||
content: [{ type: "output_text", text: msg.content, annotations: [] }],
|
||||
status: "completed",
|
||||
id: msg.contentSignature || "msg_" + Math.random().toString(36).substring(2, 15),
|
||||
} satisfies ResponseOutputMessage);
|
||||
}
|
||||
// Add all output items to input
|
||||
input.push(...output);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ export interface AssistantMessage {
|
|||
// Leaky abstraction: provider specific, does not translate to other providers
|
||||
thinkingSignature?: string;
|
||||
content?: string;
|
||||
// Leaky abstraction: provider specific, does not translate to other providers
|
||||
// e.g. OpenAI responses must include id for assistant responses
|
||||
contentSignature?: string;
|
||||
toolCalls?: {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,18 @@ async function basicTextGeneration<T extends LLMOptions>(llm: LLM<T>) {
|
|||
expect(response.usage.output).toBeGreaterThan(0);
|
||||
expect(response.error).toBeFalsy();
|
||||
expect(response.content).toContain("Hello test successful");
|
||||
|
||||
context.messages.push(response);
|
||||
context.messages.push({ role: "user", content: "Now say 'Goodbye test successful'" });
|
||||
|
||||
const secondResponse = await llm.complete(context);
|
||||
|
||||
expect(secondResponse.role).toBe("assistant");
|
||||
expect(secondResponse.content).toBeTruthy();
|
||||
expect(secondResponse.usage.input).toBeGreaterThan(0);
|
||||
expect(secondResponse.usage.output).toBeGreaterThan(0);
|
||||
expect(secondResponse.error).toBeFalsy();
|
||||
expect(secondResponse.content).toContain("Goodbye test successful");
|
||||
}
|
||||
|
||||
async function handleToolCall<T extends LLMOptions>(llm: LLM<T>) {
|
||||
|
|
|
|||
4
packages/pods/package-lock.json
generated
4
packages/pods/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@mariozechner/pi",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@mariozechner/pi",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^2.0.5",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@mariozechner/pi",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"description": "CLI tool for managing vLLM deployments on GPU pods",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
"node": ">=20.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mariozechner/pi-agent": "^0.5.12",
|
||||
"@mariozechner/pi-agent": "^0.5.13",
|
||||
"chalk": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {}
|
||||
|
|
|
|||
4
packages/tui/package-lock.json
generated
4
packages/tui/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@mariozechner/tui",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@mariozechner/tui",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime-types": "^2.1.4",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@mariozechner/pi-tui",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue