test(opencode): cover native Gemini parity
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
AmazonBedrock,
|
||||
Anthropic,
|
||||
GitHubCopilot,
|
||||
Google,
|
||||
@@ -23,6 +24,7 @@ type Input = {
|
||||
}
|
||||
|
||||
const PROVIDERS: Record<string, ProviderDefinition> = {
|
||||
"@ai-sdk/amazon-bedrock": AmazonBedrock.provider,
|
||||
"@ai-sdk/anthropic": Anthropic.provider,
|
||||
"@ai-sdk/baseten": OpenAICompatibleFamily.provider,
|
||||
"@ai-sdk/cerebras": OpenAICompatibleFamily.provider,
|
||||
@@ -102,8 +104,11 @@ const capabilities = (input: Input, selected: Protocol) =>
|
||||
streamingInput: selected !== "gemini" && input.model.capabilities.toolcall,
|
||||
},
|
||||
cache: {
|
||||
// Both Anthropic Messages and Bedrock Converse honour positional cache
|
||||
// markers — Anthropic via `cache_control` on content blocks, Bedrock via
|
||||
// its `cachePoint` marker block (added to BedrockConverse in 9d7d518ac).
|
||||
prompt: ["anthropic-messages", "bedrock-converse"].includes(selected),
|
||||
contentBlocks: selected === "anthropic-messages",
|
||||
contentBlocks: ["anthropic-messages", "bedrock-converse"].includes(selected),
|
||||
},
|
||||
reasoning: {
|
||||
efforts: reasoningEfforts(input),
|
||||
|
||||
@@ -163,7 +163,7 @@ export const toolDefinition = (input: { readonly model: Provider.Model; readonly
|
||||
LLM.tool({
|
||||
name: input.tool.id,
|
||||
description: input.tool.description,
|
||||
inputSchema: Object.fromEntries(Object.entries(EffectZod.toJsonSchema(input.tool.parameters))),
|
||||
inputSchema: EffectZod.toJsonSchema(input.tool.parameters),
|
||||
native: {
|
||||
opencodeToolID: input.tool.id,
|
||||
},
|
||||
|
||||
@@ -148,11 +148,29 @@ describe("ProviderLLMBridge", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("maps Amazon Bedrock to Converse with bearer auth and content-block cache", () => {
|
||||
const ref = ProviderLLMBridge.toModelRef({
|
||||
provider: provider({ id: ProviderID.make("amazon-bedrock"), key: "bedrock-bearer-key" }),
|
||||
model: model({
|
||||
id: "anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
providerID: "amazon-bedrock",
|
||||
npm: "@ai-sdk/amazon-bedrock",
|
||||
}),
|
||||
})
|
||||
|
||||
expect(ref).toMatchObject({
|
||||
protocol: "bedrock-converse",
|
||||
headers: { authorization: "Bearer bedrock-bearer-key" },
|
||||
})
|
||||
// Bedrock Converse supports both prompt-level and positional content-block
|
||||
// cache markers (cachePoint blocks landed in 9d7d518ac).
|
||||
expect(ref?.capabilities.cache).toMatchObject({ prompt: true, contentBlocks: true })
|
||||
})
|
||||
|
||||
test("leaves undecided provider packages unmapped", () => {
|
||||
const unsupported = [
|
||||
["mistral", "mistral-large", "@ai-sdk/mistral"],
|
||||
["azure", "gpt-4.1", "@ai-sdk/azure"],
|
||||
["amazon-bedrock", "anthropic.claude-3-5-sonnet-20240620-v1:0", "@ai-sdk/amazon-bedrock"],
|
||||
] as const
|
||||
|
||||
expect(
|
||||
@@ -162,6 +180,6 @@ describe("ProviderLLMBridge", () => {
|
||||
model: model({ id: modelID, providerID, npm }),
|
||||
}),
|
||||
),
|
||||
).toEqual([undefined, undefined, undefined])
|
||||
).toEqual([undefined, undefined])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import { AnthropicMessages, OpenAICompatibleChat } from "@opencode-ai/llm"
|
||||
import { AnthropicMessages, Gemini, OpenAICompatibleChat } from "@opencode-ai/llm"
|
||||
import { client } from "@opencode-ai/llm/adapter"
|
||||
import { OpenAIResponses } from "@opencode-ai/llm/provider/openai-responses"
|
||||
import { Cause, Effect, Exit, Layer, Schema } from "effect"
|
||||
@@ -506,4 +506,75 @@ describe("LLMNative.request", () => {
|
||||
temperature: 0,
|
||||
})
|
||||
}))
|
||||
|
||||
it.effect("prepares Gemini text and tool request body", () => Effect.gen(function* () {
|
||||
const mdl = model({
|
||||
id: ModelID.make("gemini-2.5-flash"),
|
||||
providerID: ProviderID.make("google"),
|
||||
api: { id: "gemini-2.5-flash", url: "https://generativelanguage.googleapis.com/v1beta", npm: "@ai-sdk/google" },
|
||||
})
|
||||
const userID = MessageID.ascending()
|
||||
const assistantID = MessageID.ascending()
|
||||
const request = yield* LLMNative.request({
|
||||
provider: ProviderTest.info({ id: ProviderID.make("google"), key: "google-key" }, mdl),
|
||||
model: mdl,
|
||||
system: ["You are concise."],
|
||||
generation: { maxTokens: 32, temperature: 0 },
|
||||
messages: [
|
||||
userMessage(mdl, userID, [textPart(userID, "What is the weather?")]),
|
||||
assistantMessage(mdl, assistantID, userID, [
|
||||
toolPart(assistantID, {
|
||||
callID: "call_1",
|
||||
tool: "lookup",
|
||||
state: {
|
||||
status: "completed",
|
||||
input: { query: "weather" },
|
||||
output: '{"forecast":"sunny"}',
|
||||
title: "Weather",
|
||||
metadata: {},
|
||||
time: { start: 1, end: 2 },
|
||||
},
|
||||
}),
|
||||
]),
|
||||
],
|
||||
tools: [lookupTool],
|
||||
toolChoice: "lookup",
|
||||
})
|
||||
const prepared = yield* client({ adapters: [Gemini.adapter] }).prepare(request)
|
||||
|
||||
expect(request.model).toMatchObject({
|
||||
provider: "google",
|
||||
protocol: "gemini",
|
||||
baseURL: "https://generativelanguage.googleapis.com/v1beta",
|
||||
headers: { "x-goog-api-key": "google-key" },
|
||||
})
|
||||
expect(prepared.target).toMatchObject({
|
||||
systemInstruction: { parts: [{ text: "You are concise." }] },
|
||||
contents: [
|
||||
{ role: "user", parts: [{ text: "What is the weather?" }] },
|
||||
{ role: "model", parts: [{ functionCall: { name: "lookup", args: { query: "weather" } } }] },
|
||||
{
|
||||
role: "user",
|
||||
parts: [{ functionResponse: { name: "lookup", response: { name: "lookup", content: '{"forecast":"sunny"}' } } }],
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
{
|
||||
functionDeclarations: [
|
||||
{
|
||||
name: "lookup",
|
||||
description: "Lookup project data",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string", description: "Search query" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
toolConfig: { functionCallingConfig: { mode: "ANY", allowedFunctionNames: ["lookup"] } },
|
||||
generationConfig: { maxOutputTokens: 32, temperature: 0 },
|
||||
})
|
||||
}))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user