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/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-AFUYSNDH.mjs";
4
4
  import {
5
5
  startTunnel
6
- } from "./chunk-JO5C35FM.mjs";
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-757NVN4L.mjs";
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-BL7A7GXW.mjs");
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-YFOL2HYH.mjs");
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
- alloy: "21m00Tcm4TlvDq8ikWAM"
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 = "21m00Tcm4TlvDq8ikWAM", modelId = "eleven_turbo_v2_5", outputFormat = "pcm_16000") {
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 ?? "21m00Tcm4TlvDq8ikWAM",
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,