OrkaJS
Orka.JS

Orchestration du Fine-tuning

Validation de datasets, orchestration d'API et versionnage de modèles

Le package fine-tuning fournit des outils pour orchestrer les jobs de fine-tuning sur plusieurs fournisseurs, valider les datasets, collecter les feedbacks et gérer les versions de modèles.

Installation

npm install @orka-js/finetuning
# or
pnpm add @orka-js/finetuning

Fonctionnalités

Validation de Dataset

Validez les datasets JSONL avant l'entraînement

Multi-Fournisseur

OpenAI, Anthropic, Mistral, Together, Anyscale

Estimation des Coûts

Estimez les coûts d'entraînement avant de commencer

Suivi des Jobs

Suivez la progression et les métriques d'entraînement

Versionnage de Modèles

Suivez les versions des modèles fine-tunés

Collecte de Feedback

Collectez les feedbacks utilisateurs et convertissez-les en datasets

Format du Dataset

Le format JSONL attendu pour le fine-tuning :

{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello"}, {"role": "assistant", "content": "Hi! How can I help?"}]}
{"messages": [{"role": "user", "content": "What is 2+2?"}, {"role": "assistant", "content": "2+2 equals 4."}]}

#Validation de Dataset

Validez vos datasets JSONL avant de les soumettre pour le fine-tuning.

import { DatasetValidator } from '@orka-js/finetuning';
 
const validator = new DatasetValidator('openai');
const result = await validator.validateFile('./training.jsonl');
 
if (result.valid) {
console.log('Dataset is valid!');
console.log('Stats:', result.stats);
// { totalExamples: 100, totalTokens: 50000, avgTokensPerExample: 500, ... }
} else {
console.log('Errors:', result.errors);
// [{ line: 5, message: 'Missing assistant message' }, ...]
}
 
// Check warnings
if (result.warnings.length > 0) {
console.log('Warnings:', result.warnings);
}

#Orchestration du Fine-tuning

Créez et surveillez les jobs de fine-tuning avec suivi automatique du statut.

import { FineTuningOrchestrator } from '@orka-js/finetuning';
 
const orchestrator = new FineTuningOrchestrator({
provider: 'openai',
baseModel: 'gpt-4o-mini-2024-07-18',
apiKey: process.env.OPENAI_API_KEY,
hyperparameters: {
nEpochs: 3,
batchSize: 4,
},
});
 
// Estimate cost before training
const estimate = orchestrator.estimateCost(100000, 3); // 100k tokens, 3 epochs
console.log(`Estimated cost: $${estimate.trainingCost.toFixed(2)}`);
 
// Create a fine-tuning job
const job = await orchestrator.createJob('./training.jsonl', {
validationPath: './validation.jsonl',
});
 
console.log('Job created:', job.id);
 
// Monitor progress
orchestrator.on((event) => {
if (event.type === 'metrics') {
console.log('Training loss:', event.metrics?.trainingLoss);
}
if (event.type === 'message') {
console.log('Status:', event.message);
}
});
 
// Wait for completion
const completed = await orchestrator.waitForCompletion(job.id);
console.log('Fine-tuned model:', completed.fineTunedModel);
 
// Get model versions
const versions = orchestrator.getModelVersions();
const latest = orchestrator.getLatestVersion();

#Collecte de Feedback

Collectez les feedbacks utilisateurs de votre application et convertissez-les en données d'entraînement.

import { FeedbackCollector } from '@orka-js/finetuning';
 
const collector = new FeedbackCollector({
minSamples: 50,
filterLowRatings: true,
ratingThreshold: 4,
});
 
// Collect feedback from your application
collector.add({
input: 'What is the capital of France?',
output: 'The capital of France is Paris.',
rating: 5,
});
 
collector.add({
input: 'Explain quantum computing',
output: 'Quantum computing uses qubits...',
expectedOutput: 'Quantum computing is a type of computation...',
rating: 3,
feedback: 'Could be more detailed',
});
 
// Check stats
const stats = collector.getStats();
console.log('Total entries:', stats.total);
console.log('Filtered entries:', stats.filtered);
console.log('Average rating:', stats.avgRating);
 
// Check if ready for training
if (collector.isReadyForTraining()) {
// Convert to dataset
const dataset = collector.toDataset();
 
// Or save directly to file
await collector.saveToFile('./feedback-dataset.jsonl');
}

Fournisseurs Supportés

Live Ecosystem Compatibility

ProviderIntegration StatusCapability / Models
OpenAI
Full
GPT-4o, GPT-3.5-turbo, DALL-E
Mistral AI
Full
Mixtral, Mistral Large, Codestral
Anthropic
Beta
Claude 3.5 Sonnet / Opus
Together AI
Planned
Llama 3, Qwen, Mixtral 8x22B
Anyscale
Planned
Coming Q3 2026

* All providers are monitored 24/7 for API uptime and performance latency.

Exemples Complets

Exemple 1 : Pipeline de Fine-tuning pour Support Client

Workflow complet de la collecte de feedback au déploiement du modèle fine-tuné :

import { FeedbackCollector, DatasetValidator, FineTuningOrchestrator } from '@orka-js/finetuning';
import { OpenAIAdapter } from '@orka-js/openai';
 
// Étape 1 : Collecter les retours utilisateurs
const collector = new FeedbackCollector({
storage: 'file',
filePath: './feedback.jsonl'
});
 
// Collecter les retours des interactions en production
async function collectProductionFeedback() {
// L'utilisateur pose une question
const userInput = "Comment réinitialiser mon mot de passe ?";
const modelOutput = "Cliquez sur 'Mot de passe oublié' sur la page de connexion.";
 
// L'utilisateur note la réponse
await collector.addFeedback({
input: userInput,
output: modelOutput,
rating: 8,
correction: "Cliquez sur 'Mot de passe oublié' sur la page de connexion, puis vérifiez votre email pour le lien de réinitialisation.",
metadata: {
userId: 'user_123',
timestamp: new Date().toISOString(),
category: 'password_reset'
}
});
 
console.log('Feedback collecté. Total:', await collector.getCount());
}
 
// Étape 2 : Exporter et valider le dataset
async function prepareDataset() {
// Exporter les retours en dataset d'entraînement
const dataset = await collector.exportDataset({
format: 'openai',
minRating: 7, // Utiliser uniquement les réponses bien notées
includeCorrections: true
});
 
console.log(`${dataset.length} exemples d'entraînement exportés`);
 
// Valider le dataset
const validator = new DatasetValidator();
const validation = await validator.validate(dataset, {
provider: 'openai',
checkDuplicates: true,
checkBalance: true
});
 
if (!validation.valid) {
console.error('Erreurs de validation:', validation.errors);
throw new Error('Échec de la validation du dataset');
}
 
console.log('Dataset validé avec succès');
console.log('Statistiques:', validation.stats);
 
return dataset;
}
 
// Étape 3 : Estimer le coût et créer le job de fine-tuning
async function createFineTuningJob(dataset: any[]) {
const orchestrator = new FineTuningOrchestrator({
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY!
});
 
// Estimer le coût avant de commencer
const costEstimate = await orchestrator.estimateCost({
dataset,
baseModel: 'gpt-3.5-turbo',
epochs: 3
});
 
console.log(`Coût estimé : ${costEstimate.estimatedCost.toFixed(2)}$`);
console.log(`Tokens d'entraînement : ${costEstimate.trainingTokens}`);
 
// Créer le job de fine-tuning
const job = await orchestrator.createJob({
trainingData: dataset,
baseModel: 'gpt-3.5-turbo',
suffix: 'customer-support-v1',
hyperparameters: {
nEpochs: 3,
batchSize: 4,
learningRateMultiplier: 0.1
}
});
 
console.log('Job de fine-tuning créé:', job.id);
return job;
}
 
// Étape 4 : Surveiller la progression du job
async function monitorJob(orchestrator: FineTuningOrchestrator, jobId: string) {
const checkInterval = setInterval(async () => {
const jobs = await orchestrator.listJobs();
const job = jobs.find(j => j.id === jobId);
 
if (!job) {
clearInterval(checkInterval);
return;
}
 
console.log(`Job ${jobId} statut : ${job.status}`);
 
if (job.status === 'succeeded') {
console.log('Fine-tuning terminé !');
console.log('Modèle fine-tuné:', job.fineTunedModel);
clearInterval(checkInterval);
 
// Utiliser le modèle fine-tuné
await useFineTunedModel(job.fineTunedModel!);
} else if (job.status === 'failed') {
console.error('Échec du fine-tuning:', job.error);
clearInterval(checkInterval);
}
}, 30000); // Vérifier toutes les 30 secondes
}
 
// Étape 5 : Utiliser le modèle fine-tuné
async function useFineTunedModel(modelName: string) {
const llm = new OpenAIAdapter({
apiKey: process.env.OPENAI_API_KEY!,
model: modelName
});
 
const response = await llm.generate({
messages: [
{ role: 'user', content: 'Comment réinitialiser mon mot de passe ?' }
]
});
 
console.log('Réponse du modèle fine-tuné:', response.content);
}
 
// Exécuter le pipeline complet
async function main() {
// Collecter les retours (exécuter en continu en production)
await collectProductionFeedback();
 
// Quand prêt pour le fine-tuning (ex: après 1000+ exemples collectés)
const dataset = await prepareDataset();
const job = await createFineTuningJob(dataset);
 
const orchestrator = new FineTuningOrchestrator({
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY!
});
 
await monitorJob(orchestrator, job.id);
}
 
main().catch(console.error);

Exemple 2 : Tests A/B avec Modèles Fine-tunés

Comparer les performances du modèle de base vs modèle fine-tuné :

import { FineTuningOrchestrator, FeedbackCollector } from '@orka-js/finetuning';
import { OpenAIAdapter } from '@orka-js/openai';
 
interface ABTestResult {
modelType: 'base' | 'finetuned';
response: string;
rating?: number;
latency: number;
}
 
class ABTestManager {
private baseModel: OpenAIAdapter;
private fineTunedModel: OpenAIAdapter;
private collector: FeedbackCollector;
private results: ABTestResult[] = [];
 
constructor(fineTunedModelName: string) {
this.baseModel = new OpenAIAdapter({
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-3.5-turbo'
});
 
this.fineTunedModel = new OpenAIAdapter({
apiKey: process.env.OPENAI_API_KEY!,
model: fineTunedModelName
});
 
this.collector = new FeedbackCollector({
storage: 'file',
filePath: './ab-test-results.jsonl'
});
}
 
async runTest(userInput: string): Promise<void> {
// Sélectionner aléatoirement le modèle à utiliser (50/50)
const useFineTuned = Math.random() > 0.5;
const model = useFineTuned ? this.fineTunedModel: this.baseModel;
const modelType = useFineTuned ? 'finetuned' : 'base';
 
const startTime = Date.now();
const response = await model.generate({
messages: [{ role: 'user', content: userInput }]
});
const latency = Date.now() - startTime;
 
const result: ABTestResult = {
modelType,
response: response.content,
latency
};
 
this.results.push(result);
 
// Afficher la réponse à l'utilisateur et collecter le feedback
console.log(`Modèle : ${modelType}`);
console.log(`Réponse : ${response.content}`);
console.log(`Latence : ${latency}ms`);
 
return;
}
 
async recordFeedback(index: number, rating: number): Promise<void> {
const result = this.results[index];
if (!result) return;
 
result.rating = rating;
 
await this.collector.addFeedback({
input: 'Requête utilisateur',
output: result.response,
rating,
metadata: {
modelType: result.modelType,
latency: result.latency,
timestamp: new Date().toISOString()
}
});
}
 
async getAnalytics() {
const baseResults = this.results.filter(r => r.modelType === 'base' && r.rating);
const fineTunedResults = this.results.filter(r => r.modelType === 'finetuned' && r.rating);
 
const avgRatingBase = baseResults.reduce((sum, r) => sum + (r.rating || 0), 0) / baseResults.length;
const avgRatingFineTuned = fineTunedResults.reduce((sum, r) => sum + (r.rating || 0), 0) / fineTunedResults.length;
 
const avgLatencyBase = baseResults.reduce((sum, r) => sum + r.latency, 0) / baseResults.length;
const avgLatencyFineTuned = fineTunedResults.reduce((sum, r) => sum + r.latency, 0) / fineTunedResults.length;
 
return {
base: {
count: baseResults.length,
avgRating: avgRatingBase.toFixed(2),
avgLatency: avgLatencyBase.toFixed(0) + 'ms'
},
fineTuned: {
count: fineTunedResults.length,
avgRating: avgRatingFineTuned.toFixed(2),
avgLatency: avgLatencyFineTuned.toFixed(0) + 'ms'
},
improvement: {
rating: ((avgRatingFineTuned - avgRatingBase) / avgRatingBase * 100).toFixed(1) + '%',
latency: ((avgLatencyBase - avgLatencyFineTuned) / avgLatencyBase * 100).toFixed(1) + '%'
}
};
}
}
 
// Utilisation
async function runABTest() {
const abTest = new ABTestManager('ft:gpt-3.5-turbo:customer-support-v1');
 
// Exécuter 100 tests
const queries = [
"Comment réinitialiser mon mot de passe ?",
"Quels sont vos horaires d'ouverture ?",
"Comment annuler mon abonnement ?",
// ... plus de requêtes
];
 
for (let i = 0; i < queries.length; i++) {
await abTest.runTest(queries[i]);
 
// Simuler une note utilisateur (en production, obtenir de vrais retours)
const rating = Math.floor(Math.random() * 3) + 7; // 7-10
await abTest.recordFeedback(i, rating);
}
 
// Obtenir les analytics
const analytics = await abTest.getAnalytics();
console.log('Résultats du test A/B:', analytics);
 
// Exemple de sortie :
// {
// base: { count: 52, avgRating: '7.8', avgLatency: '1250ms' },
// fineTuned: { count: 48, avgRating: '8.9', avgLatency: '980ms' },
// improvement: { rating: '+14.1%', latency: '+21.6%' }
// }
}
 
runABTest().catch(console.error);

Exemple 3 : Boucle d'Apprentissage Continu

Réentraîner automatiquement les modèles selon les nouveaux retours :

import { FeedbackCollector, DatasetValidator, FineTuningOrchestrator } from '@orka-js/finetuning';
 
class ContinuousLearningPipeline {
private collector: FeedbackCollector;
private validator: DatasetValidator;
private orchestrator: FineTuningOrchestrator;
private retrainThreshold = 1000; // Réentraîner après 1000 nouveaux retours
private minRating = 8; // Utiliser uniquement les retours de haute qualité
 
constructor() {
this.collector = new FeedbackCollector({
storage: 'database',
connectionString: process.env.DATABASE_URL!
});
 
this.validator = new DatasetValidator();
 
this.orchestrator = new FineTuningOrchestrator({
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY!
});
}
 
async checkAndRetrain(): Promise<void> {
const feedbackCount = await this.collector.getCount();
const lastRetrainCount = await this.getLastRetrainCount();
const newFeedbackCount = feedbackCount - lastRetrainCount;
 
console.log(`Nouveaux retours depuis le dernier entraînement : ${newFeedbackCount}`);
 
if (newFeedbackCount >= this.retrainThreshold) {
console.log('Seuil atteint. Démarrage du réentraînement...');
await this.retrain();
}
}
 
private async retrain(): Promise<void> {
// Exporter les retours de haute qualité
const dataset = await this.collector.exportDataset({
format: 'openai',
minRating: this.minRating,
includeCorrections: true,
since: await this.getLastRetrainDate()
});
 
console.log(`${dataset.length} nouveaux exemples d'entraînement exportés`);
 
// Valider
const validation = await this.validator.validate(dataset, {
provider: 'openai',
checkDuplicates: true,
checkBalance: true
});
 
if (!validation.valid) {
console.error('Échec de la validation du dataset:', validation.errors);
await this.notifyTeam('Échec de la validation du dataset', validation.errors);
return;
}
 
// Estimer le coût
const costEstimate = await this.orchestrator.estimateCost({
dataset,
baseModel: 'gpt-3.5-turbo',
epochs: 3
});
 
console.log(`Coût estimé : ${costEstimate.estimatedCost.toFixed(2)}$`);
 
// Auto-approuver si le coût est en dessous du seuil
if (costEstimate.estimatedCost > 100) {
await this.notifyTeam('Approbation manuelle nécessaire', {
cost: costEstimate.estimatedCost,
examples: dataset.length
});
return;
}
 
// Créer le job
const job = await this.orchestrator.createJob({
trainingData: dataset,
baseModel: 'gpt-3.5-turbo',
suffix: `v${await this.getNextVersion()}`,
hyperparameters: {
nEpochs: 3,
batchSize: 4
}
});
 
console.log(`Job de fine-tuning créé : ${job.id}`);
 
// Sauvegarder les métadonnées du réentraînement
await this.saveRetrainMetadata({
jobId: job.id,
feedbackCount: await this.collector.getCount(),
timestamp: new Date()
});
 
// Surveiller le job
await this.monitorAndDeploy(job.id);
}
 
private async monitorAndDeploy(jobId: string): Promise<void> {
const checkInterval = setInterval(async () => {
const jobs = await this.orchestrator.listJobs();
const job = jobs.find(j => j.id === jobId);
 
if (!job) {
clearInterval(checkInterval);
return;
}
 
if (job.status === 'succeeded') {
console.log(`Job ${jobId} réussi !`);
console.log(`Nouveau modèle : ${job.fineTunedModel}`);
 
clearInterval(checkInterval);
 
// Auto-déployer en staging
await this.deployToStaging(job.fineTunedModel!);
 
// Notifier l'équipe
await this.notifyTeam('Nouveau modèle déployé en staging', {
model: job.fineTunedModel,
jobId: job.id
});
} else if (job.status === 'failed') {
console.error(`Job ${jobId} échoué :`, job.error);
clearInterval(checkInterval);
await this.notifyTeam('Échec du fine-tuning', { jobId, error: job.error });
}
}, 60000); // Vérifier chaque minute
}
 
private async deployToStaging(modelName: string): Promise<void> {
// Mettre à jour la variable d'environnement ou la config
console.log(`Déploiement de ${modelName} en staging...`);
// L'implémentation dépend de votre configuration de déploiement
}
 
private async notifyTeam(subject: string, data: any): Promise<void> {
// Envoyer une notification (Slack, email, etc.)
console.log(`[NOTIFICATION] ${subject}`, data);
}
 
private async getLastRetrainCount(): Promise<number> {
// Obtenir depuis la base de données
return 0;
}
 
private async getLastRetrainDate(): Promise<Date> {
// Obtenir depuis la base de données
return new Date(0);
}
 
private async getNextVersion(): Promise<number> {
// Obtenir depuis la base de données
return 1;
}
 
private async saveRetrainMetadata(metadata: any): Promise<void> {
// Sauvegarder en base de données
console.log('Sauvegarde des métadonnées de réentraînement:', metadata);
}
}
 
// Exécuter comme un cron job (ex: quotidien)
async function main() {
const pipeline = new ContinuousLearningPipeline();
await pipeline.checkAndRetrain();
}
 
// Ou exécuter en continu
async function runContinuously() {
const pipeline = new ContinuousLearningPipeline();
 
setInterval(async () => {
await pipeline.checkAndRetrain();
}, 24 * 60 * 60 * 1000); // Vérifier quotidiennement
}
 
main().catch(console.error);