harmony-protocol 0.1.0

Reverse-engineered OpenAI Harmony response format library for structured conversation handling
Documentation
//! Tool integration example for the Harmony Response Format library
//!
//! ⚠️ IMPORTANT: This library does NOT include an AI model.
//! This example shows how to FORMAT conversations with tools, but you still need
//! to send the formatted tokens to OpenAI's API and receive responses.
//!
//! This example demonstrates:
//! - Creating custom tools with JSON schemas
//! - Using built-in browser and Python tools
//! - Tool calling with recipients
//! - Developer instructions
//! - Complete tool conversation workflow (formatting only)

use harmony_response::{
    chat::{
        Conversation, DeveloperContent, Message, Role, SystemContent,
        ToolDescription, ToolNamespaceConfig
    },
    load_harmony_encoding, HarmonyEncodingName,
};

fn main() -> anyhow::Result<()> {
    println!("🔧 Harmony Response Format - Tool Integration Example");
    println!("=====================================================");

    // Step 1: Create custom tools
    println!("\n🛠️  Step 1: Creating custom tools");

    let weather_tool = ToolDescription::new(
        "get_weather",
        "Gets current weather information for a specified location",
        Some(serde_json::json!({
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "default": "fahrenheit",
                    "description": "Temperature units"
                },
                "include_forecast": {
                    "type": "boolean",
                    "default": false,
                    "description": "Include 5-day forecast"
                }
            },
            "required": ["location"]
        }))
    );

    let calculator_tool = ToolDescription::new(
        "calculate",
        "Performs mathematical calculations with high precision",
        Some(serde_json::json!({
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "Mathematical expression to evaluate"
                },
                "precision": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 15,
                    "default": 2,
                    "description": "Decimal precision for results"
                }
            },
            "required": ["expression"]
        }))
    );

    println!("✓ Created weather tool with location and units parameters");
    println!("✓ Created calculator tool with expression and precision parameters");

    // Step 2: Set up tool namespaces
    println!("\n📦 Step 2: Setting up tool namespaces");

    let custom_functions = ToolNamespaceConfig::new(
        "functions",
        Some("Custom function tools for weather and calculations".to_string()),
        vec![weather_tool, calculator_tool]
    );

    let browser_tools = ToolNamespaceConfig::browser();
    let python_tools = ToolNamespaceConfig::python();

    println!("✓ Created custom functions namespace with {} tools", custom_functions.tools.len());
    println!("✓ Created browser namespace with {} tools", browser_tools.tools.len());
    println!("✓ Created Python namespace");

    // Step 3: Create system content with tools
    println!("\n⚙️  Step 3: Creating system content with tools");

    let system_content = SystemContent::new()
        .with_model_identity("Tool-Enhanced Assistant")
        .with_required_channels(["analysis", "commentary", "final"])
        .with_browser_tool()
        .with_python_tool()
        .with_tools(custom_functions)
        .with_conversation_start_date("2024-01-15")
        .with_knowledge_cutoff("2024-06");

    println!("✓ System content configured with multiple tool namespaces");

    // Step 4: Create developer instructions
    println!("\n👩‍💻 Step 4: Creating developer instructions");

    let dev_content = DeveloperContent::new()
        .with_instructions(
            "You are a helpful assistant with access to weather, calculation, browser, and Python tools. \
            Use the analysis channel to reason about which tools to use, \
            the commentary channel to explain your process, and the final channel for user responses. \
            Always be precise and cite your sources when using browser tools."
        );

    println!("✓ Created developer instructions with tool usage guidelines");

    // Step 5: Create conversation with tool usage
    println!("\n🗣️  Step 5: Creating conversation with tool interactions");

    let conversation = Conversation::from_messages([
        Message::from_role_and_content(Role::System, system_content),
        Message::from_role_and_content(Role::Developer, dev_content),

        Message::from_role_and_content(
            Role::User,
            "What's the weather in San Francisco, and can you calculate what 15% tip would be on a $47.50 dinner bill?"
        ),

        Message::from_role_and_content(
            Role::Assistant,
            "I need to use both the weather and calculator tools to answer this question."
        ).with_channel("analysis"),

        Message::from_role_and_content(
            Role::Assistant,
            "First, I'll get the weather for San Francisco, then calculate the tip."
        ).with_channel("commentary"),

        Message::from_role_and_content(
            Role::Assistant,
            r#"{"tool": "get_weather", "parameters": {"location": "San Francisco, CA", "units": "fahrenheit"}}"#
        ).with_recipient("functions")
         .with_channel("commentary"),

        Message::from_role_and_content(
            Role::Tool,
            r#"{"weather": "Partly cloudy, 68°F", "humidity": "65%", "wind": "8 mph W"}"#
        ).with_recipient("assistant"),

        Message::from_role_and_content(
            Role::Assistant,
            r#"{"tool": "calculate", "parameters": {"expression": "47.50 * 0.15", "precision": 2}}"#
        ).with_recipient("functions")
         .with_channel("commentary"),

        Message::from_role_and_content(
            Role::Tool,
            r#"{"result": 7.13}"#
        ).with_recipient("assistant"),

        Message::from_role_and_content(
            Role::Assistant,
            "The weather in San Francisco is partly cloudy at 68°F with 65% humidity and winds at 8 mph from the west. \
            For your dinner bill of $47.50, a 15% tip would be $7.13, making your total $54.63."
        ).with_channel("final"),
    ]);

    println!("✓ Created conversation with {} messages including tool calls", conversation.messages.len());

    // Step 6: Try to process with encoding
    println!("\n🎨 Step 6: Processing conversation");

    match load_harmony_encoding(HarmonyEncodingName::HarmonyGptOss) {
        Ok(encoding) => {
            println!("✅ Successfully loaded encoding");

            let tokens = encoding.render_conversation(&conversation, None)?;
            println!("✓ Rendered conversation with tools to {} tokens", tokens.len());

            let completion_tokens = encoding
                .render_conversation_for_completion(&conversation, Role::Assistant, None)?;
            println!("✓ Rendered for completion: {} tokens", completion_tokens.len());

            // Show tool usage statistics
            let tool_messages = conversation.messages.iter()
                .filter(|msg| msg.author.role == Role::Tool || msg.recipient.is_some())
                .count();
            println!("✓ Conversation contains {} tool-related messages", tool_messages);

            println!("\n🎉 Tool integration example completed successfully!");
        }
        Err(e) => {
            println!("⚠️  Encoding load failed (expected without network access)");
            println!("   Error: {}", e);
            println!("   Tool integration structure created successfully!");

            // Show what we created anyway
            let tool_messages = conversation.messages.iter()
                .filter(|msg| msg.author.role == Role::Tool || msg.recipient.is_some())
                .count();
            println!("✓ Created conversation with {} tool-related messages", tool_messages);
        }
    }

    Ok(())
}