Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions open-sse/config/providerModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export function getModelsByProviderId(providerId: string): RegistryModel[] {

const CLAUDE_MODEL_PATTERN = /(?:^|[\/._-])claude(?:[._-]|$)/;
const CLAUDE_MAX_EFFORT_UNSUPPORTED_FAMILY_PATTERNS = [/(?:^|[\/._-])haiku(?:[._-]|$)/] as const;
const ANTHROPIC_COMPATIBLE_PREFIX = "anthropic-compatible-";

export function supportsClaudeMaxEffort(modelId: string | null | undefined): boolean {
if (typeof modelId !== "string" || modelId.length === 0) return false;
Expand All @@ -73,19 +74,37 @@ export function supportsClaudeMaxEffort(modelId: string | null | undefined): boo
);
}

function resolveProviderModelList(aliasOrId: string): {
alias: string;
models: RegistryModel[] | null;
} {
const resolvedId = aliasOrId.startsWith(ANTHROPIC_COMPATIBLE_PREFIX) ? "claude" : aliasOrId;
const alias = PROVIDER_ID_TO_ALIAS[resolvedId] || resolvedId;
const models = PROVIDER_MODELS[alias] || PROVIDER_MODELS[resolvedId] || null;
return { alias, models };
}

export function supportsXHighEffort(aliasOrId: string, modelId: string): boolean {
const alias = PROVIDER_ID_TO_ALIAS[aliasOrId] || aliasOrId;
const providerModels = PROVIDER_MODELS[alias] || PROVIDER_MODELS[aliasOrId];
const { models: providerModels } = resolveProviderModelList(aliasOrId);
// Unknown provider (not in registry) — pass through unchanged.
if (!providerModels) return true;
const model = getProviderModel(alias, modelId);
const model = providerModels.find((entry) => entry.id === modelId);

// Keep explicit false entries as the unsupported-model list. Unlisted models
// and models without an explicit flag pass through unchanged.
return model?.supportsXHighEffort !== false;
}

export function supportsXHighEffortForMaxNormalization(
aliasOrId: string,
modelId: string
): boolean {
const { alias, models: providerModels } = resolveProviderModelList(aliasOrId);
if (!providerModels) return true;
const model = providerModels.find((entry) => entry.id === modelId);

// Claude Code models default to supporting extra-high effort. Keep explicit
// false entries as the unsupported-model list so newly added Claude models do
// not need opt-in flags.
if (alias === "cc") {
return model?.supportsXHighEffort !== false;
}

return model?.supportsXHighEffort === true;
}
Comment on lines +93 to 110

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

For dynamic or custom Anthropic-compatible providers (whose IDs start with anthropic-compatible-), PROVIDER_ID_TO_ALIAS will not contain a static mapping, causing supportsXHighEffort and supportsXHighEffortForMaxNormalization to treat them as unknown providers. This defaults them to true (pass-through) for all models, bypassing safety checks and allowing unsupported effort levels (like xhigh or max) on models that explicitly opt out (e.g., claude-haiku-4-5-20251001).

Resolving any anthropic-compatible- prefix to "cc" (the alias for "claude") ensures we correctly look up the model's capabilities in the Claude registry.

  // Keep explicit false entries as the unsupported-model list. Unlisted models
  // and models without an explicit flag pass through unchanged.
  const resolvedId = aliasOrId.startsWith("anthropic-compatible-") ? "cc" : aliasOrId;
  const resolvedAlias = PROVIDER_ID_TO_ALIAS[resolvedId] || resolvedId;
  const resolvedModel = getProviderModel(resolvedAlias, modelId);
  return resolvedModel?.supportsXHighEffort !== false;
}

