141 lines
4.9 KiB
TypeScript
141 lines
4.9 KiB
TypeScript
import { describe, expect } from "bun:test"
|
|
import { Effect } from "effect"
|
|
import { LLM, LLMEvent, Message } from "../../src"
|
|
import { configure } from "../../src/providers/openai-compatible-responses"
|
|
import { OpenAI } from "../../src/providers"
|
|
import { OpenResponses } from "../../src/protocols/open-responses"
|
|
import { OpenAICompatibleResponses } from "../../src/protocols/openai-compatible-responses"
|
|
import { OpenAIResponses } from "../../src/protocols/openai-responses"
|
|
import { LLMClient } from "../../src/route"
|
|
import { it } from "../lib/effect"
|
|
import { fixedResponse } from "../lib/http"
|
|
import { sseEvents } from "../lib/sse"
|
|
|
|
describe("Open Responses-compatible route", () => {
|
|
it.effect("uses the Open Responses baseline for a configured deployment", () =>
|
|
Effect.gen(function* () {
|
|
expect(OpenAICompatibleResponses.route.body).toBe(OpenResponses.protocol.body)
|
|
expect(OpenAICompatibleResponses.route.transport).toBe(OpenResponses.httpTransport)
|
|
expect(OpenAICompatibleResponses.route.body).not.toBe(OpenAIResponses.protocol.body)
|
|
|
|
const model = configure({
|
|
apiKey: "test-key",
|
|
baseURL: "https://responses.example.test/v1",
|
|
provider: "example",
|
|
}).model("example-model")
|
|
const prepared = yield* LLMClient.prepare(
|
|
LLM.request({
|
|
model,
|
|
system: "You are concise.",
|
|
prompt: "Say hello.",
|
|
}),
|
|
)
|
|
|
|
expect(prepared.route).toBe("openai-compatible-responses")
|
|
expect(prepared.protocol).toBe("open-responses")
|
|
expect(prepared.model).toMatchObject({
|
|
id: "example-model",
|
|
provider: "example",
|
|
route: {
|
|
id: "openai-compatible-responses",
|
|
endpoint: {
|
|
baseURL: "https://responses.example.test/v1",
|
|
path: "/responses",
|
|
},
|
|
},
|
|
})
|
|
expect(prepared.body).toEqual({
|
|
model: "example-model",
|
|
input: [
|
|
{ role: "system", content: "You are concise." },
|
|
{ role: "user", content: [{ type: "input_text", text: "Say hello." }] },
|
|
],
|
|
stream: true,
|
|
})
|
|
}),
|
|
)
|
|
|
|
it.effect("rejects OpenAI-native tools", () =>
|
|
Effect.gen(function* () {
|
|
const model = configure({
|
|
apiKey: "test-key",
|
|
baseURL: "https://responses.example.test/v1",
|
|
}).model("example-model")
|
|
const error = yield* LLMClient.prepare(
|
|
LLM.request({ model, prompt: "Draw.", tools: [OpenAI.imageGeneration()] }),
|
|
).pipe(Effect.flip)
|
|
|
|
expect(error.reason._tag).toBe("InvalidRequest")
|
|
expect(error.message).toContain("Open Responses does not support provider-native tool image_generation")
|
|
}),
|
|
)
|
|
|
|
it.effect("omits OpenAI-only nullable phases from the Open Responses baseline", () =>
|
|
Effect.gen(function* () {
|
|
const model = configure({
|
|
apiKey: "test-key",
|
|
baseURL: "https://responses.example.test/v1",
|
|
}).model("example-model")
|
|
const prepared = yield* LLMClient.prepare(
|
|
LLM.request({
|
|
model,
|
|
messages: [
|
|
Message.assistant({
|
|
type: "text",
|
|
text: "Unclassified.",
|
|
providerMetadata: { openresponses: { phase: null } },
|
|
}),
|
|
],
|
|
}),
|
|
)
|
|
|
|
expect(prepared.body).toMatchObject({
|
|
input: [{ role: "assistant", content: [{ type: "output_text", text: "Unclassified." }] }],
|
|
})
|
|
}),
|
|
)
|
|
|
|
it.effect("reads standard options from the Open Responses namespace", () =>
|
|
Effect.gen(function* () {
|
|
const model = configure({
|
|
apiKey: "test-key",
|
|
baseURL: "https://responses.example.test/v1",
|
|
providerOptions: { openresponses: { reasoningEffort: "low", store: true } },
|
|
}).model("example-model")
|
|
const prepared = yield* LLMClient.prepare(LLM.request({ model, prompt: "Think." }))
|
|
|
|
expect(prepared.body).toMatchObject({
|
|
reasoning: { effort: "low" },
|
|
store: true,
|
|
})
|
|
}),
|
|
)
|
|
|
|
it.effect("does not interpret OpenAI hosted-tool items", () =>
|
|
Effect.gen(function* () {
|
|
const model = configure({
|
|
apiKey: "test-key",
|
|
baseURL: "https://responses.example.test/v1",
|
|
provider: "example",
|
|
}).model("example-model")
|
|
const response = yield* LLMClient.generate(LLM.request({ model, prompt: "Search." })).pipe(
|
|
Effect.provide(
|
|
fixedResponse(
|
|
sseEvents(
|
|
{
|
|
type: "response.output_item.done",
|
|
item: { type: "web_search_call", id: "ws_1", status: "completed", action: { query: "news" } },
|
|
},
|
|
{ type: "response.completed", response: { id: "resp_1" } },
|
|
),
|
|
),
|
|
),
|
|
)
|
|
|
|
expect(response.toolCalls).toEqual([])
|
|
expect(response.events.find(LLMEvent.is.finish)).toMatchObject({
|
|
providerMetadata: { openresponses: { responseId: "resp_1" } },
|
|
})
|
|
}),
|
|
)
|
|
})
|