mirror of
https://github.com/stablyai/orca.git
synced 2026-10-05 16:02:22 +00:00
refactor(claude): keep the model pre-flight's permissive case in the authority
claudeCatalogAdmitsModel now answers the question outright instead of handing back a nullable id set the caller had to interpret. The rule that an unidentified catalog refuses nothing lives inside the function, so a second caller cannot get it wrong by omission — and getting it wrong is silent, because restore swallows the rejection into restoreSkippedOptions. The refusal message names the model the user asked for, since it reaches them as the chat error row.
This commit is contained in:
@@ -5,8 +5,8 @@ import {
|
||||
isAgentSessionOptionRejectedError
|
||||
} from '../native-chat/agent-session-wire/structured-agent-session-option-error'
|
||||
import {
|
||||
claudeCatalogAdmitsModel,
|
||||
readClaudeCurrentModel,
|
||||
readClaudeListedModelIds,
|
||||
readClaudeModelEffortLevels,
|
||||
readClaudeSettingsEffort
|
||||
} from './claude-structured-session-options'
|
||||
@@ -71,11 +71,8 @@ export async function setClaudeStructuredOption(
|
||||
// fails every turn with zero tokens, so the acceptance proves nothing and only
|
||||
// the catalog does. Restore replays a pick the provider may since have retired,
|
||||
// which reaches here with no user error at all.
|
||||
if (input.key === 'model') {
|
||||
const listed = await readClaudeListedModelIds(session, timeoutMs)
|
||||
if (listed && !listed.has(input.value)) {
|
||||
throw new AgentSessionOptionRejectedError(`claude does not offer model ${input.value}`)
|
||||
}
|
||||
if (input.key === 'model' && !(await claudeCatalogAdmitsModel(session, input.value, timeoutMs))) {
|
||||
throw new AgentSessionOptionRejectedError(`claude does not list a model named ${input.value}`)
|
||||
}
|
||||
const modelWasConfirmed = readClaudeCurrentModel(session).confirmed
|
||||
const mutationSequence = ++session.optionMutationSequence
|
||||
|
||||
@@ -150,24 +150,25 @@ export async function readClaudeModelEffortLevels(
|
||||
}
|
||||
|
||||
/**
|
||||
* Every id the catalog answers to — alias and resolved id alike, so a pick stored
|
||||
* as either one matches. Null when nothing identified a model: an absent, failed
|
||||
* or empty `list_models` is not evidence against a model, or a live CLI that
|
||||
* predates it would have every model refused under it.
|
||||
* Whether the catalog admits the model, matched by alias or resolved id so a pick
|
||||
* stored as either one is found. The permissive case lives here rather than at the
|
||||
* call site: every caller must treat an unidentified catalog the same way, and one
|
||||
* that forgot would refuse every model on a CLI that cannot answer.
|
||||
*/
|
||||
export async function readClaudeListedModelIds(
|
||||
export async function claudeCatalogAdmitsModel(
|
||||
session: ClaudeSession,
|
||||
modelId: string,
|
||||
timeoutMs: number | undefined
|
||||
): Promise<ReadonlySet<string> | null> {
|
||||
): Promise<boolean> {
|
||||
const catalog = await session.connection.supportedModels({ timeoutMs }).catch(() => null)
|
||||
const models = listedModels(catalog ? { models: catalog } : null)
|
||||
return models.length > 0
|
||||
? new Set(
|
||||
models.flatMap((model) =>
|
||||
model.resolvedModel ? [model.id, model.resolvedModel] : [model.id]
|
||||
)
|
||||
)
|
||||
: null
|
||||
// An empty list identifies no model, so it is not evidence against one — a live
|
||||
// CLI predating `list_models` would otherwise have every model refused under it.
|
||||
// Do not turn this into a refusal.
|
||||
return (
|
||||
models.length === 0 ||
|
||||
models.some((model) => model.id === modelId || model.resolvedModel === modelId)
|
||||
)
|
||||
}
|
||||
|
||||
export async function readClaudeStructuredSessionOptions(
|
||||
|
||||
Reference in New Issue
Block a user