export function supportsXHighEffortForMaxNormalization(
  aliasOrId: string,
  modelId: string
): boolean {
  const resolvedId = aliasOrId.startsWith("anthropic-compatible-") ? "cc" : aliasOrId;
  const alias = PROVIDER_ID_TO_ALIAS[resolvedId] || resolvedId;
  if (alias === "cc") {
    return supportsXHighEffort(aliasOrId, modelId);
  }
  const providerModels = PROVIDER_MODELS[alias] || PROVIDER_MODELS[resolvedId];
  if (!providerModels) return true;
  const model = getProviderModel(alias, modelId);
  return model?.supportsXHighEffort === true;
}

28 changes: 17 additions & 11 deletions open-sse/executors/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.ts";
import { applyFingerprint, isCliCompatEnabled } from "../config/cliFingerprints.ts";
import { supportsClaudeMaxEffort, supportsXHighEffort } from "../config/providerModels.ts";
import {
supportsClaudeMaxEffort,
supportsXHighEffort,
supportsXHighEffortForMaxNormalization,
} from "../config/providerModels.ts";
import type { PoolConfig } from "../services/sessionPool/types.ts";
import type { Session } from "../services/sessionPool/session.ts";
import { SessionPool } from "../services/sessionPool/sessionPool.ts";
Expand All @@ -25,7 +29,10 @@ import {
modelSupportsContext1mBeta,
} from "../services/claudeCodeCompatible.ts";
import { getClaudeCodeCompatibleRequestDefaults } from "@/lib/providers/requestDefaults";
import { cloakThirdPartyToolNames, remapToolNamesInRequest } from "../services/claudeCodeToolRemapper.ts";
import {
cloakThirdPartyToolNames,
remapToolNamesInRequest,
} from "../services/claudeCodeToolRemapper.ts";
import { obfuscateInBody } from "../services/claudeCodeObfuscation.ts";
import { sanitizeClaudeToolSchemas } from "../translator/helpers/schemaCoercion.ts";
import { sanitizeResponsesInputItems } from "../services/responsesInputSanitizer.ts";
Expand Down Expand Up @@ -223,12 +230,11 @@ function hasActiveClaudeThinking(body: Record<string, unknown>): boolean {
* Each rejection burns a combo fallback attempt before reaching a working
* provider. Apply provider-aware sanitation here (after transformRequest, so
* reintroductions by per-provider transforms are also caught) before fetch.
* xhigh support is registry-gated: models that genuinely support xhigh pass
* through unchanged, and Claude models default to xhigh support unless marked
* as legacy unsupported entries. max support is Claude/CC-compatible only and
* xhigh support is opt-out: pass through unchanged unless the registry marks
* a model as unsupported. max support is Claude/CC-compatible only and
* intentionally separate: older Opus/Sonnet models may support max even when
* they do not support xhigh. For OpenAI-shape providers, normalize max to
* xhigh when that top tier is allowed; otherwise downgrade to high.
* they do not support xhigh. For OpenAI-shape providers, keep the existing
* max normalization behavior.
*/
const MISTRAL_NO_REASONING_EFFORT_PATTERN = /devstral/i;
const GITHUB_NO_REASONING_EFFORT_PATTERN = /(claude|haiku|oswe)/i;
Expand Down Expand Up @@ -260,10 +266,10 @@ export function sanitizeReasoningEffortForProvider(

const supportsXHigh = supportsXHighEffort(provider, modelStr);
const shouldDowngradeXHigh = effortStr === "xhigh" && !supportsXHigh;
const shouldNormalizeMaxToXHigh =
effortStr === "max" && !supportsMaxEffortForProvider(provider, modelStr) && supportsXHigh;
const shouldDowngradeMax =
effortStr === "max" && !supportsMaxEffortForProvider(provider, modelStr) && !supportsXHigh;
const supportsXHighForMax = supportsXHighEffortForMaxNormalization(provider, modelStr);
const supportsMax = supportsMaxEffortForProvider(provider, modelStr);
const shouldNormalizeMaxToXHigh = effortStr === "max" && !supportsMax && supportsXHighForMax;
const shouldDowngradeMax = effortStr === "max" && !supportsMax && !supportsXHighForMax;

if (shouldNormalizeMaxToXHigh) {
log?.info?.(
Expand Down
Loading