mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
- Take `Arc<Catalog>` by value again through the validation entry points. `AppState::catalog()` returns an owned `Arc`, so `&state.catalog()` was cloning, borrowing the temporary, then cloning again at the leaf. Every consumer ends up owning the `Arc`, so by-value is the honest shape and it drops one clone per call. The one caller holding the catalog in a field now says `Arc::clone(&self.catalog)` explicitly. - Correct the `RenderMode` doc comment. It claimed `Strict` is "used by run-create", but run-create renders `Structural` and promotes the resulting warnings to errors itself; `Strict` has no production caller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
104 lines
3.5 KiB
Rust
104 lines
3.5 KiB
Rust
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use fabro_model::{Catalog, ProviderId};
|
|
use fabro_types::WorkflowSettings;
|
|
|
|
use super::create::{configured_default_provider, preprocess_and_validate, template_context};
|
|
use super::source::{ResolveWorkflowInput, WorkflowInput, resolve_workflow};
|
|
use crate::error::Error;
|
|
use crate::operations::RenderMode;
|
|
use crate::pipeline::{TransformOptions, Validated};
|
|
use crate::transforms::{ModelResolutionTransform, Transform};
|
|
|
|
pub struct ValidateInput {
|
|
pub workflow: WorkflowInput,
|
|
pub settings: WorkflowSettings,
|
|
/// Run-scoped variables (`{{ vars.* }}`) available to prompts and goals.
|
|
/// Empty for offline/CLI validation.
|
|
pub vars: HashMap<String, String>,
|
|
pub cwd: PathBuf,
|
|
pub custom_transforms: Vec<Box<dyn Transform>>,
|
|
}
|
|
|
|
/// Parse, transform, and structurally validate a DOT source string without a
|
|
/// model catalog. Model and provider availability is left to the caller that
|
|
/// owns a catalog — typically the server.
|
|
///
|
|
/// Returns `Validated` even when validation produced errors. Call
|
|
/// `validated.raise_on_errors()` if the caller wants to fail fast.
|
|
pub fn validate(input: ValidateInput) -> Result<Validated, Error> {
|
|
validate_resolving_models(input, None)
|
|
}
|
|
|
|
/// Parse, transform, and validate a DOT source string against `catalog`.
|
|
pub fn validate_with_catalog(
|
|
input: ValidateInput,
|
|
catalog: Arc<Catalog>,
|
|
) -> Result<Validated, Error> {
|
|
validate_resolving_models(input, Some(ModelResolutionTransform::new(catalog)))
|
|
}
|
|
|
|
/// Parse, transform, and validate, resolving models against the ready
|
|
/// providers first and falling back to the full catalog only for
|
|
/// provider-readiness selection failures.
|
|
pub fn validate_with_ready_providers(
|
|
input: ValidateInput,
|
|
catalog: Arc<Catalog>,
|
|
ready_providers: &[ProviderId],
|
|
) -> Result<Validated, Error> {
|
|
validate_resolving_models(
|
|
input,
|
|
Some(
|
|
ModelResolutionTransform::for_eligible(
|
|
catalog,
|
|
ready_providers.iter().cloned().collect(),
|
|
)
|
|
.with_catalog_fallback(true),
|
|
),
|
|
)
|
|
}
|
|
|
|
/// The workflow's own default provider is only known once the workflow is
|
|
/// resolved, so callers hand in a partially built transform and it is
|
|
/// completed here.
|
|
fn validate_resolving_models(
|
|
input: ValidateInput,
|
|
model_resolution: Option<ModelResolutionTransform>,
|
|
) -> Result<Validated, Error> {
|
|
let ValidateInput {
|
|
workflow,
|
|
settings,
|
|
vars,
|
|
cwd,
|
|
custom_transforms,
|
|
} = input;
|
|
let resolved = resolve_workflow(ResolveWorkflowInput {
|
|
workflow,
|
|
settings,
|
|
cwd,
|
|
})
|
|
.map_err(|err| Error::Parse(err.to_string()))?;
|
|
|
|
let model_resolution = model_resolution.map(|resolution| {
|
|
resolution.with_default_provider(configured_default_provider(&resolved.settings))
|
|
});
|
|
|
|
preprocess_and_validate(
|
|
&resolved.raw_source,
|
|
resolved.goal_override.as_deref(),
|
|
&TransformOptions {
|
|
current_dir: resolved.current_dir,
|
|
file_resolver: resolved.file_resolver,
|
|
template_context: template_context(Some(&resolved.settings), vars),
|
|
source_name: resolved
|
|
.dot_path
|
|
.as_ref()
|
|
.map(|path| path.display().to_string()),
|
|
render_mode: RenderMode::Structural,
|
|
custom_transforms,
|
|
model_resolution,
|
|
},
|
|
)
|
|
}
|