rho-coding-agent 0.28.1

A lightweight agent harness inspired by Pi
Documentation
use super::*;
use pretty_assertions::assert_eq;
use serde_json::json;

#[test]
fn parses_reasoning_effort_options() {
    let api = serde_json::json!({
        "openai": {
            "models": {
                "gpt-test": {
                    "reasoning": true,
                    "reasoning_options": [{
                        "type": "effort",
                        "values": ["none", "low", "high", "xhigh"]
                    }]
                }
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "openai", "gpt-test").unwrap();

    assert_eq!(
        metadata.reasoning_off_behavior,
        ReasoningOffBehavior::EffortNone
    );
    assert_eq!(metadata.reasoning_effort(ReasoningLevel::Off), Some("none"));
    assert!(metadata.reasoning_capabilities_known);
    assert_eq!(
        metadata.supported_reasoning_levels,
        Some(vec![
            ReasoningLevel::Off,
            ReasoningLevel::Low,
            ReasoningLevel::High,
            ReasoningLevel::Xhigh,
        ])
    );
}

#[test]
fn effort_options_without_none_still_support_off_by_omission() {
    let api = serde_json::json!({
        "openai": {
            "models": {
                "gpt-test": {
                    "reasoning": true,
                    "reasoning_options": [{
                        "type": "effort",
                        "values": ["low", "medium", "high", "xhigh"]
                    }]
                }
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "openai", "gpt-test").unwrap();

    assert_eq!(metadata.reasoning_off_behavior, ReasoningOffBehavior::Omit);
    assert_eq!(metadata.reasoning_effort(ReasoningLevel::Off), None);
    assert!(metadata.reasoning_capabilities_known);
    assert_eq!(
        metadata.supported_reasoning_levels,
        Some(vec![
            ReasoningLevel::Off,
            ReasoningLevel::Low,
            ReasoningLevel::Medium,
            ReasoningLevel::High,
            ReasoningLevel::Xhigh,
        ])
    );
}

#[test]
fn unknown_effort_values_do_not_restrict_reasoning() {
    let api = serde_json::json!({
        "openai": {
            "models": {
                "gpt-test": {
                    "reasoning": true,
                    "reasoning_options": [{"type": "effort", "values": ["default"]}]
                }
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "openai", "gpt-test").unwrap();

    assert!(metadata.reasoning_capabilities_known);
    assert_eq!(metadata.supported_reasoning_levels, None);
}

#[test]
fn models_without_effort_choices_only_expose_off() {
    let api = serde_json::json!({
        "openai": {
            "models": {
                "gpt-test": {"reasoning": true, "reasoning_options": []}
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "openai", "gpt-test").unwrap();

    assert!(metadata.reasoning_capabilities_known);
    assert_eq!(
        metadata.supported_reasoning_levels,
        Some(vec![ReasoningLevel::Off])
    );
}

#[test]
fn leaves_unknown_reasoning_option_schemas_unrestricted() {
    let api = serde_json::json!({
        "anthropic": {
            "models": {
                "claude-test": {
                    "reasoning": true,
                    "reasoning_options": [{"type": "budget_tokens", "min": 1024}]
                }
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "anthropic", "claude-test").unwrap();

    assert!(metadata.reasoning_capabilities_known);
    assert_eq!(metadata.supported_reasoning_levels, None);
}

#[test]
fn non_reasoning_models_only_support_off() {
    let api = serde_json::json!({
        "openai": {"models": {"gpt-test": {"reasoning": false}}}
    });

    let metadata = model_metadata_from_api(&api, "openai", "gpt-test").unwrap();

    assert!(metadata.reasoning_capabilities_known);
    assert_eq!(
        metadata.supported_reasoning_levels,
        Some(vec![ReasoningLevel::Off])
    );
}

#[test]
fn reasoning_models_without_options_are_not_capability_complete() {
    let api = json!({
        "xai": {
            "models": {
                "grok-4.5": {
                    "reasoning": true,
                    "limit": { "context": 500000, "output": 500000 }
                }
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "xai", "grok-4.5").unwrap();

    assert!(!metadata.reasoning_capabilities_known);
    assert_eq!(metadata.supported_reasoning_levels, None);
}

#[test]
fn builtin_gpt_56_codex_overrides_use_temporary_context_limit() {
    for model in ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"] {
        let metadata = apply_builtin_overrides("openai-codex", model, ModelMetadata::default());

        assert_eq!(metadata.effective_context_window, Some(272_000));
        assert_eq!(metadata.usable_context_window, Some(272_000));
        assert_eq!(metadata.display_context_window(), Some(272_000));
    }
}

#[test]
fn builtin_gpt_55_overrides_use_safer_effective_windows() {
    let upstream = ModelMetadata {
        advertised_context_window: Some(1_050_000),
        effective_context_window: Some(922_000),
        max_output_tokens: Some(128_000),
        cost_default: Some(ModelCost {
            input_micros_per_m: Some(5_000_000),
            output_micros_per_m: Some(30_000_000),
            cache_read_micros_per_m: Some(500_000),
            cache_write_micros_per_m: None,
        }),
        ..ModelMetadata::default()
    };
    let openai = apply_builtin_overrides("openai", "gpt-5.5", upstream.clone());
    let codex = apply_builtin_overrides("openai-codex", "gpt-5.5", upstream);

    assert_eq!(openai.display_context_window(), Some(272_000));
    assert_eq!(openai.effective_context_window, Some(922_000));
    assert_eq!(codex.display_context_window(), Some(272_000));
    assert_eq!(codex.effective_context_window, Some(400_000));
    assert_eq!(codex.advertised_context_window, Some(1_050_000));
    assert_eq!(codex.long_context_threshold, Some(272_000));
    assert_eq!(codex.max_output_tokens, Some(128_000));
    assert_eq!(
        codex.cost_default.unwrap().input_micros_per_m,
        Some(5_000_000)
    );
}

#[test]
fn models_dev_parses_long_context_cost_tiers() {
    let api = json!({
        "xai": {
            "models": {
                "grok-4.5": {
                    "reasoning": true,
                    "reasoning_options": [{
                        "type": "effort",
                        "values": ["low", "medium", "high"]
                    }],
                    "limit": { "context": 500000, "output": 500000 },
                    "cost": {
                        "input": 2.0,
                        "output": 6.0,
                        "cache_read": 0.5,
                        "tiers": [{
                            "input": 4.0,
                            "output": 12.0,
                            "cache_read": 1.0,
                            "tier": { "type": "context", "size": 200000 }
                        }],
                        "context_over_200k": {
                            "input": 4.0,
                            "output": 12.0,
                            "cache_read": 1.0
                        }
                    }
                }
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "xai", "grok-4.5").unwrap();

    assert_eq!(
        metadata,
        ModelMetadata {
            advertised_context_window: Some(500_000),
            effective_context_window: Some(500_000),
            usable_context_window: None,
            long_context_threshold: Some(200_000),
            max_output_tokens: Some(500_000),
            cost_default: Some(ModelCost {
                input_micros_per_m: Some(2_000_000),
                output_micros_per_m: Some(6_000_000),
                cache_read_micros_per_m: Some(500_000),
                cache_write_micros_per_m: None,
            }),
            cost_long_context: Some(ModelCost {
                input_micros_per_m: Some(4_000_000),
                output_micros_per_m: Some(12_000_000),
                cache_read_micros_per_m: Some(1_000_000),
                cache_write_micros_per_m: None,
            }),
            supported_reasoning_levels: Some(vec![
                ReasoningLevel::Off,
                ReasoningLevel::Low,
                ReasoningLevel::Medium,
                ReasoningLevel::High,
            ]),
            reasoning_off_behavior: ReasoningOffBehavior::Omit,
            reasoning_capabilities_known: true,
        }
    );
    assert_eq!(
        metadata
            .cost_for_input_tokens(200_001)
            .unwrap()
            .input_micros_per_m,
        Some(4_000_000)
    );
    assert_eq!(
        metadata
            .cost_for_input_tokens(200_000)
            .unwrap()
            .input_micros_per_m,
        Some(2_000_000)
    );
}

#[test]
fn rehydrates_when_cache_version_is_stale_or_capabilities_are_unknown() {
    let complete = ModelMetadata {
        supported_reasoning_levels: Some(vec![
            ReasoningLevel::Off,
            ReasoningLevel::Low,
            ReasoningLevel::Medium,
            ReasoningLevel::High,
        ]),
        reasoning_capabilities_known: true,
        ..ModelMetadata::default()
    };
    let missing_flag = ModelMetadata {
        supported_reasoning_levels: Some(vec![ReasoningLevel::Off, ReasoningLevel::High]),
        reasoning_capabilities_known: false,
        ..ModelMetadata::default()
    };
    let intentional_unrestricted = ModelMetadata {
        supported_reasoning_levels: None,
        reasoning_capabilities_known: true,
        ..ModelMetadata::default()
    };
    let sealed_null_without_flag = ModelMetadata {
        supported_reasoning_levels: None,
        reasoning_capabilities_known: false,
        ..ModelMetadata::default()
    };

    assert!(should_rehydrate_cached_metadata(1, &complete));
    assert!(should_rehydrate_cached_metadata(
        MODEL_METADATA_CACHE_VERSION,
        &missing_flag
    ));
    assert!(should_rehydrate_cached_metadata(
        MODEL_METADATA_CACHE_VERSION,
        &sealed_null_without_flag
    ));
    assert!(!should_rehydrate_cached_metadata(
        MODEL_METADATA_CACHE_VERSION,
        &complete
    ));
    // Intentional unrestricted schemes (budget tokens, unknown efforts) seal
    // with known=true and levels=None; those must not thrash on rehydrate.
    assert!(!should_rehydrate_cached_metadata(
        MODEL_METADATA_CACHE_VERSION,
        &intentional_unrestricted
    ));
}

#[test]
fn codex_models_skip_minimal_when_models_dev_omits_it() {
    let api = json!({
        "openai": {
            "models": {
                "gpt-5.3-codex": {
                    "reasoning": true,
                    "reasoning_options": [{
                        "type": "effort",
                        "values": ["none", "low", "medium", "high", "xhigh"]
                    }]
                }
            }
        }
    });

    let metadata = model_metadata_from_api(&api, "openai", "gpt-5.3-codex").unwrap();

    assert!(metadata.reasoning_capabilities_known);
    assert_eq!(
        metadata.supported_reasoning_levels,
        Some(vec![
            ReasoningLevel::Off,
            ReasoningLevel::Low,
            ReasoningLevel::Medium,
            ReasoningLevel::High,
            ReasoningLevel::Xhigh,
        ])
    );
    assert_eq!(
        metadata.reasoning_off_behavior,
        ReasoningOffBehavior::EffortNone
    );
}