Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **feat(opencode):** send Muse Spark `max` reasoning effort verbatim first (instead of pre-emptively clamping to `xhigh`), retrying once with `xhigh` only when upstream rejects it with the specific unsupported-effort 400 — covers both the fast path and the account-rotation loop ([#12687](https://github.com/diegosouzapw/OmniRoute/pull/12687)) — thanks @Chewji9875
14 changes: 14 additions & 0 deletions open-sse/config/providerModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,20 @@ export function getModelTargetFormat(aliasOrId: string, modelId: string): string
// and OpenAI-shaped Mistral/Open-MaaS requests. Resource names retain enough publisher data to
// route future dynamically-synced models without adding another pinned prefix here.
if (alias === "vertex" || alias === "vp") return getVertexModelTargetFormat(bareModelId);
// #10867: Muse Spark is served by OpenCode / Zen / Go / Freebuff only on the
// OpenAI Responses API (/responses), not /chat/completions.
if (
(alias === "opencode" ||
alias === "opencode-go" ||
alias === "opencode-zen" ||
alias === "oc" ||
alias === "ocg" ||
alias === "ocz" ||
alias === "freebuff") &&
/(?:^|\/)muse-spark/i.test(bareModelId)
) {
return "openai-responses";
}
// Model-level targetFormat is provider-scoped: a catalog entry declares how THIS
// provider's endpoint serves the model — do NOT import another provider's tag.
// #9994 scoped this for providers WITH a catalog; #10072 extends it to catalogless
Expand Down
18 changes: 18 additions & 0 deletions open-sse/config/providers/registry/command-code/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,5 +184,23 @@ export const command_codeProvider: RegistryEntry = {
contextLength: 1000000,
maxOutputTokens: 32768,
},
{
id: "meta/muse-spark-1.3-contributor",
name: "Muse Spark 1.3 Contributor (CC)",
supportsReasoning: true,
supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS,
supportsVision: true,
contextLength: 1048576,
maxOutputTokens: 131072,
},
{
id: "meta/muse-spark-1.2-contributor",
name: "Muse Spark 1.2 Contributor (CC)",
supportsReasoning: true,
supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS,
supportsVision: true,
contextLength: 1048576,
maxOutputTokens: 131072,
},
],
};
6 changes: 6 additions & 0 deletions open-sse/config/providers/registry/freebuff/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ export const freebuffProvider: RegistryEntry = {
supportsReasoning: true,
contextLength: 131_072,
},
{
id: "meta/muse-spark-1.3-contributor",
name: "Meta Muse Spark 1.3 Contributor",
supportsReasoning: true,
contextLength: 131_072,
},
{
id: "meta/muse-spark-1.2-contributor",
name: "Meta Muse Spark 1.2 Contributor",
Expand Down
15 changes: 14 additions & 1 deletion open-sse/config/providers/registry/opencode/go/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,9 @@ export const opencode_goProvider: RegistryEntry = {
},
// #12674: Muse Spark 1.3 Contributor — base + effort-tier aliases from the
// OpenCode Go registry (`opencode models opencode-go --refresh --verbose`;
// exact suffix set: minimal/low/medium/high/xhigh, no max — same as 1.2).
// exact suffix set: minimal/low/medium/high/xhigh — same as 1.2). #12687 adds
// a `-max` variant: the executor sends `max` verbatim and retries once with
// `xhigh` only if upstream rejects it (dispatchWithMuseSparkMaxFallback).
// Upstream serves Muse Spark only on the Responses API; without
// targetFormat:"openai-responses" these fall through to /chat/completions
// and the upstream returns 500 (same class as #12196).
Expand Down Expand Up @@ -317,6 +319,17 @@ export const opencode_goProvider: RegistryEntry = {
supportsVideo: true,
targetFormat: "openai-responses",
},
{
id: "muse-spark-1.3-contributor-max",
name: "Muse Spark 1.3 Contributor (max effort)",
contextLength: 1048576,
maxOutputTokens: 131072,
supportsReasoning: true,
supportsVision: true,
supportsAudio: true,
supportsVideo: true,
targetFormat: "openai-responses",
},
// #8353: Grok 4.5 + effort tiers from the OpenCode Go registry.
{
id: "grok-4.5",
Expand Down
57 changes: 32 additions & 25 deletions open-sse/executors/base/reasoningEffort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const MAX_TIER_REASONING_MODEL_PATTERN =

export const O1_O3_REASONING_MODELS_PATTERN = /(?:^|\/|\b)(?:o1-mini|o1|o3-mini|o3-pro|o3)(?:$|-)/i;
export const O1_PREVIEW_PATTERN = /(?:^|\/|\b)o1-preview(?:$|-)/i;
export const MUSE_SPARK_12_PATTERN = /(?:^|\/|\b)muse-spark-1\.2(?:$|-|\b)/i;
export const MUSE_SPARK_PATTERN = /(?:^|\/|\b)muse-spark/i;
export const MINIMAX_REASONING_PATTERN = /(?:^|\/|\b)minimax(?:-m3|-m2)/i;
export const GROK_45_PATTERN = /(?:^|\/|\b)grok-4\.5/i;
Expand All @@ -70,19 +71,11 @@ export const GLM_53_FAMILY_PATTERN = /(?:^|\/|\b)glm-5\.3(?:$|-)/i;
export const GLM_52_FAMILY_PATTERN = /(?:^|\/|\b)glm-5\.2(?:$|-)/i;

export function isCommandCodeProvider(provider: string): boolean {
return (
provider === "command-code" ||
provider === "cmd" ||
provider === "command_code"
);
return provider === "command-code" || provider === "cmd" || provider === "command_code";
}

export function isOllamaCloudProvider(provider: string): boolean {
return (
provider === "ollama-cloud" ||
provider === "ollamacloud" ||
provider === "ollama_cloud"
);
return provider === "ollama-cloud" || provider === "ollamacloud" || provider === "ollama_cloud";
}

export function isOpencodeGoProvider(provider: string): boolean {
Expand Down Expand Up @@ -206,12 +199,7 @@ export function supportsMaxEffortForProvider(provider: string, model: string): b
MAX_TIER_REASONING_MODEL_PATTERN.test(resolvedModelId) ||
MAX_TIER_REASONING_MODEL_PATTERN.test(model);
return (
isClaude ||
isOpencodeGo ||
isOllamaCloud ||
isMoonshotK3 ||
isCommandCode ||
isMaxTierModel
isClaude || isOpencodeGo || isOllamaCloud || isMoonshotK3 || isCommandCode || isMaxTierModel
);
}

Expand Down Expand Up @@ -390,14 +378,16 @@ export function sanitizeReasoningEffortForProvider(
return body;
}

// ── Muse Spark models (muse-spark-1.2, etc.) ─────────────────────────────
// Accepts minimal|low|medium|high|xhigh. Rejects none (400) and max.
// max/ultra → xhigh; none → minimal.
if (MUSE_SPARK_PATTERN.test(modelStr)) {
// ── Muse Spark models (muse-spark-1.2 vs 1.3+ / future versions) ───────
// 1.2: Accepts minimal|low|medium|high|xhigh (no max).
// max/ultra → xhigh; none → minimal.
// 1.3+ / future versions: Accepts minimal|low|medium|high|xhigh|max.
// ultra → max; none → minimal.
if (MUSE_SPARK_12_PATTERN.test(modelStr)) {
if (effortStr === "max" || effortStr === "ultra") {
log?.info?.(
"REASONING_SANITIZE",
`${provider}/${modelStr}: clamped reasoning_effort ${effortStr} → xhigh (Muse Spark ceiling)`
`${provider}/${modelStr}: clamped reasoning_effort ${effortStr} → xhigh (Muse Spark 1.2 ceiling)`
);
return writeEffortValue(b, "xhigh", c);
}
Expand All @@ -411,6 +401,24 @@ export function sanitizeReasoningEffortForProvider(
return body;
}

if (MUSE_SPARK_PATTERN.test(modelStr)) {
if (effortStr === "ultra") {
log?.info?.(
"REASONING_SANITIZE",
`${provider}/${modelStr}: clamped reasoning_effort ultra → max (Muse Spark ceiling)`
);
return writeEffortValue(b, "max", c);
}
if (effortStr === "none") {
log?.info?.(
"REASONING_SANITIZE",
`${provider}/${modelStr}: clamped reasoning_effort none → minimal (Muse Spark floor)`
);
return writeEffortValue(b, "minimal", c);
}
return body;
}

// ── OpenAI o1 / o3-mini models ───────────────────────────────────────────
// Accepts only low|medium|high. Clamp xhigh/max/ultra → high.
if (O1_O3_REASONING_MODELS_PATTERN.test(modelStr)) {
Expand Down Expand Up @@ -549,11 +557,10 @@ export function sanitizeReasoningEffortForProvider(
? modelStr.slice(provider.length + 1)
: modelStr;
const declaredEfforts = getProviderModels(provider).find(
(entry) => entry.id === providerModelIdForClamp || entry.aliases?.includes(providerModelIdForClamp)
(entry) =>
entry.id === providerModelIdForClamp || entry.aliases?.includes(providerModelIdForClamp)
)?.supportedThinkingEfforts;
const declaredRanked = (
Array.isArray(declaredEfforts) ? declaredEfforts : []
)
const declaredRanked = (Array.isArray(declaredEfforts) ? declaredEfforts : [])
.map((tier) => ({ tier, rank: REASONING_EFFORT_ORDER.indexOf(tier) }))
.filter((x) => x.rank >= 0)
.sort((a, b) => a.rank - b.rank);
Expand Down
1 change: 1 addition & 0 deletions open-sse/executors/freebuff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const MODEL_TO_AGENT: Record<string, string> = {
"z-ai/glm-5.2": "base2-free-glm",
"crof/kimi-k3-eco": "base2-free-kimi-k3-eco",
"anthropic/claude-fable-5": "base2-free-fable",
"meta/muse-spark-1.3-contributor": "base2-free-muse-spark",
"meta/muse-spark-1.2-contributor": "base2-free-muse-spark",
};

Expand Down
42 changes: 29 additions & 13 deletions open-sse/executors/opencode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ import {
isResponsesTerminalLine,
normalizeMuseSparkFinishReason,
} from "./opencodeMuseSpark.ts";
import {
applyMuseSparkResponsesEffort,
dispatchWithMuseSparkMaxFallback,
parseDynamicMuseSparkEffort,
type HttpExecuteResult,
} from "./opencodeMuseSparkEffort.ts";
export { isUnsupportedReasoningEffortRejection } from "./opencodeMuseSparkEffort.ts";
import { currentRequestContext, runInRequestContext } from "./opencodeRequestContext.ts";
import {
handleLoopFreeTierRefusal,
Expand Down Expand Up @@ -138,8 +145,11 @@ const EFFORT_LEVELS = ["none", "low", "high", "max"] as const;
* grok-4.5 low/medium/high; hy3 none/low/high; kimi-k3 max;
* qwen3.6-plus / qwen3.7-max / qwen3.7-plus high/max;
* muse-spark-1.2-contributor minimal/low/medium/high/xhigh (no max)
* - #12674 Muse Spark 1.3 Contributor: minimal/low/medium/high/xhigh (no max),
* verified via `opencode models opencode-go --refresh --verbose`
* - #12674 Muse Spark 1.3 Contributor: minimal/low/medium/high/xhigh verified
* via `opencode models opencode-go --refresh --verbose`; #12687 adds `max`
* as an explicit alias suffix — the wire tier is still sent verbatim and
* falls back to xhigh only on the upstream's unsupported-effort 400 (see
* dispatchWithMuseSparkMaxFallback).
*/
const EFFORT_TIERS: Record<string, readonly string[]> = {
"deepseek-v4-pro": EFFORT_LEVELS,
Expand All @@ -152,8 +162,8 @@ const EFFORT_TIERS: Record<string, readonly string[]> = {
"qwen3.6-plus": ["high", "max"],
"qwen3.7-max": ["high", "max"],
"qwen3.7-plus": ["high", "max"],
"muse-spark-1.3-contributor": ["minimal", "low", "medium", "high", "xhigh", "max"],
"muse-spark-1.2-contributor": ["minimal", "low", "medium", "high", "xhigh"],
"muse-spark-1.3-contributor": ["minimal", "low", "medium", "high", "xhigh"],
};

/**
Expand All @@ -171,7 +181,7 @@ export function parseEffortLevel(model: string): { baseModel: string; effort: st
}
}
}
return null;
return parseDynamicMuseSparkEffort(m);
}

/**
Expand Down Expand Up @@ -543,9 +553,14 @@ export class OpencodeExecutor extends BaseExecutor {
// execute() in runWithProxyContext(proxyInfo.proxy, ...) before we run.
// Only pin direct egress when no such context exists; otherwise let the
// ambient proxy stand instead of clobbering it with the direct sentinel.
const dispatch = () => super.execute(input);
const dispatch = (effInput: ExecuteInput) =>
super.execute(effInput) as Promise<HttpExecuteResult>;
const single = (await guardStall(
await (hasAmbientProxyContext() ? dispatch() : runWithDirectFetchContext(dispatch))
await (hasAmbientProxyContext()
? dispatchWithMuseSparkMaxFallback(input, dispatch, parseEffortLevel)
: runWithDirectFetchContext(() =>
dispatchWithMuseSparkMaxFallback(input, dispatch, parseEffortLevel)
))
)) as HttpExecuteResult;
const retryAfterRefusal = await retryFreeTierRefusalWithObservedTools(
this.freeTierRetryCtx(input),
Expand Down Expand Up @@ -597,10 +612,6 @@ export class OpencodeExecutor extends BaseExecutor {
// This loop only ever dispatches through super.execute() (the HTTP request
// path), which always resolves the object-shaped arm of ExecutorExecuteResult
// — the bare-Response arm belongs to web/scraping executors only (base.ts:290).
type HttpExecuteResult = Extract<
Awaited<ReturnType<BaseExecutor["execute"]>>,
{ response: Response }
>;
let lastResult: HttpExecuteResult | null = null;
let lastSharedEgressError: unknown = null;
const sharedEgressGuardEnabled = isNetworkRotationSharedEgressGuardEnabled();
Expand Down Expand Up @@ -749,10 +760,12 @@ export class OpencodeExecutor extends BaseExecutor {
let result: HttpExecuteResult;
try {
// super.execute() dispatches the HTTP path (never the web/scraping arm).
const loopDispatch = (effInput: ExecuteInput) =>
runWithProxyContext(account.proxy, () =>
super.execute({ ...effInput, skipUpstreamRetry: true })
) as Promise<HttpExecuteResult>;
result = (await guardStall(
await runWithProxyContext(account.proxy, () =>
super.execute({ ...input, skipUpstreamRetry: true })
)
await dispatchWithMuseSparkMaxFallback(input, loopDispatch, parseEffortLevel)
)) as HttpExecuteResult;
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
Expand Down Expand Up @@ -1272,13 +1285,16 @@ export class OpencodeExecutor extends BaseExecutor {
if (parsed) {
const deepseekFamily =
parsed.baseModel === "deepseek-v4-pro" || parsed.baseModel === "deepseek-v4-flash";
const museSparkFamily = parsed.baseModel.startsWith("muse-spark");
if (deepseekFamily) {
// DeepSeek via opencode-go proxies the native DeepSeek contract, which
// accepts a flat reasoning_effort field (#4647).
mb.model = parsed.baseModel;
if (mb.reasoning_effort === undefined) {
mb.reasoning_effort = parsed.effort;
}
} else if (museSparkFamily) {
applyMuseSparkResponsesEffort(mb, parsed);
}
// #10788: every other family's ONLY native effort mechanism is the
// -<tier> suffix in the model id itself (the ids `opencode models
Expand Down
Loading
Loading