mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-05 02:41:45 +00:00
## Summary This PR separates provider identity from adapter behavior across the LLM stack. Provider IDs now represent catalog rows and provider metadata, while adapter/profile routing owns protocol behavior for Anthropic, OpenAI, Gemini, and OpenAI-compatible providers. ## Changes - Replace the shared `fabro_model::Provider` enum with open-ended `ProviderId` catalog identity and typed `AdapterKind` metadata. - Route auth, CLI, ACP, workflow, memory selection, profile construction, and LLM client registration through catalog provider rows instead of provider-ID fallbacks. - Move API-key URL/header/env metadata into provider catalog/auth flows and require configured provider rows for credential-backed clients. - Simplify billing to `algorithm`-tagged OpenAI, Anthropic, and Gemini shapes; OpenAI-compatible adapters bill through the OpenAI algorithm. - Remove greenfield compatibility paths for old provider aliases, legacy provider-tagged billing JSON, and the `openai_compatible` pseudo-provider env fallback. - Update fixtures and tests to exercise catalog-driven Kimi/Zai/Minimax/Inception/custom OpenAI-compatible routing. ## Validation - `cargo test --no-run -p fabro-model -p fabro-auth -p fabro-agent -p fabro-workflow -p fabro-server -p fabro-llm -p fabro-api -p fabro-cli -p fabro-store -p fabro-static` - `cargo nextest run -p fabro-model -p fabro-auth -p fabro-agent -p fabro-workflow -p fabro-server --no-fail-fast` - `cargo nextest run -p fabro-llm -p fabro-api -p fabro-cli -p fabro-store -p fabro-static --no-fail-fast` - `cargo +nightly-2026-04-14 fmt --check --all` - `git diff --check` - `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` --- [](https://github.com/EveryInc/compound-engineering-plugin) 🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
91 lines
2.4 KiB
Rust
91 lines
2.4 KiB
Rust
use fabro_graphviz::graph::Graph;
|
|
use fabro_graphviz::parser;
|
|
use fabro_model::{Catalog, ProviderId};
|
|
use fabro_types::WorkflowSettings;
|
|
use fabro_types::settings::InterpString;
|
|
use fabro_types::settings::run::{PullRequestSettings, RunGoal, RunModelSettings, RunNamespace};
|
|
use fabro_workflow::run_materialization::materialize_run;
|
|
|
|
fn graph(source: &str) -> Graph {
|
|
parser::parse(source).expect("graph should parse")
|
|
}
|
|
|
|
#[test]
|
|
fn materialize_run_applies_graph_and_catalog_defaults() {
|
|
let source = r#"digraph Test {
|
|
graph [goal="Build feature"]
|
|
start [shape=Mdiamond]
|
|
exit [shape=Msquare]
|
|
start -> exit
|
|
}"#;
|
|
|
|
let settings = WorkflowSettings {
|
|
run: RunNamespace {
|
|
model: RunModelSettings {
|
|
name: Some(InterpString::parse("sonnet")),
|
|
..RunModelSettings::default()
|
|
},
|
|
pull_request: Some(PullRequestSettings {
|
|
enabled: false,
|
|
..PullRequestSettings::default()
|
|
}),
|
|
..RunNamespace::default()
|
|
},
|
|
..WorkflowSettings::default()
|
|
};
|
|
|
|
let materialized = materialize_run(settings, &graph(source), Catalog::builtin(), &[]);
|
|
let resolved = &materialized.run;
|
|
|
|
assert_eq!(
|
|
resolved
|
|
.model
|
|
.name
|
|
.as_ref()
|
|
.map(InterpString::as_source)
|
|
.as_deref(),
|
|
Some("claude-sonnet-4-6")
|
|
);
|
|
assert_eq!(
|
|
resolved
|
|
.model
|
|
.provider
|
|
.as_ref()
|
|
.map(InterpString::as_source)
|
|
.as_deref(),
|
|
Some("anthropic")
|
|
);
|
|
assert_eq!(
|
|
materialized.run.goal.as_ref(),
|
|
Some(&RunGoal::Inline(InterpString::parse("Build feature")))
|
|
);
|
|
assert!(resolved.pull_request.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn materialize_run_uses_configured_provider_defaults() {
|
|
let source = r#"digraph Test {
|
|
graph [goal="Build feature"]
|
|
start [shape=Mdiamond]
|
|
exit [shape=Msquare]
|
|
start -> exit
|
|
}"#;
|
|
|
|
let materialized = materialize_run(
|
|
WorkflowSettings::default(),
|
|
&graph(source),
|
|
Catalog::builtin(),
|
|
&[ProviderId::openai()],
|
|
);
|
|
let resolved = &materialized.run;
|
|
|
|
assert_eq!(
|
|
resolved
|
|
.model
|
|
.provider
|
|
.as_ref()
|
|
.map(InterpString::as_source)
|
|
.as_deref(),
|
|
Some("openai")
|
|
);
|
|
}
|