fix(ai-client): clean up regressions from OpenAI SDK migration and canvas frame fix (#74)
Three follow-ups toef3b579(OpenAI SDK migration) andebe39ef(canvas frame): - .env.example / config.ts / AGENTS.md: anthropic & google native protocols were removed with the Vercel AI SDK, but .env.example and AGENTS.md still advertised them. Rewrite the docs to point Claude/Gemini at their OpenAI-compatible endpoints (api.anthropic.com/v1, generativelanguage.googleapis.com/v1beta/openai), drop the dead Gemini "Nano Banana" image example, sync AGENTS.md (text/vision protocol list, image protocol list, the "OpenAI/Gemini via AI SDK" reference note), and append a short hint in readProvider() error message guiding anthropic/google users to openai_compatible instead of a bare rejection. - chat.ts: drop the unsafe `as { prompt_tokens_details?: ... }` cast; read cached_tokens straight off the SDK's CompletionUsage type. Add a comment noting the OpenAI usage object reports cache reads only (no cache-write count), so the create cost the old AI SDK path logged is unrecoverable. - PlayCanvas.tsx: revert <img key={imageUrl}> to key={imageUrl.slice(-48)}. The gpt-image/mock paths emit multi-MB data URIs; using the full string as React's reconciliation key adds avoidable diff overhead during the frequent re-renders. Matches the existing <audio> element's key convention. Validation: pnpm typecheck passes. (pnpm lint fails on a pre-existing Next 16 `next lint` CLI issue, identical on staging — unrelated to this change.)
This commit is contained in:
@@ -7,6 +7,12 @@ export type ChatMessage = {
|
||||
content: string;
|
||||
};
|
||||
|
||||
// Cache observability for the prompt-prefix caching that the Writer stable
|
||||
// prefix relies on. The OpenAI usage object reports only cached READS
|
||||
// (prompt_tokens_details.cached_tokens) and has no field for cache WRITES
|
||||
// (tokens written to the cache on a cold pass), so unlike the old AI SDK
|
||||
// path we can show the hit rate but not the create cost. cached_tokens lives
|
||||
// directly on the SDK's CompletionUsage type — no cast needed.
|
||||
function summarizeSdkUsage(
|
||||
tag: string,
|
||||
usage: OpenAI.Completions.CompletionUsage | undefined,
|
||||
@@ -14,8 +20,7 @@ function summarizeSdkUsage(
|
||||
if (!usage) return `[cache] ${tag} no-usage`;
|
||||
const input = usage.prompt_tokens ?? 0;
|
||||
const output = usage.completion_tokens ?? 0;
|
||||
const details = (usage as { prompt_tokens_details?: { cached_tokens?: number } }).prompt_tokens_details;
|
||||
const cached = details?.cached_tokens;
|
||||
const cached = usage.prompt_tokens_details?.cached_tokens;
|
||||
if (typeof cached === "number") {
|
||||
const rate = input > 0 ? ((cached / input) * 100).toFixed(1) : "n/a";
|
||||
return `[cache] ${tag} hit=${cached} input=${input} rate=${rate}% completion=${output}`;
|
||||
|
||||
+7
-1
@@ -40,8 +40,14 @@ function readProvider(name: string): ProviderProtocol | undefined {
|
||||
if ((VALID_PROTOCOLS as readonly string[]).includes(v)) {
|
||||
return v as ProviderProtocol;
|
||||
}
|
||||
// anthropic/google were removed with the Vercel AI SDK — nudge users who
|
||||
// still set them toward the OpenAI-compatible endpoints (see .env.example).
|
||||
const hint =
|
||||
v === "anthropic" || v === "google"
|
||||
? ` — use openai_compatible with their OpenAI-compatible endpoint instead`
|
||||
: "";
|
||||
throw new Error(
|
||||
`Invalid ${name}: "${v}". Must be one of: ${VALID_PROTOCOLS.join(", ")}`,
|
||||
`Invalid ${name}: "${v}". Must be one of: ${VALID_PROTOCOLS.join(", ")}${hint}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user