harmony-protocol 0.1.0

Reverse-engineered OpenAI Harmony response format library for structured conversation handling
Documentation
//! Channel management example for the Harmony Response Format library
//!
//! โš ๏ธ IMPORTANT: This library does NOT include an AI model.
//! This example shows how to structure multi-channel conversations, but the
//! actual intelligence comes from OpenAI's model processing these formatted tokens.
//!
//! This example demonstrates:
//! - Multi-channel conversations (analysis, commentary, final)
//! - Analysis dropping behavior for efficiency
//! - Channel configuration and routing
//! - Structured reasoning workflows (formatting only)

use harmony_response::{
    chat::{Conversation, Message, Role, SystemContent, ReasoningEffort},
    encoding::RenderConversationConfig,
    load_harmony_encoding, HarmonyEncodingName,
};

fn main() -> anyhow::Result<()> {
    println!("๐Ÿ“บ Harmony Response Format - Channel Management Example");
    println!("=======================================================");

    // Step 1: Create system with channel configuration
    println!("\nโš™๏ธ  Step 1: Setting up channel configuration");

    let system_content = SystemContent::new()
        .with_model_identity("Channel Management Demo Assistant")
        .with_reasoning_effort(ReasoningEffort::High)
        .with_required_channels(["analysis", "commentary", "final"])
        .with_browser_tool()
        .with_conversation_start_date("2024-01-15");

    println!("โœ“ Configured system with required channels: analysis, commentary, final");
    println!("โœ“ Set reasoning effort to HIGH for detailed analysis");

    // Step 2: Create multi-channel conversation
    println!("\n๐Ÿ’ฌ Step 2: Creating multi-channel conversation");

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

        Message::from_role_and_content(
            Role::User,
            "I'm trying to decide between two job offers. One pays more but has longer hours, \
            the other has better work-life balance but lower salary. How should I approach this decision?"
        ),

        // Assistant analysis (internal reasoning)
        Message::from_role_and_content(
            Role::Assistant,
            "This is a complex decision involving multiple factors: compensation, work-life balance, \
            career growth potential, personal values, and life circumstances. I need to provide a \
            structured approach that helps them evaluate both options objectively."
        ).with_channel("analysis"),

        Message::from_role_and_content(
            Role::Assistant,
            "I should consider both quantitative factors (salary difference, hours) and qualitative \
            factors (job satisfaction, stress levels, career development). A decision framework \
            would be most helpful here."
        ).with_channel("analysis"),

        // Assistant commentary (meta-reasoning)
        Message::from_role_and_content(
            Role::Assistant,
            "The user is facing a classic trade-off dilemma. Rather than telling them what to choose, \
            I should provide a systematic approach they can use to make the decision themselves, \
            taking into account their personal priorities and circumstances."
        ).with_channel("commentary"),

        Message::from_role_and_content(
            Role::Assistant,
            "I'll structure my response to include: a decision framework, key questions to consider, \
            and a practical evaluation method. This empowers them to make an informed choice."
        ).with_channel("commentary"),

        // Assistant final response (user-facing)
        Message::from_role_and_content(
            Role::Assistant,
            "This is a great question that many people face! Here's a structured approach to help you make this decision:\n\n\
            **1. Quantify the Trade-offs**\n\
            - Calculate the actual hourly rate for both jobs (salary รท total hours worked)\n\
            - Consider the long-term financial impact over 2-5 years\n\
            - Factor in benefits, bonuses, and growth potential\n\n\
            **2. Assess Personal Priorities**\n\
            - How important is work-life balance to you right now?\n\
            - Do you have family commitments or personal goals requiring time?\n\
            - What are your career aspirations in the next 5 years?\n\n\
            **3. Evaluate the Roles**\n\
            - Which role offers better learning opportunities?\n\
            - What are the company cultures like?\n\
            - Which position aligns better with your long-term goals?\n\n\
            **4. Create a Decision Matrix**\n\
            Score each job (1-10) on factors important to you: salary, hours, growth, culture, etc. \
            Weight each factor by importance to you personally.\n\n\
            Remember, there's no universally 'right' choice - only what's right for your current situation and goals."
        ).with_channel("final"),

        // User follow-up
        Message::from_role_and_content(
            Role::User,
            "That's helpful! Can you also help me think about how to negotiate with either company?"
        ),

        // More analysis
        Message::from_role_and_content(
            Role::Assistant,
            "Now they're asking about negotiation strategies. This requires understanding leverage, \
            timing, and what's typically negotiable. I should provide practical, actionable advice \
            while being realistic about what's possible in different scenarios."
        ).with_channel("analysis"),

        // Final response to follow-up
        Message::from_role_and_content(
            Role::Assistant,
            "Absolutely! Here are key negotiation strategies:\n\n\
            **Timing is Everything**\n\
            - Wait for a written offer before negotiating\n\
            - Negotiate after acceptance but before starting\n\n\
            **What You Can Negotiate**\n\
            - Salary (most obvious)\n\
            - Start date (gives you time)\n\
            - Remote work options\n\
            - Professional development budget\n\
            - Additional vacation days\n\
            - Flexible hours\n\n\
            **How to Approach It**\n\
            - Express enthusiasm first: 'I'm excited about this opportunity...'\n\
            - Be specific: 'Based on my research and experience...'\n\
            - Provide rationale: market rates, unique skills, competing offers\n\
            - Be prepared to compromise\n\n\
            Having two offers gives you significant leverage - use it wisely!"
        ).with_channel("final"),
    ]);

    println!("โœ“ Created conversation with {} messages across multiple channels", conversation.messages.len());

    // Count messages by channel
    let mut channel_counts = std::collections::HashMap::new();
    for message in &conversation.messages {
        if let Some(channel) = &message.channel {
            *channel_counts.entry(channel.clone()).or_insert(0) += 1;
        } else {
            *channel_counts.entry("none".to_string()).or_insert(0) += 1;
        }
    }

    println!("   Channel distribution:");
    for (channel, count) in &channel_counts {
        println!("   - {}: {} messages", channel, count);
    }

    // Step 3: Try encoding with different configurations
    println!("\n๐ŸŽจ Step 3: Testing different rendering configurations");

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

            // Render without analysis dropping
            let config_keep_all = RenderConversationConfig { auto_drop_analysis: false };
            let tokens_with_all = encoding.render_conversation(&conversation, Some(&config_keep_all))?;
            println!("โœ“ Rendered with all channels: {} tokens", tokens_with_all.len());

            // Render with analysis dropping (default behavior)
            let config_drop_analysis = RenderConversationConfig { auto_drop_analysis: true };
            let tokens_dropped = encoding.render_conversation(&conversation, Some(&config_drop_analysis))?;
            println!("โœ“ Rendered with analysis dropping: {} tokens", tokens_dropped.len());

            let token_reduction = tokens_with_all.len() - tokens_dropped.len();
            if token_reduction > 0 {
                println!("๐Ÿ“‰ Analysis dropping saved {} tokens ({:.1}% reduction)",
                    token_reduction,
                    (token_reduction as f64 / tokens_with_all.len() as f64) * 100.0
                );
            } else {
                println!("๐Ÿ“Š No analysis dropping occurred (no final messages found)");
            }

            // Test completion rendering
            let completion_tokens = encoding
                .render_conversation_for_completion(&conversation, Role::Assistant, Some(&config_drop_analysis))?;
            println!("โœ“ Rendered for completion: {} tokens", completion_tokens.len());

            // Test training rendering
            let training_tokens = encoding
                .render_conversation_for_training(&conversation, Some(&config_drop_analysis))?;
            println!("โœ“ Rendered for training: {} tokens", training_tokens.len());

            println!("\n๐Ÿ“Š Step 4: Analyzing conversation structure");

            // Analyze message flow
            let mut message_flow = Vec::new();
            for (i, message) in conversation.messages.iter().enumerate() {
                let channel = message.channel.as_deref().unwrap_or("none");
                let role = message.author.role.as_str();
                message_flow.push(format!("{}. {} ({})", i + 1, role, channel));
            }

            println!("Message flow:");
            for flow_item in message_flow {
                println!("   {}", flow_item);
            }

            println!("\n๐ŸŽ‰ Channel management example completed successfully!");

        }
        Err(e) => {
            println!("โš ๏ธ  Encoding load failed (expected without network access)");
            println!("   Error: {}", e);
            println!("   Channel structure created successfully!");

            // Show what we would have analyzed
            println!("\n๐Ÿ“Š Channel analysis (without encoding):");
            for (channel, count) in &channel_counts {
                println!("   - {} channel: {} messages", channel, count);
            }

            let analysis_messages = conversation.messages.iter()
                .filter(|msg| msg.channel.as_deref() == Some("analysis"))
                .count();

            let final_messages = conversation.messages.iter()
                .filter(|msg| msg.channel.as_deref() == Some("final"))
                .count();

            if analysis_messages > 0 && final_messages > 0 {
                println!("   Analysis dropping would remove {} analysis messages", analysis_messages);
            }
        }
    }

    Ok(())
}