mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-30 01:53:45 +00:00
## Summary This PR moves Fabro’s provider/model catalog toward settings-driven provider identity by replacing the closed provider schema at the API/auth/model boundary with `ProviderId`, then loading built-in provider and model metadata from embedded per-provider TOML files. The immediate result is that built-ins now use the same settings-shaped catalog data that custom providers will use later, while request-serving paths still keep the existing bootstrap/default catalog behavior until the resolved-catalog plumbing lands. ## Changes - Replaces API-facing provider enum usage with string-backed `ProviderId`, including OpenAPI/progenitor replacements and regenerated TypeScript client models. - Routes model, auth, billing, CLI, server, and workflow call sites through provider IDs where they cross product identity boundaries. - Builds `Catalog` from settings-shaped provider/model data with validation for adapter keys, OpenAI-compatible `base_url`, duplicate aliases, provider defaults, disabled entries, model controls, and per-speed cost rows. - Replaces `catalog.json` with embedded provider TOML files under `lib/crates/fabro-model/src/catalog/providers/`. - Adds an explicit `fabro_model::bootstrap_catalog` hatch for setup/install paths and extends the dev policy test to keep bootstrap access contained. - Preserves public training and knowledge-cutoff labels in LLM model settings while still accepting bare TOML dates. ## Verification - `cargo nextest run -p fabro-model -p fabro-config -p fabro-api` — 416 passed - `cargo nextest run -p fabro-dev --features dev bootstrap_catalog_references_stay_in_allowlist` — 1 passed - `cargo +nightly-2026-04-14 fmt --check --all` - `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` - `cargo build --workspace` - `git diff --check` --- [](https://github.com/EveryInc/compound-engineering-plugin) 🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
75 lines
2.1 KiB
Rust
75 lines
2.1 KiB
Rust
use fabro_graphviz::graph::Graph;
|
|
use fabro_model::{Catalog, ProviderId};
|
|
use fabro_types::WorkflowSettings;
|
|
use fabro_types::settings::InterpString;
|
|
use fabro_types::settings::run::RunGoal;
|
|
|
|
pub fn materialize_run(
|
|
mut settings: WorkflowSettings,
|
|
graph: &Graph,
|
|
catalog: &Catalog,
|
|
configured_providers: &[ProviderId],
|
|
) -> WorkflowSettings {
|
|
let configured_model = settings
|
|
.run
|
|
.model
|
|
.name
|
|
.as_ref()
|
|
.map(InterpString::as_source);
|
|
let configured_provider = settings
|
|
.run
|
|
.model
|
|
.provider
|
|
.as_ref()
|
|
.map(InterpString::as_source);
|
|
let graph_provider = graph
|
|
.attrs
|
|
.get("default_provider")
|
|
.and_then(|value| value.as_str())
|
|
.map(str::to_string);
|
|
let graph_model = graph
|
|
.attrs
|
|
.get("default_model")
|
|
.and_then(|value| value.as_str())
|
|
.map(str::to_string);
|
|
|
|
let provider = configured_provider.or(graph_provider);
|
|
let model = configured_model.or(graph_model).unwrap_or_else(|| {
|
|
provider
|
|
.as_deref()
|
|
.map(ProviderId::from)
|
|
.and_then(|provider| catalog.default_for_provider(&provider))
|
|
.unwrap_or_else(|| catalog.default_for_configured_ids(configured_providers))
|
|
.id
|
|
.clone()
|
|
});
|
|
|
|
let (resolved_model, resolved_provider) = match catalog.get(&model) {
|
|
Some(info) => (
|
|
info.id.clone(),
|
|
provider.or(Some(info.provider.to_string())),
|
|
),
|
|
None => (model, provider),
|
|
};
|
|
|
|
settings.run.model.name = Some(InterpString::parse(&resolved_model));
|
|
settings.run.model.provider = resolved_provider.as_deref().map(InterpString::parse);
|
|
|
|
let goal = graph.goal().to_string();
|
|
settings.run.goal = if goal.is_empty() {
|
|
None
|
|
} else {
|
|
Some(RunGoal::Inline(InterpString::parse(&goal)))
|
|
};
|
|
|
|
if settings
|
|
.run
|
|
.pull_request
|
|
.as_ref()
|
|
.is_some_and(|pull_request| !pull_request.enabled)
|
|
{
|
|
settings.run.pull_request = None;
|
|
}
|
|
|
|
settings
|
|
}
|