use serde_json::{Map, Value}; use crate::formats::shared::model_directives::ReasoningEffort; pub fn parse_openai_stop_sequences(stop: Option<&Value>) -> Option> { match stop { Some(Value::String(value)) if !value.trim().is_empty() => { Some(vec![Value::String(value.clone())]) } Some(Value::Array(values)) => Some( values .iter() .filter_map(|value| value.as_str()) .map(str::trim) .filter(|value| !value.is_empty()) .map(|value| Value::String(value.to_string())) .collect::>(), ) .filter(|values| !values.is_empty()), _ => None, } } pub fn resolve_openai_chat_max_tokens(request: &Map) -> u64 { request .get("max_completion_tokens") .and_then(value_as_u64) .or_else(|| request.get("max_tokens").and_then(value_as_u64)) .unwrap_or(4096) } pub fn value_as_u64(value: &Value) -> Option { value .as_u64() .or_else(|| value.as_i64().and_then(|value| u64::try_from(value).ok())) } pub fn copy_request_number_field( request: &Map, target: &mut Map, key: &str, ) { copy_request_number_field_as(request, target, key, key); } pub fn copy_request_number_field_as( request: &Map, target: &mut Map, source_key: &str, target_key: &str, ) { if let Some(value) = request.get(source_key).cloned() { if value.is_number() { target.insert(target_key.to_string(), value); } } } pub fn map_openai_reasoning_effort_to_claude_output(value: &str) -> Option<&'static str> { ReasoningEffort::parse(value).map(ReasoningEffort::as_claude_output_value) } pub fn map_openai_reasoning_effort_to_thinking_budget(value: &str) -> Option { ReasoningEffort::parse(value).map(ReasoningEffort::thinking_budget_tokens) } pub fn map_openai_reasoning_effort_to_gemini_budget(value: &str) -> Option { map_openai_reasoning_effort_to_thinking_budget(value) } pub fn map_thinking_budget_to_openai_reasoning_effort(value: u64) -> &'static str { match value { 0..=1664 => "low", 1665..=3072 => "medium", 3073..=6144 => "high", _ => "xhigh", } } pub fn extract_openai_reasoning_effort(request: &Map) -> Option { request .get("reasoning_effort") .and_then(Value::as_str) .or_else(|| { request .get("reasoning") .and_then(Value::as_object) .and_then(|reasoning| reasoning.get("effort")) .and_then(Value::as_str) }) .map(str::trim) .filter(|value| !value.is_empty()) .map(|value| value.to_ascii_lowercase()) }