@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
22
33use schemars:: JsonSchema ;
44use serde:: { Deserialize , Serialize } ;
5- use serde_json:: Value ;
5+ use serde_json:: { Value , json } ;
66
77/// Provider-agnostic model tuning parameters.
88#[ derive( Clone , Debug , Default , PartialEq , Serialize , Deserialize , JsonSchema ) ]
@@ -41,6 +41,60 @@ pub struct ReasoningSettings {
4141}
4242
4343impl ModelSettings {
44+ /// Return the model settings that are safe to attach to trace metadata.
45+ ///
46+ /// Provider-specific request extras can contain credentials or other request-scoped
47+ /// sensitive data, so they are intentionally excluded.
48+ pub fn to_traceable_map ( & self ) -> BTreeMap < String , Value > {
49+ let mut traceable = BTreeMap :: new ( ) ;
50+ if let Some ( value) = self . temperature {
51+ traceable. insert ( "temperature" . to_owned ( ) , json ! ( value) ) ;
52+ }
53+ if let Some ( value) = self . top_p {
54+ traceable. insert ( "top_p" . to_owned ( ) , json ! ( value) ) ;
55+ }
56+ if let Some ( value) = self . max_output_tokens {
57+ traceable. insert ( "max_output_tokens" . to_owned ( ) , json ! ( value) ) ;
58+ }
59+ if let Some ( value) = self . frequency_penalty {
60+ traceable. insert ( "frequency_penalty" . to_owned ( ) , json ! ( value) ) ;
61+ }
62+ if let Some ( value) = self . presence_penalty {
63+ traceable. insert ( "presence_penalty" . to_owned ( ) , json ! ( value) ) ;
64+ }
65+ if let Some ( value) = & self . tool_choice {
66+ traceable. insert ( "tool_choice" . to_owned ( ) , json ! ( value) ) ;
67+ }
68+ if let Some ( value) = self . parallel_tool_calls {
69+ traceable. insert ( "parallel_tool_calls" . to_owned ( ) , json ! ( value) ) ;
70+ }
71+ if let Some ( value) = & self . truncation {
72+ traceable. insert ( "truncation" . to_owned ( ) , json ! ( value) ) ;
73+ }
74+ if let Some ( value) = self . store {
75+ traceable. insert ( "store" . to_owned ( ) , json ! ( value) ) ;
76+ }
77+ if let Some ( value) = self . include_usage {
78+ traceable. insert ( "include_usage" . to_owned ( ) , json ! ( value) ) ;
79+ }
80+ if !self . response_include . is_empty ( ) {
81+ traceable. insert ( "response_include" . to_owned ( ) , json ! ( self . response_include) ) ;
82+ }
83+ if let Some ( value) = self . top_logprobs {
84+ traceable. insert ( "top_logprobs" . to_owned ( ) , json ! ( value) ) ;
85+ }
86+ if let Some ( value) = & self . reasoning {
87+ traceable. insert ( "reasoning" . to_owned ( ) , json ! ( value) ) ;
88+ }
89+ if let Some ( value) = & self . verbosity {
90+ traceable. insert ( "verbosity" . to_owned ( ) , json ! ( value) ) ;
91+ }
92+ if !self . metadata . is_empty ( ) {
93+ traceable. insert ( "metadata" . to_owned ( ) , json ! ( self . metadata) ) ;
94+ }
95+ traceable
96+ }
97+
4498 pub fn resolve ( & self , override_settings : Option < & Self > ) -> Self {
4599 let Some ( override_settings) = override_settings else {
46100 return self . clone ( ) ;
@@ -212,4 +266,32 @@ mod tests {
212266 assert_eq ! ( resolved. extra_args. get( "timeout" ) , Some ( & json!( 10 ) ) ) ;
213267 assert_eq ! ( resolved. extra_args. get( "retry" ) , Some ( & json!( 2 ) ) ) ;
214268 }
269+
270+ #[ test]
271+ fn traceable_model_settings_omit_request_extras ( ) {
272+ let settings = ModelSettings {
273+ temperature : Some ( 0.5 ) ,
274+ metadata : BTreeMap :: from ( [ ( "purpose" . to_owned ( ) , json ! ( "trace" ) ) ] ) ,
275+ extra_query : BTreeMap :: from ( [ ( "api-key" . to_owned ( ) , json ! ( "query-secret" ) ) ] ) ,
276+ extra_body : BTreeMap :: from ( [ ( "secret" . to_owned ( ) , json ! ( "body-secret" ) ) ] ) ,
277+ extra_headers : BTreeMap :: from ( [ (
278+ "authorization" . to_owned ( ) ,
279+ json ! ( "Bearer header-secret" ) ,
280+ ) ] ) ,
281+ extra_args : BTreeMap :: from ( [ ( "api_key" . to_owned ( ) , json ! ( "arg-secret" ) ) ] ) ,
282+ ..Default :: default ( )
283+ } ;
284+
285+ let traceable = settings. to_traceable_map ( ) ;
286+
287+ assert_eq ! ( traceable. get( "temperature" ) , Some ( & json!( 0.5 ) ) ) ;
288+ assert_eq ! (
289+ traceable. get( "metadata" ) ,
290+ Some ( & json!( { "purpose" : "trace" } ) )
291+ ) ;
292+ assert ! ( !traceable. contains_key( "extra_query" ) ) ;
293+ assert ! ( !traceable. contains_key( "extra_body" ) ) ;
294+ assert ! ( !traceable. contains_key( "extra_headers" ) ) ;
295+ assert ! ( !traceable. contains_key( "extra_args" ) ) ;
296+ }
215297}
0 commit comments