mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 06:04:51 +00:00
feat(ai): Migrate tests to Vitest and add provider test coverage
- Switch from Node.js test runner to Vitest for better DX - Add test suites for Grok, Groq, Cerebras, and OpenRouter providers - Add Ollama test suite with automatic server lifecycle management - Include thinking mode and multi-turn tests for all providers - Remove example files (consolidated into test suite) - Add VS Code test configuration
This commit is contained in:
parent
da66a97ea7
commit
3f36051bc6
14 changed files with 1319 additions and 636 deletions
|
|
@ -45,8 +45,8 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
stream_options: { include_usage: true },
|
||||
};
|
||||
|
||||
// Cerebras doesn't like the "store" field
|
||||
if (!this.client.baseURL?.includes("cerebras.ai")) {
|
||||
// Cerebras/xAI dont like the "store" field
|
||||
if (!this.client.baseURL?.includes("cerebras.ai") || this.client.baseURL?.includes("api.x.ai")) {
|
||||
(params as any).store = false;
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
params.tool_choice = options.toolChoice;
|
||||
}
|
||||
|
||||
if (options?.reasoningEffort && this.isReasoningModel()) {
|
||||
if (options?.reasoningEffort && this.isReasoningModel() && !this.model.toLowerCase().includes("grok")) {
|
||||
params.reasoning_effort = options.reasoningEffort;
|
||||
}
|
||||
|
||||
|
|
@ -77,14 +77,7 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
let content = "";
|
||||
let reasoningContent = "";
|
||||
let reasoningField: "reasoning" | "reasoning_content" | null = null;
|
||||
const toolCallsMap = new Map<
|
||||
number,
|
||||
{
|
||||
id: string;
|
||||
name: string;
|
||||
arguments: string;
|
||||
}
|
||||
>();
|
||||
const parsedToolCalls: { id: string; name: string; arguments: string }[] = [];
|
||||
let usage: TokenUsage = {
|
||||
input: 0,
|
||||
output: 0,
|
||||
|
|
@ -97,7 +90,9 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
if (chunk.usage) {
|
||||
usage = {
|
||||
input: chunk.usage.prompt_tokens || 0,
|
||||
output: chunk.usage.completion_tokens || 0,
|
||||
output:
|
||||
(chunk.usage.completion_tokens || 0) +
|
||||
(chunk.usage.completion_tokens_details?.reasoning_tokens || 0),
|
||||
cacheRead: chunk.usage.prompt_tokens_details?.cached_tokens || 0,
|
||||
cacheWrite: 0,
|
||||
};
|
||||
|
|
@ -122,7 +117,7 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
blockType = "text";
|
||||
}
|
||||
|
||||
// Handle LLAMA.cpp reasoning_content
|
||||
// Handle reasoning_content field
|
||||
if (
|
||||
(choice.delta as any).reasoning_content !== null &&
|
||||
(choice.delta as any).reasoning_content !== undefined
|
||||
|
|
@ -137,7 +132,7 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
blockType = "thinking";
|
||||
}
|
||||
|
||||
// Handle Ollama reasoning field
|
||||
// Handle reasoning field
|
||||
if ((choice.delta as any).reasoning !== null && (choice.delta as any).reasoning !== undefined) {
|
||||
if (blockType === "text") {
|
||||
options?.onText?.("", true);
|
||||
|
|
@ -160,21 +155,22 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
blockType = null;
|
||||
}
|
||||
for (const toolCall of choice.delta.tool_calls) {
|
||||
const index = toolCall.index;
|
||||
|
||||
if (!toolCallsMap.has(index)) {
|
||||
toolCallsMap.set(index, {
|
||||
if (
|
||||
parsedToolCalls.length === 0 ||
|
||||
(toolCall.id !== undefined && parsedToolCalls[parsedToolCalls.length - 1].id !== toolCall.id)
|
||||
) {
|
||||
parsedToolCalls.push({
|
||||
id: toolCall.id || "",
|
||||
name: toolCall.function?.name || "",
|
||||
arguments: "",
|
||||
});
|
||||
}
|
||||
|
||||
const existing = toolCallsMap.get(index)!;
|
||||
if (toolCall.id) existing.id = toolCall.id;
|
||||
if (toolCall.function?.name) existing.name = toolCall.function.name;
|
||||
const current = parsedToolCalls[parsedToolCalls.length - 1];
|
||||
if (toolCall.id) current.id = toolCall.id;
|
||||
if (toolCall.function?.name) current.name = toolCall.function.name;
|
||||
if (toolCall.function?.arguments) {
|
||||
existing.arguments += toolCall.function.arguments;
|
||||
current.arguments += toolCall.function.arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,7 +191,7 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
}
|
||||
|
||||
// Convert tool calls map to array
|
||||
const toolCalls: ToolCall[] = Array.from(toolCallsMap.values()).map((tc) => ({
|
||||
const toolCalls: ToolCall[] = parsedToolCalls.map((tc) => ({
|
||||
id: tc.id,
|
||||
name: tc.name,
|
||||
arguments: JSON.parse(tc.arguments),
|
||||
|
|
@ -232,8 +228,12 @@ export class OpenAICompletionsLLM implements LLM<OpenAICompletionsLLMOptions> {
|
|||
|
||||
// Add system prompt if provided
|
||||
if (systemPrompt) {
|
||||
// Cerebras doesn't like the "developer" role
|
||||
const role = this.isReasoningModel() && !this.client.baseURL?.includes("cerebras.ai") ? "developer" : "system";
|
||||
// Cerebras/xAi don't like the "developer" role
|
||||
const useDeveloperRole =
|
||||
this.isReasoningModel() &&
|
||||
!this.client.baseURL?.includes("cerebras.ai") &&
|
||||
!this.client.baseURL?.includes("api.x.ai");
|
||||
const role = useDeveloperRole ? "developer" : "system";
|
||||
params.push({ role: role, content: systemPrompt });
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue