Skip to content

feat(core,runtime,desktop): declare thinking levels on Anthropic-protocol relays - #3686

Open
qian0817 wants to merge 1 commit into
apache:mainfrom
qian0817:feat/anthropic-relay-thinking-levels
Open

feat(core,runtime,desktop): declare thinking levels on Anthropic-protocol relays#3686
qian0817 wants to merge 1 commit into
apache:mainfrom
qian0817:feat/anthropic-relay-thinking-levels

Conversation

@qian0817

Copy link
Copy Markdown
Contributor

Summary

An anthropic-compatible relay fronts models the built-in metadata chain cannot resolve (no provider alias exists for it), so its models offered no thinking menu at all — while the per-model declaration mechanism that would fix it (relayModelProfiles.thinkingLevels, #2463) was gated OpenAI-relay-only.

This PR opens that gate for anthropic-compatible:

  • The gate is now per-field rather than per-provider: thinking-level declarations are legal on all three custom relays; serviceTier stays OpenAI Responses-only.
  • The declarable vocabulary is per provider: off joins it only for anthropic-compatible, whose protocol has a true disable wire (thinking: { type: 'disabled' }). The OpenAI relays keep their vocabulary unchanged (off there encodes reasoning_effort: 'none', which no generic relay is presumed to honor).
  • DECLARABLE_RELAY_THINKING_LEVELS becomes declarableRelayThinkingLevels(providerType); normalizeRelayModelProfiles gains an optional provider argument and stays provider-blind without one (the host-wire decode edge has no provider context — the canonical store codec has already validated fit).
  • The catalog codec asserts field-level provider fit, and the update path threads providerType through to table decode.
  • The runtime anthropic wire maps a declared off to thinking.disabled and other tiers to a passthrough effort.
  • The settings page's thinking menu, bulk control, and draft seed use the per-provider vocabulary — the same controls the OpenAI relays already expose.
  • The isRelayProviderType type predicate now covers all three relay providers.

Breaking change (forward compatibility, same posture as #3309's serviceTier): older builds reject a catalog document carrying these declarations. SCHEMA_VERSION stays 1, matching the repo convention that value-range growth is not a schema change. Worth a release-note line: clear Anthropic-relay thinking declarations before downgrading.

Verification

  • packages/core: clean build + full suite pass (22/22 in the touched suites; per-provider vocabulary, codec field-level fit incl. off accept/reject on both relay families, update-path positive case)
  • packages/runtime: clean rebuild + full suite pass — 2987 pass / 0 fail (incl. wire-shape tests: declared offthinking.disabled, effort passthrough, undeclared models emit nothing)
  • packages/runtime-host: 1119 pass / 0 fail
  • apps/desktop: npm test 1340 pass / 0 fail; typecheck clean (incl. draft-seed per-provider sanitation and the bulk-control vocabulary)
  • packages/storage: 903/904 — the single failure (managed-dependency-environment-crash) reproduces on a clean checkout without these changes (environment-sensitive SQLite crash harness), verified via stash comparison
  • biome check clean on all touched files
  • Verification runs performed in a shell with rg on PATH; test runs in the repo's parallel CI-style harness without it fail on spawn rg ENOENT for pre-existing environment reasons unrelated to this change

Review focus

The trilayer consistency — vocabulary (model-thinking.ts) / codec (connection-catalog-codec.ts) / wire (model-factory.ts) — is the invariant to check: off is declarable only where the provider has a true disable wire, and only that provider's runtime branch can emit it.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Sonnet 4.5 via pi — implementation, tests, and PR description, following a human-approved design (field-level gate, manual declarations only, off supported). Commits carry a Generated-by trailer.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

…ocol relays

An anthropic-compatible relay fronts models the metadata chain cannot
resolve (no provider alias exists for it), so its models offered no
thinking menu at all — while the declaration mechanism that would fix
it was gated OpenAI-relay-only since apache#2463.

The gate is now per-field rather than per-provider: thinking-level
declarations are legal on all three custom relays, serviceTier stays
OpenAI Responses-only. The declarable vocabulary is per provider:
`off` joins it only for anthropic-compatible, whose protocol has a
true disable wire (`thinking: { type: 'disabled' }`); the OpenAI
relays keep their vocabulary unchanged (`off` there encodes
`reasoning_effort: 'none'`, which no generic relay is presumed to
honor).

- `DECLARABLE_RELAY_THINKING_LEVELS` becomes
  `declarableRelayThinkingLevels(providerType)`; normalize gains an
  optional provider and stays provider-blind without one (the
  host-wire decode edge has no provider context — the canonical store
  codec has already validated fit).
- The catalog codec asserts field-level provider fit, and the update
  path threads providerType through to table decode.
- The runtime anthropic wire maps a declared `off` to
  `thinking.disabled` and other tiers to a passthrough `effort`.
- The settings page's thinking menu, bulk control, and draft seed use
  the per-provider vocabulary (the same controls the OpenAI relays
  already expose).

Older builds reject a document carrying these declarations (same
forward-compat posture as apache#3309's serviceTier); SCHEMA_VERSION stays
1, matching the repo convention that value-range growth is not a
schema change.

Generated-by: Claude Sonnet 4.5 via pi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant