| #[cfg(test)] |
| mod tests { |
| use crate::proxy::mappers::claude::models::{ |
| ClaudeRequest, Message, MessageContent, ContentBlock, ThinkingConfig |
| }; |
| use crate::proxy::mappers::claude::request::transform_claude_request_in; |
| use crate::proxy::mappers::claude::thinking_utils::{analyze_conversation_state, close_tool_loop_for_thinking}; |
| use serde_json::json; |
|
|
| |
| |
| |
| |
| |
| #[test] |
| fn test_first_thinking_request_permissive_mode() { |
| |
| let req = ClaudeRequest { |
| model: "claude-3-7-sonnet-20250219".to_string(), |
| messages: vec![ |
| Message { |
| role: "user".to_string(), |
| content: MessageContent::String("Hello, please think.".to_string()), |
| } |
| ], |
| system: None, |
| tools: None, |
| stream: false, |
| max_tokens: None, |
| temperature: None, |
| top_p: None, |
| top_k: None, |
| thinking: Some(ThinkingConfig { |
| type_: "enabled".to_string(), |
| budget_tokens: Some(1024), |
| effort: None, |
| }), |
| metadata: None, |
| output_config: None, |
| size: None, |
| quality: None, |
| }; |
|
|
| |
| |
| let result = transform_claude_request_in(&req, "test-project", false, None, "test_session", None); |
| assert!(result.is_ok(), "First thinking request should be allowed"); |
|
|
| let body = result.unwrap(); |
| let request = &body["request"]; |
| |
| |
| let has_thinking_config = request.get("generationConfig") |
| .and_then(|g| g.get("thinkingConfig")) |
| .is_some(); |
| |
| assert!(has_thinking_config, "Thinking config should be preserved for first request without tool calls"); |
| } |
|
|
| |
| |
| |
| |
| #[test] |
| fn test_tool_loop_recovery() { |
| |
| |
| |
| let mut messages = vec![ |
| Message { |
| role: "user".to_string(), |
| content: MessageContent::String("Check weather".to_string()), |
| }, |
| Message { |
| role: "assistant".to_string(), |
| content: MessageContent::Array(vec![ |
| |
| ContentBlock::ToolUse { |
| id: "call_1".to_string(), |
| name: "get_weather".to_string(), |
| input: json!({"location": "Beijing"}), |
| signature: None, |
| cache_control: None, |
| } |
| ]), |
| }, |
| Message { |
| role: "user".to_string(), |
| content: MessageContent::Array(vec![ |
| ContentBlock::ToolResult { |
| tool_use_id: "call_1".to_string(), |
| content: json!("Sunny"), |
| is_error: None, |
| } |
| ]), |
| } |
| ]; |
|
|
| |
| let state = analyze_conversation_state(&messages); |
| assert!(state.in_tool_loop, "Should detect tool loop"); |
|
|
| |
| close_tool_loop_for_thinking(&mut messages); |
|
|
| |
| assert_eq!(messages.len(), 5, "Should have injected 2 synthetic messages"); |
| |
| |
| let injected_assistant = &messages[3]; |
| assert_eq!(injected_assistant.role, "assistant"); |
| |
| |
| let injected_user = &messages[4]; |
| assert_eq!(injected_user.role, "user"); |
| |
| |
| let new_state = analyze_conversation_state(&messages); |
| assert!(!new_state.in_tool_loop, "Tool loop should be broken/closed"); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| } |
|
|