Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@odda-ai/matching-core

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@odda-ai/matching-core

Core AI provider library with support for OpenAI, Ollama, and Anthropic

latest
npmnpm
Version
1.0.18
Version published
Maintainers
1
Created
Source

🧠 @odda-ai/matching-core

Libreria TypeScript modulare per l'integrazione di provider AI multipli, parsing di CV in PDF e analisi intelligente di competenze e profili professionali.

Version License TypeScript

📋 Indice

  • Overview
  • Features
  • Installazione
  • Quick Start
  • Architettura
  • AI Providers
  • CV Parser
  • Features & Services
  • API Reference
  • Esempi Avanzati
  • Configurazione
  • TypeScript Support
  • Testing

🌟 Overview

@odda-ai/matching-core è una libreria core che fornisce:

  • 🤖 Astrazione unificata per provider AI multipli (OpenAI, Anthropic, Ollama)
  • 📄 PDF parsing ottimizzato per CV e documenti strutturati
  • 🎯 Analisi CV intelligente con estrazione automatica di skill, certificazioni, esperienza
  • 📊 Skill extraction da job description con matching semantico
  • 🔧 Type-safe con pieno supporto TypeScript
  • 🧩 Modulare con architettura a plugin per provider AI

Quando usarlo

  • ✅ Costruire sistemi di recruitment e talent matching
  • ✅ Automatizzare l'analisi di CV e profili professionali
  • ✅ Estrarre competenze da job description
  • ✅ Integrare AI in applicazioni esistenti con provider pluggable
  • ✅ Processare documenti PDF strutturati

✨ Features

🤖 AI Provider Abstraction

  • Multi-provider support: OpenAI, Anthropic (Claude), Ollama
  • Factory pattern per creazione provider
  • Registry centralizzato per adapter management
  • Configurazione per provider con modelli specifici
  • Error handling consistente tra provider

📄 CV Parsing

  • PDF extraction con pdf-parse
  • Text cleaning e normalizzazione
  • Metadata extraction automatica
  • Buffer-based API per flessibilità
  • Error handling robusto

🎯 CV Analysis

  • Structured extraction di:
    • Personal info (nome, email, phone, location, LinkedIn)
    • Technical skills con proficiency level
    • Soft skills con importanza
    • Work experience con ruoli e durata
    • Education con titoli e istituzioni
    • Certifications con provider e validità
    • Languages con livello di competenza
  • Seniority assessment automatico (Junior, Mid, Senior, Lead, Principal)
  • Years of experience calcolati
  • JSON-structured output per facile integrazione

📊 Job Matching

  • Skill extraction da job description
  • Semantic matching con skill esistenti
  • Importance weighting automatico
  • Categorization (Technical/Soft, Required/Nice-to-have)

📦 Installazione

npm install @odda-ai/matching-core

Peer Dependencies

# Se usi OpenAI
npm install openai

# Se usi Anthropic Claude
npm install @anthropic-ai/sdk

# Se usi Ollama (nessuna dipendenza extra)

🚀 Quick Start

1. Setup Base con OpenAI

import { createAIProvider, AiTalentService } from '@odda-ai/matching-core';

// Crea provider AI
const aiProvider = createAIProvider({
  provider: 'openai',
  apiKey: process.env.OPENAI_API_KEY,
  model: 'gpt-4-turbo-preview',
});

// Crea service principale
const aiTalent = new AiTalentService(aiProvider);

2. Analizza un CV

import fs from 'fs';

// Leggi CV PDF
const cvBuffer = fs.readFileSync('./cv-mario-rossi.pdf');

// Analizza CV
const analysis = await aiTalent.cv.analyzeCvResume(cvBuffer);

console.log('Nome:', analysis.personalInfo.firstName, analysis.personalInfo.lastName);
console.log('Seniority:', analysis.overallSeniority);
console.log('Skills:', analysis.technicalSkills.map(s => s.name));
console.log('Certificazioni:', analysis.certifications.length);

3. Estrai Skills da Job Description

const jobDescription = `
  Cerchiamo un Senior Full-Stack Developer con esperienza in:
  - React, TypeScript, Node.js
  - AWS, Docker, Kubernetes
  - PostgreSQL, MongoDB
`;

const existingSkills = [
  'React', 'Vue', 'Angular', 'TypeScript', 'JavaScript',
  'Node.js', 'Python', 'AWS', 'Azure', 'Docker'
];

const jobSkills = await aiTalent.jobs.getSkillsFromJobDescription(
  jobDescription,
  existingSkills
);

console.log('Skills richieste:', jobSkills);

🏗️ Architettura

