From 0d75b7af9f5bf8f70215b68047f3f8804f497b6e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 08:31:28 +0000 Subject: [PATCH 1/3] Initial plan From 48cc4c47ee0f4f0d8ad6d3035585322b6b1c794c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 08:38:38 +0000 Subject: [PATCH 2/3] Add AI workflow automation and predictive analytics protocols Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- content/docs/references/ai/AITask.mdx | 28 + content/docs/references/ai/AITaskType.mdx | 17 + .../references/ai/AIWorkflowAutomation.mdx | 34 + .../ai/AIWorkflowExecutionResult.mdx | 20 + .../docs/references/ai/AIWorkflowTrigger.mdx | 14 + .../ai/BatchAIWorkflowExecution.mdx | 14 + .../docs/references/ai/EvaluationMetrics.mdx | 23 + .../docs/references/ai/Hyperparameters.mdx | 25 + content/docs/references/ai/ModelDrift.mdx | 16 + .../references/ai/PostProcessingAction.mdx | 13 + .../docs/references/ai/PredictionRequest.mdx | 14 + .../docs/references/ai/PredictionResult.mdx | 17 + .../docs/references/ai/PredictiveModel.mdx | 40 + .../references/ai/PredictiveModelType.mdx | 14 + content/docs/references/ai/TrainingConfig.mdx | 23 + .../references/ai/WorkflowFieldCondition.mdx | 12 + .../docs/references/ai/WorkflowSchedule.mdx | 16 + content/docs/references/ai/config/Feature.mdx | 15 + packages/spec/json-schema/AITask.json | 123 ++ packages/spec/json-schema/AITaskType.json | 21 + .../json-schema/AIWorkflowAutomation.json | 379 +++++++ .../AIWorkflowExecutionResult.json | 105 ++ .../spec/json-schema/AIWorkflowTrigger.json | 18 + .../json-schema/BatchAIWorkflowExecution.json | 48 + .../spec/json-schema/EvaluationMetrics.json | 64 ++ .../spec/json-schema/Hyperparameters.json | 80 ++ packages/spec/json-schema/ModelDrift.json | 73 ++ .../json-schema/PostProcessingAction.json | 41 + .../spec/json-schema/PredictionRequest.json | 39 + .../spec/json-schema/PredictionResult.json | 82 ++ .../spec/json-schema/PredictiveModel.json | 445 ++++++++ .../spec/json-schema/PredictiveModelType.json | 18 + packages/spec/json-schema/TrainingConfig.json | 88 ++ .../json-schema/WorkflowFieldCondition.json | 33 + .../spec/json-schema/WorkflowSchedule.json | 51 + packages/spec/src/ai/index.ts | 4 + packages/spec/src/ai/predictive.test.ts | 1001 +++++++++++++++++ packages/spec/src/ai/predictive.zod.ts | 295 +++++ .../spec/src/ai/workflow-automation.test.ts | 789 +++++++++++++ .../spec/src/ai/workflow-automation.zod.ts | 235 ++++ 40 files changed, 4387 insertions(+) create mode 100644 content/docs/references/ai/AITask.mdx create mode 100644 content/docs/references/ai/AITaskType.mdx create mode 100644 content/docs/references/ai/AIWorkflowAutomation.mdx create mode 100644 content/docs/references/ai/AIWorkflowExecutionResult.mdx create mode 100644 content/docs/references/ai/AIWorkflowTrigger.mdx create mode 100644 content/docs/references/ai/BatchAIWorkflowExecution.mdx create mode 100644 content/docs/references/ai/EvaluationMetrics.mdx create mode 100644 content/docs/references/ai/Hyperparameters.mdx create mode 100644 content/docs/references/ai/ModelDrift.mdx create mode 100644 content/docs/references/ai/PostProcessingAction.mdx create mode 100644 content/docs/references/ai/PredictionRequest.mdx create mode 100644 content/docs/references/ai/PredictionResult.mdx create mode 100644 content/docs/references/ai/PredictiveModel.mdx create mode 100644 content/docs/references/ai/PredictiveModelType.mdx create mode 100644 content/docs/references/ai/TrainingConfig.mdx create mode 100644 content/docs/references/ai/WorkflowFieldCondition.mdx create mode 100644 content/docs/references/ai/WorkflowSchedule.mdx create mode 100644 content/docs/references/ai/config/Feature.mdx create mode 100644 packages/spec/json-schema/AITask.json create mode 100644 packages/spec/json-schema/AITaskType.json create mode 100644 packages/spec/json-schema/AIWorkflowAutomation.json create mode 100644 packages/spec/json-schema/AIWorkflowExecutionResult.json create mode 100644 packages/spec/json-schema/AIWorkflowTrigger.json create mode 100644 packages/spec/json-schema/BatchAIWorkflowExecution.json create mode 100644 packages/spec/json-schema/EvaluationMetrics.json create mode 100644 packages/spec/json-schema/Hyperparameters.json create mode 100644 packages/spec/json-schema/ModelDrift.json create mode 100644 packages/spec/json-schema/PostProcessingAction.json create mode 100644 packages/spec/json-schema/PredictionRequest.json create mode 100644 packages/spec/json-schema/PredictionResult.json create mode 100644 packages/spec/json-schema/PredictiveModel.json create mode 100644 packages/spec/json-schema/PredictiveModelType.json create mode 100644 packages/spec/json-schema/TrainingConfig.json create mode 100644 packages/spec/json-schema/WorkflowFieldCondition.json create mode 100644 packages/spec/json-schema/WorkflowSchedule.json create mode 100644 packages/spec/src/ai/predictive.test.ts create mode 100644 packages/spec/src/ai/predictive.zod.ts create mode 100644 packages/spec/src/ai/workflow-automation.test.ts create mode 100644 packages/spec/src/ai/workflow-automation.zod.ts diff --git a/content/docs/references/ai/AITask.mdx b/content/docs/references/ai/AITask.mdx new file mode 100644 index 0000000..0911f29 --- /dev/null +++ b/content/docs/references/ai/AITask.mdx @@ -0,0 +1,28 @@ +--- +title: AITask +description: AITask Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | optional | Optional task ID for referencing | +| **name** | `string` | ✅ | Human-readable task name | +| **type** | `Enum<'classify' \| 'extract' \| 'summarize' \| 'generate' \| 'predict' \| 'translate' \| 'sentiment' \| 'entity_recognition' \| 'anomaly_detection' \| 'recommendation'>` | ✅ | | +| **model** | `string` | optional | Model ID from registry (uses default if not specified) | +| **promptTemplate** | `string` | optional | Prompt template ID for this task | +| **inputFields** | `string[]` | ✅ | Source fields to process (e.g., ["description", "comments"]) | +| **inputContext** | `Record` | optional | Additional context for the AI model | +| **outputField** | `string` | ✅ | Target field to store the result | +| **outputFormat** | `Enum<'text' \| 'json' \| 'number' \| 'boolean' \| 'array'>` | optional | | +| **classes** | `string[]` | optional | Valid classes for classification tasks | +| **multiClass** | `boolean` | optional | Allow multiple classes to be selected | +| **extractionSchema** | `Record` | optional | JSON schema for structured extraction | +| **maxLength** | `number` | optional | Maximum length for generated content | +| **temperature** | `number` | optional | Model temperature override | +| **fallbackValue** | `any` | optional | Fallback value if AI task fails | +| **retryAttempts** | `integer` | optional | | +| **condition** | `string` | optional | Formula condition - task only runs if TRUE | +| **description** | `string` | optional | | +| **active** | `boolean` | optional | | diff --git a/content/docs/references/ai/AITaskType.mdx b/content/docs/references/ai/AITaskType.mdx new file mode 100644 index 0000000..5bc8523 --- /dev/null +++ b/content/docs/references/ai/AITaskType.mdx @@ -0,0 +1,17 @@ +--- +title: AITaskType +description: AITaskType Schema Reference +--- + +## Allowed Values + +* `classify` +* `extract` +* `summarize` +* `generate` +* `predict` +* `translate` +* `sentiment` +* `entity_recognition` +* `anomaly_detection` +* `recommendation` \ No newline at end of file diff --git a/content/docs/references/ai/AIWorkflowAutomation.mdx b/content/docs/references/ai/AIWorkflowAutomation.mdx new file mode 100644 index 0000000..68c0d8e --- /dev/null +++ b/content/docs/references/ai/AIWorkflowAutomation.mdx @@ -0,0 +1,34 @@ +--- +title: AIWorkflowAutomation +description: AIWorkflowAutomation Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Workflow unique identifier (snake_case) | +| **label** | `string` | ✅ | Workflow display name | +| **description** | `string` | optional | | +| **objectName** | `string` | ✅ | Target object for this workflow | +| **trigger** | `Enum<'record_created' \| 'record_updated' \| 'field_changed' \| 'scheduled' \| 'manual' \| 'webhook' \| 'batch'>` | ✅ | | +| **fieldConditions** | `object[]` | optional | Fields to monitor (for field_changed trigger) | +| **schedule** | `object` | optional | Schedule configuration (for scheduled trigger) | +| **webhookConfig** | `object` | optional | Webhook configuration (for webhook trigger) | +| **entryCriteria** | `string` | optional | Formula condition - workflow only runs if TRUE | +| **aiTasks** | `object[]` | ✅ | AI tasks to execute in sequence | +| **postActions** | `object[]` | optional | Actions after AI tasks complete | +| **executionMode** | `Enum<'sequential' \| 'parallel'>` | optional | How to execute multiple AI tasks | +| **stopOnError** | `boolean` | optional | Stop workflow if any task fails | +| **timeout** | `number` | optional | Maximum execution time in seconds | +| **priority** | `Enum<'low' \| 'normal' \| 'high' \| 'critical'>` | optional | | +| **enableLogging** | `boolean` | optional | | +| **enableMetrics** | `boolean` | optional | | +| **notifyOnFailure** | `string[]` | optional | User IDs to notify on failure | +| **active** | `boolean` | optional | | +| **version** | `string` | optional | | +| **tags** | `string[]` | optional | | +| **category** | `string` | optional | Workflow category (e.g., "support", "sales", "hr") | +| **owner** | `string` | optional | User ID of workflow owner | +| **createdAt** | `string` | optional | ISO timestamp | +| **updatedAt** | `string` | optional | ISO timestamp | diff --git a/content/docs/references/ai/AIWorkflowExecutionResult.mdx b/content/docs/references/ai/AIWorkflowExecutionResult.mdx new file mode 100644 index 0000000..c8c3227 --- /dev/null +++ b/content/docs/references/ai/AIWorkflowExecutionResult.mdx @@ -0,0 +1,20 @@ +--- +title: AIWorkflowExecutionResult +description: AIWorkflowExecutionResult Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **workflowName** | `string` | ✅ | | +| **recordId** | `string` | ✅ | | +| **status** | `Enum<'success' \| 'partial_success' \| 'failed' \| 'skipped'>` | ✅ | | +| **executionTime** | `number` | ✅ | Execution time in milliseconds | +| **tasksExecuted** | `integer` | ✅ | Number of tasks executed | +| **tasksSucceeded** | `integer` | ✅ | Number of tasks succeeded | +| **tasksFailed** | `integer` | ✅ | Number of tasks failed | +| **taskResults** | `object[]` | optional | | +| **error** | `string` | optional | | +| **startedAt** | `string` | ✅ | ISO timestamp | +| **completedAt** | `string` | optional | ISO timestamp | diff --git a/content/docs/references/ai/AIWorkflowTrigger.mdx b/content/docs/references/ai/AIWorkflowTrigger.mdx new file mode 100644 index 0000000..5fa3b5a --- /dev/null +++ b/content/docs/references/ai/AIWorkflowTrigger.mdx @@ -0,0 +1,14 @@ +--- +title: AIWorkflowTrigger +description: AIWorkflowTrigger Schema Reference +--- + +## Allowed Values + +* `record_created` +* `record_updated` +* `field_changed` +* `scheduled` +* `manual` +* `webhook` +* `batch` \ No newline at end of file diff --git a/content/docs/references/ai/BatchAIWorkflowExecution.mdx b/content/docs/references/ai/BatchAIWorkflowExecution.mdx new file mode 100644 index 0000000..191432a --- /dev/null +++ b/content/docs/references/ai/BatchAIWorkflowExecution.mdx @@ -0,0 +1,14 @@ +--- +title: BatchAIWorkflowExecution +description: BatchAIWorkflowExecution Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **workflowName** | `string` | ✅ | Workflow to execute | +| **recordIds** | `string[]` | ✅ | Records to process | +| **batchSize** | `integer` | optional | | +| **parallelism** | `integer` | optional | | +| **priority** | `Enum<'low' \| 'normal' \| 'high'>` | optional | | diff --git a/content/docs/references/ai/EvaluationMetrics.mdx b/content/docs/references/ai/EvaluationMetrics.mdx new file mode 100644 index 0000000..a41fda4 --- /dev/null +++ b/content/docs/references/ai/EvaluationMetrics.mdx @@ -0,0 +1,23 @@ +--- +title: EvaluationMetrics +description: EvaluationMetrics Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **accuracy** | `number` | optional | | +| **precision** | `number` | optional | | +| **recall** | `number` | optional | | +| **f1Score** | `number` | optional | | +| **auc** | `number` | optional | Area Under ROC Curve | +| **mse** | `number` | optional | Mean Squared Error | +| **rmse** | `number` | optional | Root Mean Squared Error | +| **mae** | `number` | optional | Mean Absolute Error | +| **r2Score** | `number` | optional | R-squared score | +| **silhouetteScore** | `number` | optional | | +| **daviesBouldinIndex** | `number` | optional | | +| **mape** | `number` | optional | Mean Absolute Percentage Error | +| **smape** | `number` | optional | Symmetric MAPE | +| **custom** | `Record` | optional | | diff --git a/content/docs/references/ai/Hyperparameters.mdx b/content/docs/references/ai/Hyperparameters.mdx new file mode 100644 index 0000000..41f8e9d --- /dev/null +++ b/content/docs/references/ai/Hyperparameters.mdx @@ -0,0 +1,25 @@ +--- +title: Hyperparameters +description: Hyperparameters Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **learningRate** | `number` | optional | Learning rate for training | +| **epochs** | `integer` | optional | Number of training epochs | +| **batchSize** | `integer` | optional | Training batch size | +| **maxDepth** | `integer` | optional | Maximum tree depth | +| **numTrees** | `integer` | optional | Number of trees in ensemble | +| **minSamplesSplit** | `integer` | optional | Minimum samples to split node | +| **minSamplesLeaf** | `integer` | optional | Minimum samples in leaf node | +| **hiddenLayers** | `integer[]` | optional | Hidden layer sizes | +| **activation** | `string` | optional | Activation function | +| **dropout** | `number` | optional | Dropout rate | +| **l1Regularization** | `number` | optional | L1 regularization strength | +| **l2Regularization** | `number` | optional | L2 regularization strength | +| **numClusters** | `integer` | optional | Number of clusters (k-means, etc.) | +| **seasonalPeriod** | `integer` | optional | Seasonal period for time series | +| **forecastHorizon** | `integer` | optional | Number of periods to forecast | +| **custom** | `Record` | optional | Algorithm-specific parameters | diff --git a/content/docs/references/ai/ModelDrift.mdx b/content/docs/references/ai/ModelDrift.mdx new file mode 100644 index 0000000..703e71b --- /dev/null +++ b/content/docs/references/ai/ModelDrift.mdx @@ -0,0 +1,16 @@ +--- +title: ModelDrift +description: ModelDrift Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **modelName** | `string` | ✅ | | +| **driftType** | `Enum<'feature_drift' \| 'prediction_drift' \| 'performance_drift'>` | ✅ | | +| **severity** | `Enum<'low' \| 'medium' \| 'high' \| 'critical'>` | ✅ | | +| **detectedAt** | `string` | ✅ | ISO timestamp | +| **metrics** | `object` | ✅ | | +| **recommendation** | `string` | optional | | +| **autoRetrainTriggered** | `boolean` | optional | | diff --git a/content/docs/references/ai/PostProcessingAction.mdx b/content/docs/references/ai/PostProcessingAction.mdx new file mode 100644 index 0000000..a8c93ca --- /dev/null +++ b/content/docs/references/ai/PostProcessingAction.mdx @@ -0,0 +1,13 @@ +--- +title: PostProcessingAction +description: PostProcessingAction Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `Enum<'field_update' \| 'send_email' \| 'create_record' \| 'update_related' \| 'trigger_flow' \| 'webhook'>` | ✅ | | +| **name** | `string` | ✅ | Action name | +| **config** | `Record` | ✅ | Action-specific configuration | +| **condition** | `string` | optional | Execute only if condition is TRUE | diff --git a/content/docs/references/ai/PredictionRequest.mdx b/content/docs/references/ai/PredictionRequest.mdx new file mode 100644 index 0000000..9927956 --- /dev/null +++ b/content/docs/references/ai/PredictionRequest.mdx @@ -0,0 +1,14 @@ +--- +title: PredictionRequest +description: PredictionRequest Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **modelName** | `string` | ✅ | Model to use for prediction | +| **recordIds** | `string[]` | optional | Specific records to predict (if not provided, uses all) | +| **inputData** | `Record` | optional | Direct input data (alternative to recordIds) | +| **returnConfidence** | `boolean` | optional | | +| **returnExplanation** | `boolean` | optional | | diff --git a/content/docs/references/ai/PredictionResult.mdx b/content/docs/references/ai/PredictionResult.mdx new file mode 100644 index 0000000..6c9a6c5 --- /dev/null +++ b/content/docs/references/ai/PredictionResult.mdx @@ -0,0 +1,17 @@ +--- +title: PredictionResult +description: PredictionResult Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **modelName** | `string` | ✅ | | +| **modelVersion** | `string` | ✅ | | +| **recordId** | `string` | optional | | +| **prediction** | `any` | optional | The predicted value | +| **confidence** | `number` | optional | Confidence score (0-1) | +| **probabilities** | `Record` | optional | Class probabilities (for classification) | +| **explanation** | `object` | optional | | +| **metadata** | `object` | optional | | diff --git a/content/docs/references/ai/PredictiveModel.mdx b/content/docs/references/ai/PredictiveModel.mdx new file mode 100644 index 0000000..f408f12 --- /dev/null +++ b/content/docs/references/ai/PredictiveModel.mdx @@ -0,0 +1,40 @@ +--- +title: PredictiveModel +description: PredictiveModel Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Model unique identifier (snake_case) | +| **label** | `string` | ✅ | Model display name | +| **description** | `string` | optional | | +| **type** | `Enum<'classification' \| 'regression' \| 'clustering' \| 'forecasting' \| 'anomaly_detection' \| 'recommendation' \| 'ranking'>` | ✅ | | +| **algorithm** | `string` | optional | Specific algorithm (e.g., "random_forest", "xgboost", "lstm") | +| **objectName** | `string` | ✅ | Target object for predictions | +| **target** | `string` | ✅ | Target field to predict | +| **targetType** | `Enum<'numeric' \| 'categorical' \| 'binary'>` | optional | Target field type | +| **features** | `object[]` | ✅ | Input features for the model | +| **hyperparameters** | `object` | optional | | +| **training** | `object` | optional | | +| **metrics** | `object` | optional | Evaluation metrics from last training | +| **deploymentStatus** | `Enum<'draft' \| 'training' \| 'trained' \| 'deployed' \| 'deprecated'>` | optional | | +| **version** | `string` | optional | | +| **predictionField** | `string` | optional | Field to store predictions | +| **confidenceField** | `string` | optional | Field to store confidence scores | +| **updateTrigger** | `Enum<'on_create' \| 'on_update' \| 'manual' \| 'scheduled'>` | optional | | +| **autoRetrain** | `boolean` | optional | | +| **retrainSchedule** | `string` | optional | Cron expression for auto-retraining | +| **retrainThreshold** | `number` | optional | Performance threshold to trigger retraining | +| **enableExplainability** | `boolean` | optional | Generate feature importance & explanations | +| **enableMonitoring** | `boolean` | optional | | +| **alertOnDrift** | `boolean` | optional | Alert when model drift is detected | +| **active** | `boolean` | optional | | +| **owner** | `string` | optional | User ID of model owner | +| **permissions** | `string[]` | optional | User/group IDs with access | +| **tags** | `string[]` | optional | | +| **category** | `string` | optional | Model category (e.g., "sales", "marketing", "operations") | +| **lastTrainedAt** | `string` | optional | ISO timestamp | +| **createdAt** | `string` | optional | ISO timestamp | +| **updatedAt** | `string` | optional | ISO timestamp | diff --git a/content/docs/references/ai/PredictiveModelType.mdx b/content/docs/references/ai/PredictiveModelType.mdx new file mode 100644 index 0000000..b325066 --- /dev/null +++ b/content/docs/references/ai/PredictiveModelType.mdx @@ -0,0 +1,14 @@ +--- +title: PredictiveModelType +description: PredictiveModelType Schema Reference +--- + +## Allowed Values + +* `classification` +* `regression` +* `clustering` +* `forecasting` +* `anomaly_detection` +* `recommendation` +* `ranking` \ No newline at end of file diff --git a/content/docs/references/ai/TrainingConfig.mdx b/content/docs/references/ai/TrainingConfig.mdx new file mode 100644 index 0000000..f42a11d --- /dev/null +++ b/content/docs/references/ai/TrainingConfig.mdx @@ -0,0 +1,23 @@ +--- +title: TrainingConfig +description: TrainingConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **trainingDataRatio** | `number` | optional | Proportion of data for training | +| **validationDataRatio** | `number` | optional | Proportion for validation | +| **testDataRatio** | `number` | optional | Proportion for testing | +| **dataFilter** | `string` | optional | Formula to filter training data | +| **minRecords** | `integer` | optional | Minimum records required | +| **maxRecords** | `integer` | optional | Maximum records to use | +| **strategy** | `Enum<'full' \| 'incremental' \| 'online' \| 'transfer_learning'>` | optional | | +| **crossValidation** | `boolean` | optional | | +| **folds** | `integer` | optional | Cross-validation folds | +| **earlyStoppingEnabled** | `boolean` | optional | | +| **earlyStoppingPatience** | `integer` | optional | Epochs without improvement before stopping | +| **maxTrainingTime** | `number` | optional | Maximum training time in seconds | +| **gpuEnabled** | `boolean` | optional | | +| **randomSeed** | `integer` | optional | Random seed for reproducibility | diff --git a/content/docs/references/ai/WorkflowFieldCondition.mdx b/content/docs/references/ai/WorkflowFieldCondition.mdx new file mode 100644 index 0000000..c5044e3 --- /dev/null +++ b/content/docs/references/ai/WorkflowFieldCondition.mdx @@ -0,0 +1,12 @@ +--- +title: WorkflowFieldCondition +description: WorkflowFieldCondition Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **field** | `string` | ✅ | Field name to monitor | +| **operator** | `Enum<'changed' \| 'changed_to' \| 'changed_from' \| 'is' \| 'is_not'>` | optional | | +| **value** | `any` | optional | Value to compare against (for changed_to/changed_from/is/is_not) | diff --git a/content/docs/references/ai/WorkflowSchedule.mdx b/content/docs/references/ai/WorkflowSchedule.mdx new file mode 100644 index 0000000..add9349 --- /dev/null +++ b/content/docs/references/ai/WorkflowSchedule.mdx @@ -0,0 +1,16 @@ +--- +title: WorkflowSchedule +description: WorkflowSchedule Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `Enum<'cron' \| 'interval' \| 'daily' \| 'weekly' \| 'monthly'>` | optional | | +| **cron** | `string` | optional | Cron expression (required if type is "cron") | +| **interval** | `number` | optional | Interval in minutes (required if type is "interval") | +| **time** | `string` | optional | Time of day for daily schedules (HH:MM format) | +| **dayOfWeek** | `integer` | optional | Day of week for weekly (0=Sunday) | +| **dayOfMonth** | `integer` | optional | Day of month for monthly | +| **timezone** | `string` | optional | | diff --git a/content/docs/references/ai/config/Feature.mdx b/content/docs/references/ai/config/Feature.mdx new file mode 100644 index 0000000..3a480aa --- /dev/null +++ b/content/docs/references/ai/config/Feature.mdx @@ -0,0 +1,15 @@ +--- +title: Feature +description: Feature Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **code** | `string` | ✅ | Feature code (e.g. core.api_access) | +| **label** | `string` | ✅ | | +| **description** | `string` | optional | | +| **type** | `Enum<'boolean' \| 'counter' \| 'gauge'>` | optional | | +| **unit** | `Enum<'count' \| 'bytes' \| 'seconds' \| 'percent'>` | optional | | +| **requires** | `string[]` | optional | | diff --git a/packages/spec/json-schema/AITask.json b/packages/spec/json-schema/AITask.json new file mode 100644 index 0000000..0b098e8 --- /dev/null +++ b/packages/spec/json-schema/AITask.json @@ -0,0 +1,123 @@ +{ + "$ref": "#/definitions/AITask", + "definitions": { + "AITask": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Optional task ID for referencing" + }, + "name": { + "type": "string", + "description": "Human-readable task name" + }, + "type": { + "type": "string", + "enum": [ + "classify", + "extract", + "summarize", + "generate", + "predict", + "translate", + "sentiment", + "entity_recognition", + "anomaly_detection", + "recommendation" + ] + }, + "model": { + "type": "string", + "description": "Model ID from registry (uses default if not specified)" + }, + "promptTemplate": { + "type": "string", + "description": "Prompt template ID for this task" + }, + "inputFields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Source fields to process (e.g., [\"description\", \"comments\"])" + }, + "inputContext": { + "type": "object", + "additionalProperties": {}, + "description": "Additional context for the AI model" + }, + "outputField": { + "type": "string", + "description": "Target field to store the result" + }, + "outputFormat": { + "type": "string", + "enum": [ + "text", + "json", + "number", + "boolean", + "array" + ], + "default": "text" + }, + "classes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Valid classes for classification tasks" + }, + "multiClass": { + "type": "boolean", + "default": false, + "description": "Allow multiple classes to be selected" + }, + "extractionSchema": { + "type": "object", + "additionalProperties": {}, + "description": "JSON schema for structured extraction" + }, + "maxLength": { + "type": "number", + "description": "Maximum length for generated content" + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2, + "description": "Model temperature override" + }, + "fallbackValue": { + "description": "Fallback value if AI task fails" + }, + "retryAttempts": { + "type": "integer", + "minimum": 0, + "maximum": 5, + "default": 1 + }, + "condition": { + "type": "string", + "description": "Formula condition - task only runs if TRUE" + }, + "description": { + "type": "string" + }, + "active": { + "type": "boolean", + "default": true + } + }, + "required": [ + "name", + "type", + "inputFields", + "outputField" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AITaskType.json b/packages/spec/json-schema/AITaskType.json new file mode 100644 index 0000000..e8deef9 --- /dev/null +++ b/packages/spec/json-schema/AITaskType.json @@ -0,0 +1,21 @@ +{ + "$ref": "#/definitions/AITaskType", + "definitions": { + "AITaskType": { + "type": "string", + "enum": [ + "classify", + "extract", + "summarize", + "generate", + "predict", + "translate", + "sentiment", + "entity_recognition", + "anomaly_detection", + "recommendation" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AIWorkflowAutomation.json b/packages/spec/json-schema/AIWorkflowAutomation.json new file mode 100644 index 0000000..5ba6154 --- /dev/null +++ b/packages/spec/json-schema/AIWorkflowAutomation.json @@ -0,0 +1,379 @@ +{ + "$ref": "#/definitions/AIWorkflowAutomation", + "definitions": { + "AIWorkflowAutomation": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Workflow unique identifier (snake_case)" + }, + "label": { + "type": "string", + "description": "Workflow display name" + }, + "description": { + "type": "string" + }, + "objectName": { + "type": "string", + "description": "Target object for this workflow" + }, + "trigger": { + "type": "string", + "enum": [ + "record_created", + "record_updated", + "field_changed", + "scheduled", + "manual", + "webhook", + "batch" + ] + }, + "fieldConditions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string", + "description": "Field name to monitor" + }, + "operator": { + "type": "string", + "enum": [ + "changed", + "changed_to", + "changed_from", + "is", + "is_not" + ], + "default": "changed" + }, + "value": { + "description": "Value to compare against (for changed_to/changed_from/is/is_not)" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "Fields to monitor (for field_changed trigger)" + }, + "schedule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "cron", + "interval", + "daily", + "weekly", + "monthly" + ], + "default": "cron" + }, + "cron": { + "type": "string", + "description": "Cron expression (required if type is \"cron\")" + }, + "interval": { + "type": "number", + "description": "Interval in minutes (required if type is \"interval\")" + }, + "time": { + "type": "string", + "description": "Time of day for daily schedules (HH:MM format)" + }, + "dayOfWeek": { + "type": "integer", + "minimum": 0, + "maximum": 6, + "description": "Day of week for weekly (0=Sunday)" + }, + "dayOfMonth": { + "type": "integer", + "minimum": 1, + "maximum": 31, + "description": "Day of month for monthly" + }, + "timezone": { + "type": "string", + "default": "UTC" + } + }, + "additionalProperties": false, + "description": "Schedule configuration (for scheduled trigger)" + }, + "webhookConfig": { + "type": "object", + "properties": { + "secret": { + "type": "string", + "description": "Webhook verification secret" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Expected headers" + } + }, + "additionalProperties": false, + "description": "Webhook configuration (for webhook trigger)" + }, + "entryCriteria": { + "type": "string", + "description": "Formula condition - workflow only runs if TRUE" + }, + "aiTasks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Optional task ID for referencing" + }, + "name": { + "type": "string", + "description": "Human-readable task name" + }, + "type": { + "type": "string", + "enum": [ + "classify", + "extract", + "summarize", + "generate", + "predict", + "translate", + "sentiment", + "entity_recognition", + "anomaly_detection", + "recommendation" + ] + }, + "model": { + "type": "string", + "description": "Model ID from registry (uses default if not specified)" + }, + "promptTemplate": { + "type": "string", + "description": "Prompt template ID for this task" + }, + "inputFields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Source fields to process (e.g., [\"description\", \"comments\"])" + }, + "inputContext": { + "type": "object", + "additionalProperties": {}, + "description": "Additional context for the AI model" + }, + "outputField": { + "type": "string", + "description": "Target field to store the result" + }, + "outputFormat": { + "type": "string", + "enum": [ + "text", + "json", + "number", + "boolean", + "array" + ], + "default": "text" + }, + "classes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Valid classes for classification tasks" + }, + "multiClass": { + "type": "boolean", + "default": false, + "description": "Allow multiple classes to be selected" + }, + "extractionSchema": { + "type": "object", + "additionalProperties": {}, + "description": "JSON schema for structured extraction" + }, + "maxLength": { + "type": "number", + "description": "Maximum length for generated content" + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2, + "description": "Model temperature override" + }, + "fallbackValue": { + "description": "Fallback value if AI task fails" + }, + "retryAttempts": { + "type": "integer", + "minimum": 0, + "maximum": 5, + "default": 1 + }, + "condition": { + "type": "string", + "description": "Formula condition - task only runs if TRUE" + }, + "description": { + "type": "string" + }, + "active": { + "type": "boolean", + "default": true + } + }, + "required": [ + "name", + "type", + "inputFields", + "outputField" + ], + "additionalProperties": false + }, + "description": "AI tasks to execute in sequence" + }, + "postActions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "field_update", + "send_email", + "create_record", + "update_related", + "trigger_flow", + "webhook" + ] + }, + "name": { + "type": "string", + "description": "Action name" + }, + "config": { + "type": "object", + "additionalProperties": {}, + "description": "Action-specific configuration" + }, + "condition": { + "type": "string", + "description": "Execute only if condition is TRUE" + } + }, + "required": [ + "type", + "name", + "config" + ], + "additionalProperties": false + }, + "description": "Actions after AI tasks complete" + }, + "executionMode": { + "type": "string", + "enum": [ + "sequential", + "parallel" + ], + "default": "sequential", + "description": "How to execute multiple AI tasks" + }, + "stopOnError": { + "type": "boolean", + "default": false, + "description": "Stop workflow if any task fails" + }, + "timeout": { + "type": "number", + "description": "Maximum execution time in seconds" + }, + "priority": { + "type": "string", + "enum": [ + "low", + "normal", + "high", + "critical" + ], + "default": "normal" + }, + "enableLogging": { + "type": "boolean", + "default": true + }, + "enableMetrics": { + "type": "boolean", + "default": true + }, + "notifyOnFailure": { + "type": "array", + "items": { + "type": "string" + }, + "description": "User IDs to notify on failure" + }, + "active": { + "type": "boolean", + "default": true + }, + "version": { + "type": "string", + "default": "1.0.0" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "category": { + "type": "string", + "description": "Workflow category (e.g., \"support\", \"sales\", \"hr\")" + }, + "owner": { + "type": "string", + "description": "User ID of workflow owner" + }, + "createdAt": { + "type": "string", + "description": "ISO timestamp" + }, + "updatedAt": { + "type": "string", + "description": "ISO timestamp" + } + }, + "required": [ + "name", + "label", + "objectName", + "trigger", + "aiTasks" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AIWorkflowExecutionResult.json b/packages/spec/json-schema/AIWorkflowExecutionResult.json new file mode 100644 index 0000000..4233dc3 --- /dev/null +++ b/packages/spec/json-schema/AIWorkflowExecutionResult.json @@ -0,0 +1,105 @@ +{ + "$ref": "#/definitions/AIWorkflowExecutionResult", + "definitions": { + "AIWorkflowExecutionResult": { + "type": "object", + "properties": { + "workflowName": { + "type": "string" + }, + "recordId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "success", + "partial_success", + "failed", + "skipped" + ] + }, + "executionTime": { + "type": "number", + "description": "Execution time in milliseconds" + }, + "tasksExecuted": { + "type": "integer", + "description": "Number of tasks executed" + }, + "tasksSucceeded": { + "type": "integer", + "description": "Number of tasks succeeded" + }, + "tasksFailed": { + "type": "integer", + "description": "Number of tasks failed" + }, + "taskResults": { + "type": "array", + "items": { + "type": "object", + "properties": { + "taskId": { + "type": "string" + }, + "taskName": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "success", + "failed", + "skipped" + ] + }, + "output": {}, + "error": { + "type": "string" + }, + "executionTime": { + "type": "number", + "description": "Task execution time in milliseconds" + }, + "modelUsed": { + "type": "string" + }, + "tokensUsed": { + "type": "number" + } + }, + "required": [ + "taskName", + "status" + ], + "additionalProperties": false + } + }, + "error": { + "type": "string" + }, + "startedAt": { + "type": "string", + "description": "ISO timestamp" + }, + "completedAt": { + "type": "string", + "description": "ISO timestamp" + } + }, + "required": [ + "workflowName", + "recordId", + "status", + "executionTime", + "tasksExecuted", + "tasksSucceeded", + "tasksFailed", + "startedAt" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AIWorkflowTrigger.json b/packages/spec/json-schema/AIWorkflowTrigger.json new file mode 100644 index 0000000..531ac4e --- /dev/null +++ b/packages/spec/json-schema/AIWorkflowTrigger.json @@ -0,0 +1,18 @@ +{ + "$ref": "#/definitions/AIWorkflowTrigger", + "definitions": { + "AIWorkflowTrigger": { + "type": "string", + "enum": [ + "record_created", + "record_updated", + "field_changed", + "scheduled", + "manual", + "webhook", + "batch" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/BatchAIWorkflowExecution.json b/packages/spec/json-schema/BatchAIWorkflowExecution.json new file mode 100644 index 0000000..90452a6 --- /dev/null +++ b/packages/spec/json-schema/BatchAIWorkflowExecution.json @@ -0,0 +1,48 @@ +{ + "$ref": "#/definitions/BatchAIWorkflowExecution", + "definitions": { + "BatchAIWorkflowExecution": { + "type": "object", + "properties": { + "workflowName": { + "type": "string", + "description": "Workflow to execute" + }, + "recordIds": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Records to process" + }, + "batchSize": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "default": 10 + }, + "parallelism": { + "type": "integer", + "minimum": 1, + "maximum": 10, + "default": 3 + }, + "priority": { + "type": "string", + "enum": [ + "low", + "normal", + "high" + ], + "default": "normal" + } + }, + "required": [ + "workflowName", + "recordIds" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/EvaluationMetrics.json b/packages/spec/json-schema/EvaluationMetrics.json new file mode 100644 index 0000000..74a3154 --- /dev/null +++ b/packages/spec/json-schema/EvaluationMetrics.json @@ -0,0 +1,64 @@ +{ + "$ref": "#/definitions/EvaluationMetrics", + "definitions": { + "EvaluationMetrics": { + "type": "object", + "properties": { + "accuracy": { + "type": "number" + }, + "precision": { + "type": "number" + }, + "recall": { + "type": "number" + }, + "f1Score": { + "type": "number" + }, + "auc": { + "type": "number", + "description": "Area Under ROC Curve" + }, + "mse": { + "type": "number", + "description": "Mean Squared Error" + }, + "rmse": { + "type": "number", + "description": "Root Mean Squared Error" + }, + "mae": { + "type": "number", + "description": "Mean Absolute Error" + }, + "r2Score": { + "type": "number", + "description": "R-squared score" + }, + "silhouetteScore": { + "type": "number" + }, + "daviesBouldinIndex": { + "type": "number" + }, + "mape": { + "type": "number", + "description": "Mean Absolute Percentage Error" + }, + "smape": { + "type": "number", + "description": "Symmetric MAPE" + }, + "custom": { + "type": "object", + "additionalProperties": { + "type": "number" + } + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/Hyperparameters.json b/packages/spec/json-schema/Hyperparameters.json new file mode 100644 index 0000000..94010bd --- /dev/null +++ b/packages/spec/json-schema/Hyperparameters.json @@ -0,0 +1,80 @@ +{ + "$ref": "#/definitions/Hyperparameters", + "definitions": { + "Hyperparameters": { + "type": "object", + "properties": { + "learningRate": { + "type": "number", + "description": "Learning rate for training" + }, + "epochs": { + "type": "integer", + "description": "Number of training epochs" + }, + "batchSize": { + "type": "integer", + "description": "Training batch size" + }, + "maxDepth": { + "type": "integer", + "description": "Maximum tree depth" + }, + "numTrees": { + "type": "integer", + "description": "Number of trees in ensemble" + }, + "minSamplesSplit": { + "type": "integer", + "description": "Minimum samples to split node" + }, + "minSamplesLeaf": { + "type": "integer", + "description": "Minimum samples in leaf node" + }, + "hiddenLayers": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "Hidden layer sizes" + }, + "activation": { + "type": "string", + "description": "Activation function" + }, + "dropout": { + "type": "number", + "description": "Dropout rate" + }, + "l1Regularization": { + "type": "number", + "description": "L1 regularization strength" + }, + "l2Regularization": { + "type": "number", + "description": "L2 regularization strength" + }, + "numClusters": { + "type": "integer", + "description": "Number of clusters (k-means, etc.)" + }, + "seasonalPeriod": { + "type": "integer", + "description": "Seasonal period for time series" + }, + "forecastHorizon": { + "type": "integer", + "description": "Number of periods to forecast" + }, + "custom": { + "type": "object", + "additionalProperties": {}, + "description": "Algorithm-specific parameters" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ModelDrift.json b/packages/spec/json-schema/ModelDrift.json new file mode 100644 index 0000000..8e4c9f6 --- /dev/null +++ b/packages/spec/json-schema/ModelDrift.json @@ -0,0 +1,73 @@ +{ + "$ref": "#/definitions/ModelDrift", + "definitions": { + "ModelDrift": { + "type": "object", + "properties": { + "modelName": { + "type": "string" + }, + "driftType": { + "type": "string", + "enum": [ + "feature_drift", + "prediction_drift", + "performance_drift" + ] + }, + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "critical" + ] + }, + "detectedAt": { + "type": "string", + "description": "ISO timestamp" + }, + "metrics": { + "type": "object", + "properties": { + "driftScore": { + "type": "number", + "description": "Drift magnitude (0-1)" + }, + "affectedFeatures": { + "type": "array", + "items": { + "type": "string" + } + }, + "performanceChange": { + "type": "number", + "description": "Change in performance metric" + } + }, + "required": [ + "driftScore" + ], + "additionalProperties": false + }, + "recommendation": { + "type": "string" + }, + "autoRetrainTriggered": { + "type": "boolean", + "default": false + } + }, + "required": [ + "modelName", + "driftType", + "severity", + "detectedAt", + "metrics" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/PostProcessingAction.json b/packages/spec/json-schema/PostProcessingAction.json new file mode 100644 index 0000000..0729bc6 --- /dev/null +++ b/packages/spec/json-schema/PostProcessingAction.json @@ -0,0 +1,41 @@ +{ + "$ref": "#/definitions/PostProcessingAction", + "definitions": { + "PostProcessingAction": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "field_update", + "send_email", + "create_record", + "update_related", + "trigger_flow", + "webhook" + ] + }, + "name": { + "type": "string", + "description": "Action name" + }, + "config": { + "type": "object", + "additionalProperties": {}, + "description": "Action-specific configuration" + }, + "condition": { + "type": "string", + "description": "Execute only if condition is TRUE" + } + }, + "required": [ + "type", + "name", + "config" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/PredictionRequest.json b/packages/spec/json-schema/PredictionRequest.json new file mode 100644 index 0000000..1b7d8b7 --- /dev/null +++ b/packages/spec/json-schema/PredictionRequest.json @@ -0,0 +1,39 @@ +{ + "$ref": "#/definitions/PredictionRequest", + "definitions": { + "PredictionRequest": { + "type": "object", + "properties": { + "modelName": { + "type": "string", + "description": "Model to use for prediction" + }, + "recordIds": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Specific records to predict (if not provided, uses all)" + }, + "inputData": { + "type": "object", + "additionalProperties": {}, + "description": "Direct input data (alternative to recordIds)" + }, + "returnConfidence": { + "type": "boolean", + "default": true + }, + "returnExplanation": { + "type": "boolean", + "default": false + } + }, + "required": [ + "modelName" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/PredictionResult.json b/packages/spec/json-schema/PredictionResult.json new file mode 100644 index 0000000..fef3097 --- /dev/null +++ b/packages/spec/json-schema/PredictionResult.json @@ -0,0 +1,82 @@ +{ + "$ref": "#/definitions/PredictionResult", + "definitions": { + "PredictionResult": { + "type": "object", + "properties": { + "modelName": { + "type": "string" + }, + "modelVersion": { + "type": "string" + }, + "recordId": { + "type": "string" + }, + "prediction": { + "description": "The predicted value" + }, + "confidence": { + "type": "number", + "description": "Confidence score (0-1)" + }, + "probabilities": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "Class probabilities (for classification)" + }, + "explanation": { + "type": "object", + "properties": { + "topFeatures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string" + }, + "importance": { + "type": "number" + }, + "value": {} + }, + "required": [ + "feature", + "importance" + ], + "additionalProperties": false + } + }, + "reasoning": { + "type": "string" + } + }, + "additionalProperties": false + }, + "metadata": { + "type": "object", + "properties": { + "executionTime": { + "type": "number", + "description": "Execution time in milliseconds" + }, + "timestamp": { + "type": "string", + "description": "ISO timestamp" + } + }, + "additionalProperties": false + } + }, + "required": [ + "modelName", + "modelVersion" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/PredictiveModel.json b/packages/spec/json-schema/PredictiveModel.json new file mode 100644 index 0000000..74fc3bb --- /dev/null +++ b/packages/spec/json-schema/PredictiveModel.json @@ -0,0 +1,445 @@ +{ + "$ref": "#/definitions/PredictiveModel", + "definitions": { + "PredictiveModel": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Model unique identifier (snake_case)" + }, + "label": { + "type": "string", + "description": "Model display name" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "classification", + "regression", + "clustering", + "forecasting", + "anomaly_detection", + "recommendation", + "ranking" + ] + }, + "algorithm": { + "type": "string", + "description": "Specific algorithm (e.g., \"random_forest\", \"xgboost\", \"lstm\")" + }, + "objectName": { + "type": "string", + "description": "Target object for predictions" + }, + "target": { + "type": "string", + "description": "Target field to predict" + }, + "targetType": { + "type": "string", + "enum": [ + "numeric", + "categorical", + "binary" + ], + "description": "Target field type" + }, + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Feature name (snake_case)" + }, + "label": { + "type": "string", + "description": "Human-readable label" + }, + "field": { + "type": "string", + "description": "Source field name" + }, + "object": { + "type": "string", + "description": "Source object (if different from target)" + }, + "dataType": { + "type": "string", + "enum": [ + "numeric", + "categorical", + "text", + "datetime", + "boolean" + ], + "description": "Feature data type" + }, + "transformation": { + "type": "string", + "enum": [ + "none", + "normalize", + "standardize", + "one_hot_encode", + "label_encode", + "log_transform", + "binning", + "embedding" + ], + "default": "none" + }, + "required": { + "type": "boolean", + "default": true + }, + "defaultValue": {}, + "description": { + "type": "string" + }, + "importance": { + "type": "number", + "description": "Feature importance score (0-1)" + } + }, + "required": [ + "name", + "field", + "dataType" + ], + "additionalProperties": false + }, + "description": "Input features for the model" + }, + "hyperparameters": { + "type": "object", + "properties": { + "learningRate": { + "type": "number", + "description": "Learning rate for training" + }, + "epochs": { + "type": "integer", + "description": "Number of training epochs" + }, + "batchSize": { + "type": "integer", + "description": "Training batch size" + }, + "maxDepth": { + "type": "integer", + "description": "Maximum tree depth" + }, + "numTrees": { + "type": "integer", + "description": "Number of trees in ensemble" + }, + "minSamplesSplit": { + "type": "integer", + "description": "Minimum samples to split node" + }, + "minSamplesLeaf": { + "type": "integer", + "description": "Minimum samples in leaf node" + }, + "hiddenLayers": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "Hidden layer sizes" + }, + "activation": { + "type": "string", + "description": "Activation function" + }, + "dropout": { + "type": "number", + "description": "Dropout rate" + }, + "l1Regularization": { + "type": "number", + "description": "L1 regularization strength" + }, + "l2Regularization": { + "type": "number", + "description": "L2 regularization strength" + }, + "numClusters": { + "type": "integer", + "description": "Number of clusters (k-means, etc.)" + }, + "seasonalPeriod": { + "type": "integer", + "description": "Seasonal period for time series" + }, + "forecastHorizon": { + "type": "integer", + "description": "Number of periods to forecast" + }, + "custom": { + "type": "object", + "additionalProperties": {}, + "description": "Algorithm-specific parameters" + } + }, + "additionalProperties": false + }, + "training": { + "type": "object", + "properties": { + "trainingDataRatio": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.8, + "description": "Proportion of data for training" + }, + "validationDataRatio": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "description": "Proportion for validation" + }, + "testDataRatio": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "description": "Proportion for testing" + }, + "dataFilter": { + "type": "string", + "description": "Formula to filter training data" + }, + "minRecords": { + "type": "integer", + "default": 100, + "description": "Minimum records required" + }, + "maxRecords": { + "type": "integer", + "description": "Maximum records to use" + }, + "strategy": { + "type": "string", + "enum": [ + "full", + "incremental", + "online", + "transfer_learning" + ], + "default": "full" + }, + "crossValidation": { + "type": "boolean", + "default": true + }, + "folds": { + "type": "integer", + "minimum": 2, + "maximum": 10, + "default": 5, + "description": "Cross-validation folds" + }, + "earlyStoppingEnabled": { + "type": "boolean", + "default": true + }, + "earlyStoppingPatience": { + "type": "integer", + "default": 10, + "description": "Epochs without improvement before stopping" + }, + "maxTrainingTime": { + "type": "number", + "description": "Maximum training time in seconds" + }, + "gpuEnabled": { + "type": "boolean", + "default": false + }, + "randomSeed": { + "type": "integer", + "description": "Random seed for reproducibility" + } + }, + "additionalProperties": false + }, + "metrics": { + "type": "object", + "properties": { + "accuracy": { + "type": "number" + }, + "precision": { + "type": "number" + }, + "recall": { + "type": "number" + }, + "f1Score": { + "type": "number" + }, + "auc": { + "type": "number", + "description": "Area Under ROC Curve" + }, + "mse": { + "type": "number", + "description": "Mean Squared Error" + }, + "rmse": { + "type": "number", + "description": "Root Mean Squared Error" + }, + "mae": { + "type": "number", + "description": "Mean Absolute Error" + }, + "r2Score": { + "type": "number", + "description": "R-squared score" + }, + "silhouetteScore": { + "type": "number" + }, + "daviesBouldinIndex": { + "type": "number" + }, + "mape": { + "type": "number", + "description": "Mean Absolute Percentage Error" + }, + "smape": { + "type": "number", + "description": "Symmetric MAPE" + }, + "custom": { + "type": "object", + "additionalProperties": { + "type": "number" + } + } + }, + "additionalProperties": false, + "description": "Evaluation metrics from last training" + }, + "deploymentStatus": { + "type": "string", + "enum": [ + "draft", + "training", + "trained", + "deployed", + "deprecated" + ], + "default": "draft" + }, + "version": { + "type": "string", + "default": "1.0.0" + }, + "predictionField": { + "type": "string", + "description": "Field to store predictions" + }, + "confidenceField": { + "type": "string", + "description": "Field to store confidence scores" + }, + "updateTrigger": { + "type": "string", + "enum": [ + "on_create", + "on_update", + "manual", + "scheduled" + ], + "default": "on_create" + }, + "autoRetrain": { + "type": "boolean", + "default": false + }, + "retrainSchedule": { + "type": "string", + "description": "Cron expression for auto-retraining" + }, + "retrainThreshold": { + "type": "number", + "description": "Performance threshold to trigger retraining" + }, + "enableExplainability": { + "type": "boolean", + "default": false, + "description": "Generate feature importance & explanations" + }, + "enableMonitoring": { + "type": "boolean", + "default": true + }, + "alertOnDrift": { + "type": "boolean", + "default": true, + "description": "Alert when model drift is detected" + }, + "active": { + "type": "boolean", + "default": true + }, + "owner": { + "type": "string", + "description": "User ID of model owner" + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "User/group IDs with access" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "category": { + "type": "string", + "description": "Model category (e.g., \"sales\", \"marketing\", \"operations\")" + }, + "lastTrainedAt": { + "type": "string", + "description": "ISO timestamp" + }, + "createdAt": { + "type": "string", + "description": "ISO timestamp" + }, + "updatedAt": { + "type": "string", + "description": "ISO timestamp" + } + }, + "required": [ + "name", + "label", + "type", + "objectName", + "target", + "features" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/PredictiveModelType.json b/packages/spec/json-schema/PredictiveModelType.json new file mode 100644 index 0000000..b836bad --- /dev/null +++ b/packages/spec/json-schema/PredictiveModelType.json @@ -0,0 +1,18 @@ +{ + "$ref": "#/definitions/PredictiveModelType", + "definitions": { + "PredictiveModelType": { + "type": "string", + "enum": [ + "classification", + "regression", + "clustering", + "forecasting", + "anomaly_detection", + "recommendation", + "ranking" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/TrainingConfig.json b/packages/spec/json-schema/TrainingConfig.json new file mode 100644 index 0000000..e7d92e9 --- /dev/null +++ b/packages/spec/json-schema/TrainingConfig.json @@ -0,0 +1,88 @@ +{ + "$ref": "#/definitions/TrainingConfig", + "definitions": { + "TrainingConfig": { + "type": "object", + "properties": { + "trainingDataRatio": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.8, + "description": "Proportion of data for training" + }, + "validationDataRatio": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "description": "Proportion for validation" + }, + "testDataRatio": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "description": "Proportion for testing" + }, + "dataFilter": { + "type": "string", + "description": "Formula to filter training data" + }, + "minRecords": { + "type": "integer", + "default": 100, + "description": "Minimum records required" + }, + "maxRecords": { + "type": "integer", + "description": "Maximum records to use" + }, + "strategy": { + "type": "string", + "enum": [ + "full", + "incremental", + "online", + "transfer_learning" + ], + "default": "full" + }, + "crossValidation": { + "type": "boolean", + "default": true + }, + "folds": { + "type": "integer", + "minimum": 2, + "maximum": 10, + "default": 5, + "description": "Cross-validation folds" + }, + "earlyStoppingEnabled": { + "type": "boolean", + "default": true + }, + "earlyStoppingPatience": { + "type": "integer", + "default": 10, + "description": "Epochs without improvement before stopping" + }, + "maxTrainingTime": { + "type": "number", + "description": "Maximum training time in seconds" + }, + "gpuEnabled": { + "type": "boolean", + "default": false + }, + "randomSeed": { + "type": "integer", + "description": "Random seed for reproducibility" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/WorkflowFieldCondition.json b/packages/spec/json-schema/WorkflowFieldCondition.json new file mode 100644 index 0000000..d4d31c9 --- /dev/null +++ b/packages/spec/json-schema/WorkflowFieldCondition.json @@ -0,0 +1,33 @@ +{ + "$ref": "#/definitions/WorkflowFieldCondition", + "definitions": { + "WorkflowFieldCondition": { + "type": "object", + "properties": { + "field": { + "type": "string", + "description": "Field name to monitor" + }, + "operator": { + "type": "string", + "enum": [ + "changed", + "changed_to", + "changed_from", + "is", + "is_not" + ], + "default": "changed" + }, + "value": { + "description": "Value to compare against (for changed_to/changed_from/is/is_not)" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/WorkflowSchedule.json b/packages/spec/json-schema/WorkflowSchedule.json new file mode 100644 index 0000000..6794420 --- /dev/null +++ b/packages/spec/json-schema/WorkflowSchedule.json @@ -0,0 +1,51 @@ +{ + "$ref": "#/definitions/WorkflowSchedule", + "definitions": { + "WorkflowSchedule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "cron", + "interval", + "daily", + "weekly", + "monthly" + ], + "default": "cron" + }, + "cron": { + "type": "string", + "description": "Cron expression (required if type is \"cron\")" + }, + "interval": { + "type": "number", + "description": "Interval in minutes (required if type is \"interval\")" + }, + "time": { + "type": "string", + "description": "Time of day for daily schedules (HH:MM format)" + }, + "dayOfWeek": { + "type": "integer", + "minimum": 0, + "maximum": 6, + "description": "Day of week for weekly (0=Sunday)" + }, + "dayOfMonth": { + "type": "integer", + "minimum": 1, + "maximum": 31, + "description": "Day of month for monthly" + }, + "timezone": { + "type": "string", + "default": "UTC" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/src/ai/index.ts b/packages/spec/src/ai/index.ts index 6faa104..431b93c 100644 --- a/packages/spec/src/ai/index.ts +++ b/packages/spec/src/ai/index.ts @@ -6,9 +6,13 @@ * - Model Registry & Selection * - RAG Pipeline * - Natural Language Query (NLQ) + * - Workflow Automation + * - Predictive Analytics */ export * from './agent.zod'; export * from './model-registry.zod'; export * from './rag-pipeline.zod'; export * from './nlq.zod'; +export * from './workflow-automation.zod'; +export * from './predictive.zod'; diff --git a/packages/spec/src/ai/predictive.test.ts b/packages/spec/src/ai/predictive.test.ts new file mode 100644 index 0000000..b95f637 --- /dev/null +++ b/packages/spec/src/ai/predictive.test.ts @@ -0,0 +1,1001 @@ +import { describe, it, expect } from 'vitest'; +import { + PredictiveModelSchema, + PredictiveModelTypeSchema, + FeatureSchema, + HyperparametersSchema, + TrainingConfigSchema, + EvaluationMetricsSchema, + PredictionRequestSchema, + PredictionResultSchema, + ModelDriftSchema, + type PredictiveModel, + type Feature, + type PredictionRequest, +} from './predictive.zod'; + +describe('PredictiveModelTypeSchema', () => { + it('should accept all model types', () => { + const types = [ + 'classification', + 'regression', + 'clustering', + 'forecasting', + 'anomaly_detection', + 'recommendation', + 'ranking', + ] as const; + + types.forEach(type => { + expect(() => PredictiveModelTypeSchema.parse(type)).not.toThrow(); + }); + }); +}); + +describe('FeatureSchema', () => { + it('should accept minimal feature', () => { + const feature: Feature = { + name: 'user_age', + field: 'age', + dataType: 'numeric', + }; + + const result = FeatureSchema.parse(feature); + expect(result.transformation).toBe('none'); + expect(result.required).toBe(true); + }); + + it('should enforce snake_case for feature name', () => { + const validNames = ['user_age', 'total_revenue', '_internal_score']; + validNames.forEach(name => { + expect(() => FeatureSchema.parse({ + name, + field: 'test', + dataType: 'numeric', + })).not.toThrow(); + }); + + const invalidNames = ['userAge', 'User-Age', '123age']; + invalidNames.forEach(name => { + expect(() => FeatureSchema.parse({ + name, + field: 'test', + dataType: 'numeric', + })).toThrow(); + }); + }); + + it('should accept feature with transformation', () => { + const feature: Feature = { + name: 'normalized_revenue', + field: 'annual_revenue', + dataType: 'numeric', + transformation: 'normalize', + label: 'Normalized Annual Revenue', + }; + + expect(() => FeatureSchema.parse(feature)).not.toThrow(); + }); + + it('should accept categorical feature', () => { + const feature: Feature = { + name: 'industry_encoded', + field: 'industry', + dataType: 'categorical', + transformation: 'one_hot_encode', + description: 'Industry category with one-hot encoding', + }; + + expect(() => FeatureSchema.parse(feature)).not.toThrow(); + }); + + it('should accept feature from related object', () => { + const feature: Feature = { + name: 'account_revenue', + field: 'annual_revenue', + object: 'account', + dataType: 'numeric', + transformation: 'log_transform', + }; + + expect(() => FeatureSchema.parse(feature)).not.toThrow(); + }); +}); + +describe('HyperparametersSchema', () => { + it('should accept empty hyperparameters', () => { + const params = {}; + expect(() => HyperparametersSchema.parse(params)).not.toThrow(); + }); + + it('should accept general training parameters', () => { + const params = { + learningRate: 0.001, + epochs: 100, + batchSize: 32, + }; + + expect(() => HyperparametersSchema.parse(params)).not.toThrow(); + }); + + it('should accept tree-based model parameters', () => { + const params = { + maxDepth: 10, + numTrees: 100, + minSamplesSplit: 2, + minSamplesLeaf: 1, + }; + + expect(() => HyperparametersSchema.parse(params)).not.toThrow(); + }); + + it('should accept neural network parameters', () => { + const params = { + hiddenLayers: [128, 64, 32], + activation: 'relu', + dropout: 0.2, + learningRate: 0.001, + }; + + expect(() => HyperparametersSchema.parse(params)).not.toThrow(); + }); + + it('should accept clustering parameters', () => { + const params = { + numClusters: 5, + }; + + expect(() => HyperparametersSchema.parse(params)).not.toThrow(); + }); + + it('should accept custom parameters', () => { + const params = { + maxDepth: 5, + custom: { + subsample: 0.8, + colsampleByTree: 0.8, + gamma: 0.1, + }, + }; + + expect(() => HyperparametersSchema.parse(params)).not.toThrow(); + }); +}); + +describe('TrainingConfigSchema', () => { + it('should accept minimal training config', () => { + const config = {}; + const result = TrainingConfigSchema.parse(config); + + expect(result.trainingDataRatio).toBe(0.8); + expect(result.validationDataRatio).toBe(0.1); + expect(result.testDataRatio).toBe(0.1); + expect(result.strategy).toBe('full'); + expect(result.crossValidation).toBe(true); + expect(result.folds).toBe(5); + expect(result.earlyStoppingEnabled).toBe(true); + expect(result.earlyStoppingPatience).toBe(10); + expect(result.gpuEnabled).toBe(false); + }); + + it('should accept custom data split', () => { + const config = { + trainingDataRatio: 0.7, + validationDataRatio: 0.15, + testDataRatio: 0.15, + }; + + expect(() => TrainingConfigSchema.parse(config)).not.toThrow(); + }); + + it('should enforce data ratio constraints', () => { + expect(() => TrainingConfigSchema.parse({ + trainingDataRatio: 1.5, + })).toThrow(); + + expect(() => TrainingConfigSchema.parse({ + trainingDataRatio: -0.1, + })).toThrow(); + }); + + it('should accept training with data filter', () => { + const config = { + dataFilter: 'created_date >= DATE_SUB(NOW(), INTERVAL 1 YEAR)', + minRecords: 1000, + maxRecords: 100000, + }; + + expect(() => TrainingConfigSchema.parse(config)).not.toThrow(); + }); + + it('should accept incremental learning config', () => { + const config = { + strategy: 'incremental' as const, + maxTrainingTime: 3600, + randomSeed: 42, + }; + + expect(() => TrainingConfigSchema.parse(config)).not.toThrow(); + }); + + it('should enforce fold constraints', () => { + expect(() => TrainingConfigSchema.parse({ + folds: 1, + })).toThrow(); + + expect(() => TrainingConfigSchema.parse({ + folds: 11, + })).toThrow(); + + expect(() => TrainingConfigSchema.parse({ + folds: 5, + })).not.toThrow(); + }); +}); + +describe('EvaluationMetricsSchema', () => { + it('should accept classification metrics', () => { + const metrics = { + accuracy: 0.92, + precision: 0.89, + recall: 0.91, + f1Score: 0.90, + auc: 0.95, + }; + + expect(() => EvaluationMetricsSchema.parse(metrics)).not.toThrow(); + }); + + it('should accept regression metrics', () => { + const metrics = { + mse: 0.045, + rmse: 0.212, + mae: 0.18, + r2Score: 0.87, + }; + + expect(() => EvaluationMetricsSchema.parse(metrics)).not.toThrow(); + }); + + it('should accept clustering metrics', () => { + const metrics = { + silhouetteScore: 0.65, + daviesBouldinIndex: 0.42, + }; + + expect(() => EvaluationMetricsSchema.parse(metrics)).not.toThrow(); + }); + + it('should accept custom metrics', () => { + const metrics = { + accuracy: 0.88, + custom: { + topKAccuracy: 0.95, + meanReciprocalRank: 0.82, + }, + }; + + expect(() => EvaluationMetricsSchema.parse(metrics)).not.toThrow(); + }); +}); + +describe('PredictiveModelSchema', () => { + describe('Basic Properties', () => { + it('should accept minimal model', () => { + const model: PredictiveModel = { + name: 'lead_scoring_model', + label: 'Lead Scoring Model', + type: 'classification', + objectName: 'lead', + target: 'converted', + features: [ + { + name: 'engagement_score', + field: 'engagement_score', + dataType: 'numeric', + }, + ], + }; + + const result = PredictiveModelSchema.parse(model); + expect(result.deploymentStatus).toBe('draft'); + expect(result.version).toBe('1.0.0'); + expect(result.updateTrigger).toBe('on_create'); + expect(result.autoRetrain).toBe(false); + expect(result.enableExplainability).toBe(false); + expect(result.enableMonitoring).toBe(true); + expect(result.alertOnDrift).toBe(true); + expect(result.active).toBe(true); + }); + + it('should enforce snake_case for model name', () => { + const validNames = ['lead_scoring', 'churn_prediction', '_experimental_model']; + validNames.forEach(name => { + expect(() => PredictiveModelSchema.parse({ + name, + label: 'Test', + type: 'classification', + objectName: 'test', + target: 'outcome', + features: [{ + name: 'feature1', + field: 'field1', + dataType: 'numeric', + }], + })).not.toThrow(); + }); + + const invalidNames = ['leadScoring', 'Lead-Scoring', '123model']; + invalidNames.forEach(name => { + expect(() => PredictiveModelSchema.parse({ + name, + label: 'Test', + type: 'classification', + objectName: 'test', + target: 'outcome', + features: [{ + name: 'feature1', + field: 'field1', + dataType: 'numeric', + }], + })).toThrow(); + }); + }); + }); + + describe('Classification Models', () => { + it('should accept binary classification model', () => { + const model: PredictiveModel = { + name: 'churn_predictor', + label: 'Customer Churn Predictor', + description: 'Predicts likelihood of customer churn', + type: 'classification', + algorithm: 'random_forest', + objectName: 'customer', + target: 'churned', + targetType: 'binary', + features: [ + { + name: 'account_age', + field: 'created_date', + dataType: 'datetime', + transformation: 'none', + }, + { + name: 'total_spend', + field: 'lifetime_value', + dataType: 'numeric', + transformation: 'log_transform', + }, + { + name: 'support_tickets', + field: 'ticket_count', + dataType: 'numeric', + }, + ], + hyperparameters: { + numTrees: 100, + maxDepth: 10, + minSamplesSplit: 5, + }, + training: { + trainingDataRatio: 0.7, + validationDataRatio: 0.15, + testDataRatio: 0.15, + crossValidation: true, + folds: 5, + }, + metrics: { + accuracy: 0.89, + precision: 0.85, + recall: 0.92, + f1Score: 0.88, + auc: 0.94, + }, + deploymentStatus: 'deployed', + predictionField: 'churn_probability', + confidenceField: 'prediction_confidence', + updateTrigger: 'on_update', + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + + it('should accept multi-class classification model', () => { + const model: PredictiveModel = { + name: 'lead_quality_classifier', + label: 'Lead Quality Classifier', + type: 'classification', + algorithm: 'xgboost', + objectName: 'lead', + target: 'quality_tier', + targetType: 'categorical', + features: [ + { + name: 'company_size', + field: 'employee_count', + dataType: 'numeric', + transformation: 'binning', + }, + { + name: 'industry', + field: 'industry', + dataType: 'categorical', + transformation: 'one_hot_encode', + }, + { + name: 'engagement', + field: 'engagement_score', + dataType: 'numeric', + transformation: 'standardize', + }, + ], + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + }); + + describe('Regression Models', () => { + it('should accept regression model', () => { + const model: PredictiveModel = { + name: 'revenue_forecaster', + label: 'Monthly Revenue Forecaster', + type: 'regression', + algorithm: 'linear_regression', + objectName: 'opportunity', + target: 'amount', + targetType: 'numeric', + features: [ + { + name: 'deal_size', + field: 'estimated_value', + dataType: 'numeric', + transformation: 'normalize', + }, + { + name: 'sales_cycle', + field: 'days_in_pipeline', + dataType: 'numeric', + }, + { + name: 'product_count', + field: 'num_products', + dataType: 'numeric', + }, + ], + hyperparameters: { + l2Regularization: 0.01, + }, + training: { + minRecords: 500, + strategy: 'full', + }, + metrics: { + rmse: 1250.5, + mae: 980.2, + r2Score: 0.85, + }, + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + }); + + describe('Time Series Models', () => { + it('should accept forecasting model', () => { + const model: PredictiveModel = { + name: 'sales_forecaster', + label: 'Sales Forecasting Model', + type: 'forecasting', + algorithm: 'lstm', + objectName: 'sales_data', + target: 'monthly_revenue', + features: [ + { + name: 'historical_revenue', + field: 'revenue', + dataType: 'numeric', + }, + { + name: 'seasonality', + field: 'month', + dataType: 'categorical', + }, + ], + hyperparameters: { + seasonalPeriod: 12, + forecastHorizon: 3, + hiddenLayers: [64, 32], + learningRate: 0.001, + }, + metrics: { + mape: 0.08, + smape: 0.075, + }, + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + }); + + describe('Clustering Models', () => { + it('should accept clustering model', () => { + const model: PredictiveModel = { + name: 'customer_segmentation', + label: 'Customer Segmentation Model', + type: 'clustering', + algorithm: 'kmeans', + objectName: 'customer', + target: 'segment', + features: [ + { + name: 'total_purchases', + field: 'purchase_count', + dataType: 'numeric', + transformation: 'normalize', + }, + { + name: 'avg_order_value', + field: 'average_order', + dataType: 'numeric', + transformation: 'normalize', + }, + { + name: 'recency', + field: 'last_purchase_date', + dataType: 'datetime', + }, + ], + hyperparameters: { + numClusters: 5, + }, + metrics: { + silhouetteScore: 0.68, + }, + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + }); + + describe('Advanced Features', () => { + it('should accept model with auto-retraining', () => { + const model: PredictiveModel = { + name: 'auto_retrain_model', + label: 'Auto-Retrain Model', + type: 'classification', + objectName: 'lead', + target: 'converted', + features: [ + { name: 'score', field: 'score', dataType: 'numeric' }, + ], + autoRetrain: true, + retrainSchedule: '0 2 * * 0', + retrainThreshold: 0.05, + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + + it('should accept model with explainability', () => { + const model: PredictiveModel = { + name: 'explainable_model', + label: 'Explainable Prediction Model', + type: 'classification', + objectName: 'loan_application', + target: 'approved', + features: [ + { name: 'credit_score', field: 'credit_score', dataType: 'numeric' }, + { name: 'income', field: 'annual_income', dataType: 'numeric' }, + ], + enableExplainability: true, + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + + it('should accept complete model with all features', () => { + const model: PredictiveModel = { + name: 'comprehensive_ml_model', + label: 'Comprehensive ML Model', + description: 'Full-featured predictive model', + type: 'classification', + algorithm: 'neural_network', + objectName: 'opportunity', + target: 'will_close', + targetType: 'binary', + features: [ + { + name: 'deal_value', + label: 'Deal Value', + field: 'amount', + dataType: 'numeric', + transformation: 'normalize', + required: true, + description: 'Opportunity amount', + importance: 0.85, + }, + { + name: 'stage_duration', + field: 'days_in_stage', + dataType: 'numeric', + transformation: 'log_transform', + importance: 0.72, + }, + ], + hyperparameters: { + learningRate: 0.001, + epochs: 100, + batchSize: 32, + hiddenLayers: [128, 64, 32], + activation: 'relu', + dropout: 0.2, + l2Regularization: 0.001, + }, + training: { + trainingDataRatio: 0.7, + validationDataRatio: 0.15, + testDataRatio: 0.15, + dataFilter: 'created_date >= "2023-01-01"', + minRecords: 1000, + strategy: 'incremental', + crossValidation: true, + folds: 5, + earlyStoppingEnabled: true, + earlyStoppingPatience: 15, + maxTrainingTime: 7200, + gpuEnabled: true, + randomSeed: 42, + }, + metrics: { + accuracy: 0.91, + precision: 0.88, + recall: 0.93, + f1Score: 0.90, + auc: 0.96, + }, + deploymentStatus: 'deployed', + version: '2.1.0', + predictionField: 'predicted_close_probability', + confidenceField: 'prediction_confidence', + updateTrigger: 'on_update', + autoRetrain: true, + retrainSchedule: '0 3 * * 0', + retrainThreshold: 0.05, + enableExplainability: true, + enableMonitoring: true, + alertOnDrift: true, + active: true, + owner: 'data_science_team', + permissions: ['sales_team', 'analytics_team'], + tags: ['sales', 'ml', 'production'], + category: 'sales', + lastTrainedAt: '2024-01-15T03:00:00Z', + createdAt: '2023-06-01T10:00:00Z', + updatedAt: '2024-01-15T03:30:00Z', + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + }); +}); + +describe('PredictionRequestSchema', () => { + it('should accept minimal prediction request', () => { + const request: PredictionRequest = { + modelName: 'lead_scoring_model', + }; + + const result = PredictionRequestSchema.parse(request); + expect(result.returnConfidence).toBe(true); + expect(result.returnExplanation).toBe(false); + }); + + it('should accept request with record IDs', () => { + const request: PredictionRequest = { + modelName: 'churn_predictor', + recordIds: ['cust_1', 'cust_2', 'cust_3'], + returnExplanation: true, + }; + + expect(() => PredictionRequestSchema.parse(request)).not.toThrow(); + }); + + it('should accept request with direct input data', () => { + const request: PredictionRequest = { + modelName: 'revenue_forecaster', + inputData: { + deal_size: 50000, + sales_cycle: 45, + product_count: 3, + }, + returnConfidence: true, + }; + + expect(() => PredictionRequestSchema.parse(request)).not.toThrow(); + }); +}); + +describe('PredictionResultSchema', () => { + it('should accept basic prediction result', () => { + const result = { + modelName: 'lead_scoring_model', + modelVersion: '1.0.0', + recordId: 'lead_123', + prediction: 0.87, + confidence: 0.92, + }; + + expect(() => PredictionResultSchema.parse(result)).not.toThrow(); + }); + + it('should accept classification result with probabilities', () => { + const result = { + modelName: 'lead_quality_classifier', + modelVersion: '2.0.0', + recordId: 'lead_456', + prediction: 'A', + confidence: 0.85, + probabilities: { + A: 0.85, + B: 0.10, + C: 0.04, + D: 0.01, + }, + }; + + expect(() => PredictionResultSchema.parse(result)).not.toThrow(); + }); + + it('should accept result with explanation', () => { + const result = { + modelName: 'churn_predictor', + modelVersion: '1.5.0', + recordId: 'customer_789', + prediction: true, + confidence: 0.78, + explanation: { + topFeatures: [ + { + feature: 'support_tickets', + importance: 0.42, + value: 15, + }, + { + feature: 'account_age', + importance: 0.28, + value: 90, + }, + { + feature: 'total_spend', + importance: 0.20, + value: 1250.5, + }, + ], + reasoning: 'High support ticket count indicates dissatisfaction', + }, + metadata: { + executionTime: 125, + timestamp: '2024-01-01T12:00:00Z', + }, + }; + + expect(() => PredictionResultSchema.parse(result)).not.toThrow(); + }); +}); + +describe('ModelDriftSchema', () => { + it('should accept feature drift alert', () => { + const drift = { + modelName: 'lead_scoring_model', + driftType: 'feature_drift' as const, + severity: 'medium' as const, + detectedAt: '2024-01-15T10:00:00Z', + metrics: { + driftScore: 0.35, + affectedFeatures: ['engagement_score', 'company_size'], + }, + recommendation: 'Consider retraining the model with recent data', + }; + + expect(() => ModelDriftSchema.parse(drift)).not.toThrow(); + }); + + it('should accept performance drift alert', () => { + const drift = { + modelName: 'churn_predictor', + driftType: 'performance_drift' as const, + severity: 'high' as const, + detectedAt: '2024-01-15T11:30:00Z', + metrics: { + driftScore: 0.62, + performanceChange: -0.12, + }, + recommendation: 'Immediate retraining recommended', + autoRetrainTriggered: true, + }; + + expect(() => ModelDriftSchema.parse(drift)).not.toThrow(); + }); +}); + +describe('Real-World Model Examples', () => { + it('should accept lead conversion prediction model', () => { + const model: PredictiveModel = { + name: 'lead_conversion_predictor', + label: 'Lead Conversion Prediction', + description: 'Predicts probability of lead conversion to opportunity', + type: 'classification', + algorithm: 'gradient_boosting', + objectName: 'lead', + target: 'converted', + targetType: 'binary', + features: [ + { + name: 'engagement_score', + field: 'engagement_score', + dataType: 'numeric', + transformation: 'standardize', + description: 'Composite engagement metric', + }, + { + name: 'company_size', + field: 'employee_count', + dataType: 'numeric', + transformation: 'log_transform', + }, + { + name: 'industry_sector', + field: 'industry', + dataType: 'categorical', + transformation: 'one_hot_encode', + }, + { + name: 'email_interactions', + field: 'email_open_rate', + dataType: 'numeric', + transformation: 'normalize', + }, + { + name: 'website_visits', + field: 'site_visit_count', + dataType: 'numeric', + }, + ], + hyperparameters: { + numTrees: 200, + maxDepth: 8, + learningRate: 0.1, + l2Regularization: 0.01, + }, + training: { + trainingDataRatio: 0.75, + validationDataRatio: 0.15, + testDataRatio: 0.10, + dataFilter: 'created_date >= DATE_SUB(NOW(), INTERVAL 2 YEAR)', + minRecords: 5000, + strategy: 'full', + crossValidation: true, + folds: 5, + }, + metrics: { + accuracy: 0.88, + precision: 0.84, + recall: 0.91, + f1Score: 0.87, + auc: 0.93, + }, + deploymentStatus: 'deployed', + version: '3.2.1', + predictionField: 'conversion_probability', + confidenceField: 'prediction_confidence', + updateTrigger: 'on_update', + autoRetrain: true, + retrainSchedule: '0 2 * * 0', + retrainThreshold: 0.05, + enableExplainability: true, + enableMonitoring: true, + alertOnDrift: true, + active: true, + category: 'sales', + tags: ['lead-scoring', 'conversion', 'ml'], + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + + it('should accept customer lifetime value prediction', () => { + const model: PredictiveModel = { + name: 'customer_ltv_predictor', + label: 'Customer Lifetime Value Prediction', + type: 'regression', + algorithm: 'random_forest', + objectName: 'customer', + target: 'lifetime_value', + targetType: 'numeric', + features: [ + { + name: 'purchase_frequency', + field: 'orders_per_month', + dataType: 'numeric', + transformation: 'standardize', + }, + { + name: 'average_order_value', + field: 'avg_order_amount', + dataType: 'numeric', + transformation: 'log_transform', + }, + { + name: 'customer_tenure', + field: 'days_since_first_purchase', + dataType: 'numeric', + }, + { + name: 'product_categories', + field: 'purchased_categories', + dataType: 'text', + transformation: 'embedding', + }, + ], + hyperparameters: { + numTrees: 150, + maxDepth: 12, + minSamplesSplit: 10, + }, + metrics: { + rmse: 245.8, + mae: 189.2, + r2Score: 0.82, + }, + deploymentStatus: 'deployed', + category: 'marketing', + active: true, + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); + + it('should accept demand forecasting model', () => { + const model: PredictiveModel = { + name: 'product_demand_forecaster', + label: 'Product Demand Forecasting', + type: 'forecasting', + algorithm: 'arima', + objectName: 'inventory', + target: 'demand_quantity', + features: [ + { + name: 'historical_demand', + field: 'daily_sales', + dataType: 'numeric', + }, + { + name: 'day_of_week', + field: 'weekday', + dataType: 'categorical', + }, + { + name: 'promotion_active', + field: 'has_promotion', + dataType: 'boolean', + }, + ], + hyperparameters: { + seasonalPeriod: 7, + forecastHorizon: 14, + }, + metrics: { + mape: 0.12, + smape: 0.115, + }, + deploymentStatus: 'deployed', + updateTrigger: 'scheduled', + category: 'operations', + active: true, + }; + + expect(() => PredictiveModelSchema.parse(model)).not.toThrow(); + }); +}); diff --git a/packages/spec/src/ai/predictive.zod.ts b/packages/spec/src/ai/predictive.zod.ts new file mode 100644 index 0000000..aec7f7e --- /dev/null +++ b/packages/spec/src/ai/predictive.zod.ts @@ -0,0 +1,295 @@ +import { z } from 'zod'; + +/** + * Predictive Analytics Protocol + * + * Defines predictive models and machine learning configurations for + * data-driven decision making and forecasting in ObjectStack applications. + * + * Use Cases: + * - Lead scoring and conversion prediction + * - Customer churn prediction + * - Sales forecasting + * - Demand forecasting + * - Anomaly detection in operational data + * - Customer segmentation and clustering + * - Price optimization + * - Recommendation systems + */ + +/** + * Predictive Model Types + */ +export const PredictiveModelTypeSchema = z.enum([ + 'classification', // Binary or multi-class classification + 'regression', // Numerical prediction + 'clustering', // Unsupervised grouping + 'forecasting', // Time-series prediction + 'anomaly_detection', // Outlier detection + 'recommendation', // Item or action recommendation + 'ranking', // Ordering items by relevance +]); + +/** + * Feature Definition + * Describes an input feature for the model + */ +export const FeatureSchema = z.object({ + /** Feature Identity */ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Feature name (snake_case)'), + label: z.string().optional().describe('Human-readable label'), + + /** Data Source */ + field: z.string().describe('Source field name'), + object: z.string().optional().describe('Source object (if different from target)'), + + /** Feature Type */ + dataType: z.enum(['numeric', 'categorical', 'text', 'datetime', 'boolean']).describe('Feature data type'), + + /** Feature Engineering */ + transformation: z.enum([ + 'none', + 'normalize', // Normalize to 0-1 range + 'standardize', // Z-score standardization + 'one_hot_encode', // One-hot encoding for categorical + 'label_encode', // Label encoding for categorical + 'log_transform', // Logarithmic transformation + 'binning', // Discretize continuous values + 'embedding', // Text/categorical embedding + ]).optional().default('none'), + + /** Configuration */ + required: z.boolean().optional().default(true), + defaultValue: z.any().optional(), + + /** Metadata */ + description: z.string().optional(), + importance: z.number().optional().describe('Feature importance score (0-1)'), +}); + +/** + * Model Hyperparameters + * Configuration specific to model algorithms + */ +export const HyperparametersSchema = z.object({ + /** General Parameters */ + learningRate: z.number().optional().describe('Learning rate for training'), + epochs: z.number().int().optional().describe('Number of training epochs'), + batchSize: z.number().int().optional().describe('Training batch size'), + + /** Tree-based Models (Random Forest, XGBoost, etc.) */ + maxDepth: z.number().int().optional().describe('Maximum tree depth'), + numTrees: z.number().int().optional().describe('Number of trees in ensemble'), + minSamplesSplit: z.number().int().optional().describe('Minimum samples to split node'), + minSamplesLeaf: z.number().int().optional().describe('Minimum samples in leaf node'), + + /** Neural Networks */ + hiddenLayers: z.array(z.number().int()).optional().describe('Hidden layer sizes'), + activation: z.string().optional().describe('Activation function'), + dropout: z.number().optional().describe('Dropout rate'), + + /** Regularization */ + l1Regularization: z.number().optional().describe('L1 regularization strength'), + l2Regularization: z.number().optional().describe('L2 regularization strength'), + + /** Clustering */ + numClusters: z.number().int().optional().describe('Number of clusters (k-means, etc.)'), + + /** Time Series */ + seasonalPeriod: z.number().int().optional().describe('Seasonal period for time series'), + forecastHorizon: z.number().int().optional().describe('Number of periods to forecast'), + + /** Additional custom parameters */ + custom: z.record(z.any()).optional().describe('Algorithm-specific parameters'), +}); + +/** + * Model Training Configuration + */ +export const TrainingConfigSchema = z.object({ + /** Data Split */ + trainingDataRatio: z.number().min(0).max(1).optional().default(0.8).describe('Proportion of data for training'), + validationDataRatio: z.number().min(0).max(1).optional().default(0.1).describe('Proportion for validation'), + testDataRatio: z.number().min(0).max(1).optional().default(0.1).describe('Proportion for testing'), + + /** Data Filtering */ + dataFilter: z.string().optional().describe('Formula to filter training data'), + minRecords: z.number().int().optional().default(100).describe('Minimum records required'), + maxRecords: z.number().int().optional().describe('Maximum records to use'), + + /** Training Strategy */ + strategy: z.enum(['full', 'incremental', 'online', 'transfer_learning']).optional().default('full'), + crossValidation: z.boolean().optional().default(true), + folds: z.number().int().min(2).max(10).optional().default(5).describe('Cross-validation folds'), + + /** Early Stopping */ + earlyStoppingEnabled: z.boolean().optional().default(true), + earlyStoppingPatience: z.number().int().optional().default(10).describe('Epochs without improvement before stopping'), + + /** Resource Limits */ + maxTrainingTime: z.number().optional().describe('Maximum training time in seconds'), + gpuEnabled: z.boolean().optional().default(false), + + /** Reproducibility */ + randomSeed: z.number().int().optional().describe('Random seed for reproducibility'), +}); + +/** + * Model Evaluation Metrics + */ +export const EvaluationMetricsSchema = z.object({ + /** Classification Metrics */ + accuracy: z.number().optional(), + precision: z.number().optional(), + recall: z.number().optional(), + f1Score: z.number().optional(), + auc: z.number().optional().describe('Area Under ROC Curve'), + + /** Regression Metrics */ + mse: z.number().optional().describe('Mean Squared Error'), + rmse: z.number().optional().describe('Root Mean Squared Error'), + mae: z.number().optional().describe('Mean Absolute Error'), + r2Score: z.number().optional().describe('R-squared score'), + + /** Clustering Metrics */ + silhouetteScore: z.number().optional(), + daviesBouldinIndex: z.number().optional(), + + /** Time Series Metrics */ + mape: z.number().optional().describe('Mean Absolute Percentage Error'), + smape: z.number().optional().describe('Symmetric MAPE'), + + /** Additional Metrics */ + custom: z.record(z.number()).optional(), +}); + +/** + * Predictive Model Schema + * Complete definition of a predictive model + */ +export const PredictiveModelSchema = z.object({ + /** Identity */ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Model unique identifier (snake_case)'), + label: z.string().describe('Model display name'), + description: z.string().optional(), + + /** Model Type */ + type: PredictiveModelTypeSchema, + algorithm: z.string().optional().describe('Specific algorithm (e.g., "random_forest", "xgboost", "lstm")'), + + /** Target Object & Field */ + objectName: z.string().describe('Target object for predictions'), + target: z.string().describe('Target field to predict'), + targetType: z.enum(['numeric', 'categorical', 'binary']).optional().describe('Target field type'), + + /** Features */ + features: z.array(FeatureSchema).describe('Input features for the model'), + + /** Hyperparameters */ + hyperparameters: HyperparametersSchema.optional(), + + /** Training Configuration */ + training: TrainingConfigSchema.optional(), + + /** Model Performance */ + metrics: EvaluationMetricsSchema.optional().describe('Evaluation metrics from last training'), + + /** Deployment */ + deploymentStatus: z.enum(['draft', 'training', 'trained', 'deployed', 'deprecated']).optional().default('draft'), + version: z.string().optional().default('1.0.0'), + + /** Prediction Configuration */ + predictionField: z.string().optional().describe('Field to store predictions'), + confidenceField: z.string().optional().describe('Field to store confidence scores'), + updateTrigger: z.enum(['on_create', 'on_update', 'manual', 'scheduled']).optional().default('on_create'), + + /** Retraining */ + autoRetrain: z.boolean().optional().default(false), + retrainSchedule: z.string().optional().describe('Cron expression for auto-retraining'), + retrainThreshold: z.number().optional().describe('Performance threshold to trigger retraining'), + + /** Explainability */ + enableExplainability: z.boolean().optional().default(false).describe('Generate feature importance & explanations'), + + /** Monitoring */ + enableMonitoring: z.boolean().optional().default(true), + alertOnDrift: z.boolean().optional().default(true).describe('Alert when model drift is detected'), + + /** Access Control */ + active: z.boolean().optional().default(true), + owner: z.string().optional().describe('User ID of model owner'), + permissions: z.array(z.string()).optional().describe('User/group IDs with access'), + + /** Metadata */ + tags: z.array(z.string()).optional(), + category: z.string().optional().describe('Model category (e.g., "sales", "marketing", "operations")'), + lastTrainedAt: z.string().optional().describe('ISO timestamp'), + createdAt: z.string().optional().describe('ISO timestamp'), + updatedAt: z.string().optional().describe('ISO timestamp'), +}); + +/** + * Prediction Request + * Request for making predictions using a trained model + */ +export const PredictionRequestSchema = z.object({ + modelName: z.string().describe('Model to use for prediction'), + recordIds: z.array(z.string()).optional().describe('Specific records to predict (if not provided, uses all)'), + inputData: z.record(z.any()).optional().describe('Direct input data (alternative to recordIds)'), + returnConfidence: z.boolean().optional().default(true), + returnExplanation: z.boolean().optional().default(false), +}); + +/** + * Prediction Result + * Result of a prediction request + */ +export const PredictionResultSchema = z.object({ + modelName: z.string(), + modelVersion: z.string(), + recordId: z.string().optional(), + prediction: z.any().describe('The predicted value'), + confidence: z.number().optional().describe('Confidence score (0-1)'), + probabilities: z.record(z.number()).optional().describe('Class probabilities (for classification)'), + explanation: z.object({ + topFeatures: z.array(z.object({ + feature: z.string(), + importance: z.number(), + value: z.any(), + })).optional(), + reasoning: z.string().optional(), + }).optional(), + metadata: z.object({ + executionTime: z.number().optional().describe('Execution time in milliseconds'), + timestamp: z.string().optional().describe('ISO timestamp'), + }).optional(), +}); + +/** + * Model Drift Detection + * Monitoring for model performance degradation + */ +export const ModelDriftSchema = z.object({ + modelName: z.string(), + driftType: z.enum(['feature_drift', 'prediction_drift', 'performance_drift']), + severity: z.enum(['low', 'medium', 'high', 'critical']), + detectedAt: z.string().describe('ISO timestamp'), + metrics: z.object({ + driftScore: z.number().describe('Drift magnitude (0-1)'), + affectedFeatures: z.array(z.string()).optional(), + performanceChange: z.number().optional().describe('Change in performance metric'), + }), + recommendation: z.string().optional(), + autoRetrainTriggered: z.boolean().optional().default(false), +}); + +// Type exports +export type PredictiveModelType = z.infer; +export type Feature = z.infer; +export type Hyperparameters = z.infer; +export type TrainingConfig = z.infer; +export type EvaluationMetrics = z.infer; +export type PredictiveModel = z.infer; +export type PredictionRequest = z.infer; +export type PredictionResult = z.infer; +export type ModelDrift = z.infer; diff --git a/packages/spec/src/ai/workflow-automation.test.ts b/packages/spec/src/ai/workflow-automation.test.ts new file mode 100644 index 0000000..e210259 --- /dev/null +++ b/packages/spec/src/ai/workflow-automation.test.ts @@ -0,0 +1,789 @@ +import { describe, it, expect } from 'vitest'; +import { + AIWorkflowAutomationSchema, + AIWorkflowTriggerSchema, + AITaskTypeSchema, + AITaskSchema, + WorkflowFieldConditionSchema, + WorkflowScheduleSchema, + PostProcessingActionSchema, + BatchAIWorkflowExecutionSchema, + AIWorkflowExecutionResultSchema, + type AIWorkflowAutomation, + type AITask, +} from './workflow-automation.zod'; + +describe('AIWorkflowTriggerSchema', () => { + it('should accept all trigger types', () => { + const triggers = [ + 'record_created', + 'record_updated', + 'field_changed', + 'scheduled', + 'manual', + 'webhook', + 'batch', + ] as const; + + triggers.forEach(trigger => { + expect(() => AIWorkflowTriggerSchema.parse(trigger)).not.toThrow(); + }); + }); +}); + +describe('AITaskTypeSchema', () => { + it('should accept all task types', () => { + const types = [ + 'classify', + 'extract', + 'summarize', + 'generate', + 'predict', + 'translate', + 'sentiment', + 'entity_recognition', + 'anomaly_detection', + 'recommendation', + ] as const; + + types.forEach(type => { + expect(() => AITaskTypeSchema.parse(type)).not.toThrow(); + }); + }); +}); + +describe('AITaskSchema', () => { + it('should accept minimal task configuration', () => { + const task: AITask = { + name: 'Classify ticket', + type: 'classify', + inputFields: ['description'], + outputField: 'category', + }; + + const result = AITaskSchema.parse(task); + expect(result.outputFormat).toBe('text'); + expect(result.active).toBe(true); + expect(result.retryAttempts).toBe(1); + expect(result.multiClass).toBe(false); + }); + + it('should accept classification task with classes', () => { + const task: AITask = { + name: 'Categorize support ticket', + type: 'classify', + inputFields: ['description', 'subject'], + outputField: 'category', + classes: ['bug', 'feature_request', 'question', 'complaint'], + multiClass: true, + }; + + expect(() => AITaskSchema.parse(task)).not.toThrow(); + }); + + it('should accept extraction task with schema', () => { + const task: AITask = { + name: 'Extract contact info', + type: 'extract', + inputFields: ['email_body'], + outputField: 'structured_data', + outputFormat: 'json', + extractionSchema: { + name: { type: 'string' }, + email: { type: 'string', format: 'email' }, + phone: { type: 'string' }, + }, + }; + + expect(() => AITaskSchema.parse(task)).not.toThrow(); + }); + + it('should accept generation task with temperature', () => { + const task: AITask = { + name: 'Generate response', + type: 'generate', + inputFields: ['customer_message'], + outputField: 'suggested_response', + maxLength: 500, + temperature: 0.7, + model: 'gpt-4-turbo', + }; + + expect(() => AITaskSchema.parse(task)).not.toThrow(); + }); + + it('should enforce temperature constraints', () => { + expect(() => AITaskSchema.parse({ + name: 'Test', + type: 'generate', + inputFields: ['input'], + outputField: 'output', + temperature: -0.1, + })).toThrow(); + + expect(() => AITaskSchema.parse({ + name: 'Test', + type: 'generate', + inputFields: ['input'], + outputField: 'output', + temperature: 2.1, + })).toThrow(); + }); + + it('should accept task with fallback and retry', () => { + const task: AITask = { + name: 'Predict score', + type: 'predict', + inputFields: ['engagement_metrics'], + outputField: 'lead_score', + outputFormat: 'number', + fallbackValue: 0, + retryAttempts: 3, + }; + + expect(() => AITaskSchema.parse(task)).not.toThrow(); + }); + + it('should accept task with conditional execution', () => { + const task: AITask = { + name: 'Summarize conversation', + type: 'summarize', + inputFields: ['conversation_history'], + outputField: 'summary', + condition: 'LENGTH(conversation_history) > 1000', + }; + + expect(() => AITaskSchema.parse(task)).not.toThrow(); + }); +}); + +describe('WorkflowFieldConditionSchema', () => { + it('should accept field change condition', () => { + const condition = { + field: 'status', + operator: 'changed' as const, + }; + + const result = WorkflowFieldConditionSchema.parse(condition); + expect(result.operator).toBe('changed'); + }); + + it('should accept field value condition', () => { + const condition = { + field: 'priority', + operator: 'changed_to' as const, + value: 'high', + }; + + expect(() => WorkflowFieldConditionSchema.parse(condition)).not.toThrow(); + }); +}); + +describe('WorkflowScheduleSchema', () => { + it('should accept cron schedule', () => { + const schedule = { + type: 'cron' as const, + cron: '0 9 * * 1-5', + timezone: 'America/New_York', + }; + + expect(() => WorkflowScheduleSchema.parse(schedule)).not.toThrow(); + }); + + it('should accept daily schedule', () => { + const schedule = { + type: 'daily' as const, + time: '09:00', + }; + + const result = WorkflowScheduleSchema.parse(schedule); + expect(result.timezone).toBe('UTC'); + }); + + it('should accept interval schedule', () => { + const schedule = { + type: 'interval' as const, + interval: 60, + }; + + expect(() => WorkflowScheduleSchema.parse(schedule)).not.toThrow(); + }); + + it('should accept weekly schedule', () => { + const schedule = { + type: 'weekly' as const, + dayOfWeek: 1, + time: '10:00', + }; + + expect(() => WorkflowScheduleSchema.parse(schedule)).not.toThrow(); + }); +}); + +describe('PostProcessingActionSchema', () => { + it('should accept all action types', () => { + const types = [ + 'field_update', + 'send_email', + 'create_record', + 'update_related', + 'trigger_flow', + 'webhook', + ] as const; + + types.forEach(type => { + const action = { + type, + name: `${type}_action`, + config: { test: true }, + }; + expect(() => PostProcessingActionSchema.parse(action)).not.toThrow(); + }); + }); + + it('should accept action with condition', () => { + const action = { + type: 'send_email' as const, + name: 'notify_manager', + config: { template: 'high_priority_alert' }, + condition: 'priority == "high"', + }; + + expect(() => PostProcessingActionSchema.parse(action)).not.toThrow(); + }); +}); + +describe('AIWorkflowAutomationSchema', () => { + describe('Basic Properties', () => { + it('should accept minimal workflow', () => { + const workflow: AIWorkflowAutomation = { + name: 'auto_classify_tickets', + label: 'Auto-classify Support Tickets', + objectName: 'support_ticket', + trigger: 'record_created', + aiTasks: [ + { + name: 'Classify ticket', + type: 'classify', + inputFields: ['description'], + outputField: 'category', + classes: ['bug', 'feature', 'question'], + }, + ], + }; + + const result = AIWorkflowAutomationSchema.parse(workflow); + expect(result.active).toBe(true); + expect(result.version).toBe('1.0.0'); + expect(result.executionMode).toBe('sequential'); + expect(result.priority).toBe('normal'); + expect(result.enableLogging).toBe(true); + }); + + it('should enforce snake_case for workflow name', () => { + const validNames = ['auto_classify', 'lead_scoring', '_internal_workflow']; + validNames.forEach(name => { + expect(() => AIWorkflowAutomationSchema.parse({ + name, + label: 'Test', + objectName: 'test_object', + trigger: 'record_created', + aiTasks: [{ + name: 'Task', + type: 'classify', + inputFields: ['field'], + outputField: 'output', + }], + })).not.toThrow(); + }); + + const invalidNames = ['autoClassify', 'Auto-Classify', '123workflow']; + invalidNames.forEach(name => { + expect(() => AIWorkflowAutomationSchema.parse({ + name, + label: 'Test', + objectName: 'test_object', + trigger: 'record_created', + aiTasks: [{ + name: 'Task', + type: 'classify', + inputFields: ['field'], + outputField: 'output', + }], + })).toThrow(); + }); + }); + }); + + describe('Field Change Trigger', () => { + it('should accept field change workflow', () => { + const workflow: AIWorkflowAutomation = { + name: 'priority_change_handler', + label: 'Handle Priority Changes', + objectName: 'ticket', + trigger: 'field_changed', + fieldConditions: [ + { + field: 'priority', + operator: 'changed_to', + value: 'critical', + }, + ], + aiTasks: [ + { + name: 'Generate escalation message', + type: 'generate', + inputFields: ['description', 'history'], + outputField: 'escalation_note', + }, + ], + }; + + expect(() => AIWorkflowAutomationSchema.parse(workflow)).not.toThrow(); + }); + }); + + describe('Scheduled Workflow', () => { + it('should accept scheduled workflow', () => { + const workflow: AIWorkflowAutomation = { + name: 'daily_lead_scoring', + label: 'Daily Lead Scoring', + objectName: 'lead', + trigger: 'scheduled', + schedule: { + type: 'daily', + time: '06:00', + timezone: 'UTC', + }, + aiTasks: [ + { + name: 'Calculate lead score', + type: 'predict', + inputFields: ['engagement_score', 'company_size', 'industry'], + outputField: 'predicted_score', + outputFormat: 'number', + }, + ], + }; + + expect(() => AIWorkflowAutomationSchema.parse(workflow)).not.toThrow(); + }); + }); + + describe('Complex Workflows', () => { + it('should accept workflow with multiple AI tasks', () => { + const workflow: AIWorkflowAutomation = { + name: 'comprehensive_ticket_handler', + label: 'Comprehensive Ticket Handler', + objectName: 'support_ticket', + trigger: 'record_created', + entryCriteria: 'status == "new"', + aiTasks: [ + { + name: 'Extract entities', + type: 'entity_recognition', + inputFields: ['description'], + outputField: 'extracted_entities', + outputFormat: 'json', + }, + { + name: 'Analyze sentiment', + type: 'sentiment', + inputFields: ['description', 'tone'], + outputField: 'sentiment_score', + outputFormat: 'text', + }, + { + name: 'Classify category', + type: 'classify', + inputFields: ['description'], + outputField: 'category', + classes: ['technical', 'billing', 'general'], + }, + { + name: 'Generate summary', + type: 'summarize', + inputFields: ['description'], + outputField: 'summary', + maxLength: 200, + }, + ], + executionMode: 'parallel', + postActions: [ + { + type: 'field_update', + name: 'update_status', + config: { + field: 'status', + value: 'triaged', + }, + }, + ], + }; + + expect(() => AIWorkflowAutomationSchema.parse(workflow)).not.toThrow(); + }); + + it('should accept workflow with all features', () => { + const workflow: AIWorkflowAutomation = { + name: 'advanced_automation', + label: 'Advanced Automation Workflow', + description: 'Comprehensive AI-powered automation', + objectName: 'opportunity', + trigger: 'record_updated', + fieldConditions: [ + { field: 'stage', operator: 'changed' }, + ], + entryCriteria: 'amount > 10000', + aiTasks: [ + { + id: 'task1', + name: 'Predict win probability', + type: 'predict', + model: 'win_probability_model', + inputFields: ['amount', 'stage', 'industry'], + outputField: 'win_probability', + outputFormat: 'number', + fallbackValue: 0.5, + retryAttempts: 2, + condition: 'stage IN ("proposal", "negotiation")', + }, + ], + postActions: [ + { + type: 'send_email', + name: 'notify_sales_manager', + config: { + template: 'high_value_opportunity', + recipients: ['sales_manager'], + }, + condition: 'win_probability > 0.8', + }, + ], + executionMode: 'sequential', + stopOnError: true, + timeout: 300, + priority: 'high', + enableLogging: true, + enableMetrics: true, + notifyOnFailure: ['admin@example.com'], + active: true, + version: '2.1.0', + tags: ['sales', 'ml', 'high-priority'], + category: 'sales', + owner: 'user_123', + }; + + expect(() => AIWorkflowAutomationSchema.parse(workflow)).not.toThrow(); + }); + }); +}); + +describe('BatchAIWorkflowExecutionSchema', () => { + it('should accept batch execution request', () => { + const request = { + workflowName: 'auto_classify_tickets', + recordIds: ['rec_1', 'rec_2', 'rec_3'], + }; + + const result = BatchAIWorkflowExecutionSchema.parse(request); + expect(result.batchSize).toBe(10); + expect(result.parallelism).toBe(3); + expect(result.priority).toBe('normal'); + }); + + it('should accept custom batch configuration', () => { + const request = { + workflowName: 'lead_scoring', + recordIds: Array.from({ length: 100 }, (_, i) => `lead_${i}`), + batchSize: 50, + parallelism: 5, + priority: 'high' as const, + }; + + expect(() => BatchAIWorkflowExecutionSchema.parse(request)).not.toThrow(); + }); + + it('should enforce batch size limits', () => { + expect(() => BatchAIWorkflowExecutionSchema.parse({ + workflowName: 'test', + recordIds: ['rec_1'], + batchSize: 0, + })).toThrow(); + + expect(() => BatchAIWorkflowExecutionSchema.parse({ + workflowName: 'test', + recordIds: ['rec_1'], + batchSize: 1001, + })).toThrow(); + }); +}); + +describe('AIWorkflowExecutionResultSchema', () => { + it('should accept successful execution result', () => { + const result = { + workflowName: 'auto_classify_tickets', + recordId: 'ticket_123', + status: 'success' as const, + executionTime: 1250, + tasksExecuted: 3, + tasksSucceeded: 3, + tasksFailed: 0, + startedAt: '2024-01-01T10:00:00Z', + completedAt: '2024-01-01T10:00:01Z', + }; + + expect(() => AIWorkflowExecutionResultSchema.parse(result)).not.toThrow(); + }); + + it('should accept result with task details', () => { + const result = { + workflowName: 'comprehensive_handler', + recordId: 'rec_456', + status: 'partial_success' as const, + executionTime: 2500, + tasksExecuted: 4, + tasksSucceeded: 3, + tasksFailed: 1, + taskResults: [ + { + taskName: 'Classify', + status: 'success' as const, + output: 'technical', + executionTime: 500, + modelUsed: 'gpt-4', + tokensUsed: 150, + }, + { + taskName: 'Summarize', + status: 'success' as const, + output: 'Customer reports login issue...', + executionTime: 800, + }, + { + taskName: 'Extract entities', + status: 'failed' as const, + error: 'Model timeout', + executionTime: 1200, + }, + ], + startedAt: '2024-01-01T10:00:00Z', + completedAt: '2024-01-01T10:00:02Z', + }; + + expect(() => AIWorkflowExecutionResultSchema.parse(result)).not.toThrow(); + }); + + it('should accept failed execution result', () => { + const result = { + workflowName: 'test_workflow', + recordId: 'rec_789', + status: 'failed' as const, + executionTime: 100, + tasksExecuted: 1, + tasksSucceeded: 0, + tasksFailed: 1, + error: 'Model not found', + startedAt: '2024-01-01T10:00:00Z', + }; + + expect(() => AIWorkflowExecutionResultSchema.parse(result)).not.toThrow(); + }); +}); + +describe('Real-World Workflow Examples', () => { + it('should accept customer support ticket automation', () => { + const workflow: AIWorkflowAutomation = { + name: 'auto_triage_support_tickets', + label: 'Auto-Triage Support Tickets', + description: 'Automatically classify, prioritize, and route support tickets', + objectName: 'support_ticket', + trigger: 'record_created', + aiTasks: [ + { + name: 'Classify ticket type', + type: 'classify', + inputFields: ['subject', 'description'], + outputField: 'ticket_type', + classes: ['bug', 'feature_request', 'question', 'complaint', 'feedback'], + }, + { + name: 'Analyze urgency', + type: 'classify', + inputFields: ['description', 'customer_tier'], + outputField: 'urgency', + classes: ['low', 'medium', 'high', 'critical'], + }, + { + name: 'Detect sentiment', + type: 'sentiment', + inputFields: ['description'], + outputField: 'customer_sentiment', + }, + { + name: 'Generate summary', + type: 'summarize', + inputFields: ['description'], + outputField: 'ai_summary', + maxLength: 150, + }, + ], + postActions: [ + { + type: 'field_update', + name: 'assign_to_team', + config: { + field: 'assigned_team', + formula: 'CASE(ticket_type, "bug", "engineering", "feature_request", "product", "support")', + }, + }, + { + type: 'send_email', + name: 'notify_on_critical', + config: { + template: 'critical_ticket_alert', + recipients: ['support_manager'], + }, + condition: 'urgency == "critical"', + }, + ], + executionMode: 'parallel', + priority: 'high', + active: true, + category: 'support', + tags: ['automation', 'ai', 'support'], + }; + + expect(() => AIWorkflowAutomationSchema.parse(workflow)).not.toThrow(); + }); + + it('should accept lead scoring automation', () => { + const workflow: AIWorkflowAutomation = { + name: 'ai_lead_scoring', + label: 'AI-Powered Lead Scoring', + objectName: 'lead', + trigger: 'scheduled', + schedule: { + type: 'daily', + time: '02:00', + timezone: 'UTC', + }, + entryCriteria: 'status == "new" OR status == "contacted"', + aiTasks: [ + { + name: 'Predict conversion probability', + type: 'predict', + model: 'lead_conversion_model', + inputFields: [ + 'company_size', + 'industry', + 'engagement_score', + 'email_open_rate', + 'website_visits', + ], + outputField: 'ai_score', + outputFormat: 'number', + }, + { + name: 'Classify quality tier', + type: 'classify', + inputFields: ['ai_score', 'company_revenue'], + outputField: 'quality_tier', + classes: ['A', 'B', 'C', 'D'], + }, + { + name: 'Generate insights', + type: 'generate', + inputFields: ['ai_score', 'engagement_score', 'industry'], + outputField: 'sales_insights', + promptTemplate: 'lead_insights_template', + maxLength: 300, + }, + ], + postActions: [ + { + type: 'trigger_flow', + name: 'assign_to_sales_rep', + config: { + flowName: 'lead_assignment_flow', + }, + condition: 'quality_tier IN ("A", "B")', + }, + ], + active: true, + category: 'sales', + tags: ['lead-scoring', 'ml', 'automation'], + }; + + expect(() => AIWorkflowAutomationSchema.parse(workflow)).not.toThrow(); + }); + + it('should accept document processing workflow', () => { + const workflow: AIWorkflowAutomation = { + name: 'process_invoice_documents', + label: 'Process Invoice Documents', + objectName: 'invoice', + trigger: 'record_created', + aiTasks: [ + { + name: 'Extract invoice data', + type: 'extract', + inputFields: ['document_url'], + outputField: 'extracted_data', + outputFormat: 'json', + extractionSchema: { + invoiceNumber: { type: 'string' }, + date: { type: 'string', format: 'date' }, + amount: { type: 'number' }, + vendorName: { type: 'string' }, + items: { + type: 'array', + items: { + type: 'object', + properties: { + description: { type: 'string' }, + quantity: { type: 'number' }, + price: { type: 'number' }, + }, + }, + }, + }, + }, + { + name: 'Detect anomalies', + type: 'anomaly_detection', + inputFields: ['extracted_data.amount', 'vendor_history'], + outputField: 'has_anomaly', + outputFormat: 'boolean', + }, + ], + postActions: [ + { + type: 'field_update', + name: 'populate_fields', + config: { + fields: { + invoice_number: 'extracted_data.invoiceNumber', + invoice_date: 'extracted_data.date', + total_amount: 'extracted_data.amount', + vendor_name: 'extracted_data.vendorName', + }, + }, + }, + { + type: 'trigger_flow', + name: 'flag_for_review', + config: { + flowName: 'manual_review_flow', + }, + condition: 'has_anomaly == true', + }, + ], + active: true, + category: 'finance', + tags: ['document-processing', 'ocr', 'automation'], + }; + + expect(() => AIWorkflowAutomationSchema.parse(workflow)).not.toThrow(); + }); +}); diff --git a/packages/spec/src/ai/workflow-automation.zod.ts b/packages/spec/src/ai/workflow-automation.zod.ts new file mode 100644 index 0000000..e306b8f --- /dev/null +++ b/packages/spec/src/ai/workflow-automation.zod.ts @@ -0,0 +1,235 @@ +import { z } from 'zod'; + +/** + * AI Workflow Automation Protocol + * + * Defines intelligent automation workflows that leverage AI/ML capabilities + * to automate business processes with cognitive tasks like classification, + * extraction, summarization, content generation, and prediction. + * + * Use Cases: + * - Automatically classify support tickets by urgency and route to teams + * - Extract key information from documents and populate fields + * - Generate summaries of long text content + * - Predict lead scores or customer churn risk + * - Auto-fill forms based on natural language input + */ + +/** + * Workflow Trigger Types + * Defines when an AI workflow should be initiated + */ +export const AIWorkflowTriggerSchema = z.enum([ + 'record_created', // When a new record is created + 'record_updated', // When a record is updated + 'field_changed', // When specific field(s) change + 'scheduled', // Time-based trigger (cron) + 'manual', // User-initiated trigger + 'webhook', // External system trigger + 'batch', // Batch processing trigger +]); + +/** + * AI Task Types + * Cognitive operations that can be performed by AI + */ +export const AITaskTypeSchema = z.enum([ + 'classify', // Categorize content into predefined classes + 'extract', // Extract structured data from unstructured content + 'summarize', // Generate concise summaries of text + 'generate', // Generate new content (text, code, etc.) + 'predict', // Make predictions based on historical data + 'translate', // Translate text between languages + 'sentiment', // Analyze sentiment (positive, negative, neutral) + 'entity_recognition', // Identify named entities (people, places, etc.) + 'anomaly_detection', // Detect outliers or unusual patterns + 'recommendation', // Recommend items or actions +]); + +/** + * AI Task Configuration + * Individual AI task within a workflow + */ +export const AITaskSchema = z.object({ + /** Task Identity */ + id: z.string().optional().describe('Optional task ID for referencing'), + name: z.string().describe('Human-readable task name'), + type: AITaskTypeSchema, + + /** Model Configuration */ + model: z.string().optional().describe('Model ID from registry (uses default if not specified)'), + promptTemplate: z.string().optional().describe('Prompt template ID for this task'), + + /** Input Configuration */ + inputFields: z.array(z.string()).describe('Source fields to process (e.g., ["description", "comments"])'), + inputContext: z.record(z.any()).optional().describe('Additional context for the AI model'), + + /** Output Configuration */ + outputField: z.string().describe('Target field to store the result'), + outputFormat: z.enum(['text', 'json', 'number', 'boolean', 'array']).optional().default('text'), + + /** Classification-specific options */ + classes: z.array(z.string()).optional().describe('Valid classes for classification tasks'), + multiClass: z.boolean().optional().default(false).describe('Allow multiple classes to be selected'), + + /** Extraction-specific options */ + extractionSchema: z.record(z.any()).optional().describe('JSON schema for structured extraction'), + + /** Generation-specific options */ + maxLength: z.number().optional().describe('Maximum length for generated content'), + temperature: z.number().min(0).max(2).optional().describe('Model temperature override'), + + /** Error Handling */ + fallbackValue: z.any().optional().describe('Fallback value if AI task fails'), + retryAttempts: z.number().int().min(0).max(5).optional().default(1), + + /** Conditional Execution */ + condition: z.string().optional().describe('Formula condition - task only runs if TRUE'), + + /** Task Metadata */ + description: z.string().optional(), + active: z.boolean().optional().default(true), +}); + +/** + * Workflow Field Condition + * Specifies which field changes trigger the workflow + */ +export const WorkflowFieldConditionSchema = z.object({ + field: z.string().describe('Field name to monitor'), + operator: z.enum(['changed', 'changed_to', 'changed_from', 'is', 'is_not']).optional().default('changed'), + value: z.any().optional().describe('Value to compare against (for changed_to/changed_from/is/is_not)'), +}); + +/** + * Workflow Schedule Configuration + * For time-based workflow execution + */ +export const WorkflowScheduleSchema = z.object({ + type: z.enum(['cron', 'interval', 'daily', 'weekly', 'monthly']).default('cron'), + cron: z.string().optional().describe('Cron expression (required if type is "cron")'), + interval: z.number().optional().describe('Interval in minutes (required if type is "interval")'), + time: z.string().optional().describe('Time of day for daily schedules (HH:MM format)'), + dayOfWeek: z.number().int().min(0).max(6).optional().describe('Day of week for weekly (0=Sunday)'), + dayOfMonth: z.number().int().min(1).max(31).optional().describe('Day of month for monthly'), + timezone: z.string().optional().default('UTC'), +}); + +/** + * Post-Processing Action + * Actions to execute after AI tasks complete + */ +export const PostProcessingActionSchema = z.object({ + type: z.enum(['field_update', 'send_email', 'create_record', 'update_related', 'trigger_flow', 'webhook']), + name: z.string().describe('Action name'), + config: z.record(z.any()).describe('Action-specific configuration'), + condition: z.string().optional().describe('Execute only if condition is TRUE'), +}); + +/** + * AI Workflow Automation Schema + * Complete workflow definition with AI-powered tasks + */ +export const AIWorkflowAutomationSchema = z.object({ + /** Identity */ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Workflow unique identifier (snake_case)'), + label: z.string().describe('Workflow display name'), + description: z.string().optional(), + + /** Target Object */ + objectName: z.string().describe('Target object for this workflow'), + + /** Trigger Configuration */ + trigger: AIWorkflowTriggerSchema, + + /** Trigger-specific configuration */ + fieldConditions: z.array(WorkflowFieldConditionSchema).optional().describe('Fields to monitor (for field_changed trigger)'), + schedule: WorkflowScheduleSchema.optional().describe('Schedule configuration (for scheduled trigger)'), + webhookConfig: z.object({ + secret: z.string().optional().describe('Webhook verification secret'), + headers: z.record(z.string()).optional().describe('Expected headers'), + }).optional().describe('Webhook configuration (for webhook trigger)'), + + /** Entry Criteria */ + entryCriteria: z.string().optional().describe('Formula condition - workflow only runs if TRUE'), + + /** AI Tasks */ + aiTasks: z.array(AITaskSchema).describe('AI tasks to execute in sequence'), + + /** Post-Processing */ + postActions: z.array(PostProcessingActionSchema).optional().describe('Actions after AI tasks complete'), + + /** Execution Options */ + executionMode: z.enum(['sequential', 'parallel']).optional().default('sequential').describe('How to execute multiple AI tasks'), + stopOnError: z.boolean().optional().default(false).describe('Stop workflow if any task fails'), + + /** Performance & Limits */ + timeout: z.number().optional().describe('Maximum execution time in seconds'), + priority: z.enum(['low', 'normal', 'high', 'critical']).optional().default('normal'), + + /** Monitoring & Logging */ + enableLogging: z.boolean().optional().default(true), + enableMetrics: z.boolean().optional().default(true), + notifyOnFailure: z.array(z.string()).optional().describe('User IDs to notify on failure'), + + /** Status */ + active: z.boolean().optional().default(true), + version: z.string().optional().default('1.0.0'), + + /** Metadata */ + tags: z.array(z.string()).optional(), + category: z.string().optional().describe('Workflow category (e.g., "support", "sales", "hr")'), + owner: z.string().optional().describe('User ID of workflow owner'), + createdAt: z.string().optional().describe('ISO timestamp'), + updatedAt: z.string().optional().describe('ISO timestamp'), +}); + +/** + * Batch AI Workflow Execution Request + * For processing multiple records at once + */ +export const BatchAIWorkflowExecutionSchema = z.object({ + workflowName: z.string().describe('Workflow to execute'), + recordIds: z.array(z.string()).describe('Records to process'), + batchSize: z.number().int().min(1).max(1000).optional().default(10), + parallelism: z.number().int().min(1).max(10).optional().default(3), + priority: z.enum(['low', 'normal', 'high']).optional().default('normal'), +}); + +/** + * AI Workflow Execution Result + * Result of a single workflow execution + */ +export const AIWorkflowExecutionResultSchema = z.object({ + workflowName: z.string(), + recordId: z.string(), + status: z.enum(['success', 'partial_success', 'failed', 'skipped']), + executionTime: z.number().describe('Execution time in milliseconds'), + tasksExecuted: z.number().int().describe('Number of tasks executed'), + tasksSucceeded: z.number().int().describe('Number of tasks succeeded'), + tasksFailed: z.number().int().describe('Number of tasks failed'), + taskResults: z.array(z.object({ + taskId: z.string().optional(), + taskName: z.string(), + status: z.enum(['success', 'failed', 'skipped']), + output: z.any().optional(), + error: z.string().optional(), + executionTime: z.number().optional().describe('Task execution time in milliseconds'), + modelUsed: z.string().optional(), + tokensUsed: z.number().optional(), + })).optional(), + error: z.string().optional(), + startedAt: z.string().describe('ISO timestamp'), + completedAt: z.string().optional().describe('ISO timestamp'), +}); + +// Type exports +export type AIWorkflowTrigger = z.infer; +export type AITaskType = z.infer; +export type AITask = z.infer; +export type WorkflowFieldCondition = z.infer; +export type WorkflowSchedule = z.infer; +export type PostProcessingAction = z.infer; +export type AIWorkflowAutomation = z.infer; +export type BatchAIWorkflowExecution = z.infer; +export type AIWorkflowExecutionResult = z.infer; From 38df1790302db46be43b2da1731efb4a93608861 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 08:41:02 +0000 Subject: [PATCH 3/3] Fix naming collision: rename Feature to ModelFeature in predictive analytics Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- content/docs/references/ai/ModelFeature.mdx | 19 +++++ content/docs/references/ai/config/Feature.mdx | 15 ---- packages/spec/json-schema/ModelFeature.json | 71 +++++++++++++++++++ packages/spec/src/ai/predictive.test.ts | 26 +++---- packages/spec/src/ai/predictive.zod.ts | 10 +-- 5 files changed, 108 insertions(+), 33 deletions(-) create mode 100644 content/docs/references/ai/ModelFeature.mdx delete mode 100644 content/docs/references/ai/config/Feature.mdx create mode 100644 packages/spec/json-schema/ModelFeature.json diff --git a/content/docs/references/ai/ModelFeature.mdx b/content/docs/references/ai/ModelFeature.mdx new file mode 100644 index 0000000..992f21d --- /dev/null +++ b/content/docs/references/ai/ModelFeature.mdx @@ -0,0 +1,19 @@ +--- +title: ModelFeature +description: ModelFeature Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Feature name (snake_case) | +| **label** | `string` | optional | Human-readable label | +| **field** | `string` | ✅ | Source field name | +| **object** | `string` | optional | Source object (if different from target) | +| **dataType** | `Enum<'numeric' \| 'categorical' \| 'text' \| 'datetime' \| 'boolean'>` | ✅ | Feature data type | +| **transformation** | `Enum<'none' \| 'normalize' \| 'standardize' \| 'one_hot_encode' \| 'label_encode' \| 'log_transform' \| 'binning' \| 'embedding'>` | optional | | +| **required** | `boolean` | optional | | +| **defaultValue** | `any` | optional | | +| **description** | `string` | optional | | +| **importance** | `number` | optional | Feature importance score (0-1) | diff --git a/content/docs/references/ai/config/Feature.mdx b/content/docs/references/ai/config/Feature.mdx deleted file mode 100644 index 3a480aa..0000000 --- a/content/docs/references/ai/config/Feature.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Feature -description: Feature Schema Reference ---- - -## Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **code** | `string` | ✅ | Feature code (e.g. core.api_access) | -| **label** | `string` | ✅ | | -| **description** | `string` | optional | | -| **type** | `Enum<'boolean' \| 'counter' \| 'gauge'>` | optional | | -| **unit** | `Enum<'count' \| 'bytes' \| 'seconds' \| 'percent'>` | optional | | -| **requires** | `string[]` | optional | | diff --git a/packages/spec/json-schema/ModelFeature.json b/packages/spec/json-schema/ModelFeature.json new file mode 100644 index 0000000..37726d3 --- /dev/null +++ b/packages/spec/json-schema/ModelFeature.json @@ -0,0 +1,71 @@ +{ + "$ref": "#/definitions/ModelFeature", + "definitions": { + "ModelFeature": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Feature name (snake_case)" + }, + "label": { + "type": "string", + "description": "Human-readable label" + }, + "field": { + "type": "string", + "description": "Source field name" + }, + "object": { + "type": "string", + "description": "Source object (if different from target)" + }, + "dataType": { + "type": "string", + "enum": [ + "numeric", + "categorical", + "text", + "datetime", + "boolean" + ], + "description": "Feature data type" + }, + "transformation": { + "type": "string", + "enum": [ + "none", + "normalize", + "standardize", + "one_hot_encode", + "label_encode", + "log_transform", + "binning", + "embedding" + ], + "default": "none" + }, + "required": { + "type": "boolean", + "default": true + }, + "defaultValue": {}, + "description": { + "type": "string" + }, + "importance": { + "type": "number", + "description": "Feature importance score (0-1)" + } + }, + "required": [ + "name", + "field", + "dataType" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/src/ai/predictive.test.ts b/packages/spec/src/ai/predictive.test.ts index b95f637..a38dcce 100644 --- a/packages/spec/src/ai/predictive.test.ts +++ b/packages/spec/src/ai/predictive.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'; import { PredictiveModelSchema, PredictiveModelTypeSchema, - FeatureSchema, + ModelFeatureSchema, HyperparametersSchema, TrainingConfigSchema, EvaluationMetricsSchema, @@ -10,7 +10,7 @@ import { PredictionResultSchema, ModelDriftSchema, type PredictiveModel, - type Feature, + type ModelFeature, type PredictionRequest, } from './predictive.zod'; @@ -32,15 +32,15 @@ describe('PredictiveModelTypeSchema', () => { }); }); -describe('FeatureSchema', () => { +describe('ModelFeatureSchema', () => { it('should accept minimal feature', () => { - const feature: Feature = { + const feature: ModelFeature = { name: 'user_age', field: 'age', dataType: 'numeric', }; - const result = FeatureSchema.parse(feature); + const result = ModelFeatureSchema.parse(feature); expect(result.transformation).toBe('none'); expect(result.required).toBe(true); }); @@ -48,7 +48,7 @@ describe('FeatureSchema', () => { it('should enforce snake_case for feature name', () => { const validNames = ['user_age', 'total_revenue', '_internal_score']; validNames.forEach(name => { - expect(() => FeatureSchema.parse({ + expect(() => ModelFeatureSchema.parse({ name, field: 'test', dataType: 'numeric', @@ -57,7 +57,7 @@ describe('FeatureSchema', () => { const invalidNames = ['userAge', 'User-Age', '123age']; invalidNames.forEach(name => { - expect(() => FeatureSchema.parse({ + expect(() => ModelFeatureSchema.parse({ name, field: 'test', dataType: 'numeric', @@ -66,7 +66,7 @@ describe('FeatureSchema', () => { }); it('should accept feature with transformation', () => { - const feature: Feature = { + const feature: ModelFeature = { name: 'normalized_revenue', field: 'annual_revenue', dataType: 'numeric', @@ -74,11 +74,11 @@ describe('FeatureSchema', () => { label: 'Normalized Annual Revenue', }; - expect(() => FeatureSchema.parse(feature)).not.toThrow(); + expect(() => ModelFeatureSchema.parse(feature)).not.toThrow(); }); it('should accept categorical feature', () => { - const feature: Feature = { + const feature: ModelFeature = { name: 'industry_encoded', field: 'industry', dataType: 'categorical', @@ -86,11 +86,11 @@ describe('FeatureSchema', () => { description: 'Industry category with one-hot encoding', }; - expect(() => FeatureSchema.parse(feature)).not.toThrow(); + expect(() => ModelFeatureSchema.parse(feature)).not.toThrow(); }); it('should accept feature from related object', () => { - const feature: Feature = { + const feature: ModelFeature = { name: 'account_revenue', field: 'annual_revenue', object: 'account', @@ -98,7 +98,7 @@ describe('FeatureSchema', () => { transformation: 'log_transform', }; - expect(() => FeatureSchema.parse(feature)).not.toThrow(); + expect(() => ModelFeatureSchema.parse(feature)).not.toThrow(); }); }); diff --git a/packages/spec/src/ai/predictive.zod.ts b/packages/spec/src/ai/predictive.zod.ts index aec7f7e..497bffa 100644 --- a/packages/spec/src/ai/predictive.zod.ts +++ b/packages/spec/src/ai/predictive.zod.ts @@ -31,10 +31,10 @@ export const PredictiveModelTypeSchema = z.enum([ ]); /** - * Feature Definition - * Describes an input feature for the model + * Model Feature Definition + * Describes an input feature for a predictive model */ -export const FeatureSchema = z.object({ +export const ModelFeatureSchema = z.object({ /** Feature Identity */ name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Feature name (snake_case)'), label: z.string().optional().describe('Human-readable label'), @@ -183,7 +183,7 @@ export const PredictiveModelSchema = z.object({ targetType: z.enum(['numeric', 'categorical', 'binary']).optional().describe('Target field type'), /** Features */ - features: z.array(FeatureSchema).describe('Input features for the model'), + features: z.array(ModelFeatureSchema).describe('Input features for the model'), /** Hyperparameters */ hyperparameters: HyperparametersSchema.optional(), @@ -285,7 +285,7 @@ export const ModelDriftSchema = z.object({ // Type exports export type PredictiveModelType = z.infer; -export type Feature = z.infer; +export type ModelFeature = z.infer; export type Hyperparameters = z.infer; export type TrainingConfig = z.infer; export type EvaluationMetrics = z.infer;