wulf-pulse/lib/services/workflow-steps/classify.ts

113 lines
3.1 KiB
TypeScript

/**
* Classify Step — keyword-based classification using classification_rules table.
* Config: {
* rule_type: 'branch_routing' | 'ticket_type' | 'issue_classification' | 'priority' | 'queue_routing',
* result_field: 'branch' | 'ticket_type' | 'issue_type' | 'priority' | 'queue_id',
* result_field_2?: 'sub_issue_type', // for issue_classification only
* default_value?: any // fallback if no rules match
* }
*/
import { registerWorkflowStepExecutor } from '../ticket-workflow-engine';
import { roboticClassifier } from '../robotic-classifier';
import { TicketWorkflowStep, WorkflowStepContext, WorkflowStepResult } from '../../types/ticket-workflow';
async function executeClassify(
step: TicketWorkflowStep,
context: WorkflowStepContext,
_executionId: number
): Promise<WorkflowStepResult> {
const ruleType = step.config.rule_type;
const resultField = step.config.result_field;
const resultField2 = step.config.result_field_2;
const defaultValue = step.config.default_value;
if (!ruleType || !resultField) {
return {
success: false,
error: 'Missing required config: rule_type and result_field'
};
}
// Ensure rules are loaded
await roboticClassifier.loadRules();
// Build ticket context (may have values from previous steps)
const ticket = {
...context.ticket,
...(context.field_changes || {})
};
// Run classification for this rule type
const result = await (roboticClassifier as any).classifyByType(ruleType, ticket);
if (!result && defaultValue !== undefined) {
// No match, use default
const output: any = {
[resultField]: defaultValue,
matched_rule: null,
confidence: 'default',
method: 'default'
};
// Track field change
if (!context.field_changes) {
context.field_changes = {};
}
context.field_changes[resultField] = {
before: (ticket as any)[resultField] || null,
after: defaultValue
};
return { success: true, output };
}
if (!result) {
// No match and no default
return {
success: true,
output: {
[resultField]: null,
matched_rule: null,
confidence: 'none',
method: 'no_match'
}
};
}
// Matched a rule
const output: any = {
[resultField]: result.value,
matched_rule: result.matched_rule_name,
confidence: result.confidence,
method: 'robotic'
};
// Track field change for primary result
if (!context.field_changes) {
context.field_changes = {};
}
context.field_changes[resultField] = {
before: (ticket as any)[resultField] || null,
after: result.value
};
// Handle secondary result (e.g., sub_issue_type)
if (resultField2 && result.value_2 !== undefined) {
output[resultField2] = result.value_2;
context.field_changes[resultField2] = {
before: (ticket as any)[resultField2] || null,
after: result.value_2
};
}
// Store full classification result in context for later steps
if (!context.classification) {
context.classification = {};
}
context.classification[ruleType] = result;
return { success: true, output };
}
registerWorkflowStepExecutor('classify', executeClassify);