From b796cf4f182d0c2000be8df2440ae7ef86713ddc Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 29 May 2026 04:07:59 +0000 Subject: [PATCH] Address Greptile Claude model feedback --- packages/adapters/claude-local/src/server/models.ts | 9 +++++++-- server/src/__tests__/adapter-models.test.ts | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/adapters/claude-local/src/server/models.ts b/packages/adapters/claude-local/src/server/models.ts index 4c1a8bc8d87..cbb8553d46f 100644 --- a/packages/adapters/claude-local/src/server/models.ts +++ b/packages/adapters/claude-local/src/server/models.ts @@ -1,3 +1,4 @@ +import { createHash } from "node:crypto"; import type { AdapterModel } from "@paperclipai/adapter-utils"; import { models as DIRECT_MODELS } from "../index.js"; @@ -26,7 +27,8 @@ function isBedrockEnv(): boolean { } function fingerprint(apiKey: string): string { - return `${apiKey.length}:${apiKey.slice(-6)}`; + const digest = createHash("sha256").update(apiKey).digest("base64url").slice(0, 16); + return `${apiKey.length}:${digest}`; } function dedupeModels(models: AdapterModel[]): AdapterModel[] { @@ -88,7 +90,10 @@ async function fetchAnthropicModels(apiKey: string, baseUrl: string): Promise { expect(refreshed.some((model) => model.id === "claude-opus-4-8-20260529")).toBe(true); }); + it("falls back to static claude models when Anthropic model discovery fails", async () => { + process.env.ANTHROPIC_API_KEY = "sk-ant-test"; + vi.spyOn(globalThis, "fetch").mockResolvedValue({ + ok: false, + status: 401, + json: async () => ({}), + } as Response); + + const models = await listAdapterModels("claude_local"); + expect(models).toEqual(claudeFallbackModels); + }); + it("loads codex models dynamically and merges fallback options", async () => { process.env.OPENAI_API_KEY = "sk-test"; const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue({