mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 04:18:05 +00:00
- reorganize bin/ into auth/, glmt/, management/, utils/ - add budget calculator and locale enforcer - enhance test coverage with unit/integration separation
460 lines
14 KiB
JavaScript
460 lines
14 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
/**
|
|
* TaskClassifier Unit Tests
|
|
*
|
|
* Tests 3 scenarios:
|
|
* 1. Reasoning prompt ("plan architecture") → 'reasoning' classification
|
|
* 2. Execution prompt ("fix bug") → 'execution' classification
|
|
* 3. Mixed prompt ("analyze and fix") → 'mixed' classification
|
|
*/
|
|
|
|
const assert = require('assert');
|
|
const TaskClassifier = require('../../../bin/glmt/task-classifier');
|
|
|
|
describe('TaskClassifier', () => {
|
|
describe('Scenario 1: Reasoning tasks', () => {
|
|
it('should classify "plan architecture" as reasoning', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Plan a microservices architecture' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should classify "design system" as reasoning', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Design a database schema for e-commerce' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should classify "analyze performance" as reasoning', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Analyze the performance bottlenecks' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should detect multiple reasoning keywords', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Evaluate different approaches and recommend the best strategy' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should classify research tasks as reasoning', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Research best practices for API authentication' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should handle case-insensitive reasoning keywords', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'PLAN THE ARCHITECTURE' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should detect reasoning in array content', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'text', text: 'Consider the pros and cons of GraphQL vs REST' }
|
|
]
|
|
}
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
});
|
|
|
|
describe('Scenario 2: Execution tasks', () => {
|
|
it('should classify "fix bug" as execution', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Fix the bug in login.js' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
|
|
it('should classify "implement feature" as execution', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Implement user authentication' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
|
|
it('should classify "debug issue" as execution', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Debug the memory leak in worker.js' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
|
|
it('should classify "refactor code" as execution', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Refactor the database queries' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
|
|
it('should detect multiple execution keywords', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Add validation and update the form component' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
|
|
it('should handle case-insensitive execution keywords', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'FIX THE BUG IN AUTH MODULE' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
|
|
it('should classify test tasks as execution', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Run the integration tests' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
|
|
it('should detect execution in array content', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'text', text: 'Create a new API endpoint for users' }
|
|
]
|
|
}
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'execution');
|
|
});
|
|
});
|
|
|
|
describe('Scenario 3: Mixed or ambiguous tasks', () => {
|
|
it('should classify "analyze and fix" as mixed (tied scores)', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Analyze the issue and fix it' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'mixed');
|
|
});
|
|
|
|
it('should classify tasks with equal reasoning and execution keywords as mixed', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Design the API structure and implement it' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'mixed');
|
|
});
|
|
|
|
it('should classify tasks with no keywords as mixed', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Help me with the code' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'mixed');
|
|
});
|
|
|
|
it('should classify empty content as mixed', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: '' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'mixed');
|
|
});
|
|
|
|
it('should return mixed for empty messages array', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'mixed');
|
|
});
|
|
|
|
it('should return mixed when no user messages exist', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'assistant', content: 'Hello!' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'mixed');
|
|
});
|
|
|
|
it('should handle ambiguous prompts', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'What should I do about the authentication?' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'mixed');
|
|
});
|
|
});
|
|
|
|
describe('classifyWithDetails method', () => {
|
|
it('should return detailed classification for reasoning task', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Plan and design the system architecture' }
|
|
];
|
|
|
|
const result = classifier.classifyWithDetails(messages);
|
|
|
|
assert.strictEqual(result.type, 'reasoning');
|
|
assert.ok(result.reasoningScore > 0);
|
|
assert.ok(result.reasoningScore > result.executionScore);
|
|
assert.ok(result.textLength > 0);
|
|
assert.ok(result.textPreview.includes('plan'));
|
|
});
|
|
|
|
it('should return detailed classification for execution task', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Fix the bug and run tests' }
|
|
];
|
|
|
|
const result = classifier.classifyWithDetails(messages);
|
|
|
|
assert.strictEqual(result.type, 'execution');
|
|
assert.ok(result.executionScore > 0);
|
|
assert.ok(result.executionScore > result.reasoningScore);
|
|
assert.ok(result.textLength > 0);
|
|
});
|
|
|
|
it('should show scores for mixed task', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Evaluate the options and implement the best one' }
|
|
];
|
|
|
|
const result = classifier.classifyWithDetails(messages);
|
|
|
|
assert.strictEqual(result.type, 'mixed');
|
|
assert.strictEqual(result.reasoningScore, result.executionScore);
|
|
});
|
|
|
|
it('should truncate long text in preview', () => {
|
|
const classifier = new TaskClassifier();
|
|
const longText = 'a'.repeat(200);
|
|
const messages = [
|
|
{ role: 'user', content: longText }
|
|
];
|
|
|
|
const result = classifier.classifyWithDetails(messages);
|
|
|
|
assert.strictEqual(result.textPreview.length, 103); // 100 + '...'
|
|
assert.ok(result.textPreview.endsWith('...'));
|
|
});
|
|
});
|
|
|
|
describe('Edge cases and special scenarios', () => {
|
|
it('should handle multiple user messages', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Plan the architecture' },
|
|
{ role: 'assistant', content: 'Here is a plan...' },
|
|
{ role: 'user', content: 'Implement it' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
// ACTUAL BEHAVIOR: Combines both user messages: "plan the architecture implement it"
|
|
// "plan" matches reasoning keyword, "implement" matches execution keyword
|
|
// Score: reasoning=2 (plan, architecture), execution=1 (implement)
|
|
// Result: reasoning wins
|
|
assert.strictEqual(result, 'reasoning'); // Changed from 'mixed'
|
|
});
|
|
|
|
it('should handle word boundary matching', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Update the replanning module' } // "plan" in "replanning"
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
// Should not match "plan" in "replanning" due to word boundary
|
|
assert.strictEqual(result, 'execution'); // Only "update" should match
|
|
});
|
|
|
|
it('should handle custom keywords', () => {
|
|
const classifier = new TaskClassifier({
|
|
customKeywords: {
|
|
reasoning: ['brainstorm', 'strategize'],
|
|
execution: ['deploy', 'ship']
|
|
}
|
|
});
|
|
|
|
const messages1 = [{ role: 'user', content: 'Brainstorm ideas' }];
|
|
const messages2 = [{ role: 'user', content: 'Deploy to production' }];
|
|
|
|
assert.strictEqual(classifier.classify(messages1), 'reasoning');
|
|
assert.strictEqual(classifier.classify(messages2), 'execution');
|
|
});
|
|
|
|
it('should extract text from multiple content blocks', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'text', text: 'Plan the' },
|
|
{ type: 'text', text: 'architecture' }
|
|
]
|
|
}
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should ignore non-text content blocks', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'image', source: 'data:...' },
|
|
{ type: 'text', text: 'Analyze this screenshot' }
|
|
]
|
|
}
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning');
|
|
});
|
|
|
|
it('should handle special characters in keywords', () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [
|
|
{ role: 'user', content: 'Think about the pros and cons' }
|
|
];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, 'reasoning'); // "think about" and "pros and cons" match
|
|
});
|
|
});
|
|
|
|
describe('Real-world prompts', () => {
|
|
const testCases = [
|
|
{ prompt: 'Create a React component for user profile', expected: 'execution' },
|
|
{ prompt: 'What is the best approach for state management?', expected: 'reasoning' },
|
|
{ prompt: 'Compare Redux vs MobX', expected: 'reasoning' },
|
|
{ prompt: 'Add error handling to the API', expected: 'execution' },
|
|
{ prompt: 'Investigate why the tests are failing', expected: 'reasoning' },
|
|
{ prompt: 'Optimize database queries', expected: 'execution' },
|
|
{ prompt: 'Review the security implications', expected: 'reasoning' },
|
|
{ prompt: 'Build and deploy the application', expected: 'execution' },
|
|
{ prompt: 'Should I use TypeScript or JavaScript?', expected: 'mixed' },
|
|
{ prompt: 'Help me understand this code', expected: 'mixed' }
|
|
];
|
|
|
|
testCases.forEach(({ prompt, expected }) => {
|
|
it(`should classify "${prompt}" as ${expected}`, () => {
|
|
const classifier = new TaskClassifier();
|
|
const messages = [{ role: 'user', content: prompt }];
|
|
|
|
const result = classifier.classify(messages);
|
|
|
|
assert.strictEqual(result, expected);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
// Run tests if executed directly
|
|
if (require.main === module) {
|
|
const Mocha = require('mocha');
|
|
const mocha = new Mocha({ reporter: 'spec' });
|
|
mocha.suite.emit('pre-require', global, null, mocha);
|
|
|
|
// Load this test file
|
|
require(module.filename);
|
|
|
|
mocha.run(failures => {
|
|
process.exitCode = failures ? 1 : 0;
|
|
});
|
|
}
|