feat(core,runtime-host,desktop): declare per-model relay profiles for OpenAI-compatible connections - #2463
Merged
Merged
Conversation
A generic openai-compatible relay fronts models the built-in metadata chain
has never heard of: no thinking-level menu, no vision decision, a guessed
context window. This adds first-class, per-model user declarations for the
three facts only the user can decide.
Data model: `Connection.relayModelProfiles: Record<modelId,
{ thinkingLevels?; vision?; contextWindow? }>` on the single connection type
chain. Two invariants are enforced at every store boundary — profiles exist
only on openai-compatible connections (the metadata chain is the truth
elsewhere), and only for ids in `enabledModelIds` (both stores prune the
table when a model is disabled, so no declaration can outlive its row).
Every read enters through one gated seam, `relayModelProfile(connection,
modelId)`; writes are sanitized by `normalizeRelayModelProfiles`, and the
catalog codec validates strictly (closed level vocabulary, subset of the
selection, positive-integer context window) — a malformed legacy document is
noise, a malformed catalog document is a loud failure.
Updates are whole-table tri-state: an absent key leaves the stored table
untouched (capability-blind writers cannot clobber), `null` clears it, a
table replaces it. A baseUrl change in the same update retires an untouched
table unless the update submits one of its own — declarations belong to the
endpoint they were declared against. The embedded store mirrors the same
merge rules, and config import round-trips the typed field instead of an
extras bag.
Host wire: profiles travel on their own `enabled_model_id` page item
(`relayProfile`), never in one header table — a header item is atomic to the
paginator's 48 KiB page budget, so a long declaration list there would have
made the whole connection unreadable. The client reader reassembles the
per-model map; compatibility epoch bumps to 6. Session-model gating and
execution authority consume the same typed table, so a level that passes the
gate is exactly what the wire can send; the connection slug is the
provider-options namespace, and a fake-fetch test pins the translated
`reasoning_effort` request body.
Settings UI: each enabled model row edits a local draft (precise
multi-select of thinking levels — generic relays never declare the 'off'
wire —, Auto/Enabled/Disabled vision tri-state, positive-integer context
window) committed by one explicit 保存能力声明; there is no per-keystroke
persistence machinery. The add-provider form declares the connection's
first (default) model at create time through the same typed field.
…tion Create previously passed only defaultModel into relayProfilesForStorage, so import/create payloads that seed enabledModelIds together with per-model profiles dropped every non-default declaration. Use the same enabled set the connection will store, and cover the multi-model create path.
…OAuth claude-subscription discovery is curated/fallback-only after #2336, so beginModelFetch returns provider_action_unavailable. Seed the OAuth backend abort test from the curated inventory create already writes, and target a model id that inventory contains.
jackwener
force-pushed
the
feat/2391-relay-model-profiles
branch
from
August 7, 2026 17:48
a86b99b to
86cbd27
Compare
6 tasks
6 tasks
likun666661
pushed a commit
that referenced
this pull request
Aug 20, 2026
…3321) * fix(runtime): emit camelCase OpenAI-compatible provider option keys The AI SDK resolves openai-compatible providerOptions under the camelCase alias of the provider name and flags raw dashed keys as deprecated on every doGenerate. Derive the key via toCamelCase for all openai-compatible identities (extends #2463, which only covered custom relay slugs) and fix the three direct dashed emissions. The open-responses path is unchanged: that SDK resolves only the raw name. Closes #1430 Generated-by: Claude Code (Fable 5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(runtime): pin the metadata namespace and the passthrough lane Address review: note in the providerOptions-key docstring that the camelCase alias also selects the SDK's response metadata namespace, assert the capture test's providerMetadata comes back under the camelCase key, and add a wire-level capture for volcengine-ark's passthrough thinking object - the lane the existing reasoningEffort captures do not exercise. Generated-by: Claude Code (Fable 5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
5 tasks
M4n5ter
pushed a commit
to qian0817/maka
that referenced
this pull request
Aug 26, 2026
…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
M4n5ter
pushed a commit
to qian0817/maka
that referenced
this pull request
Aug 26, 2026
…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
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
Rebased and fixed landing of #2391 onto current
main.OpenAI-compatible relays can front models Maka's built-in metadata does not know about. This adds first-class, per-model
relayModelProfilesfor:thinkingLevels)Profiles live on the connection (not inside fetched
models[]), so catalog refresh does not wipe user declarations. Storage enforces: openai-compatible only, and profile keys ⊆enabledModelIds. The same table is carried through Desktop, embedded storage, Runtime Host, Runtime, CLI, and Headless.Review changes vs upstream #2391
main(resolved conflict inprovider-add-form.tsx— kept bothcreatedDefaultModeland opencode-free defaultenabledModelIds).FileConnectionStore.createpreviously pruned profiles against onlydefaultModel, so import/create payloads that seed multi-modelenabledModelIds+ profiles dropped every non-default declaration. Now uses the full create-time selection; regression test added.Original PR
Closes #2391 (supersedes the conflicting fork branch). Also supersedes #2351 (thinking-only design that stored declarations on
models[]).Refs #2219.
Validation (this rebased branch)
@maka/storageconnection-store: 41/41@maka/coremodel-thinking / chat-model-choice / model-metadata: 41/41@maka/runtimemodel-factory-thinking: 39/39 (includes real HTTP body assert forreasoning_effort)@maka/storageruntime-policy-stores: 39/39@maka/runtime-hostcatalog-reader + session-catalog-coordinator (+ related): 34/34Credits
Primary design and implementation: @sanshanya in #2391.