CognxSafeTrack commited on
Commit ·
d95a6d7
1
Parent(s): b8ae83d
Chore: Added AI regression test framework and enhanced Mock provider
Browse files
apps/api/package.json
CHANGED
|
@@ -7,6 +7,7 @@
|
|
| 7 |
"build": "tsc --build",
|
| 8 |
"start": "node dist/index.js",
|
| 9 |
"test": "vitest run",
|
|
|
|
| 10 |
"test:watch": "vitest"
|
| 11 |
},
|
| 12 |
"dependencies": {
|
|
|
|
| 7 |
"build": "tsc --build",
|
| 8 |
"start": "node dist/index.js",
|
| 9 |
"test": "vitest run",
|
| 10 |
+
"test:ai": "vitest run test/regression/ai-pedagogy.test.ts",
|
| 11 |
"test:watch": "vitest"
|
| 12 |
},
|
| 13 |
"dependencies": {
|
apps/api/src/services/ai/mock-provider.ts
CHANGED
|
@@ -39,7 +39,16 @@ export class MockLLMProvider implements LLMProvider {
|
|
| 39 |
return schema.parse(MOCK_FEEDBACK) as any;
|
| 40 |
}
|
| 41 |
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
}
|
| 44 |
|
| 45 |
async transcribeAudio(_audioBuffer: Buffer, filename: string): Promise<{ text: string, confidence: number }> {
|
|
|
|
| 39 |
return schema.parse(MOCK_FEEDBACK) as any;
|
| 40 |
}
|
| 41 |
|
| 42 |
+
// Fallback for profile extraction or other schemas
|
| 43 |
+
return {
|
| 44 |
+
activityLabel: 'Mock Activity',
|
| 45 |
+
activityType: 'Mock Type',
|
| 46 |
+
mainCustomer: 'Mock Customer',
|
| 47 |
+
mainProblem: 'Mock Problem',
|
| 48 |
+
offerSimple: 'Mock Offer',
|
| 49 |
+
promise: 'Mock Promise',
|
| 50 |
+
slides: Array(13).fill({ title: 'Mock Slide', content: 'Mock Content' })
|
| 51 |
+
} as any;
|
| 52 |
}
|
| 53 |
|
| 54 |
async transcribeAudio(_audioBuffer: Buffer, filename: string): Promise<{ text: string, confidence: number }> {
|
apps/api/test/regression/ai-pedagogy.test.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { describe, it, expect } from 'vitest';
|
| 2 |
+
import dotenv from 'dotenv';
|
| 3 |
+
import path from 'path';
|
| 4 |
+
|
| 5 |
+
// Load env before importing AIService
|
| 6 |
+
dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
|
| 7 |
+
|
| 8 |
+
describe('AI Pedagogy Regression Tests (v1.0)', () => {
|
| 9 |
+
|
| 10 |
+
it('should generate a valid feedback in WOLOF without any French words', async () => {
|
| 11 |
+
// Dynamic import to ensure dotenv is loaded
|
| 12 |
+
const { aiService } = await import('../../src/services/ai');
|
| 13 |
+
|
| 14 |
+
const userInput = "Damay jaay jus guir am xaliss";
|
| 15 |
+
const expectedExercise = "Décris ton projet de vente.";
|
| 16 |
+
const lessonContent = "Leçon sur le Business Model de vente directe.";
|
| 17 |
+
|
| 18 |
+
const result = await aiService.generateFeedback(
|
| 19 |
+
userInput,
|
| 20 |
+
expectedExercise,
|
| 21 |
+
lessonContent,
|
| 22 |
+
'WOLOF',
|
| 23 |
+
{ activityLabel: 'Vente de jus', region: 'Dakar' }
|
| 24 |
+
);
|
| 25 |
+
|
| 26 |
+
expect(result.validation).toBeDefined();
|
| 27 |
+
|
| 28 |
+
const forbiddenWords = ['bien', 'très', 'exercice', 'bravo', 'félicitations', 'étudiant'];
|
| 29 |
+
const responseText = (result.validation + ' ' + result.enrichedVersion + ' ' + result.actionableAdvice).toLowerCase();
|
| 30 |
+
|
| 31 |
+
if (result.aiSource !== 'MOCK') {
|
| 32 |
+
forbiddenWords.forEach(word => {
|
| 33 |
+
expect(responseText).not.toContain(word);
|
| 34 |
+
});
|
| 35 |
+
expect(responseText).toMatch(/[ñëé]/);
|
| 36 |
+
}
|
| 37 |
+
}, 30000);
|
| 38 |
+
|
| 39 |
+
it('should extract business profile correctly', async () => {
|
| 40 |
+
const { aiService } = await import('../../src/services/ai');
|
| 41 |
+
const userInput = "Man dama beugue def coiffure";
|
| 42 |
+
const result = await aiService.extractBusinessProfile(userInput, 1, 'WOLOF');
|
| 43 |
+
|
| 44 |
+
if (result.aiSource !== 'MOCK') {
|
| 45 |
+
expect(result.activityLabel).toContain('Coiffure');
|
| 46 |
+
}
|
| 47 |
+
expect(result.aiSource).toBeDefined();
|
| 48 |
+
}, 30000);
|
| 49 |
+
|
| 50 |
+
it('should generate pitch deck data with consistent slides (13 slides)', async () => {
|
| 51 |
+
const { aiService } = await import('../../src/services/ai');
|
| 52 |
+
const userContext = "Vente de produits cosmétiques bio à base de beurre de karité au Sénégal.";
|
| 53 |
+
const result = await aiService.generatePitchDeckData(userContext, 'FR');
|
| 54 |
+
|
| 55 |
+
expect(result.slides).toHaveLength(13);
|
| 56 |
+
}, 60000);
|
| 57 |
+
});
|
packages/prompts/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
"main": "dist/index.js",
|
| 5 |
"types": "dist/index.d.ts",
|
| 6 |
"scripts": {
|
| 7 |
-
"build": "tsc"
|
| 8 |
},
|
| 9 |
"dependencies": {
|
| 10 |
"fs": "0.0.1-security",
|
|
|
|
| 4 |
"main": "dist/index.js",
|
| 5 |
"types": "dist/index.d.ts",
|
| 6 |
"scripts": {
|
| 7 |
+
"build": "tsc && mkdir -p dist/templates && cp src/templates/*.md dist/templates/"
|
| 8 |
},
|
| 9 |
"dependencies": {
|
| 10 |
"fs": "0.0.1-security",
|