diff --git a/bindings/typescript/test/composite.test.mjs b/bindings/typescript/test/composite.test.mjs index b2c888b839..062e7fc3af 100644 --- a/bindings/typescript/test/composite.test.mjs +++ b/bindings/typescript/test/composite.test.mjs @@ -47,6 +47,7 @@ test("decision request, cutoff, and branch-owned state", async () => { assert.deepEqual(request.context, { task: messages, candidates: ["a", "b"], comparison: { capable: "a", efficient: "b" }, evidence: options.classifier.decision.evidence, + session: null, }); assert.deepEqual(Object.keys(request.questions), ["route"]); assert.deepEqual(request.questions.route.instructions, instructions); diff --git a/crates/libsy/src/algorithms/llm_class.rs b/crates/libsy/src/algorithms/llm_class.rs index 1997ab3b79..189fc9c3d5 100644 --- a/crates/libsy/src/algorithms/llm_class.rs +++ b/crates/libsy/src/algorithms/llm_class.rs @@ -1388,6 +1388,7 @@ mod tests { call.request.context, json!({ "task": expected_task, + "session": "session-1", "candidates": ["a", "b", "c"], "comparison": {"capable": "a", "efficient": "b"}, "evidence": settings.evidence, diff --git a/crates/libsy/src/algorithms/llm_class/decision.rs b/crates/libsy/src/algorithms/llm_class/decision.rs index 35efe06a6c..76b0d40313 100644 --- a/crates/libsy/src/algorithms/llm_class/decision.rs +++ b/crates/libsy/src/algorithms/llm_class/decision.rs @@ -124,6 +124,7 @@ impl Classifier for DecisionClassifier { model: None, context: json!({ "task": self.input.messages(request), + "session": request.metadata.as_ref().and_then(|m| m.session_id.as_deref()), "candidates": self.config.candidates.keys().collect::>(), "comparison": { "capable": self.config.candidate(capable)?, diff --git a/crates/switchyard-runner/tests/openai_decisions.rs b/crates/switchyard-runner/tests/openai_decisions.rs index 6447e212e0..b635fdf11b 100644 --- a/crates/switchyard-runner/tests/openai_decisions.rs +++ b/crates/switchyard-runner/tests/openai_decisions.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, mpsc}; use serde_json::{Value, json}; use switchyard_llm_client::RunObservation; -use switchyard_protocol::{Request, Usage, text_request}; +use switchyard_protocol::{Metadata, Request, Usage, text_request}; use switchyard_runner::Runner; use wiremock::matchers::{body_partial_json, header, method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -84,6 +84,10 @@ evidence = {{ description = "Compare a and b" }} .execute( Request { llm_request: text_request(None, "Route this request"), + metadata: Some(Metadata { + session_id: Some("session-1".into()), + ..Metadata::default() + }), ..Request::default() }, Some(Arc::new(move |event| { @@ -114,6 +118,7 @@ evidence = {{ description = "Compare a and b" }} context["evidence"], json!({"description": "Compare a and b"}) ); + assert_eq!(context["session"], "session-1"); server.verify().await; Ok(()) } diff --git a/crates/switchyard-server/CONFIGURATION.md b/crates/switchyard-server/CONFIGURATION.md index 275718ece4..3f2f04ecef 100644 --- a/crates/switchyard-server/CONFIGURATION.md +++ b/crates/switchyard-server/CONFIGURATION.md @@ -67,6 +67,7 @@ evidence = { candidates = { a = "Higher-quality model", b = "Lower-cost model" } Supply evidence from your evaluations and choose a cutoff using those results. - `decision.instructions` can replace the default relative-advantage instructions. It must preserve the meaning of `advantage` and `no_advantage`. Evidence is passed unchanged. +- The context includes `session`, the request's session ID, or `null` when the request has none. - The capable tier is selected only when its advantage score exceeds `cutoff`. Client failures and deadlines use the capable tier by default; set `fail_open = false` on the route to propagate those errors.