@imenam/unified-llm-client 1.2.0
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.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/adapters/anthropic/AnthropicLogAdapter.d.ts +6 -0
- package/dist/adapters/anthropic/AnthropicLogAdapter.d.ts.map +1 -0
- package/dist/adapters/anthropic/AnthropicLogAdapter.js +41 -0
- package/dist/adapters/anthropic/AnthropicOutputAdapter.d.ts +6 -0
- package/dist/adapters/anthropic/AnthropicOutputAdapter.d.ts.map +1 -0
- package/dist/adapters/anthropic/AnthropicOutputAdapter.js +41 -0
- package/dist/adapters/anthropic/AnthropicPromptAdapter.d.ts +12 -0
- package/dist/adapters/anthropic/AnthropicPromptAdapter.d.ts.map +1 -0
- package/dist/adapters/anthropic/AnthropicPromptAdapter.js +249 -0
- package/dist/adapters/anthropic/AnthropicToolAdapter.d.ts +6 -0
- package/dist/adapters/anthropic/AnthropicToolAdapter.d.ts.map +1 -0
- package/dist/adapters/anthropic/AnthropicToolAdapter.js +20 -0
- package/dist/adapters/gemini/GeminiAudioAdapter.d.ts +40 -0
- package/dist/adapters/gemini/GeminiAudioAdapter.d.ts.map +1 -0
- package/dist/adapters/gemini/GeminiAudioAdapter.js +273 -0
- package/dist/adapters/gemini/GeminiLogAdapter.d.ts +6 -0
- package/dist/adapters/gemini/GeminiLogAdapter.d.ts.map +1 -0
- package/dist/adapters/gemini/GeminiLogAdapter.js +43 -0
- package/dist/adapters/gemini/GeminiOutputAdapter.d.ts +6 -0
- package/dist/adapters/gemini/GeminiOutputAdapter.d.ts.map +1 -0
- package/dist/adapters/gemini/GeminiOutputAdapter.js +38 -0
- package/dist/adapters/gemini/GeminiPromptAdapter.d.ts +26 -0
- package/dist/adapters/gemini/GeminiPromptAdapter.d.ts.map +1 -0
- package/dist/adapters/gemini/GeminiPromptAdapter.js +334 -0
- package/dist/adapters/gemini/GeminiToolAdapter.d.ts +8 -0
- package/dist/adapters/gemini/GeminiToolAdapter.d.ts.map +1 -0
- package/dist/adapters/gemini/GeminiToolAdapter.js +42 -0
- package/dist/adapters/gemini/types.d.ts +14 -0
- package/dist/adapters/gemini/types.d.ts.map +1 -0
- package/dist/adapters/gemini/types.js +2 -0
- package/dist/adapters/openai/OpenAIOutputAdapter.d.ts +6 -0
- package/dist/adapters/openai/OpenAIOutputAdapter.d.ts.map +1 -0
- package/dist/adapters/openai/OpenAIOutputAdapter.js +18 -0
- package/dist/adapters/openai/OpenAIPromptAdapter.d.ts +13 -0
- package/dist/adapters/openai/OpenAIPromptAdapter.d.ts.map +1 -0
- package/dist/adapters/openai/OpenAIPromptAdapter.js +215 -0
- package/dist/adapters/openai/OpenAiLogAdapter.d.ts +6 -0
- package/dist/adapters/openai/OpenAiLogAdapter.d.ts.map +1 -0
- package/dist/adapters/openai/OpenAiLogAdapter.js +37 -0
- package/dist/adapters/openai/OpenAiToolAdapter.d.ts +6 -0
- package/dist/adapters/openai/OpenAiToolAdapter.d.ts.map +1 -0
- package/dist/adapters/openai/OpenAiToolAdapter.js +20 -0
- package/dist/adapters/qwen/QwenLogAdapter.d.ts +7 -0
- package/dist/adapters/qwen/QwenLogAdapter.d.ts.map +1 -0
- package/dist/adapters/qwen/QwenLogAdapter.js +49 -0
- package/dist/adapters/qwen/QwenOutputAdapter.d.ts +6 -0
- package/dist/adapters/qwen/QwenOutputAdapter.d.ts.map +1 -0
- package/dist/adapters/qwen/QwenOutputAdapter.js +9 -0
- package/dist/adapters/qwen/QwenPromptAdapter.d.ts +13 -0
- package/dist/adapters/qwen/QwenPromptAdapter.d.ts.map +1 -0
- package/dist/adapters/qwen/QwenPromptAdapter.js +313 -0
- package/dist/adapters/qwen/QwenToolAdapter.d.ts +6 -0
- package/dist/adapters/qwen/QwenToolAdapter.d.ts.map +1 -0
- package/dist/adapters/qwen/QwenToolAdapter.js +20 -0
- package/dist/common/types.d.ts +106 -0
- package/dist/common/types.d.ts.map +1 -0
- package/dist/common/types.js +2 -0
- package/dist/core/LLMAdapter.d.ts +74 -0
- package/dist/core/LLMAdapter.d.ts.map +1 -0
- package/dist/core/LLMAdapter.js +70 -0
- package/dist/core/Models.d.ts +16 -0
- package/dist/core/Models.d.ts.map +1 -0
- package/dist/core/Models.js +118 -0
- package/dist/core/UnifiedLLMClient.d.ts +68 -0
- package/dist/core/UnifiedLLMClient.d.ts.map +1 -0
- package/dist/core/UnifiedLLMClient.js +185 -0
- package/dist/examples/anthropic-example.d.ts +2 -0
- package/dist/examples/anthropic-example.d.ts.map +1 -0
- package/dist/examples/anthropic-example.js +243 -0
- package/dist/examples/audio-example.d.ts +14 -0
- package/dist/examples/audio-example.d.ts.map +1 -0
- package/dist/examples/audio-example.js +204 -0
- package/dist/examples/example-logging.d.ts +2 -0
- package/dist/examples/example-logging.d.ts.map +1 -0
- package/dist/examples/example-logging.js +53 -0
- package/dist/examples/example.d.ts +2 -0
- package/dist/examples/example.d.ts.map +1 -0
- package/dist/examples/example.js +373 -0
- package/dist/examples/qwen-vl-example.d.ts +26 -0
- package/dist/examples/qwen-vl-example.d.ts.map +1 -0
- package/dist/examples/qwen-vl-example.js +314 -0
- package/dist/examples/test-multiturn-tools.d.ts +2 -0
- package/dist/examples/test-multiturn-tools.d.ts.map +1 -0
- package/dist/examples/test-multiturn-tools.js +139 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/utils/audioUtils.d.ts +41 -0
- package/dist/utils/audioUtils.d.ts.map +1 -0
- package/dist/utils/audioUtils.js +185 -0
- package/package.json +64 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.qwenVLImageAnalysisExample = qwenVLImageAnalysisExample;
|
|
49
|
+
exports.qwenVLBoundingBoxesExample = qwenVLBoundingBoxesExample;
|
|
50
|
+
exports.qwenVLPdfAnalysisExample = qwenVLPdfAnalysisExample;
|
|
51
|
+
exports.qwenVLStreamingExample = qwenVLStreamingExample;
|
|
52
|
+
exports.runAllQwenVLExamples = runAllQwenVLExamples;
|
|
53
|
+
exports.parseQwenJsonResponse = parseQwenJsonResponse;
|
|
54
|
+
const UnifiedLLMClient_1 = require("../core/UnifiedLLMClient");
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const sharp_1 = __importDefault(require("sharp"));
|
|
57
|
+
const Models_1 = require("../core/Models");
|
|
58
|
+
/**
|
|
59
|
+
* Redimensionne une image pour que le plus long côté fasse au maximum maxSize pixels
|
|
60
|
+
* @param imagePath Chemin vers l'image source
|
|
61
|
+
* @param maxSize Taille maximale du plus long côté (défaut: 1000)
|
|
62
|
+
* @returns Buffer de l'image redimensionnée
|
|
63
|
+
*/
|
|
64
|
+
function resizeImageForVision(imagePath_1) {
|
|
65
|
+
return __awaiter(this, arguments, void 0, function* (imagePath, maxSize = 1000) {
|
|
66
|
+
try {
|
|
67
|
+
// Obtenir les dimensions originales
|
|
68
|
+
const metadata = yield (0, sharp_1.default)(imagePath).metadata();
|
|
69
|
+
const { width = 0, height = 0 } = metadata;
|
|
70
|
+
console.log(`📏 Image originale: ${width}x${height}`);
|
|
71
|
+
// Calculer les nouvelles dimensions
|
|
72
|
+
let newWidth;
|
|
73
|
+
let newHeight;
|
|
74
|
+
if (width > height) {
|
|
75
|
+
// Image paysage
|
|
76
|
+
newWidth = maxSize;
|
|
77
|
+
newHeight = Math.round((height * maxSize) / width);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Image portrait ou carrée
|
|
81
|
+
newHeight = maxSize;
|
|
82
|
+
newWidth = Math.round((width * maxSize) / height);
|
|
83
|
+
}
|
|
84
|
+
console.log(`🔄 Redimensionnement vers: ${newWidth}x${newHeight}`);
|
|
85
|
+
// Redimensionner l'image
|
|
86
|
+
const resizedBuffer = yield (0, sharp_1.default)(imagePath)
|
|
87
|
+
.resize(newWidth, newHeight, {
|
|
88
|
+
fit: 'inside',
|
|
89
|
+
withoutEnlargement: true // Ne pas agrandir si l'image est plus petite
|
|
90
|
+
})
|
|
91
|
+
.jpeg({ quality: 90 }) // Convertir en JPEG avec bonne qualité
|
|
92
|
+
.toBuffer();
|
|
93
|
+
console.log(`✅ Image redimensionnée: ${resizedBuffer.length} bytes`);
|
|
94
|
+
return resizedBuffer;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.warn(`⚠️ Impossible de redimensionner ${imagePath}, utilisation de l'original:`, error);
|
|
98
|
+
// En cas d'erreur, retourner l'image originale
|
|
99
|
+
return fs.readFileSync(imagePath);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Exemple 1: Analyse d'une image simple avec Qwen VL
|
|
105
|
+
*/
|
|
106
|
+
function qwenVLImageAnalysisExample() {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
try {
|
|
109
|
+
console.log("🔍 Exemple 1: Analyse d'une image avec Qwen VL...");
|
|
110
|
+
// Initialiser le client Qwen
|
|
111
|
+
const client = new UnifiedLLMClient_1.UnifiedLLMClient(Models_1.MODELS.QWEN_VL_3);
|
|
112
|
+
// Redimensionner l'image avant l'envoi
|
|
113
|
+
const resizedImageBuffer = yield resizeImageForVision("./media/image.png", 1000);
|
|
114
|
+
const base64Image = resizedImageBuffer.toString('base64');
|
|
115
|
+
const response = yield client.chat({
|
|
116
|
+
message: {
|
|
117
|
+
role: "user",
|
|
118
|
+
content: [
|
|
119
|
+
{ type: "text", text: "Décris cette image en détail. Qu'est-ce que tu vois ?" },
|
|
120
|
+
{
|
|
121
|
+
type: "image_url",
|
|
122
|
+
image_url: {
|
|
123
|
+
url: `data:image/jpeg;base64,${base64Image}`
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
console.log("📝 Réponse de Qwen VL:");
|
|
130
|
+
console.log(response.content);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error("❌ Erreur lors de l'analyse d'image:", error);
|
|
134
|
+
console.log("\n🔧 Assurez-vous que ALIBABA_CLOUD_API_KEY est défini dans votre fichier .env");
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Exemple 2: Extraction de texte structuré (bounding boxes) avec Qwen VL
|
|
140
|
+
*/
|
|
141
|
+
function qwenVLBoundingBoxesExample() {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
try {
|
|
144
|
+
console.log("📦 Exemple 2: Extraction de bounding boxes...");
|
|
145
|
+
// Initialiser le client Qwen
|
|
146
|
+
const client = new UnifiedLLMClient_1.UnifiedLLMClient(Models_1.MODELS.QWEN_VL_3);
|
|
147
|
+
const expectedJsonFormat = `{
|
|
148
|
+
"data": [
|
|
149
|
+
{
|
|
150
|
+
"bbox_2d": [x1, y1, x2, y2],
|
|
151
|
+
"text_content": "contenu du texte"
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}`;
|
|
155
|
+
// Redimensionner l'image pour l'extraction de bounding boxes
|
|
156
|
+
const resizedImageBuffer = yield resizeImageForVision("./media/Gaspard_5_with_lines.png", 1000);
|
|
157
|
+
const base64Image = resizedImageBuffer.toString('base64');
|
|
158
|
+
const response = yield client.chat({
|
|
159
|
+
message: {
|
|
160
|
+
role: "user",
|
|
161
|
+
content: [
|
|
162
|
+
{ type: "text", text: `Identifie tous les éléments de texte sur cette image et retourne les coordonnées des bounding boxes au format JSON: ${expectedJsonFormat}. Inclus le texte contenu dans chaque boîte.` },
|
|
163
|
+
{
|
|
164
|
+
type: "image_url",
|
|
165
|
+
image_url: {
|
|
166
|
+
url: `data:image/jpeg;base64,${base64Image}`
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
console.log("📝 Données extraites:");
|
|
173
|
+
console.log(response.content);
|
|
174
|
+
// Sauvegarder la réponse
|
|
175
|
+
if (response.content) {
|
|
176
|
+
fs.writeFileSync("qwen_response.json", JSON.stringify({
|
|
177
|
+
response: response.content,
|
|
178
|
+
timestamp: new Date().toISOString()
|
|
179
|
+
}, null, 2));
|
|
180
|
+
console.log("✅ Réponse sauvegardée dans qwen_response.json");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
console.error("❌ Erreur lors de l'extraction de bounding boxes:", error);
|
|
185
|
+
console.log("\n🔧 Assurez-vous que ALIBABA_CLOUD_API_KEY est défini dans votre fichier .env");
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Exemple 3: Analyse de document PDF avec Qwen VL
|
|
191
|
+
*/
|
|
192
|
+
function qwenVLPdfAnalysisExample() {
|
|
193
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
194
|
+
try {
|
|
195
|
+
console.log("📄 Exemple 3: Analyse d'un document PDF...");
|
|
196
|
+
// Initialiser le client Qwen
|
|
197
|
+
const client = new UnifiedLLMClient_1.UnifiedLLMClient(Models_1.MODELS.QWEN_VL_3);
|
|
198
|
+
const response = yield client.chat({
|
|
199
|
+
message: {
|
|
200
|
+
role: "user",
|
|
201
|
+
content: "Analyse ce document PDF. Quel est son contenu principal ? Y a-t-il des tableaux ou des graphiques ?",
|
|
202
|
+
attachedFile: "./media/subject.pdf"
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
console.log("📝 Analyse du PDF:");
|
|
206
|
+
console.log(response.content);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
console.error("❌ Erreur lors de l'analyse du PDF:", error);
|
|
210
|
+
console.log("\n🔧 Assurez-vous que ALIBABA_CLOUD_API_KEY est défini dans votre fichier .env");
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Exemple 4: Analyse d'image avec streaming pour voir la génération en temps réel
|
|
216
|
+
*/
|
|
217
|
+
function qwenVLStreamingExample() {
|
|
218
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
219
|
+
var _a;
|
|
220
|
+
try {
|
|
221
|
+
console.log("🌊 Exemple 4: Analyse d'image avec streaming...");
|
|
222
|
+
// Initialiser le client Qwen
|
|
223
|
+
const client = new UnifiedLLMClient_1.UnifiedLLMClient(Models_1.MODELS.QWEN_VL_3);
|
|
224
|
+
// Redimensionner l'image avant l'envoi
|
|
225
|
+
const resizedImageBuffer = yield resizeImageForVision("./media/image.png", 1000);
|
|
226
|
+
const base64Image = resizedImageBuffer.toString('base64');
|
|
227
|
+
// Logging des tailles d'image
|
|
228
|
+
const originalSizeBytes = resizedImageBuffer.length;
|
|
229
|
+
const base64SizeChars = base64Image.length;
|
|
230
|
+
const base64SizeBytes = Math.ceil(base64SizeChars * 6 / 8); // Conversion base64 vers bytes
|
|
231
|
+
console.log(`📏 Taille de l'image source: ${originalSizeBytes} bytes (${(originalSizeBytes / 1024).toFixed(2)} KB)`);
|
|
232
|
+
console.log(`📏 Taille après encodage base64: ${base64SizeChars} caractères (${(base64SizeBytes / 1024).toFixed(2)} KB)`);
|
|
233
|
+
console.log(`📏 Ratio d'encodage: ${(base64SizeBytes / originalSizeBytes).toFixed(2)}x`);
|
|
234
|
+
console.log("📡 Démarrage du streaming...");
|
|
235
|
+
let startTime = Date.now();
|
|
236
|
+
let firstChunkReceived = false;
|
|
237
|
+
const response = yield client.chat({
|
|
238
|
+
message: {
|
|
239
|
+
role: "user",
|
|
240
|
+
content: [
|
|
241
|
+
{ type: "text", text: "Donne moi le texte de la première ligne de la page." },
|
|
242
|
+
{
|
|
243
|
+
type: "image_url",
|
|
244
|
+
image_url: {
|
|
245
|
+
url: `data:image/jpeg;base64,${base64Image}`
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
stream: true,
|
|
251
|
+
onChunk: (chunk) => {
|
|
252
|
+
if (!firstChunkReceived) {
|
|
253
|
+
firstChunkReceived = true;
|
|
254
|
+
let elapsedTime = Date.now() - startTime;
|
|
255
|
+
console.log(`Temps écoulé pour recevoir le premier chunk: ${elapsedTime}ms`);
|
|
256
|
+
}
|
|
257
|
+
if (chunk.contentChunk) {
|
|
258
|
+
process.stdout.write(chunk.contentChunk); // Écrire directement sur stdout pour voir le streaming en temps réel
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
onFinalResponse: (finalResponse) => {
|
|
262
|
+
console.log("\n\n✅ Streaming terminé!");
|
|
263
|
+
console.log("📝 Réponse complète:");
|
|
264
|
+
console.log(finalResponse.content);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
console.log("\n📊 Statistiques du streaming:");
|
|
268
|
+
console.log(`Longueur totale de la réponse: ${((_a = response.content) === null || _a === void 0 ? void 0 : _a.length) || 0} caractères`);
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
console.error("❌ Erreur lors du streaming:", error);
|
|
272
|
+
console.log("\n🔧 Assurez-vous que ALIBABA_CLOUD_API_KEY est défini dans votre fichier .env");
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Exécute tous les exemples Qwen VL
|
|
278
|
+
*/
|
|
279
|
+
function runAllQwenVLExamples() {
|
|
280
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
281
|
+
console.log("🚀 Démarrage de tous les exemples Qwen VL...\n");
|
|
282
|
+
yield qwenVLImageAnalysisExample();
|
|
283
|
+
console.log("\n" + "=".repeat(50) + "\n");
|
|
284
|
+
yield qwenVLBoundingBoxesExample();
|
|
285
|
+
console.log("\n" + "=".repeat(50) + "\n");
|
|
286
|
+
yield qwenVLPdfAnalysisExample();
|
|
287
|
+
console.log("\n" + "=".repeat(50) + "\n");
|
|
288
|
+
yield qwenVLStreamingExample();
|
|
289
|
+
console.log("\n✅ Tous les exemples terminés!");
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Fonction utilitaire pour parser les réponses JSON de Qwen
|
|
294
|
+
*/
|
|
295
|
+
function parseQwenJsonResponse(responseContent) {
|
|
296
|
+
if (!responseContent)
|
|
297
|
+
return null;
|
|
298
|
+
try {
|
|
299
|
+
// Nettoyer la réponse (enlever les balises markdown si présentes)
|
|
300
|
+
let cleanResponse = responseContent.replace(/```json\s*/g, '').replace(/```\s*$/g, '');
|
|
301
|
+
// Corriger les clés non-quoted si nécessaire
|
|
302
|
+
cleanResponse = cleanResponse.replace(/(\{|\,)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\:/g, '$1"$2":');
|
|
303
|
+
return JSON.parse(cleanResponse);
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
console.warn("⚠️ Impossible de parser la réponse JSON:", error);
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Exécuter tous les exemples si ce fichier est appelé directement
|
|
311
|
+
if (require.main === module) {
|
|
312
|
+
// runAllQwenVLExamples().catch(console.error);
|
|
313
|
+
qwenVLStreamingExample().catch(console.error);
|
|
314
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-multiturn-tools.d.ts","sourceRoot":"","sources":["../../src/examples/test-multiturn-tools.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const __1 = require("..");
|
|
13
|
+
// ─── Changer ici pour tester un autre provider ───────────────────────────────
|
|
14
|
+
// const MODEL = MODELS.CLAUDE_SONNET_4_6;
|
|
15
|
+
const MODEL = __1.MODELS.GEMINI_3_FLASH;
|
|
16
|
+
// const MODEL = MODELS.GPT_4O_MINI;
|
|
17
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
const tools = [
|
|
19
|
+
{
|
|
20
|
+
name: "get_weather",
|
|
21
|
+
description: "Retourne la météo actuelle pour une ville donnée.",
|
|
22
|
+
parameters: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
city: { type: "string", description: "Nom de la ville" },
|
|
26
|
+
},
|
|
27
|
+
required: ["city"],
|
|
28
|
+
},
|
|
29
|
+
executor: (_a) => __awaiter(void 0, [_a], void 0, function* ({ city }) { return `Météo à ${city} : 22°C, ensoleillé.`; }),
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
function pass(label) { console.log(` ✓ ${label}`); }
|
|
33
|
+
function fail(label, detail) { console.error(` ✗ ${label}`, detail !== null && detail !== void 0 ? detail : ""); }
|
|
34
|
+
// ─── Test 1 : tool call simple ────────────────────────────────────────────────
|
|
35
|
+
function testSingleTurnToolCall() {
|
|
36
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
var _a;
|
|
38
|
+
console.log("\n[Test 1] Single-turn tool call");
|
|
39
|
+
const client = new __1.UnifiedLLMClient(MODEL);
|
|
40
|
+
const response = yield client.chat({
|
|
41
|
+
message: "Quel temps fait-il à Lyon ?",
|
|
42
|
+
tools,
|
|
43
|
+
toolChoice: "auto",
|
|
44
|
+
});
|
|
45
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
46
|
+
pass("toolCalls présent dans la réponse");
|
|
47
|
+
const tc = response.toolCalls[0];
|
|
48
|
+
if (tc.name === "get_weather")
|
|
49
|
+
pass(`outil correct : ${tc.name}`);
|
|
50
|
+
else
|
|
51
|
+
fail("nom d'outil inattendu", tc.name);
|
|
52
|
+
if ((_a = tc.arguments) === null || _a === void 0 ? void 0 : _a.city)
|
|
53
|
+
pass(`argument city présent : ${tc.arguments.city}`);
|
|
54
|
+
else
|
|
55
|
+
fail("argument city manquant", tc.arguments);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
fail("aucun toolCall reçu — le modèle a répondu en texte", response.content);
|
|
59
|
+
}
|
|
60
|
+
return response;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// ─── Test 2 : conversation multi-tour avec injection de résultat ──────────────
|
|
64
|
+
function testMultiTurnToolCall() {
|
|
65
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
var _a;
|
|
67
|
+
console.log("\n[Test 2] Multi-turn tool call");
|
|
68
|
+
const client = new __1.UnifiedLLMClient(MODEL);
|
|
69
|
+
const history = [
|
|
70
|
+
{ role: "user", content: "Quel temps fait-il à Bordeaux ?" },
|
|
71
|
+
];
|
|
72
|
+
// Tour 1 — le modèle appelle l'outil
|
|
73
|
+
const r1 = yield client.chat({ message: history, tools, toolChoice: "auto" });
|
|
74
|
+
if (!r1.toolCalls || r1.toolCalls.length === 0) {
|
|
75
|
+
fail("Tour 1 : aucun toolCall reçu", r1.content);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
pass("Tour 1 : toolCall reçu");
|
|
79
|
+
const tc = r1.toolCalls[0];
|
|
80
|
+
// Injection du message assistant qui déclare le tool call
|
|
81
|
+
history.push({
|
|
82
|
+
role: "assistant",
|
|
83
|
+
content: r1.content,
|
|
84
|
+
tool_calls: r1.toolCalls,
|
|
85
|
+
});
|
|
86
|
+
// Exécution de l'outil
|
|
87
|
+
const toolResult = yield tools[0].executor(tc.arguments);
|
|
88
|
+
// Injection du résultat
|
|
89
|
+
// Note : pour Gemini, tool_call_id doit être le nom de la fonction (tc.name)
|
|
90
|
+
history.push({
|
|
91
|
+
role: "tool",
|
|
92
|
+
tool_call_id: (_a = tc.id) !== null && _a !== void 0 ? _a : tc.name,
|
|
93
|
+
content: toolResult,
|
|
94
|
+
});
|
|
95
|
+
pass(`Tour 1 : historique injecté (${history.length} messages)`);
|
|
96
|
+
// Tour 2 — le modèle formule sa réponse finale avec le résultat
|
|
97
|
+
const r2 = yield client.chat({ message: history, tools });
|
|
98
|
+
if (r2.content && r2.content.trim().length > 0) {
|
|
99
|
+
pass("Tour 2 : réponse finale reçue");
|
|
100
|
+
console.log(` → "${r2.content.trim().slice(0, 120)}..."`);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
fail("Tour 2 : réponse vide ou absente", r2);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// ─── Test 3 : toolChoice forcé (structured output) ────────────────────────────
|
|
108
|
+
function testForcedToolChoice() {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
console.log("\n[Test 3] toolChoice forcé");
|
|
111
|
+
const client = new __1.UnifiedLLMClient(MODEL);
|
|
112
|
+
const response = yield client.chat({
|
|
113
|
+
message: "Donne-moi la météo de Nantes",
|
|
114
|
+
tools,
|
|
115
|
+
toolChoice: "get_weather",
|
|
116
|
+
});
|
|
117
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
118
|
+
pass(`outil forcé appelé : ${response.toolCalls[0].name}`);
|
|
119
|
+
pass(`arguments : ${JSON.stringify(response.toolCalls[0].arguments)}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
fail("toolCall forcé non reçu", response.content);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// ─── Runner ───────────────────────────────────────────────────────────────────
|
|
127
|
+
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
128
|
+
console.log(`\n=== Tests multi-turn tool calls — provider : ${MODEL.provider} (${MODEL.modelName}) ===`);
|
|
129
|
+
try {
|
|
130
|
+
yield testSingleTurnToolCall();
|
|
131
|
+
yield testMultiTurnToolCall();
|
|
132
|
+
yield testForcedToolChoice();
|
|
133
|
+
console.log("\n✅ Tous les tests terminés.\n");
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
console.error("\n❌ Erreur inattendue :", err);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}))();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ChatMessage, ToolDefinition, ToolCall, ChatResponse, AudioGenerationOptions, AudioGenerationResponse, VoiceConfig, SpeakerConfig } from "./common/types";
|
|
2
|
+
import { PromptAdapter, PromptAdapterOptions, OutputAdapter, AudioAdapter } from "./core/LLMAdapter";
|
|
3
|
+
import { UnifiedLLMClient } from "./core/UnifiedLLMClient";
|
|
4
|
+
import { MODELS } from "./core/Models";
|
|
5
|
+
export { UnifiedLLMClient, MODELS };
|
|
6
|
+
export type { ChatMessage, ToolDefinition, ToolCall, ChatResponse, PromptAdapter, PromptAdapterOptions, OutputAdapter, AudioAdapter, AudioGenerationOptions, AudioGenerationResponse, VoiceConfig, SpeakerConfig };
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClK,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC;AACpC,YAAY,EACV,WAAW,EACX,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,WAAW,EACX,aAAa,EACd,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MODELS = exports.UnifiedLLMClient = void 0;
|
|
4
|
+
const UnifiedLLMClient_1 = require("./core/UnifiedLLMClient");
|
|
5
|
+
Object.defineProperty(exports, "UnifiedLLMClient", { enumerable: true, get: function () { return UnifiedLLMClient_1.UnifiedLLMClient; } });
|
|
6
|
+
const Models_1 = require("./core/Models");
|
|
7
|
+
Object.defineProperty(exports, "MODELS", { enumerable: true, get: function () { return Models_1.MODELS; } });
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface AudioParseOptions {
|
|
2
|
+
numChannels: number;
|
|
3
|
+
sampleRate: number;
|
|
4
|
+
bitsPerSample: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Génère un nom de fichier unique pour l'audio
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateUniqueAttachedFilename(prefix?: string, extension?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Valide et normalise un chemin de sortie
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateOutputPath(outputPath: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Sauvegarde des données audio dans un fichier
|
|
16
|
+
*/
|
|
17
|
+
export declare function saveAttachedFile(filePath: string, audioData: Buffer): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Parse le type MIME pour extraire les options audio
|
|
20
|
+
* Basé sur l'exemple google-tts-example.js
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseMimeType(mimeType: string): AudioParseOptions;
|
|
23
|
+
/**
|
|
24
|
+
* Crée un header WAV pour les données audio
|
|
25
|
+
* Basé sur l'exemple google-tts-example.js
|
|
26
|
+
*/
|
|
27
|
+
export declare function createWavHeader(dataLength: number, options: AudioParseOptions): Buffer;
|
|
28
|
+
/**
|
|
29
|
+
* Convertit des données audio brutes au format WAV
|
|
30
|
+
* Basé sur l'exemple google-tts-example.js
|
|
31
|
+
*/
|
|
32
|
+
export declare function convertToWav(rawData: string, mimeType: string): Buffer;
|
|
33
|
+
/**
|
|
34
|
+
* Nettoie les fichiers temporaires
|
|
35
|
+
*/
|
|
36
|
+
export declare function cleanupTempFiles(filePaths: string[]): void;
|
|
37
|
+
/**
|
|
38
|
+
* Détecte l'extension de fichier appropriée basée sur le type MIME
|
|
39
|
+
*/
|
|
40
|
+
export declare function getAttachedFileExtension(mimeType: string): string;
|
|
41
|
+
//# sourceMappingURL=audioUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audioUtils.d.ts","sourceRoot":"","sources":["../../src/utils/audioUtils.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,GAAE,MAAgB,EAAE,SAAS,GAAE,MAAc,GAAG,MAAM,CAI1G;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAU7D;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUzF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAyBjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAqBtF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKtE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAU1D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAejE"}
|