theway-core 0.1.22

theway core — stateful agent runtime + harness (Agent loop, skills, prompt templates, sessions, compaction) on top of theway-llm-provider.
Documentation
//! `resolve_run_model`: explicit provider/model catalog resolution, the legacy
//! model-only id rewrite, and the no-override parent fallback.

use crate::multiagent::runner::{resolve_run_model, subagent_model_not_found_message};
use theway_llm_provider::{
    Api, InputModality, Model, ModelCost, Provider, register_custom_model,
    unregister_custom_model,
};

fn model(provider: &str, id: &str, base_url: &str) -> Model {
    Model {
        id: id.into(),
        name: format!("{provider} {id}"),
        api: Api::from("faux"),
        provider: Provider::from(provider),
        base_url: base_url.into(),
        reasoning: false,
        thinking_level_map: None,
        input: vec![InputModality::Text],
        cost: ModelCost::default(),
        context_window: 0,
        max_tokens: 0,
        headers: None,
        compat: None,
    }
}

#[test]
fn explicit_pair_resolves_from_catalog_without_parent() {
    let provider = "test-resolve-provider";
    let id = "test-resolve-model";
    register_custom_model(model(provider, id, "http://catalog.local/v1"));

    let resolved = resolve_run_model(None, Some(provider), Some(id))
        .expect("catalog pair must resolve")
        .expect("explicit pair resolves without a parent model");
    assert_eq!(resolved.provider.0, provider);
    assert_eq!(resolved.id, id);
    assert_eq!(resolved.base_url, "http://catalog.local/v1");

    unregister_custom_model(&Provider::from(provider), id);
}

#[test]
fn unknown_pair_errors_with_catalog_hint() {
    let err = resolve_run_model(None, Some("test-no-such-provider"), Some("x"))
        .expect_err("unknown provider must fail");
    assert!(
        err.contains("model provider not found in catalog"),
        "{err}"
    );
    assert!(err.contains("test-no-such-provider"), "{err}");
}

#[test]
fn provider_without_model_errors() {
    let err = resolve_run_model(Some(&model("p", "m", "u")), Some("p"), None)
        .expect_err("provider-only override must fail");
    assert!(err.contains("provider override requires a model override"), "{err}");
}

#[test]
fn model_only_uses_catalog_entry_on_the_parent_provider() {
    let parent = model("test-rewrite-provider", "parent-id", "http://parent.local/v1");
    register_custom_model(model(
        "test-rewrite-provider",
        "catalog-id",
        "http://catalog.local/v1",
    ));

    let resolved = resolve_run_model(Some(&parent), None, Some("catalog-id"))
        .expect("model-only override must resolve")
        .expect("parent is present");
    assert_eq!(resolved.provider.0, "test-rewrite-provider");
    assert_eq!(resolved.id, "catalog-id");
    assert_eq!(resolved.base_url, "http://catalog.local/v1");

    unregister_custom_model(
        &Provider::from("test-rewrite-provider"),
        "catalog-id",
    );
}

#[test]
fn model_only_unknown_id_rewrites_the_parent_descriptor() {
    let parent = model("test-rewrite-provider", "parent-id", "http://parent.local/v1");
    let resolved = resolve_run_model(Some(&parent), None, Some("unknown-id"))
        .expect("legacy rewrite must not fail")
        .expect("parent is present");
    assert_eq!(resolved.provider.0, "test-rewrite-provider");
    assert_eq!(resolved.id, "unknown-id");
    assert_eq!(resolved.base_url, "http://parent.local/v1");
}

#[test]
fn model_only_without_parent_returns_none() {
    assert!(resolve_run_model(None, None, Some("any-id"))
        .expect("no explicit pair must not error")
        .is_none());
}

#[test]
fn same_id_returns_the_parent_model() {
    let parent = model("test-rewrite-provider", "same-id", "http://parent.local/v1");
    let resolved = resolve_run_model(Some(&parent), None, Some("same-id"))
        .unwrap()
        .unwrap();
    assert_eq!(resolved.provider.0, parent.provider.0);
    assert_eq!(resolved.id, parent.id);
    assert_eq!(resolved.base_url, parent.base_url);
}

#[test]
fn no_overrides_return_the_parent_model() {
    let parent = model("test-rewrite-provider", "parent-id", "http://parent.local/v1");
    let resolved = resolve_run_model(Some(&parent), None, None)
        .unwrap()
        .expect("parent is present");
    assert_eq!(resolved.provider.0, parent.provider.0);
    assert_eq!(resolved.id, parent.id);
    assert_eq!(resolved.base_url, parent.base_url);
    assert!(resolve_run_model(None, None, None)
        .expect("no overrides must not error")
        .is_none());
}

#[test]
fn model_not_found_message_for_known_provider_lists_candidates() {
    let known = theway_llm_provider::list_models();
    let Some(first) = known.first() else {
        return;
    };
    let provider = first.provider.0.clone();

    let message = subagent_model_not_found_message(&provider, "no-such-model-id");

    assert!(message.contains("model not found in catalog"), "{message}");
    assert!(message.contains(&provider), "{message}");
}

#[test]
fn model_not_found_message_for_small_provider_lists_candidates_without_more_hint() {
    let provider = "test-small-provider";
    register_custom_model(model(provider, "only-model", "http://catalog.local/v1"));

    let message = subagent_model_not_found_message(provider, "missing-id");

    assert!(message.contains("model not found in catalog"), "{message}");
    assert!(message.contains("only-model"), "{message}");
    assert!(!message.contains("inside theway for all"), "{message}");

    unregister_custom_model(&Provider::from(provider), "only-model");
}

#[test]
fn model_not_found_message_for_provider_with_many_models_adds_more_hint() {
    let provider = "test-many-provider";
    for i in 0..13 {
        register_custom_model(model(
            provider,
            &format!("model-{i}"),
            "http://catalog.local/v1",
        ));
    }

    let message = subagent_model_not_found_message(provider, "missing-id");

    assert!(message.contains("model not found in catalog"), "{message}");
    assert!(message.contains("inside theway for all 13 models"), "{message}");

    for i in 0..13 {
        unregister_custom_model(&Provider::from(provider), &format!("model-{i}"));
    }
}