@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,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MODELS = void 0;
|
|
4
|
+
exports.MODELS = {
|
|
5
|
+
GEMINI_FLASH_2_5: {
|
|
6
|
+
provider: "gemini",
|
|
7
|
+
modelName: "gemini-2.5-flash",
|
|
8
|
+
costsPer1MTokens: {
|
|
9
|
+
prompt: 0.3,
|
|
10
|
+
prompt_cache: 0.075,
|
|
11
|
+
prompt_image: 0.0000774,
|
|
12
|
+
output: 2.5,
|
|
13
|
+
},
|
|
14
|
+
currency: "USD",
|
|
15
|
+
},
|
|
16
|
+
GEMINI_2_5_PRO: {
|
|
17
|
+
provider: "gemini",
|
|
18
|
+
modelName: "gemini-2.5-pro",
|
|
19
|
+
costsPer1MTokens: {
|
|
20
|
+
prompt: 1.25,
|
|
21
|
+
prompt_cache: 0.3125,
|
|
22
|
+
prompt_image: 0.0003225,
|
|
23
|
+
output: 10.0,
|
|
24
|
+
},
|
|
25
|
+
currency: "USD",
|
|
26
|
+
},
|
|
27
|
+
GPT_4O_MINI: {
|
|
28
|
+
provider: "openai",
|
|
29
|
+
modelName: "gpt-4o-mini",
|
|
30
|
+
costsPer1MTokens: {
|
|
31
|
+
prompt: 0.6,
|
|
32
|
+
prompt_cache: 0.15,
|
|
33
|
+
prompt_image: 0.0012,
|
|
34
|
+
output: 2.4,
|
|
35
|
+
},
|
|
36
|
+
currency: "USD",
|
|
37
|
+
},
|
|
38
|
+
GPT_5: {
|
|
39
|
+
provider: "openai",
|
|
40
|
+
modelName: "gpt-5.2-2025-12-11",
|
|
41
|
+
costsPer1MTokens: {
|
|
42
|
+
prompt: 1.75,
|
|
43
|
+
prompt_cache: 0.175,
|
|
44
|
+
output: 14,
|
|
45
|
+
},
|
|
46
|
+
currency: "USD",
|
|
47
|
+
},
|
|
48
|
+
QWEN_VL_3: {
|
|
49
|
+
provider: "qwen",
|
|
50
|
+
modelName: "qwen3-vl-235b-a22b-instruct",
|
|
51
|
+
costsPer1MTokens: {
|
|
52
|
+
prompt: 0.2,
|
|
53
|
+
prompt_cache: 0.04,
|
|
54
|
+
output: 1.6,
|
|
55
|
+
},
|
|
56
|
+
currency: "USD",
|
|
57
|
+
},
|
|
58
|
+
QWEN_VL_FLASH: {
|
|
59
|
+
provider: "qwen",
|
|
60
|
+
modelName: "qwen3-vl-flash",
|
|
61
|
+
costsPer1MTokens: {
|
|
62
|
+
prompt: 0.05,
|
|
63
|
+
prompt_cache: 0.01,
|
|
64
|
+
output: 0.4,
|
|
65
|
+
},
|
|
66
|
+
currency: "USD",
|
|
67
|
+
},
|
|
68
|
+
QWEN_VL_3_FLASH: {
|
|
69
|
+
provider: "qwen",
|
|
70
|
+
modelName: "qwen3-vl-flash",
|
|
71
|
+
costsPer1MTokens: {
|
|
72
|
+
prompt: 0.05,
|
|
73
|
+
prompt_cache: 0.01,
|
|
74
|
+
output: 0.4,
|
|
75
|
+
},
|
|
76
|
+
currency: "USD",
|
|
77
|
+
},
|
|
78
|
+
GEMINI_3_PRO: {
|
|
79
|
+
provider: "gemini",
|
|
80
|
+
modelName: "gemini-3-pro-preview",
|
|
81
|
+
costsPer1MTokens: {
|
|
82
|
+
prompt: 2,
|
|
83
|
+
prompt_cache: 0.2,
|
|
84
|
+
output: 12,
|
|
85
|
+
},
|
|
86
|
+
currency: "USD",
|
|
87
|
+
},
|
|
88
|
+
GEMINI_3_1_PRO: {
|
|
89
|
+
provider: "gemini",
|
|
90
|
+
modelName: "gemini-3.1-pro-preview",
|
|
91
|
+
costsPer1MTokens: {
|
|
92
|
+
prompt: 2,
|
|
93
|
+
prompt_cache: 0.2,
|
|
94
|
+
output: 12,
|
|
95
|
+
},
|
|
96
|
+
currency: "USD",
|
|
97
|
+
},
|
|
98
|
+
GEMINI_3_FLASH: {
|
|
99
|
+
provider: "gemini",
|
|
100
|
+
modelName: "gemini-3-flash-preview",
|
|
101
|
+
costsPer1MTokens: {
|
|
102
|
+
prompt: 0.5,
|
|
103
|
+
prompt_cache: 0.05,
|
|
104
|
+
prompt_audio: 1,
|
|
105
|
+
prompt_audio_cache: 0.1,
|
|
106
|
+
output: 3.0,
|
|
107
|
+
},
|
|
108
|
+
currency: "USD",
|
|
109
|
+
},
|
|
110
|
+
CLAUDE_SONNET_4_6: {
|
|
111
|
+
provider: "anthropic",
|
|
112
|
+
modelName: "claude-sonnet-4-6",
|
|
113
|
+
},
|
|
114
|
+
CLAUDE_OPUS_4_6: {
|
|
115
|
+
provider: "anthropic",
|
|
116
|
+
modelName: "claude-opus-4-6",
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ChatMessage, ChatResponse, ToolDefinition, AudioGenerationOptions, AudioGenerationResponse, UnifiedLog } from "../common/types";
|
|
2
|
+
import { PromptAdapterOptions, StreamedChatChunk, ChatOptionalConfig } from "./LLMAdapter";
|
|
3
|
+
import { Model } from "../common/types";
|
|
4
|
+
export type UnifiedLLMClientOptions = {
|
|
5
|
+
provider: "openai" | "gemini" | "qwen" | "anthropic";
|
|
6
|
+
adapterOptions?: PromptAdapterOptions;
|
|
7
|
+
logger?: (log: UnifiedLog) => any | Promise<any>;
|
|
8
|
+
};
|
|
9
|
+
export type ChatOptions = {
|
|
10
|
+
message: string | ChatMessage | ChatMessage[];
|
|
11
|
+
tools?: ToolDefinition[];
|
|
12
|
+
toolChoice?: "auto" | "none" | string;
|
|
13
|
+
onChunk?: (chunk: StreamedChatChunk) => void;
|
|
14
|
+
onFinalResponse?: (finalResponse: ChatResponse) => void;
|
|
15
|
+
onLog?: (log: UnifiedLog) => void;
|
|
16
|
+
stream?: boolean;
|
|
17
|
+
config?: ChatOptionalConfig;
|
|
18
|
+
};
|
|
19
|
+
export declare class UnifiedLLMClient {
|
|
20
|
+
private promptAdapter;
|
|
21
|
+
private outputAdapter;
|
|
22
|
+
private toolAdapter;
|
|
23
|
+
private audioAdapter?;
|
|
24
|
+
private rawMessages;
|
|
25
|
+
private model;
|
|
26
|
+
private logger?;
|
|
27
|
+
static setDefaultLogger(logger: (log: UnifiedLog) => any | Promise<any>): void;
|
|
28
|
+
constructor(model: Model, options?: {
|
|
29
|
+
logger?: (log: UnifiedLog) => any | Promise<any>;
|
|
30
|
+
});
|
|
31
|
+
private checkNotImplementedFeatures;
|
|
32
|
+
chat({ message, tools, toolChoice, onChunk, onFinalResponse, onLog, stream, config }: ChatOptions): Promise<ChatResponse>;
|
|
33
|
+
private handleLogging;
|
|
34
|
+
/**
|
|
35
|
+
* Vérifie si la génération d'audio est supportée pour le provider actuel
|
|
36
|
+
*/
|
|
37
|
+
isAudioSupported(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Génère de l'audio à partir de texte
|
|
40
|
+
*/
|
|
41
|
+
generateAudio(options: AudioGenerationOptions): Promise<AudioGenerationResponse>;
|
|
42
|
+
/**
|
|
43
|
+
* Génère de l'audio en streaming
|
|
44
|
+
*/
|
|
45
|
+
generateAudioStream(options: AudioGenerationOptions): AsyncIterable<Buffer>;
|
|
46
|
+
/**
|
|
47
|
+
* Récupère la liste des voix supportées
|
|
48
|
+
*/
|
|
49
|
+
getSupportedAudioVoices(): Promise<string[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Génère de l'audio simple avec une voix
|
|
52
|
+
*/
|
|
53
|
+
generateSingleVoiceAudio(text: string, voiceName: string, options?: {
|
|
54
|
+
outputPath?: string;
|
|
55
|
+
temperature?: number;
|
|
56
|
+
}): Promise<AudioGenerationResponse>;
|
|
57
|
+
/**
|
|
58
|
+
* Génère de l'audio multi-voix
|
|
59
|
+
*/
|
|
60
|
+
generateMultiVoiceAudio(text: string, speakers: Array<{
|
|
61
|
+
speaker: string;
|
|
62
|
+
voiceName: string;
|
|
63
|
+
}>, options?: {
|
|
64
|
+
outputPath?: string;
|
|
65
|
+
temperature?: number;
|
|
66
|
+
}): Promise<AudioGenerationResponse>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=UnifiedLLMClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnifiedLLMClient.d.ts","sourceRoot":"","sources":["../../src/core/UnifiedLLMClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
exports.UnifiedLLMClient = void 0;
|
|
13
|
+
const LLMAdapter_1 = require("./LLMAdapter");
|
|
14
|
+
class UnifiedLLMClient {
|
|
15
|
+
static setDefaultLogger(logger) {
|
|
16
|
+
global._unifiedLlmDefaultLogger = logger;
|
|
17
|
+
}
|
|
18
|
+
constructor(model, options) {
|
|
19
|
+
this.rawMessages = [];
|
|
20
|
+
this.model = model;
|
|
21
|
+
this.promptAdapter = LLMAdapter_1.PROVIDERS_CONFIG[model.provider].promptAdapter;
|
|
22
|
+
this.outputAdapter = LLMAdapter_1.PROVIDERS_CONFIG[model.provider].outputAdapter;
|
|
23
|
+
this.toolAdapter = LLMAdapter_1.PROVIDERS_CONFIG[model.provider].toolAdapter;
|
|
24
|
+
this.audioAdapter = LLMAdapter_1.PROVIDERS_CONFIG[model.provider].audioAdapter;
|
|
25
|
+
this.logger = (options === null || options === void 0 ? void 0 : options.logger) || global._unifiedLlmDefaultLogger;
|
|
26
|
+
}
|
|
27
|
+
checkNotImplementedFeatures(message) {
|
|
28
|
+
if (message.attachedFile && this.model.provider !== "gemini" && this.model.provider !== "qwen" && this.model.provider !== "anthropic" && this.model.provider !== "openai") {
|
|
29
|
+
throw new Error("Attached files are not supported for this provider");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
chat(_a) {
|
|
33
|
+
return __awaiter(this, arguments, void 0, function* ({ message, tools, toolChoice = "auto", onChunk, onFinalResponse, onLog, stream = false, config }) {
|
|
34
|
+
const startTime = Date.now();
|
|
35
|
+
const messageArray = Array.isArray(message)
|
|
36
|
+
? message
|
|
37
|
+
: [typeof message === "string" ? { role: "user", content: message } : message];
|
|
38
|
+
for (const m of messageArray)
|
|
39
|
+
this.checkNotImplementedFeatures(m);
|
|
40
|
+
const messages = yield Promise.all(messageArray.map(m => this.promptAdapter.formatMessage(m)));
|
|
41
|
+
let finalResponse;
|
|
42
|
+
if (stream) {
|
|
43
|
+
if (!this.promptAdapter.streamFromProvider) {
|
|
44
|
+
throw new Error("The current adapter does not support streaming.");
|
|
45
|
+
}
|
|
46
|
+
let aggregatedContent = "";
|
|
47
|
+
finalResponse = yield this.promptAdapter.streamFromProvider({
|
|
48
|
+
messages,
|
|
49
|
+
tools,
|
|
50
|
+
toolChoice,
|
|
51
|
+
model: this.model.modelName,
|
|
52
|
+
onChunk: (chunk) => {
|
|
53
|
+
aggregatedContent += chunk.contentChunk || "";
|
|
54
|
+
if (onChunk) {
|
|
55
|
+
onChunk(chunk);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
onFinalResponse: (resp) => {
|
|
59
|
+
resp.content = aggregatedContent;
|
|
60
|
+
if (onFinalResponse) {
|
|
61
|
+
onFinalResponse(resp);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
finalResponse.content = aggregatedContent;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const providerResponse = yield this.promptAdapter.sendToProvider(messages, tools, toolChoice, undefined, this.model.modelName, config);
|
|
69
|
+
finalResponse = this.outputAdapter.parse(providerResponse);
|
|
70
|
+
}
|
|
71
|
+
const latencyMs = Date.now() - startTime;
|
|
72
|
+
yield this.handleLogging(messageArray, finalResponse, latencyMs, config === null || config === void 0 ? void 0 : config.metadata, onLog);
|
|
73
|
+
return finalResponse;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
handleLogging(messages, response, latencyMs, metadata, onLog) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
const logAdapter = LLMAdapter_1.PROVIDERS_CONFIG[this.model.provider].logAdapter;
|
|
79
|
+
if (logAdapter) {
|
|
80
|
+
try {
|
|
81
|
+
const unifiedLog = logAdapter.toUnifiedLog({
|
|
82
|
+
model: this.model.modelName,
|
|
83
|
+
provider: this.model.provider,
|
|
84
|
+
messages,
|
|
85
|
+
response,
|
|
86
|
+
latencyMs,
|
|
87
|
+
metadata
|
|
88
|
+
});
|
|
89
|
+
response.requestId = unifiedLog.requestId;
|
|
90
|
+
if (onLog) {
|
|
91
|
+
onLog(unifiedLog);
|
|
92
|
+
}
|
|
93
|
+
if (this.logger) {
|
|
94
|
+
try {
|
|
95
|
+
const logResult = yield this.logger(unifiedLog);
|
|
96
|
+
if (logResult && typeof logResult === 'object' && 'id' in logResult) {
|
|
97
|
+
response.llmLogId = logResult.id;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.error("[Logger Error]:", err);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
console.error("[LogAdapter Error]:", err);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// ===== AUDIO GENERATION METHODS =====
|
|
112
|
+
/**
|
|
113
|
+
* Vérifie si la génération d'audio est supportée pour le provider actuel
|
|
114
|
+
*/
|
|
115
|
+
isAudioSupported() {
|
|
116
|
+
return this.audioAdapter !== undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Génère de l'audio à partir de texte
|
|
120
|
+
*/
|
|
121
|
+
generateAudio(options) {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
if (!this.audioAdapter) {
|
|
124
|
+
throw new Error(`La génération d'audio n'est pas supportée pour le provider ${this.model.provider}`);
|
|
125
|
+
}
|
|
126
|
+
return this.audioAdapter.generateAudio(options);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Génère de l'audio en streaming
|
|
131
|
+
*/
|
|
132
|
+
generateAudioStream(options) {
|
|
133
|
+
if (!this.audioAdapter) {
|
|
134
|
+
throw new Error(`Le streaming audio n'est pas supporté pour le provider ${this.model.provider}`);
|
|
135
|
+
}
|
|
136
|
+
return this.audioAdapter.generateAudioStream(options);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Récupère la liste des voix supportées
|
|
140
|
+
*/
|
|
141
|
+
getSupportedAudioVoices() {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
if (!this.audioAdapter) {
|
|
144
|
+
throw new Error(`La génération d'audio n'est pas supportée pour le provider ${this.model.provider}`);
|
|
145
|
+
}
|
|
146
|
+
return this.audioAdapter.getSupportedVoices();
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Génère de l'audio simple avec une voix
|
|
151
|
+
*/
|
|
152
|
+
generateSingleVoiceAudio(text, voiceName, options) {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
return this.generateAudio({
|
|
155
|
+
text,
|
|
156
|
+
outputPath: options === null || options === void 0 ? void 0 : options.outputPath,
|
|
157
|
+
voiceConfig: {
|
|
158
|
+
voiceName,
|
|
159
|
+
temperature: options === null || options === void 0 ? void 0 : options.temperature
|
|
160
|
+
},
|
|
161
|
+
multiSpeaker: false
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Génère de l'audio multi-voix
|
|
167
|
+
*/
|
|
168
|
+
generateMultiVoiceAudio(text, speakers, options) {
|
|
169
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
170
|
+
return this.generateAudio({
|
|
171
|
+
text,
|
|
172
|
+
outputPath: options === null || options === void 0 ? void 0 : options.outputPath,
|
|
173
|
+
multiSpeaker: true,
|
|
174
|
+
speakers: speakers.map(s => ({
|
|
175
|
+
speaker: s.speaker,
|
|
176
|
+
voiceConfig: {
|
|
177
|
+
voiceName: s.voiceName,
|
|
178
|
+
temperature: options === null || options === void 0 ? void 0 : options.temperature
|
|
179
|
+
}
|
|
180
|
+
}))
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.UnifiedLLMClient = UnifiedLLMClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-example.d.ts","sourceRoot":"","sources":["../../src/examples/anthropic-example.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,243 @@
|
|
|
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
|
+
// ───────────────────────────────────────────────
|
|
14
|
+
// 1. Chat simple
|
|
15
|
+
// ───────────────────────────────────────────────
|
|
16
|
+
function basicChat() {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
console.log("=== 1. Chat simple ===");
|
|
19
|
+
const client = new __1.UnifiedLLMClient(__1.MODELS.CLAUDE_SONNET_4_6);
|
|
20
|
+
const response = yield client.chat({
|
|
21
|
+
message: "Présente-toi en une phrase.",
|
|
22
|
+
});
|
|
23
|
+
console.log("Réponse :", response.content);
|
|
24
|
+
console.log("Usage :", response.usage);
|
|
25
|
+
console.log();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// ───────────────────────────────────────────────
|
|
29
|
+
// 2. Streaming
|
|
30
|
+
// ───────────────────────────────────────────────
|
|
31
|
+
function streamingChat() {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
console.log("=== 2. Streaming ===");
|
|
34
|
+
const client = new __1.UnifiedLLMClient(__1.MODELS.CLAUDE_SONNET_4_6);
|
|
35
|
+
process.stdout.write("Réponse : ");
|
|
36
|
+
const response = yield client.chat({
|
|
37
|
+
message: "Explique-moi le principe de la relativité restreinte en 3 phrases.",
|
|
38
|
+
stream: true,
|
|
39
|
+
onChunk: (chunk) => {
|
|
40
|
+
if (chunk.contentChunk) {
|
|
41
|
+
process.stdout.write(chunk.contentChunk);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
console.log("\nUsage final :", response.usage);
|
|
46
|
+
console.log();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// ───────────────────────────────────────────────
|
|
50
|
+
// 3. Tool calling
|
|
51
|
+
// ───────────────────────────────────────────────
|
|
52
|
+
function toolCallingChat() {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
console.log("=== 3. Tool calling ===");
|
|
55
|
+
const tools = [
|
|
56
|
+
{
|
|
57
|
+
name: "get_weather",
|
|
58
|
+
description: "Retourne la météo actuelle pour une ville donnée.",
|
|
59
|
+
parameters: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
city: { type: "string", description: "Nom de la ville" },
|
|
63
|
+
},
|
|
64
|
+
required: ["city"],
|
|
65
|
+
},
|
|
66
|
+
executor: (_a) => __awaiter(this, [_a], void 0, function* ({ city }) {
|
|
67
|
+
const temperature = Math.floor(10 + Math.random() * 20);
|
|
68
|
+
return `Météo à ${city} : ${temperature}°C, ensoleillé.`;
|
|
69
|
+
}),
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
const client = new __1.UnifiedLLMClient(__1.MODELS.CLAUDE_SONNET_4_6);
|
|
73
|
+
const response = yield client.chat({
|
|
74
|
+
message: "Quel temps fait-il à Paris en ce moment ?",
|
|
75
|
+
tools,
|
|
76
|
+
toolChoice: "auto",
|
|
77
|
+
});
|
|
78
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
79
|
+
console.log("Tool call détecté :", response.toolCalls[0].name);
|
|
80
|
+
console.log("Arguments :", response.toolCalls[0].arguments);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log("Réponse directe :", response.content);
|
|
84
|
+
}
|
|
85
|
+
console.log();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// ───────────────────────────────────────────────
|
|
89
|
+
// 4. Logging
|
|
90
|
+
// ───────────────────────────────────────────────
|
|
91
|
+
function chatWithLogging() {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
console.log("=== 4. Chat avec logging ===");
|
|
94
|
+
const client = new __1.UnifiedLLMClient(__1.MODELS.CLAUDE_SONNET_4_6, {
|
|
95
|
+
logger: (log) => {
|
|
96
|
+
console.log("[LOG]", {
|
|
97
|
+
requestId: log.requestId,
|
|
98
|
+
model: log.model,
|
|
99
|
+
promptTokens: log.usage.promptTokens,
|
|
100
|
+
completionTokens: log.usage.completionTokens,
|
|
101
|
+
totalTokens: log.usage.totalTokens,
|
|
102
|
+
latencyMs: log.latencyMs,
|
|
103
|
+
finishReason: log.output.finishReason,
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
const response = yield client.chat({
|
|
108
|
+
message: "Quelle est la capitale de la France ?",
|
|
109
|
+
});
|
|
110
|
+
console.log("Réponse :", response.content);
|
|
111
|
+
console.log();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// ───────────────────────────────────────────────
|
|
115
|
+
// 5. OCR — extraction de texte depuis une image
|
|
116
|
+
// ───────────────────────────────────────────────
|
|
117
|
+
function ocrFromImage() {
|
|
118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
console.log("=== 5. OCR depuis une image ===");
|
|
120
|
+
const tools = [
|
|
121
|
+
{
|
|
122
|
+
name: "save_ocr_result",
|
|
123
|
+
description: "Sauvegarde le texte extrait d'une image.",
|
|
124
|
+
parameters: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
text: {
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "Texte intégral extrait de l'image, dans l'ordre de lecture.",
|
|
130
|
+
},
|
|
131
|
+
language: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Langue détectée dans le document (ex: 'fr', 'en').",
|
|
134
|
+
},
|
|
135
|
+
confidence: {
|
|
136
|
+
type: "string",
|
|
137
|
+
enum: ["high", "medium", "low"],
|
|
138
|
+
description: "Niveau de confiance global sur la transcription.",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
required: ["text", "language", "confidence"],
|
|
142
|
+
},
|
|
143
|
+
executor: (_a) => __awaiter(this, [_a], void 0, function* ({ text, language, confidence }) {
|
|
144
|
+
return `OCR enregistré (${language}, confiance: ${confidence}) — ${String(text).length} caractères extraits.`;
|
|
145
|
+
}),
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
const client = new __1.UnifiedLLMClient(__1.MODELS.CLAUDE_SONNET_4_6);
|
|
149
|
+
// Remplace "./media/document.png" par le chemin de ton image ou PDF
|
|
150
|
+
const response = yield client.chat({
|
|
151
|
+
message: {
|
|
152
|
+
role: "user",
|
|
153
|
+
content: "Extrais tout le texte visible dans ce document, mot pour mot, en respectant l'ordre de lecture.",
|
|
154
|
+
attachedFile: "./media/document.png",
|
|
155
|
+
},
|
|
156
|
+
tools,
|
|
157
|
+
toolChoice: "save_ocr_result",
|
|
158
|
+
});
|
|
159
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
160
|
+
const args = response.toolCalls[0].arguments;
|
|
161
|
+
console.log("Langue détectée :", args.language);
|
|
162
|
+
console.log("Confiance :", args.confidence);
|
|
163
|
+
console.log("Texte extrait :\n", args.text);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.log("Réponse texte :", response.content);
|
|
167
|
+
}
|
|
168
|
+
console.log();
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
// ───────────────────────────────────────────────
|
|
172
|
+
// 6. OCR — extraction structurée depuis un PDF
|
|
173
|
+
// ───────────────────────────────────────────────
|
|
174
|
+
function ocrFromPdf() {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
console.log("=== 6. OCR structuré depuis un PDF ===");
|
|
177
|
+
const tools = [
|
|
178
|
+
{
|
|
179
|
+
name: "save_structured_document",
|
|
180
|
+
description: "Sauvegarde le contenu structuré extrait d'un document PDF.",
|
|
181
|
+
parameters: {
|
|
182
|
+
type: "object",
|
|
183
|
+
properties: {
|
|
184
|
+
title: { type: "string", description: "Titre principal du document." },
|
|
185
|
+
sections: {
|
|
186
|
+
type: "array",
|
|
187
|
+
description: "Sections du document dans l'ordre.",
|
|
188
|
+
items: {
|
|
189
|
+
type: "object",
|
|
190
|
+
properties: {
|
|
191
|
+
heading: { type: "string", description: "Titre de la section." },
|
|
192
|
+
content: { type: "string", description: "Contenu textuel de la section." },
|
|
193
|
+
},
|
|
194
|
+
required: ["heading", "content"],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
page_count: { type: "number", description: "Nombre de pages du document." },
|
|
198
|
+
},
|
|
199
|
+
required: ["title", "sections", "page_count"],
|
|
200
|
+
},
|
|
201
|
+
executor: (_a) => __awaiter(this, [_a], void 0, function* ({ title, sections, page_count }) {
|
|
202
|
+
return `Document "${title}" analysé — ${page_count} page(s), ${sections.length} section(s).`;
|
|
203
|
+
}),
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
const client = new __1.UnifiedLLMClient(__1.MODELS.CLAUDE_SONNET_4_6);
|
|
207
|
+
// Remplace "./media/document.pdf" par le chemin de ton PDF
|
|
208
|
+
const response = yield client.chat({
|
|
209
|
+
message: {
|
|
210
|
+
role: "user",
|
|
211
|
+
content: "Analyse ce document PDF et extrais le contenu de la première page de manière structurée : titre, sections et leur contenu.",
|
|
212
|
+
attachedFile: "./media/document.pdf",
|
|
213
|
+
},
|
|
214
|
+
tools,
|
|
215
|
+
toolChoice: "save_structured_document",
|
|
216
|
+
});
|
|
217
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
218
|
+
const args = response.toolCalls[0].arguments;
|
|
219
|
+
console.log("Titre :", args.title);
|
|
220
|
+
console.log("Pages :", args.page_count);
|
|
221
|
+
console.log("Sections :", args.sections.length);
|
|
222
|
+
args.sections.forEach((s, i) => {
|
|
223
|
+
console.log(`\n [${i + 1}] ${s.heading}`);
|
|
224
|
+
console.log(` ${s.content.slice(0, 120)}...`);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
console.log("Réponse texte :", response.content);
|
|
229
|
+
}
|
|
230
|
+
console.log();
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// ───────────────────────────────────────────────
|
|
234
|
+
// Exécution
|
|
235
|
+
// ───────────────────────────────────────────────
|
|
236
|
+
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
237
|
+
yield basicChat();
|
|
238
|
+
yield streamingChat();
|
|
239
|
+
yield toolCallingChat();
|
|
240
|
+
yield chatWithLogging();
|
|
241
|
+
yield ocrFromImage();
|
|
242
|
+
yield ocrFromPdf();
|
|
243
|
+
}))();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { UnifiedLLMClient } from "../core/UnifiedLLMClient";
|
|
2
|
+
declare function listAvailableVoices(client: UnifiedLLMClient): Promise<string[]>;
|
|
3
|
+
declare function generateSimpleAudio(client: UnifiedLLMClient): Promise<import("../index").AudioGenerationResponse>;
|
|
4
|
+
declare function generateMultiVoiceAudio(client: UnifiedLLMClient): Promise<import("../index").AudioGenerationResponse>;
|
|
5
|
+
declare function generateAdvancedAudio(client: UnifiedLLMClient): Promise<import("../index").AudioGenerationResponse>;
|
|
6
|
+
declare function generateStreamingAudio(client: UnifiedLLMClient): Promise<{
|
|
7
|
+
totalChunks: number;
|
|
8
|
+
totalBytes: number;
|
|
9
|
+
}>;
|
|
10
|
+
declare function generateInMemoryAudio(client: UnifiedLLMClient): Promise<import("../index").AudioGenerationResponse>;
|
|
11
|
+
declare function runAudioExamples(): Promise<void>;
|
|
12
|
+
export declare function cleanupAudioExamples(): void;
|
|
13
|
+
export { runAudioExamples, listAvailableVoices, generateSimpleAudio, generateMultiVoiceAudio, generateAdvancedAudio, generateStreamingAudio, generateInMemoryAudio };
|
|
14
|
+
//# sourceMappingURL=audio-example.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-example.d.ts","sourceRoot":"","sources":["../../src/examples/audio-example.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAW5D,iBAAe,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,qBAG1D;AAED,iBAAe,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,uDAU1D;AAED,iBAAe,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,uDAgB9D;AAED,iBAAe,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,uDAe5D;AAED,iBAAe,sBAAsB,CAAC,MAAM,EAAE,gBAAgB;;;GAiB7D;AAED,iBAAe,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,uDAQ5D;AAED,iBAAe,gBAAgB,kBAwB9B;AAQD,wBAAgB,oBAAoB,SAkBnC;AAaD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACtB,CAAC"}
|