mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-20 00:11:34 +00:00
The fallback provider set was always catalog.all_provider_ids(), computed at every call site and threaded through five layers alongside the catalog itself. Fold it into Catalog::resolve_selection_with_catalog_fallback and carry only a catalog_fallback flag through the transform/validate/ materialize entry points. - materialize_run delegates to resolve_run_model again instead of re-inlining its provider normalization and selection - run_preflight derives ready providers from llm_result instead of taking both, so callers cannot pass inconsistent pairs; the legacy tests now exercise the production ready-first routing path - AppState::resolve_llm_client_with_ready_ids replaces three copies of resolve-then-extract-provider-ids, and ready_llm_provider_ids delegates to it - the unreachable "model resolution failed" preflight check becomes an invariant error where the materialized run is produced - validate_prepared_manifest_with_vars/_for_preflight share the ValidateInput construction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
101 lines
3.1 KiB
Rust
101 lines
3.1 KiB
Rust
use std::collections::HashSet;
|
|
|
|
use fabro_graphviz::graph::Graph;
|
|
use fabro_model::{Catalog, ModelSelectionError, ProviderId};
|
|
use fabro_types::WorkflowSettings;
|
|
use fabro_types::settings::InterpString;
|
|
use fabro_types::settings::run::RunGoal;
|
|
|
|
use crate::error::Error;
|
|
|
|
pub fn materialize_run(
|
|
settings: WorkflowSettings,
|
|
graph: &Graph,
|
|
catalog: &Catalog,
|
|
configured_providers: &[ProviderId],
|
|
) -> Result<WorkflowSettings, Error> {
|
|
materialize_run_with_eligible_providers(settings, graph, catalog, configured_providers, false)
|
|
}
|
|
|
|
/// Materialize while resolving the run model against the ready providers
|
|
/// first, falling back to the full catalog only for provider-readiness
|
|
/// selection failures.
|
|
pub fn materialize_run_with_ready_providers(
|
|
settings: WorkflowSettings,
|
|
graph: &Graph,
|
|
catalog: &Catalog,
|
|
ready_providers: &[ProviderId],
|
|
) -> Result<WorkflowSettings, Error> {
|
|
materialize_run_with_eligible_providers(settings, graph, catalog, ready_providers, true)
|
|
}
|
|
|
|
fn materialize_run_with_eligible_providers(
|
|
mut settings: WorkflowSettings,
|
|
graph: &Graph,
|
|
catalog: &Catalog,
|
|
eligible_providers: &[ProviderId],
|
|
catalog_fallback: bool,
|
|
) -> Result<WorkflowSettings, Error> {
|
|
let configured_model = settings.run.model.name.take();
|
|
let configured_provider = settings.run.model.provider.take();
|
|
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);
|
|
let eligible = eligible_providers.iter().cloned().collect::<HashSet<_>>();
|
|
let (resolved_model, resolved_provider) = resolve_run_model(
|
|
catalog,
|
|
&eligible,
|
|
model.as_deref(),
|
|
provider.as_deref(),
|
|
catalog_fallback,
|
|
)?;
|
|
|
|
settings.run.model.name = Some(resolved_model);
|
|
settings.run.model.provider = Some(resolved_provider.into_inner());
|
|
|
|
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;
|
|
}
|
|
|
|
Ok(settings)
|
|
}
|
|
|
|
pub(crate) fn resolve_run_model(
|
|
catalog: &Catalog,
|
|
eligible: &HashSet<ProviderId>,
|
|
model: Option<&str>,
|
|
provider: Option<&str>,
|
|
catalog_fallback: bool,
|
|
) -> Result<(String, ProviderId), ModelSelectionError> {
|
|
let provider = provider
|
|
.filter(|provider| !provider.is_empty())
|
|
.map(ProviderId::new);
|
|
let selected = if catalog_fallback {
|
|
catalog.resolve_selection_with_catalog_fallback(model, provider.as_ref(), eligible)?
|
|
} else {
|
|
catalog.resolve_selection(model, provider.as_ref(), eligible)?
|
|
};
|
|
Ok((selected.model, selected.provider))
|
|
}
|