@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.
- package/README.md +65 -0
- package/build.mjs +204 -0
- package/package.json +29 -0
- package/skills/pinecall-concepts/SKILL.md +41 -0
- package/skills/pinecall-concepts/references/concepts/agents-and-channels.md +155 -0
- package/skills/pinecall-concepts/references/concepts/deployment-topologies.md +120 -0
- package/skills/pinecall-concepts/references/concepts/hot-reload.md +119 -0
- package/skills/pinecall-concepts/references/concepts/philosophy.md +100 -0
- package/skills/pinecall-concepts/references/concepts/server-vs-client-llm.md +119 -0
- package/skills/pinecall-examples/SKILL.md +59 -0
- package/skills/pinecall-examples/references/examples/browser-widget.md +206 -0
- package/skills/pinecall-examples/references/examples/chat-bot.md +184 -0
- package/skills/pinecall-examples/references/examples/headless-agent.md +121 -0
- package/skills/pinecall-examples/references/examples/index.md +183 -0
- package/skills/pinecall-examples/references/examples/multi-channel-bot.md +173 -0
- package/skills/pinecall-examples/references/examples/outbound-dispatch.md +109 -0
- package/skills/pinecall-examples/references/examples/turn-detection.md +150 -0
- package/skills/pinecall-guides/SKILL.md +68 -0
- package/skills/pinecall-guides/references/guides/call-ringing.md +149 -0
- package/skills/pinecall-guides/references/guides/conversation-history.md +377 -0
- package/skills/pinecall-guides/references/guides/dev-mode.md +130 -0
- package/skills/pinecall-guides/references/guides/events.md +677 -0
- package/skills/pinecall-guides/references/guides/human-takeover.md +184 -0
- package/skills/pinecall-guides/references/guides/inbound-voice.md +201 -0
- package/skills/pinecall-guides/references/guides/knowledge-bases.md +166 -0
- package/skills/pinecall-guides/references/guides/live-listening.md +199 -0
- package/skills/pinecall-guides/references/guides/multi-tenant.md +158 -0
- package/skills/pinecall-guides/references/guides/outbound-calls.md +279 -0
- package/skills/pinecall-guides/references/guides/sse-streaming.md +207 -0
- package/skills/pinecall-guides/references/guides/testing-agents.md +272 -0
- package/skills/pinecall-guides/references/guides/tools-and-functions.md +254 -0
- package/skills/pinecall-guides/references/guides/webrtc-browser.md +200 -0
- package/skills/pinecall-guides/references/guides/whatsapp.md +370 -0
- package/skills/pinecall-guides/references/guides/ws-streaming.md +235 -0
- package/skills/pinecall-quickstart/SKILL.md +54 -0
- package/skills/pinecall-quickstart/references/index.md +123 -0
- package/skills/pinecall-quickstart/references/quickstart.md +185 -0
- package/skills/pinecall-reference/SKILL.md +43 -0
- package/skills/pinecall-reference/references/reference/cli.md +578 -0
- package/skills/pinecall-reference/references/reference/events.md +366 -0
- package/skills/pinecall-reference/references/reference/llm-providers.md +263 -0
- package/skills/pinecall-reference/references/reference/rest-api.md +122 -0
- package/skills/pinecall-reference/references/reference/session-limits.md +119 -0
- package/skills/pinecall-reference/references/reference/stt-providers.md +174 -0
- package/skills/pinecall-reference/references/reference/tts-providers.md +149 -0
- package/skills/pinecall-sdk-api/SKILL.md +56 -0
- package/skills/pinecall-sdk-api/references/api/agent.md +328 -0
- package/skills/pinecall-sdk-api/references/api/call.md +324 -0
- package/skills/pinecall-sdk-api/references/api/pinecall.md +186 -0
- package/skills/pinecall-sdk-api/references/api/reply-stream.md +148 -0
- package/skills/pinecall-security/SKILL.md +37 -0
- package/skills/pinecall-security/references/security.md +138 -0
- package/skills/pinecall-web-chat/SKILL.md +38 -0
- package/skills/pinecall-web-chat/references/web/chat/chat-session.md +178 -0
- package/skills/pinecall-web-chat/references/web/chat/overview.md +98 -0
- package/skills/pinecall-web-components/SKILL.md +37 -0
- package/skills/pinecall-web-components/references/web/components/overview.md +128 -0
- package/skills/pinecall-web-voice/SKILL.md +40 -0
- package/skills/pinecall-web-voice/references/web/core/datachannel-protocol.md +149 -0
- package/skills/pinecall-web-voice/references/web/core/overview.md +70 -0
- package/skills/pinecall-web-voice/references/web/core/state-and-phases.md +153 -0
- package/skills/pinecall-web-voice/references/web/core/voice-session.md +279 -0
- package/skills/pinecall-web-widget/SKILL.md +41 -0
- package/skills/pinecall-web-widget/references/web/widget/overview.md +67 -0
- package/skills/pinecall-web-widget/references/web/widget/props.md +291 -0
- package/skills/pinecall-web-widget/references/web/widget/theming.md +131 -0
- package/skills/pinecall-web-widget/references/web/widget/tools-api.md +381 -0
- 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)
|