Files
next-ai-draw-io/tests/unit/ai-providers-cache.test.ts
T
dayuan.jiang ac62a58c9f fix(providers): update the v6 SDK packages and fix Claude and Gemini settings
- Update ai to 6.0.300 and the @ai-sdk providers to their latest v6-line
  versions. @ai-sdk/anthropic 3.0.47 did not know claude-opus-4-7/4-8
  and capped their output at 32000 tokens; 3.0.127 allows 128000
- Drop the fine-grained-tool-streaming beta header for the Anthropic API:
  the provider now streams tool input per tool (eager_input_streaming)
- Claude 4.7 and later reject a non-default temperature/top_p/top_k and
  the extended thinking budget with a 400. A middleware retries once
  without them, so TEMPERATURE and *_THINKING_BUDGET_TOKENS no longer
  break those models
- Prompt caching also reaches Claude on the Anthropic API and OpenRouter;
  before, only Bedrock got a cache marker
- GOOGLE_TOP_K and GOOGLE_TOP_P never reached Gemini: they were sent as
  Google provider options, which drops them. They are call settings now.
  GOOGLE_CANDIDATE_COUNT and GOOGLE_REASONING_EFFORT, which the provider
  does not support, are removed
- Add @ai-sdk/openai-compatible as a direct dependency
2026-10-04 13:07:56 +09:00

86 lines
2.6 KiB
TypeScript

// @vitest-environment node
import { generateText, type ModelMessage } from "ai"
import { afterEach, describe, expect, it, vi } from "vitest"
import { CACHE_POINT, getAIModel } from "@/lib/ai-providers"
const ENV = ["ANTHROPIC_API_KEY", "OPENROUTER_API_KEY"]
const saved = Object.fromEntries(ENV.map((k) => [k, process.env[k]]))
afterEach(() => {
for (const k of ENV) {
if (saved[k] === undefined) delete process.env[k]
else process.env[k] = saved[k]
}
vi.unstubAllGlobals()
})
// The chat route marks its system messages like this
const messages: ModelMessage[] = [
{ role: "system", content: "Instructions", providerOptions: CACHE_POINT },
{ role: "user", content: "hi" },
]
/** Send the messages through a real provider and return the request body */
async function requestBody(
provider: "anthropic" | "openrouter",
reply: unknown,
) {
let body: any
vi.stubGlobal(
"fetch",
vi.fn(async (_url: string, init: RequestInit) => {
body = JSON.parse(init.body as string)
return new Response(JSON.stringify(reply), {
headers: { "content-type": "application/json" },
})
}),
)
const { model } = getAIModel({
provider,
modelId:
provider === "anthropic"
? "claude-sonnet-4-5"
: "anthropic/claude-sonnet-4.5",
})
await generateText({ model, messages, maxRetries: 0 }).catch(() => {})
return body
}
describe("prompt cache breakpoints", () => {
it("reach the Anthropic API", async () => {
process.env.ANTHROPIC_API_KEY = "test-key"
const body = await requestBody("anthropic", {
id: "msg_1",
type: "message",
role: "assistant",
content: [{ type: "text", text: "ok" }],
stop_reason: "end_turn",
usage: { input_tokens: 1, output_tokens: 1 },
})
expect(body.system).toEqual([
{
type: "text",
text: "Instructions",
cache_control: { type: "ephemeral" },
},
])
})
it("reach OpenRouter", async () => {
process.env.OPENROUTER_API_KEY = "test-key"
const body = await requestBody("openrouter", {
id: "gen-1",
choices: [
{
index: 0,
message: { role: "assistant", content: "ok" },
finish_reason: "stop",
},
],
})
expect(JSON.stringify(body.messages[0])).toContain(
'"cache_control":{"type":"ephemeral"}',
)
})
})