feat: add native Anthropic provider#36
Open
weeta-code wants to merge 1 commit into
Open
Conversation
Adds Anthropic as a first-class provider alongside OpenRouter, OpenAI,
and Z.ai, letting users bring an sk-ant-... key directly to the native
Messages API without requiring an OpenRouter account or a local proxy.
Implementation:
- New 'anthropic' entry in AI_PROVIDER_PRESETS — sidebar UI picks it up
automatically (no UI code changes needed)
- getProviderHeaders branches to emit x-api-key + anthropic-version +
anthropic-dangerous-direct-browser-access for the Anthropic path
- ai-enrich.ts and ai-ghost.ts branch on provider === 'anthropic' at
the HTTP-call boundary: POST to {baseUrl}/messages, system prompt
moved to top-level 'system' field, structured output via forced
tool_use (tool_choice: {type:'tool', name:'...', disable_parallel_tool_use})
- Response parsing uses content[].type === 'tool_use' and reads .input
directly, giving schema fidelity equivalent to OpenAI's json_schema
strict mode
- CSP connect-src in next.config.mjs gains https://api.anthropic.com
- README gets an 'Anthropic (direct)' subsection mirroring existing
providers with the Opus 4.5/4.6/4.7, Sonnet 4.5/4.6, Haiku 4.5 lineup
Design note on thinking: no 'thinking' field is sent on any Claude
model, including 4.7. Forced tool_choice is incompatible with
extended/adaptive thinking per Anthropic's docs, and thinking defaults
to off on all exposed models, so omitting the field is correct,
forward-compatible, and gives deterministic low-latency enrichment.
Web grounding is intentionally not wired up (supportsGrounding: false
on all Claude models cleanly hides the UI toggle — same pattern as
the Z.ai provider).
Tested live against claude-sonnet-4-6 and claude-opus-4-7:
enrichment and ghost synthesis both produce valid, schema-conformant
output. Existing OpenRouter, OpenAI, and Z.ai code paths unchanged
(branches occur before divergent logic).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Anthropic as a first-class provider alongside OpenRouter, OpenAI, and Z.ai. Users can bring an
sk-ant-...key directly and talk to the Messages API from the browser — no OpenRouter account, no local proxy.How it works
"anthropic"entry inAI_PROVIDER_PRESETS; the sidebar settings UI picks it up automatically (no UI code changes needed — the existing map overAI_PROVIDER_PRESETShandles it, and the grounding toggle is already scoped to openrouter/openai only).getProviderHeadersbranches to emitx-api-key+anthropic-version: 2023-06-01+anthropic-dangerous-direct-browser-access: trueinstead ofAuthorization: Bearer.ai-enrich.tsandai-ghost.tsbranch onprovider === "anthropic"at the HTTP-call boundary:{baseUrl}/messagesinstead of/chat/completionssystemfield (Anthropic's Messages API shape)tool_use— a synthetic tool (enrichment_result/emergent_thesis) is defined withinput_schemaand forced viatool_choice: {type: "tool", name: "...", disable_parallel_tool_use: true}. Schema fidelity is equivalent to OpenAI's strictjson_schemamode; no regex fallback needed.content[].type === "tool_use"and uses.inputdirectly.connect-srcinnext.config.mjsgainshttps://api.anthropic.com(without this, Firefox silently showsNetworkError when attempting to fetch resource; Chrome says "Refused to connect... violates CSP").Design notes
Why no
thinkingfield is sent, even on 4.7: forcedtool_choice: {type: "tool"}is explicitly incompatible with extended/adaptive thinking — the API rejects the combination with a 400. Thinking defaults to off on all exposed models (Opus 4.5/4.6/4.7, Sonnet 4.5/4.6, Haiku 4.5), so omitting the field is correct, forward-compatible, and gives deterministic low-latency enrichment that matches the schema every time. Task-fit also supports this: classification, connection inference, and short annotations don't meaningfully benefit from deep reasoning, and enrichment is the user-facing latency path.Ghost synthesis could plausibly benefit from adaptive thinking, but that would require dropping forced tool_choice to
autoand handling two response shapes (tool_use + text fallback). That's a cleaner follow-up PR than bundling with provider addition.Web grounding is intentionally not wired up on the Anthropic path. Anthropic has a server-side web-search tool but it's a separate feature;
supportsGrounding: falseon all Claude models cleanly hides the grounding UI (same pattern as the Z.ai provider).Files changed
lib/ai-settings.ts"anthropic"to provider union, preset,ANTHROPIC_MODELS, header branchlib/ai-enrich.tslib/ai-ghost.tsnext.config.mjsconnect-srcallowlistREADME.mdTesting
npx tsc --noEmit— cleannpm run build— passes (2.3s compile, all pages generated)claude-sonnet-4-6andclaude-opus-4-7: enrichment produces valid classifications, annotations, andinfluencedByIndices; ghost synthesis fires correctly after accumulating notes across categories