Skip to content

Commit fc47044

Browse files
committed
test: update assertions for spec conformance fixes across all providers
1 parent 7205bef commit fc47044

10 files changed

Lines changed: 882 additions & 132 deletions

src/__tests__/cohere.test.ts

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,3 +1734,228 @@ describe("Cohere ResponseOverrides", () => {
17341734
expect(json.finish_reason).toBe("COMPLETE");
17351735
});
17361736
});
1737+
1738+
// ─── Fix: structured content extraction ───────────────────────────────────
1739+
1740+
describe("cohereToCompletionRequest (structured content)", () => {
1741+
it("extracts text from array-of-parts content", () => {
1742+
const result = cohereToCompletionRequest({
1743+
model: "command-r-plus",
1744+
messages: [
1745+
{
1746+
role: "user",
1747+
content: [
1748+
{ type: "text", text: "Hello " },
1749+
{ type: "text", text: "world" },
1750+
],
1751+
},
1752+
],
1753+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1754+
expect(result.messages[0].content).toBe("Hello world");
1755+
});
1756+
1757+
it("ignores non-text parts in structured content", () => {
1758+
const result = cohereToCompletionRequest({
1759+
model: "command-r-plus",
1760+
messages: [
1761+
{
1762+
role: "user",
1763+
content: [
1764+
{ type: "image", text: undefined },
1765+
{ type: "text", text: "Only this" },
1766+
],
1767+
},
1768+
],
1769+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1770+
expect(result.messages[0].content).toBe("Only this");
1771+
});
1772+
1773+
it("handles string content unchanged", () => {
1774+
const result = cohereToCompletionRequest({
1775+
model: "command-r-plus",
1776+
messages: [{ role: "user", content: "plain string" }],
1777+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1778+
expect(result.messages[0].content).toBe("plain string");
1779+
});
1780+
1781+
it("extracts structured content for system messages", () => {
1782+
const result = cohereToCompletionRequest({
1783+
model: "command-r-plus",
1784+
messages: [
1785+
{
1786+
role: "system",
1787+
content: [{ type: "text", text: "Be helpful" }],
1788+
},
1789+
{ role: "user", content: "hi" },
1790+
],
1791+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1792+
expect(result.messages[0].content).toBe("Be helpful");
1793+
});
1794+
1795+
it("extracts structured content for assistant messages", () => {
1796+
const result = cohereToCompletionRequest({
1797+
model: "command-r-plus",
1798+
messages: [
1799+
{ role: "user", content: "hi" },
1800+
{
1801+
role: "assistant",
1802+
content: [{ type: "text", text: "Hello!" }],
1803+
},
1804+
],
1805+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1806+
expect(result.messages[1].content).toBe("Hello!");
1807+
});
1808+
1809+
it("extracts structured content for tool messages", () => {
1810+
const result = cohereToCompletionRequest({
1811+
model: "command-r-plus",
1812+
messages: [
1813+
{
1814+
role: "tool",
1815+
content: [{ type: "text", text: '{"result":42}' }],
1816+
tool_call_id: "call_1",
1817+
},
1818+
],
1819+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1820+
expect(result.messages[0].content).toBe('{"result":42}');
1821+
});
1822+
});
1823+
1824+
// ─── Fix: Cohere v2 native tool format ────────────────────────────────────
1825+
1826+
describe("cohereToCompletionRequest (native Cohere v2 tools)", () => {
1827+
it("converts Cohere v2 native tool format (parameter_definitions)", () => {
1828+
const result = cohereToCompletionRequest({
1829+
model: "command-r-plus",
1830+
messages: [{ role: "user", content: "hi" }],
1831+
tools: [
1832+
{
1833+
name: "get_weather",
1834+
description: "Get the weather",
1835+
parameter_definitions: {
1836+
city: { type: "str", description: "City name", required: true },
1837+
},
1838+
},
1839+
],
1840+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1841+
expect(result.tools).toHaveLength(1);
1842+
expect(result.tools![0]).toEqual({
1843+
type: "function",
1844+
function: {
1845+
name: "get_weather",
1846+
description: "Get the weather",
1847+
parameters: {
1848+
city: { type: "str", description: "City name", required: true },
1849+
},
1850+
},
1851+
});
1852+
});
1853+
1854+
it("still accepts OpenAI-style tool format", () => {
1855+
const result = cohereToCompletionRequest({
1856+
model: "command-r-plus",
1857+
messages: [{ role: "user", content: "hi" }],
1858+
tools: [
1859+
{
1860+
type: "function",
1861+
function: {
1862+
name: "search",
1863+
description: "Search things",
1864+
parameters: { type: "object", properties: { q: { type: "string" } } },
1865+
},
1866+
},
1867+
],
1868+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1869+
expect(result.tools).toHaveLength(1);
1870+
expect(result.tools![0].function.name).toBe("search");
1871+
expect(result.tools![0].function.parameters).toEqual({
1872+
type: "object",
1873+
properties: { q: { type: "string" } },
1874+
});
1875+
});
1876+
1877+
it("handles mixed OpenAI and native tool formats", () => {
1878+
const result = cohereToCompletionRequest({
1879+
model: "command-r-plus",
1880+
messages: [{ role: "user", content: "hi" }],
1881+
tools: [
1882+
{
1883+
type: "function",
1884+
function: {
1885+
name: "openai_tool",
1886+
description: "OpenAI style",
1887+
},
1888+
},
1889+
{
1890+
name: "native_tool",
1891+
description: "Cohere native",
1892+
parameter_definitions: { x: { type: "int" } },
1893+
},
1894+
],
1895+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1896+
expect(result.tools).toHaveLength(2);
1897+
expect(result.tools![0].function.name).toBe("openai_tool");
1898+
expect(result.tools![1].function.name).toBe("native_tool");
1899+
expect(result.tools![1].function.parameters).toEqual({ x: { type: "int" } });
1900+
});
1901+
});
1902+
1903+
// ─── Fix: temperature forwarding ──────────────────────────────────────────
1904+
1905+
describe("cohereToCompletionRequest (temperature)", () => {
1906+
it("forwards temperature to ChatCompletionRequest", () => {
1907+
const result = cohereToCompletionRequest({
1908+
model: "command-r-plus",
1909+
messages: [{ role: "user", content: "hello" }],
1910+
temperature: 0.7,
1911+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1912+
expect(result.temperature).toBe(0.7);
1913+
});
1914+
1915+
it("forwards temperature=0", () => {
1916+
const result = cohereToCompletionRequest({
1917+
model: "command-r-plus",
1918+
messages: [{ role: "user", content: "hello" }],
1919+
temperature: 0,
1920+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1921+
expect(result.temperature).toBe(0);
1922+
});
1923+
1924+
it("omits temperature when not provided", () => {
1925+
const result = cohereToCompletionRequest({
1926+
model: "command-r-plus",
1927+
messages: [{ role: "user", content: "hello" }],
1928+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1929+
expect(result.temperature).toBeUndefined();
1930+
});
1931+
});
1932+
1933+
// ─── Fix: max_tokens forwarding ───────────────────────────────────────────
1934+
1935+
describe("cohereToCompletionRequest (max_tokens)", () => {
1936+
it("forwards max_tokens to ChatCompletionRequest", () => {
1937+
const result = cohereToCompletionRequest({
1938+
model: "command-r-plus",
1939+
messages: [{ role: "user", content: "hello" }],
1940+
max_tokens: 1024,
1941+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1942+
expect(result.max_tokens).toBe(1024);
1943+
});
1944+
1945+
it("forwards max_tokens=0", () => {
1946+
const result = cohereToCompletionRequest({
1947+
model: "command-r-plus",
1948+
messages: [{ role: "user", content: "hello" }],
1949+
max_tokens: 0,
1950+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1951+
expect(result.max_tokens).toBe(0);
1952+
});
1953+
1954+
it("omits max_tokens when not provided", () => {
1955+
const result = cohereToCompletionRequest({
1956+
model: "command-r-plus",
1957+
messages: [{ role: "user", content: "hello" }],
1958+
} as Parameters<typeof cohereToCompletionRequest>[0]);
1959+
expect(result.max_tokens).toBeUndefined();
1960+
});
1961+
});

src/__tests__/gemini.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,45 @@ describe("geminiToCompletionRequest", () => {
305305
expect(result.temperature).toBe(0.7);
306306
});
307307

308+
it("forwards generationConfig.maxOutputTokens as max_tokens", () => {
309+
const result = geminiToCompletionRequest(
310+
{
311+
contents: [{ role: "user", parts: [{ text: "hi" }] }],
312+
generationConfig: { maxOutputTokens: 1024 },
313+
},
314+
"gemini-2.0-flash",
315+
false,
316+
);
317+
expect(result.max_tokens).toBe(1024);
318+
});
319+
320+
it("forwards generationConfig.topP and topK", () => {
321+
const result = geminiToCompletionRequest(
322+
{
323+
contents: [{ role: "user", parts: [{ text: "hi" }] }],
324+
generationConfig: { topP: 0.95, topK: 40 },
325+
},
326+
"gemini-2.0-flash",
327+
false,
328+
);
329+
expect(result.top_p).toBe(0.95);
330+
expect(result.top_k).toBe(40);
331+
});
332+
333+
it("leaves max_tokens/top_p/top_k undefined when generationConfig omits them", () => {
334+
const result = geminiToCompletionRequest(
335+
{
336+
contents: [{ role: "user", parts: [{ text: "hi" }] }],
337+
generationConfig: { temperature: 0.5 },
338+
},
339+
"gemini-2.0-flash",
340+
false,
341+
);
342+
expect(result.max_tokens).toBeUndefined();
343+
expect(result.top_p).toBeUndefined();
344+
expect(result.top_k).toBeUndefined();
345+
});
346+
308347
it("converts multiple functionResponse parts with unique tool_call_ids", () => {
309348
const result = geminiToCompletionRequest(
310349
{
@@ -427,6 +466,20 @@ describe("POST /v1beta/models/{model}:generateContent (non-streaming)", () => {
427466
expect(body.candidates[0].content.parts[0].functionCall.name).toBe("get_weather");
428467
expect(body.candidates[0].content.parts[1].functionCall.name).toBe("get_time");
429468
});
469+
470+
it("functionCall parts do NOT contain an id field", async () => {
471+
instance = await createServer(allFixtures);
472+
const res = await post(`${instance.url}/v1beta/models/gemini-2.0-flash:generateContent`, {
473+
contents: [{ role: "user", parts: [{ text: "weather" }] }],
474+
});
475+
476+
const body = JSON.parse(res.body);
477+
const fc = body.candidates[0].content.parts[0].functionCall;
478+
expect(fc.name).toBe("get_weather");
479+
expect(fc.args).toEqual({ city: "NYC" });
480+
// Gemini FunctionCall schema only has name + args — no id
481+
expect(fc).not.toHaveProperty("id");
482+
});
430483
});
431484

432485
// ─── Integration tests: Gemini streaming ────────────────────────────────────

0 commit comments

Comments
 (0)