getpatter 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -1
- package/dist/banner-FLR2HE5Z.mjs +19 -0
- package/dist/{chunk-757NVN4L.mjs → chunk-7SDDK2AO.mjs} +61 -46
- package/dist/{chunk-JO5C35FM.mjs → chunk-AKQFOFLG.mjs} +1 -1
- package/dist/cli.js +7 -2
- package/dist/index.d.mts +363 -56
- package/dist/index.d.ts +363 -56
- package/dist/index.js +728 -870
- package/dist/index.mjs +640 -7
- package/dist/{test-mode-YFOL2HYH.mjs → test-mode-K2TTPRGE.mjs} +1 -1
- package/dist/{tunnel-BL7A7GXW.mjs → tunnel-O7ICMSTP.mjs} +1 -1
- package/package.json +1 -1
- package/dist/lib-4WCAS54J.mjs +0 -830
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-AFUYSNDH.mjs";
|
|
4
4
|
import {
|
|
5
5
|
startTunnel
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AKQFOFLG.mjs";
|
|
7
7
|
import {
|
|
8
8
|
CallMetricsAccumulator,
|
|
9
9
|
DEFAULT_MIN_SENTENCE_LEN,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
resample16kTo8k,
|
|
37
37
|
resample24kTo16k,
|
|
38
38
|
resample8kTo16k
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-7SDDK2AO.mjs";
|
|
40
40
|
import {
|
|
41
41
|
getLogger,
|
|
42
42
|
setLogger
|
|
@@ -351,6 +351,8 @@ var Patter = class {
|
|
|
351
351
|
"Unknown engine. Expected OpenAIRealtime or ElevenLabsConvAI instance."
|
|
352
352
|
);
|
|
353
353
|
}
|
|
354
|
+
} else if (!working.provider && (working.stt !== void 0 || working.tts !== void 0 || working.llm !== void 0)) {
|
|
355
|
+
working = { ...working, provider: "pipeline" };
|
|
354
356
|
}
|
|
355
357
|
if (working.provider) {
|
|
356
358
|
const valid = ["openai_realtime", "elevenlabs_convai", "pipeline"];
|
|
@@ -358,6 +360,19 @@ var Patter = class {
|
|
|
358
360
|
throw new Error(`provider must be one of: ${valid.join(", ")}. Got: '${working.provider}'`);
|
|
359
361
|
}
|
|
360
362
|
}
|
|
363
|
+
if (working.llm !== void 0) {
|
|
364
|
+
const llm = working.llm;
|
|
365
|
+
if (!llm || typeof llm.stream !== "function") {
|
|
366
|
+
throw new Error(
|
|
367
|
+
"`llm` must be an LLMProvider instance (e.g. new AnthropicLLM()). Got a value without a `.stream` method."
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
if (working.engine) {
|
|
371
|
+
getLogger().warn(
|
|
372
|
+
"agent({ engine, llm }): `llm` is ignored when `engine` is set \u2014 realtime/ConvAI engines run their own model. Remove `llm` or switch to pipeline mode (stt + tts + llm) to silence this warning."
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
361
376
|
if (working.tools) {
|
|
362
377
|
if (!Array.isArray(working.tools)) {
|
|
363
378
|
throw new TypeError("tools must be an array");
|
|
@@ -400,8 +415,10 @@ var Patter = class {
|
|
|
400
415
|
if (wantsCloudflared && webhookUrl) {
|
|
401
416
|
throw new Error("Cannot use both tunnel: true and webhookUrl. Pick one.");
|
|
402
417
|
}
|
|
418
|
+
const { showBanner } = await import("./banner-FLR2HE5Z.mjs");
|
|
419
|
+
showBanner();
|
|
403
420
|
if (wantsCloudflared) {
|
|
404
|
-
const { startTunnel: startTunnel2 } = await import("./tunnel-
|
|
421
|
+
const { startTunnel: startTunnel2 } = await import("./tunnel-O7ICMSTP.mjs");
|
|
405
422
|
this.tunnelHandle = await startTunnel2(port);
|
|
406
423
|
webhookUrl = this.tunnelHandle.hostname;
|
|
407
424
|
}
|
|
@@ -452,7 +469,7 @@ var Patter = class {
|
|
|
452
469
|
if (this.mode !== "local") {
|
|
453
470
|
throw new Error("test() is only available in local mode");
|
|
454
471
|
}
|
|
455
|
-
const { TestSession: TestSession2 } = await import("./test-mode-
|
|
472
|
+
const { TestSession: TestSession2 } = await import("./test-mode-K2TTPRGE.mjs");
|
|
456
473
|
const session = new TestSession2();
|
|
457
474
|
await session.run({
|
|
458
475
|
agent: opts.agent,
|
|
@@ -2274,7 +2291,8 @@ var ELEVENLABS_VOICE_ID_BY_NAME = {
|
|
|
2274
2291
|
glinda: "z9fAnlkpzviPz146aGWa",
|
|
2275
2292
|
giovanni: "zcAOhNBS3c14rBihAFp1",
|
|
2276
2293
|
mimi: "zrHiDhphv9ZnVXBqCLjz",
|
|
2277
|
-
|
|
2294
|
+
sarah: "EXAVITQu4vr4xnSDxMaL",
|
|
2295
|
+
alloy: "EXAVITQu4vr4xnSDxMaL"
|
|
2278
2296
|
};
|
|
2279
2297
|
var VOICE_ID_PATTERN = /^[A-Za-z0-9]{20}$/;
|
|
2280
2298
|
function resolveVoiceId(voice) {
|
|
@@ -2283,7 +2301,7 @@ function resolveVoiceId(voice) {
|
|
|
2283
2301
|
return ELEVENLABS_VOICE_ID_BY_NAME[voice.toLowerCase()] ?? voice;
|
|
2284
2302
|
}
|
|
2285
2303
|
var ElevenLabsTTS = class {
|
|
2286
|
-
constructor(apiKey, voiceId = "
|
|
2304
|
+
constructor(apiKey, voiceId = "EXAVITQu4vr4xnSDxMaL", modelId = "eleven_turbo_v2_5", outputFormat = "pcm_16000") {
|
|
2287
2305
|
this.apiKey = apiKey;
|
|
2288
2306
|
this.modelId = modelId;
|
|
2289
2307
|
this.outputFormat = outputFormat;
|
|
@@ -2354,7 +2372,7 @@ var TTS = class extends ElevenLabsTTS {
|
|
|
2354
2372
|
}
|
|
2355
2373
|
super(
|
|
2356
2374
|
key,
|
|
2357
|
-
opts.voiceId ?? "
|
|
2375
|
+
opts.voiceId ?? "EXAVITQu4vr4xnSDxMaL",
|
|
2358
2376
|
opts.modelId ?? "eleven_turbo_v2_5",
|
|
2359
2377
|
opts.outputFormat ?? "pcm_16000"
|
|
2360
2378
|
);
|
|
@@ -2850,6 +2868,616 @@ var TTS5 = class extends LMNTTTS {
|
|
|
2850
2868
|
}
|
|
2851
2869
|
};
|
|
2852
2870
|
|
|
2871
|
+
// src/llm/openai.ts
|
|
2872
|
+
var LLM = class extends OpenAILLMProvider {
|
|
2873
|
+
constructor(opts = {}) {
|
|
2874
|
+
const key = opts.apiKey ?? process.env.OPENAI_API_KEY;
|
|
2875
|
+
if (!key) {
|
|
2876
|
+
throw new Error(
|
|
2877
|
+
"OpenAI LLM requires an apiKey. Pass { apiKey: 'sk-...' } or set OPENAI_API_KEY."
|
|
2878
|
+
);
|
|
2879
|
+
}
|
|
2880
|
+
super(key, opts.model ?? "gpt-4o-mini");
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2883
|
+
|
|
2884
|
+
// src/providers/anthropic-llm.ts
|
|
2885
|
+
var DEFAULT_ANTHROPIC_URL = "https://api.anthropic.com/v1/messages";
|
|
2886
|
+
var DEFAULT_ANTHROPIC_VERSION = "2023-06-01";
|
|
2887
|
+
var DEFAULT_MODEL = "claude-3-5-sonnet-20241022";
|
|
2888
|
+
var DEFAULT_MAX_TOKENS = 1024;
|
|
2889
|
+
var AnthropicLLMProvider = class {
|
|
2890
|
+
apiKey;
|
|
2891
|
+
model;
|
|
2892
|
+
maxTokens;
|
|
2893
|
+
temperature;
|
|
2894
|
+
url;
|
|
2895
|
+
anthropicVersion;
|
|
2896
|
+
constructor(options) {
|
|
2897
|
+
if (!options.apiKey) {
|
|
2898
|
+
throw new Error(
|
|
2899
|
+
"Anthropic API key is required. Pass it via { apiKey } or set the ANTHROPIC_API_KEY environment variable before constructing the provider."
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
this.apiKey = options.apiKey;
|
|
2903
|
+
this.model = options.model ?? DEFAULT_MODEL;
|
|
2904
|
+
this.maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
2905
|
+
this.temperature = options.temperature;
|
|
2906
|
+
this.url = options.baseUrl ?? DEFAULT_ANTHROPIC_URL;
|
|
2907
|
+
this.anthropicVersion = options.anthropicVersion ?? DEFAULT_ANTHROPIC_VERSION;
|
|
2908
|
+
}
|
|
2909
|
+
async *stream(messages, tools) {
|
|
2910
|
+
const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
|
|
2911
|
+
const anthropicTools = tools ? toAnthropicTools(tools) : null;
|
|
2912
|
+
const body = {
|
|
2913
|
+
model: this.model,
|
|
2914
|
+
messages: anthropicMessages,
|
|
2915
|
+
max_tokens: this.maxTokens,
|
|
2916
|
+
stream: true
|
|
2917
|
+
};
|
|
2918
|
+
if (system) body.system = system;
|
|
2919
|
+
if (anthropicTools && anthropicTools.length > 0) body.tools = anthropicTools;
|
|
2920
|
+
if (this.temperature !== void 0) body.temperature = this.temperature;
|
|
2921
|
+
const response = await fetch(this.url, {
|
|
2922
|
+
method: "POST",
|
|
2923
|
+
headers: {
|
|
2924
|
+
"Content-Type": "application/json",
|
|
2925
|
+
"x-api-key": this.apiKey,
|
|
2926
|
+
"anthropic-version": this.anthropicVersion
|
|
2927
|
+
},
|
|
2928
|
+
body: JSON.stringify(body),
|
|
2929
|
+
signal: AbortSignal.timeout(3e4)
|
|
2930
|
+
});
|
|
2931
|
+
if (!response.ok) {
|
|
2932
|
+
const errText = await response.text();
|
|
2933
|
+
getLogger().error(`Anthropic API error: ${response.status} ${errText}`);
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
const reader = response.body?.getReader();
|
|
2937
|
+
if (!reader) return;
|
|
2938
|
+
const decoder = new TextDecoder();
|
|
2939
|
+
let buffer = "";
|
|
2940
|
+
const toolIndexByBlock = /* @__PURE__ */ new Map();
|
|
2941
|
+
const toolIdByBlock = /* @__PURE__ */ new Map();
|
|
2942
|
+
let nextIndex = 0;
|
|
2943
|
+
while (true) {
|
|
2944
|
+
const { done, value } = await reader.read();
|
|
2945
|
+
if (done) break;
|
|
2946
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2947
|
+
const lines = buffer.split("\n");
|
|
2948
|
+
buffer = lines.pop() || "";
|
|
2949
|
+
for (const line of lines) {
|
|
2950
|
+
const trimmed = line.trim();
|
|
2951
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
2952
|
+
const data = trimmed.slice(6);
|
|
2953
|
+
if (!data || data === "[DONE]") continue;
|
|
2954
|
+
let event;
|
|
2955
|
+
try {
|
|
2956
|
+
event = JSON.parse(data);
|
|
2957
|
+
} catch {
|
|
2958
|
+
continue;
|
|
2959
|
+
}
|
|
2960
|
+
if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
|
|
2961
|
+
const blockIdx = event.index ?? 0;
|
|
2962
|
+
const toolId = event.content_block.id ?? "";
|
|
2963
|
+
const toolName = event.content_block.name ?? "";
|
|
2964
|
+
const patterIndex = nextIndex++;
|
|
2965
|
+
toolIndexByBlock.set(blockIdx, patterIndex);
|
|
2966
|
+
toolIdByBlock.set(blockIdx, toolId);
|
|
2967
|
+
yield {
|
|
2968
|
+
type: "tool_call",
|
|
2969
|
+
index: patterIndex,
|
|
2970
|
+
id: toolId,
|
|
2971
|
+
name: toolName,
|
|
2972
|
+
arguments: ""
|
|
2973
|
+
};
|
|
2974
|
+
continue;
|
|
2975
|
+
}
|
|
2976
|
+
if (event.type === "content_block_delta") {
|
|
2977
|
+
if (event.delta?.type === "text_delta" && event.delta.text) {
|
|
2978
|
+
yield { type: "text", content: event.delta.text };
|
|
2979
|
+
continue;
|
|
2980
|
+
}
|
|
2981
|
+
if (event.delta?.type === "input_json_delta" && event.delta.partial_json) {
|
|
2982
|
+
const blockIdx = event.index ?? 0;
|
|
2983
|
+
const patterIndex = toolIndexByBlock.get(blockIdx);
|
|
2984
|
+
if (patterIndex !== void 0) {
|
|
2985
|
+
yield {
|
|
2986
|
+
type: "tool_call",
|
|
2987
|
+
index: patterIndex,
|
|
2988
|
+
id: toolIdByBlock.get(blockIdx),
|
|
2989
|
+
arguments: event.delta.partial_json
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
yield { type: "done" };
|
|
2997
|
+
}
|
|
2998
|
+
};
|
|
2999
|
+
function toAnthropicTools(tools) {
|
|
3000
|
+
return tools.map((t) => {
|
|
3001
|
+
const fn = t.function ?? t;
|
|
3002
|
+
return {
|
|
3003
|
+
name: String(fn.name ?? ""),
|
|
3004
|
+
description: String(fn.description ?? ""),
|
|
3005
|
+
input_schema: fn.parameters ?? { type: "object", properties: {} }
|
|
3006
|
+
};
|
|
3007
|
+
});
|
|
3008
|
+
}
|
|
3009
|
+
function toAnthropicMessages(messages) {
|
|
3010
|
+
const systemParts = [];
|
|
3011
|
+
const out = [];
|
|
3012
|
+
for (const rawMsg of messages) {
|
|
3013
|
+
const role = rawMsg.role;
|
|
3014
|
+
if (role === "system") {
|
|
3015
|
+
if (typeof rawMsg.content === "string" && rawMsg.content) {
|
|
3016
|
+
systemParts.push(rawMsg.content);
|
|
3017
|
+
}
|
|
3018
|
+
continue;
|
|
3019
|
+
}
|
|
3020
|
+
if (role === "user") {
|
|
3021
|
+
if (typeof rawMsg.content === "string") {
|
|
3022
|
+
out.push({ role: "user", content: rawMsg.content });
|
|
3023
|
+
} else if (rawMsg.content) {
|
|
3024
|
+
out.push({ role: "user", content: rawMsg.content });
|
|
3025
|
+
}
|
|
3026
|
+
continue;
|
|
3027
|
+
}
|
|
3028
|
+
if (role === "assistant") {
|
|
3029
|
+
const blocks = [];
|
|
3030
|
+
if (typeof rawMsg.content === "string" && rawMsg.content) {
|
|
3031
|
+
blocks.push({ type: "text", text: rawMsg.content });
|
|
3032
|
+
}
|
|
3033
|
+
for (const tc of rawMsg.tool_calls ?? []) {
|
|
3034
|
+
let args = {};
|
|
3035
|
+
try {
|
|
3036
|
+
args = JSON.parse(tc.function?.arguments ?? "{}");
|
|
3037
|
+
} catch {
|
|
3038
|
+
args = {};
|
|
3039
|
+
}
|
|
3040
|
+
blocks.push({
|
|
3041
|
+
type: "tool_use",
|
|
3042
|
+
id: tc.id ?? "",
|
|
3043
|
+
name: tc.function?.name ?? "",
|
|
3044
|
+
input: args
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
3047
|
+
if (blocks.length > 0) {
|
|
3048
|
+
out.push({ role: "assistant", content: blocks });
|
|
3049
|
+
}
|
|
3050
|
+
continue;
|
|
3051
|
+
}
|
|
3052
|
+
if (role === "tool") {
|
|
3053
|
+
const contentStr = typeof rawMsg.content === "string" ? rawMsg.content : JSON.stringify(rawMsg.content);
|
|
3054
|
+
out.push({
|
|
3055
|
+
role: "user",
|
|
3056
|
+
content: [
|
|
3057
|
+
{
|
|
3058
|
+
type: "tool_result",
|
|
3059
|
+
tool_use_id: rawMsg.tool_call_id ?? "",
|
|
3060
|
+
content: contentStr
|
|
3061
|
+
}
|
|
3062
|
+
]
|
|
3063
|
+
});
|
|
3064
|
+
continue;
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
return { system: systemParts.join("\n\n"), messages: out };
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
// src/llm/anthropic.ts
|
|
3071
|
+
var LLM2 = class extends AnthropicLLMProvider {
|
|
3072
|
+
constructor(opts = {}) {
|
|
3073
|
+
const key = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
3074
|
+
if (!key) {
|
|
3075
|
+
throw new Error(
|
|
3076
|
+
"Anthropic LLM requires an apiKey. Pass { apiKey: 'sk-ant-...' } or set ANTHROPIC_API_KEY."
|
|
3077
|
+
);
|
|
3078
|
+
}
|
|
3079
|
+
super({
|
|
3080
|
+
apiKey: key,
|
|
3081
|
+
model: opts.model,
|
|
3082
|
+
maxTokens: opts.maxTokens,
|
|
3083
|
+
temperature: opts.temperature,
|
|
3084
|
+
baseUrl: opts.baseUrl,
|
|
3085
|
+
anthropicVersion: opts.anthropicVersion
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
};
|
|
3089
|
+
|
|
3090
|
+
// src/providers/groq-llm.ts
|
|
3091
|
+
var GROQ_BASE_URL = "https://api.groq.com/openai/v1";
|
|
3092
|
+
var DEFAULT_MODEL2 = "llama-3.3-70b-versatile";
|
|
3093
|
+
var GroqLLMProvider = class {
|
|
3094
|
+
apiKey;
|
|
3095
|
+
model;
|
|
3096
|
+
baseUrl;
|
|
3097
|
+
constructor(options) {
|
|
3098
|
+
if (!options.apiKey) {
|
|
3099
|
+
throw new Error(
|
|
3100
|
+
"Groq API key is required. Pass it via { apiKey } or read GROQ_API_KEY from the environment."
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
this.apiKey = options.apiKey;
|
|
3104
|
+
this.model = options.model ?? DEFAULT_MODEL2;
|
|
3105
|
+
this.baseUrl = options.baseUrl ?? GROQ_BASE_URL;
|
|
3106
|
+
}
|
|
3107
|
+
async *stream(messages, tools) {
|
|
3108
|
+
const body = {
|
|
3109
|
+
model: this.model,
|
|
3110
|
+
messages,
|
|
3111
|
+
stream: true
|
|
3112
|
+
};
|
|
3113
|
+
if (tools) body.tools = tools;
|
|
3114
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
3115
|
+
method: "POST",
|
|
3116
|
+
headers: {
|
|
3117
|
+
"Content-Type": "application/json",
|
|
3118
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
3119
|
+
},
|
|
3120
|
+
body: JSON.stringify(body),
|
|
3121
|
+
signal: AbortSignal.timeout(3e4)
|
|
3122
|
+
});
|
|
3123
|
+
if (!response.ok) {
|
|
3124
|
+
const errText = await response.text();
|
|
3125
|
+
getLogger().error(`Groq API error: ${response.status} ${errText}`);
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
yield* parseOpenAISseStream(response);
|
|
3129
|
+
}
|
|
3130
|
+
};
|
|
3131
|
+
async function* parseOpenAISseStream(response) {
|
|
3132
|
+
const reader = response.body?.getReader();
|
|
3133
|
+
if (!reader) return;
|
|
3134
|
+
const decoder = new TextDecoder();
|
|
3135
|
+
let buffer = "";
|
|
3136
|
+
while (true) {
|
|
3137
|
+
const { done, value } = await reader.read();
|
|
3138
|
+
if (done) break;
|
|
3139
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3140
|
+
const lines = buffer.split("\n");
|
|
3141
|
+
buffer = lines.pop() || "";
|
|
3142
|
+
for (const line of lines) {
|
|
3143
|
+
const trimmed = line.trim();
|
|
3144
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
3145
|
+
const data = trimmed.slice(6);
|
|
3146
|
+
if (data === "[DONE]") continue;
|
|
3147
|
+
let chunk;
|
|
3148
|
+
try {
|
|
3149
|
+
chunk = JSON.parse(data);
|
|
3150
|
+
} catch {
|
|
3151
|
+
continue;
|
|
3152
|
+
}
|
|
3153
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
3154
|
+
if (!delta) continue;
|
|
3155
|
+
if (delta.content) {
|
|
3156
|
+
yield { type: "text", content: delta.content };
|
|
3157
|
+
}
|
|
3158
|
+
if (delta.tool_calls) {
|
|
3159
|
+
for (const tc of delta.tool_calls) {
|
|
3160
|
+
yield {
|
|
3161
|
+
type: "tool_call",
|
|
3162
|
+
index: tc.index,
|
|
3163
|
+
id: tc.id,
|
|
3164
|
+
name: tc.function?.name,
|
|
3165
|
+
arguments: tc.function?.arguments
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
|
|
3173
|
+
// src/llm/groq.ts
|
|
3174
|
+
var LLM3 = class extends GroqLLMProvider {
|
|
3175
|
+
constructor(opts = {}) {
|
|
3176
|
+
const key = opts.apiKey ?? process.env.GROQ_API_KEY;
|
|
3177
|
+
if (!key) {
|
|
3178
|
+
throw new Error(
|
|
3179
|
+
"Groq LLM requires an apiKey. Pass { apiKey: 'gsk_...' } or set GROQ_API_KEY."
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3182
|
+
super({
|
|
3183
|
+
apiKey: key,
|
|
3184
|
+
model: opts.model,
|
|
3185
|
+
baseUrl: opts.baseUrl
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
};
|
|
3189
|
+
|
|
3190
|
+
// src/providers/cerebras-llm.ts
|
|
3191
|
+
var CEREBRAS_BASE_URL = "https://api.cerebras.ai/v1";
|
|
3192
|
+
var DEFAULT_MODEL3 = "llama3.1-8b";
|
|
3193
|
+
var CerebrasLLMProvider = class {
|
|
3194
|
+
apiKey;
|
|
3195
|
+
model;
|
|
3196
|
+
baseUrl;
|
|
3197
|
+
gzipCompression;
|
|
3198
|
+
constructor(options) {
|
|
3199
|
+
if (!options.apiKey) {
|
|
3200
|
+
throw new Error(
|
|
3201
|
+
"Cerebras API key is required. Pass it via { apiKey } or read CEREBRAS_API_KEY from the environment."
|
|
3202
|
+
);
|
|
3203
|
+
}
|
|
3204
|
+
this.apiKey = options.apiKey;
|
|
3205
|
+
this.model = options.model ?? DEFAULT_MODEL3;
|
|
3206
|
+
this.baseUrl = options.baseUrl ?? CEREBRAS_BASE_URL;
|
|
3207
|
+
this.gzipCompression = options.gzipCompression ?? false;
|
|
3208
|
+
}
|
|
3209
|
+
async *stream(messages, tools) {
|
|
3210
|
+
const body = {
|
|
3211
|
+
model: this.model,
|
|
3212
|
+
messages,
|
|
3213
|
+
stream: true
|
|
3214
|
+
};
|
|
3215
|
+
if (tools) body.tools = tools;
|
|
3216
|
+
const headers = {
|
|
3217
|
+
"Content-Type": "application/json",
|
|
3218
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
3219
|
+
};
|
|
3220
|
+
let payload = JSON.stringify(body);
|
|
3221
|
+
if (this.gzipCompression) {
|
|
3222
|
+
const compressed = await gzipEncode(payload);
|
|
3223
|
+
if (compressed) {
|
|
3224
|
+
payload = compressed;
|
|
3225
|
+
headers["Content-Encoding"] = "gzip";
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
3229
|
+
method: "POST",
|
|
3230
|
+
headers,
|
|
3231
|
+
body: payload,
|
|
3232
|
+
signal: AbortSignal.timeout(3e4)
|
|
3233
|
+
});
|
|
3234
|
+
if (!response.ok) {
|
|
3235
|
+
const errText = await response.text();
|
|
3236
|
+
getLogger().error(`Cerebras API error: ${response.status} ${errText}`);
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
yield* parseOpenAISseStream(response);
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
async function gzipEncode(data) {
|
|
3243
|
+
const CompressionCtor = globalThis.CompressionStream;
|
|
3244
|
+
if (!CompressionCtor) return null;
|
|
3245
|
+
const stream = new CompressionCtor("gzip");
|
|
3246
|
+
const writer = stream.writable.getWriter();
|
|
3247
|
+
const encoder = new TextEncoder();
|
|
3248
|
+
await writer.write(encoder.encode(data));
|
|
3249
|
+
await writer.close();
|
|
3250
|
+
const chunks = [];
|
|
3251
|
+
const reader = stream.readable.getReader();
|
|
3252
|
+
while (true) {
|
|
3253
|
+
const { done, value } = await reader.read();
|
|
3254
|
+
if (done) break;
|
|
3255
|
+
if (value) chunks.push(value);
|
|
3256
|
+
}
|
|
3257
|
+
const total = chunks.reduce((n, c) => n + c.length, 0);
|
|
3258
|
+
const out = new Uint8Array(total);
|
|
3259
|
+
let offset = 0;
|
|
3260
|
+
for (const c of chunks) {
|
|
3261
|
+
out.set(c, offset);
|
|
3262
|
+
offset += c.length;
|
|
3263
|
+
}
|
|
3264
|
+
return out;
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3267
|
+
// src/llm/cerebras.ts
|
|
3268
|
+
var LLM4 = class extends CerebrasLLMProvider {
|
|
3269
|
+
constructor(opts = {}) {
|
|
3270
|
+
const key = opts.apiKey ?? process.env.CEREBRAS_API_KEY;
|
|
3271
|
+
if (!key) {
|
|
3272
|
+
throw new Error(
|
|
3273
|
+
"Cerebras LLM requires an apiKey. Pass { apiKey: 'csk-...' } or set CEREBRAS_API_KEY."
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3276
|
+
super({
|
|
3277
|
+
apiKey: key,
|
|
3278
|
+
model: opts.model,
|
|
3279
|
+
baseUrl: opts.baseUrl,
|
|
3280
|
+
gzipCompression: opts.gzipCompression
|
|
3281
|
+
});
|
|
3282
|
+
}
|
|
3283
|
+
};
|
|
3284
|
+
|
|
3285
|
+
// src/providers/google-llm.ts
|
|
3286
|
+
var DEFAULT_MODEL4 = "gemini-2.5-flash";
|
|
3287
|
+
var DEFAULT_BASE_URL3 = "https://generativelanguage.googleapis.com/v1beta";
|
|
3288
|
+
var GoogleLLMProvider = class {
|
|
3289
|
+
apiKey;
|
|
3290
|
+
model;
|
|
3291
|
+
baseUrl;
|
|
3292
|
+
temperature;
|
|
3293
|
+
maxOutputTokens;
|
|
3294
|
+
constructor(options) {
|
|
3295
|
+
if (!options.apiKey) {
|
|
3296
|
+
throw new Error(
|
|
3297
|
+
"Google API key is required. Pass it via { apiKey } or read GOOGLE_API_KEY from the environment."
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
3300
|
+
this.apiKey = options.apiKey;
|
|
3301
|
+
this.model = options.model ?? DEFAULT_MODEL4;
|
|
3302
|
+
this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL3;
|
|
3303
|
+
this.temperature = options.temperature;
|
|
3304
|
+
this.maxOutputTokens = options.maxOutputTokens;
|
|
3305
|
+
}
|
|
3306
|
+
async *stream(messages, tools) {
|
|
3307
|
+
const { systemInstruction, contents } = toGeminiContents(messages);
|
|
3308
|
+
const geminiTools = tools ? toGeminiTools(tools) : null;
|
|
3309
|
+
const body = { contents };
|
|
3310
|
+
if (systemInstruction) {
|
|
3311
|
+
body.systemInstruction = { role: "system", parts: [{ text: systemInstruction }] };
|
|
3312
|
+
}
|
|
3313
|
+
if (geminiTools) body.tools = geminiTools;
|
|
3314
|
+
const generationConfig = {};
|
|
3315
|
+
if (this.temperature !== void 0) generationConfig.temperature = this.temperature;
|
|
3316
|
+
if (this.maxOutputTokens !== void 0)
|
|
3317
|
+
generationConfig.maxOutputTokens = this.maxOutputTokens;
|
|
3318
|
+
if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
|
|
3319
|
+
const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(this.apiKey)}`;
|
|
3320
|
+
const response = await fetch(url, {
|
|
3321
|
+
method: "POST",
|
|
3322
|
+
headers: { "Content-Type": "application/json" },
|
|
3323
|
+
body: JSON.stringify(body),
|
|
3324
|
+
signal: AbortSignal.timeout(3e4)
|
|
3325
|
+
});
|
|
3326
|
+
if (!response.ok) {
|
|
3327
|
+
const errText = await response.text();
|
|
3328
|
+
getLogger().error(`Gemini API error: ${response.status} ${errText}`);
|
|
3329
|
+
return;
|
|
3330
|
+
}
|
|
3331
|
+
const reader = response.body?.getReader();
|
|
3332
|
+
if (!reader) return;
|
|
3333
|
+
const decoder = new TextDecoder();
|
|
3334
|
+
let buffer = "";
|
|
3335
|
+
let nextIndex = 0;
|
|
3336
|
+
while (true) {
|
|
3337
|
+
const { done, value } = await reader.read();
|
|
3338
|
+
if (done) break;
|
|
3339
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3340
|
+
const lines = buffer.split("\n");
|
|
3341
|
+
buffer = lines.pop() || "";
|
|
3342
|
+
for (const line of lines) {
|
|
3343
|
+
const trimmed = line.trim();
|
|
3344
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
3345
|
+
const data = trimmed.slice(6);
|
|
3346
|
+
if (!data) continue;
|
|
3347
|
+
let payload;
|
|
3348
|
+
try {
|
|
3349
|
+
payload = JSON.parse(data);
|
|
3350
|
+
} catch {
|
|
3351
|
+
continue;
|
|
3352
|
+
}
|
|
3353
|
+
const candidate = payload.candidates?.[0];
|
|
3354
|
+
const parts = candidate?.content?.parts ?? [];
|
|
3355
|
+
for (const part of parts) {
|
|
3356
|
+
if (part.functionCall) {
|
|
3357
|
+
const args = part.functionCall.args ?? {};
|
|
3358
|
+
const callId = part.functionCall.id ?? `gemini_call_${nextIndex}`;
|
|
3359
|
+
yield {
|
|
3360
|
+
type: "tool_call",
|
|
3361
|
+
index: nextIndex,
|
|
3362
|
+
id: callId,
|
|
3363
|
+
name: part.functionCall.name ?? "",
|
|
3364
|
+
arguments: JSON.stringify(args)
|
|
3365
|
+
};
|
|
3366
|
+
nextIndex++;
|
|
3367
|
+
continue;
|
|
3368
|
+
}
|
|
3369
|
+
if (part.text) {
|
|
3370
|
+
yield { type: "text", content: part.text };
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
yield { type: "done" };
|
|
3376
|
+
}
|
|
3377
|
+
};
|
|
3378
|
+
function toGeminiTools(tools) {
|
|
3379
|
+
const functionDeclarations = tools.map((t) => {
|
|
3380
|
+
const fn = t.function ?? t;
|
|
3381
|
+
return {
|
|
3382
|
+
name: String(fn.name ?? ""),
|
|
3383
|
+
description: String(fn.description ?? ""),
|
|
3384
|
+
parameters: fn.parameters ?? { type: "object", properties: {} }
|
|
3385
|
+
};
|
|
3386
|
+
});
|
|
3387
|
+
if (functionDeclarations.length === 0) return [];
|
|
3388
|
+
return [{ functionDeclarations }];
|
|
3389
|
+
}
|
|
3390
|
+
function toGeminiContents(messages) {
|
|
3391
|
+
const systemParts = [];
|
|
3392
|
+
const contents = [];
|
|
3393
|
+
for (const rawMsg of messages) {
|
|
3394
|
+
const role = rawMsg.role;
|
|
3395
|
+
if (role === "system") {
|
|
3396
|
+
if (typeof rawMsg.content === "string" && rawMsg.content) {
|
|
3397
|
+
systemParts.push(rawMsg.content);
|
|
3398
|
+
}
|
|
3399
|
+
continue;
|
|
3400
|
+
}
|
|
3401
|
+
if (role === "user") {
|
|
3402
|
+
if (typeof rawMsg.content === "string" && rawMsg.content) {
|
|
3403
|
+
contents.push({ role: "user", parts: [{ text: rawMsg.content }] });
|
|
3404
|
+
}
|
|
3405
|
+
continue;
|
|
3406
|
+
}
|
|
3407
|
+
if (role === "assistant") {
|
|
3408
|
+
const parts = [];
|
|
3409
|
+
if (typeof rawMsg.content === "string" && rawMsg.content) {
|
|
3410
|
+
parts.push({ text: rawMsg.content });
|
|
3411
|
+
}
|
|
3412
|
+
for (const tc of rawMsg.tool_calls ?? []) {
|
|
3413
|
+
let args = {};
|
|
3414
|
+
try {
|
|
3415
|
+
const parsed = JSON.parse(tc.function?.arguments ?? "{}");
|
|
3416
|
+
if (parsed && typeof parsed === "object") args = parsed;
|
|
3417
|
+
} catch {
|
|
3418
|
+
args = {};
|
|
3419
|
+
}
|
|
3420
|
+
parts.push({
|
|
3421
|
+
functionCall: {
|
|
3422
|
+
name: tc.function?.name ?? "",
|
|
3423
|
+
args,
|
|
3424
|
+
id: tc.id
|
|
3425
|
+
}
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3428
|
+
if (parts.length > 0) contents.push({ role: "model", parts });
|
|
3429
|
+
continue;
|
|
3430
|
+
}
|
|
3431
|
+
if (role === "tool") {
|
|
3432
|
+
const raw = rawMsg.content;
|
|
3433
|
+
let response;
|
|
3434
|
+
if (typeof raw === "string") {
|
|
3435
|
+
try {
|
|
3436
|
+
const parsed = JSON.parse(raw);
|
|
3437
|
+
response = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { result: parsed };
|
|
3438
|
+
} catch {
|
|
3439
|
+
response = { result: raw };
|
|
3440
|
+
}
|
|
3441
|
+
} else {
|
|
3442
|
+
response = raw ?? {};
|
|
3443
|
+
}
|
|
3444
|
+
contents.push({
|
|
3445
|
+
role: "user",
|
|
3446
|
+
parts: [
|
|
3447
|
+
{
|
|
3448
|
+
functionResponse: {
|
|
3449
|
+
name: rawMsg.name ?? rawMsg.tool_call_id ?? "",
|
|
3450
|
+
response,
|
|
3451
|
+
id: rawMsg.tool_call_id
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
]
|
|
3455
|
+
});
|
|
3456
|
+
continue;
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
return { systemInstruction: systemParts.join("\n\n"), contents };
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
// src/llm/google.ts
|
|
3463
|
+
var LLM5 = class extends GoogleLLMProvider {
|
|
3464
|
+
constructor(opts = {}) {
|
|
3465
|
+
const key = opts.apiKey ?? process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY;
|
|
3466
|
+
if (!key) {
|
|
3467
|
+
throw new Error(
|
|
3468
|
+
"Google LLM requires an apiKey. Pass { apiKey: 'AIza...' } or set GEMINI_API_KEY (or GOOGLE_API_KEY)."
|
|
3469
|
+
);
|
|
3470
|
+
}
|
|
3471
|
+
super({
|
|
3472
|
+
apiKey: key,
|
|
3473
|
+
model: opts.model,
|
|
3474
|
+
baseUrl: opts.baseUrl,
|
|
3475
|
+
temperature: opts.temperature,
|
|
3476
|
+
maxOutputTokens: opts.maxOutputTokens
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
|
|
2853
3481
|
// src/carriers/twilio.ts
|
|
2854
3482
|
var Carrier = class {
|
|
2855
3483
|
kind = "twilio";
|
|
@@ -3558,6 +4186,7 @@ function isAudioConfig(value) {
|
|
|
3558
4186
|
}
|
|
3559
4187
|
export {
|
|
3560
4188
|
AllProvidersFailedError,
|
|
4189
|
+
LLM2 as AnthropicLLM,
|
|
3561
4190
|
STT5 as AssemblyAISTT,
|
|
3562
4191
|
AuthenticationError,
|
|
3563
4192
|
BackgroundAudioPlayer,
|
|
@@ -3565,6 +4194,7 @@ export {
|
|
|
3565
4194
|
CallMetricsAccumulator,
|
|
3566
4195
|
STT3 as CartesiaSTT,
|
|
3567
4196
|
TTS3 as CartesiaTTS,
|
|
4197
|
+
LLM4 as CerebrasLLM,
|
|
3568
4198
|
ChatContext,
|
|
3569
4199
|
CloudflareTunnel,
|
|
3570
4200
|
DEFAULT_MIN_SENTENCE_LEN,
|
|
@@ -3578,11 +4208,14 @@ export {
|
|
|
3578
4208
|
GEMINI_DEFAULT_INPUT_SR,
|
|
3579
4209
|
GEMINI_DEFAULT_OUTPUT_SR,
|
|
3580
4210
|
GeminiLiveAdapter,
|
|
4211
|
+
LLM5 as GoogleLLM,
|
|
4212
|
+
LLM3 as GroqLLM,
|
|
3581
4213
|
Guardrail,
|
|
3582
4214
|
IVRActivity,
|
|
3583
4215
|
LLMLoop,
|
|
3584
4216
|
TTS5 as LMNTTTS,
|
|
3585
4217
|
MetricsStore,
|
|
4218
|
+
LLM as OpenAILLM,
|
|
3586
4219
|
OpenAILLMProvider,
|
|
3587
4220
|
Realtime as OpenAIRealtime,
|
|
3588
4221
|
OpenAIRealtimeAdapter,
|