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
15 changes: 9 additions & 6 deletions docs/cloudflare-containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,15 @@ Wrangler `vars` in `wrangler.jsonc` already set:
- `SERVER_PORT=8080`
- `APP_NAME=MultiLLM Proxy`

`RUNTIME_FEATURE_FLAGS` groups seven nonsecret feature settings into one JSON
binding to leave room for provider secrets. The Container, usage ledger and
roleplay session read these grouped settings. An individual binding with the
same setting name takes precedence, including an explicit disabled value.
Keep credentials in separate Worker secrets; the grouped binding accepts only
the seven names committed in `wrangler.jsonc`.
`RUNTIME_FEATURE_FLAGS` groups nine nonsecret settings into one JSON binding to
leave room for provider secrets. Worker vars and secrets share a limit of 128
text bindings, and Cloudflare rejects an upload that exceeds it. The Container,
usage ledger, roleplay settings and roleplay session read these grouped
settings. An individual binding with the same setting name takes precedence,
including an explicit disabled value. Keep credentials in separate Worker
secrets; the grouped binding accepts only the nine names committed in
`wrangler.jsonc`. Add a new nonsecret setting to the group rather than as a
separate var.

Public HTTP requests receive a `308` redirect to the same HTTPS URL before any
route or container work. For Container requests, the Worker overwrites
Expand Down
7 changes: 4 additions & 3 deletions docs/roleplay.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,10 @@ configured key. Leave the variable unset for the adaptive behaviour above.

