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,407 @@
1
+ """Generated from schema/defs.json: the shapes shared across the wire."""
2
+
3
+ from typing import Any, Literal
4
+
5
+ from pydantic import Field
6
+
7
+ from pinecall_protocol._base import WireModel
8
+
9
+ # The door the public came through: a phone call over SIP, the browser widget over WebRTC, or
10
+ # WhatsApp text.
11
+ type Channel = Literal["phone", "web", "whatsapp"]
12
+
13
+
14
+ # Inbound: the public reached the agent. Outbound: the agent reached out (a dial).
15
+ type Direction = Literal["inbound", "outbound"]
16
+
17
+
18
+ # Which of the two worlds a key opens, and so which world an agent is held in and a call ran in. A
19
+ # key is issued into one; an agent registered on it and every call it takes carry that one; a door
20
+ # claimed in one is refused to a key of the other. `sandbox` is where things are written and
21
+ # `production` is what the public reaches — and whether a sandbox agent is one PERSON's copy or the
22
+ # team's shared one is not this field: it is whether the key that registered it names a person.
23
+ # Every key issued before the field existed is production.
24
+ type Env = Literal["production", "sandbox"]
25
+
26
+
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 from its personas, its goldens
29
+ # and a suite of them, its knowledge folder pushed or its golden asked, its memory goldens, a call
30
+ # promoted to a candidate file, the drift of the last two windows, and the reproductions a broken
31
+ # run left on that disk. Everything else a console needs is a door of the gateway.
32
+ type DevVerb = Literal[
33
+ "chat.roster",
34
+ "chat.start",
35
+ "chat.say",
36
+ "chat.end",
37
+ "simulate.roster",
38
+ "simulate.start",
39
+ "goldens.roster",
40
+ "goldens.run",
41
+ "knowledge.roster",
42
+ "knowledge.push",
43
+ "knowledge.eval",
44
+ "memory.roster",
45
+ "memory.eval",
46
+ "memory.extraction",
47
+ "promote.roster",
48
+ "promote.write",
49
+ "drift.read",
50
+ "reproductions.roster",
51
+ "reproductions.read",
52
+ ]
53
+
54
+
55
+ # Why the call is over. Who hung up, what failed before anybody could, drained: the platform took
56
+ # the worker down (a deploy, a stop) with the call still on it, or app_detached: the app holding the
57
+ # agent closed its socket mid-call, so nothing was rendering the prompt or answering a tool — both
58
+ # are nobody's fault and neither is an error.
59
+ type EndReason = Literal[
60
+ "caller_hung_up",
61
+ "agent_hung_up",
62
+ "supervisor_ended",
63
+ "transferred",
64
+ "no_answer",
65
+ "busy",
66
+ "dial_failed",
67
+ "timeout",
68
+ "drained",
69
+ "app_detached",
70
+ "error",
71
+ ]
72
+
73
+
74
+ # Whose action ended the call. platform covers timeouts, errors and a drained worker.
75
+ type EndedBy = Literal["caller", "agent", "supervisor", "platform"]
76
+
77
+
78
+ # What one judge answered about a finished call. Held: the rule held. Broken: it did not, and the
79
+ # reason names the evidence. Deferred: the judge was asked and could not settle it. Skipped: nobody
80
+ # asked it — no model was reachable inside the call's judging budget.
81
+ type ScoreVerdict = Literal["held", "broken", "deferred", "skipped"]
82
+
83
+
84
+ # Cold: the caller is sent on and the agent leaves. Warm: the agent stays on the line until the
85
+ # other side answers, then leaves.
86
+ type TransferMode = Literal["cold", "warm"]
87
+
88
+
89
+ # Which region of the prompt a block lives in: static, before the history, cached by the provider;
90
+ # or dynamic, after the history, replaced every turn. The append-only history in between is never
91
+ # written by the app.
92
+ type PromptRegion = Literal["static", "dynamic"]
93
+
94
+
95
+ # How the knowledge base reaches the model: retrieved, the platform runs search itself when the
96
+ # caller's turn ends; or tool, the model calls search when it decides to. Either way the chunks
97
+ # arrive as a tool result.
98
+ type DocsMode = Literal["retrieved", "tool"]
99
+
100
+
101
+ # The two tools the platform runs on the app's behalf: recall reads the contact's facts out of
102
+ # memory, search reads chunks out of the knowledge base. The app declares memory and docs and writes
103
+ # neither method; the platform runs the lookup, and the answer reaches the model as a tool result
104
+ # rather than as part of the prompt.
105
+ type PlatformTool = Literal["recall", "search"]
106
+
107
+
108
+ # The default layout, when an agent declares none, is identity, knowledge and tools (static), then
109
+ # the history, then view (dynamic). A block's text is written per call with prompt.set.
110
+ class PromptBlockSpec(WireModel):
111
+ """One named block of the prompt and the region it lives in."""
112
+
113
+ name: str = Field(pattern="^[a-z][a-z0-9_]*$")
114
+ region: PromptRegion
115
+
116
+
117
+ # What the platform believes the person on the line is doing right now. The states are the session's
118
+ # own.
119
+ type UserState = Literal["listening", "speaking", "away"]
120
+
121
+
122
+ # What the agent is doing right now, in the session's own words: warming up, waiting, hearing the
123
+ # caller, generating, or playing audio.
124
+ type AgentState = Literal["initializing", "idle", "listening", "thinking", "speaking"]
125
+
126
+
127
+ # Who a participant is to the call: the person the agent serves (over SIP or the widget), the agent
128
+ # itself, a supervisor who took a seat in the room, a listener who only hears, or a second SIP leg
129
+ # that room.invite brought in.
130
+ type ParticipantKind = Literal["caller", "agent", "supervisor", "listener", "sip"]
131
+
132
+
133
+ # What a track carries: a microphone's audio, a camera's video, or a screen share.
134
+ type TrackKind = Literal["audio", "video", "screen"]
135
+
136
+
137
+ # Where a track comes from, as livekit's TrackSource names it, in lower case.
138
+ type TrackSource = Literal["microphone", "camera", "screen_share", "screen_share_audio", "unknown"]
139
+
140
+
141
+ # Where an outside fact came from: the tenant's backend over the app socket (app), or a
142
+ # participant's browser over the DataChannel (participant).
143
+ type EventSource = Literal["app", "participant"]
144
+
145
+
146
+ # Who may see a field of the app's state: everyone in the call (public), the tenant's own readers
147
+ # (tenant, the default for a field never declared), or nobody without masking (pii).
148
+ type Visibility = Literal["public", "tenant", "pii"]
149
+
150
+
151
+ # Which projection a sink applies before a state or an entry leaves the platform: public for a
152
+ # participant reading its own call, tenant for the tenant's readers. The contract is
153
+ # docs/protocol/projections.md; a client never applies one.
154
+ type Projection = Literal["public", "tenant"]
155
+
156
+
157
+ # Everything is optional: a web visitor may be nobody yet.
158
+ class Contact(WireModel):
159
+ """Who is on the line, as far as the platform knows."""
160
+
161
+ id: str | None = None
162
+ phone: str | None = None
163
+ name: str | None = None
164
+ email: str | None = None
165
+ external_id: str | None = None
166
+
167
+
168
+ # A number is a route, never an agent.
169
+ class Route(WireModel):
170
+ """One door to an agent: a channel and, for phone and WhatsApp, the number that answers."""
171
+
172
+ channel: Channel
173
+ number: str | None
174
+ label: str | None = None
175
+
176
+
177
+ class Supervisor(WireModel):
178
+ """The human who sent a supervise verb, as the token that let them in names them."""
179
+
180
+ id: str
181
+ name: str | None = None
182
+
183
+
184
+ # What the app declares about one tool: the contract the model sees and the rules the platform
185
+ # enforces before running it.
186
+ class ToolSpec(WireModel):
187
+ """What the app declares about one tool."""
188
+
189
+ name: str
190
+ description: str
191
+ parameters: dict[str, Any]
192
+ side_effect: Literal["read", "write", "irreversible"] = "read"
193
+ confirm: str | None = None
194
+ pii: list[str] | None = None
195
+ timeout_s: float | None = None
196
+
197
+
198
+ # Either an output or an error, never both.
199
+ class ToolResult(WireModel):
200
+ """What came back from running a tool in the app's process."""
201
+
202
+ call_id: str
203
+ name: str
204
+ output: Any = None
205
+ error: str | None = None
206
+ summary: str | None = None
207
+ duration_s: float | None = None
208
+
209
+
210
+ # One thing remembered about a contact: a sentence, where it came from, and how well it matched when
211
+ # recalled.
212
+ class MemoryFact(WireModel):
213
+ """One thing remembered about a contact."""
214
+
215
+ id: str | None = None
216
+ text: str
217
+ category: str | None = None
218
+ score: float | None = None
219
+ source: str | None = None
220
+
221
+
222
+ # One operation against the contact's memory: a recall during the turn, a remember at hangup, or a
223
+ # forget on request.
224
+ class MemoryOp(WireModel):
225
+ """One operation against the contact's memory."""
226
+
227
+ op: Literal["recall", "remember", "forget"]
228
+ contact: str | None = None
229
+ query: str | None = None
230
+ facts: list[MemoryFact]
231
+ took_ms: float
232
+
233
+
234
+ class DocSource(WireModel):
235
+ """One chunk of the knowledge base that retrieval put in front of the model for this turn."""
236
+
237
+ id: str
238
+ path: str
239
+ heading: str | None = None
240
+ score: float
241
+ excerpt: str | None = None
242
+
243
+
244
+ class CostRate(WireModel):
245
+ """The exchange rate the cost was computed with, stated so the number can be reproduced."""
246
+
247
+ currency: Literal["EUR"] = "EUR"
248
+ usd_to_eur: float
249
+ as_of: str
250
+
251
+
252
+ class CostRow(WireModel):
253
+ """One priced line: a model, what was counted, how much, and what it came to."""
254
+
255
+ provider: str
256
+ model: str
257
+ unit: Literal[
258
+ "input_tokens",
259
+ "cached_input_tokens",
260
+ "cache_creation_tokens",
261
+ "output_tokens",
262
+ "characters",
263
+ "audio_seconds",
264
+ "requests",
265
+ "session_seconds",
266
+ ]
267
+ quantity: float
268
+ unit_price_usd: float
269
+ eur: float
270
+
271
+
272
+ # It is listed, never priced at zero.
273
+ class UnpricedRow(WireModel):
274
+ """A usage row the price table does not know."""
275
+
276
+ provider: str
277
+ model: str
278
+
279
+
280
+ # The runtime never prices commercially; this is the provider's bill as best we know it.
281
+ class Cost(WireModel):
282
+ """What the call cost in provider fees, informational, in euros."""
283
+
284
+ eur: float
285
+ rate: CostRate
286
+ rows: list[CostRow]
287
+ unpriced: list[UnpricedRow]
288
+
289
+
290
+ # Which voice speaks for the agent: the name it was asked for, or the id the provider knows it by.
291
+ class VoiceConfig(WireModel):
292
+ """Which voice speaks for the agent."""
293
+
294
+ name: str | None = None
295
+ provider: str | None = None
296
+ model: str | None = None
297
+ voice_id: str | None = None
298
+
299
+
300
+ class ModelConfig(WireModel):
301
+ """Which model does a job (the LLM, or the STT), and the one or two knobs worth turning."""
302
+
303
+ provider: str
304
+ model: str
305
+ temperature: float | None = None
306
+
307
+
308
+ class TurnConfig(WireModel):
309
+ """How the session decides that the caller has finished, and when the caller may interrupt."""
310
+
311
+ min_interruption_words: int | None = None
312
+ endpointing_ms: int | None = None
313
+
314
+
315
+ # How the voice says one word it would otherwise get wrong: a proper name, a brand, a street.
316
+ class Pronunciation(WireModel):
317
+ """How the voice says one word it would otherwise get wrong."""
318
+
319
+ word: str
320
+ spoken: str
321
+
322
+
323
+ # A field never declared is tenant.
324
+ class StateFieldSpec(WireModel):
325
+ """What the app declares about one field of its state: who may see it."""
326
+
327
+ name: str
328
+ visibility: Visibility
329
+
330
+
331
+ # An event nobody declared is refused before it touches the log.
332
+ class EventSpec(WireModel):
333
+ """One outside event the agent accepts, and from whom."""
334
+
335
+ name: str
336
+ from_: list[EventSource] = Field(alias="from")
337
+
338
+
339
+ class KnowledgeFile(WireModel):
340
+ """One file of knowledge, sent whole: its path as the tenant keeps it, and its text."""
341
+
342
+ path: str
343
+ text: str
344
+
345
+
346
+ # It is named by the base it was pushed under, with PUT /v1/knowledge/{base}.
347
+ class DocsConfig(WireModel):
348
+ """The knowledge base the agent answers from, and how its chunks reach the model."""
349
+
350
+ base: str
351
+ mode: DocsMode = "retrieved"
352
+ k: int = 8
353
+ min_score: float | None = None
354
+
355
+
356
+ # Exactly one of the two, because there are only two ways to open one: `say` are the words
357
+ # themselves and `reply` is what the model is told before it finds its own. They are agent.say and
358
+ # agent.reply declared instead of called, so a class that opens every call the same way needs no
359
+ # onCall hook to do it, and an operator can turn the opening at the pipeline door without a deploy.
360
+ # Absent: nobody speaks until the caller does.
361
+ class GreetingConfig(WireModel):
362
+ """How the agent opens a call, before the caller has said anything."""
363
+
364
+ say: str | None = None
365
+ reply: str | None = None
366
+ allow_interruptions: bool | None = None
367
+
368
+
369
+ # Declaring this is what puts livekit's own end_call tool in front of the model; a class that says
370
+ # nothing here cannot hang up, and the call ends when the caller does or when a supervisor says so.
371
+ # The tool is hidden while the agent is greeting, because a model that can hang up on its first turn
372
+ # eventually does.
373
+ class HangupConfig(WireModel):
374
+ """Whether the model may end the call itself."""
375
+
376
+ when: str = ""
377
+
378
+
379
+ # Both lists are in the tenant's own words.
380
+ class MemoryConfig(WireModel):
381
+ """What memory keeps about a contact across calls, and what it must never keep."""
382
+
383
+ remember: list[str] = Field(default_factory=list[str])
384
+ forget: list[str] = Field(default_factory=list[str])
385
+
386
+
387
+ # What an app declares about its agent: the voice, the models, the language, the greeting, the
388
+ # tools, and who may see and send what. Every field is optional so a configure can change one thing.
389
+ class AgentConfig(WireModel):
390
+ """What an app declares about its agent."""
391
+
392
+ prompt: list[PromptBlockSpec] | None = None
393
+ language: str | None = None
394
+ greeting: GreetingConfig | None = None
395
+ voice: VoiceConfig | None = None
396
+ llm: ModelConfig | None = None
397
+ stt: ModelConfig | None = None
398
+ turn: TurnConfig | None = None
399
+ says: list[Pronunciation] | None = None
400
+ hears: list[str] | None = None
401
+ knowledge: KnowledgeFile | None = None
402
+ docs: DocsConfig | None = None
403
+ memory: MemoryConfig | None = None
404
+ hangup: HangupConfig | None = None
405
+ tools: list[ToolSpec] | None = None
406
+ state_fields: list[StateFieldSpec] | None = None
407
+ events: list[EventSpec] | None = None
@@ -0,0 +1,30 @@
1
+ """Generated from schema/envelope.json: the log entry and the command frame."""
2
+
3
+ from typing import Any
4
+
5
+ from pinecall_protocol._base import WireModel
6
+
7
+
8
+ # seq is written before control returns, so two readers never disagree about order.
9
+ class Entry(WireModel):
10
+ """One line of a call's log, or of an agent's log when call is null."""
11
+
12
+ seq: int
13
+ ts: float
14
+ call: str | None
15
+ agent: str
16
+ type: str
17
+ ephemeral: bool
18
+ data: dict[str, Any]
19
+
20
+
21
+ # The gateway answers with the events the command produces, or with an error naming the command's
22
+ # id.
23
+ class Command(WireModel):
24
+ """One instruction from an app to the gateway over its WebSocket."""
25
+
26
+ type: str
27
+ agent: str
28
+ call: str | None
29
+ id: str | None = None
30
+ data: dict[str, Any]