mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 08:03:39 +00:00
Add timestamp to messages
This commit is contained in:
parent
ef09efaac9
commit
55dc0b6e08
24 changed files with 388 additions and 220 deletions
|
|
@ -159,6 +159,7 @@ export class Agent {
|
|||
role: "user",
|
||||
content,
|
||||
attachments: attachments?.length ? attachments : undefined,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
this.abortController = new AbortController();
|
||||
|
|
@ -260,6 +261,7 @@ export class Agent {
|
|||
},
|
||||
stopReason: this.abortController?.signal.aborted ? "aborted" : "error",
|
||||
errorMessage: err?.message || String(err),
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
this.appendMessage(msg as AppMessage);
|
||||
this.patch({ error: err?.message || String(err) });
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ function streamSimpleProxy(
|
|||
cacheWrite: 0,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ describe("Agent", () => {
|
|||
expect(agent.state.tools).toBe(tools);
|
||||
|
||||
// Test replaceMessages
|
||||
const messages = [{ role: "user" as const, content: "Hello" }];
|
||||
const messages = [{ role: "user" as const, content: "Hello", timestamp: Date.now() }];
|
||||
agent.replaceMessages(messages);
|
||||
expect(agent.state.messages).toEqual(messages);
|
||||
expect(agent.state.messages).not.toBe(messages); // Should be a copy
|
||||
|
|
@ -107,7 +107,7 @@ describe("Agent", () => {
|
|||
transport: new ProviderTransport(),
|
||||
});
|
||||
|
||||
const message = { role: "user" as const, content: "Queued message" };
|
||||
const message = { role: "user" as const, content: "Queued message", timestamp: Date.now() };
|
||||
await agent.queueMessage(message);
|
||||
|
||||
// The message is queued but not yet in state.messages
|
||||
|
|
|
|||
|
|
@ -223,6 +223,7 @@ async function executeToolCalls<T>(
|
|||
output: typeof resultOrError === "string" ? resultOrError : resultOrError.output,
|
||||
details: typeof resultOrError === "string" ? ({} as T) : resultOrError.details,
|
||||
isError,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
results.push(toolResultMessage);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,23 @@ import type { Model } from "./types.js";
|
|||
|
||||
export const MODELS = {
|
||||
anthropic: {
|
||||
"claude-opus-4-0": {
|
||||
id: "claude-opus-4-0",
|
||||
name: "Claude Opus 4 (latest)",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 15,
|
||||
output: 75,
|
||||
cacheRead: 1.5,
|
||||
cacheWrite: 18.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 32000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-3-5-sonnet-20241022": {
|
||||
id: "claude-3-5-sonnet-20241022",
|
||||
name: "Claude Sonnet 3.5 v2",
|
||||
|
|
@ -22,6 +39,40 @@ export const MODELS = {
|
|||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-opus-4-1": {
|
||||
id: "claude-opus-4-1",
|
||||
name: "Claude Opus 4.1 (latest)",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 15,
|
||||
output: 75,
|
||||
cacheRead: 1.5,
|
||||
cacheWrite: 18.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 32000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-haiku-4-5": {
|
||||
id: "claude-haiku-4-5",
|
||||
name: "Claude Haiku 4.5 (latest)",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 1,
|
||||
output: 5,
|
||||
cacheRead: 0.1,
|
||||
cacheWrite: 1.25,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-3-5-sonnet-20240620": {
|
||||
id: "claude-3-5-sonnet-20240620",
|
||||
name: "Claude Sonnet 3.5",
|
||||
|
|
@ -39,6 +90,23 @@ export const MODELS = {
|
|||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-3-5-haiku-latest": {
|
||||
id: "claude-3-5-haiku-latest",
|
||||
name: "Claude Haiku 3.5 (latest)",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 0.8,
|
||||
output: 4,
|
||||
cacheRead: 0.08,
|
||||
cacheWrite: 1,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-3-opus-20240229": {
|
||||
id: "claude-3-opus-20240229",
|
||||
name: "Claude Opus 3",
|
||||
|
|
@ -56,6 +124,23 @@ export const MODELS = {
|
|||
contextWindow: 200000,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-sonnet-4-5": {
|
||||
id: "claude-sonnet-4-5",
|
||||
name: "Claude Sonnet 4.5 (latest)",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-sonnet-4-5-20250929": {
|
||||
id: "claude-sonnet-4-5-20250929",
|
||||
name: "Claude Sonnet 4.5",
|
||||
|
|
@ -158,6 +243,40 @@ export const MODELS = {
|
|||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-3-7-sonnet-latest": {
|
||||
id: "claude-3-7-sonnet-latest",
|
||||
name: "Claude Sonnet 3.7 (latest)",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-sonnet-4-0": {
|
||||
id: "claude-sonnet-4-0",
|
||||
name: "Claude Sonnet 4 (latest)",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-opus-4-1-20250805": {
|
||||
id: "claude-opus-4-1-20250805",
|
||||
name: "Claude Opus 4.1",
|
||||
|
|
@ -209,125 +328,6 @@ export const MODELS = {
|
|||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-sonnet-4-0": {
|
||||
id: "claude-sonnet-4-0",
|
||||
name: "Claude Sonnet 4",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-3-7-sonnet-latest": {
|
||||
id: "claude-3-7-sonnet-latest",
|
||||
name: "Claude Sonnet 3.7",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-sonnet-4-5": {
|
||||
id: "claude-sonnet-4-5",
|
||||
name: "Claude Sonnet 4.5",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-3-5-haiku-latest": {
|
||||
id: "claude-3-5-haiku-latest",
|
||||
name: "Claude Haiku 3.5",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 0.8,
|
||||
output: 4,
|
||||
cacheRead: 0.08,
|
||||
cacheWrite: 1,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-haiku-4-5": {
|
||||
id: "claude-haiku-4-5",
|
||||
name: "Claude Haiku 4.5",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 1,
|
||||
output: 5,
|
||||
cacheRead: 0.1,
|
||||
cacheWrite: 1.25,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 64000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-opus-4-1": {
|
||||
id: "claude-opus-4-1",
|
||||
name: "Claude Opus 4.1",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 15,
|
||||
output: 75,
|
||||
cacheRead: 1.5,
|
||||
cacheWrite: 18.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 32000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"claude-opus-4-0": {
|
||||
id: "claude-opus-4-0",
|
||||
name: "Claude Opus 4",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 15,
|
||||
output: 75,
|
||||
cacheRead: 1.5,
|
||||
cacheWrite: 18.75,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 32000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
},
|
||||
google: {
|
||||
"gemini-2.5-flash-preview-05-20": {
|
||||
|
|
@ -1804,6 +1804,23 @@ export const MODELS = {
|
|||
} satisfies Model<"anthropic-messages">,
|
||||
},
|
||||
openrouter: {
|
||||
"minimax/minimax-m2:free": {
|
||||
id: "minimax/minimax-m2:free",
|
||||
name: "MiniMax: MiniMax M2 (free)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 204800,
|
||||
maxTokens: 131072,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"openrouter/andromeda-alpha": {
|
||||
id: "openrouter/andromeda-alpha",
|
||||
name: "Andromeda Alpha",
|
||||
|
|
@ -1974,6 +1991,23 @@ export const MODELS = {
|
|||
contextWindow: 202752,
|
||||
maxTokens: 202752,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"z-ai/glm-4.6:exacto": {
|
||||
id: "z-ai/glm-4.6:exacto",
|
||||
name: "Z.AI: GLM 4.6 (exacto)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.6,
|
||||
output: 1.9,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 202752,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"deepseek/deepseek-v3.2-exp": {
|
||||
id: "deepseek/deepseek-v3.2-exp",
|
||||
name: "DeepSeek: DeepSeek V3.2 Exp",
|
||||
|
|
@ -2014,7 +2048,7 @@ export const MODELS = {
|
|||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: true,
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 0.3,
|
||||
|
|
@ -2076,6 +2110,23 @@ export const MODELS = {
|
|||
contextWindow: 163840,
|
||||
maxTokens: 163840,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"deepseek/deepseek-v3.1-terminus:exacto": {
|
||||
id: "deepseek/deepseek-v3.1-terminus:exacto",
|
||||
name: "DeepSeek: DeepSeek V3.1 Terminus (exacto)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.27,
|
||||
output: 1,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 131072,
|
||||
maxTokens: 65536,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"alibaba/tongyi-deepresearch-30b-a3b:free": {
|
||||
id: "alibaba/tongyi-deepresearch-30b-a3b:free",
|
||||
name: "Tongyi DeepResearch 30B A3B (free)",
|
||||
|
|
@ -2136,13 +2187,13 @@ export const MODELS = {
|
|||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.14,
|
||||
input: 0.15,
|
||||
output: 1.2,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 262144,
|
||||
maxTokens: 4096,
|
||||
maxTokens: 262144,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"qwen/qwen3-next-80b-a3b-instruct": {
|
||||
id: "qwen/qwen3-next-80b-a3b-instruct",
|
||||
|
|
@ -2263,6 +2314,23 @@ export const MODELS = {
|
|||
contextWindow: 262144,
|
||||
maxTokens: 262144,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"moonshotai/kimi-k2-0905:exacto": {
|
||||
id: "moonshotai/kimi-k2-0905:exacto",
|
||||
name: "MoonshotAI: Kimi K2 0905 (exacto)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.6,
|
||||
output: 2.5,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 262144,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"deepcogito/cogito-v2-preview-llama-70b": {
|
||||
id: "deepcogito/cogito-v2-preview-llama-70b",
|
||||
name: "Deep Cogito: Cogito V2 Preview Llama 70B",
|
||||
|
|
@ -2545,7 +2613,7 @@ export const MODELS = {
|
|||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.35,
|
||||
output: 1.5,
|
||||
output: 1.55,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
|
|
@ -2654,6 +2722,23 @@ export const MODELS = {
|
|||
contextWindow: 262144,
|
||||
maxTokens: 262144,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"qwen/qwen3-coder:exacto": {
|
||||
id: "qwen/qwen3-coder:exacto",
|
||||
name: "Qwen: Qwen3 Coder 480B A35B (exacto)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.38,
|
||||
output: 1.53,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 262144,
|
||||
maxTokens: 262144,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"qwen/qwen3-235b-a22b-2507": {
|
||||
id: "qwen/qwen3-235b-a22b-2507",
|
||||
name: "Qwen: Qwen3 235B A22B Instruct 2507",
|
||||
|
|
@ -2824,9 +2909,9 @@ export const MODELS = {
|
|||
contextWindow: 40000,
|
||||
maxTokens: 40000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"mistralai/magistral-medium-2506": {
|
||||
id: "mistralai/magistral-medium-2506",
|
||||
name: "Mistral: Magistral Medium 2506",
|
||||
"mistralai/magistral-medium-2506:thinking": {
|
||||
id: "mistralai/magistral-medium-2506:thinking",
|
||||
name: "Mistral: Magistral Medium 2506 (thinking)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
|
|
@ -2841,9 +2926,9 @@ export const MODELS = {
|
|||
contextWindow: 40960,
|
||||
maxTokens: 40000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"mistralai/magistral-medium-2506:thinking": {
|
||||
id: "mistralai/magistral-medium-2506:thinking",
|
||||
name: "Mistral: Magistral Medium 2506 (thinking)",
|
||||
"mistralai/magistral-medium-2506": {
|
||||
id: "mistralai/magistral-medium-2506",
|
||||
name: "Mistral: Magistral Medium 2506",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
|
|
@ -3028,6 +3113,23 @@ export const MODELS = {
|
|||
contextWindow: 40960,
|
||||
maxTokens: 40960,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"qwen/qwen3-8b": {
|
||||
id: "qwen/qwen3-8b",
|
||||
name: "Qwen: Qwen3 8B",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.035,
|
||||
output: 0.13799999999999998,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128000,
|
||||
maxTokens: 20000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"qwen/qwen3-14b": {
|
||||
id: "qwen/qwen3-14b",
|
||||
name: "Qwen: Qwen3 14B",
|
||||
|
|
@ -3212,8 +3314,8 @@ export const MODELS = {
|
|||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128000,
|
||||
maxTokens: 4096,
|
||||
contextWindow: 96000,
|
||||
maxTokens: 96000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"mistralai/mistral-small-3.1-24b-instruct": {
|
||||
id: "mistralai/mistral-small-3.1-24b-instruct",
|
||||
|
|
@ -3603,7 +3705,7 @@ export const MODELS = {
|
|||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128000,
|
||||
contextWindow: 131072,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"qwen/qwen-2.5-7b-instruct": {
|
||||
|
|
@ -3621,7 +3723,7 @@ export const MODELS = {
|
|||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 32768,
|
||||
maxTokens: 16384,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"nvidia/llama-3.1-nemotron-70b-instruct": {
|
||||
id: "nvidia/llama-3.1-nemotron-70b-instruct",
|
||||
|
|
@ -3708,23 +3810,6 @@ export const MODELS = {
|
|||
contextWindow: 32768,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"cohere/command-r-plus-08-2024": {
|
||||
id: "cohere/command-r-plus-08-2024",
|
||||
name: "Cohere: Command R+ (08-2024)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 2.5,
|
||||
output: 10,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128000,
|
||||
maxTokens: 4000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"cohere/command-r-08-2024": {
|
||||
id: "cohere/command-r-08-2024",
|
||||
name: "Cohere: Command R (08-2024)",
|
||||
|
|
@ -3742,6 +3827,23 @@ export const MODELS = {
|
|||
contextWindow: 128000,
|
||||
maxTokens: 4000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"cohere/command-r-plus-08-2024": {
|
||||
id: "cohere/command-r-plus-08-2024",
|
||||
name: "Cohere: Command R+ (08-2024)",
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 2.5,
|
||||
output: 10,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128000,
|
||||
maxTokens: 4000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"sao10k/l3.1-euryale-70b": {
|
||||
id: "sao10k/l3.1-euryale-70b",
|
||||
name: "Sao10K: Llama 3.1 Euryale 70B v2.2",
|
||||
|
|
@ -3793,23 +3895,6 @@ export const MODELS = {
|
|||
contextWindow: 65536,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"meta-llama/llama-3.1-405b-instruct": {
|
||||
id: "meta-llama/llama-3.1-405b-instruct",
|
||||
name: "Meta: Llama 3.1 405B Instruct",
|
||||
api: "openai-completions",
|
||||
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<"openai-completions">,
|
||||
"meta-llama/llama-3.1-8b-instruct": {
|
||||
id: "meta-llama/llama-3.1-8b-instruct",
|
||||
name: "Meta: Llama 3.1 8B Instruct",
|
||||
|
|
@ -3844,6 +3929,23 @@ export const MODELS = {
|
|||
contextWindow: 131072,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"meta-llama/llama-3.1-405b-instruct": {
|
||||
id: "meta-llama/llama-3.1-405b-instruct",
|
||||
name: "Meta: Llama 3.1 405B Instruct",
|
||||
api: "openai-completions",
|
||||
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<"openai-completions">,
|
||||
"mistralai/mistral-nemo": {
|
||||
id: "mistralai/mistral-nemo",
|
||||
name: "Mistral: Mistral Nemo",
|
||||
|
|
@ -3963,23 +4065,6 @@ export const MODELS = {
|
|||
contextWindow: 128000,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"meta-llama/llama-3-8b-instruct": {
|
||||
id: "meta-llama/llama-3-8b-instruct",
|
||||
name: "Meta: Llama 3 8B Instruct",
|
||||
api: "openai-completions",
|
||||
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<"openai-completions">,
|
||||
"meta-llama/llama-3-70b-instruct": {
|
||||
id: "meta-llama/llama-3-70b-instruct",
|
||||
name: "Meta: Llama 3 70B Instruct",
|
||||
|
|
@ -3997,6 +4082,23 @@ export const MODELS = {
|
|||
contextWindow: 8192,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"meta-llama/llama-3-8b-instruct": {
|
||||
id: "meta-llama/llama-3-8b-instruct",
|
||||
name: "Meta: Llama 3 8B Instruct",
|
||||
api: "openai-completions",
|
||||
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<"openai-completions">,
|
||||
"mistralai/mixtral-8x22b-instruct": {
|
||||
id: "mistralai/mixtral-8x22b-instruct",
|
||||
name: "Mistral: Mixtral 8x22B Instruct",
|
||||
|
|
@ -4031,23 +4133,6 @@ export const MODELS = {
|
|||
contextWindow: 128000,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"mistralai/mistral-small": {
|
||||
id: "mistralai/mistral-small",
|
||||
name: "Mistral Small",
|
||||
api: "openai-completions",
|
||||
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<"openai-completions">,
|
||||
"mistralai/mistral-tiny": {
|
||||
id: "mistralai/mistral-tiny",
|
||||
name: "Mistral Tiny",
|
||||
|
|
@ -4065,6 +4150,23 @@ export const MODELS = {
|
|||
contextWindow: 32768,
|
||||
maxTokens: 4096,
|
||||
} satisfies Model<"openai-completions">,
|
||||
"mistralai/mistral-small": {
|
||||
id: "mistralai/mistral-small",
|
||||
name: "Mistral Small",
|
||||
api: "openai-completions",
|
||||
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<"openai-completions">,
|
||||
"mistralai/mixtral-8x7b-instruct": {
|
||||
id: "mistralai/mixtral-8x7b-instruct",
|
||||
name: "Mistral: Mixtral 8x7B Instruct",
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai"> = (
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ export type StopReason = "stop" | "length" | "toolUse" | "error" | "aborted";
|
|||
export interface UserMessage {
|
||||
role: "user";
|
||||
content: string | (TextContent | ImageContent)[];
|
||||
timestamp: number; // Unix timestamp in milliseconds
|
||||
}
|
||||
|
||||
export interface AssistantMessage {
|
||||
|
|
@ -106,6 +107,7 @@ export interface AssistantMessage {
|
|||
usage: Usage;
|
||||
stopReason: StopReason;
|
||||
errorMessage?: string;
|
||||
timestamp: number; // Unix timestamp in milliseconds
|
||||
}
|
||||
|
||||
export interface ToolResultMessage<TDetails = any> {
|
||||
|
|
@ -115,6 +117,7 @@ export interface ToolResultMessage<TDetails = any> {
|
|||
output: string;
|
||||
details?: TDetails;
|
||||
isError: boolean;
|
||||
timestamp: number; // Unix timestamp in milliseconds
|
||||
}
|
||||
|
||||
export type Message = UserMessage | AssistantMessage | ToolResultMessage;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ async function testAbortSignal<TApi extends Api>(llm: Model<TApi>, options: Opti
|
|||
{
|
||||
role: "user",
|
||||
content: "What is 15 + 27? Think step by step. Then list 50 first names.",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -29,7 +30,11 @@ async function testAbortSignal<TApi extends Api>(llm: Model<TApi>, options: Opti
|
|||
expect(msg.content.length).toBeGreaterThan(0);
|
||||
|
||||
context.messages.push(msg);
|
||||
context.messages.push({ role: "user", content: "Please continue, but only generate 5 names." });
|
||||
context.messages.push({
|
||||
role: "user",
|
||||
content: "Please continue, but only generate 5 names.",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
const followUp = await complete(llm, context, options);
|
||||
expect(followUp.stopReason).toBe("stop");
|
||||
|
|
@ -42,7 +47,7 @@ async function testImmediateAbort<TApi extends Api>(llm: Model<TApi>, options: O
|
|||
controller.abort();
|
||||
|
||||
const context: Context = {
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
messages: [{ role: "user", content: "Hello", timestamp: Date.now() }],
|
||||
};
|
||||
|
||||
const response = await complete(llm, context, { ...options, signal: controller.signal });
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ async function calculateTest<TApi extends Api>(model: Model<TApi>, options: Opti
|
|||
1. Calculate 3485 * 4234 and 88823 * 3482 in parallel
|
||||
2. Calculate the sum of the two results using the calculator tool
|
||||
3. Output ONLY the final sum as a single integer number, nothing else.`,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
// Calculate expected results (using integers)
|
||||
|
|
@ -176,6 +177,7 @@ async function abortTest<TApi extends Api>(model: Model<TApi>, options: OptionsF
|
|||
const userPrompt: UserMessage = {
|
||||
role: "user",
|
||||
content: "Calculate 100 * 200, then 300 * 400, then 500 * 600, then sum all three results.",
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
// Create abort controller
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ async function testEmptyMessage<TApi extends Api>(llm: Model<TApi>, options: Opt
|
|||
const emptyMessage: UserMessage = {
|
||||
role: "user",
|
||||
content: [],
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
const context: Context = {
|
||||
|
|
@ -34,6 +35,7 @@ async function testEmptyStringMessage<TApi extends Api>(llm: Model<TApi>, option
|
|||
{
|
||||
role: "user",
|
||||
content: "",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -58,6 +60,7 @@ async function testWhitespaceOnlyMessage<TApi extends Api>(llm: Model<TApi>, opt
|
|||
{
|
||||
role: "user",
|
||||
content: " \n\t ",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -92,6 +95,7 @@ async function testEmptyAssistantMessage<TApi extends Api>(llm: Model<TApi>, opt
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
const context: Context = {
|
||||
|
|
@ -99,11 +103,13 @@ async function testEmptyAssistantMessage<TApi extends Api>(llm: Model<TApi>, opt
|
|||
{
|
||||
role: "user",
|
||||
content: "Hello, how are you?",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
emptyAssistant,
|
||||
{
|
||||
role: "user",
|
||||
content: "Please respond this time.",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ const providerContexts = {
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "toolUse",
|
||||
timestamp: Date.now(),
|
||||
} satisfies AssistantMessage,
|
||||
toolResult: {
|
||||
role: "toolResult" as const,
|
||||
|
|
@ -56,6 +57,7 @@ const providerContexts = {
|
|||
toolName: "get_weather",
|
||||
output: "Weather in Tokyo: 18°C, partly cloudy",
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
} satisfies ToolResultMessage,
|
||||
facts: {
|
||||
calculation: 391,
|
||||
|
|
@ -98,6 +100,7 @@ const providerContexts = {
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "toolUse",
|
||||
timestamp: Date.now(),
|
||||
} satisfies AssistantMessage,
|
||||
toolResult: {
|
||||
role: "toolResult" as const,
|
||||
|
|
@ -105,6 +108,7 @@ const providerContexts = {
|
|||
toolName: "get_weather",
|
||||
output: "Weather in Berlin: 22°C, sunny",
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
} satisfies ToolResultMessage,
|
||||
facts: {
|
||||
calculation: 456,
|
||||
|
|
@ -146,6 +150,7 @@ const providerContexts = {
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "toolUse",
|
||||
timestamp: Date.now(),
|
||||
} satisfies AssistantMessage,
|
||||
toolResult: {
|
||||
role: "toolResult" as const,
|
||||
|
|
@ -153,6 +158,7 @@ const providerContexts = {
|
|||
toolName: "get_weather",
|
||||
output: "Weather in London: 15°C, rainy",
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
} satisfies ToolResultMessage,
|
||||
facts: {
|
||||
calculation: 525,
|
||||
|
|
@ -196,6 +202,7 @@ const providerContexts = {
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "toolUse",
|
||||
timestamp: Date.now(),
|
||||
} satisfies AssistantMessage,
|
||||
toolResult: {
|
||||
role: "toolResult" as const,
|
||||
|
|
@ -203,6 +210,7 @@ const providerContexts = {
|
|||
toolName: "get_weather",
|
||||
output: "Weather in Sydney: 25°C, clear",
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
} satisfies ToolResultMessage,
|
||||
facts: {
|
||||
calculation: 486,
|
||||
|
|
@ -239,6 +247,7 @@ const providerContexts = {
|
|||
},
|
||||
stopReason: "error",
|
||||
errorMessage: "Request was aborted",
|
||||
timestamp: Date.now(),
|
||||
} satisfies AssistantMessage,
|
||||
toolResult: null,
|
||||
facts: {
|
||||
|
|
@ -263,6 +272,7 @@ async function testProviderHandoff<TApi extends Api>(
|
|||
{
|
||||
role: "user",
|
||||
content: "Please do some calculations, tell me about capitals, and check the weather.",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
sourceContext.message,
|
||||
];
|
||||
|
|
@ -281,6 +291,7 @@ async function testProviderHandoff<TApi extends Api>(
|
|||
3) What was the temperature?
|
||||
4) What capital city was mentioned?
|
||||
Please include the specific numbers and names.`,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
const context: Context = {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const calculatorTool: Tool<typeof calculatorSchema> = {
|
|||
async function basicTextGeneration<TApi extends Api>(model: Model<TApi>, options?: OptionsForApi<TApi>) {
|
||||
const context: Context = {
|
||||
systemPrompt: "You are a helpful assistant. Be concise.",
|
||||
messages: [{ role: "user", content: "Reply with exactly: 'Hello test successful'" }],
|
||||
messages: [{ role: "user", content: "Reply with exactly: 'Hello test successful'", timestamp: Date.now() }],
|
||||
};
|
||||
const response = await complete(model, context, options);
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ async function basicTextGeneration<TApi extends Api>(model: Model<TApi>, options
|
|||
expect(response.content.map((b) => (b.type === "text" ? b.text : "")).join("")).toContain("Hello test successful");
|
||||
|
||||
context.messages.push(response);
|
||||
context.messages.push({ role: "user", content: "Now say 'Goodbye test successful'" });
|
||||
context.messages.push({ role: "user", content: "Now say 'Goodbye test successful'", timestamp: Date.now() });
|
||||
|
||||
const secondResponse = await complete(model, context, options);
|
||||
|
||||
|
|
@ -65,6 +65,7 @@ async function handleToolCall<TApi extends Api>(model: Model<TApi>, options?: Op
|
|||
{
|
||||
role: "user",
|
||||
content: "Calculate 15 + 27 using the calculator tool.",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
tools: [calculatorTool],
|
||||
|
|
@ -141,7 +142,7 @@ async function handleStreaming<TApi extends Api>(model: Model<TApi>, options?: O
|
|||
let textCompleted = false;
|
||||
|
||||
const context: Context = {
|
||||
messages: [{ role: "user", content: "Count from 1 to 3" }],
|
||||
messages: [{ role: "user", content: "Count from 1 to 3", timestamp: Date.now() }],
|
||||
};
|
||||
|
||||
const s = stream(model, context, options);
|
||||
|
|
@ -174,6 +175,7 @@ async function handleThinking<TApi extends Api>(model: Model<TApi>, options?: Op
|
|||
{
|
||||
role: "user",
|
||||
content: `Think long and hard about ${(Math.random() * 255) | 0} + 27. Think step by step. Then output the result.`,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -228,6 +230,7 @@ async function handleImage<TApi extends Api>(model: Model<TApi>, options?: Optio
|
|||
},
|
||||
imageContent,
|
||||
],
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -251,6 +254,7 @@ async function multiTurn<TApi extends Api>(model: Model<TApi>, options?: Options
|
|||
{
|
||||
role: "user",
|
||||
content: "Think about this briefly, then calculate 42 * 17 and 453 + 434 using the calculator tool.",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
tools: [calculatorTool],
|
||||
|
|
@ -303,6 +307,7 @@ async function multiTurn<TApi extends Api>(model: Model<TApi>, options?: Options
|
|||
toolName: block.name,
|
||||
output: `${result}`,
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ describe("Tool Call Without Result Tests", () => {
|
|||
context.messages.push({
|
||||
role: "user",
|
||||
content: "Please calculate 25 * 18 using the calculate tool.",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// Step 3: Get the assistant's response (should contain a tool call)
|
||||
|
|
@ -54,6 +55,7 @@ describe("Tool Call Without Result Tests", () => {
|
|||
context.messages.push({
|
||||
role: "user",
|
||||
content: "Never mind, just tell me what is 2+2?",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// Step 5: The fix should filter out the orphaned tool call, and the request should succeed
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ async function testEmojiInToolResults<TApi extends Api>(llm: Model<TApi>, option
|
|||
{
|
||||
role: "user",
|
||||
content: "Use the test tool",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
|
|
@ -44,6 +45,7 @@ async function testEmojiInToolResults<TApi extends Api>(llm: Model<TApi>, option
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "toolUse",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
|
|
@ -72,6 +74,7 @@ async function testEmojiInToolResults<TApi extends Api>(llm: Model<TApi>, option
|
|||
- Mathematical symbols: ∑∫∂√
|
||||
- Special quotes: "curly" 'quotes'`,
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
context.messages.push(toolResult);
|
||||
|
|
@ -80,6 +83,7 @@ async function testEmojiInToolResults<TApi extends Api>(llm: Model<TApi>, option
|
|||
context.messages.push({
|
||||
role: "user",
|
||||
content: "Summarize the tool result briefly.",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// This should not throw a surrogate pair error
|
||||
|
|
@ -97,6 +101,7 @@ async function testRealWorldLinkedInData<TApi extends Api>(llm: Model<TApi>, opt
|
|||
{
|
||||
role: "user",
|
||||
content: "Use the linkedin tool to get comments",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
|
|
@ -119,6 +124,7 @@ async function testRealWorldLinkedInData<TApi extends Api>(llm: Model<TApi>, opt
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "toolUse",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
|
|
@ -151,6 +157,7 @@ Unanswered Comments: 2
|
|||
]
|
||||
}`,
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
context.messages.push(toolResult);
|
||||
|
|
@ -158,6 +165,7 @@ Unanswered Comments: 2
|
|||
context.messages.push({
|
||||
role: "user",
|
||||
content: "How many comments are there?",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// This should not throw a surrogate pair error
|
||||
|
|
@ -175,6 +183,7 @@ async function testUnpairedHighSurrogate<TApi extends Api>(llm: Model<TApi>, opt
|
|||
{
|
||||
role: "user",
|
||||
content: "Use the test tool",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
|
|
@ -197,6 +206,7 @@ async function testUnpairedHighSurrogate<TApi extends Api>(llm: Model<TApi>, opt
|
|||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "toolUse",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
|
|
@ -218,6 +228,7 @@ async function testUnpairedHighSurrogate<TApi extends Api>(llm: Model<TApi>, opt
|
|||
toolName: "test_tool",
|
||||
output: `Text with unpaired surrogate: ${unpairedSurrogate} <- should be sanitized`,
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
context.messages.push(toolResult);
|
||||
|
|
@ -225,6 +236,7 @@ async function testUnpairedHighSurrogate<TApi extends Api>(llm: Model<TApi>, opt
|
|||
context.messages.push({
|
||||
role: "user",
|
||||
content: "What did the tool return?",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// This should not throw a surrogate pair error
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ export class Agent {
|
|||
role: "user",
|
||||
content,
|
||||
attachments: attachments?.length ? attachments : undefined,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
this.abortController = new AbortController();
|
||||
|
|
@ -311,6 +312,7 @@ export class Agent {
|
|||
},
|
||||
stopReason: this.abortController?.signal.aborted ? "aborted" : "error",
|
||||
errorMessage: err?.message || String(err),
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
this.appendMessage(msg as AppMessage);
|
||||
this.patch({ error: err?.message || String(err) });
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ function streamSimpleProxy(
|
|||
cacheWrite: 0,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
|
||||
|
|
|
|||
|
|
@ -229,7 +229,14 @@ export class ToolMessage extends LitElement {
|
|||
|
||||
// Render tool content (renderer handles errors and styling)
|
||||
const result: ToolResultMessageType<any> | undefined = this.aborted
|
||||
? { role: "toolResult", isError: true, output: "", toolCallId: this.toolCall.id, toolName: this.toolCall.name }
|
||||
? {
|
||||
role: "toolResult",
|
||||
isError: true,
|
||||
output: "",
|
||||
toolCallId: this.toolCall.id,
|
||||
toolName: this.toolCall.name,
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
: this.result;
|
||||
const renderResult = renderTool(
|
||||
toolName,
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export class ProviderKeyInput extends LitElement {
|
|||
}
|
||||
|
||||
const context: Context = {
|
||||
messages: [{ role: "user", content: "Reply with: ok" }],
|
||||
messages: [{ role: "user", content: "Reply with: ok", timestamp: Date.now() }],
|
||||
};
|
||||
|
||||
const result = await complete(model, context, {
|
||||
|
|
|
|||
|
|
@ -208,13 +208,6 @@ export class SettingsDialog extends LitElement {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="pt-4 flex-shrink-0">
|
||||
<p class="text-xs text-muted-foreground text-center">
|
||||
${i18n("Settings are stored locally in your browser")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import type { AgentState } from "../../agent/agent.js";
|
||||
import { Store } from "../store.js";
|
||||
import type { SessionData, SessionMetadata, StoreConfig } from "../types.js";
|
||||
|
||||
|
|
@ -82,7 +83,12 @@ export class SessionsStore extends Store {
|
|||
}
|
||||
|
||||
// Alias methods for backward compatibility
|
||||
async saveSession(id: string, state: any, metadata: SessionMetadata | undefined, title?: string): Promise<void> {
|
||||
async saveSession(
|
||||
id: string,
|
||||
state: AgentState,
|
||||
metadata: SessionMetadata | undefined,
|
||||
title?: string,
|
||||
): Promise<void> {
|
||||
// If metadata is provided, use it; otherwise create it from state
|
||||
const meta: SessionMetadata = metadata || {
|
||||
id,
|
||||
|
|
@ -90,7 +96,7 @@ export class SessionsStore extends Store {
|
|||
createdAt: new Date().toISOString(),
|
||||
lastModified: new Date().toISOString(),
|
||||
messageCount: state.messages?.length || 0,
|
||||
usage: state.usage || {
|
||||
usage: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
|
|
|
|||
|
|
@ -100,7 +100,10 @@ export function createExtractDocumentTool(): AgentTool<typeof extractDocumentSch
|
|||
|
||||
// Extract filename from URL
|
||||
const urlParts = url.split("/");
|
||||
const fileName = urlParts[urlParts.length - 1]?.split("?")[0] || "document";
|
||||
let fileName = urlParts[urlParts.length - 1]?.split("?")[0] || "document";
|
||||
if (url.startsWith("https://arxiv.org/")) {
|
||||
fileName = fileName + ".pdf";
|
||||
}
|
||||
|
||||
// Use loadAttachment to process the document
|
||||
const attachment = await loadAttachment(arrayBuffer, fileName);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue