@pinecall/skills 0.1.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.
Files changed (68) hide show
  1. package/README.md +65 -0
  2. package/build.mjs +204 -0
  3. package/package.json +29 -0
  4. package/skills/pinecall-concepts/SKILL.md +41 -0
  5. package/skills/pinecall-concepts/references/concepts/agents-and-channels.md +155 -0
  6. package/skills/pinecall-concepts/references/concepts/deployment-topologies.md +120 -0
  7. package/skills/pinecall-concepts/references/concepts/hot-reload.md +119 -0
  8. package/skills/pinecall-concepts/references/concepts/philosophy.md +100 -0
  9. package/skills/pinecall-concepts/references/concepts/server-vs-client-llm.md +119 -0
  10. package/skills/pinecall-examples/SKILL.md +59 -0
  11. package/skills/pinecall-examples/references/examples/browser-widget.md +206 -0
  12. package/skills/pinecall-examples/references/examples/chat-bot.md +184 -0
  13. package/skills/pinecall-examples/references/examples/headless-agent.md +121 -0
  14. package/skills/pinecall-examples/references/examples/index.md +183 -0
  15. package/skills/pinecall-examples/references/examples/multi-channel-bot.md +173 -0
  16. package/skills/pinecall-examples/references/examples/outbound-dispatch.md +109 -0
  17. package/skills/pinecall-examples/references/examples/turn-detection.md +150 -0
  18. package/skills/pinecall-guides/SKILL.md +68 -0
  19. package/skills/pinecall-guides/references/guides/call-ringing.md +149 -0
  20. package/skills/pinecall-guides/references/guides/conversation-history.md +377 -0
  21. package/skills/pinecall-guides/references/guides/dev-mode.md +130 -0
  22. package/skills/pinecall-guides/references/guides/events.md +677 -0
  23. package/skills/pinecall-guides/references/guides/human-takeover.md +184 -0
  24. package/skills/pinecall-guides/references/guides/inbound-voice.md +201 -0
  25. package/skills/pinecall-guides/references/guides/knowledge-bases.md +166 -0
  26. package/skills/pinecall-guides/references/guides/live-listening.md +199 -0
  27. package/skills/pinecall-guides/references/guides/multi-tenant.md +158 -0
  28. package/skills/pinecall-guides/references/guides/outbound-calls.md +279 -0
  29. package/skills/pinecall-guides/references/guides/sse-streaming.md +207 -0
  30. package/skills/pinecall-guides/references/guides/testing-agents.md +272 -0
  31. package/skills/pinecall-guides/references/guides/tools-and-functions.md +254 -0
  32. package/skills/pinecall-guides/references/guides/webrtc-browser.md +200 -0
  33. package/skills/pinecall-guides/references/guides/whatsapp.md +370 -0
  34. package/skills/pinecall-guides/references/guides/ws-streaming.md +235 -0
  35. package/skills/pinecall-quickstart/SKILL.md +54 -0
  36. package/skills/pinecall-quickstart/references/index.md +123 -0
  37. package/skills/pinecall-quickstart/references/quickstart.md +185 -0
  38. package/skills/pinecall-reference/SKILL.md +43 -0
  39. package/skills/pinecall-reference/references/reference/cli.md +578 -0
  40. package/skills/pinecall-reference/references/reference/events.md +366 -0
  41. package/skills/pinecall-reference/references/reference/llm-providers.md +263 -0
  42. package/skills/pinecall-reference/references/reference/rest-api.md +122 -0
  43. package/skills/pinecall-reference/references/reference/session-limits.md +119 -0
  44. package/skills/pinecall-reference/references/reference/stt-providers.md +174 -0
  45. package/skills/pinecall-reference/references/reference/tts-providers.md +149 -0
  46. package/skills/pinecall-sdk-api/SKILL.md +56 -0
  47. package/skills/pinecall-sdk-api/references/api/agent.md +328 -0
  48. package/skills/pinecall-sdk-api/references/api/call.md +324 -0
  49. package/skills/pinecall-sdk-api/references/api/pinecall.md +186 -0
  50. package/skills/pinecall-sdk-api/references/api/reply-stream.md +148 -0
  51. package/skills/pinecall-security/SKILL.md +37 -0
  52. package/skills/pinecall-security/references/security.md +138 -0
  53. package/skills/pinecall-web-chat/SKILL.md +38 -0
  54. package/skills/pinecall-web-chat/references/web/chat/chat-session.md +178 -0
  55. package/skills/pinecall-web-chat/references/web/chat/overview.md +98 -0
  56. package/skills/pinecall-web-components/SKILL.md +37 -0
  57. package/skills/pinecall-web-components/references/web/components/overview.md +128 -0
  58. package/skills/pinecall-web-voice/SKILL.md +40 -0
  59. package/skills/pinecall-web-voice/references/web/core/datachannel-protocol.md +149 -0
  60. package/skills/pinecall-web-voice/references/web/core/overview.md +70 -0
  61. package/skills/pinecall-web-voice/references/web/core/state-and-phases.md +153 -0
  62. package/skills/pinecall-web-voice/references/web/core/voice-session.md +279 -0
  63. package/skills/pinecall-web-widget/SKILL.md +41 -0
  64. package/skills/pinecall-web-widget/references/web/widget/overview.md +67 -0
  65. package/skills/pinecall-web-widget/references/web/widget/props.md +291 -0
  66. package/skills/pinecall-web-widget/references/web/widget/theming.md +131 -0
  67. package/skills/pinecall-web-widget/references/web/widget/tools-api.md +381 -0
  68. package/skills/pinecall-web-widget/references/web/widget/use-voice-session-hook.md +130 -0
