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
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ fn make_request(input: &str, stream: bool, prev_id: Option<String>) -> RequestPa
max_output_tokens: None,
truncation: None,
metadata: None,
cache_salt: None,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ mod tests {
max_output_tokens: None,
truncation: None,
metadata: None,
cache_salt: None,
};
RequestContext {
enriched_request: req.clone(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ mod tests {
max_output_tokens: None,
truncation: None,
metadata: None,
cache_salt: None,
};
RequestContext {
enriched_request: req.clone(),
Expand Down
20 changes: 20 additions & 0 deletions crates/agentic-server-core/src/types/request_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub struct RequestPayload {
pub max_output_tokens: Option<u32>,
pub truncation: Option<String>,
pub metadata: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_salt: Option<String>,
}

fn default_true() -> bool {
Expand Down Expand Up @@ -61,6 +63,8 @@ pub struct UpstreamRequest<'a> {
pub truncation: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<&'a Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_salt: Option<&'a str>,
}

// serde's `skip_serializing_if` requires a `&Option<T>` receiver, so the
Expand Down Expand Up @@ -109,6 +113,7 @@ impl RequestPayload {
max_output_tokens: self.max_output_tokens,
truncation: self.truncation.as_deref(),
metadata: self.metadata.as_ref(),
cache_salt: self.cache_salt.as_deref(),
})
}
}
Expand Down Expand Up @@ -178,6 +183,21 @@ impl From<&ResponsesInput> for Vec<InputItem> {
mod tests {
use super::*;

#[test]
fn request_payload_forwards_cache_salt_upstream() {
let payload: RequestPayload = serde_json::from_value(serde_json::json!({
"model": "test-model",
"input": "hello",
"cache_salt": "tenant-a"
}))
.expect("request should deserialize");

let upstream = serde_json::to_value(payload.to_upstream_request(false).expect("request should normalize"))
.expect("upstream request should serialize");

assert_eq!(upstream["cache_salt"], "tenant-a");
}

#[test]
fn request_payload_uses_option_tool_choice_for_missing_vs_explicit() {
let absent: RequestPayload = serde_json::from_value(serde_json::json!({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ fn request(text: &str, tools: Option<Vec<ResponsesTool>>) -> RequestPayload {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/agentic-server-core/tests/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ pub fn make_request(
max_output_tokens: None,
truncation: None,
metadata: None,
cache_salt: None,
}
}

Expand Down
16 changes: 16 additions & 0 deletions crates/agentic-server-core/tests/web_search_tool_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ async fn execute_runs_web_search_and_sends_tool_output_back_to_model() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, Arc::clone(&exec_ctx)).run().await.unwrap();
Expand Down Expand Up @@ -667,6 +668,7 @@ async fn execute_relaxes_forced_tool_choice_after_web_search_result() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, Arc::clone(&exec_ctx)).run().await.unwrap();
Expand Down Expand Up @@ -714,6 +716,7 @@ async fn execute_returns_mixed_client_tool_calls_without_followup_model_request(
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, Arc::clone(&exec_ctx)).run().await.unwrap();
Expand Down Expand Up @@ -760,6 +763,7 @@ async fn execute_returns_mixed_client_tool_calls_without_followup_model_request(
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};
let continuation = ExecuteRequest::new(continuation_payload, exec_ctx).run().await.unwrap();
assert!(matches!(continuation, Either::Left(_)));
Expand Down Expand Up @@ -829,6 +833,7 @@ async fn execute_accumulates_usage_across_web_search_model_rounds() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, exec_ctx).run().await.unwrap();
Expand Down Expand Up @@ -871,6 +876,7 @@ async fn stream_emits_web_search_lifecycle_events_before_final_payload() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, Arc::clone(&exec_ctx)).run().await.unwrap();
Expand Down Expand Up @@ -952,6 +958,7 @@ async fn stream_hides_web_search_function_events_when_name_arrives_on_done() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, Arc::clone(&exec_ctx)).run().await.unwrap();
Expand Down Expand Up @@ -1017,6 +1024,7 @@ async fn execute_runs_multiple_web_search_calls_concurrently() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = tokio::time::timeout(Duration::from_secs(2), ExecuteRequest::new(payload, exec_ctx).run())
Expand Down Expand Up @@ -1064,6 +1072,7 @@ async fn execute_feeds_web_search_execution_errors_back_to_model() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, exec_ctx).run().await.unwrap();
Expand Down Expand Up @@ -1113,6 +1122,7 @@ async fn execute_returns_incomplete_after_max_gateway_tool_rounds() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

// Budget exhausted while the model keeps requesting tools → the response is
Expand Down Expand Up @@ -1162,6 +1172,7 @@ async fn execute_feeds_invalid_web_search_arguments_back_to_model() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, exec_ctx).run().await.unwrap();
Expand Down Expand Up @@ -1218,6 +1229,7 @@ async fn execute_runs_large_gateway_fanout_without_hard_cap() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, exec_ctx)
Expand Down Expand Up @@ -1280,6 +1292,7 @@ async fn stream_error_events_escape_error_messages() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, exec_ctx).run().await.unwrap();
Expand Down Expand Up @@ -1354,6 +1367,7 @@ async fn incomplete_turn_persists_a_consistent_conversation_for_continuation() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, Arc::clone(&exec_ctx)).run().await.unwrap();
Expand All @@ -1379,6 +1393,7 @@ async fn incomplete_turn_persists_a_consistent_conversation_for_continuation() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};
let _ = ExecuteRequest::new(continuation_payload, exec_ctx).run().await.unwrap();

Expand Down Expand Up @@ -1448,6 +1463,7 @@ async fn stream_returns_incomplete_after_max_gateway_tool_rounds() {
max_output_tokens: Some(1024),
truncation: None,
metadata: None,
cache_salt: None,
};

let result = ExecuteRequest::new(payload, exec_ctx).run().await.unwrap();
Expand Down