Skip to main content

nemo_relay_switchyard/
contract.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! JSON compatibility types for the Switchyard Decision API.
5
6use std::collections::BTreeMap;
7
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10
11/// Routing request schema supported by this plugin.
12pub const ROUTING_REQUEST_SCHEMA_VERSION: &str = "switchyard.routing_request.v1";
13/// Routing decision schema supported by this plugin.
14pub const ROUTING_DECISION_SCHEMA_VERSION: &str = "switchyard.routing_decision.v1";
15
16/// Request-time materialization supplied to Switchyard.
17#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
18#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
19#[serde(rename_all = "snake_case")]
20pub enum RequestMaterialization {
21    /// Identity, protocol, summary, and attempt only.
22    None,
23    /// Baseline summary without current request material.
24    SummaryOnly,
25    /// Latest user prompt.
26    LatestUserPrompt,
27    /// Bounded recent message window.
28    RecentMessageWindow,
29    /// Relay-normalized request plus its provider body.
30    AnnotatedRequest,
31    /// Complete provider request body.
32    FullBody,
33}
34
35/// Switchyard profile selection.
36#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
37pub struct DecisionProfile {
38    /// Profile ID loaded by Switchyard.
39    pub profile_id: String,
40    /// Request materialization mode.
41    pub request_materialization: RequestMaterialization,
42}
43
44/// Normalized Relay identity.
45#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
46pub struct RequestIdentity {
47    /// Stable session identifier.
48    pub session_id: String,
49    /// Per-request identifier.
50    pub request_id: String,
51    /// Optional turn identifier.
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub turn_id: Option<String>,
54    /// Optional parent scope identifier.
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub parent_scope_id: Option<String>,
57    /// Optional root scope identifier.
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub root_scope_id: Option<String>,
60    /// Harness name.
61    pub harness: String,
62    /// Request source.
63    pub source: String,
64    /// Optional resolved work owner.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub owner_id: Option<String>,
67    /// Native, explicit, or synthetic identity quality.
68    pub quality: String,
69}
70
71/// Inbound protocol context.
72#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
73pub struct RequestProtocol {
74    /// Inbound protocol profile.
75    pub inbound_profile: String,
76    /// Inbound endpoint.
77    pub inbound_endpoint: String,
78    /// Response profile expected by the harness.
79    pub desired_response_profile: String,
80}
81
82/// Cheap provider-request summary.
83#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
84pub struct RequestSummary {
85    /// Client-requested model.
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub client_requested_model: Option<String>,
88    /// Optional prompt-token estimate.
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub prompt_token_estimate: Option<u64>,
91    /// Number of tools in the request.
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub tool_count_in_payload: Option<u64>,
94    /// Whether a system prompt is present.
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub has_system_prompt: Option<bool>,
97}
98
99/// Routing-attempt context. Additive fields are ignored by older Switchyard servers.
100#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
101pub struct DecisionAttempt {
102    /// One-indexed routing attempt.
103    pub routing_attempt: u32,
104    /// Maximum Decision API attempts.
105    pub max_routing_attempts: u32,
106    /// Previously selected backend.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub previous_route: Option<String>,
109    /// Reason another decision is requested.
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub retry_reason: Option<String>,
112}
113
114/// Canonical routing request sent by Relay.
115#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
116pub struct RoutingRequest {
117    /// Schema identifier.
118    pub schema_version: String,
119    /// Switchyard profile selection.
120    pub decision_profile: DecisionProfile,
121    /// Normalized identity.
122    pub identity: RequestIdentity,
123    /// Inbound protocol.
124    pub protocol: RequestProtocol,
125    /// Cheap request summary.
126    pub request_summary: RequestSummary,
127    /// Optional request materialization.
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub current_request: Option<Value>,
130    /// Attempt metadata.
131    pub attempt: DecisionAttempt,
132}
133
134/// Router metadata returned by Switchyard.
135#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
136pub struct DecisionProvider {
137    /// Router name.
138    pub name: String,
139    /// Router version.
140    pub version: String,
141}
142
143/// Selected target returned by Switchyard.
144#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
145pub struct RoutingTarget {
146    /// Tier label.
147    pub tier: String,
148    /// Selected model.
149    pub target_model: String,
150    /// Backend binding ID.
151    pub backend_id: String,
152    /// Target protocol profile.
153    pub target_protocol_profile: String,
154    /// Target endpoint.
155    pub target_endpoint: String,
156}
157
158/// Canonical Switchyard routing decision.
159#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
160pub struct RoutingDecision {
161    /// Schema identifier.
162    pub schema_version: String,
163    /// Decision identifier.
164    pub decision_id: String,
165    /// Router metadata.
166    pub router: DecisionProvider,
167    /// Selected target.
168    pub route: RoutingTarget,
169    /// Explicit counterfactual route supplied by routers that define one.
170    #[serde(default, skip_serializing_if = "Option::is_none")]
171    pub baseline_route: Option<RoutingTarget>,
172    /// Optional confidence.
173    #[serde(default)]
174    pub confidence: Option<f64>,
175    /// Optional reason code.
176    #[serde(default)]
177    pub reason_code: Option<String>,
178    /// Optional reason summary.
179    #[serde(default)]
180    pub reason_summary: Option<String>,
181    /// Additive router metadata.
182    #[serde(default)]
183    pub metadata: BTreeMap<String, Value>,
184    /// Unknown additive response fields.
185    #[serde(flatten)]
186    pub extra: BTreeMap<String, Value>,
187}