@@ -0,0 +1,366 @@
1
+ ---
2
+ title: "Events"
3
+ description: "Every event the SDK emits, with payload shapes and timing."
4
+ ---
5
+
6
+ # Events
7
+
8
+ This is the complete catalog of events. Subscribe via `agent.on(event, handler)`. All call-scoped events include the `Call` as the final argument.
9
+
10
+ ## Real-time flow
11
+
12
+ This is the order events fire during a typical exchange:
13
+
14
+ ```
15
+ User speaks → speech.started
16
+ → user.speaking (interim, fires multiple times)
17
+ → speech.ended
18
+ → user.message (final confirmed text)
19
+ → eager.turn / turn.end
20
+
21
+ Bot responds → bot.speaking (message ID assigned)
22
+ → bot.word (word-by-word as TTS plays)
23
+ → bot.finished (done speaking)
24
+
25
+ Interruption → bot.interrupted
26
+ → turn.continued (active ReplyStreams auto-aborted)
27
+ ```
28
+
29
+ ## Lifecycle events
30
+
31
+ ### `call.started`
32
+
33
+ ```typescript
34
+ agent.on("call.started", (call: Call) => { });
35
+ ```
36
+
37
+ A new **voice** call connected (phone or WebRTC). The `Call` object is partially populated — `id`, `from`, `to`, `direction`, `transport`, `metadata` are available. `duration`, `endedAt`, `reason` are not yet.
38
+
39
+ > **Note:** `call.started` fires only for voice transports (`phone`, `webrtc`). For chat and WhatsApp, use `chat.started` and `whatsapp.started` instead.
40
+
41
+ ### `chat.started`
42
+
43
+ ```typescript
44
+ agent.on("chat.started", (call: Call) => { });
45
+ ```
46
+
47
+ A new chat session started. Receives the same `Call` object, with `call.transport === "chat"`. Use `setPromptVars()`, `addContext()`, and all other Call methods as usual.
48
+
49
+ ### `whatsapp.started`
50
+
51
+ ```typescript
52
+ agent.on("whatsapp.started", (call: Call, session: WhatsAppSession) => { });
53
+ ```
54
+
55
+ A new WhatsApp session started (first message from a new contact). Receives both:
56
+ - `call` — the universal `Call` object for `setPromptVars()`, `addContext()`, etc.
57
+ - `session` — a `WhatsAppSession` with `contactPhone`, `contactName`, and history methods.
58
+
59
+ ### `call.preparing`
60
+
61
+ ```typescript
62
+ agent.on("call.preparing", (call: Call) => { });
63
+ ```
64
+
65
+ Fires before **every** LLM generation — voice, chat, and WhatsApp. Use it to refresh per-call variables that need to be current for every turn:
66
+
67
+ ```typescript
68
+ agent.on("call.preparing", (call) => {
69
+ call.setPromptVars({
70
+ date_block: buildFreshDate(),
71
+ format_rules: call.transport === "phone" ? VOICE_FORMAT : CHAT_FORMAT,
72
+ });
73
+ });
74
+ ```
75
+
76
+ The server waits briefly (~150ms) for your handler to call `setPromptVars()` before proceeding with the LLM call. This runs just-in-time, so variables are always fresh — even in long-lived WhatsApp sessions.
77
+
78
+ ### `call.ended`
79
+
80
+ ```typescript
81
+ agent.on("call.ended", (call: Call, reason: string) => { });
82
+ ```
83
+
84
+ The call ended. The `Call` is now fully populated, including `duration`, `endedAt`, `messages`, and `transcript`.
85
+
86
+ `reason` values: `hangup`, `timeout`, `idle_timeout`, `max_duration`, `no_answer`, `busy`, `failed`.
87
+
88
+ ## User speech events
89
+
90
+ ### `speech.started` / `speech.ended`
91
+
92
+ ```typescript
93
+ agent.on("speech.started", (event, call: Call) => { });
94
+ agent.on("speech.ended", (event, call: Call) => { });
95
+ ```
96
+
97
+ VAD-level events: fire when the audio energy crosses the speech threshold.
98
+
99
+ ### `user.speaking`
100
+
101
+ ```typescript
102
+ agent.on("user.speaking", (event: { text: string }, call: Call) => { });
103
+ ```
104
+
105
+ Interim STT transcript. Fires multiple times as the STT engine refines its guess.
106
+
107
+ ### `user.message`
108
+
109
+ ```typescript
110
+ agent.on("user.message", (event: { text: string; messageId: string }, call: Call) => { });
111
+ ```
112
+
113
+ Final confirmed user text. After this fires, `eager.turn` or `turn.end` follows shortly.
114
+
115
+ ## Turn events
116
+
117
+ ### `eager.turn`
118
+
119
+ ```typescript
120
+ agent.on("eager.turn", (turn: { text: string; probability: number }, call: Call) => { });
121
+ ```
122
+
123
+ Early signal that the user *probably* finished a turn. Use for low-latency responses — start the LLM, but be ready to abort if `turn.continued` fires.
124
+
125
+ ### `turn.end`
126
+
127
+ ```typescript
128
+ agent.on("turn.end", (turn: { text: string; probability: number }, call: Call) => { });
129
+ ```
130
+
131
+ Final turn signal. Higher confidence than `eager.turn`. This is where most apps trigger the LLM.
132
+
133
+ ### `turn.continued`
134
+
135
+ ```typescript
136
+ agent.on("turn.continued", (event, call: Call) => { });
137
+ ```
138
+
139
+ The user kept talking after a turn signal. Any active `ReplyStream` auto-aborts. Your handler doesn't need to do anything — just don't be surprised when the stream stops.
140
+
141
+ ## Bot speech events
142
+
143
+ Bot speech follows this lifecycle:
144
+
145
+ ```
146
+ bot.speaking → bot.word × N → bot.finished (completed normally)
147
+ bot.interrupted (user barged in)
148
+ message.confirmed (full text saved to history)
149
+ ```
150
+
151
+ `call.currentBotText` accumulates `bot.word` events into a live preview string.
152
+ It resets on each new `bot.speaking` and clears after `bot.finished` / `bot.interrupted`.
153
+
154
+ ### `bot.speaking`
155
+
156
+ ```typescript
157
+ agent.on("bot.speaking", (event: { messageId: string; text: string }, call: Call) => { });
158
+ ```
159
+
160
+ The bot started speaking a message. `messageId` lets you track this specific utterance.
161
+
162
+ `text` contains the full response text for non-streaming replies (`call.say()`, `call.reply()`). For streaming replies (`call.replyStream()`), `text` is empty because tokens arrive incrementally — use `bot.word` events or `call.currentBotText` to track what the bot is saying.
163
+
164
+ ### `bot.word`
165
+
166
+ ```typescript
167
+ agent.on("bot.word", (event: { messageId: string; word: string }, call: Call) => { });
168
+ ```
169
+
170
+ A word was just played by TTS — synchronized with the actual audio playback. Use for live captions, subtitles, or transcript UIs.
171
+
172
+ Each `bot.word` is automatically accumulated into `call.currentBotText`:
173
+
174
+ ```typescript
175
+ // Live preview — grows word-by-word as the bot speaks
176
+ agent.on("bot.word", (event, call) => {
177
+ console.log(`🗣 "${call.currentBotText}"`);
178
+ // "¡Hola!"
179
+ // "¡Hola! Estoy"
180
+ // "¡Hola! Estoy bien,"
181
+ // "¡Hola! Estoy bien, gracias."
182
+ });
183
+ ```
184
+
185
+ > **Note:** `bot.word` timing is aligned with TTS audio. If the bot says a 5-second sentence, words arrive spread across those 5 seconds — not all at once.
186
+
187
+ ### `bot.finished`
188
+
189
+ ```typescript
190
+ agent.on("bot.finished", (event: { messageId: string; durationMs: number }, call: Call) => { });
191
+ ```
192
+
193
+ The bot finished speaking. TTS audio fully played. `call.currentBotText` still contains the accumulated words during this handler — it clears immediately after.
194
+
195
+ ```typescript
196
+ agent.on("bot.finished", (event, call) => {
197
+ console.log(`Done (${event.durationMs}ms): "${call.currentBotText}"`);
198
+ });
199
+ ```
200
+
201
+ ### `bot.interrupted`
202
+
203
+ ```typescript
204
+ agent.on("bot.interrupted", (event: { messageId: string; playedMs: number; reason: string }, call: Call) => { });
205
+ ```
206
+
207
+ The user cut off the bot mid-speech. `call.currentBotText` shows what the bot managed to say before being interrupted.
208
+
209
+ ```typescript
210
+ agent.on("bot.interrupted", (event, call) => {
211
+ console.log(`Interrupted after ${event.playedMs}ms, said: "${call.currentBotText}"`);
212
+ });
213
+ ```
214
+
215
+ ## Protocol events
216
+
217
+ ### `message.confirmed`
218
+
219
+ ```typescript
220
+ agent.on("message.confirmed", (event: { messageId: string }, call: Call) => { });
221
+ ```
222
+
223
+ The server acknowledged a bot message you sent (via `say`, `reply`, or `replyStream`).
224
+
225
+ ### `llm.toolCall`
226
+
227
+ ```typescript
228
+ agent.on("llm.toolCall", (data: {
229
+ msgId: string;
230
+ toolCalls: Array<{ id: string; name: string; arguments: string }>;
231
+ }, call: Call) => { });
232
+ ```
233
+
234
+ The server-side LLM is requesting one or more tool calls. If you defined tools with `tool()`, the SDK auto-executes them and sends results via `call.toolResult()`. This event still fires — use it for logging, metrics, or UI updates.
235
+
236
+ See [Tools and Functions](/guides/tools-and-functions).
237
+
238
+ ### `session.idleWarning`
239
+
240
+ ```typescript
241
+ agent.on("session.idleWarning", (event: {
242
+ remainingSeconds: number;
243
+ idleTimeoutSeconds: number;
244
+ }, call: Call) => { });
245
+ ```
246
+
247
+ Fires before idle timeout. The user hasn't spoken in a while. Use it to prompt them.
248
+
249
+ ```typescript
250
+ agent.on("session.idleWarning", (event, call) => {
251
+ call.say("Are you still there?");
252
+ });
253
+ ```
254
+
255
+ ### `session.timeout`
256
+
257
+ ```typescript
258
+ agent.on("session.timeout", (event: {
259
+ reason: "max_duration" | "idle_timeout";
260
+ }, call: Call) => { });
261
+ ```
262
+
263
+ A session limit hit. The call is about to end.
264
+
265
+ ## WhatsApp events
266
+
267
+ ### `whatsapp.message`
268
+
269
+ ```typescript
270
+ agent.on("whatsapp.message", (event: {
271
+ sessionId: string;
272
+ from: string;
273
+ name: string;
274
+ type: "text" | "audio" | "image" | "video" | "document";
275
+ text: string;
276
+ messageId: string;
277
+ paused: boolean; // true when agent is paused (human-in-the-loop)
278
+ }) => { });
279
+ ```
280
+
281
+ Incoming WhatsApp message. For voice notes (`type: "audio"`), `text` is the transcript.
282
+
283
+ When `paused` is `true`, the AI did not respond — a human should handle this message via `agent.sendMessage()`.
284
+
285
+ ### `whatsapp.response`
286
+
287
+ ```typescript
288
+ agent.on("whatsapp.response", (event: {
289
+ sessionId: string;
290
+ to: string;
291
+ text: string;
292
+ source?: "human"; // present when sent by human via agent.sendMessage()
293
+ }) => { });
294
+ ```
295
+
296
+ The agent sent a WhatsApp response. When `source` is `"human"`, the message was sent by a human operator (not the AI).
297
+
298
+ ### `whatsapp.status`
299
+
300
+ ```typescript
301
+ agent.on("whatsapp.status", (event: {
302
+ status: "sent" | "delivered" | "read";
303
+ recipient: string;
304
+ messageId: string;
305
+ }) => { });
306
+ ```
307
+
308
+ Delivery status update from Meta.
309
+
310
+ ## Human-in-the-loop events
311
+
312
+ ### `session.paused`
313
+
314
+ ```typescript
315
+ agent.on("session.paused", (event: {
316
+ sessionId?: string; // set for session-level pause
317
+ contact?: string; // set for contact-level pause
318
+ // both undefined = global pause
319
+ }) => { });
320
+ ```
321
+
322
+ Confirmation that the agent was paused. Fires after `agent.pause()`.
323
+
324
+ ### `session.resumed`
325
+
326
+ ```typescript
327
+ agent.on("session.resumed", (event: {
328
+ sessionId?: string;
329
+ contact?: string;
330
+ }) => { });
331
+ ```
332
+
333
+ Confirmation that the agent was resumed. Fires after `agent.resume()`.
334
+
335
+ ## Audio metrics
336
+
337
+ When you enable `analysis.send_audio_metrics`:
338
+
339
+ ```typescript
340
+ agent.on("audio.metrics", (event: {
341
+ source: "user" | "bot";
342
+ energyDb: number; // -60 to 0
343
+ rms: number; // 0–1
344
+ peak: number; // 0–1
345
+ isSpeech: boolean;
346
+ vadProb: number; // 0–1
347
+ }, call: Call) => { });
348
+ ```
349
+
350
+ Use for live waveform UIs, energy meters, or VAD visualization.
351
+
352
+ ## SSE events
353
+
354
+ When streamed over SSE (via `pc.stream()` or `agent.stream()`), each event has an `event:` field and a JSON `data:` body with `agent` ID:
355
+
356
+ ```
357
+ event: user.message
358
+ data: {"callId":"CA123","text":"Hello","messageId":"msg_abc","agent":"mara"}
359
+ ```
360
+
361
+ A `:ping` comment is sent every 30s as keepalive.
362
+
363
+ ## What's next
364
+
365
+ - [`Call` API reference](/api/call) — methods to call in response to events
366
+ - [Multi-tenant](/guides/multi-tenant) — scope SSE event streams
@@ -0,0 +1,263 @@
1
+ ---
2
+ title: "LLM Providers"
3
+ description: "Server-side LLM providers and configuration."
4
+ ---
5
+
6
+ # LLM Providers
7
+
8
+ When using server-side LLM (the recommended path for most agents), the server runs the LLM and streams responses directly through TTS. Configure it via the `llm` and `prompt` fields on the agent.
9
+
10
+ For client-side LLMs, see [ReplyStream](/api/reply-stream).
11
+
12
+ ## Quick start
13
+
14
+ ```typescript
15
+ const agent = pc.agent("my-bot", {
16
+ voice: "elevenlabs/sarah",
17
+ stt: "deepgram/flux",
18
+ llm: "openai/gpt-5-chat-latest",
19
+ prompt: "You are a friendly assistant. Keep responses short.",
20
+ });
21
+ ```
22
+
23
+ The `llm` shortcut takes the `provider/model` format. `prompt` is a top-level field — no need to nest it inside an object.
24
+
25
+ ## Shortcut format
26
+
27
+ ```typescript
28
+ // Recommended: provider/model
29
+ llm: "openai/gpt-5-chat-latest"
30
+
31
+ // Bare model name (assumes OpenAI)
32
+ llm: "gpt-5-chat-latest"
33
+
34
+ // Both expand to:
35
+ // { provider: "openai", model: "gpt-5-chat-latest", enabled: true }
36
+ ```
37
+
38
+ > The legacy `provider:model` format (e.g. `"openai:gpt-5-chat-latest"`) still works but is not recommended.
39
+
40
+ ## Tuning with a full config object
41
+
42
+ For `temperature`, `max_tokens`, and other tuning parameters, use the full config object:
43
+
44
+ ```typescript
45
+ const agent = pc.agent("my-bot", {
46
+ voice: "elevenlabs/sarah",
47
+ stt: "deepgram/flux",
48
+ llm: {
49
+ provider: "openai",
50
+ llm: "openai/gpt-5-chat-latest",
51
+ enabled: true,
52
+ temperature: 0.3, // 0-2. Lower = more deterministic
53
+ max_tokens: 256, // caps response length
54
+ },
55
+ prompt: "You are a customer support agent. Be concise.",
56
+ });
57
+ ```
58
+
59
+ > **Tip:** `prompt` stays top-level even when using the full `llm` object. The server merges them. You can also put `prompt` inside the `llm` object — both work.
60
+
61
+ ## OpenAI
62
+
63
+ ```typescript
64
+ llm: "openai/gpt-5-chat-latest"
65
+ ```
66
+
67
+ Or with tuning:
68
+
69
+ ```typescript
70
+ llm: {
71
+ provider: "openai",
72
+ llm: "openai/gpt-5-chat-latest",
73
+ enabled: true,
74
+ temperature: 0.7,
75
+ max_tokens: 512,
76
+ }
77
+ ```
78
+
79
+ **Model picker:**
80
+
81
+ | Model | Best for |
82
+ |---|---|
83
+ | `gpt-5-chat-latest` | Most agents — strong reasoning, good cost (recommended default) |
84
+ | `gpt-5-chat-mini` | Highest-volume, simple flows; lowest cost |
85
+
86
+ ## Mistral
87
+
88
+ ```typescript
89
+ llm: "mistral/mistral-medium"
90
+ ```
91
+
92
+ Or with tuning:
93
+
94
+ ```typescript
95
+ llm: {
96
+ provider: "mistral",
97
+ model: "mistral-medium",
98
+ enabled: true,
99
+ temperature: 0.7,
100
+ max_tokens: 512,
101
+ }
102
+ ```
103
+
104
+ ## Google (Gemini)
105
+
106
+ ```typescript
107
+ llm: "google/gemini-2.0-flash"
108
+ ```
109
+
110
+ Or with tuning:
111
+
112
+ ```typescript
113
+ llm: {
114
+ provider: "google",
115
+ model: "gemini-2.0-flash",
116
+ enabled: true,
117
+ temperature: 0.7,
118
+ max_tokens: 512,
119
+ }
120
+ ```
121
+
122
+ > `gemini` is accepted as an alias for `google` (e.g. `llm: "gemini/gemini-2.5-flash"`).
123
+
124
+ **Model picker:**
125
+
126
+ | Model | Best for |
127
+ |---|---|
128
+ | `gemini-2.0-flash` | Most voice agents — fast and low cost (recommended default) |
129
+ | `gemini-2.5-flash` | Stronger reasoning at a modest cost bump |
130
+
131
+ ## Anthropic
132
+
133
+ ```typescript
134
+ llm: "anthropic/claude-haiku-4-5"
135
+ ```
136
+
137
+ Or with tuning:
138
+
139
+ ```typescript
140
+ llm: {
141
+ provider: "anthropic",
142
+ model: "claude-haiku-4-5",
143
+ enabled: true,
144
+ temperature: 0.7,
145
+ max_tokens: 512,
146
+ }
147
+ ```
148
+
149
+ > `claude` is accepted as an alias for `anthropic` (e.g. `llm: "claude/claude-sonnet-4-6"`).
150
+
151
+ **Model picker:**
152
+
153
+ | Model | Best for |
154
+ |---|---|
155
+ | `claude-haiku-4-5` | Most voice agents — fast and low cost (recommended default) |
156
+ | `claude-sonnet-4-6` | Higher reasoning quality when latency/cost matter less |
157
+
158
+ > Opus is intentionally **not** offered for voice agents — it's the premium tier (too slow/costly for real-time). Sonnet 4.6 and Haiku 4.5 are the supported Anthropic models. Set your `ANTHROPIC_API_KEY` on the server (managed) or add an Anthropic credential to your org (BYOK).
159
+
160
+ ## The `enabled` field
161
+
162
+ `enabled: false` disables server-side LLM for this agent. The server still does STT and TTS, but it won't generate responses — you handle every `turn.end` yourself with a client-side LLM.
163
+
164
+ ```typescript
165
+ // Server-side off — bring your own LLM
166
+ const agent = pc.agent("my-bot", {
167
+ voice: "elevenlabs/sarah",
168
+ language: "en",
169
+ // no llm field — or llm: { provider: "openai", enabled: false }
170
+ });
171
+
172
+ agent.on("turn.end", async (turn, call) => {
173
+ const stream = call.replyStream(turn);
174
+ // ... your LLM here
175
+ });
176
+ ```
177
+
178
+ ## Prompt template variables
179
+
180
+ Define a prompt with `{{placeholders}}`. The server resolves them before each LLM request. Built-in: `{{date}}`, `{{time}}`.
181
+
182
+ ```typescript
183
+ const agent = pc.agent("support-bot", {
184
+ voice: "elevenlabs/sarah",
185
+ stt: "deepgram/flux",
186
+ llm: "openai/gpt-5-chat-latest",
187
+ prompt: `You are {{agent_name}}, support agent at {{company}}.
188
+ Today is {{date}}. Customer: {{customer_name}}.`,
189
+ });
190
+ ```
191
+
192
+ Set values per-call:
193
+
194
+ ```typescript
195
+ agent.on("call.started", async (call) => {
196
+ await call.setPromptVars({
197
+ agent_name: "Nova",
198
+ company: "Acme",
199
+ customer_name: "Maria",
200
+ });
201
+ });
202
+ ```
203
+
204
+ See [Hot-Reload](/concepts/hot-reload) for the full pattern.
205
+
206
+ ## Temperature & max_tokens
207
+
208
+ Standard parameters supported by all providers:
209
+
210
+ - `temperature` — 0–2. Lower = more deterministic. For voice agents, `0.3–0.7` is typical.
211
+ - `max_tokens` — caps response length. For voice, keep it short — `256–512` is common to avoid long monologues.
212
+
213
+ ```typescript
214
+ // Short, deterministic answers (IVR, routing)
215
+ llm: { provider: "openai", model: "gpt-5-chat-mini", temperature: 0.2, max_tokens: 128 }
216
+
217
+ // Natural conversation
218
+ llm: { provider: "openai", model: "gpt-5-chat-latest", temperature: 0.7, max_tokens: 512 }
219
+
220
+ // Creative, open-ended
221
+ llm: { provider: "openai", model: "gpt-5-chat-latest", temperature: 1.0, max_tokens: 1024 }
222
+ ```
223
+
224
+ ## Tools
225
+
226
+ Define tools with `tool()` and Zod schemas. The SDK auto-converts them to the OpenAI function-calling wire format and auto-executes them:
227
+
228
+ ```typescript
229
+ import { tool } from "@pinecall/sdk";
230
+ import { z } from "zod";
231
+
232
+ const lookupOrder = tool({
233
+ name: "lookupOrder",
234
+ description: "Look up an order by ID",
235
+ schema: z.object({ orderId: z.string() }),
236
+ execute: async ({ orderId }) => await db.orders.findOne(orderId),
237
+ });
238
+
239
+ // Pass to agent config
240
+ tools: [lookupOrder],
241
+ ```
242
+
243
+ See [Tools and Functions](/guides/tools-and-functions) for the full pattern.
244
+
245
+ ## Hot-reloading the LLM
246
+
247
+ Swap models or providers at runtime:
248
+
249
+ ```typescript
250
+ // Agent-wide (all future calls)
251
+ agent.update({ llm: "openai/gpt-5-chat-latest" });
252
+
253
+ // One call only
254
+ call.update({ llm: "mistral/mistral-medium" });
255
+ ```
256
+
257
+ This is useful for A/B testing different models, or upgrading the model for VIP callers without redeploying.
258
+
259
+ ## What's next
260
+
261
+ - [Server-side vs client-side LLM](/concepts/server-vs-client-llm)
262
+ - [Tools and Functions](/guides/tools-and-functions)
263
+ - [Hot-reload](/concepts/hot-reload)