pinecall-protocol 0.6.2__tar.gz → 0.6.14__tar.gz
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.
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/PKG-INFO +1 -1
- pinecall_protocol-0.6.14/pinecall_protocol/_version.py +4 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/commands.py +31 -3
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/defs.py +22 -4
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/events.py +119 -23
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/fixtures/__init__.py +1 -1
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/fixtures/call-log-golden.state.json +1 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/registry.py +46 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/rest.py +123 -1
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/state.py +13 -1
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/verbs.py +4 -3
- pinecall_protocol-0.6.2/pinecall_protocol/_version.py +0 -4
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/.gitignore +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/.python-version +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/LICENSE +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/README.md +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/__init__.py +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/_base.py +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/codec.py +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/envelope.py +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/fixtures/call-log-golden.json +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/metrics.py +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/py.typed +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/room.py +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pyproject.toml +0 -0
- {pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/uv.lock +0 -0
|
@@ -17,6 +17,10 @@ class AgentConfigure(WireModel):
|
|
|
17
17
|
config: AgentConfig
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
class AgentDrain(WireModel):
|
|
21
|
+
"""This socket is leaving, and its calls go on without it."""
|
|
22
|
+
|
|
23
|
+
|
|
20
24
|
# The gateway answers agent.registered, or error.
|
|
21
25
|
class AgentRegister(WireModel):
|
|
22
26
|
"""The app's first message: this socket speaks for this agent and answers these doors."""
|
|
@@ -46,6 +50,30 @@ class AgentSay(WireModel):
|
|
|
46
50
|
allow_interruptions: bool | None = None
|
|
47
51
|
|
|
48
52
|
|
|
53
|
+
# Ask for a person without sending the caller anywhere: the call waits on hold until a supervisor
|
|
54
|
+
# takes the line, or until wait_s passes with nobody taking it. attention.answered says which.
|
|
55
|
+
class CallAttention(WireModel):
|
|
56
|
+
"""Ask for a person without sending the caller anywhere."""
|
|
57
|
+
|
|
58
|
+
reason: str
|
|
59
|
+
wait_s: float
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Lands as callback.requested with via agent; placing the call is the app's.
|
|
63
|
+
class CallCallback(WireModel):
|
|
64
|
+
"""Write down that the caller wants to be called back."""
|
|
65
|
+
|
|
66
|
+
number: str
|
|
67
|
+
when: str | None = None
|
|
68
|
+
note: str | None = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class CallClaim(WireModel):
|
|
72
|
+
"""The code the caller said, to bind this call to the page that shows it."""
|
|
73
|
+
|
|
74
|
+
code: str = Field(pattern="^[0-9]{4}$")
|
|
75
|
+
|
|
76
|
+
|
|
49
77
|
# The new call's log opens with call.dialing; call.started follows when the far end answers.
|
|
50
78
|
class CallDial(WireModel):
|
|
51
79
|
"""Place an outbound call as this agent."""
|
|
@@ -95,12 +123,12 @@ class CallMute(WireModel):
|
|
|
95
123
|
"""Mute the agent: it keeps listening and thinking, produces no audio."""
|
|
96
124
|
|
|
97
125
|
|
|
98
|
-
# call.transferred says whether it worked.
|
|
126
|
+
# call.transferred says whether it worked, and which mode it was.
|
|
99
127
|
class CallTransfer(WireModel):
|
|
100
|
-
"""Send the caller to another number."""
|
|
128
|
+
"""Send the caller to another number, or bring that number into the call."""
|
|
101
129
|
|
|
102
130
|
to: str
|
|
103
|
-
mode: TransferMode
|
|
131
|
+
mode: TransferMode | None = None
|
|
104
132
|
|
|
105
133
|
|
|
106
134
|
class CallUnhold(WireModel):
|
|
@@ -27,13 +27,15 @@ type Env = Literal["production", "sandbox"]
|
|
|
27
27
|
# What a console may ask of the process standing in the agent's directory, relayed by the gateway: a
|
|
28
28
|
# written call to the class mounted there (chat), a simulated caller put on the class it holds, its
|
|
29
29
|
# goldens and a suite of them, its knowledge folder pushed or its golden asked, its memory goldens,
|
|
30
|
-
# a call promoted to a candidate file, the drift of
|
|
31
|
-
# broken run left on that disk. Everything else a
|
|
30
|
+
# the panel it draws beside a conversation (view), a call promoted to a candidate file, the drift of
|
|
31
|
+
# the last two windows, and the reproductions a broken run left on that disk. Everything else a
|
|
32
|
+
# console needs is a door of the gateway.
|
|
32
33
|
type DevVerb = Literal[
|
|
33
34
|
"chat.roster",
|
|
34
35
|
"chat.start",
|
|
35
36
|
"chat.say",
|
|
36
37
|
"chat.end",
|
|
38
|
+
"view.render",
|
|
37
39
|
"simulate.start",
|
|
38
40
|
"goldens.roster",
|
|
39
41
|
"goldens.run",
|
|
@@ -80,8 +82,9 @@ type EndedBy = Literal["caller", "agent", "supervisor", "platform"]
|
|
|
80
82
|
type ScoreVerdict = Literal["held", "broken", "deferred", "skipped"]
|
|
81
83
|
|
|
82
84
|
|
|
83
|
-
# Cold: the caller is sent on
|
|
84
|
-
# other side answers
|
|
85
|
+
# Cold: the caller is sent on with a REFER on their SIP leg and the call ends here. Warm: the number
|
|
86
|
+
# is dialled into the call's own room, the agent stays on the line until the other side answers and
|
|
87
|
+
# then falls silent; the call ends when either of them hangs up.
|
|
85
88
|
type TransferMode = Literal["cold", "warm"]
|
|
86
89
|
|
|
87
90
|
|
|
@@ -234,6 +237,7 @@ class DocSource(WireModel):
|
|
|
234
237
|
"""One chunk of the knowledge base that retrieval put in front of the model for this turn."""
|
|
235
238
|
|
|
236
239
|
id: str
|
|
240
|
+
base: str | None = None
|
|
237
241
|
path: str
|
|
238
242
|
heading: str | None = None
|
|
239
243
|
score: float
|
|
@@ -309,6 +313,8 @@ class TurnConfig(WireModel):
|
|
|
309
313
|
|
|
310
314
|
min_interruption_words: int | None = None
|
|
311
315
|
endpointing_ms: int | None = None
|
|
316
|
+
eot_threshold: float | None = None
|
|
317
|
+
eager_eot_threshold: float | None = None
|
|
312
318
|
|
|
313
319
|
|
|
314
320
|
# How the voice says one word it would otherwise get wrong: a proper name, a brand, a street.
|
|
@@ -327,6 +333,16 @@ class StateFieldSpec(WireModel):
|
|
|
327
333
|
visibility: Visibility
|
|
328
334
|
|
|
329
335
|
|
|
336
|
+
# The panel the agent draws beside a conversation: it is declared here so a console knows the agent
|
|
337
|
+
# has one before it asks for it, and draws its own about the contact when it has not. What the panel
|
|
338
|
+
# CONTAINS never comes this way — it is asked for a conversation at a time, through the view.render
|
|
339
|
+
# dev verb.
|
|
340
|
+
class ViewSpec(WireModel):
|
|
341
|
+
"""The panel the agent draws beside a conversation."""
|
|
342
|
+
|
|
343
|
+
name: str
|
|
344
|
+
|
|
345
|
+
|
|
330
346
|
# An event nobody declared is refused before it touches the log.
|
|
331
347
|
class EventSpec(WireModel):
|
|
332
348
|
"""One outside event the agent accepts, and from whom."""
|
|
@@ -404,7 +420,9 @@ class AgentConfig(WireModel):
|
|
|
404
420
|
docs: DocsConfig | None = None
|
|
405
421
|
memory: MemoryConfig | None = None
|
|
406
422
|
hangup: HangupConfig | None = None
|
|
423
|
+
record: bool = True
|
|
407
424
|
tools: list[ToolSpec] | None = None
|
|
408
425
|
uses_knowledge: bool = False
|
|
409
426
|
state_fields: list[StateFieldSpec] | None = None
|
|
427
|
+
view: ViewSpec | None = None
|
|
410
428
|
events: list[EventSpec] | None = None
|
|
@@ -17,6 +17,7 @@ from pinecall_protocol.defs import (
|
|
|
17
17
|
EndReason,
|
|
18
18
|
Env,
|
|
19
19
|
MemoryOp,
|
|
20
|
+
Projection,
|
|
20
21
|
Route,
|
|
21
22
|
ScoreVerdict,
|
|
22
23
|
Supervisor,
|
|
@@ -42,6 +43,15 @@ class AgentDetached(WireModel):
|
|
|
42
43
|
left: bool
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
class AgentDraining(WireModel):
|
|
47
|
+
"""One socket let go of its calls without cutting them."""
|
|
48
|
+
|
|
49
|
+
app: str
|
|
50
|
+
env: Env
|
|
51
|
+
handed: int
|
|
52
|
+
parked: int
|
|
53
|
+
|
|
54
|
+
|
|
45
55
|
# The gateway accepted an agent.register: this socket now speaks for the agent and answers its
|
|
46
56
|
# routes. Many sockets may hold one agent at once — a new call takes the newest of them, unless the
|
|
47
57
|
# caller names one by its `app` id.
|
|
@@ -74,6 +84,59 @@ class AgentTranscript(WireModel):
|
|
|
74
84
|
end: float | None = None
|
|
75
85
|
|
|
76
86
|
|
|
87
|
+
# An ask for a person settled: a supervisor took the line, or the wait ran out and the agent has the
|
|
88
|
+
# caller back.
|
|
89
|
+
class AttentionAnswered(WireModel):
|
|
90
|
+
"""An ask for a person settled."""
|
|
91
|
+
|
|
92
|
+
ok: bool
|
|
93
|
+
by: Supervisor | None
|
|
94
|
+
error: str | None = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# The agent asked for a person: the caller is on hold and waits for a supervisor to take the line.
|
|
98
|
+
# What a supervisor's console and phone are notified by.
|
|
99
|
+
class AttentionRequested(WireModel):
|
|
100
|
+
"""The agent asked for a person."""
|
|
101
|
+
|
|
102
|
+
reason: str
|
|
103
|
+
wait_s: float
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# Everything the agent says and hears comes after this.
|
|
107
|
+
class CallStarted(WireModel):
|
|
108
|
+
"""Media is up: the caller and the agent can hear each other, or the text session is open."""
|
|
109
|
+
|
|
110
|
+
channel: Channel
|
|
111
|
+
direction: Direction
|
|
112
|
+
from_: str = Field(alias="from")
|
|
113
|
+
to: str
|
|
114
|
+
run: str | None = None
|
|
115
|
+
persona: str | None = None
|
|
116
|
+
accepts_when: str | None = None
|
|
117
|
+
declines_when: str | None = None
|
|
118
|
+
caller: Contact | None
|
|
119
|
+
started_at: float
|
|
120
|
+
env: Env | None = None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class CallAttached(WireModel):
|
|
124
|
+
"""A call changed hands without ending."""
|
|
125
|
+
|
|
126
|
+
app: str
|
|
127
|
+
started: CallStarted
|
|
128
|
+
state: dict[str, Any]
|
|
129
|
+
seq: int
|
|
130
|
+
claimed: str | None = None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class CallClaimed(WireModel):
|
|
134
|
+
"""The call was bound to a page's code."""
|
|
135
|
+
|
|
136
|
+
code: str
|
|
137
|
+
via: Literal["keypad", "agent"]
|
|
138
|
+
|
|
139
|
+
|
|
77
140
|
# The first entry of an outbound call's log.
|
|
78
141
|
class CallDialing(WireModel):
|
|
79
142
|
"""The platform is placing an outbound call and the far end has not answered yet."""
|
|
@@ -150,21 +213,6 @@ class CallScore(WireModel):
|
|
|
150
213
|
judge_cost_eur: float | None = None
|
|
151
214
|
|
|
152
215
|
|
|
153
|
-
# Everything the agent says and hears comes after this.
|
|
154
|
-
class CallStarted(WireModel):
|
|
155
|
-
"""Media is up: the caller and the agent can hear each other, or the text session is open."""
|
|
156
|
-
|
|
157
|
-
channel: Channel
|
|
158
|
-
direction: Direction
|
|
159
|
-
from_: str = Field(alias="from")
|
|
160
|
-
to: str
|
|
161
|
-
run: str | None = None
|
|
162
|
-
persona: str | None = None
|
|
163
|
-
caller: Contact | None
|
|
164
|
-
started_at: float
|
|
165
|
-
env: Env | None = None
|
|
166
|
-
|
|
167
|
-
|
|
168
216
|
# Written after call.ended, once memory and pricing are done; call.score follows it and seals the
|
|
169
217
|
# log.
|
|
170
218
|
class CallSummary(WireModel):
|
|
@@ -183,24 +231,42 @@ class CallTransferred(WireModel):
|
|
|
183
231
|
"""A transfer asked for by the agent or a supervisor finished, one way or the other."""
|
|
184
232
|
|
|
185
233
|
to: str
|
|
186
|
-
mode: TransferMode
|
|
234
|
+
mode: TransferMode | None = None
|
|
187
235
|
ok: bool
|
|
188
236
|
error: str | None = None
|
|
189
237
|
|
|
190
238
|
|
|
191
|
-
# Somebody asked to be called back
|
|
192
|
-
#
|
|
193
|
-
# tenant's app reads it and places the call.
|
|
239
|
+
# Somebody asked to be called back: a phone caller the overflow agent answered, a web visitor who
|
|
240
|
+
# left a number at the widget, or a caller who asked the agent for one (call.callback). Written into
|
|
241
|
+
# the agent's own log; the tenant's app reads it and places the call.
|
|
194
242
|
class CallbackRequested(WireModel):
|
|
195
|
-
"""Somebody asked to be called back
|
|
243
|
+
"""Somebody asked to be called back."""
|
|
196
244
|
|
|
197
245
|
channel: Channel
|
|
198
246
|
number: str
|
|
199
|
-
via: Literal["overflow", "widget"]
|
|
247
|
+
via: Literal["overflow", "widget", "agent"]
|
|
200
248
|
call: str | None
|
|
249
|
+
when: str | None = None
|
|
250
|
+
note: str | None = None
|
|
201
251
|
contact: Contact | None
|
|
202
252
|
|
|
203
253
|
|
|
254
|
+
class CodeClaimed(WireModel):
|
|
255
|
+
"""One code, taken or expired."""
|
|
256
|
+
|
|
257
|
+
code: str
|
|
258
|
+
call: str | None
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class CodeIssued(WireModel):
|
|
262
|
+
"""One code, waiting for the call that keys it."""
|
|
263
|
+
|
|
264
|
+
code: str
|
|
265
|
+
env: Env
|
|
266
|
+
expires_at: float
|
|
267
|
+
log: Projection
|
|
268
|
+
|
|
269
|
+
|
|
204
270
|
# The tool does not run; the model is told.
|
|
205
271
|
class ConfirmDeclined(WireModel):
|
|
206
272
|
"""The caller did not say yes, or the request lapsed."""
|
|
@@ -236,9 +302,10 @@ class ConfirmRequest(WireModel):
|
|
|
236
302
|
ttl_s: int
|
|
237
303
|
|
|
238
304
|
|
|
305
|
+
# The gateway refused a call, a written turn or a register because one of the org's quotas ran out.
|
|
239
306
|
# Written into the agent's own log, which is the org's, before the door says no.
|
|
240
307
|
class CreditsExhausted(WireModel):
|
|
241
|
-
"""
|
|
308
|
+
"""CreditsExhausted, as protocol/schema declares it."""
|
|
242
309
|
|
|
243
310
|
org: str
|
|
244
311
|
quota: Literal[
|
|
@@ -250,6 +317,7 @@ class CreditsExhausted(WireModel):
|
|
|
250
317
|
"knowledge_chunks",
|
|
251
318
|
"numbers",
|
|
252
319
|
"seats",
|
|
320
|
+
"llm_tokens",
|
|
253
321
|
]
|
|
254
322
|
used: float
|
|
255
323
|
limit: int
|
|
@@ -281,6 +349,13 @@ class DocsSources(WireModel):
|
|
|
281
349
|
speech_id: str | None = None
|
|
282
350
|
|
|
283
351
|
|
|
352
|
+
class DtmfReceived(WireModel):
|
|
353
|
+
"""One tone the caller keyed."""
|
|
354
|
+
|
|
355
|
+
digit: Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "#"]
|
|
356
|
+
code: int
|
|
357
|
+
|
|
358
|
+
|
|
284
359
|
# Inside a call it says what failed; outside a call it says which command the gateway refused.
|
|
285
360
|
class ErrorEvent(WireModel):
|
|
286
361
|
"""Something went wrong."""
|
|
@@ -329,6 +404,27 @@ class MemoryOps(WireModel):
|
|
|
329
404
|
speech_id: str | None = None
|
|
330
405
|
|
|
331
406
|
|
|
407
|
+
class MessageTaken(WireModel):
|
|
408
|
+
"""One waiting message, off the queue."""
|
|
409
|
+
|
|
410
|
+
message_id: str
|
|
411
|
+
call: str | None
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class MessageWaiting(WireModel):
|
|
415
|
+
"""One message, kept until somebody can answer it."""
|
|
416
|
+
|
|
417
|
+
channel: Channel
|
|
418
|
+
env: Env
|
|
419
|
+
number: str
|
|
420
|
+
phone_number_id: str
|
|
421
|
+
from_: str = Field(alias="from")
|
|
422
|
+
name: str | None
|
|
423
|
+
message_id: str
|
|
424
|
+
text: str
|
|
425
|
+
received_at: float
|
|
426
|
+
|
|
427
|
+
|
|
332
428
|
# Ephemeral: it proves the socket is alive and says nothing else.
|
|
333
429
|
class Pong(WireModel):
|
|
334
430
|
"""The answer to ping."""
|
|
@@ -409,7 +505,7 @@ class SupervisorTransferred(WireModel):
|
|
|
409
505
|
|
|
410
506
|
by: Supervisor
|
|
411
507
|
to: str
|
|
412
|
-
mode: TransferMode
|
|
508
|
+
mode: TransferMode | None = None
|
|
413
509
|
|
|
414
510
|
|
|
415
511
|
class SupervisorWhispered(WireModel):
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"held": false,
|
|
31
31
|
"muted": false,
|
|
32
32
|
"transfer": null,
|
|
33
|
+
"attention": null,
|
|
33
34
|
"usage": [{"input_audio_tokens": 0, "input_cache_creation_tokens": 562, "input_cached_audio_tokens": 0, "input_cached_image_tokens": 0, "input_cached_text_tokens": 0, "input_cached_tokens": 13145, "input_image_tokens": 0, "input_text_tokens": 0, "input_tokens": 14963, "model": "claude-haiku-4-5", "output_audio_tokens": 0, "output_reasoning_tokens": 0, "output_text_tokens": 0, "output_tokens": 281, "provider": "anthropic", "session_duration": 0.0, "type": "llm_usage"}, {"audio_duration": 25.6, "characters_count": 397, "input_tokens": 0, "model": "eleven_flash_v2_5", "output_tokens": 0, "provider": "elevenlabs", "type": "tts_usage"}, {"audio_duration": 12.1, "input_tokens": 0, "model": "stt-rt-v5", "output_tokens": 0, "provider": "soniox", "type": "stt_usage"}, {"model": "adaptive", "provider": "livekit", "total_requests": 12, "type": "interruption_usage"}, {"model": "turn-detector-v1", "provider": "livekit", "total_requests": 5, "type": "eot_usage"}],
|
|
34
35
|
"cost": {"eur": 0.022937, "rate": {"as_of": "2026-08-12", "currency": "EUR", "usd_to_eur": 0.92}, "rows": [{"eur": 0.001156, "model": "claude-haiku-4-5", "provider": "anthropic", "quantity": 1256.0, "unit": "input_tokens", "unit_price_usd": 1e-06}, {"eur": 0.001209, "model": "claude-haiku-4-5", "provider": "anthropic", "quantity": 13145.0, "unit": "cached_input_tokens", "unit_price_usd": 1e-07}, {"eur": 0.000646, "model": "claude-haiku-4-5", "provider": "anthropic", "quantity": 562.0, "unit": "cache_creation_tokens", "unit_price_usd": 1.25e-06}, {"eur": 0.001293, "model": "claude-haiku-4-5", "provider": "anthropic", "quantity": 281.0, "unit": "output_tokens", "unit_price_usd": 5e-06}, {"eur": 0.018262, "model": "eleven_flash_v2_5", "provider": "elevenlabs", "quantity": 397.0, "unit": "characters", "unit_price_usd": 5e-05}, {"eur": 0.000371, "model": "stt-rt-v5", "provider": "soniox", "quantity": 12.1, "unit": "audio_seconds", "unit_price_usd": 3.33e-05}], "unpriced": [{"model": "adaptive", "provider": "livekit"}, {"model": "turn-detector-v1", "provider": "livekit"}]},
|
|
35
36
|
"routes": [],
|
|
@@ -5,9 +5,13 @@ from typing import Literal
|
|
|
5
5
|
from pinecall_protocol._base import WireModel
|
|
6
6
|
from pinecall_protocol.commands import (
|
|
7
7
|
AgentConfigure,
|
|
8
|
+
AgentDrain,
|
|
8
9
|
AgentRegister,
|
|
9
10
|
AgentReply,
|
|
10
11
|
AgentSay,
|
|
12
|
+
CallAttention,
|
|
13
|
+
CallCallback,
|
|
14
|
+
CallClaim,
|
|
11
15
|
CallDial,
|
|
12
16
|
CallDtmf,
|
|
13
17
|
CallEvent,
|
|
@@ -34,11 +38,16 @@ from pinecall_protocol.defs import ToolResult
|
|
|
34
38
|
from pinecall_protocol.events import (
|
|
35
39
|
AgentConfigured,
|
|
36
40
|
AgentDetached,
|
|
41
|
+
AgentDraining,
|
|
37
42
|
AgentRegistered,
|
|
38
43
|
AgentStateChanged,
|
|
39
44
|
AgentTranscript,
|
|
40
45
|
AgentTurnEnded,
|
|
46
|
+
AttentionAnswered,
|
|
47
|
+
AttentionRequested,
|
|
48
|
+
CallAttached,
|
|
41
49
|
CallbackRequested,
|
|
50
|
+
CallClaimed,
|
|
42
51
|
CallDialing,
|
|
43
52
|
CallEnded,
|
|
44
53
|
CallLine,
|
|
@@ -47,6 +56,8 @@ from pinecall_protocol.events import (
|
|
|
47
56
|
CallStarted,
|
|
48
57
|
CallSummary,
|
|
49
58
|
CallTransferred,
|
|
59
|
+
CodeClaimed,
|
|
60
|
+
CodeIssued,
|
|
50
61
|
ConfirmDeclined,
|
|
51
62
|
ConfirmGranted,
|
|
52
63
|
ConfirmRequest,
|
|
@@ -54,11 +65,14 @@ from pinecall_protocol.events import (
|
|
|
54
65
|
Custom,
|
|
55
66
|
DevRequest,
|
|
56
67
|
DocsSources,
|
|
68
|
+
DtmfReceived,
|
|
57
69
|
ErrorEvent,
|
|
58
70
|
FleetFull,
|
|
59
71
|
LogCaughtUp,
|
|
60
72
|
LogGap,
|
|
61
73
|
MemoryOps,
|
|
74
|
+
MessageTaken,
|
|
75
|
+
MessageWaiting,
|
|
62
76
|
Pong,
|
|
63
77
|
PromptChanged,
|
|
64
78
|
StateChanged,
|
|
@@ -100,9 +114,14 @@ from pinecall_protocol.room import (
|
|
|
100
114
|
EVENTS: dict[str, type[WireModel]] = {
|
|
101
115
|
"agent.configured": AgentConfigured,
|
|
102
116
|
"agent.detached": AgentDetached,
|
|
117
|
+
"agent.draining": AgentDraining,
|
|
103
118
|
"agent.registered": AgentRegistered,
|
|
104
119
|
"agent.state": AgentStateChanged,
|
|
105
120
|
"agent.transcript": AgentTranscript,
|
|
121
|
+
"attention.answered": AttentionAnswered,
|
|
122
|
+
"attention.requested": AttentionRequested,
|
|
123
|
+
"call.attached": CallAttached,
|
|
124
|
+
"call.claimed": CallClaimed,
|
|
106
125
|
"call.dialing": CallDialing,
|
|
107
126
|
"call.ended": CallEnded,
|
|
108
127
|
"call.line": CallLine,
|
|
@@ -112,6 +131,8 @@ EVENTS: dict[str, type[WireModel]] = {
|
|
|
112
131
|
"call.summary": CallSummary,
|
|
113
132
|
"call.transferred": CallTransferred,
|
|
114
133
|
"callback.requested": CallbackRequested,
|
|
134
|
+
"code.claimed": CodeClaimed,
|
|
135
|
+
"code.issued": CodeIssued,
|
|
115
136
|
"confirm.declined": ConfirmDeclined,
|
|
116
137
|
"confirm.granted": ConfirmGranted,
|
|
117
138
|
"confirm.request": ConfirmRequest,
|
|
@@ -119,12 +140,15 @@ EVENTS: dict[str, type[WireModel]] = {
|
|
|
119
140
|
"custom": Custom,
|
|
120
141
|
"dev.request": DevRequest,
|
|
121
142
|
"docs.sources": DocsSources,
|
|
143
|
+
"dtmf.received": DtmfReceived,
|
|
122
144
|
"error": ErrorEvent,
|
|
123
145
|
"event.received": EventReceived,
|
|
124
146
|
"fleet.full": FleetFull,
|
|
125
147
|
"log.caught_up": LogCaughtUp,
|
|
126
148
|
"log.gap": LogGap,
|
|
127
149
|
"memory.ops": MemoryOps,
|
|
150
|
+
"message.taken": MessageTaken,
|
|
151
|
+
"message.waiting": MessageWaiting,
|
|
128
152
|
"metrics.avatar": AvatarMetrics,
|
|
129
153
|
"metrics.eot": EOTInferenceMetrics,
|
|
130
154
|
"metrics.eou": EOUMetrics,
|
|
@@ -162,9 +186,14 @@ EVENTS: dict[str, type[WireModel]] = {
|
|
|
162
186
|
type EventType = Literal[
|
|
163
187
|
"agent.configured",
|
|
164
188
|
"agent.detached",
|
|
189
|
+
"agent.draining",
|
|
165
190
|
"agent.registered",
|
|
166
191
|
"agent.state",
|
|
167
192
|
"agent.transcript",
|
|
193
|
+
"attention.answered",
|
|
194
|
+
"attention.requested",
|
|
195
|
+
"call.attached",
|
|
196
|
+
"call.claimed",
|
|
168
197
|
"call.dialing",
|
|
169
198
|
"call.ended",
|
|
170
199
|
"call.line",
|
|
@@ -174,6 +203,8 @@ type EventType = Literal[
|
|
|
174
203
|
"call.summary",
|
|
175
204
|
"call.transferred",
|
|
176
205
|
"callback.requested",
|
|
206
|
+
"code.claimed",
|
|
207
|
+
"code.issued",
|
|
177
208
|
"confirm.declined",
|
|
178
209
|
"confirm.granted",
|
|
179
210
|
"confirm.request",
|
|
@@ -181,12 +212,15 @@ type EventType = Literal[
|
|
|
181
212
|
"custom",
|
|
182
213
|
"dev.request",
|
|
183
214
|
"docs.sources",
|
|
215
|
+
"dtmf.received",
|
|
184
216
|
"error",
|
|
185
217
|
"event.received",
|
|
186
218
|
"fleet.full",
|
|
187
219
|
"log.caught_up",
|
|
188
220
|
"log.gap",
|
|
189
221
|
"memory.ops",
|
|
222
|
+
"message.taken",
|
|
223
|
+
"message.waiting",
|
|
190
224
|
"metrics.avatar",
|
|
191
225
|
"metrics.eot",
|
|
192
226
|
"metrics.eou",
|
|
@@ -246,9 +280,13 @@ TERMINAL_EVENT: str = "call.score"
|
|
|
246
280
|
# Every command by its wire type; codec looks the model up here.
|
|
247
281
|
COMMANDS: dict[str, type[WireModel]] = {
|
|
248
282
|
"agent.configure": AgentConfigure,
|
|
283
|
+
"agent.drain": AgentDrain,
|
|
249
284
|
"agent.register": AgentRegister,
|
|
250
285
|
"agent.reply": AgentReply,
|
|
251
286
|
"agent.say": AgentSay,
|
|
287
|
+
"call.attention": CallAttention,
|
|
288
|
+
"call.callback": CallCallback,
|
|
289
|
+
"call.claim": CallClaim,
|
|
252
290
|
"call.dial": CallDial,
|
|
253
291
|
"call.dtmf": CallDtmf,
|
|
254
292
|
"call.event": CallEvent,
|
|
@@ -275,9 +313,13 @@ COMMANDS: dict[str, type[WireModel]] = {
|
|
|
275
313
|
|
|
276
314
|
type CommandType = Literal[
|
|
277
315
|
"agent.configure",
|
|
316
|
+
"agent.drain",
|
|
278
317
|
"agent.register",
|
|
279
318
|
"agent.reply",
|
|
280
319
|
"agent.say",
|
|
320
|
+
"call.attention",
|
|
321
|
+
"call.callback",
|
|
322
|
+
"call.claim",
|
|
281
323
|
"call.dial",
|
|
282
324
|
"call.dtmf",
|
|
283
325
|
"call.event",
|
|
@@ -306,9 +348,13 @@ type CommandType = Literal[
|
|
|
306
348
|
# Which events a command lands in the log as, so a caller knows what to wait for.
|
|
307
349
|
PRODUCES: dict[str, tuple[str, ...]] = {
|
|
308
350
|
"agent.configure": ("agent.configured",),
|
|
351
|
+
"agent.drain": ("agent.draining",),
|
|
309
352
|
"agent.register": ("agent.registered",),
|
|
310
353
|
"agent.reply": ("turn.agent",),
|
|
311
354
|
"agent.say": ("turn.agent",),
|
|
355
|
+
"call.attention": ("attention.requested", "call.line", "attention.answered"),
|
|
356
|
+
"call.callback": ("callback.requested",),
|
|
357
|
+
"call.claim": ("call.claimed",),
|
|
312
358
|
"call.dial": ("call.dialing",),
|
|
313
359
|
"call.dtmf": (),
|
|
314
360
|
"call.event": ("event.received",),
|
|
@@ -22,7 +22,7 @@ from pinecall_protocol.defs import (
|
|
|
22
22
|
TurnConfig,
|
|
23
23
|
)
|
|
24
24
|
from pinecall_protocol.envelope import Entry
|
|
25
|
-
from pinecall_protocol.state import CallStatus, State
|
|
25
|
+
from pinecall_protocol.state import AttentionState, CallStatus, State
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class CallState(WireModel):
|
|
@@ -78,6 +78,15 @@ class SessionLine(WireModel):
|
|
|
78
78
|
cost: Cost | None
|
|
79
79
|
score: SessionScore | None = None
|
|
80
80
|
flags: list[SessionFlag] | None = None
|
|
81
|
+
attention: AttentionState | None = None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# The operator's stream, every org of the box at once.
|
|
85
|
+
class BoxEvent(WireModel):
|
|
86
|
+
"""One frame of GET /v1/ops/events: an entry of some org's floor, and whose floor it is."""
|
|
87
|
+
|
|
88
|
+
org: str
|
|
89
|
+
entry: Entry
|
|
81
90
|
|
|
82
91
|
|
|
83
92
|
# GET /v1/agents/{slug}/sessions and GET /v1/sessions: the calls that match, newest first, a page at
|
|
@@ -138,6 +147,29 @@ class Insights(WireModel):
|
|
|
138
147
|
budget: InsightsBudget
|
|
139
148
|
|
|
140
149
|
|
|
150
|
+
class Limit(WireModel):
|
|
151
|
+
"""One quota: the limit the org was given, and how much of it is used."""
|
|
152
|
+
|
|
153
|
+
limit: int | None
|
|
154
|
+
used: float
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# GET /v1/limits: what the key's org may use and has used on this instance, where it bought more,
|
|
158
|
+
# and which of the box's vendor keys it runs on. Any key of the org.
|
|
159
|
+
class Limits(WireModel):
|
|
160
|
+
"""GET /v1/limits."""
|
|
161
|
+
|
|
162
|
+
minutes: Limit
|
|
163
|
+
messages: Limit
|
|
164
|
+
llm_tokens: Limit
|
|
165
|
+
concurrent_calls: Limit
|
|
166
|
+
agents: Limit
|
|
167
|
+
seats: Limit
|
|
168
|
+
numbers: Limit
|
|
169
|
+
lends: list[str] | None
|
|
170
|
+
billing_url: str | None
|
|
171
|
+
|
|
172
|
+
|
|
141
173
|
# GET and PUT /v1/org/judging: whether the org's calls are judged at hang-up, and what judging one
|
|
142
174
|
# may spend on a model.
|
|
143
175
|
class Judging(WireModel):
|
|
@@ -235,6 +267,11 @@ class AgentMemory(WireModel):
|
|
|
235
267
|
next: str | None
|
|
236
268
|
|
|
237
269
|
|
|
270
|
+
# auto: the visitor's own light or dark, as the browser says. light and dark: that one, whatever the
|
|
271
|
+
# browser says.
|
|
272
|
+
type WidgetTheme = Literal["auto", "light", "dark"]
|
|
273
|
+
|
|
274
|
+
|
|
238
275
|
# GET and PUT /v1/agents/{slug}/widget: how the widget presents this agent, kept per org, world and
|
|
239
276
|
# agent. PUT takes the whole set.
|
|
240
277
|
class WidgetSettings(WireModel):
|
|
@@ -245,6 +282,7 @@ class WidgetSettings(WireModel):
|
|
|
245
282
|
greeting: str | None
|
|
246
283
|
accent: str | None
|
|
247
284
|
autostart: bool
|
|
285
|
+
theme: WidgetTheme | None = None
|
|
248
286
|
|
|
249
287
|
|
|
250
288
|
# GET and PUT /v1/org/sso: the OpenID Connect provider this org's people sign in at, and never the
|
|
@@ -738,6 +776,41 @@ class Dialled(WireModel):
|
|
|
738
776
|
to: str
|
|
739
777
|
from_: str = Field(alias="from")
|
|
740
778
|
env: Env
|
|
779
|
+
log_token: str
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
# POST /v1/codes, the answer: the four digits a page shows beside the agent's number, when they stop
|
|
783
|
+
# being a code, and the token the page asks about them with.
|
|
784
|
+
class Code(WireModel):
|
|
785
|
+
"""POST /v1/codes, the answer."""
|
|
786
|
+
|
|
787
|
+
code: str
|
|
788
|
+
number: str
|
|
789
|
+
expires_at: float
|
|
790
|
+
code_token: str
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
# GET /v1/codes/{code}: whether a call has claimed the code yet, and once one has, the call and the
|
|
794
|
+
# token that reads it.
|
|
795
|
+
class CodeStanding(WireModel):
|
|
796
|
+
"""GET /v1/codes/{code}."""
|
|
797
|
+
|
|
798
|
+
code: str
|
|
799
|
+
status: Literal["waiting", "claimed", "expired"]
|
|
800
|
+
expires_at: float
|
|
801
|
+
call: str | None
|
|
802
|
+
log_token: str | None
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
# POST /v1/tokens, the answer: where the browser joins, as whom, the call it becomes, and a token
|
|
806
|
+
# that reads that call's log.
|
|
807
|
+
class Minted(WireModel):
|
|
808
|
+
"""POST /v1/tokens, the answer."""
|
|
809
|
+
|
|
810
|
+
server_url: str
|
|
811
|
+
participant_token: str
|
|
812
|
+
call: str
|
|
813
|
+
log_token: str
|
|
741
814
|
|
|
742
815
|
|
|
743
816
|
class DialGuards(WireModel):
|
|
@@ -783,6 +856,8 @@ class TuningBody(WireModel):
|
|
|
783
856
|
hangup: HangupConfig | None = None
|
|
784
857
|
turn: TurnConfig | None = None
|
|
785
858
|
memory: MemoryConfig | None = None
|
|
859
|
+
record: bool | None = None
|
|
860
|
+
max_duration_s: int | None = None
|
|
786
861
|
knowledge: str | None = None
|
|
787
862
|
bases: list[DocsConfig] | None = None
|
|
788
863
|
|
|
@@ -982,6 +1057,11 @@ class Persona(WireModel):
|
|
|
982
1057
|
style: str
|
|
983
1058
|
facts: dict[str, str]
|
|
984
1059
|
state: dict[str, Any]
|
|
1060
|
+
llm: str | None
|
|
1061
|
+
tts: str | None
|
|
1062
|
+
voice: str | None
|
|
1063
|
+
accepts_when: str
|
|
1064
|
+
declines_when: str
|
|
985
1065
|
author: str
|
|
986
1066
|
set_at: float
|
|
987
1067
|
|
|
@@ -1002,6 +1082,11 @@ class PersonaPut(WireModel):
|
|
|
1002
1082
|
style: str
|
|
1003
1083
|
facts: dict[str, str] | None = None
|
|
1004
1084
|
state: dict[str, Any] | None = None
|
|
1085
|
+
llm: str | None = None
|
|
1086
|
+
tts: str | None = None
|
|
1087
|
+
voice: str | None = None
|
|
1088
|
+
accepts_when: str | None = None
|
|
1089
|
+
declines_when: str | None = None
|
|
1005
1090
|
was: str | None = None
|
|
1006
1091
|
|
|
1007
1092
|
|
|
@@ -1028,3 +1113,40 @@ class PersonaRunList(WireModel):
|
|
|
1028
1113
|
runs: list[PersonaRun]
|
|
1029
1114
|
total: int
|
|
1030
1115
|
next: str | None
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
# One voice a picker offers, as GET /v1/voices lists it: the id the `voice` setting takes, and what
|
|
1119
|
+
# a person chooses by. `country` and `accent` are the vendor's own words (`ES` is Spain, `MX`
|
|
1120
|
+
# Mexico), empty when the vendor said none.
|
|
1121
|
+
class ListedVoice(WireModel):
|
|
1122
|
+
"""One voice a picker offers, as GET /v1/voices lists it."""
|
|
1123
|
+
|
|
1124
|
+
id: str
|
|
1125
|
+
name: str
|
|
1126
|
+
language: str
|
|
1127
|
+
description: str
|
|
1128
|
+
gender: str
|
|
1129
|
+
country: str
|
|
1130
|
+
accent: str
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
# GET /v1/voices?tts=&language=: a voice vendor's own voices in one language, in the vendor's order.
|
|
1134
|
+
class VoicesListed(WireModel):
|
|
1135
|
+
"""GET /v1/voices?tts=&language=."""
|
|
1136
|
+
|
|
1137
|
+
tts: str
|
|
1138
|
+
language: str | None
|
|
1139
|
+
voices: list[ListedVoice]
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
# POST /v1/voices/sample, the body: which vendor, which voice, which model and which words to hear.
|
|
1143
|
+
# The answer is the WAV itself (`audio/wav`), with `Server-Timing: first-audio;dur=…, total;dur=…`
|
|
1144
|
+
# in milliseconds.
|
|
1145
|
+
class VoiceSample(WireModel):
|
|
1146
|
+
"""POST /v1/voices/sample, the body."""
|
|
1147
|
+
|
|
1148
|
+
tts: str
|
|
1149
|
+
voice: str
|
|
1150
|
+
model: str | None = None
|
|
1151
|
+
language: str | None = None
|
|
1152
|
+
text: str | None = None
|
|
@@ -134,11 +134,22 @@ class TransferState(WireModel):
|
|
|
134
134
|
"""The transfer in flight or the one that happened."""
|
|
135
135
|
|
|
136
136
|
to: str
|
|
137
|
-
mode: TransferMode
|
|
137
|
+
mode: TransferMode | None = None
|
|
138
138
|
status: Literal["requested", "done", "failed"]
|
|
139
139
|
by: Literal["agent", "supervisor"]
|
|
140
140
|
|
|
141
141
|
|
|
142
|
+
# The agent's last ask for a person, from attention.requested until attention.answered settles it.
|
|
143
|
+
class AttentionState(WireModel):
|
|
144
|
+
"""AttentionState, as protocol/schema declares it."""
|
|
145
|
+
|
|
146
|
+
reason: str
|
|
147
|
+
wait_s: float
|
|
148
|
+
status: Literal["open", "answered", "lapsed"]
|
|
149
|
+
asked_at: float
|
|
150
|
+
by: Supervisor | None
|
|
151
|
+
|
|
152
|
+
|
|
142
153
|
class LiveTranscript(WireModel):
|
|
143
154
|
"""The words on screen right now: interim transcripts that a finished turn clears."""
|
|
144
155
|
|
|
@@ -233,6 +244,7 @@ class State(WireModel):
|
|
|
233
244
|
held: bool
|
|
234
245
|
muted: bool
|
|
235
246
|
transfer: TransferState | None
|
|
247
|
+
attention: AttentionState | None = None
|
|
236
248
|
usage: list[ModelUsage]
|
|
237
249
|
cost: Cost | None
|
|
238
250
|
routes: list[Route]
|
|
@@ -45,12 +45,13 @@ class TransferVerb(WireModel):
|
|
|
45
45
|
|
|
46
46
|
verb: Literal["transfer"] = "transfer"
|
|
47
47
|
to: str
|
|
48
|
-
mode: TransferMode
|
|
48
|
+
mode: TransferMode | None = None
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
#
|
|
51
|
+
# Hang up on the caller's behalf, at once: a sentence playing and a reply still being written are
|
|
52
|
+
# cut, not finished. Logged as supervisor.ended, then call.ended with reason supervisor_ended.
|
|
52
53
|
class EndVerb(WireModel):
|
|
53
|
-
"""Hang up on the caller's behalf."""
|
|
54
|
+
"""Hang up on the caller's behalf, at once."""
|
|
54
55
|
|
|
55
56
|
verb: Literal["end"] = "end"
|
|
56
57
|
reason: str | None = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pinecall_protocol-0.6.2 → pinecall_protocol-0.6.14}/pinecall_protocol/fixtures/call-log-golden.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|