@odda-ai/matching-core/
├── ai/                          # AI Provider Layer
│   ├── AIProvider.ts            # Interface unificata
│   ├── factory.ts               # Factory per creazione provider
│   ├── registry.ts              # Registry adapter
│   ├── types.ts                 # Types condivisi
│   └── adapters/
│       ├── OpenAIAdapter.ts     # Adapter OpenAI
│       ├── AnthropicAdapter.ts  # Adapter Anthropic
│       └── OllamaAdapter.ts     # Adapter Ollama
│
├── cv-parser/                   # PDF Parsing Layer
│   ├── PDFParserService.ts      # Service parsing PDF
│   └── types.ts                 # Types per parsing
│
└── features/                          # Business Logic Layer
    ├── ai-talent.service.ts           # Facade principale
    ├── ai-cv-resume.service.ts        # CV analysis
    ├── ai-talent-review.service.ts    # Talent review (free-form AI)
    ├── job-matcher.service.ts         # Job skill extraction
    ├── cv-chunking.service.ts         # CV chunking per RAG
    ├── prompts.ts                     # AI prompts
    ├── system-messages.ts             # System messages
    └── types.ts                       # Response types

Data Flow

┌─────────────┐       ┌──────────────┐       ┌─────────────┐
│   PDF CV    │──────▶│ PDFParser    │──────▶│  Raw Text   │
└─────────────┘       └──────────────┘       └─────────────┘
                                                     │
                                                     ▼
┌─────────────┐       ┌──────────────┐       ┌─────────────┐
│ Structured  │◀──────│ AI Provider  │◀──────│  Prompts    │
│   Output    │       │ (GPT/Claude) │       │  + Context  │
└─────────────┘       └──────────────┘       └─────────────┘

🤖 AI Providers

Supported Providers

ProviderModel SupportStreamingVisionFunction Calling
OpenAIGPT-3.5, GPT-4, GPT-4o
AnthropicClaude 3 (Opus, Sonnet, Haiku)
OllamaLlama 2, Mistral, CodeLlama, etc.⚠️ Limited

Factory Pattern

import { createAIProvider } from '@odda-ai/matching-core';

// OpenAI
const openai = createAIProvider({
  provider: 'openai',
  apiKey: process.env.OPENAI_API_KEY,
  model: 'gpt-4-turbo-preview',
});

// Anthropic Claude
const claude = createAIProvider({
  provider: 'anthropic',
  apiKey: process.env.ANTHROPIC_API_KEY,
  model: 'claude-3-opus-20240229',
});

// Ollama (local)
const ollama = createAIProvider({
  provider: 'ollama',
  baseURL: 'http://localhost:11434',
  model: 'llama2',
});

Custom Provider Configuration

const provider = createAIProvider({
  provider: 'openai',
  apiKey: process.env.OPENAI_API_KEY,
  model: 'gpt-4',
  temperature: 0.3,          // Più deterministico
  maxTokens: 4000,           // Max token response
  responseFormat: 'json',    // JSON mode
});

Direct Provider Usage

import { AIProvider } from '@odda-ai/matching-core';

// Usa direttamente il provider
const response = await aiProvider.chat([
  {
    role: 'system',
    content: 'You are a helpful assistant.',
  },
  {
    role: 'user',
    content: 'Explain TypeScript generics.',
  },
]);

console.log(response.content);

📄 CV Parser

Basic Usage

import { PDFParserService } from '@odda-ai/matching-core';

const parser = new PDFParserService();

// Parse PDF
const result = await parser.parsePDF(pdfBuffer);

console.log('Text:', result.text);
console.log('Pages:', result.numPages);
console.log('Info:', result.info);

Advanced Options

const result = await parser.parsePDF(pdfBuffer, {
  max: 50,              // Max pages to parse
  version: '1.10.100',  // PDF.js version
  verbosity: 0,         // Logging level (0-5)
});

Error Handling

try {
  const result = await parser.parsePDF(buffer);
} catch (error) {
  if (error.message.includes('Invalid PDF')) {
    console.error('File non è un PDF valido');
  } else {
    console.error('Errore parsing:', error.message);
  }
}

🎯 Features & Services

AiTalentService

Facade principale che orchestra tutti i servizi.

import { AiTalentService } from '@odda-ai/matching-core';

const service = new AiTalentService(aiProvider);

// CV Analysis (output strutturato)
const cvAnalysis = await service.cv.analyzeCvResume(buffer);

// CV Chunking (per RAG/Vector DB)
const chunks = await service.cv.chunkCvAnalysis(cvAnalysis, {
  chunkSize: 500,
  overlap: 50,
});

// Job Skills Extraction
const skills = await service.jobs.getSkillsFromJobDescription(
  jobDescription,
  existingSkills
);

