pinecall-protocol 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,471 @@
1
+ """Generated from schema/events/: one model per event."""
2
+
3
+ from typing import Annotated, Any, Literal
4
+
5
+ from pydantic import Field
6
+
7
+ from pinecall_protocol._base import WireModel
8
+ from pinecall_protocol.defs import (
9
+ AgentState,
10
+ Channel,
11
+ Contact,
12
+ Cost,
13
+ DevVerb,
14
+ Direction,
15
+ DocSource,
16
+ EndedBy,
17
+ EndReason,
18
+ Env,
19
+ MemoryOp,
20
+ Route,
21
+ ScoreVerdict,
22
+ Supervisor,
23
+ TransferMode,
24
+ UserState,
25
+ )
26
+ from pinecall_protocol.metrics import AgentTurnMetrics, ModelUsage, UserTurnMetrics
27
+ from pinecall_protocol.state import State
28
+
29
+
30
+ # Live calls keep their session; the next call starts with the new config.
31
+ class AgentConfigured(WireModel):
32
+ """The gateway applied an agent.configure."""
33
+
34
+ changed: list[str]
35
+
36
+
37
+ class AgentDetached(WireModel):
38
+ """One socket stopped holding the agent."""
39
+
40
+ app: str
41
+ env: Env
42
+ left: bool
43
+
44
+
45
+ # The gateway accepted an agent.register: this socket now speaks for the agent and answers its
46
+ # routes. Many sockets may hold one agent at once — a new call takes the newest of them, unless the
47
+ # caller names one by its `app` id.
48
+ class AgentRegistered(WireModel):
49
+ """The gateway accepted an agent.register."""
50
+
51
+ routes: list[Route]
52
+ app: str
53
+ sdk: str | None = None
54
+ env: Env | None = None
55
+
56
+
57
+ class AgentStateChanged(WireModel):
58
+ """The agent's state changed, in the session's own words."""
59
+
60
+ state: AgentState
61
+
62
+
63
+ # Interim while final is false; the whole reply becomes turn.agent. Interim entries are ephemeral.
64
+ class AgentTranscript(WireModel):
65
+ """Words from the agent as they are played, synced to the audio."""
66
+
67
+ speech_id: str
68
+ text: str
69
+ final: bool
70
+ start: float | None = None
71
+ end: float | None = None
72
+
73
+
74
+ # The first entry of an outbound call's log.
75
+ class CallDialing(WireModel):
76
+ """The platform is placing an outbound call and the far end has not answered yet."""
77
+
78
+ channel: Channel
79
+ from_: str = Field(alias="from")
80
+ to: str
81
+ run: str | None = None
82
+ caller: Contact | None
83
+ external_id: str | None = None
84
+
85
+
86
+ # Nothing about the conversation follows; call.summary still does.
87
+ class CallEnded(WireModel):
88
+ """The call is over."""
89
+
90
+ reason: EndReason
91
+ ended_by: EndedBy
92
+ ended_at: float
93
+ duration_s: float
94
+
95
+
96
+ # Both are stated so a reader never has to remember the other.
97
+ class CallLine(WireModel):
98
+ """The line's hold and mute flags after one of them changed."""
99
+
100
+ held: bool
101
+ muted: bool
102
+
103
+
104
+ # The first entry of an inbound call's log.
105
+ class CallRinging(WireModel):
106
+ """An inbound call is offered to this agent and has not been answered yet."""
107
+
108
+ channel: Channel
109
+ from_: str = Field(alias="from")
110
+ to: str
111
+ route: Route
112
+ run: str | None = None
113
+ caller: Contact | None
114
+ external_id: str | None = None
115
+
116
+
117
+ # A reader who disagrees with a verdict opens the log at those lines.
118
+ class JudgmentEvidence(WireModel):
119
+ """Where in the call's own log a judgment is about."""
120
+
121
+ seqs: list[int]
122
+ said: str | None = None
123
+
124
+
125
+ # One judge's answer about one call: the question it was asked, the word it answered and the
126
+ # sentence that says why.
127
+ class Judgment(WireModel):
128
+ """One judge's answer about one call."""
129
+
130
+ name: str
131
+ verdict: ScoreVerdict
132
+ criteria: str
133
+ reason: str
134
+ evidence: JudgmentEvidence
135
+
136
+
137
+ # Written after call.summary, and the entry the log seals on.
138
+ class CallScore(WireModel):
139
+ """The last entry of a call: what the judges said about it at hang-up, one row per judge."""
140
+
141
+ passed: bool | None = None
142
+ not_judged: str | None = None
143
+ judges: list[Judgment]
144
+ panel: list[str] | None = None
145
+ judge_calls: int
146
+ judge_cost_eur: float | None = None
147
+
148
+
149
+ # Everything the agent says and hears comes after this.
150
+ class CallStarted(WireModel):
151
+ """Media is up: the caller and the agent can hear each other, or the text session is open."""
152
+
153
+ channel: Channel
154
+ direction: Direction
155
+ from_: str = Field(alias="from")
156
+ to: str
157
+ run: str | None = None
158
+ caller: Contact | None
159
+ started_at: float
160
+ env: Env | None = None
161
+
162
+
163
+ # Written after call.ended, once memory and pricing are done; call.score follows it and seals the
164
+ # log.
165
+ class CallSummary(WireModel):
166
+ """What the call was about, how it went, what it consumed and what that cost."""
167
+
168
+ reason: EndReason
169
+ outcome: str
170
+ duration_s: float
171
+ turns: int
172
+ usage: list[ModelUsage]
173
+ cost: Cost
174
+ recording: str | None = None
175
+
176
+
177
+ class CallTransferred(WireModel):
178
+ """A transfer asked for by the agent or a supervisor finished, one way or the other."""
179
+
180
+ to: str
181
+ mode: TransferMode
182
+ ok: bool
183
+ error: str | None = None
184
+
185
+
186
+ # Somebody asked to be called back because no seat was free: a phone caller the overflow agent
187
+ # answered, or a web visitor who left a number at the widget. Written into the agent's own log; the
188
+ # tenant's app reads it and places the call.
189
+ class CallbackRequested(WireModel):
190
+ """Somebody asked to be called back because no seat was free."""
191
+
192
+ channel: Channel
193
+ number: str
194
+ via: Literal["overflow", "widget"]
195
+ call: str | None
196
+ contact: Contact | None
197
+
198
+
199
+ # The tool does not run; the model is told.
200
+ class ConfirmDeclined(WireModel):
201
+ """The caller did not say yes, or the request lapsed."""
202
+
203
+ tool: str
204
+ call_id: str
205
+ audience: str
206
+ said: str | None = None
207
+ reason: Literal["no", "timeout", "changed", "cancelled"]
208
+
209
+
210
+ # The platform minted a one-shot token bound to the audience and the tool now runs. The token itself
211
+ # never enters the log.
212
+ class ConfirmGranted(WireModel):
213
+ """The caller said yes."""
214
+
215
+ tool: str
216
+ call_id: str
217
+ audience: str
218
+ said: str
219
+ ttl_s: int
220
+
221
+
222
+ # The agent reads the phrase; nothing runs until confirm.granted.
223
+ class ConfirmRequest(WireModel):
224
+ """A tool with confirm set is about to run and the platform is asking the caller."""
225
+
226
+ tool: str
227
+ call_id: str
228
+ arguments: dict[str, Any]
229
+ audience: str
230
+ phrase: str
231
+ ttl_s: int
232
+
233
+
234
+ # Written into the agent's own log, which is the org's, before the door says no.
235
+ class CreditsExhausted(WireModel):
236
+ """The gateway refused a call or a register because one of the org's quotas ran out."""
237
+
238
+ org: str
239
+ quota: Literal[
240
+ "minutes",
241
+ "messages",
242
+ "agents",
243
+ "concurrent_calls",
244
+ "memory_facts",
245
+ "knowledge_chunks",
246
+ "numbers",
247
+ "seats",
248
+ ]
249
+ used: float
250
+ limit: int
251
+
252
+
253
+ # The platform never reads it; the console shows it and evals may.
254
+ class Custom(WireModel):
255
+ """A line the app wrote into the log with call.log."""
256
+
257
+ name: str
258
+ data: dict[str, Any]
259
+
260
+
261
+ class DevRequest(WireModel):
262
+ """One ask of the process in the agent's directory, on a console's behalf."""
263
+
264
+ id: str
265
+ verb: DevVerb
266
+ data: dict[str, Any]
267
+
268
+
269
+ # An answer can be traced back to its chunks.
270
+ class DocsSources(WireModel):
271
+ """What retrieval put in front of the model for this turn."""
272
+
273
+ query: str
274
+ sources: list[DocSource]
275
+ took_ms: float
276
+ speech_id: str | None = None
277
+
278
+
279
+ # Inside a call it says what failed; outside a call it says which command the gateway refused.
280
+ class ErrorEvent(WireModel):
281
+ """Something went wrong."""
282
+
283
+ code: str
284
+ message: str
285
+ command: str | None = None
286
+ id: str | None = None
287
+ recoverable: bool
288
+
289
+
290
+ # Written into the agent's own log, which is the org's, before the door says no — the caller was
291
+ # offered a call back instead of a room.
292
+ class FleetFull(WireModel):
293
+ """The gateway refused to open a call because every worker of the fleet was full."""
294
+
295
+ channel: Channel
296
+ workers: int
297
+ active: int
298
+
299
+
300
+ # Never stored; sent to the reader.
301
+ class LogCaughtUp(WireModel):
302
+ """The replay is done: everything up to seq has been sent and what follows is live."""
303
+
304
+ seq: int
305
+
306
+
307
+ # This reader missed a stretch: it reconnected too late for the store, or fell behind and the fanout
308
+ # dropped ephemeral entries. When the platform has a snapshot, it is here so the reader can catch up
309
+ # in one step. Never stored; sent to the reader.
310
+ class LogGap(WireModel):
311
+ """This reader missed a stretch."""
312
+
313
+ from_seq: int
314
+ to_seq: int
315
+ snapshot: State | None
316
+
317
+
318
+ # What memory did for this turn or at hangup: a recall before the reply, a remember after the call,
319
+ # a forget on request.
320
+ class MemoryOps(WireModel):
321
+ """What memory did for this turn or at hangup."""
322
+
323
+ ops: list[MemoryOp]
324
+ speech_id: str | None = None
325
+
326
+
327
+ # Ephemeral: it proves the socket is alive and says nothing else.
328
+ class Pong(WireModel):
329
+ """The answer to ping."""
330
+
331
+ ts: float
332
+
333
+
334
+ # The text stays out of the log; its hash and length let two states be compared.
335
+ class PromptChanged(WireModel):
336
+ """A block of the prompt was rewritten."""
337
+
338
+ name: str
339
+ hash: str
340
+ chars: int
341
+
342
+
343
+ class StateCauseTool(WireModel):
344
+ """A tool call's result changed the state."""
345
+
346
+ kind: Literal["tool"] = "tool"
347
+ tool: str
348
+ call_id: str
349
+
350
+
351
+ # A fact from outside changed the state: the app's handler for an event.received moved a field.
352
+ class StateCauseEvent(WireModel):
353
+ """A fact from outside changed the state."""
354
+
355
+ kind: Literal["event"] = "event"
356
+ name: str
357
+ seq: int
358
+
359
+
360
+ # What changed the state: a tool's result, or a fact from outside. Told apart by kind.
361
+ type StateCause = Annotated[StateCauseTool | StateCauseEvent, Field(discriminator="kind")]
362
+
363
+
364
+ # A tool's result or an outside fact caused it, and the cause says which; the whole state travels so
365
+ # a reader never needs the previous entry.
366
+ class StateChanged(WireModel):
367
+ """The app's declared state changed."""
368
+
369
+ state: dict[str, Any]
370
+ changed: list[str]
371
+ cause: StateCause | None = None
372
+
373
+
374
+ # call.ended follows with reason supervisor_ended.
375
+ class SupervisorEnded(WireModel):
376
+ """A supervisor hung up the call."""
377
+
378
+ by: Supervisor
379
+ reason: str | None = None
380
+
381
+
382
+ class SupervisorReleased(WireModel):
383
+ """The supervisor gave the line back; the agent resumes with the history intact."""
384
+
385
+ by: Supervisor
386
+
387
+
388
+ class SupervisorSaid(WireModel):
389
+ """A supervisor made the agent say this to the caller."""
390
+
391
+ by: Supervisor
392
+ text: str
393
+
394
+
395
+ class SupervisorTookOver(WireModel):
396
+ """A supervisor took the line; the agent is quiet until supervisor.released."""
397
+
398
+ by: Supervisor
399
+
400
+
401
+ # call.transferred says how it went.
402
+ class SupervisorTransferred(WireModel):
403
+ """A supervisor asked for a transfer."""
404
+
405
+ by: Supervisor
406
+ to: str
407
+ mode: TransferMode
408
+
409
+
410
+ class SupervisorWhispered(WireModel):
411
+ """A supervisor told the agent something the caller never heard."""
412
+
413
+ by: Supervisor
414
+ text: str
415
+
416
+
417
+ # The platform sends this to the app and the app's method runs in the app's own process; tool.result
418
+ # closes it.
419
+ class ToolCall(WireModel):
420
+ """The model called a tool."""
421
+
422
+ call_id: str
423
+ name: str
424
+ arguments: dict[str, Any]
425
+ speech_id: str | None = None
426
+
427
+
428
+ # The app's state moved and each tool's when was recomputed.
429
+ class ToolsChanged(WireModel):
430
+ """The tools the model can see changed."""
431
+
432
+ visible: list[str]
433
+
434
+
435
+ # With it, everything the session measured about the reply.
436
+ class AgentTurnEnded(WireModel):
437
+ """The agent's reply is over and this is what was said."""
438
+
439
+ speech_id: str
440
+ item_id: str | None = None
441
+ text: str
442
+ interrupted: bool
443
+ metrics: AgentTurnMetrics
444
+
445
+
446
+ # With it, everything the session measured about the turn.
447
+ class UserTurnEnded(WireModel):
448
+ """The caller's turn is over and this is what they said."""
449
+
450
+ speech_id: str
451
+ item_id: str | None = None
452
+ text: str
453
+ language: str | None = None
454
+ transcript_confidence: float | None = None
455
+ metrics: UserTurnMetrics
456
+
457
+
458
+ class UserStateChanged(WireModel):
459
+ """The caller's state changed, in the session's own words."""
460
+
461
+ state: UserState
462
+
463
+
464
+ # Interim while final is false; the final one becomes turn.user. Interim entries are ephemeral.
465
+ class UserTranscript(WireModel):
466
+ """Words from the caller as the recognizer hears them."""
467
+
468
+ text: str
469
+ final: bool
470
+ language: str | None = None
471
+ confidence: float | None = None
@@ -0,0 +1,7 @@
1
+ """The golden call log and the state it reduces to: every consumer proves the same thing."""
2
+
3
+ from importlib.resources import files
4
+ from pathlib import Path
5
+
6
+ GOLDEN_LOG = Path(str(files(__package__) / "call-log-golden.json"))
7
+ GOLDEN_STATE = Path(str(files(__package__) / "call-log-golden.state.json"))