Some checks failed
ci/woodpecker/push/ci Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
160 lines
4.6 KiB
TypeScript
160 lines
4.6 KiB
TypeScript
import type { TaskType, Complexity, Domain, TaskClassification } from './routing.types.js';
|
|
|
|
// ─── Pattern Banks ──────────────────────────────────────────────────────────
|
|
|
|
const CODING_PATTERNS: RegExp[] = [
|
|
/\bcode\b/i,
|
|
/\bfunction\b/i,
|
|
/\bimplement\b/i,
|
|
/\bdebug\b/i,
|
|
/\bfix\b/i,
|
|
/\brefactor\b/i,
|
|
/\btypescript\b/i,
|
|
/\bjavascript\b/i,
|
|
/\bpython\b/i,
|
|
/\bSQL\b/i,
|
|
/\bAPI\b/i,
|
|
/\bendpoint\b/i,
|
|
/\bclass\b/i,
|
|
/\bmethod\b/i,
|
|
/`[^`]*`/,
|
|
];
|
|
|
|
const RESEARCH_PATTERNS: RegExp[] = [
|
|
/\bresearch\b/i,
|
|
/\bfind\b/i,
|
|
/\bsearch\b/i,
|
|
/\bwhat is\b/i,
|
|
/\bexplain\b/i,
|
|
/\bhow do(es)?\b/i,
|
|
/\bcompare\b/i,
|
|
/\banalyze\b/i,
|
|
];
|
|
|
|
const SUMMARIZATION_PATTERNS: RegExp[] = [
|
|
/\bsummariz(e|ation)\b/i,
|
|
/\bsummary\b/i,
|
|
/\btldr\b/i,
|
|
/\bcondense\b/i,
|
|
/\bbrief\b/i,
|
|
];
|
|
|
|
const CREATIVE_PATTERNS: RegExp[] = [
|
|
/\bwrite\b/i,
|
|
/\bstory\b/i,
|
|
/\bpoem\b/i,
|
|
/\bgenerate\b/i,
|
|
/\bcreate content\b/i,
|
|
/\bblog post\b/i,
|
|
];
|
|
|
|
const ANALYSIS_PATTERNS: RegExp[] = [
|
|
/\banalyze\b/i,
|
|
/\breview\b/i,
|
|
/\bevaluate\b/i,
|
|
/\bassess\b/i,
|
|
/\baudit\b/i,
|
|
];
|
|
|
|
// ─── Complexity Indicators ───────────────────────────────────────────────────
|
|
|
|
const COMPLEX_KEYWORDS: RegExp[] = [
|
|
/\barchitecture\b/i,
|
|
/\bdesign\b/i,
|
|
/\bcomplex\b/i,
|
|
/\bsystem\b/i,
|
|
];
|
|
|
|
const SIMPLE_QUESTION_PATTERN = /^[^.!?]+[?]$/;
|
|
|
|
/** Counts occurrences of triple-backtick code fences in the message */
|
|
function countCodeBlocks(message: string): number {
|
|
return (message.match(/```/g) ?? []).length / 2;
|
|
}
|
|
|
|
// ─── Domain Indicators ───────────────────────────────────────────────────────
|
|
|
|
const FRONTEND_PATTERNS: RegExp[] = [
|
|
/\breact\b/i,
|
|
/\bcss\b/i,
|
|
/\bhtml\b/i,
|
|
/\bcomponent\b/i,
|
|
/\bUI\b/,
|
|
/\btailwind\b/i,
|
|
/\bnext\.js\b/i,
|
|
];
|
|
|
|
const BACKEND_PATTERNS: RegExp[] = [
|
|
/\bAPI\b/i,
|
|
/\bserver\b/i,
|
|
/\bdatabase\b/i,
|
|
/\bendpoint\b/i,
|
|
/\bnest(js)?\b/i,
|
|
/\bexpress\b/i,
|
|
];
|
|
|
|
const DEVOPS_PATTERNS: RegExp[] = [
|
|
/\bdocker(file|compose|hub)?\b/i,
|
|
/\bCI\b/,
|
|
/\bdeploy\b/i,
|
|
/\bpipeline\b/i,
|
|
/\bkubernetes\b/i,
|
|
];
|
|
|
|
const DOCS_PATTERNS: RegExp[] = [/\bdocumentation\b/i, /\breadme\b/i, /\bguide\b/i];
|
|
|
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
function matchesAny(message: string, patterns: RegExp[]): boolean {
|
|
return patterns.some((p) => p.test(message));
|
|
}
|
|
|
|
// ─── Classifier ──────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Classify a task based on the user's message using deterministic regex/keyword matching.
|
|
* No LLM calls are made — this is a pure, fast, synchronous classification.
|
|
*/
|
|
export function classifyTask(message: string): TaskClassification {
|
|
return {
|
|
taskType: detectTaskType(message),
|
|
complexity: estimateComplexity(message),
|
|
domain: detectDomain(message),
|
|
requiredCapabilities: [],
|
|
};
|
|
}
|
|
|
|
function detectTaskType(message: string): TaskType {
|
|
if (matchesAny(message, CODING_PATTERNS)) return 'coding';
|
|
if (matchesAny(message, SUMMARIZATION_PATTERNS)) return 'summarization';
|
|
if (matchesAny(message, CREATIVE_PATTERNS)) return 'creative';
|
|
if (matchesAny(message, ANALYSIS_PATTERNS)) return 'analysis';
|
|
if (matchesAny(message, RESEARCH_PATTERNS)) return 'research';
|
|
return 'conversation';
|
|
}
|
|
|
|
function estimateComplexity(message: string): Complexity {
|
|
const trimmed = message.trim();
|
|
const codeBlocks = countCodeBlocks(trimmed);
|
|
|
|
// Complex: long messages, multiple code blocks, or complexity keywords
|
|
if (trimmed.length > 500 || codeBlocks > 1 || matchesAny(trimmed, COMPLEX_KEYWORDS)) {
|
|
return 'complex';
|
|
}
|
|
|
|
// Simple: short messages or a single direct question
|
|
if (trimmed.length < 100 || SIMPLE_QUESTION_PATTERN.test(trimmed)) {
|
|
return 'simple';
|
|
}
|
|
|
|
return 'moderate';
|
|
}
|
|
|
|
function detectDomain(message: string): Domain {
|
|
if (matchesAny(message, DEVOPS_PATTERNS)) return 'devops';
|
|
if (matchesAny(message, DOCS_PATTERNS)) return 'docs';
|
|
if (matchesAny(message, FRONTEND_PATTERNS)) return 'frontend';
|
|
if (matchesAny(message, BACKEND_PATTERNS)) return 'backend';
|
|
return 'general';
|
|
}
|