// Talent Review (analisi libera via AI, senza schema fisso)
const review = await service.talentReview.analyzeResume(buffer);

CV Analysis Response Structure

enum Seniority {
  JUNIOR = 'JUNIOR',
  MID = 'MID',
  SENIOR = 'SENIOR',
  LEAD = 'LEAD',
  PRINCIPAL = 'PRINCIPAL',
}

type PersonalInfo = {
  firstName?: string;
  lastName?: string;
  email?: string;
  phone?: string;
  address?: string;
  dateOfBirth?: string;   // YYYY-MM-DD
  nationality?: string;
  linkedIn?: string;
  github?: string;
  website?: string;
};

type TechnicalSkill = {
  name: string;
  proficiency: number;    // 0-100
  isInferred: boolean;    // true se dedotta da skill correlate
  seniority: Seniority;
};

type Certification = {
  name: string;
  issuer?: string;
  year?: number;
};

type CvAnalysisResponse = {
  personalInfo: PersonalInfo;
  description: string;                 // profilo professionale (2-3 frasi)
  technicalSkills: TechnicalSkill[];
  workExperienceSummary: string;       // riassunto testuale delle esperienze
  certifications: Certification[];
  overallSeniority: Seniority;
  yearsOfExperience: number;
};

Nota: workExperienceSummary è una stringa testuale sintetica, non un array di esperienze strutturate. Il prompt AI è progettato per la massima stabilità e ripetibilità dell'output.

CV Chunking

Divide il CV analysis in chunk per vector databases o RAG systems.

const chunks = await service.cv.chunkCvAnalysis(cvAnalysis, {
  chunkSize: 500,      // Caratteri per chunk
  overlap: 50,         // Overlap tra chunk
  strategy: 'semantic' // 'semantic' | 'fixed'
});

// Ogni chunk contiene:
chunks.forEach(chunk => {
  console.log('Text:', chunk.text);
  console.log('Type:', chunk.type);          // 'personal' | 'skills' | 'experience' | 'education'
  console.log('Metadata:', chunk.metadata);
  console.log('Embedding:', chunk.embedding); // Optional, se generato
});

Job Skills Extraction

const skills = await service.jobs.getSkillsFromJobDescription(
  jobDescription,
  existingSkillsList
);

La risposta è tipizzata tramite JobSkillType:

enum JobSkillType {
  TECHNICAL_REQUIRED = 'technical_required',
  TECHNICAL_NICE_TO_HAVE = 'technical_nice_to_have',
  SOFT_REQUIRED = 'soft_required',
  SOFT_NICE_TO_HAVE = 'soft_nice_to_have',
}

Talent Review

Analisi libera di un CV via AI senza schema rigido. Utile per note di screening, commenti HR, ecc.

const review = await service.talentReview.analyzeResume(buffer);
// Restituisce il JSON grezzo parsato dalla risposta AI (struttura flessibile)

📚 API Reference

createAIProvider()

function createAIProvider(config: AIProviderConfig): AIProvider

Parameters:

  • provider: 'openai' | 'anthropic' | 'ollama'
  • apiKey: string (required per openai/anthropic)
  • model: string (es. 'gpt-4', 'claude-3-opus-20240229')
  • baseURL?: string (per Ollama o proxy)
  • temperature?: number (0-2)
  • maxTokens?: number
  • responseFormat?: 'text' | 'json'

AiTalentService

Constructor

new AiTalentService(aiProvider: AIProvider)

Methods

cv.analyzeCvResume()

async analyzeCvResume(
  pdfBuffer: Buffer,
  options?: AnalyzeResumeOptions
): Promise<CvAnalysisResponse>

cv.chunkCvAnalysis()

async chunkCvAnalysis(
  cvAnalysis: CvAnalysisResponse,
  options?: ChunkingOptions
): Promise<CvChunk[]>

jobs.getSkillsFromJobDescription()

async getSkillsFromJobDescription(
  jobDescription: string,
  existingSkills: string[]
): Promise<JobSkill[]>

talentReview.analyzeResume()

async analyzeResume(
  pdfBuffer: Buffer,
  options?: AnalyzeResumeOptions
): Promise<unknown>   // struttura flessibile, parsata dalla risposta AI

PDFParserService

class PDFParserService {
  async parsePDF(
    buffer: Buffer,
    options?: ParseOptions
  ): Promise<ParsedPDF>
}

💡 Esempi Avanzati

Batch CV Analysis

