pinecall-protocol 0.3.0__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.3.0 → pinecall_protocol-0.6.14}/PKG-INFO +1 -1
- pinecall_protocol-0.6.14/pinecall_protocol/_version.py +4 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/commands.py +32 -3
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/defs.py +31 -10
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/events.py +119 -22
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/fixtures/__init__.py +1 -1
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/fixtures/call-log-golden.state.json +1 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/registry.py +46 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/rest.py +440 -3
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/state.py +13 -1
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/verbs.py +4 -3
- pinecall_protocol-0.3.0/pinecall_protocol/_version.py +0 -4
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/.gitignore +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/.python-version +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/LICENSE +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/README.md +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/__init__.py +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/_base.py +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/codec.py +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/envelope.py +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/fixtures/call-log-golden.json +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/metrics.py +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/py.typed +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pinecall_protocol/room.py +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/pyproject.toml +0 -0
- {pinecall_protocol-0.3.0 → pinecall_protocol-0.6.14}/uv.lock +0 -0
|
@@ -17,12 +17,17 @@ 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."""
|
|
23
27
|
|
|
24
28
|
routes: list[Route]
|
|
25
29
|
sdk: str | None = None
|
|
30
|
+
host: str | None = None
|
|
26
31
|
takes_unclaimed: bool = True
|
|
27
32
|
|
|
28
33
|
|
|
@@ -45,6 +50,30 @@ class AgentSay(WireModel):
|
|
|
45
50
|
allow_interruptions: bool | None = None
|
|
46
51
|
|
|
47
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
|
+
|
|
48
77
|
# The new call's log opens with call.dialing; call.started follows when the far end answers.
|
|
49
78
|
class CallDial(WireModel):
|
|
50
79
|
"""Place an outbound call as this agent."""
|
|
@@ -94,12 +123,12 @@ class CallMute(WireModel):
|
|
|
94
123
|
"""Mute the agent: it keeps listening and thinking, produces no audio."""
|
|
95
124
|
|
|
96
125
|
|
|
97
|
-
# call.transferred says whether it worked.
|
|
126
|
+
# call.transferred says whether it worked, and which mode it was.
|
|
98
127
|
class CallTransfer(WireModel):
|
|
99
|
-
"""Send the caller to another number."""
|
|
128
|
+
"""Send the caller to another number, or bring that number into the call."""
|
|
100
129
|
|
|
101
130
|
to: str
|
|
102
|
-
mode: TransferMode
|
|
131
|
+
mode: TransferMode | None = None
|
|
103
132
|
|
|
104
133
|
|
|
105
134
|
class CallUnhold(WireModel):
|
|
@@ -25,16 +25,17 @@ type Env = Literal["production", "sandbox"]
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
# What a console may ask of the process standing in the agent's directory, relayed by the gateway: a
|
|
28
|
-
# written call to the class mounted there (chat), a simulated caller
|
|
29
|
-
# and a suite of them, its knowledge folder pushed or its golden asked, its memory goldens,
|
|
30
|
-
#
|
|
31
|
-
# run left on that disk. Everything else a
|
|
28
|
+
# written call to the class mounted there (chat), a simulated caller put on the class it holds, its
|
|
29
|
+
# goldens and a suite of them, its knowledge folder pushed or its golden asked, its memory goldens,
|
|
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",
|
|
37
|
-
"
|
|
38
|
+
"view.render",
|
|
38
39
|
"simulate.start",
|
|
39
40
|
"goldens.roster",
|
|
40
41
|
"goldens.run",
|
|
@@ -81,8 +82,9 @@ type EndedBy = Literal["caller", "agent", "supervisor", "platform"]
|
|
|
81
82
|
type ScoreVerdict = Literal["held", "broken", "deferred", "skipped"]
|
|
82
83
|
|
|
83
84
|
|
|
84
|
-
# Cold: the caller is sent on
|
|
85
|
-
# 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.
|
|
86
88
|
type TransferMode = Literal["cold", "warm"]
|
|
87
89
|
|
|
88
90
|
|
|
@@ -235,6 +237,7 @@ class DocSource(WireModel):
|
|
|
235
237
|
"""One chunk of the knowledge base that retrieval put in front of the model for this turn."""
|
|
236
238
|
|
|
237
239
|
id: str
|
|
240
|
+
base: str | None = None
|
|
238
241
|
path: str
|
|
239
242
|
heading: str | None = None
|
|
240
243
|
score: float
|
|
@@ -310,6 +313,8 @@ class TurnConfig(WireModel):
|
|
|
310
313
|
|
|
311
314
|
min_interruption_words: int | None = None
|
|
312
315
|
endpointing_ms: int | None = None
|
|
316
|
+
eot_threshold: float | None = None
|
|
317
|
+
eager_eot_threshold: float | None = None
|
|
313
318
|
|
|
314
319
|
|
|
315
320
|
# How the voice says one word it would otherwise get wrong: a proper name, a brand, a street.
|
|
@@ -328,6 +333,16 @@ class StateFieldSpec(WireModel):
|
|
|
328
333
|
visibility: Visibility
|
|
329
334
|
|
|
330
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
|
+
|
|
331
346
|
# An event nobody declared is refused before it touches the log.
|
|
332
347
|
class EventSpec(WireModel):
|
|
333
348
|
"""One outside event the agent accepts, and from whom."""
|
|
@@ -337,7 +352,7 @@ class EventSpec(WireModel):
|
|
|
337
352
|
|
|
338
353
|
|
|
339
354
|
class KnowledgeFile(WireModel):
|
|
340
|
-
"""One file of
|
|
355
|
+
"""One file of a base, as a push sends it: its path as the tenant keeps it, and its text."""
|
|
341
356
|
|
|
342
357
|
path: str
|
|
343
358
|
text: str
|
|
@@ -384,8 +399,11 @@ class MemoryConfig(WireModel):
|
|
|
384
399
|
forget: list[str] = Field(default_factory=list[str])
|
|
385
400
|
|
|
386
401
|
|
|
387
|
-
# What an app declares about its agent: the
|
|
388
|
-
#
|
|
402
|
+
# What an app declares about its agent: the prompt's layout, the language, the tools, whether it
|
|
403
|
+
# searches its bases itself, and who may see and send what. Every field is optional so a configure
|
|
404
|
+
# can change one thing. The environment — voice, models, greeting, hangup, turn, says, hears,
|
|
405
|
+
# knowledge, docs, memory — is the world's, set in the agent's settings, and no longer read off this
|
|
406
|
+
# declaration.
|
|
389
407
|
class AgentConfig(WireModel):
|
|
390
408
|
"""What an app declares about its agent."""
|
|
391
409
|
|
|
@@ -402,6 +420,9 @@ class AgentConfig(WireModel):
|
|
|
402
420
|
docs: DocsConfig | None = None
|
|
403
421
|
memory: MemoryConfig | None = None
|
|
404
422
|
hangup: HangupConfig | None = None
|
|
423
|
+
record: bool = True
|
|
405
424
|
tools: list[ToolSpec] | None = None
|
|
425
|
+
uses_knowledge: bool = False
|
|
406
426
|
state_fields: list[StateFieldSpec] | None = None
|
|
427
|
+
view: ViewSpec | None = None
|
|
407
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,20 +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
|
-
caller: Contact | None
|
|
163
|
-
started_at: float
|
|
164
|
-
env: Env | None = None
|
|
165
|
-
|
|
166
|
-
|
|
167
216
|
# Written after call.ended, once memory and pricing are done; call.score follows it and seals the
|
|
168
217
|
# log.
|
|
169
218
|
class CallSummary(WireModel):
|
|
@@ -182,24 +231,42 @@ class CallTransferred(WireModel):
|
|
|
182
231
|
"""A transfer asked for by the agent or a supervisor finished, one way or the other."""
|
|
183
232
|
|
|
184
233
|
to: str
|
|
185
|
-
mode: TransferMode
|
|
234
|
+
mode: TransferMode | None = None
|
|
186
235
|
ok: bool
|
|
187
236
|
error: str | None = None
|
|
188
237
|
|
|
189
238
|
|
|
190
|
-
# Somebody asked to be called back
|
|
191
|
-
#
|
|
192
|
-
# 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.
|
|
193
242
|
class CallbackRequested(WireModel):
|
|
194
|
-
"""Somebody asked to be called back
|
|
243
|
+
"""Somebody asked to be called back."""
|
|
195
244
|
|
|
196
245
|
channel: Channel
|
|
197
246
|
number: str
|
|
198
|
-
via: Literal["overflow", "widget"]
|
|
247
|
+
via: Literal["overflow", "widget", "agent"]
|
|
199
248
|
call: str | None
|
|
249
|
+
when: str | None = None
|
|
250
|
+
note: str | None = None
|
|
200
251
|
contact: Contact | None
|
|
201
252
|
|
|
202
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
|
+
|
|
203
270
|
# The tool does not run; the model is told.
|
|
204
271
|
class ConfirmDeclined(WireModel):
|
|
205
272
|
"""The caller did not say yes, or the request lapsed."""
|
|
@@ -235,9 +302,10 @@ class ConfirmRequest(WireModel):
|
|
|
235
302
|
ttl_s: int
|
|
236
303
|
|
|
237
304
|
|
|
305
|
+
# The gateway refused a call, a written turn or a register because one of the org's quotas ran out.
|
|
238
306
|
# Written into the agent's own log, which is the org's, before the door says no.
|
|
239
307
|
class CreditsExhausted(WireModel):
|
|
240
|
-
"""
|
|
308
|
+
"""CreditsExhausted, as protocol/schema declares it."""
|
|
241
309
|
|
|
242
310
|
org: str
|
|
243
311
|
quota: Literal[
|
|
@@ -249,6 +317,7 @@ class CreditsExhausted(WireModel):
|
|
|
249
317
|
"knowledge_chunks",
|
|
250
318
|
"numbers",
|
|
251
319
|
"seats",
|
|
320
|
+
"llm_tokens",
|
|
252
321
|
]
|
|
253
322
|
used: float
|
|
254
323
|
limit: int
|
|
@@ -280,6 +349,13 @@ class DocsSources(WireModel):
|
|
|
280
349
|
speech_id: str | None = None
|
|
281
350
|
|
|
282
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
|
+
|
|
283
359
|
# Inside a call it says what failed; outside a call it says which command the gateway refused.
|
|
284
360
|
class ErrorEvent(WireModel):
|
|
285
361
|
"""Something went wrong."""
|
|
@@ -328,6 +404,27 @@ class MemoryOps(WireModel):
|
|
|
328
404
|
speech_id: str | None = None
|
|
329
405
|
|
|
330
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
|
+
|
|
331
428
|
# Ephemeral: it proves the socket is alive and says nothing else.
|
|
332
429
|
class Pong(WireModel):
|
|
333
430
|
"""The answer to ping."""
|
|
@@ -408,7 +505,7 @@ class SupervisorTransferred(WireModel):
|
|
|
408
505
|
|
|
409
506
|
by: Supervisor
|
|
410
507
|
to: str
|
|
411
|
-
mode: TransferMode
|
|
508
|
+
mode: TransferMode | None = None
|
|
412
509
|
|
|
413
510
|
|
|
414
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",),
|
|
@@ -10,13 +10,19 @@ from pinecall_protocol.defs import (
|
|
|
10
10
|
Contact,
|
|
11
11
|
Cost,
|
|
12
12
|
Direction,
|
|
13
|
+
DocsConfig,
|
|
13
14
|
EndReason,
|
|
14
15
|
Env,
|
|
16
|
+
GreetingConfig,
|
|
17
|
+
HangupConfig,
|
|
15
18
|
KnowledgeFile,
|
|
19
|
+
MemoryConfig,
|
|
16
20
|
PlatformTool,
|
|
21
|
+
Pronunciation,
|
|
22
|
+
TurnConfig,
|
|
17
23
|
)
|
|
18
24
|
from pinecall_protocol.envelope import Entry
|
|
19
|
-
from pinecall_protocol.state import CallStatus, State
|
|
25
|
+
from pinecall_protocol.state import AttentionState, CallStatus, State
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
class CallState(WireModel):
|
|
@@ -72,6 +78,15 @@ class SessionLine(WireModel):
|
|
|
72
78
|
cost: Cost | None
|
|
73
79
|
score: SessionScore | None = None
|
|
74
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
|
|
75
90
|
|
|
76
91
|
|
|
77
92
|
# GET /v1/agents/{slug}/sessions and GET /v1/sessions: the calls that match, newest first, a page at
|
|
@@ -132,6 +147,29 @@ class Insights(WireModel):
|
|
|
132
147
|
budget: InsightsBudget
|
|
133
148
|
|
|
134
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
|
+
|
|
135
173
|
# GET and PUT /v1/org/judging: whether the org's calls are judged at hang-up, and what judging one
|
|
136
174
|
# may spend on a model.
|
|
137
175
|
class Judging(WireModel):
|
|
@@ -229,6 +267,11 @@ class AgentMemory(WireModel):
|
|
|
229
267
|
next: str | None
|
|
230
268
|
|
|
231
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
|
+
|
|
232
275
|
# GET and PUT /v1/agents/{slug}/widget: how the widget presents this agent, kept per org, world and
|
|
233
276
|
# agent. PUT takes the whole set.
|
|
234
277
|
class WidgetSettings(WireModel):
|
|
@@ -239,6 +282,7 @@ class WidgetSettings(WireModel):
|
|
|
239
282
|
greeting: str | None
|
|
240
283
|
accent: str | None
|
|
241
284
|
autostart: bool
|
|
285
|
+
theme: WidgetTheme | None = None
|
|
242
286
|
|
|
243
287
|
|
|
244
288
|
# GET and PUT /v1/org/sso: the OpenID Connect provider this org's people sign in at, and never the
|
|
@@ -412,6 +456,38 @@ class AgentList(WireModel):
|
|
|
412
456
|
agents: list[HeldAgent]
|
|
413
457
|
|
|
414
458
|
|
|
459
|
+
# One app connected to the gateway right now: one process on one machine, holding one or more agents
|
|
460
|
+
# in one world.
|
|
461
|
+
class AppProcess(WireModel):
|
|
462
|
+
"""One app connected to the gateway right now."""
|
|
463
|
+
|
|
464
|
+
app: str
|
|
465
|
+
agents: list[str]
|
|
466
|
+
env: Env
|
|
467
|
+
host: str | None
|
|
468
|
+
address: str | None
|
|
469
|
+
sdk: str | None
|
|
470
|
+
holder: LineHolder | None
|
|
471
|
+
connected_at: float
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
# GET /v1/apps: every app connected in the request's world that this key may see — its own corner's
|
|
475
|
+
# and the org's, every corner's with `team`.
|
|
476
|
+
class AppList(WireModel):
|
|
477
|
+
"""GET /v1/apps."""
|
|
478
|
+
|
|
479
|
+
apps: list[AppProcess]
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
# POST /v1/apps/{app}/stop, the answer: the socket was closed with the stop code, and the app exits
|
|
483
|
+
# rather than reconnect.
|
|
484
|
+
class AppStopped(WireModel):
|
|
485
|
+
"""POST /v1/apps/{app}/stop, the answer."""
|
|
486
|
+
|
|
487
|
+
app: str
|
|
488
|
+
stopped: bool
|
|
489
|
+
|
|
490
|
+
|
|
415
491
|
# GET /v1/agents/{slug}/line: whose terminal a call that RINGS at this agent's doors lands in. An
|
|
416
492
|
# org shares one sandbox number, so it rings in one place and which one is claimed.
|
|
417
493
|
class TheLine(WireModel):
|
|
@@ -643,8 +719,9 @@ class ExtractionRun(WireModel):
|
|
|
643
719
|
|
|
644
720
|
|
|
645
721
|
# POST /v1/calls/{call}/lookup, the body: which platform tool to run for this turn, and what to run
|
|
646
|
-
# it with.
|
|
647
|
-
#
|
|
722
|
+
# it with. Asked by the worker for the call it serves, and by the app for a call of its own org —
|
|
723
|
+
# `this.knowledge.search` in a tool; the gateway runs it against its own stores and writes
|
|
724
|
+
# memory.ops or docs.sources on the call's log itself.
|
|
648
725
|
class LookupRequest(WireModel):
|
|
649
726
|
"""POST /v1/calls/{call}/lookup, the body."""
|
|
650
727
|
|
|
@@ -663,6 +740,22 @@ class LookupResult(WireModel):
|
|
|
663
740
|
took_ms: float
|
|
664
741
|
|
|
665
742
|
|
|
743
|
+
class FoundChunk(WireModel):
|
|
744
|
+
"""One chunk search found: the file it came from, the heading it sits under, and its text."""
|
|
745
|
+
|
|
746
|
+
path: str
|
|
747
|
+
heading: str | None
|
|
748
|
+
text: str
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
# What search answers in a lookup's output: the best chunks of every base the agent reads, the best
|
|
752
|
+
# first. The app's `this.knowledge.search` hands the chunks to the tool that asked.
|
|
753
|
+
class SearchFound(WireModel):
|
|
754
|
+
"""What search answers in a lookup's output."""
|
|
755
|
+
|
|
756
|
+
chunks: list[FoundChunk]
|
|
757
|
+
|
|
758
|
+
|
|
666
759
|
# POST /v1/calls/{call}/remember, the answer: what the call taught about the contact, counted.
|
|
667
760
|
# Worker-only; the body is empty, the gateway reads the turns off its own log and writes memory.ops
|
|
668
761
|
# itself.
|
|
@@ -683,6 +776,41 @@ class Dialled(WireModel):
|
|
|
683
776
|
to: str
|
|
684
777
|
from_: str = Field(alias="from")
|
|
685
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
|
|
686
814
|
|
|
687
815
|
|
|
688
816
|
class DialGuards(WireModel):
|
|
@@ -713,3 +841,312 @@ class OutboundProvisioned(WireModel):
|
|
|
713
841
|
ready: bool
|
|
714
842
|
trunk: str | None = None
|
|
715
843
|
address: str | None = None
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
# Every field is optional; one left out is not set, and the runtime's own default stands for it.
|
|
847
|
+
class TuningBody(WireModel):
|
|
848
|
+
"""An agent's settings: what the org set, per world and per corner."""
|
|
849
|
+
|
|
850
|
+
voice: str | None = None
|
|
851
|
+
tts: str | None = None
|
|
852
|
+
tts_model: str | None = None
|
|
853
|
+
stt: str | None = None
|
|
854
|
+
llm: str | None = None
|
|
855
|
+
greeting: GreetingConfig | None = None
|
|
856
|
+
hangup: HangupConfig | None = None
|
|
857
|
+
turn: TurnConfig | None = None
|
|
858
|
+
memory: MemoryConfig | None = None
|
|
859
|
+
record: bool | None = None
|
|
860
|
+
max_duration_s: int | None = None
|
|
861
|
+
knowledge: str | None = None
|
|
862
|
+
bases: list[DocsConfig] | None = None
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
# One kept version of an agent's tuning: whose corner, which version, who set it and when, and what
|
|
866
|
+
# it says.
|
|
867
|
+
class TuningRow(WireModel):
|
|
868
|
+
"""One kept version of an agent's tuning."""
|
|
869
|
+
|
|
870
|
+
holder: str
|
|
871
|
+
version: int
|
|
872
|
+
author: str
|
|
873
|
+
note: str | None
|
|
874
|
+
set_at: float
|
|
875
|
+
config: TuningBody
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
# GET /v1/agents/{slug}/settings: the agent's tuning as this key sees it — its own corner's, the
|
|
879
|
+
# team's and production's, each corner's own newest row, or null when that corner set nothing.
|
|
880
|
+
class TuningAnswer(WireModel):
|
|
881
|
+
"""GET /v1/agents/{slug}/settings."""
|
|
882
|
+
|
|
883
|
+
world: Env
|
|
884
|
+
yours: TuningRow | None
|
|
885
|
+
team: TuningRow | None
|
|
886
|
+
production: TuningRow | None
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
# PUT /v1/agents/{slug}/settings, the body: the whole set, the version it was read at, why, and
|
|
890
|
+
# whose corner.
|
|
891
|
+
class TuningPut(WireModel):
|
|
892
|
+
"""PUT /v1/agents/{slug}/settings, the body."""
|
|
893
|
+
|
|
894
|
+
config: TuningBody
|
|
895
|
+
if_version: int | None = None
|
|
896
|
+
note: str | None = None
|
|
897
|
+
team: bool = False
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
class TuningHistory(WireModel):
|
|
901
|
+
"""GET /v1/agents/{slug}/settings/history: one corner's versions, newest first."""
|
|
902
|
+
|
|
903
|
+
world: Env
|
|
904
|
+
holder: str
|
|
905
|
+
rows: list[TuningRow]
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
# GET /v1/agents/{slug}/settings/diff: what this key's corner reads against another corner's newest,
|
|
909
|
+
# and which fields differ.
|
|
910
|
+
class TuningDiff(WireModel):
|
|
911
|
+
"""GET /v1/agents/{slug}/settings/diff."""
|
|
912
|
+
|
|
913
|
+
ours: TuningRow | None
|
|
914
|
+
theirs: TuningRow | None
|
|
915
|
+
changed: list[str]
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
# It comes back as a NEW version; nothing is deleted.
|
|
919
|
+
class Rollback(WireModel):
|
|
920
|
+
"""POST /v1/agents/{slug}/settings/rollback, the body: which version to bring back."""
|
|
921
|
+
|
|
922
|
+
version: int
|
|
923
|
+
team: bool = False
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
# The org's lexicon: how the voice says the words it would get wrong, and the words the ears must
|
|
927
|
+
# know. Shared by every agent of the org, laid over each one's own says and hears.
|
|
928
|
+
class LexiconBody(WireModel):
|
|
929
|
+
"""The org's lexicon."""
|
|
930
|
+
|
|
931
|
+
said: list[Pronunciation]
|
|
932
|
+
heard: list[str]
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
class LexiconRow(WireModel):
|
|
936
|
+
"""One kept version of the org's lexicon."""
|
|
937
|
+
|
|
938
|
+
holder: str
|
|
939
|
+
version: int
|
|
940
|
+
author: str
|
|
941
|
+
note: str | None
|
|
942
|
+
set_at: float
|
|
943
|
+
lexicon: LexiconBody
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
# GET /v1/lexicon: the org's words as this key sees them — its own corner's, the team's and
|
|
947
|
+
# production's, each corner's own newest, or null.
|
|
948
|
+
class LexiconAnswer(WireModel):
|
|
949
|
+
"""GET /v1/lexicon."""
|
|
950
|
+
|
|
951
|
+
world: Env
|
|
952
|
+
yours: LexiconRow | None
|
|
953
|
+
team: LexiconRow | None
|
|
954
|
+
production: LexiconRow | None
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
# PUT /v1/lexicon, the body: the whole lexicon, the version it was read at, why, and whose corner.
|
|
958
|
+
class LexiconPut(WireModel):
|
|
959
|
+
"""PUT /v1/lexicon, the body."""
|
|
960
|
+
|
|
961
|
+
lexicon: LexiconBody
|
|
962
|
+
if_version: int | None = None
|
|
963
|
+
note: str | None = None
|
|
964
|
+
team: bool = False
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
class LexiconHistory(WireModel):
|
|
968
|
+
"""GET /v1/lexicon/history: one corner's versions, newest first."""
|
|
969
|
+
|
|
970
|
+
world: Env
|
|
971
|
+
holder: str
|
|
972
|
+
rows: list[LexiconRow]
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
# GET /v1/calls/{call}/settings: the exact tuning and lexicon a call was built on, by the versions
|
|
976
|
+
# its head row recorded.
|
|
977
|
+
class CallTuning(WireModel):
|
|
978
|
+
"""GET /v1/calls/{call}/settings."""
|
|
979
|
+
|
|
980
|
+
config_version: int | None
|
|
981
|
+
lexicon_version: int | None
|
|
982
|
+
config: TuningRow | None
|
|
983
|
+
lexicon: LexiconRow | None
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
class KnowledgeFileRow(WireModel):
|
|
987
|
+
"""One file of a base as the list draws it."""
|
|
988
|
+
|
|
989
|
+
path: str
|
|
990
|
+
chars: int
|
|
991
|
+
chunks: int
|
|
992
|
+
pushed_at: float
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
# GET /v1/knowledge/{base}: every file of the base, by path, with its size and what it became. Never
|
|
996
|
+
# the text: one file is read at a time.
|
|
997
|
+
class KnowledgeFiles(WireModel):
|
|
998
|
+
"""GET /v1/knowledge/{base}."""
|
|
999
|
+
|
|
1000
|
+
base: str
|
|
1001
|
+
kept: bool
|
|
1002
|
+
files: list[KnowledgeFileRow]
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
class KnowledgeFileRead(WireModel):
|
|
1006
|
+
"""GET /v1/knowledge/{base}/files/{path}: one file, text and all."""
|
|
1007
|
+
|
|
1008
|
+
path: str
|
|
1009
|
+
text: str
|
|
1010
|
+
chunks: int
|
|
1011
|
+
pushed_at: float
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
# New under that path, or replaced in place; the base is begun when there is none.
|
|
1015
|
+
class KnowledgeFilePut(WireModel):
|
|
1016
|
+
"""PUT /v1/knowledge/{base}/files/{path}, the body: the file's whole text."""
|
|
1017
|
+
|
|
1018
|
+
text: str
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
# PUT /v1/knowledge/{base}/files/{path}, the answer: which file, how many chunks it became, and how
|
|
1022
|
+
# long that took.
|
|
1023
|
+
class KnowledgeFilePushed(WireModel):
|
|
1024
|
+
"""PUT /v1/knowledge/{base}/files/{path}, the answer."""
|
|
1025
|
+
|
|
1026
|
+
base: str
|
|
1027
|
+
path: str
|
|
1028
|
+
chunks: int
|
|
1029
|
+
took_ms: float
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
class KnowledgeUse(WireModel):
|
|
1033
|
+
"""One base and the agents whose settings attach it."""
|
|
1034
|
+
|
|
1035
|
+
base: str
|
|
1036
|
+
agents: list[str]
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
# GET /v1/knowledge/attached: which agents read each base, off every agent's newest settings in this
|
|
1040
|
+
# world.
|
|
1041
|
+
class KnowledgeUses(WireModel):
|
|
1042
|
+
"""GET /v1/knowledge/attached."""
|
|
1043
|
+
|
|
1044
|
+
bases: list[KnowledgeUse]
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
# One synthetic caller of an ORG, kept by the gateway: what they want, how they talk, and what they
|
|
1048
|
+
# may state about themselves. A model plays them turn by turn — there is no script — for `pinecall
|
|
1049
|
+
# simulate` and the console's Simulations. A caller is a person on the phone, so who they are does
|
|
1050
|
+
# not depend on which of the org's agents answers: every agent of the org calls with the same list.
|
|
1051
|
+
class Persona(WireModel):
|
|
1052
|
+
"""One synthetic caller of an ORG, kept by the gateway."""
|
|
1053
|
+
|
|
1054
|
+
name: str
|
|
1055
|
+
about: str
|
|
1056
|
+
goal: str
|
|
1057
|
+
style: str
|
|
1058
|
+
facts: dict[str, str]
|
|
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
|
|
1065
|
+
author: str
|
|
1066
|
+
set_at: float
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
# The same list for every agent of the org, and the same one in both worlds.
|
|
1070
|
+
class PersonaList(WireModel):
|
|
1071
|
+
"""GET /v1/personas: every caller the org wrote, by name."""
|
|
1072
|
+
|
|
1073
|
+
personas: list[Persona]
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
# A name that exists is replaced; `was` renames the caller it names.
|
|
1077
|
+
class PersonaPut(WireModel):
|
|
1078
|
+
"""PUT /v1/personas/{name}, the body: the caller, written whole."""
|
|
1079
|
+
|
|
1080
|
+
about: str | None = None
|
|
1081
|
+
goal: str
|
|
1082
|
+
style: str
|
|
1083
|
+
facts: dict[str, str] | None = None
|
|
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
|
|
1090
|
+
was: str | None = None
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
# Read off the call index, never off a log — the same row the sessions list is drawn from.
|
|
1094
|
+
class PersonaRun(WireModel):
|
|
1095
|
+
"""One simulation this caller has run: the call it was, and what the call came to."""
|
|
1096
|
+
|
|
1097
|
+
call: str
|
|
1098
|
+
agent: str
|
|
1099
|
+
started_at: float
|
|
1100
|
+
ended_at: float | None
|
|
1101
|
+
turns: int
|
|
1102
|
+
end_reason: EndReason | None
|
|
1103
|
+
outcome: str | None
|
|
1104
|
+
cost_eur: float | None
|
|
1105
|
+
score: SessionScore | None
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
# GET /v1/personas/{name}/runs, in the key's own world and corner, paged exactly as the sessions
|
|
1109
|
+
# list is.
|
|
1110
|
+
class PersonaRunList(WireModel):
|
|
1111
|
+
"""Every simulation this caller has run, newest first, a page at a time."""
|
|
1112
|
+
|
|
1113
|
+
runs: list[PersonaRun]
|
|
1114
|
+
total: int
|
|
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.3.0 → 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
|