fix(providers): send a per-sample x-session-id so stateful routers see one task per sample - #100
Conversation
…e one task per sample
|
I'll fix CI failures and address comments from users with write access that start with 'DevinAI' or '@devin'.
Original prompt from Ayush
|
…ID for every runAgentCli caller
|
Post-work validation for Summary:
Method: a real Example captured payloads{
"responses_request_q-1_epoch0": { "x-session-id": "wf-run-123.0.q-1", "x-openrouter-cache-salt": "wf-run-123:epoch-0" },
"responses_request_q-1_epoch1": { "x-session-id": "wf-run-123.1.q-1", "x-openrouter-cache-salt": "wf-run-123:epoch-1" },
"sanitized_sample": { "sample.id": "swe/atlas:task#7 v2", "x-session-id": "wf-run-123.0.swe-atlas-task-7-v2" },
"long_sample": { "sample.id.length": 305, "x-session-id.length": 256, "suffix": "-4ca897508d02" },
"retry_after_503": [
{ "status": 503, "x-session-id": "wf-run-123.0.q-1", "x-openrouter-cache-salt": "wf-run-123:epoch-0" },
{ "status": 200, "x-session-id": "wf-run-123.0.q-1", "x-openrouter-cache-salt": "wf-run-123:epoch-0:attempt-1" }
],
"ori_exec_env": { "sampleId": "swe/atlas:task#7 v2", "ORI_OPENROUTER_SESSION_ID": "wf-run-123.1.swe-atlas-task-7-v2" },
"no_session_run": { "x-session-id": null, "x-openrouter-cache-salt": "epoch-0" },
"prefix_collision": { "wf-run-1234.0.q-1 matches parent wf-run-123": false }
}📈 Live monitoring: Datadog, bench-harness Full validation report: |
TL;DR
Inference requests now send
x-session-id = <sessionId>.<epoch>.<sampleId>while everything else (result store, logs, progress, response-cache salt) keeps using the run-levelsessionId, soopenrouter/jevand the sticky-session pin no longer treat every sample in a run as a continuation of the first one.What changed?
src/runtime/request-session-id.ts: acurrentSampleIdRefFiberRef (set per sample inharness/run.ts, next tosetCurrentEpoch) andbuildRequestSessionId(sessionId, epoch, sampleId).${sessionId}.${epoch}.${sanitizedSampleId}; sample chars outside[A-Za-z0-9_-]become-so/, spaces, etc. cannot break the dot hierarchy.SESSION_ID_MAX_LENGTH): an overlong sample segment is truncated and suffixed with a 12-hex sha256 so long ids stay unique. Falls back to the bare run id when epoch or sample is unknown, or when there is no room.responses-client.ts: headerx-session-idnow uses the per-sample id.buildResponseCacheSaltstill receivesconfig.sessionId, so resume/replay cache keys are unchanged.ResponsesConfig.sessionIdsemantics are unchanged.agent-cli/runner.ts:runAgentClireads the same epoch and sample FiberRefs and nests them intoORI_OPENROUTER_SESSION_IDbefore exec. This covers every ori subprocess caller in one place (deep-swe, terminal-bench ori-solver, swe-atlas, and future callers) since those bypass the Responses client. Solver files are untouched. wandr/deep-swecheckpointKeyvalues (/-separated) are checkpoint-store keys, not request ids, and are untouched.Why?
Session id has routing priority over conversation id. With one id per child workflow, jev's decisions-router state carried the incumbent model across unrelated GPQA questions (390/396 requests stayed on the first pick), and the sticky endpoint/model-slug pin, Fortuna sampling key, and provider prompt-cache affinity were all run-scoped. Nesting epoch and sample under the run id gives each task fresh router state and keeps multi-turn continuity within a task, while openrouter-web's ClickHouse realized-cost and router-usage queries already match
session_id = p OR startsWith(session_id, concat(p, '.')), so run-level reporting rolls up unchanged.Slack thread: https://openrouter.slack.com/archives/C0BMHG5CG1E/p1789781836317759
Searched existing PRs (open, draft, merged, "session"); none matched.
How to test
Expected: the client test asserts
x-session-id: wf-123.2.q-17alongsidex-openrouter-cache-salt: wf-123:epoch-2forsessionId=wf-123, epoch 2, sampleq/17. The ori-solver test assertsORI_OPENROUTER_SESSION_ID=run-1234.1.terminal_bench-adaptive-rejection-samplerfor epoch 1.Benchmark impact
Routing only, no scorer/solver/dataset change. Runs of
openrouter/jev(and to a lesser degreeauto,pareto,phaser) will pick a model fresh per sample instead of inheriting the run's first pick, for both Responses-client benchmarks and agent-CLI benchmarks. Endpoint selection is per sample rather than pinned per run. Response-cache replay for in-flight runs is unaffected because the salt is unchanged.Follow-ups in openrouter-web (not in this PR):
bench_run_no_prompt_cache.tfgroups by exact@extra.session_idfrom provider-side logs and themonitor-benchmark-runSKILL usessession_id = '<workflow_id>'; both need a prefix match if they read the request id. Harness logs still emit the run-level id.Reviewer focus
buildRequestSessionIdsanitization/truncation rules.agent-cli/runner.ts: the control-character guard still runs on the caller'sopts.sessionId; the nested id only adds., digits, and[A-Za-z0-9_-], so it cannot introduce one.Checklist
Link to Devin session: https://openrouter.devinenterprise.com/sessions/b91ee8adb7514c2fa3f15e8dfa1dfd1b
Open in Devin Desktop: https://openrouter.devinenterprise.com/desktop/session/b91ee8adb7514c2fa3f15e8dfa1dfd1b?variant=devin
Requested by: @ayush-or