async function batchAnalyze(cvPaths: string[]) {
  const results = [];
  
  for (const path of cvPaths) {
    try {
      const buffer = fs.readFileSync(path);
      const analysis = await aiTalent.cv.analyzeCvResume(buffer);
      
      results.push({
        path,
        status: 'success',
        data: analysis,
      });
    } catch (error) {
      results.push({
        path,
        status: 'error',
        error: error.message,
      });
    }
  }
  
  return results;
}

Skill Matching con Tolleranza

function fuzzyMatchSkills(
  cvSkills: string[],
  jobSkills: string[],
  threshold: number = 0.8
) {
  const matches = [];
  
  for (const cvSkill of cvSkills) {
    for (const jobSkill of jobSkills) {
      const similarity = calculateSimilarity(cvSkill, jobSkill);
      
      if (similarity >= threshold) {
        matches.push({
          cvSkill,
          jobSkill,
          similarity,
        });
      }
    }
  }
  
  return matches;
}

Vector Store Integration

import { Pinecone } from '@pinecone-database/pinecone';

const pinecone = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const index = pinecone.index('cv-embeddings');

// Chunka e genera embeddings
const chunks = await service.cv.chunkCvAnalysis(cvAnalysis);

// Upsert in Pinecone
const vectors = chunks.map((chunk, i) => ({
  id: `cv-${cvAnalysis.personalInfo.firstName}-${cvAnalysis.personalInfo.lastName}-${i}`,
  values: chunk.embedding || [], // Generate with OpenAI embeddings API
  metadata: {
    text: chunk.text,
    type: chunk.type,
    ...chunk.metadata,
  },
}));

await index.upsert(vectors);

Switch Provider Dinamico

class AIService {
  private providers: Map<string, AIProvider>;
  
  constructor() {
    this.providers = new Map([
      ['fast', createAIProvider({ provider: 'openai', model: 'gpt-3.5-turbo' })],
      ['accurate', createAIProvider({ provider: 'openai', model: 'gpt-4' })],
      ['local', createAIProvider({ provider: 'ollama', model: 'llama2' })],
    ]);
  }
  
  async analyze(cv: Buffer, mode: 'fast' | 'accurate' | 'local') {
    const provider = this.providers.get(mode)!;
    const service = new AiTalentService(provider);
    return service.cv.analyzeCvResume(cv);
  }
}

⚙️ Configurazione

Environment Variables

# OpenAI
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4-turbo-preview

# Anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-3-opus-20240229

# Ollama
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama2

Configuration File

// config/ai.config.ts
import { AIProviderConfig } from '@odda-ai/matching-core';

export const aiConfig: AIProviderConfig = {
  provider: process.env.AI_PROVIDER as any || 'openai',
  apiKey: process.env.AI_API_KEY || '',
  model: process.env.AI_MODEL || 'gpt-4',
  temperature: 0.3,
  maxTokens: 4000,
  responseFormat: 'json',
};

📘 TypeScript Support

Libreria fully-typed con completo supporto TypeScript.

Type Imports

import type {
  // Provider types
  AIProvider,
  AIProviderConfig,

  // CV Analysis types
  CvAnalysisResponse,
  PersonalInfo,
  TechnicalSkill,
  Certification,

  // Enums
  Seniority,
  JobSkillType,

  // Chunking types
  CvChunk,
  ChunkingOptions,
  ChunkingResult,

  // Misc
  AnalyzeResumeOptions,
} from '@odda-ai/matching-core';

Generic Support

interface CustomAnalysis extends CvAnalysisResponse {
  customField: string;
}

const analysis: CustomAnalysis = {
  ...await service.cv.analyzeCvResume(buffer),
  customField: 'custom value',
};

🧪 Testing

Unit Tests

import { describe, it, expect } from 'vitest';
import { createAIProvider } from '@odda-ai/matching-core';

describe('AI Provider', () => {
  it('should create OpenAI provider', () => {
    const provider = createAIProvider({
      provider: 'openai',
      apiKey: 'test-key',
    });
    
    expect(provider).toBeDefined();
  });
});

Integration Tests

describe('CV Analysis', () => {
  it('should analyze CV correctly', async () => {
    const buffer = fs.readFileSync('./test/fixtures/sample-cv.pdf');
    const analysis = await service.cv.analyzeCvResume(buffer);
    
    expect(analysis.personalInfo.firstName).toBeDefined();
    expect(analysis.technicalSkills.length).toBeGreaterThan(0);
    expect(analysis.overallSeniority).toMatch(/JUNIOR|MID|SENIOR|LEAD|PRINCIPAL/);
  });
});

📄 License

ISC

🤝 Contributing

Contributions are welcome! Please open an issue or submit a pull request.

Made with ❤️ by Odda Studio

FAQs

Package last updated on 25 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts