113 lines
3.1 KiB
TypeScript
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);
|