Skip to content
Merged
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aimock",
"version": "1.19.4",
"version": "1.19.5",
"description": "Fixture authoring guidance for @copilotkit/aimock — LLM, multimedia, MCP, A2A, AG-UI, vector, and service mocking",
"author": {
"name": "CopilotKit"
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# @copilotkit/aimock

## [1.19.5] - 2026-05-09

### Fixed

- **Responses API request conversion** — forward `max_output_tokens` and `response_format` from Responses requests to the underlying Chat Completions call
- **Gemini request conversion** — forward `maxOutputTokens`, `topP`, `topK` from `generationConfig`; remove synthetic `functionCall.id` that real Gemini does not produce
- **Cohere request conversion** — structured content (images, documents), native tool definitions, `temperature`, `max_tokens`, and `stop_sequences` now forwarded
- **Ollama request conversion** — `tool_calls` on assistant messages, base64 `images` on user messages, `system` parameter on `/api/generate`
- **Chat Completions error responses** — add `param` field per OpenAI error spec
- **Moderation response shape** — correct `categories` and `category_scores` to match the real OpenAI moderation object (boolean flags + float scores)
- **Transcription verbose response** — add `task`, `duration`, `segments`, `words` fields for `verbose_json` format
- **Search response shape** — add `status` field to search results
- **Rerank response shape** — wrap results in `{ results: [...] }` with `relevance_score` per result
- **Realtime WebSocket** — add `previous_item_id` to conversation items, correct event ID prefixes, add missing fields on session and response events
- **Gemini Live WebSocket** — `generationConfig` alias for `generation_config`, `turnComplete` server event, correct gRPC status codes in error events, complete `httpToGrpc` mapper
- **Anthropic thinking blocks** — add `signature` field to `thinking` content blocks and `signature_delta` event type for extended thinking with signatures

### Added

- **Drift tests for 9 multimedia/auxiliary providers** — images, speech/TTS, transcription/STT, moderation, ElevenLabs audio, fal.ai, fal.ai queue lifecycle, video, rerank
- **Error shape drift tests** — OpenAI Chat, Anthropic Claude, Gemini, Cohere error response shapes validated against SDK types
- **Reasoning/thinking drift tests** — OpenAI Chat `reasoning_effort`, OpenAI Responses `reasoning`, Anthropic `thinking` content blocks, Gemini `thinking_config`

## [1.19.4] - 2026-05-08

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion charts/aimock/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ name: aimock
description: Mock infrastructure for AI application testing (OpenAI, Anthropic, Gemini, MCP, A2A, vector)
type: application
version: 0.1.0
appVersion: "1.19.4"
appVersion: "1.19.5"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@copilotkit/aimock",
"version": "1.19.4",
"version": "1.19.5",
"description": "Mock infrastructure for AI application testing — LLM APIs, image generation, text-to-speech, transcription, audio generation, video generation, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.",
"license": "MIT",
"keywords": [
Expand Down
225 changes: 225 additions & 0 deletions src/__tests__/cohere.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1734,3 +1734,228 @@ describe("Cohere ResponseOverrides", () => {
expect(json.finish_reason).toBe("COMPLETE");
});
});

// ─── Fix: structured content extraction ───────────────────────────────────

describe("cohereToCompletionRequest (structured content)", () => {
it("extracts text from array-of-parts content", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Hello " },
{ type: "text", text: "world" },
],
},
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.messages[0].content).toBe("Hello world");
});

it("ignores non-text parts in structured content", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [
{
role: "user",
content: [
{ type: "image", text: undefined },
{ type: "text", text: "Only this" },
],
},
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.messages[0].content).toBe("Only this");
});

it("handles string content unchanged", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "plain string" }],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.messages[0].content).toBe("plain string");
});

it("extracts structured content for system messages", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [
{
role: "system",
content: [{ type: "text", text: "Be helpful" }],
},
{ role: "user", content: "hi" },
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.messages[0].content).toBe("Be helpful");
});

it("extracts structured content for assistant messages", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [
{ role: "user", content: "hi" },
{
role: "assistant",
content: [{ type: "text", text: "Hello!" }],
},
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.messages[1].content).toBe("Hello!");
});

it("extracts structured content for tool messages", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [
{
role: "tool",
content: [{ type: "text", text: '{"result":42}' }],
tool_call_id: "call_1",
},
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.messages[0].content).toBe('{"result":42}');
});
});

// ─── Fix: Cohere v2 native tool format ────────────────────────────────────

describe("cohereToCompletionRequest (native Cohere v2 tools)", () => {
it("converts Cohere v2 native tool format (parameter_definitions)", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hi" }],
tools: [
{
name: "get_weather",
description: "Get the weather",
parameter_definitions: {
city: { type: "str", description: "City name", required: true },
},
},
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.tools).toHaveLength(1);
expect(result.tools![0]).toEqual({
type: "function",
function: {
name: "get_weather",
description: "Get the weather",
parameters: {
city: { type: "str", description: "City name", required: true },
},
},
});
});

it("still accepts OpenAI-style tool format", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hi" }],
tools: [
{
type: "function",
function: {
name: "search",
description: "Search things",
parameters: { type: "object", properties: { q: { type: "string" } } },
},
},
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.tools).toHaveLength(1);
expect(result.tools![0].function.name).toBe("search");
expect(result.tools![0].function.parameters).toEqual({
type: "object",
properties: { q: { type: "string" } },
});
});

it("handles mixed OpenAI and native tool formats", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hi" }],
tools: [
{
type: "function",
function: {
name: "openai_tool",
description: "OpenAI style",
},
},
{
name: "native_tool",
description: "Cohere native",
parameter_definitions: { x: { type: "int" } },
},
],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.tools).toHaveLength(2);
expect(result.tools![0].function.name).toBe("openai_tool");
expect(result.tools![1].function.name).toBe("native_tool");
expect(result.tools![1].function.parameters).toEqual({ x: { type: "int" } });
});
});

// ─── Fix: temperature forwarding ──────────────────────────────────────────

describe("cohereToCompletionRequest (temperature)", () => {
it("forwards temperature to ChatCompletionRequest", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hello" }],
temperature: 0.7,
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.temperature).toBe(0.7);
});

it("forwards temperature=0", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hello" }],
temperature: 0,
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.temperature).toBe(0);
});

it("omits temperature when not provided", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hello" }],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.temperature).toBeUndefined();
});
});

// ─── Fix: max_tokens forwarding ───────────────────────────────────────────

describe("cohereToCompletionRequest (max_tokens)", () => {
it("forwards max_tokens to ChatCompletionRequest", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hello" }],
max_tokens: 1024,
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.max_tokens).toBe(1024);
});

it("forwards max_tokens=0", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hello" }],
max_tokens: 0,
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.max_tokens).toBe(0);
});

it("omits max_tokens when not provided", () => {
const result = cohereToCompletionRequest({
model: "command-r-plus",
messages: [{ role: "user", content: "hello" }],
} as Parameters<typeof cohereToCompletionRequest>[0]);
expect(result.max_tokens).toBeUndefined();
});
});
Loading
Loading