diff --git a/apps/admin-api/src/service.rs b/apps/admin-api/src/service.rs index daa8fc9..cd4310e 100644 --- a/apps/admin-api/src/service.rs +++ b/apps/admin-api/src/service.rs @@ -12,10 +12,11 @@ use crank_core::{ NoopAuditSink, OperationId, OperationSecurityLevel, OperationStatus, OwnerOnlyPolicyEngine, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyScope, PlatformApiKeyStatus, PolicyEngine, ProductEdition, Protocol, ResponseCachePolicy, SampleId, Samples, Secret, SecretId, SecretKind, - SecretStatus, Target, ToolQualitySchemaKind, ToolQualitySchemaNode, UsagePeriod, UserId, - UserSessionId, WizardState, Workspace, WorkspaceId, WorkspaceStatus, + SecretStatus, Target, ToolQualityMappingRule, ToolQualityMappingSet, ToolQualitySchemaKind, + ToolQualitySchemaNode, UsagePeriod, UserId, UserSessionId, WizardState, Workspace, WorkspaceId, + WorkspaceStatus, }; -use crank_mapping::{JsonPathRoot, MappingSet, infer_mapping_from_samples}; +use crank_mapping::{JsonPathRoot, MappingRule, MappingSet, infer_mapping_from_samples}; use crank_registry::{ AgentSummary, AgentVersionRecord, CreateAgentDraftVersionRequest, CreateAgentRequest, CreateInvocationLogRequest, CreatePlatformApiKeyRequest, CreateSecretRequest, @@ -1459,6 +1460,9 @@ impl AdminService { findings.extend( crank_core::analyze_tool_schema_quality("input_schema", &input_schema).findings, ); + let output_mapping = tool_quality_mapping_set(&payload.output_mapping); + findings + .extend(crank_core::analyze_tool_response_projection_quality(&output_mapping).findings); Ok(crank_core::ToolQualityReport::new(findings)) } @@ -3832,6 +3836,23 @@ fn tool_quality_schema_kind(kind: &SchemaKind) -> ToolQualitySchemaKind { } } +fn tool_quality_mapping_set(mapping: &MappingSet) -> ToolQualityMappingSet { + ToolQualityMappingSet { + rules: mapping + .rules + .iter() + .map(tool_quality_mapping_rule) + .collect(), + } +} + +fn tool_quality_mapping_rule(rule: &MappingRule) -> ToolQualityMappingRule { + ToolQualityMappingRule { + source: rule.source.clone(), + target: rule.target.clone(), + } +} + #[cfg(test)] #[allow(clippy::items_after_test_module)] mod tests { diff --git a/apps/ui/css/wizard.css b/apps/ui/css/wizard.css index 7a4d284..8f3048c 100644 --- a/apps/ui/css/wizard.css +++ b/apps/ui/css/wizard.css @@ -1478,6 +1478,53 @@ min-height: 54px; } +.quality-findings { + display: grid; + gap: 10px; +} + +.quality-finding { + background: var(--surface-subtle); + border: 1px solid var(--border); + border-radius: 10px; + padding: 12px 14px; +} + +.quality-finding.warning { + background: rgba(210, 153, 31, 0.08); + border-color: rgba(210, 153, 31, 0.45); +} + +.quality-finding.error { + background: rgba(219, 83, 74, 0.08); + border-color: rgba(219, 83, 74, 0.45); +} + +.quality-finding.info { + background: rgba(57, 128, 247, 0.07); + border-color: rgba(57, 128, 247, 0.35); +} + +.quality-finding-title { + color: var(--text-primary); + font-size: 13px; + font-weight: 700; +} + +.quality-finding-body, +.quality-finding-action { + color: var(--text-secondary); + font-size: 12px; + line-height: 1.55; + margin-top: 4px; +} + +.quality-finding-path { + color: var(--text-muted); + font-size: 11px; + margin-top: 6px; +} + @media (max-width: 760px) { .agent-preview-summary { grid-template-columns: 1fr; diff --git a/apps/ui/html/wizard/step5.html b/apps/ui/html/wizard/step5.html index 73fa2f9..5166c93 100644 --- a/apps/ui/html/wizard/step5.html +++ b/apps/ui/html/wizard/step5.html @@ -154,6 +154,26 @@ tls: +