`ROLEPLAY_HANDOFF_FROM` names chain entries that may pass a turn on, and
`ROLEPLAY_HANDOFF_TO` the entries that take it; both use the
`ROLEPLAY_INTELLIGENCE_MODELS` format and are unset by default. Production lets
DeepSeek V4.1 Flash on OpenCode Go hand off to GLM-5.3 on OpenCode Go, then on
ClinePass. A source request carries one internal function, `multillm_hand_off_turn`,
`ROLEPLAY_INTELLIGENCE_MODELS` format and are unset by default. Production sets
both inside `RUNTIME_FEATURE_FLAGS`; a separate var of the same name takes
precedence. Production lets DeepSeek V4.1 Flash on OpenCode Go hand off to
GLM-5.3 on OpenCode Go, then on ClinePass. A source request carries one internal function, `multillm_hand_off_turn`,
whose arguments are a reason (`deeper_reasoning` or `stronger_prose`), short notes
and an optional draft. When the source calls it instead of writing, the Worker
discards that attempt (its frames are held until visible text appears, so nothing
Expand Down
1 change: 1 addition & 0 deletions tests/helpers/load_cloudflare_worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export async function roleplayModuleUrl() {
const configDataUrl = dataModuleUrl(
configSource
.replace('from "../client-headers.mjs";', `from "${new URL("../../worker/client-headers.mjs", import.meta.url)}";`)
.replace('from "../runtime-feature-flags.mjs";', `from "${new URL("../../worker/runtime-feature-flags.mjs", import.meta.url)}";`)
.replace('from "./routing-policy.mjs";', `from "${new URL("../../worker/roleplay/routing-policy.mjs", import.meta.url)}";`)
.replace(
'from "./capacity.mjs";',
Expand Down
4 changes: 2 additions & 2 deletions tests/outbound_inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -2127,7 +2127,7 @@
"worker/roleplay/config.mjs": {
"kind": "non-provider",
"reason": "Runtime module pinned independently of HTTP client spelling; network attempts require runtime admission.",
"sha256": "df8aca19da17bf36a87b3c2c9501f7c950033490288b79a866d75f1fd323c121"
"sha256": "5998981bc7e5e7f026da9e49ed80d8de3212023e970db84549cb9976ba37c777"
},
"worker/roleplay/continuation.mjs": {
"kind": "non-provider",
Expand Down Expand Up @@ -2322,7 +2322,7 @@
"worker/runtime-feature-flags.mjs": {
"kind": "non-provider",
"reason": "Runtime module pinned independently of HTTP client spelling; network attempts require runtime admission.",
"sha256": "acf379482beccbaa1faae4f1b872ee5ed613f3f3abfd600e60fb5fff1a4464fd"
"sha256": "40d379212efba252e64155364a4c26c359724c37c6c0458b5afe6c4824f18724"
},
"worker/saml-identity.mjs": {
"kind": "non-provider",
Expand Down
16 changes: 12 additions & 4 deletions tests/test_roleplay_config_worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -278,17 +278,25 @@ test("deployment lets DeepSeek hand pivotal turns to GLM 5.3, at most every othe

// Measured 2026-10-25: with the tool offered, DeepSeek V4.1 Flash wrote all 12 ordinary turns
// itself and handed off 4 of 6 climax and multi-faction planning turns, each with a full draft.
assert.equal(config.vars?.ROLEPLAY_HANDOFF_FROM, "opencode:deepseek-v4.1-flash");
assert.deepEqual(config.vars?.ROLEPLAY_HANDOFF_TO.split(","), [
// Grouped in RUNTIME_FEATURE_FLAGS: the Worker is at its limit of 128 text bindings.
const flags = config.vars?.RUNTIME_FEATURE_FLAGS;
assert.equal(config.vars?.ROLEPLAY_HANDOFF_FROM, undefined);
assert.equal(config.vars?.ROLEPLAY_HANDOFF_TO, undefined);
assert.equal(flags?.ROLEPLAY_HANDOFF_FROM, "opencode:deepseek-v4.1-flash");
assert.deepEqual(flags?.ROLEPLAY_HANDOFF_TO.split(","), [
"opencode:glm-5.3",
"cline-pass:cline-pass/glm-5.3",
]);
// ROLEPLAY_HANDOFF_MIN_TURNS keeps its default of 2.
assert.equal(config.vars?.ROLEPLAY_HANDOFF_MIN_TURNS, undefined);
const chain = config.vars?.ROLEPLAY_INTELLIGENCE_MODELS.split(",");
for (const target of config.vars.ROLEPLAY_HANDOFF_TO.split(",")) {
assert.ok(chain.indexOf(target) > chain.indexOf(config.vars.ROLEPLAY_HANDOFF_FROM), target);
for (const target of flags.ROLEPLAY_HANDOFF_TO.split(",")) {
assert.ok(chain.indexOf(target) > chain.indexOf(flags.ROLEPLAY_HANDOFF_FROM), target);
}
const settings = getRoleplaySettings(config.vars);
assert.deepEqual([...settings.handoffSources], ["opencode:deepseek-v4.1-flash"]);
assert.deepEqual([...settings.handoffTargets], ["opencode:glm-5.3", "cline-pass:cline-pass/glm-5.3"]);
assert.equal(settings.handoffMinTurns, 2);
});

test("deployment routes GLM through LinkAPI before NavyAI", async () => {
Expand Down
7 changes: 5 additions & 2 deletions tests/test_runtime_feature_flags_worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ const config = JSON.parse(await readFile(new URL("../wrangler.jsonc", import.met
const { MultiLLMProxyContainer } = await loadWorkerModule();

test("deployment groups feature settings while preserving Container flags and secrets", () => {
assert.equal(Object.keys(config.vars.RUNTIME_FEATURE_FLAGS).length, 7);
assert.equal(Object.keys(config.vars).length, 67);
assert.equal(Object.keys(config.vars.RUNTIME_FEATURE_FLAGS).length, 9);
// Vars and secrets share the Worker's limit of 128 text bindings, and the release has 127.
// Put a new nonsecret setting in RUNTIME_FEATURE_FLAGS instead of raising this count.
assert.equal(Object.keys(config.vars).length, 65);
const source = { ...config.vars, ANTHROPIC_API_KEY: "synthetic-provider-secret" };
const container = new MultiLLMProxyContainer({}, source);
assert.equal(container.envVars.MODEL_COOLDOWN_ENABLED, "true");
Expand All @@ -24,6 +26,7 @@ test("deployment groups feature settings while preserving Container flags and se
assert.equal(container.envVars.RUNTIME_FEATURE_FLAGS, undefined);
assert.equal(usageBucketsEnabled(source), true);
assert.equal(runtimeFeatureValue(source, "ROLEPLAY_CANDIDATE_CONTEXT_REFIT"), "window");
assert.equal(runtimeFeatureValue(source, "ROLEPLAY_HANDOFF_FROM"), "opencode:deepseek-v4.1-flash");
});

test("individual settings retain disabled values and precedence without mutating bindings", () => {
Expand Down
5 changes: 3 additions & 2 deletions worker/roleplay/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
rankQualityEligible,
} from "./routing-policy.mjs";
import { clientContextHeaders, withClientDefaults, withOpencodeSession } from "../client-headers.mjs";
import { runtimeFeatureValue } from "../runtime-feature-flags.mjs";
import {
parseRoleplayProviderLimits,
resolveRoleplayCandidateLimits,
Expand Down Expand Up @@ -743,8 +744,8 @@ export function getRoleplaySettings(env) {
env.ROLEPLAY_PROVIDER_LIMITS,
),
intelligenceChain: parseIntelligenceChain(env.ROLEPLAY_INTELLIGENCE_MODELS),
handoffSources: parseHandoffEntries(env.ROLEPLAY_HANDOFF_FROM),
handoffTargets: parseHandoffEntries(env.ROLEPLAY_HANDOFF_TO),
handoffSources: parseHandoffEntries(runtimeFeatureValue(env, "ROLEPLAY_HANDOFF_FROM")),
handoffTargets: parseHandoffEntries(runtimeFeatureValue(env, "ROLEPLAY_HANDOFF_TO")),
handoffMinTurns: boundedInteger(env.ROLEPLAY_HANDOFF_MIN_TURNS, 2, 0, 1_000),
emptyReplyFallbackEnabled: booleanSetting(
env.ROLEPLAY_EMPTY_REPLY_FALLBACK_ENABLED,
Expand Down
2 changes: 2 additions & 0 deletions worker/runtime-feature-flags.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const FEATURE_NAMES = Object.freeze([
"PROMPT_CACHE_AFFINITY_ENABLED",
"PROMPT_CACHE_USAGE_BUCKETS_ENABLED",
"ROLEPLAY_CANDIDATE_CONTEXT_REFIT",
"ROLEPLAY_HANDOFF_FROM",
"ROLEPLAY_HANDOFF_TO",
"UPSTREAM_RETRY_AFTER_ADVICE_ENABLED",
]);

Expand Down
4 changes: 2 additions & 2 deletions wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@
"ROLEPLAY_PROVIDER_LIMITS": "{}",
"ROLEPLAY_AUTO_ROUTE": "intelligence",
"ROLEPLAY_INTELLIGENCE_MODELS": "opencode:deepseek-v4.1-flash,opencode:glm-5.3,opencode:mimo-v2.6-pro,cline-pass:cline-pass/glm-5.3,nanogpt:xiaomi/mimo-v2.6-pro,nanogpt:z-ai/glm-5.3,nanogpt:z-ai/glm-5.3-flash,nanogpt:z-ai/glm-5.2,cline-pass:cline-pass/glm-5.3-flash,cline-pass:cline-pass/mimo-v2.6-pro",
"ROLEPLAY_HANDOFF_FROM": "opencode:deepseek-v4.1-flash",
"ROLEPLAY_HANDOFF_TO": "opencode:glm-5.3,cline-pass:cline-pass/glm-5.3",
"GLM_AUTO_OPTIMIZE": "true",
"GLM_AUTO_OPTIMIZE_TRIGGER_TOKENS": "8000",
"GLM_AUTO_OPTIMIZE_KEEP_RECENT_TURNS": "8",
Expand Down Expand Up @@ -166,6 +164,8 @@
"PROMPT_CACHE_AFFINITY_ENABLED": "true",
"PROMPT_CACHE_USAGE_BUCKETS_ENABLED": "true",
"ROLEPLAY_CANDIDATE_CONTEXT_REFIT": "window",
"ROLEPLAY_HANDOFF_FROM": "opencode:deepseek-v4.1-flash",
"ROLEPLAY_HANDOFF_TO": "opencode:glm-5.3,cline-pass:cline-pass/glm-5.3",
"UPSTREAM_RETRY_AFTER_ADVICE_ENABLED": "true",
"MULTILLM_STREAM_PREFLIGHT": "strict"
}
Expand Down
Loading