test(llm): cover OpenAI-compatible Chat parity
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
{"version":1,"interactions":[{"request":{"method":"POST","url":"https://api.deepseek.com/v1/chat/completions","headers":{"content-type":"application/json"},"body":"{\"model\":\"deepseek-chat\",\"messages\":[{\"role\":\"system\",\"content\":\"You are concise.\"},{\"role\":\"user\",\"content\":\"Reply with exactly: Hello!\"}],\"stream\":true,\"max_tokens\":20,\"temperature\":0}"},"response":{"status":200,"headers":{"content-type":"text/event-stream; charset=utf-8"},"body":"data: {\"id\":\"37be8034-f5e7-41e4-8ee7-39e0b5c613a2\",\"object\":\"chat.completion.chunk\",\"created\":1777218434,\"model\":\"deepseek-v4-flash\",\"system_fingerprint\":\"fp_058df29938_prod0820_fp8_kvcache_20260402\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"37be8034-f5e7-41e4-8ee7-39e0b5c613a2\",\"object\":\"chat.completion.chunk\",\"created\":1777218434,\"model\":\"deepseek-v4-flash\",\"system_fingerprint\":\"fp_058df29938_prod0820_fp8_kvcache_20260402\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"Hello\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"37be8034-f5e7-41e4-8ee7-39e0b5c613a2\",\"object\":\"chat.completion.chunk\",\"created\":1777218434,\"model\":\"deepseek-v4-flash\",\"system_fingerprint\":\"fp_058df29938_prod0820_fp8_kvcache_20260402\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"!\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"37be8034-f5e7-41e4-8ee7-39e0b5c613a2\",\"object\":\"chat.completion.chunk\",\"created\":1777218434,\"model\":\"deepseek-v4-flash\",\"system_fingerprint\":\"fp_058df29938_prod0820_fp8_kvcache_20260402\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\"},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":14,\"completion_tokens\":2,\"total_tokens\":16,\"prompt_tokens_details\":{\"cached_tokens\":0},\"prompt_cache_hit_tokens\":0,\"prompt_cache_miss_tokens\":14}}\n\ndata: [DONE]\n\n"}}]}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { LLM } from "../../src"
|
||||
import { client } from "../../src/adapter"
|
||||
import { OpenAICompatibleChat } from "../../src/provider/openai-compatible-chat"
|
||||
import { recordedTests } from "../recorded-test"
|
||||
|
||||
const deepseekModel = OpenAICompatibleChat.deepseek({
|
||||
id: "deepseek-chat",
|
||||
apiKey: process.env.DEEPSEEK_API_KEY ?? "fixture",
|
||||
})
|
||||
|
||||
const deepseekRequest = LLM.request({
|
||||
id: "recorded_deepseek_text",
|
||||
model: deepseekModel,
|
||||
system: "You are concise.",
|
||||
prompt: "Reply with exactly: Hello!",
|
||||
generation: { maxTokens: 20, temperature: 0 },
|
||||
})
|
||||
|
||||
const recorded = recordedTests({ prefix: "openai-compatible-chat" })
|
||||
const llm = client({ adapters: [OpenAICompatibleChat.adapter] })
|
||||
|
||||
describe("OpenAI-compatible Chat recorded", () => {
|
||||
recorded.effect.with("deepseek streams text", { requires: ["DEEPSEEK_API_KEY"] }, () =>
|
||||
Effect.gen(function* () {
|
||||
const response = yield* llm.generate(deepseekRequest)
|
||||
|
||||
expect(LLM.outputText(response)).toMatch(/^Hello!?$/)
|
||||
expect(response.events.at(-1)).toMatchObject({ type: "request-finish", reason: "stop" })
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -124,6 +124,72 @@ describe("OpenAI-compatible Chat adapter", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("matches AI SDK compatible basic request body fixture", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* client({ adapters: [OpenAICompatibleChat.adapter] }).prepare(request)
|
||||
|
||||
expect(prepared.target).toEqual({
|
||||
model: "deepseek-chat",
|
||||
messages: [
|
||||
{ role: "system", content: "You are concise." },
|
||||
{ role: "user", content: "Say hello." },
|
||||
],
|
||||
stream: true,
|
||||
max_tokens: 20,
|
||||
temperature: 0,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("matches AI SDK compatible tool request body fixture", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* client({ adapters: [OpenAICompatibleChat.adapter] }).prepare(
|
||||
LLM.request({
|
||||
id: "req_tool_parity",
|
||||
model,
|
||||
tools: [{
|
||||
name: "lookup",
|
||||
description: "Lookup data",
|
||||
inputSchema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
}],
|
||||
toolChoice: "lookup",
|
||||
messages: [
|
||||
LLM.user("What is the weather?"),
|
||||
LLM.assistant([LLM.toolCall({ id: "call_1", name: "lookup", input: { query: "weather" } })]),
|
||||
LLM.toolMessage({ id: "call_1", name: "lookup", result: { forecast: "sunny" } }),
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.target).toEqual({
|
||||
model: "deepseek-chat",
|
||||
messages: [
|
||||
{ role: "user", content: "What is the weather?" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: null,
|
||||
tool_calls: [{
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: { name: "lookup", arguments: '{"query":"weather"}' },
|
||||
}],
|
||||
},
|
||||
{ role: "tool", tool_call_id: "call_1", content: '{"forecast":"sunny"}' },
|
||||
],
|
||||
tools: [{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "lookup",
|
||||
description: "Lookup data",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
},
|
||||
}],
|
||||
tool_choice: { type: "function", function: { name: "lookup" } },
|
||||
stream: true,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("posts to the configured compatible endpoint and parses text usage", () =>
|
||||
Effect.gen(function* () {
|
||||
const response = yield* client({
|
||||
|
||||
Reference in New Issue
Block a user