@odda-ai/matching-core 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +790 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +3 -0
  4. package/dist/src/ai/AIProvider.d.ts +36 -0
  5. package/dist/src/ai/AIProvider.js +127 -0
  6. package/dist/src/ai/adapters/AnthropicAdapter.d.ts +12 -0
  7. package/dist/src/ai/adapters/AnthropicAdapter.js +38 -0
  8. package/dist/src/ai/adapters/OllamaAdapter.d.ts +17 -0
  9. package/dist/src/ai/adapters/OllamaAdapter.js +38 -0
  10. package/dist/src/ai/adapters/OpenAIAdapter.d.ts +12 -0
  11. package/dist/src/ai/adapters/OpenAIAdapter.js +47 -0
  12. package/{src/ai/adapters/index.ts → dist/src/ai/adapters/index.d.ts} +1 -1
  13. package/dist/src/ai/adapters/index.js +3 -0
  14. package/dist/src/ai/factory.d.ts +12 -0
  15. package/dist/src/ai/factory.js +37 -0
  16. package/{src/ai/index.ts → dist/src/ai/index.d.ts} +1 -1
  17. package/dist/src/ai/index.js +5 -0
  18. package/dist/src/ai/registry.d.ts +13 -0
  19. package/{src/ai/registry.ts → dist/src/ai/registry.js} +7 -8
  20. package/dist/src/ai/types.d.ts +54 -0
  21. package/dist/src/ai/types.js +1 -0
  22. package/dist/src/cv-parser/PDFParserService.d.ts +41 -0
  23. package/dist/src/cv-parser/PDFParserService.js +136 -0
  24. package/dist/src/cv-parser/index.js +1 -0
  25. package/dist/src/cv-parser/types.d.ts +51 -0
  26. package/dist/src/cv-parser/types.js +1 -0
  27. package/dist/src/features/ai-cv-resume.service.d.ts +14 -0
  28. package/dist/src/features/ai-cv-resume.service.js +78 -0
  29. package/dist/src/features/ai-talent.service.d.ts +18 -0
  30. package/dist/src/features/ai-talent.service.js +29 -0
  31. package/dist/src/features/cv-chunking.service.d.ts +140 -0
  32. package/dist/src/features/cv-chunking.service.js +334 -0
  33. package/dist/src/features/index.js +3 -0
  34. package/dist/src/features/job-matcher.service.d.ts +14 -0
  35. package/dist/src/features/job-matcher.service.js +19 -0
  36. package/dist/src/features/prompts.d.ts +8 -0
  37. package/{src/features/prompts.ts → dist/src/features/prompts.js} +6 -21
  38. package/dist/src/features/system-messages.d.ts +6 -0
  39. package/{src/features/system-messages.ts → dist/src/features/system-messages.js} +3 -4
  40. package/dist/src/features/types.d.ts +49 -0
  41. package/dist/src/features/types.js +15 -0
  42. package/package.json +8 -9
  43. package/src/ai/AIProvider.ts +0 -159
  44. package/src/ai/adapters/AnthropicAdapter.ts +0 -42
  45. package/src/ai/adapters/OllamaAdapter.ts +0 -42
  46. package/src/ai/adapters/OpenAIAdapter.ts +0 -53
  47. package/src/ai/factory.ts +0 -48
  48. package/src/ai/types.ts +0 -59
  49. package/src/cv-parser/PDFParserService.ts +0 -160
  50. package/src/cv-parser/types.ts +0 -58
  51. package/src/features/ai-cv-resume.service.ts +0 -104
  52. package/src/features/ai-talent.service.ts +0 -49
  53. package/src/features/cv-chunking.service.ts +0 -510
  54. package/src/features/job-matcher.service.ts +0 -41
  55. package/src/features/types.ts +0 -55
  56. /package/{src/cv-parser/index.ts → dist/src/cv-parser/index.d.ts} +0 -0
  57. /package/{src/features/index.ts → dist/src/features/index.d.ts} +0 -0
