Files
galaxy/app/src/ai/openai/convert_tests.rs
T
rkw6086 dbfa8bcd48 Complete agent monitoring and Galaxy Control integration
- expose command-monitor conversations and preserve visible agent transcripts
- add bounded polling and a dedicated shell interrupt tool
- improve direct-provider images, skills, tool history, and usage handling
- package and brand Galaxy Control across releases, installers, persistence, and docs
2026-07-29 15:04:58 -05:00

308 lines
9.8 KiB
Rust

use serde_json::json;
use crate::ai::openai::convert::build_openai_request;
use crate::ai::provider::types::{
ContentPart, ConversationMessage, MessageContent, MessageRole, ToolDefinition,
};
#[test]
fn test_simple_text_message_conversion() {
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::Text("Hello world".to_string()),
}];
let request = build_openai_request(messages, None, vec![], 1024, None, "test-model");
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0]["role"], "user");
assert_eq!(msgs[0]["content"], "Hello world");
assert_eq!(request["model"], "test-model");
assert_eq!(request["max_tokens"], 1024);
assert_eq!(request["stream"], true);
}
#[test]
fn test_multimodal_user_message_uses_openai_image_url_content() {
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::MultiPart(vec![
ContentPart::Text("Describe this image".to_string()),
ContentPart::Image {
data: vec![1, 2, 3, 4],
mime_type: "image/png".to_string(),
},
]),
}];
let request = build_openai_request(messages, None, vec![], 1024, None, "test-model");
let content = request["messages"][0]["content"]
.as_array()
.expect("expected multimodal content array");
assert_eq!(
content,
&vec![
json!({
"type": "text",
"text": "Describe this image",
}),
json!({
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,AQIDBA==",
},
}),
]
);
}
#[test]
fn test_system_prompt_placement() {
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::Text("Hi".to_string()),
}];
let request = build_openai_request(
messages,
Some("You are a helpful assistant.".to_string()),
vec![],
1024,
None,
"test-model",
);
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 2);
assert_eq!(msgs[0]["role"], "system");
assert_eq!(msgs[0]["content"], "You are a helpful assistant.");
assert_eq!(msgs[1]["role"], "user");
}
#[test]
fn test_assistant_tool_use_conversion() {
let messages = vec![ConversationMessage {
role: MessageRole::Assistant,
content: MessageContent::ToolUse {
tool_use_id: "call_123".to_string(),
name: "run_shell_command".to_string(),
input: json!({"command": "ls -la"}),
},
}];
let request = build_openai_request(messages, None, vec![], 1024, None, "test-model");
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0]["role"], "assistant");
assert!(msgs[0]["content"].is_null());
let tool_calls = msgs[0]["tool_calls"].as_array().unwrap();
assert_eq!(tool_calls.len(), 1);
assert_eq!(tool_calls[0]["id"], "call_123");
assert_eq!(tool_calls[0]["type"], "function");
assert_eq!(tool_calls[0]["function"]["name"], "run_shell_command");
assert_eq!(
tool_calls[0]["function"]["arguments"],
json!({"command": "ls -la"}).to_string()
);
}
#[test]
fn test_tool_result_conversion() {
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::ToolResult {
tool_use_id: "call_123".to_string(),
content: "file1.txt\nfile2.txt".to_string(),
is_error: false,
},
}];
let request = build_openai_request(messages, None, vec![], 1024, None, "test-model");
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0]["role"], "tool");
assert_eq!(msgs[0]["tool_call_id"], "call_123");
assert_eq!(msgs[0]["content"], "file1.txt\nfile2.txt");
}
#[test]
fn test_tool_result_error_conversion() {
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::ToolResult {
tool_use_id: "call_456".to_string(),
content: "command not found".to_string(),
is_error: true,
},
}];
let request = build_openai_request(messages, None, vec![], 1024, None, "test-model");
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs[0]["role"], "tool");
assert_eq!(msgs[0]["content"], "[ERROR] command not found");
}
#[test]
fn test_multipart_assistant_message() {
let messages = vec![ConversationMessage {
role: MessageRole::Assistant,
content: MessageContent::MultiPart(vec![
ContentPart::Text("I'll run that command for you.".to_string()),
ContentPart::ToolUse {
tool_use_id: "call_abc".to_string(),
name: "run_shell_command".to_string(),
input: json!({"command": "pwd"}),
},
]),
}];
let request = build_openai_request(messages, None, vec![], 1024, None, "test-model");
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0]["role"], "assistant");
assert_eq!(msgs[0]["content"], "I'll run that command for you.");
let tool_calls = msgs[0]["tool_calls"].as_array().unwrap();
assert_eq!(tool_calls.len(), 1);
assert_eq!(tool_calls[0]["id"], "call_abc");
assert_eq!(tool_calls[0]["function"]["name"], "run_shell_command");
}
#[test]
fn test_multipart_user_message_with_tool_results() {
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::MultiPart(vec![
ContentPart::ToolResult {
tool_use_id: "call_1".to_string(),
content: "result 1".to_string(),
is_error: false,
},
ContentPart::ToolResult {
tool_use_id: "call_2".to_string(),
content: "result 2".to_string(),
is_error: false,
},
]),
}];
let request = build_openai_request(messages, None, vec![], 1024, None, "test-model");
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 2);
assert_eq!(msgs[0]["role"], "tool");
assert_eq!(msgs[0]["tool_call_id"], "call_1");
assert_eq!(msgs[1]["role"], "tool");
assert_eq!(msgs[1]["tool_call_id"], "call_2");
}
#[test]
fn test_tool_definitions_conversion() {
let tools = vec![
ToolDefinition {
name: "run_shell_command".to_string(),
description: "Runs a shell command".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"command": {"type": "string"}
},
"required": ["command"]
}),
},
ToolDefinition {
name: "read_files".to_string(),
description: "Reads files from disk".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"files": {"type": "array", "items": {"type": "string"}}
}
}),
},
];
let request = build_openai_request(vec![], None, tools, 1024, None, "test-model");
let tool_defs = request["tools"].as_array().unwrap();
assert_eq!(tool_defs.len(), 2);
assert_eq!(tool_defs[0]["type"], "function");
assert_eq!(tool_defs[0]["function"]["name"], "run_shell_command");
assert_eq!(
tool_defs[0]["function"]["description"],
"Runs a shell command"
);
assert_eq!(tool_defs[0]["function"]["parameters"]["type"], "object");
assert_eq!(tool_defs[1]["function"]["name"], "read_files");
}
#[test]
fn test_temperature_handling() {
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::Text("test".to_string()),
}];
// Temperature absent when None
let request = build_openai_request(messages.clone(), None, vec![], 1024, None, "test-model");
assert!(request.get("temperature").is_none());
// Temperature present when Some
let request = build_openai_request(messages, None, vec![], 1024, Some(0.7), "test-model");
assert!(request.get("temperature").is_some());
}
#[test]
fn test_full_conversation_roundtrip() {
let messages = vec![
ConversationMessage {
role: MessageRole::User,
content: MessageContent::Text("List files".to_string()),
},
ConversationMessage {
role: MessageRole::Assistant,
content: MessageContent::ToolUse {
tool_use_id: "call_1".to_string(),
name: "run_shell_command".to_string(),
input: json!({"command": "ls"}),
},
},
ConversationMessage {
role: MessageRole::User,
content: MessageContent::ToolResult {
tool_use_id: "call_1".to_string(),
content: "file1.rs\nfile2.rs".to_string(),
is_error: false,
},
},
ConversationMessage {
role: MessageRole::Assistant,
content: MessageContent::Text("Here are the files: file1.rs and file2.rs".to_string()),
},
];
let request = build_openai_request(
messages,
Some("You are Galaxy AI.".to_string()),
vec![],
4096,
None,
"claude-sonnet",
);
let msgs = request["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 5); // system + 4 conversation messages
assert_eq!(msgs[0]["role"], "system");
assert_eq!(msgs[1]["role"], "user");
assert_eq!(msgs[2]["role"], "assistant");
assert_eq!(msgs[3]["role"], "tool");
assert_eq!(msgs[4]["role"], "assistant");
}