getpatter 0.5.0 → 0.5.1

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