package/README.md ADDED
@@ -0,0 +1,790 @@
1
+ # 🧠 @odda-ai/matching-core
2
+
3
+ Libreria TypeScript modulare per l'integrazione di provider AI multipli, parsing di CV in PDF e analisi intelligente di competenze e profili professionali.
4
+
5
+ ![Version](https://img.shields.io/badge/version-1.0.0-blue)
6
+ ![License](https://img.shields.io/badge/license-ISC-green)
7
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)
8
+
9
+ ---
10
+
11
+ ## 📋 Indice
12
+
13
+ - [Overview](#-overview)
14
+ - [Features](#-features)
15
+ - [Installazione](#-installazione)
16
+ - [Quick Start](#-quick-start)
17
+ - [Architettura](#-architettura)
18
+ - [AI Providers](#-ai-providers)
19
+ - [CV Parser](#-cv-parser)
20
+ - [Features & Services](#-features--services)
21
+ - [API Reference](#-api-reference)
22
+ - [Esempi Avanzati](#-esempi-avanzati)
23
+ - [Configurazione](#-configurazione)
24
+ - [TypeScript Support](#-typescript-support)
25
+ - [Testing](#-testing)
26
+
27
+ ---
28
+
29
+ ## 🌟 Overview
30
+
31
+ **@odda-ai/matching-core** è una libreria core che fornisce:
32
+
33
+ - 🤖 **Astrazione unificata** per provider AI multipli (OpenAI, Anthropic, Ollama)
34
+ - 📄 **PDF parsing** ottimizzato per CV e documenti strutturati
35
+ - 🎯 **Analisi CV intelligente** con estrazione automatica di skill, certificazioni, esperienza
36
+ - 📊 **Skill extraction** da job description con matching semantico
37
+ - 🔧 **Type-safe** con pieno supporto TypeScript
38
+ - 🧩 **Modulare** con architettura a plugin per provider AI
39
+
40
+ ### Quando usarlo
41
+
42
+ - ✅ Costruire sistemi di recruitment e talent matching
43
+ - ✅ Automatizzare l'analisi di CV e profili professionali
44
+ - ✅ Estrarre competenze da job description
45
+ - ✅ Integrare AI in applicazioni esistenti con provider pluggable
46
+ - ✅ Processare documenti PDF strutturati
47
+
48
+ ---
49
+
50
+ ## ✨ Features
51
+
52
+ ### 🤖 AI Provider Abstraction
53
+
54
+ - **Multi-provider support**: OpenAI, Anthropic (Claude), Ollama
55
+ - **Factory pattern** per creazione provider
56
+ - **Registry centralizzato** per adapter management
57
+ - **Configurazione per provider** con modelli specifici
58
+ - **Error handling consistente** tra provider
59
+
60
+ ### 📄 CV Parsing
61
+
62
+ - **PDF extraction** con pdf-parse
63
+ - **Text cleaning** e normalizzazione
64
+ - **Metadata extraction** automatica
65
+ - **Buffer-based API** per flessibilità
66
+ - **Error handling robusto**
67
+
68
+ ### 🎯 CV Analysis
69
+
70
+ - **Structured extraction** di:
71
+ - Personal info (nome, email, phone, location, LinkedIn)
72
+ - Technical skills con proficiency level
73
+ - Soft skills con importanza
74
+ - Work experience con ruoli e durata
75
+ - Education con titoli e istituzioni
76
+ - Certifications con provider e validità
77
+ - Languages con livello di competenza
78
+ - **Seniority assessment** automatico (Junior, Mid, Senior, Lead, Principal)
79
+ - **Years of experience** calcolati
80
+ - **JSON-structured output** per facile integrazione
81
+
82
+ ### 📊 Job Matching
83
+
84
+ - **Skill extraction** da job description
85
+ - **Semantic matching** con skill esistenti
86
+ - **Importance weighting** automatico
87
+ - **Categorization** (Technical/Soft, Required/Nice-to-have)
88
+
89
+ ---
90
+
91
+ ## 📦 Installazione
92
+
93
+ ```bash
94
+ npm install @odda-ai/matching-core
95
+ ```
96
+
97
+ ### Peer Dependencies
98
+
99
+ ```bash
100
+ # Se usi OpenAI
101
+ npm install openai
102
+
103
+ # Se usi Anthropic Claude
104
+ npm install @anthropic-ai/sdk
105
+
106
+ # Se usi Ollama (nessuna dipendenza extra)
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 🚀 Quick Start
112
+
113
+ ### 1. Setup Base con OpenAI
114
+
115
+ ```typescript
116
+ import { createAIProvider, AiTalentService } from '@odda-ai/matching-core';
117
+
118
+ // Crea provider AI
119
+ const aiProvider = createAIProvider({
120
+ provider: 'openai',
121
+ apiKey: process.env.OPENAI_API_KEY,
122
+ model: 'gpt-4-turbo-preview',
123
+ });
124
+
125
+ // Crea service principale
126
+ const aiTalent = new AiTalentService(aiProvider);
127
+ ```
128
+
129
+ ### 2. Analizza un CV
130
+
131
+ ```typescript
132
+ import fs from 'fs';
133
+
134
+ // Leggi CV PDF
135
+ const cvBuffer = fs.readFileSync('./cv-mario-rossi.pdf');
136
+
137
+ // Analizza CV
138
+ const analysis = await aiTalent.cv.analyzeCvResume(cvBuffer);
139
+
140
+ console.log('Nome:', analysis.personalInfo.name);
141
+ console.log('Seniority:', analysis.overallSeniority);
142
+ console.log('Skills:', analysis.technicalSkills.map(s => s.name));
143
+ console.log('Certificazioni:', analysis.certifications.length);
144
+ ```
145
+
146
+ ### 3. Estrai Skills da Job Description
147
+
148
+ ```typescript
149
+ const jobDescription = `
150
+ Cerchiamo un Senior Full-Stack Developer con esperienza in:
151
+ - React, TypeScript, Node.js
152
+ - AWS, Docker, Kubernetes
153
+ - PostgreSQL, MongoDB
154
+ `;
155
+
156
+ const existingSkills = [
157
+ 'React', 'Vue', 'Angular', 'TypeScript', 'JavaScript',
158
+ 'Node.js', 'Python', 'AWS', 'Azure', 'Docker'
159
+ ];
160
+
161
+ const jobSkills = await aiTalent.jobs.getSkillsFromJobDescription(
162
+ jobDescription,
163
+ existingSkills
164
+ );
165
+
166
+ console.log('Skills richieste:', jobSkills);
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 🏗️ Architettura
172
+
173
+ ```
174
+ @odda-ai/matching-core/
175
+ ├── ai/ # AI Provider Layer
176
+ │ ├── AIProvider.ts # Interface unificata
177
+ │ ├── factory.ts # Factory per creazione provider
178
+ │ ├── registry.ts # Registry adapter
179
+ │ ├── types.ts # Types condivisi
180
+ │ └── adapters/
181
+ │ ├── OpenAIAdapter.ts # Adapter OpenAI
182
+ │ ├── AnthropicAdapter.ts # Adapter Anthropic
183
+ │ └── OllamaAdapter.ts # Adapter Ollama
184
+
185
+ ├── cv-parser/ # PDF Parsing Layer
186
+ │ ├── PDFParserService.ts # Service parsing PDF
187
+ │ └── types.ts # Types per parsing
188
+
189
+ └── features/ # Business Logic Layer
190
+ ├── ai-talent.service.ts # Facade principale
191
+ ├── ai-cv-resume.service.ts # CV analysis
192
+ ├── job-matcher.service.ts # Job skill extraction
193
+ ├── cv-chunking.service.ts # CV chunking per RAG
194
+ ├── prompts.ts # AI prompts
195
+ ├── system-messages.ts # System messages
196
+ └── types.ts # Response types
197
+ ```
198
+
199
+ ### Data Flow
200
+
201
+ ```
202
+ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
203
+ │ PDF CV │──────▶│ PDFParser │──────▶│ Raw Text │
204
+ └─────────────┘ └──────────────┘ └─────────────┘
205
+
206
+
207
+ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
208
+ │ Structured │◀──────│ AI Provider │◀──────│ Prompts │
209
+ │ Output │ │ (GPT/Claude) │ │ + Context │
210
+ └─────────────┘ └──────────────┘ └─────────────┘
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 🤖 AI Providers
216
+
217
+ ### Supported Providers
218
+
219
+ | Provider | Model Support | Streaming | Vision | Function Calling |
220
+ |----------|---------------|-----------|--------|------------------|
221
+ | **OpenAI** | GPT-3.5, GPT-4, GPT-4o | ✅ | ✅ | ✅ |
222
+ | **Anthropic** | Claude 3 (Opus, Sonnet, Haiku) | ✅ | ✅ | ✅ |
223
+ | **Ollama** | Llama 2, Mistral, CodeLlama, etc. | ✅ | ❌ | ⚠️ Limited |
224
+
225
+ ### Factory Pattern
226
+
227
+ ```typescript
228
+ import { createAIProvider } from '@odda-ai/matching-core';
229
+
230
+ // OpenAI
231
+ const openai = createAIProvider({
232
+ provider: 'openai',
233
+ apiKey: process.env.OPENAI_API_KEY,
234
+ model: 'gpt-4-turbo-preview',
235
+ });
236
+
237
+ // Anthropic Claude
238
+ const claude = createAIProvider({
239
+ provider: 'anthropic',
240
+ apiKey: process.env.ANTHROPIC_API_KEY,
241
+ model: 'claude-3-opus-20240229',
242
+ });
243
+
244
+ // Ollama (local)
245
+ const ollama = createAIProvider({
246
+ provider: 'ollama',
247
+ baseURL: 'http://localhost:11434',
248
+ model: 'llama2',
249
+ });
250
+ ```
251
+
252
+ ### Custom Provider Configuration
253
+
254
+ ```typescript
255
+ const provider = createAIProvider({
256
+ provider: 'openai',
257
+ apiKey: process.env.OPENAI_API_KEY,
258
+ model: 'gpt-4',
259
+ temperature: 0.3, // Più deterministico
260
+ maxTokens: 4000, // Max token response
261
+ responseFormat: 'json', // JSON mode
262
+ });
263
+ ```
264
+
265
+ ### Direct Provider Usage
266
+
267
+ ```typescript
268
+ import { AIProvider } from '@odda-ai/matching-core';
269
+
270
+ // Usa direttamente il provider
271
+ const response = await aiProvider.chat([
272
+ {
273
+ role: 'system',
274
+ content: 'You are a helpful assistant.',
275
+ },
276
+ {
277
+ role: 'user',
278
+ content: 'Explain TypeScript generics.',
279
+ },
280
+ ]);
281
+
282
+ console.log(response.content);
283
+ ```
284
+
285
+ ---
286
+
287
+ ## 📄 CV Parser
288
+
289
+ ### Basic Usage
290
+
291
+ ```typescript
292
+ import { PDFParserService } from '@odda-ai/matching-core';
293
+
294
+ const parser = new PDFParserService();
295
+
296
+ // Parse PDF
297
+ const result = await parser.parsePDF(pdfBuffer);
298
+
299
+ console.log('Text:', result.text);
300
+ console.log('Pages:', result.numPages);
301
+ console.log('Info:', result.info);
302
+ ```
303
+
304
+ ### Advanced Options
305
+
306
+ ```typescript
307
+ const result = await parser.parsePDF(pdfBuffer, {
308
+ max: 50, // Max pages to parse
309
+ version: '1.10.100', // PDF.js version
310
+ verbosity: 0, // Logging level (0-5)
311
+ });
312
+ ```
313
+
314
+ ### Error Handling
315
+
316
+ ```typescript
317
+ try {
318
+ const result = await parser.parsePDF(buffer);
319
+ } catch (error) {
320
+ if (error.message.includes('Invalid PDF')) {
321
+ console.error('File non è un PDF valido');
322
+ } else {
323
+ console.error('Errore parsing:', error.message);
324
+ }
325
+ }
326
+ ```
327
+
328
+ ---
329
+
330
+ ## 🎯 Features & Services
331
+
332
+ ### AiTalentService
333
+
334
+ Facade principale che orchestra tutti i servizi.
335
+
336
+ ```typescript
337
+ import { AiTalentService } from '@odda-ai/matching-core';
338
+
339
+ const service = new AiTalentService(aiProvider);
340
+
341
+ // CV Analysis
342
+ const cvAnalysis = await service.cv.analyzeCvResume(buffer);
343
+
344
+ // CV Chunking (per RAG/Vector DB)
345
+ const chunks = await service.cv.chunkCvAnalysis(cvAnalysis, {
346
+ chunkSize: 500,
347
+ overlap: 50,
348
+ });
349
+
350
+ // Job Skills Extraction
351
+ const skills = await service.jobs.getSkillsFromJobDescription(
352
+ jobDescription,
353
+ existingSkills
354
+ );
355
+ ```
356
+
357
+ ### CV Analysis Response Structure
358
+
359
+ ```typescript
360
+ interface CvAnalysisResponse {
361
+ // Personal Information
362
+ personalInfo: {
363
+ name: string;
364
+ email?: string;
365
+ phone?: string;
366
+ location?: string;
367
+ linkedIn?: string;
368
+ portfolio?: string;
369
+ github?: string;
370
+ };
371
+
372
+ // Technical Skills
373
+ technicalSkills: Array<{
374
+ name: string;
375
+ proficiency: number; // 0-100
376
+ yearsOfExperience?: number;
377
+ lastUsed?: string;
378
+ }>;
379
+
380
+ // Soft Skills
381
+ softSkills: Array<{
382
+ name: string;
383
+ importance: 'HIGH' | 'MEDIUM' | 'LOW';
384
+ }>;
385
+
386
+ // Work Experience
387
+ workExperience: Array<{
388
+ company: string;
389
+ role: string;
390
+ startDate: string;
391
+ endDate?: string;
392
+ description?: string;
393
+ achievements?: string[];
394
+ }>;
395
+
396
+ // Education
397
+ education: Array<{
398
+ institution: string;
399
+ degree: string;
400
+ field?: string;
401
+ startDate?: string;
402
+ endDate?: string;
403
+ grade?: string;
404
+ }>;
405
+
406
+ // Certifications
407
+ certifications: Array<{
408
+ name: string;
409
+ provider: string;
410
+ obtainedDate?: string;
411
+ expiryDate?: string;
412
+ credentialId?: string;
413
+ }>;
414
+
415
+ // Languages
416
+ languages: Array<{
417
+ name: string;
418
+ proficiency: 'NATIVE' | 'FLUENT' | 'ADVANCED' | 'INTERMEDIATE' | 'BASIC';
419
+ }>;
420
+
421
+ // Summary
422
+ professionalSummary?: string;
423
+ overallSeniority: 'JUNIOR' | 'MID' | 'SENIOR' | 'LEAD' | 'PRINCIPAL';
424
+ yearsOfExperience: number;
425
+ strengths: string[];
426
+ areasForGrowth: string[];
427
+ keyAchievements: string[];
428
+ }
429
+ ```
430
+
431
+ ### CV Chunking
432
+
433
+ Divide il CV analysis in chunk per vector databases o RAG systems.
434
+
435
+ ```typescript
436
+ const chunks = await service.cv.chunkCvAnalysis(cvAnalysis, {
437
+ chunkSize: 500, // Caratteri per chunk
438
+ overlap: 50, // Overlap tra chunk
439
+ strategy: 'semantic' // 'semantic' | 'fixed'
440
+ });
441
+
442
+ // Ogni chunk contiene:
443
+ chunks.forEach(chunk => {
444
+ console.log('Text:', chunk.text);
445
+ console.log('Type:', chunk.type); // 'personal' | 'skills' | 'experience' | 'education'
446
+ console.log('Metadata:', chunk.metadata);
447
+ console.log('Embedding:', chunk.embedding); // Optional, se generato
448
+ });
449
+ ```
450
+
451
+ ### Job Skills Extraction
452
+
453
+ ```typescript
454
+ const skills = await service.jobs.getSkillsFromJobDescription(
455
+ jobDescription,
456
+ existingSkillsList
457
+ );
458
+
459
+ // Response structure
460
+ interface JobSkill {
461
+ name: string;
462
+ importance: number; // 0-100
463
+ skillType: 'TECHNICAL' | 'SOFT';
464
+ category: 'REQUIRED' | 'NICE_TO_HAVE';
465
+ context?: string; // Contesto dalla job description
466
+ matchedFrom?: string; // Skill originale matchata
467
+ }
468
+ ```
469
+
470
+ ---
471
+
472
+ ## 📚 API Reference
473
+
474
+ ### createAIProvider()
475
+
476
+ ```typescript
477
+ function createAIProvider(config: AIProviderConfig): AIProvider
478
+ ```
479
+
480
+ **Parameters:**
481
+ - `provider`: `'openai' | 'anthropic' | 'ollama'`
482
+ - `apiKey`: string (required per openai/anthropic)
483
+ - `model`: string (es. 'gpt-4', 'claude-3-opus-20240229')
484
+ - `baseURL?`: string (per Ollama o proxy)
485
+ - `temperature?`: number (0-2)
486
+ - `maxTokens?`: number
487
+ - `responseFormat?`: `'text' | 'json'`
488
+
489
+ ### AiTalentService
490
+
491
+ #### Constructor
492
+
493
+ ```typescript
494
+ new AiTalentService(aiProvider: AIProvider)
495
+ ```
496
+
497
+ #### Methods
498
+
499
+ **cv.analyzeCvResume()**
500
+ ```typescript
501
+ async analyzeCvResume(
502
+ pdfBuffer: Buffer,
503
+ options?: AnalyzeResumeOptions
504
+ ): Promise<CvAnalysisResponse>
505
+ ```
506
+
507
+ **cv.chunkCvAnalysis()**
508
+ ```typescript
509
+ async chunkCvAnalysis(
510
+ cvAnalysis: CvAnalysisResponse,
511
+ options?: ChunkingOptions
512
+ ): Promise<CvChunk[]>
513
+ ```
514
+
515
+ **jobs.getSkillsFromJobDescription()**
516
+ ```typescript
517
+ async getSkillsFromJobDescription(
518
+ jobDescription: string,
519
+ existingSkills: string[]
520
+ ): Promise<JobSkill[]>
521
+ ```
522
+
523
+ ### PDFParserService
524
+
525
+ ```typescript
526
+ class PDFParserService {
527
+ async parsePDF(
528
+ buffer: Buffer,
529
+ options?: ParseOptions
530
+ ): Promise<ParsedPDF>
531
+ }
532
+ ```
533
+
534
+ ---
535
+
536
+ ## 💡 Esempi Avanzati
537
+
538
+ ### Batch CV Analysis
539
+
540
+ ```typescript
541
+ async function batchAnalyze(cvPaths: string[]) {
542
+ const results = [];
543
+
544
+ for (const path of cvPaths) {
545
+ try {
546
+ const buffer = fs.readFileSync(path);
547
+ const analysis = await aiTalent.cv.analyzeCvResume(buffer);
548
+
549
+ results.push({
550
+ path,
551
+ status: 'success',
552
+ data: analysis,
553
+ });
554
+ } catch (error) {
555
+ results.push({
556
+ path,
557
+ status: 'error',
558
+ error: error.message,
559
+ });
560
+ }
561
+ }
562
+
563
+ return results;
564
+ }
565
+ ```
566
+
567
+ ### Skill Matching con Tolleranza
568
+
569
+ ```typescript
570
+ function fuzzyMatchSkills(
571
+ cvSkills: string[],
572
+ jobSkills: string[],
573
+ threshold: number = 0.8
574
+ ) {
575
+ const matches = [];
576
+
577
+ for (const cvSkill of cvSkills) {
578
+ for (const jobSkill of jobSkills) {
579
+ const similarity = calculateSimilarity(cvSkill, jobSkill);
580
+
581
+ if (similarity >= threshold) {
582
+ matches.push({
583
+ cvSkill,
584
+ jobSkill,
585
+ similarity,
586
+ });
587
+ }
588
+ }
589
+ }
590
+
591
+ return matches;
592
+ }
593
+ ```
594
+
595
+ ### Vector Store Integration
596
+
597
+ ```typescript
598
+ import { Pinecone } from '@pinecone-database/pinecone';
599
+
600
+ const pinecone = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
601
+ const index = pinecone.index('cv-embeddings');
602
+
603
+ // Chunka e genera embeddings
604
+ const chunks = await service.cv.chunkCvAnalysis(cvAnalysis);
605
+
606
+ // Upsert in Pinecone
607
+ const vectors = chunks.map((chunk, i) => ({
608
+ id: `cv-${cvAnalysis.personalInfo.name}-${i}`,
609
+ values: chunk.embedding || [], // Generate with OpenAI embeddings API
610
+ metadata: {
611
+ text: chunk.text,
612
+ type: chunk.type,
613
+ ...chunk.metadata,
614
+ },
615
+ }));
616
+
617
+ await index.upsert(vectors);
618
+ ```
619
+
620
+ ### Switch Provider Dinamico
621
+
622
+ ```typescript
623
+ class AIService {
624
+ private providers: Map<string, AIProvider>;
625
+
626
+ constructor() {
627
+ this.providers = new Map([
628
+ ['fast', createAIProvider({ provider: 'openai', model: 'gpt-3.5-turbo' })],
629
+ ['accurate', createAIProvider({ provider: 'openai', model: 'gpt-4' })],
630
+ ['local', createAIProvider({ provider: 'ollama', model: 'llama2' })],
631
+ ]);
632
+ }
633
+
634
+ async analyze(cv: Buffer, mode: 'fast' | 'accurate' | 'local') {
635
+ const provider = this.providers.get(mode)!;
636
+ const service = new AiTalentService(provider);
637
+ return service.cv.analyzeCvResume(cv);
638
+ }
639
+ }
640
+ ```
641
+
642
+ ---
643
+
644
+ ## ⚙️ Configurazione
645
+
646
+ ### Environment Variables
647
+
648
+ ```bash
649
+ # OpenAI
650
+ OPENAI_API_KEY=sk-...
651
+ OPENAI_MODEL=gpt-4-turbo-preview
652
+
653
+ # Anthropic
654
+ ANTHROPIC_API_KEY=sk-ant-...
655
+ ANTHROPIC_MODEL=claude-3-opus-20240229
656
+
657
+ # Ollama
658
+ OLLAMA_BASE_URL=http://localhost:11434
659
+ OLLAMA_MODEL=llama2
660
+ ```
661
+
662
+ ### Configuration File
663
+
664
+ ```typescript
665
+ // config/ai.config.ts
666
+ import { AIProviderConfig } from '@odda-ai/matching-core';
667
+
668
+ export const aiConfig: AIProviderConfig = {
669
+ provider: process.env.AI_PROVIDER as any || 'openai',
670
+ apiKey: process.env.AI_API_KEY || '',
671
+ model: process.env.AI_MODEL || 'gpt-4',
672
+ temperature: 0.3,
673
+ maxTokens: 4000,
674
+ responseFormat: 'json',
675
+ };
676
+ ```
677
+
678
+ ---
679
+
680
+ ## 📘 TypeScript Support
681
+
682
+ Libreria fully-typed con completo supporto TypeScript.
683
+
684
+ ### Type Imports
685
+
686
+ ```typescript
687
+ import type {
688
+ // Provider types
689
+ AIProvider,
690
+ AIProviderConfig,
691
+ ChatMessage,
692
+ ChatResponse,
693
+
694
+ // CV Analysis types
695
+ CvAnalysisResponse,
696
+ PersonalInfo,
697
+ TechnicalSkill,
698
+ SoftSkill,
699
+ WorkExperience,
700
+ Education,
701
+ Certification,
702
+ Language,
703
+
704
+ // Chunking types
705
+ CvChunk,
706
+ ChunkingOptions,
707
+ ChunkingResult,
708
+
709
+ // Job Matching types
710
+ JobSkill,
711
+
712
+ // Parser types
713
+ ParsedPDF,
714
+ ParseOptions,
715
+ } from '@odda-ai/matching-core';
716
+ ```
717
+
718
+ ### Generic Support
719
+
720
+ ```typescript
721
+ interface CustomAnalysis extends CvAnalysisResponse {
722
+ customField: string;
723
+ }
724
+
725
+ const analysis: CustomAnalysis = {
726
+ ...await service.cv.analyzeCvResume(buffer),
727
+ customField: 'custom value',
728
+ };
729
+ ```
730
+
731
+ ---
732
+
733
+ ## 🧪 Testing
734
+
735
+ ### Unit Tests
736
+
737
+ ```typescript
738
+ import { describe, it, expect } from 'vitest';
739
+ import { createAIProvider } from '@odda-ai/matching-core';
740
+
741
+ describe('AI Provider', () => {
742
+ it('should create OpenAI provider', () => {
743
+ const provider = createAIProvider({
744
+ provider: 'openai',
745
+ apiKey: 'test-key',
746
+ });
747
+
748
+ expect(provider).toBeDefined();
749
+ });
750
+ });
751
+ ```
752
+
753
+ ### Integration Tests
754
+
755
+ ```typescript
756
+ describe('CV Analysis', () => {
757
+ it('should analyze CV correctly', async () => {
758
+ const buffer = fs.readFileSync('./test/fixtures/sample-cv.pdf');
759
+ const analysis = await service.cv.analyzeCvResume(buffer);
760
+
761
+ expect(analysis.personalInfo.name).toBeDefined();
762
+ expect(analysis.technicalSkills.length).toBeGreaterThan(0);
763
+ expect(analysis.overallSeniority).toMatch(/JUNIOR|MID|SENIOR|LEAD|PRINCIPAL/);
764
+ });
765
+ });
766
+ ```
767
+
768
+ ---
769
+
770
+ ## 📄 License
771
+
772
+ ISC
773
+
774
+ ---
775
+
776
+ ## 🤝 Contributing
777
+
778
+ Contributions are welcome! Please open an issue or submit a pull request.
779
+
780
+ ---
781
+
782
+ ## 🔗 Links
783
+
784
+ - **Main Project**: [Odda Matching AI](../README.md)
785
+ - **API Documentation**: [ai-talent-api/README.md](../ai-talent-api/README.md)
786
+ - **Dashboard**: [insights/README.md](../insights/README.md)
787
+
788
+ ---
789
+
790
+ **Made with ❤️ by Odda Studio**