Skip to main content

licoup_workflow/
diagnostic.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
4#[serde(rename_all = "snake_case")]
5pub enum WorkflowDiagnosticCode {
6    WorkflowSyntaxInvalid,
7    WorkflowShapeInvalid,
8    WorkflowRequiredFieldMissing,
9    WorkflowFieldTypeInvalid,
10    WorkflowFieldValueInvalid,
11    WorkflowUnknownField,
12    WorkflowSchemaUnsupported,
13    WorkflowMetadataIdInvalid,
14    WorkflowMetadataNameInvalid,
15    WorkflowMetadataVersionInvalid,
16    WorkflowStateLimit,
17    WorkflowTransitionLimit,
18    WorkflowBindingLimit,
19    WorkflowRuntimeLimit,
20    WorkflowParallelismInvalid,
21    WorkflowWorksetLimitInvalid,
22    WorkflowRetryLimitInvalid,
23    WorkflowBindingIdInvalid,
24    WorkflowBindingDuplicate,
25    WorkflowBindingLabelInvalid,
26    WorkflowFallbackInvalid,
27    WorkflowEntrySlotInvalid,
28    WorkflowRuntimeIdInvalid,
29    WorkflowRuntimeBindingInvalid,
30    WorkflowWorksetIdInvalid,
31    WorkflowWorksetItemBindingInvalid,
32    WorkflowWorksetPredecessorFieldInvalid,
33    WorkflowWorksetFieldConflict,
34    WorkflowStateIdInvalid,
35    WorkflowStateLabelInvalid,
36    WorkflowStateInstructionInvalid,
37    WorkflowStateDuplicate,
38    WorkflowStateRetryInvalid,
39    WorkflowActorBindingInvalid,
40    WorkflowStateFieldInvalid,
41    WorkflowScriptRuntimeInvalid,
42    WorkflowScriptEntryMissing,
43    WorkflowScriptEntryInvalid,
44    WorkflowWorksetReferenceInvalid,
45    WorkflowWorksetBindingInvalid,
46    WorkflowInitialUnknown,
47    WorkflowTransitionIdInvalid,
48    WorkflowTransitionDuplicate,
49    WorkflowTransitionStateUnknown,
50    WorkflowGuardInvalid,
51    WorkflowGuardAmbiguous,
52    WorkflowRoutingInvalid,
53    WorkflowTransitionModeInvalid,
54    WorkflowFlowTargetIncomplete,
55    WorkflowTopologyInvalid,
56    WorkflowStateUnreachable,
57    WorkflowTerminalUnreachable,
58    WorkflowEffectCycle,
59    WorkflowInvalid,
60    GraphIdentityNotAssistantTemporary,
61    GraphRuntimeAssetUnavailable,
62    GraphAssistantMembershipRejected,
63    GraphAssistantDesignationRejected,
64    GraphAssistantSkillUnavailable,
65    GraphAuthorityRejected,
66    GraphBindingDuplicate,
67    GraphBindingUnknown,
68    GraphBindingKindRejected,
69    GraphBindingIncomplete,
70    GraphMembershipRejected,
71    GraphModelUnavailable,
72    GraphModelRejected,
73    GraphReadinessRejected,
74    GraphEnvironmentUnavailable,
75    GraphProfileRejected,
76    ConversationStateUnavailable,
77    ConversationNotFound,
78    GraphRouteStale,
79}
80
81impl WorkflowDiagnosticCode {
82    pub const fn wire(self) -> &'static str {
83        match self {
84            Self::WorkflowSyntaxInvalid => "workflow_syntax_invalid",
85            Self::WorkflowShapeInvalid => "workflow_shape_invalid",
86            Self::WorkflowRequiredFieldMissing => "workflow_required_field_missing",
87            Self::WorkflowFieldTypeInvalid => "workflow_field_type_invalid",
88            Self::WorkflowFieldValueInvalid => "workflow_field_value_invalid",
89            Self::WorkflowUnknownField => "workflow_unknown_field",
90            Self::WorkflowSchemaUnsupported => "workflow_schema_unsupported",
91            Self::WorkflowMetadataIdInvalid => "workflow_metadata_id_invalid",
92            Self::WorkflowMetadataNameInvalid => "workflow_metadata_name_invalid",
93            Self::WorkflowMetadataVersionInvalid => "workflow_metadata_version_invalid",
94            Self::WorkflowStateLimit => "workflow_state_limit",
95            Self::WorkflowTransitionLimit => "workflow_transition_limit",
96            Self::WorkflowBindingLimit => "workflow_binding_limit",
97            Self::WorkflowRuntimeLimit => "workflow_runtime_limit",
98            Self::WorkflowParallelismInvalid => "workflow_parallelism_invalid",
99            Self::WorkflowWorksetLimitInvalid => "workflow_workset_limit_invalid",
100            Self::WorkflowRetryLimitInvalid => "workflow_retry_limit_invalid",
101            Self::WorkflowBindingIdInvalid => "workflow_binding_id_invalid",
102            Self::WorkflowBindingDuplicate => "workflow_binding_duplicate",
103            Self::WorkflowBindingLabelInvalid => "workflow_binding_label_invalid",
104            Self::WorkflowFallbackInvalid => "workflow_fallback_invalid",
105            Self::WorkflowEntrySlotInvalid => "workflow_entry_slot_invalid",
106            Self::WorkflowRuntimeIdInvalid => "workflow_runtime_id_invalid",
107            Self::WorkflowRuntimeBindingInvalid => "workflow_runtime_binding_invalid",
108            Self::WorkflowWorksetIdInvalid => "workflow_workset_id_invalid",
109            Self::WorkflowWorksetItemBindingInvalid => "workflow_workset_item_binding_invalid",
110            Self::WorkflowWorksetPredecessorFieldInvalid => {
111                "workflow_workset_predecessor_field_invalid"
112            }
113            Self::WorkflowWorksetFieldConflict => "workflow_workset_field_conflict",
114            Self::WorkflowStateIdInvalid => "workflow_state_id_invalid",
115            Self::WorkflowStateLabelInvalid => "workflow_state_label_invalid",
116            Self::WorkflowStateInstructionInvalid => "workflow_state_instruction_invalid",
117            Self::WorkflowStateDuplicate => "workflow_state_duplicate",
118            Self::WorkflowStateRetryInvalid => "workflow_state_retry_invalid",
119            Self::WorkflowActorBindingInvalid => "workflow_actor_binding_invalid",
120            Self::WorkflowStateFieldInvalid => "workflow_state_field_invalid",
121            Self::WorkflowScriptRuntimeInvalid => "workflow_script_runtime_invalid",
122            Self::WorkflowScriptEntryMissing => "workflow_script_entry_missing",
123            Self::WorkflowScriptEntryInvalid => "workflow_script_entry_invalid",
124            Self::WorkflowWorksetReferenceInvalid => "workflow_workset_reference_invalid",
125            Self::WorkflowWorksetBindingInvalid => "workflow_workset_binding_invalid",
126            Self::WorkflowInitialUnknown => "workflow_initial_unknown",
127            Self::WorkflowTransitionIdInvalid => "workflow_transition_id_invalid",
128            Self::WorkflowTransitionDuplicate => "workflow_transition_duplicate",
129            Self::WorkflowTransitionStateUnknown => "workflow_transition_state_unknown",
130            Self::WorkflowGuardInvalid => "workflow_guard_invalid",
131            Self::WorkflowGuardAmbiguous => "workflow_guard_ambiguous",
132            Self::WorkflowRoutingInvalid => "workflow_routing_invalid",
133            Self::WorkflowTransitionModeInvalid => "workflow_transition_mode_invalid",
134            Self::WorkflowFlowTargetIncomplete => "workflow_flow_target_incomplete",
135            Self::WorkflowTopologyInvalid => "workflow_topology_invalid",
136            Self::WorkflowStateUnreachable => "workflow_state_unreachable",
137            Self::WorkflowTerminalUnreachable => "workflow_terminal_unreachable",
138            Self::WorkflowEffectCycle => "workflow_effect_cycle",
139            Self::WorkflowInvalid => "workflow_invalid",
140            Self::GraphIdentityNotAssistantTemporary => "graph_identity_not_assistant_temporary",
141            Self::GraphRuntimeAssetUnavailable => "graph_runtime_asset_unavailable",
142            Self::GraphAssistantMembershipRejected => "graph_assistant_membership_rejected",
143            Self::GraphAssistantDesignationRejected => "graph_assistant_designation_rejected",
144            Self::GraphAssistantSkillUnavailable => "graph_assistant_skill_unavailable",
145            Self::GraphAuthorityRejected => "graph_authority_rejected",
146            Self::GraphBindingDuplicate => "graph_binding_duplicate",
147            Self::GraphBindingUnknown => "graph_binding_unknown",
148            Self::GraphBindingKindRejected => "graph_binding_kind_rejected",
149            Self::GraphBindingIncomplete => "graph_binding_incomplete",
150            Self::GraphMembershipRejected => "graph_membership_rejected",
151            Self::GraphModelUnavailable => "graph_model_unavailable",
152            Self::GraphModelRejected => "graph_model_rejected",
153            Self::GraphReadinessRejected => "graph_readiness_rejected",
154            Self::GraphEnvironmentUnavailable => "graph_environment_unavailable",
155            Self::GraphProfileRejected => "graph_profile_rejected",
156            Self::ConversationStateUnavailable => "conversation_state_unavailable",
157            Self::ConversationNotFound => "conversation_not_found",
158            Self::GraphRouteStale => "graph_route_stale",
159        }
160    }
161
162    pub fn from_wire(value: &str) -> Option<Self> {
163        const VALUES: &[WorkflowDiagnosticCode] = &[
164            WorkflowDiagnosticCode::WorkflowSyntaxInvalid,
165            WorkflowDiagnosticCode::WorkflowShapeInvalid,
166            WorkflowDiagnosticCode::WorkflowRequiredFieldMissing,
167            WorkflowDiagnosticCode::WorkflowFieldTypeInvalid,
168            WorkflowDiagnosticCode::WorkflowFieldValueInvalid,
169            WorkflowDiagnosticCode::WorkflowUnknownField,
170            WorkflowDiagnosticCode::WorkflowSchemaUnsupported,
171            WorkflowDiagnosticCode::WorkflowMetadataIdInvalid,
172            WorkflowDiagnosticCode::WorkflowMetadataNameInvalid,
173            WorkflowDiagnosticCode::WorkflowMetadataVersionInvalid,
174            WorkflowDiagnosticCode::WorkflowStateLimit,
175            WorkflowDiagnosticCode::WorkflowTransitionLimit,
176            WorkflowDiagnosticCode::WorkflowBindingLimit,
177            WorkflowDiagnosticCode::WorkflowRuntimeLimit,
178            WorkflowDiagnosticCode::WorkflowParallelismInvalid,
179            WorkflowDiagnosticCode::WorkflowWorksetLimitInvalid,
180            WorkflowDiagnosticCode::WorkflowRetryLimitInvalid,
181            WorkflowDiagnosticCode::WorkflowBindingIdInvalid,
182            WorkflowDiagnosticCode::WorkflowBindingDuplicate,
183            WorkflowDiagnosticCode::WorkflowBindingLabelInvalid,
184            WorkflowDiagnosticCode::WorkflowFallbackInvalid,
185            WorkflowDiagnosticCode::WorkflowEntrySlotInvalid,
186            WorkflowDiagnosticCode::WorkflowRuntimeIdInvalid,
187            WorkflowDiagnosticCode::WorkflowRuntimeBindingInvalid,
188            WorkflowDiagnosticCode::WorkflowWorksetIdInvalid,
189            WorkflowDiagnosticCode::WorkflowWorksetItemBindingInvalid,
190            WorkflowDiagnosticCode::WorkflowWorksetPredecessorFieldInvalid,
191            WorkflowDiagnosticCode::WorkflowWorksetFieldConflict,
192            WorkflowDiagnosticCode::WorkflowStateIdInvalid,
193            WorkflowDiagnosticCode::WorkflowStateLabelInvalid,
194            WorkflowDiagnosticCode::WorkflowStateInstructionInvalid,
195            WorkflowDiagnosticCode::WorkflowStateDuplicate,
196            WorkflowDiagnosticCode::WorkflowStateRetryInvalid,
197            WorkflowDiagnosticCode::WorkflowActorBindingInvalid,
198            WorkflowDiagnosticCode::WorkflowStateFieldInvalid,
199            WorkflowDiagnosticCode::WorkflowScriptRuntimeInvalid,
200            WorkflowDiagnosticCode::WorkflowScriptEntryMissing,
201            WorkflowDiagnosticCode::WorkflowScriptEntryInvalid,
202            WorkflowDiagnosticCode::WorkflowWorksetReferenceInvalid,
203            WorkflowDiagnosticCode::WorkflowWorksetBindingInvalid,
204            WorkflowDiagnosticCode::WorkflowInitialUnknown,
205            WorkflowDiagnosticCode::WorkflowTransitionIdInvalid,
206            WorkflowDiagnosticCode::WorkflowTransitionDuplicate,
207            WorkflowDiagnosticCode::WorkflowTransitionStateUnknown,
208            WorkflowDiagnosticCode::WorkflowGuardInvalid,
209            WorkflowDiagnosticCode::WorkflowGuardAmbiguous,
210            WorkflowDiagnosticCode::WorkflowRoutingInvalid,
211            WorkflowDiagnosticCode::WorkflowTransitionModeInvalid,
212            WorkflowDiagnosticCode::WorkflowFlowTargetIncomplete,
213            WorkflowDiagnosticCode::WorkflowTopologyInvalid,
214            WorkflowDiagnosticCode::WorkflowStateUnreachable,
215            WorkflowDiagnosticCode::WorkflowTerminalUnreachable,
216            WorkflowDiagnosticCode::WorkflowEffectCycle,
217            WorkflowDiagnosticCode::WorkflowInvalid,
218            WorkflowDiagnosticCode::GraphIdentityNotAssistantTemporary,
219            WorkflowDiagnosticCode::GraphRuntimeAssetUnavailable,
220            WorkflowDiagnosticCode::GraphAssistantMembershipRejected,
221            WorkflowDiagnosticCode::GraphAssistantDesignationRejected,
222            WorkflowDiagnosticCode::GraphAssistantSkillUnavailable,
223            WorkflowDiagnosticCode::GraphAuthorityRejected,
224            WorkflowDiagnosticCode::GraphBindingDuplicate,
225            WorkflowDiagnosticCode::GraphBindingUnknown,
226            WorkflowDiagnosticCode::GraphBindingKindRejected,
227            WorkflowDiagnosticCode::GraphBindingIncomplete,
228            WorkflowDiagnosticCode::GraphMembershipRejected,
229            WorkflowDiagnosticCode::GraphModelUnavailable,
230            WorkflowDiagnosticCode::GraphModelRejected,
231            WorkflowDiagnosticCode::GraphReadinessRejected,
232            WorkflowDiagnosticCode::GraphEnvironmentUnavailable,
233            WorkflowDiagnosticCode::GraphProfileRejected,
234            WorkflowDiagnosticCode::ConversationStateUnavailable,
235            WorkflowDiagnosticCode::ConversationNotFound,
236            WorkflowDiagnosticCode::GraphRouteStale,
237        ];
238        VALUES
239            .iter()
240            .copied()
241            .find(|candidate| candidate.wire() == value)
242    }
243}
244
245impl PartialEq<&str> for WorkflowDiagnosticCode {
246    fn eq(&self, other: &&str) -> bool {
247        self.wire() == *other
248    }
249}
250
251#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
252pub enum WorkflowDiagnosticStage {
253    #[serde(rename = "workflow/parse")]
254    WorkflowParse,
255    #[serde(rename = "workflow/compile")]
256    WorkflowCompile,
257    #[serde(rename = "package/validate")]
258    PackageValidate,
259    #[serde(rename = "assistant-workflow/preflight")]
260    AssistantWorkflowPreflight,
261    #[serde(rename = "assistant-workflow/revalidate")]
262    AssistantWorkflowRevalidate,
263}
264
265impl WorkflowDiagnosticStage {
266    pub const fn wire(self) -> &'static str {
267        match self {
268            Self::WorkflowParse => "workflow/parse",
269            Self::WorkflowCompile => "workflow/compile",
270            Self::PackageValidate => "package/validate",
271            Self::AssistantWorkflowPreflight => "assistant-workflow/preflight",
272            Self::AssistantWorkflowRevalidate => "assistant-workflow/revalidate",
273        }
274    }
275
276    pub fn from_wire(value: &str) -> Option<Self> {
277        match value {
278            "workflow/parse" => Some(Self::WorkflowParse),
279            "workflow/compile" => Some(Self::WorkflowCompile),
280            "package/validate" => Some(Self::PackageValidate),
281            "assistant-workflow/preflight" => Some(Self::AssistantWorkflowPreflight),
282            "assistant-workflow/revalidate" => Some(Self::AssistantWorkflowRevalidate),
283            _ => None,
284        }
285    }
286}
287
288#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
289#[serde(rename_all = "snake_case")]
290pub enum WorkflowDiagnosticExpected {
291    Object,
292    Array,
293    String,
294    Integer,
295    Boolean,
296    EnumValue,
297    Identifier,
298    NonEmptyText,
299    UniqueId,
300    ExistingReference,
301    SupportedSchema,
302    ValidRouting,
303    ValidTopology,
304}
305
306#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
307#[serde(rename_all = "snake_case")]
308pub enum WorkflowDiagnosticActualKind {
309    Missing,
310    Null,
311    Object,
312    Array,
313    String,
314    Number,
315    Boolean,
316}
317
318#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
319#[serde(rename_all = "snake_case")]
320pub enum WorkflowDiagnosticRecovery {
321    AddRequiredField,
322    CorrectField,
323    RemoveUnknownField,
324    RemoveDuplicate,
325    CorrectReference,
326    ReduceResource,
327    CorrectRouting,
328    CorrectTopology,
329    UpdateAssistantProfile,
330    UpdateBinding,
331    RefreshConversationState,
332}
333
334#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
335#[serde(rename_all = "camelCase", deny_unknown_fields)]
336pub struct PreflightDiagnostic {
337    pub code: WorkflowDiagnosticCode,
338    pub stage: WorkflowDiagnosticStage,
339    #[serde(default, skip_serializing_if = "Option::is_none")]
340    pub path: Option<String>,
341    #[serde(default, skip_serializing_if = "Vec::is_empty")]
342    pub related_paths: Vec<String>,
343    #[serde(default, skip_serializing_if = "Option::is_none")]
344    pub membership_id: Option<String>,
345    #[serde(default, skip_serializing_if = "Option::is_none")]
346    pub actual: Option<u64>,
347    #[serde(default, skip_serializing_if = "Option::is_none")]
348    pub limit: Option<u64>,
349    #[serde(default, skip_serializing_if = "Option::is_none")]
350    pub expected: Option<WorkflowDiagnosticExpected>,
351    #[serde(default, skip_serializing_if = "Option::is_none")]
352    pub actual_kind: Option<WorkflowDiagnosticActualKind>,
353    #[serde(default, skip_serializing_if = "Option::is_none")]
354    pub recovery: Option<WorkflowDiagnosticRecovery>,
355    #[serde(default, skip_serializing_if = "Option::is_none")]
356    pub line: Option<u64>,
357    #[serde(default, skip_serializing_if = "Option::is_none")]
358    pub column: Option<u64>,
359}
360
361impl From<acativa::workflow::diagnostic::PreflightDiagnostic> for PreflightDiagnostic {
362    /// The engine's diagnostic record is a strict field-and-variant subset of
363    /// this one (`membershipId` is the only extra, and it is optional), so the
364    /// wire form carries the conversion.
365    fn from(diagnostic: acativa::workflow::diagnostic::PreflightDiagnostic) -> Self {
366        let wire = serde_json::to_value(&diagnostic).expect("an engine diagnostic always encodes");
367        serde_json::from_value(wire).expect("engine diagnostics are a strict subset of this record")
368    }
369}