pinecall-runtime 0.1.6__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.
- pinecall/__init__.py +0 -0
- pinecall/channels/__init__.py +0 -0
- pinecall/channels/_chooser.py +45 -0
- pinecall/channels/offers.py +219 -0
- pinecall/channels/rooms.py +176 -0
- pinecall/channels/routes.py +275 -0
- pinecall/channels/telephony/__init__.py +0 -0
- pinecall/channels/telephony/_twilio.py +349 -0
- pinecall/channels/telephony/carrier.py +229 -0
- pinecall/channels/telephony/carrier_catalog.py +92 -0
- pinecall/channels/telephony/carriers.csv +41 -0
- pinecall/channels/telephony/dialing.py +419 -0
- pinecall/channels/telephony/firewall.py +58 -0
- pinecall/channels/telephony/hand_over.py +78 -0
- pinecall/channels/telephony/number_path.py +176 -0
- pinecall/channels/telephony/numbers.py +530 -0
- pinecall/channels/telephony/origination.py +77 -0
- pinecall/channels/telephony/sip.py +448 -0
- pinecall/channels/whatsapp.py +413 -0
- pinecall/cli/__init__.py +0 -0
- pinecall/cli/_archive.py +85 -0
- pinecall/cli/_drift.py +38 -0
- pinecall/cli/_facts.py +67 -0
- pinecall/cli/_load.py +433 -0
- pinecall/cli/_operator.py +626 -0
- pinecall/cli/_sessions.py +164 -0
- pinecall/cli/_traceback.py +95 -0
- pinecall/cli/_usage.py +34 -0
- pinecall/cli/main.py +521 -0
- pinecall/domain/__init__.py +0 -0
- pinecall/domain/agent.py +446 -0
- pinecall/domain/call.py +152 -0
- pinecall/domain/country_codes.py +32 -0
- pinecall/domain/errors.py +151 -0
- pinecall/domain/names.py +115 -0
- pinecall/domain/org.py +120 -0
- pinecall/domain/person.py +208 -0
- pinecall/domain/scope.py +21 -0
- pinecall/evals/__init__.py +0 -0
- pinecall/evals/_evidence.py +158 -0
- pinecall/evals/calibration.py +112 -0
- pinecall/evals/callers.py +160 -0
- pinecall/evals/case.py +327 -0
- pinecall/evals/checks.py +437 -0
- pinecall/evals/compliance.py +197 -0
- pinecall/evals/dataset.py +451 -0
- pinecall/evals/goldens.py +331 -0
- pinecall/evals/judges.py +525 -0
- pinecall/evals/runs.py +252 -0
- pinecall/evals/spoken.py +356 -0
- pinecall/fleet/__init__.py +0 -0
- pinecall/fleet/client.py +569 -0
- pinecall/fleet/demand.py +51 -0
- pinecall/fleet/heartbeat.py +194 -0
- pinecall/fleet/measures.py +157 -0
- pinecall/fleet/roster.py +232 -0
- pinecall/fleet/worlds.py +44 -0
- pinecall/gateway/__init__.py +0 -0
- pinecall/gateway/_call_setup.py +53 -0
- pinecall/gateway/_deps.py +556 -0
- pinecall/gateway/_gateway.py +75 -0
- pinecall/gateway/_served.py +396 -0
- pinecall/gateway/_sockets.py +444 -0
- pinecall/gateway/_streams.py +83 -0
- pinecall/gateway/_text_calls.py +182 -0
- pinecall/gateway/api/__init__.py +0 -0
- pinecall/gateway/api/accounts.py +324 -0
- pinecall/gateway/api/agents.py +150 -0
- pinecall/gateway/api/apps.py +478 -0
- pinecall/gateway/api/box.py +658 -0
- pinecall/gateway/api/calibration.py +58 -0
- pinecall/gateway/api/callbacks.py +105 -0
- pinecall/gateway/api/calls.py +655 -0
- pinecall/gateway/api/chat.py +223 -0
- pinecall/gateway/api/dataset.py +163 -0
- pinecall/gateway/api/desk.py +113 -0
- pinecall/gateway/api/entries.py +77 -0
- pinecall/gateway/api/evals.py +647 -0
- pinecall/gateway/api/fleet.py +73 -0
- pinecall/gateway/api/hosting.py +241 -0
- pinecall/gateway/api/judges.py +97 -0
- pinecall/gateway/api/keys.py +110 -0
- pinecall/gateway/api/line.py +142 -0
- pinecall/gateway/api/members.py +197 -0
- pinecall/gateway/api/metrics.py +226 -0
- pinecall/gateway/api/numbers.py +476 -0
- pinecall/gateway/api/ops.py +428 -0
- pinecall/gateway/api/org.py +283 -0
- pinecall/gateway/api/personas.py +157 -0
- pinecall/gateway/api/pipeline.py +206 -0
- pinecall/gateway/api/prompts.py +50 -0
- pinecall/gateway/api/providers.py +240 -0
- pinecall/gateway/api/recordings.py +89 -0
- pinecall/gateway/api/relay.py +169 -0
- pinecall/gateway/api/retrieval.py +403 -0
- pinecall/gateway/api/runner.py +109 -0
- pinecall/gateway/api/sessions.py +102 -0
- pinecall/gateway/api/settings.py +463 -0
- pinecall/gateway/api/signup.py +164 -0
- pinecall/gateway/api/sso_login.py +168 -0
- pinecall/gateway/api/threads.py +187 -0
- pinecall/gateway/api/usage.py +303 -0
- pinecall/gateway/api/visitors.py +222 -0
- pinecall/gateway/api/whatsapp.py +48 -0
- pinecall/gateway/api/widget.py +49 -0
- pinecall/gateway/app.py +504 -0
- pinecall/gateway/calls/__init__.py +0 -0
- pinecall/gateway/calls/binding.py +63 -0
- pinecall/gateway/calls/commands.py +18 -0
- pinecall/gateway/calls/inbox.py +100 -0
- pinecall/gateway/calls/known.py +23 -0
- pinecall/gateway/calls/owners.py +87 -0
- pinecall/gateway/calls/pump.py +33 -0
- pinecall/gateway/calls/serving.py +118 -0
- pinecall/gateway/calls/threads.py +367 -0
- pinecall/gateway/dispatching/__init__.py +0 -0
- pinecall/gateway/dispatching/arrivals.py +58 -0
- pinecall/gateway/dispatching/sweep.py +43 -0
- pinecall/gateway/ending/__init__.py +0 -0
- pinecall/gateway/ending/reaper.py +160 -0
- pinecall/gateway/ending/seal.py +312 -0
- pinecall/gateway/ending/stranded.py +117 -0
- pinecall/log/__init__.py +0 -0
- pinecall/log/_histogram.py +74 -0
- pinecall/log/_relay.py +259 -0
- pinecall/log/_writer.py +454 -0
- pinecall/log/days.py +154 -0
- pinecall/log/drift.py +550 -0
- pinecall/log/facts.py +358 -0
- pinecall/log/inbox.py +169 -0
- pinecall/log/lists.py +155 -0
- pinecall/log/logs.py +581 -0
- pinecall/log/openings.py +71 -0
- pinecall/log/private.py +135 -0
- pinecall/log/queries.py +229 -0
- pinecall/log/readers.py +207 -0
- pinecall/log/reduce.py +659 -0
- pinecall/log/refold.py +217 -0
- pinecall/log/store.py +370 -0
- pinecall/log/usage.py +242 -0
- pinecall/postgres/__init__.py +0 -0
- pinecall/postgres/migrate.py +202 -0
- pinecall/postgres/migrations/0001_schema.sql +591 -0
- pinecall/postgres/migrations/0002_worlds.sql +15 -0
- pinecall/postgres/migrations/0003_lent.sql +4 -0
- pinecall/postgres/migrations/0004_carriers.sql +23 -0
- pinecall/postgres/migrations/0005_retrieved_only.sql +11 -0
- pinecall/postgres/migrations/0006_persona_agents.sql +9 -0
- pinecall/postgres/migrations/0007_money_in_dollars.sql +27 -0
- pinecall/postgres/migrations/0008_lexicon_per_agent.sql +27 -0
- pinecall/postgres/migrations/0009_personas_per_agent.sql +31 -0
- pinecall/postgres/migrations/0010_agent_judges.sql +12 -0
- pinecall/postgres/migrations/0011_erasure.sql +32 -0
- pinecall/postgres/migrations/0012_org_policy.sql +10 -0
- pinecall/postgres/migrations/0013_calling_rules.sql +12 -0
- pinecall/postgres/migrations/0014_consents.sql +24 -0
- pinecall/postgres/migrations/0015_disclosure.sql +7 -0
- pinecall/postgres/migrations/0016_call_records.sql +21 -0
- pinecall/postgres/migrations/0017_reads.sql +17 -0
- pinecall/postgres/migrations/0018_hosting.sql +41 -0
- pinecall/postgres/migrations/0019_hosted_state.sql +10 -0
- pinecall/postgres/migrations/0020_written_once.sql +7 -0
- pinecall/postgres/migrations/0021_hosted_running.sql +21 -0
- pinecall/postgres/migrations/0022_sealed.sql +39 -0
- pinecall/postgres/migrations/0023_usage_totals.sql +158 -0
- pinecall/postgres/migrations/0024_log_key_by_day.sql +8 -0
- pinecall/postgres/migrations/0025_log_before_the_days.sql +15 -0
- pinecall/postgres/migrations/0026_log_before_the_days_validated.sql +4 -0
- pinecall/postgres/migrations/0027_log_by_day.sql +96 -0
- pinecall/postgres/migrations/0030_whatsapp_seen.sql +17 -0
- pinecall/postgres/migrations/0040_reads_widened.sql +15 -0
- pinecall/postgres/migrations/0060_drift.sql +62 -0
- pinecall/postgres/migrations/0061_canaries.sql +24 -0
- pinecall/postgres/migrations/0062_eval_cases.sql +23 -0
- pinecall/postgres/migrations/0063_prompts.sql +16 -0
- pinecall/postgres/migrations/0075_call_private.sql +14 -0
- pinecall/postgres/migrations/0076_recording_keys.sql +13 -0
- pinecall/postgres/migrations/0077_tokens_world.sql +7 -0
- pinecall/postgres/migrations/0078_facts_cost_by_stage.sql +12 -0
- pinecall/postgres/migrations/0079_judge_labels.sql +20 -0
- pinecall/postgres/migrations/0080_call_openings.sql +20 -0
- pinecall/postgres/migrations/0081_seal_lease.sql +7 -0
- pinecall/postgres/migrations/0082_caller_codes.sql +14 -0
- pinecall/postgres/migrations/0083_one_use_words.sql +15 -0
- pinecall/postgres/migrations/0084_knocks_and_run_leases.sql +23 -0
- pinecall/postgres/migrations/0085_seal_lease_holder.sql +8 -0
- pinecall/postgres/migrations/0086_route_origin.sql +17 -0
- pinecall/postgres/migrations/0087_carrier_networks.sql +20 -0
- pinecall/postgres/migrations/0088_route_last_call.sql +3 -0
- pinecall/postgres/migrations/0089_offers.sql +21 -0
- pinecall/postgres/migrations/0090_invitation_purpose.sql +5 -0
- pinecall/postgres/migrations/0091_head_opened_by.sql +6 -0
- pinecall/postgres/migrations/0092_key_parent.sql +6 -0
- pinecall/postgres/migrations/0093_code_tries.sql +10 -0
- pinecall/postgres/migrations/0094_route_approval.sql +12 -0
- pinecall/postgres/migrations/0095_sso_domain_proofs.sql +17 -0
- pinecall/postgres/migrations/0096_row_level_security.sql +38 -0
- pinecall/postgres/migrations/0097_case_status.sql +19 -0
- pinecall/postgres/pool.py +190 -0
- pinecall/process/__init__.py +0 -0
- pinecall/process/_objects.py +97 -0
- pinecall/process/box_settings.py +23 -0
- pinecall/process/connections.py +124 -0
- pinecall/process/metrics.py +127 -0
- pinecall/process/recordings.py +323 -0
- pinecall/process/resolver.py +41 -0
- pinecall/process/sealed_audio.py +173 -0
- pinecall/process/settings.py +534 -0
- pinecall/process/shared.py +158 -0
- pinecall/process/signal.py +416 -0
- pinecall/providers/__init__.py +0 -0
- pinecall/providers/build.py +570 -0
- pinecall/providers/catalog.py +266 -0
- pinecall/providers/credentials.py +230 -0
- pinecall/providers/declared.py +69 -0
- pinecall/providers/prices.py +240 -0
- pinecall/providers/tried.py +104 -0
- pinecall/providers/voices.py +174 -0
- pinecall/public/console/assets/index-BIfKPSgn.js +164 -0
- pinecall/public/console/assets/index-ClOTU-xX.css +1 -0
- pinecall/public/console/favicon.png +0 -0
- pinecall/public/console/index.html +20 -0
- pinecall/public/console/logo-mark.png +0 -0
- pinecall/public/console/pinecall-logo-dark.png +0 -0
- pinecall/public/console/pinecall-logo.png +0 -0
- pinecall/public/console/pinecall-mark.png +0 -0
- pinecall/public/console/sw.js +37 -0
- pinecall/public/widget/pinecall-widget.js +853 -0
- pinecall/py.typed +0 -0
- pinecall/retrieval/__init__.py +0 -0
- pinecall/retrieval/_search.py +186 -0
- pinecall/retrieval/_sections.py +70 -0
- pinecall/retrieval/embed.py +340 -0
- pinecall/retrieval/extraction.py +403 -0
- pinecall/retrieval/knowledge.py +659 -0
- pinecall/retrieval/lookups.py +192 -0
- pinecall/retrieval/memory.py +603 -0
- pinecall/runner/__init__.py +0 -0
- pinecall/runner/_kube.py +459 -0
- pinecall/runner/_plan.py +157 -0
- pinecall/runner/main.py +306 -0
- pinecall/session/__init__.py +0 -0
- pinecall/session/_agent.py +141 -0
- pinecall/session/_hearing.py +101 -0
- pinecall/session/_livekit.py +195 -0
- pinecall/session/_prompt.py +147 -0
- pinecall/session/_recording.py +237 -0
- pinecall/session/call.py +367 -0
- pinecall/session/clock.py +40 -0
- pinecall/session/hold.py +239 -0
- pinecall/session/room.py +626 -0
- pinecall/session/session.py +690 -0
- pinecall/session/text.py +144 -0
- pinecall/session/tools.py +483 -0
- pinecall/session/voice.py +118 -0
- pinecall/session/widget.py +201 -0
- pinecall/tenancy/__init__.py +0 -0
- pinecall/tenancy/admission.py +204 -0
- pinecall/tenancy/agents.py +164 -0
- pinecall/tenancy/canary.py +126 -0
- pinecall/tenancy/carrier_networks.py +156 -0
- pinecall/tenancy/carriers.py +265 -0
- pinecall/tenancy/codes.py +196 -0
- pinecall/tenancy/consents.py +167 -0
- pinecall/tenancy/dial_policy.py +405 -0
- pinecall/tenancy/disclosure.py +41 -0
- pinecall/tenancy/erasure.py +204 -0
- pinecall/tenancy/export.py +167 -0
- pinecall/tenancy/hosted_running.py +166 -0
- pinecall/tenancy/hosting.py +475 -0
- pinecall/tenancy/judges.py +126 -0
- pinecall/tenancy/keys.py +467 -0
- pinecall/tenancy/knocks.py +40 -0
- pinecall/tenancy/letters.py +378 -0
- pinecall/tenancy/mail.py +458 -0
- pinecall/tenancy/org_secrets.py +123 -0
- pinecall/tenancy/orgs.py +78 -0
- pinecall/tenancy/people.py +543 -0
- pinecall/tenancy/personas.py +145 -0
- pinecall/tenancy/policy.py +65 -0
- pinecall/tenancy/prompts.py +48 -0
- pinecall/tenancy/reads.py +73 -0
- pinecall/tenancy/recording_keys.py +41 -0
- pinecall/tenancy/remembered.py +148 -0
- pinecall/tenancy/retention.py +86 -0
- pinecall/tenancy/scopes.py +406 -0
- pinecall/tenancy/signin.py +622 -0
- pinecall/tenancy/spend.py +69 -0
- pinecall/tenancy/sso.py +431 -0
- pinecall/tenancy/sso_domains.py +132 -0
- pinecall/tenancy/throttle.py +88 -0
- pinecall/tenancy/tokens.py +400 -0
- pinecall/tenancy/traceback.py +70 -0
- pinecall/tenancy/usage.py +136 -0
- pinecall/tenancy/vault.py +204 -0
- pinecall/tenancy/words.py +144 -0
- pinecall/wire/__init__.py +0 -0
- pinecall/wire/commands.py +350 -0
- pinecall/wire/events.py +655 -0
- pinecall/wire/frames.py +65 -0
- pinecall/wire/metrics.py +287 -0
- pinecall/wire/parts.py +320 -0
- pinecall/wire/rest/__init__.py +0 -0
- pinecall/wire/rest/accounts.py +512 -0
- pinecall/wire/rest/agents.py +172 -0
- pinecall/wire/rest/calls.py +397 -0
- pinecall/wire/rest/evals.py +453 -0
- pinecall/wire/rest/fleet.py +69 -0
- pinecall/wire/rest/hosting.py +174 -0
- pinecall/wire/rest/numbers.py +280 -0
- pinecall/wire/rest/ops.py +342 -0
- pinecall/wire/rest/providers.py +83 -0
- pinecall/wire/rest/retrieval.py +301 -0
- pinecall/wire/rest/settings.py +184 -0
- pinecall/wire/rest/usage.py +245 -0
- pinecall/wire/room.py +61 -0
- pinecall/wire/scores.py +49 -0
- pinecall/wire/state.py +243 -0
- pinecall/worker/__init__.py +0 -0
- pinecall/worker/_job.py +655 -0
- pinecall/worker/_recorder.py +82 -0
- pinecall/worker/_traces.py +48 -0
- pinecall/worker/main.py +307 -0
- pinecall_runtime-0.1.6.dist-info/METADATA +212 -0
- pinecall_runtime-0.1.6.dist-info/RECORD +328 -0
- pinecall_runtime-0.1.6.dist-info/WHEEL +4 -0
- pinecall_runtime-0.1.6.dist-info/entry_points.txt +2 -0
- pinecall_runtime-0.1.6.dist-info/licenses/LICENSE +202 -0
pinecall/wire/events.py
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
"""Every event a log holds, and the registry of them, the room's (`room.py`) included."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from pinecall.domain.agent import EventSource
|
|
8
|
+
from pinecall.domain.errors import DeclarationRefused
|
|
9
|
+
from pinecall.domain.names import Channel, Direction, Env, JsonObject, Medium
|
|
10
|
+
from pinecall.domain.org import QuotaName
|
|
11
|
+
from pinecall.wire.frames import Entry, WireModel
|
|
12
|
+
from pinecall.wire.metrics import (
|
|
13
|
+
AgentTurnMetrics,
|
|
14
|
+
AvatarMetrics,
|
|
15
|
+
EOTInferenceMetrics,
|
|
16
|
+
EOUMetrics,
|
|
17
|
+
InterruptionMetrics,
|
|
18
|
+
LLMMetrics,
|
|
19
|
+
ModelUsage,
|
|
20
|
+
RealtimeModelMetrics,
|
|
21
|
+
STTMetrics,
|
|
22
|
+
TTSMetrics,
|
|
23
|
+
UserTurnMetrics,
|
|
24
|
+
VADMetrics,
|
|
25
|
+
)
|
|
26
|
+
from pinecall.wire.parts import (
|
|
27
|
+
AgentState,
|
|
28
|
+
Contact,
|
|
29
|
+
Cost,
|
|
30
|
+
DevVerb,
|
|
31
|
+
DocSource,
|
|
32
|
+
EndedBy,
|
|
33
|
+
EndReason,
|
|
34
|
+
MemoryOp,
|
|
35
|
+
Projection,
|
|
36
|
+
Route,
|
|
37
|
+
Supervisor,
|
|
38
|
+
ToolResult,
|
|
39
|
+
TransferMode,
|
|
40
|
+
UserState,
|
|
41
|
+
)
|
|
42
|
+
from pinecall.wire.room import (
|
|
43
|
+
ParticipantJoined,
|
|
44
|
+
ParticipantLeft,
|
|
45
|
+
ParticipantSpeaking,
|
|
46
|
+
RoomOpened,
|
|
47
|
+
RoomSent,
|
|
48
|
+
TrackPublished,
|
|
49
|
+
TrackUnpublished,
|
|
50
|
+
)
|
|
51
|
+
from pinecall.wire.scores import CallScore
|
|
52
|
+
from pinecall.wire.state import State
|
|
53
|
+
|
|
54
|
+
# Events a store may drop and a slow reader may miss: the entry's ephemeral flag defaults to this.
|
|
55
|
+
EPHEMERAL_EVENTS: frozenset[str] = frozenset(
|
|
56
|
+
{
|
|
57
|
+
"agent.transcript",
|
|
58
|
+
"dev.request",
|
|
59
|
+
"log.caught_up",
|
|
60
|
+
"log.gap",
|
|
61
|
+
"metrics.vad",
|
|
62
|
+
"participant.speaking",
|
|
63
|
+
"pong",
|
|
64
|
+
"room.sent",
|
|
65
|
+
"user.transcript",
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# The one event that ends a call: after it nothing more is true and the log is sealed.
|
|
71
|
+
TERMINAL_EVENT = "call.score"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# What the gateway writes on a call's log itself, and no worker ever does: the arrival, a code
|
|
75
|
+
# claimed, a socket attached, the summary and the score, memory and sources, and the markers a
|
|
76
|
+
# reader is sent and no store keeps. The append doors refuse these from anybody.
|
|
77
|
+
GATEWAY_WRITES: frozenset[str] = frozenset(
|
|
78
|
+
{
|
|
79
|
+
"call.ringing",
|
|
80
|
+
"call.dialing",
|
|
81
|
+
"call.attached",
|
|
82
|
+
"call.claimed",
|
|
83
|
+
"call.summary",
|
|
84
|
+
"call.score",
|
|
85
|
+
"memory.ops",
|
|
86
|
+
"docs.sources",
|
|
87
|
+
"log.gap",
|
|
88
|
+
"log.caught_up",
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class EventReceived(WireModel):
|
|
94
|
+
"""A fact arrived from outside the conversation, from the app or a participant's browser."""
|
|
95
|
+
|
|
96
|
+
name: str
|
|
97
|
+
data: JsonObject
|
|
98
|
+
source: EventSource
|
|
99
|
+
identity: str | None = None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class AgentConfigured(WireModel):
|
|
103
|
+
"""The gateway applied an agent.configure; the next call starts with the new config."""
|
|
104
|
+
|
|
105
|
+
changed: list[str]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class AgentDetached(WireModel):
|
|
109
|
+
"""One socket stopped holding the agent."""
|
|
110
|
+
|
|
111
|
+
app: str
|
|
112
|
+
env: Env
|
|
113
|
+
left: bool
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class AgentDraining(WireModel):
|
|
117
|
+
"""One socket let go of its calls without cutting them."""
|
|
118
|
+
|
|
119
|
+
app: str
|
|
120
|
+
env: Env
|
|
121
|
+
handed: int
|
|
122
|
+
parked: int
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class AgentRegistered(WireModel):
|
|
126
|
+
"""The gateway accepted an agent.register: this socket now speaks for the agent."""
|
|
127
|
+
|
|
128
|
+
routes: list[Route]
|
|
129
|
+
app: str
|
|
130
|
+
sdk: str | None = None
|
|
131
|
+
env: Env | None = None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class AgentStateChanged(WireModel):
|
|
135
|
+
"""The agent's state changed, in the session's own words."""
|
|
136
|
+
|
|
137
|
+
state: AgentState
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class AgentTranscript(WireModel):
|
|
141
|
+
"""One delta of the reply the agent is giving, never the reply so far."""
|
|
142
|
+
|
|
143
|
+
speech_id: str
|
|
144
|
+
text: str
|
|
145
|
+
final: bool
|
|
146
|
+
start: float | None = None
|
|
147
|
+
end: float | None = None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class AttentionAnswered(WireModel):
|
|
151
|
+
"""An ask for a person settled: a supervisor took the line, or the wait ran out."""
|
|
152
|
+
|
|
153
|
+
ok: bool
|
|
154
|
+
by: Supervisor | None
|
|
155
|
+
error: str | None = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class AttentionRequested(WireModel):
|
|
159
|
+
"""The agent asked for a person: the caller is on hold until a supervisor takes the line."""
|
|
160
|
+
|
|
161
|
+
reason: str
|
|
162
|
+
wait_s: float
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class CallStarted(WireModel):
|
|
166
|
+
"""Media is up: the caller and the agent can hear each other, or the text session is open."""
|
|
167
|
+
|
|
168
|
+
channel: Channel
|
|
169
|
+
direction: Direction
|
|
170
|
+
from_: str = Field(alias="from")
|
|
171
|
+
to: str
|
|
172
|
+
run: str | None = None
|
|
173
|
+
persona: str | None = None
|
|
174
|
+
accepts_when: str | None = None
|
|
175
|
+
declines_when: str | None = None
|
|
176
|
+
caller: Contact | None
|
|
177
|
+
started_at: float
|
|
178
|
+
env: Env | None = None
|
|
179
|
+
worker: str | None = None
|
|
180
|
+
medium: Medium | None = None
|
|
181
|
+
state: JsonObject | None = None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class CallAttached(WireModel):
|
|
185
|
+
"""A call changed hands without ending."""
|
|
186
|
+
|
|
187
|
+
app: str
|
|
188
|
+
started: CallStarted
|
|
189
|
+
state: JsonObject
|
|
190
|
+
seq: int
|
|
191
|
+
claimed: str | None = None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class CallClaimed(WireModel):
|
|
195
|
+
"""The call was bound to a page's code."""
|
|
196
|
+
|
|
197
|
+
code: str
|
|
198
|
+
via: Literal["keypad", "agent"]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class CallDialing(WireModel):
|
|
202
|
+
"""The platform is placing an outbound call and the far end has not answered yet."""
|
|
203
|
+
|
|
204
|
+
channel: Channel
|
|
205
|
+
from_: str = Field(alias="from")
|
|
206
|
+
to: str
|
|
207
|
+
run: str | None = None
|
|
208
|
+
caller: Contact | None
|
|
209
|
+
external_id: str | None = None
|
|
210
|
+
asked_by: str | None = None
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class CallEnded(WireModel):
|
|
214
|
+
"""The call is over; call.summary still follows."""
|
|
215
|
+
|
|
216
|
+
reason: EndReason
|
|
217
|
+
ended_by: EndedBy
|
|
218
|
+
ended_at: float
|
|
219
|
+
duration_s: float
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class CallLine(WireModel):
|
|
223
|
+
"""The line's hold and mute flags after one of them changed."""
|
|
224
|
+
|
|
225
|
+
held: bool
|
|
226
|
+
muted: bool
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class CallRinging(WireModel):
|
|
230
|
+
"""An inbound call is offered to this agent and has not been answered yet."""
|
|
231
|
+
|
|
232
|
+
channel: Channel
|
|
233
|
+
from_: str = Field(alias="from")
|
|
234
|
+
to: str
|
|
235
|
+
route: Route
|
|
236
|
+
run: str | None = None
|
|
237
|
+
caller: Contact | None
|
|
238
|
+
external_id: str | None = None
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class CallSummary(WireModel):
|
|
242
|
+
"""What the call was about, how it went, what it consumed and what that cost."""
|
|
243
|
+
|
|
244
|
+
reason: EndReason
|
|
245
|
+
outcome: str
|
|
246
|
+
duration_s: float
|
|
247
|
+
turns: int
|
|
248
|
+
usage: list[ModelUsage]
|
|
249
|
+
cost: Cost
|
|
250
|
+
recording: str | None = None
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class CallTransferred(WireModel):
|
|
254
|
+
"""A transfer asked for by the agent or a supervisor finished, one way or the other."""
|
|
255
|
+
|
|
256
|
+
to: str
|
|
257
|
+
mode: TransferMode | None = None
|
|
258
|
+
ok: bool
|
|
259
|
+
error: str | None = None
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class CallbackRequested(WireModel):
|
|
263
|
+
"""Somebody asked to be called back; written into the agent's own log."""
|
|
264
|
+
|
|
265
|
+
channel: Channel
|
|
266
|
+
number: str
|
|
267
|
+
via: Literal["overflow", "widget", "agent"]
|
|
268
|
+
call: str | None
|
|
269
|
+
when: str | None = None
|
|
270
|
+
note: str | None = None
|
|
271
|
+
contact: Contact | None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class CodeClaimed(WireModel):
|
|
275
|
+
"""One code, taken or expired."""
|
|
276
|
+
|
|
277
|
+
code: str
|
|
278
|
+
call: str | None
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class CodeIssued(WireModel):
|
|
282
|
+
"""One code, waiting for the call that keys it."""
|
|
283
|
+
|
|
284
|
+
code: str
|
|
285
|
+
env: Env
|
|
286
|
+
expires_at: float
|
|
287
|
+
log: Projection
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class ConfirmDeclined(WireModel):
|
|
291
|
+
"""The caller did not say yes, or the request lapsed; the tool does not run."""
|
|
292
|
+
|
|
293
|
+
tool: str
|
|
294
|
+
call_id: str
|
|
295
|
+
audience: str
|
|
296
|
+
said: str | None = None
|
|
297
|
+
reason: Literal["no", "timeout", "changed", "cancelled"]
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class ConfirmGranted(WireModel):
|
|
301
|
+
"""The caller said yes; the token minted for it never enters the log."""
|
|
302
|
+
|
|
303
|
+
tool: str
|
|
304
|
+
call_id: str
|
|
305
|
+
audience: str
|
|
306
|
+
said: str
|
|
307
|
+
ttl_s: int
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class ConfirmRequest(WireModel):
|
|
311
|
+
"""A tool with confirm set is about to run and the platform is asking the caller."""
|
|
312
|
+
|
|
313
|
+
tool: str
|
|
314
|
+
call_id: str
|
|
315
|
+
arguments: JsonObject
|
|
316
|
+
audience: str
|
|
317
|
+
phrase: str
|
|
318
|
+
ttl_s: int
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class CreditsExhausted(WireModel):
|
|
322
|
+
"""The gateway refused a call, a turn or a register because one of the org's quotas ran out."""
|
|
323
|
+
|
|
324
|
+
org: str
|
|
325
|
+
quota: QuotaName
|
|
326
|
+
used: float
|
|
327
|
+
limit: int
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class SpendUnusual(WireModel):
|
|
331
|
+
"""The org's calls cost more today than its own days usually do: said once a day."""
|
|
332
|
+
|
|
333
|
+
org: str
|
|
334
|
+
day: str
|
|
335
|
+
today_usd: float
|
|
336
|
+
usual_usd: float
|
|
337
|
+
multiple: float
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class Custom(WireModel):
|
|
341
|
+
"""A line the app wrote into the log with call.log; the platform never reads it."""
|
|
342
|
+
|
|
343
|
+
name: str
|
|
344
|
+
data: JsonObject
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class DevRequest(WireModel):
|
|
348
|
+
"""One ask of the process in the agent's directory, on a console's behalf."""
|
|
349
|
+
|
|
350
|
+
id: str
|
|
351
|
+
verb: DevVerb
|
|
352
|
+
data: JsonObject
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class DocsSources(WireModel):
|
|
356
|
+
"""What retrieval put in front of the model for this turn."""
|
|
357
|
+
|
|
358
|
+
query: str
|
|
359
|
+
sources: list[DocSource]
|
|
360
|
+
took_ms: float
|
|
361
|
+
speech_id: str | None = None
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class DtmfReceived(WireModel):
|
|
365
|
+
"""One tone the caller keyed."""
|
|
366
|
+
|
|
367
|
+
digit: Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "#"]
|
|
368
|
+
code: int
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class ErrorEvent(WireModel):
|
|
372
|
+
"""Something went wrong: what failed in a call, or which command the gateway refused."""
|
|
373
|
+
|
|
374
|
+
code: str
|
|
375
|
+
message: str
|
|
376
|
+
command: str | None = None
|
|
377
|
+
id: str | None = None
|
|
378
|
+
recoverable: bool
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class FleetFull(WireModel):
|
|
382
|
+
"""The gateway refused to open a call because every worker of the fleet was full."""
|
|
383
|
+
|
|
384
|
+
channel: Channel
|
|
385
|
+
workers: int
|
|
386
|
+
active: int
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class LogCaughtUp(WireModel):
|
|
390
|
+
"""The replay is done: everything up to seq has been sent and what follows is live."""
|
|
391
|
+
|
|
392
|
+
seq: int
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class LogGap(WireModel):
|
|
396
|
+
"""This reader missed a stretch; the snapshot, when there is one, catches it up in one step."""
|
|
397
|
+
|
|
398
|
+
from_seq: int
|
|
399
|
+
to_seq: int
|
|
400
|
+
snapshot: State | None
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class MemoryOps(WireModel):
|
|
404
|
+
"""What memory did for this turn or at hangup."""
|
|
405
|
+
|
|
406
|
+
ops: list[MemoryOp]
|
|
407
|
+
speech_id: str | None = None
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class MessageTaken(WireModel):
|
|
411
|
+
"""One waiting message, off the queue."""
|
|
412
|
+
|
|
413
|
+
message_id: str
|
|
414
|
+
call: str | None
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class MessageWaiting(WireModel):
|
|
418
|
+
"""One message, kept until somebody can answer it."""
|
|
419
|
+
|
|
420
|
+
channel: Channel
|
|
421
|
+
env: Env
|
|
422
|
+
number: str
|
|
423
|
+
phone_number_id: str
|
|
424
|
+
from_: str = Field(alias="from")
|
|
425
|
+
name: str | None
|
|
426
|
+
message_id: str
|
|
427
|
+
text: str
|
|
428
|
+
received_at: float
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
class Pong(WireModel):
|
|
432
|
+
"""The answer to ping."""
|
|
433
|
+
|
|
434
|
+
ts: float
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class PromptChanged(WireModel):
|
|
438
|
+
"""A block of the prompt was rewritten; its hash and length, never its text."""
|
|
439
|
+
|
|
440
|
+
name: str
|
|
441
|
+
hash: str
|
|
442
|
+
chars: int
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
class StateCauseTool(WireModel):
|
|
446
|
+
"""A tool call's result changed the state."""
|
|
447
|
+
|
|
448
|
+
kind: Literal["tool"] = "tool"
|
|
449
|
+
tool: str
|
|
450
|
+
call_id: str
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class StateCauseEvent(WireModel):
|
|
454
|
+
"""A fact from outside changed the state."""
|
|
455
|
+
|
|
456
|
+
kind: Literal["event"] = "event"
|
|
457
|
+
name: str
|
|
458
|
+
seq: int
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
type StateCause = Annotated[StateCauseTool | StateCauseEvent, Field(discriminator="kind")]
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
class SupervisorEnded(WireModel):
|
|
465
|
+
"""A supervisor hung up the call."""
|
|
466
|
+
|
|
467
|
+
by: Supervisor
|
|
468
|
+
reason: str | None = None
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class StateChanged(WireModel):
|
|
472
|
+
"""The app's declared state changed; the whole state travels."""
|
|
473
|
+
|
|
474
|
+
state: JsonObject
|
|
475
|
+
changed: list[str]
|
|
476
|
+
cause: StateCause | None = None
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
class SupervisorReleased(WireModel):
|
|
480
|
+
"""The supervisor gave the line back; the agent resumes with the history intact."""
|
|
481
|
+
|
|
482
|
+
by: Supervisor
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
class SupervisorSaid(WireModel):
|
|
486
|
+
"""A supervisor made the agent say this to the caller."""
|
|
487
|
+
|
|
488
|
+
by: Supervisor
|
|
489
|
+
text: str
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class SupervisorTookOver(WireModel):
|
|
493
|
+
"""A supervisor took the line; the agent is quiet until supervisor.released."""
|
|
494
|
+
|
|
495
|
+
by: Supervisor
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
class SupervisorTransferred(WireModel):
|
|
499
|
+
"""A supervisor asked for a transfer."""
|
|
500
|
+
|
|
501
|
+
by: Supervisor
|
|
502
|
+
to: str
|
|
503
|
+
mode: TransferMode | None = None
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
class SupervisorWhispered(WireModel):
|
|
507
|
+
"""A supervisor told the agent something the caller never heard."""
|
|
508
|
+
|
|
509
|
+
by: Supervisor
|
|
510
|
+
text: str
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class ToolCall(WireModel):
|
|
514
|
+
"""The model called a tool; tool.result closes it."""
|
|
515
|
+
|
|
516
|
+
call_id: str
|
|
517
|
+
name: str
|
|
518
|
+
arguments: JsonObject
|
|
519
|
+
speech_id: str | None = None
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
class ToolsChanged(WireModel):
|
|
523
|
+
"""The tools the model can see changed."""
|
|
524
|
+
|
|
525
|
+
visible: list[str]
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
class AgentTurnEnded(WireModel):
|
|
529
|
+
"""The agent's reply is over and this is what was said."""
|
|
530
|
+
|
|
531
|
+
speech_id: str
|
|
532
|
+
item_id: str | None = None
|
|
533
|
+
text: str
|
|
534
|
+
interrupted: bool
|
|
535
|
+
metrics: AgentTurnMetrics
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
class UserTurnEnded(WireModel):
|
|
539
|
+
"""The caller's turn is over and this is what they said."""
|
|
540
|
+
|
|
541
|
+
speech_id: str
|
|
542
|
+
item_id: str | None = None
|
|
543
|
+
text: str
|
|
544
|
+
language: str | None = None
|
|
545
|
+
transcript_confidence: float | None = None
|
|
546
|
+
metrics: UserTurnMetrics
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class UserStateChanged(WireModel):
|
|
550
|
+
"""The caller's state changed, in the session's own words."""
|
|
551
|
+
|
|
552
|
+
state: UserState
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
class UserTranscript(WireModel):
|
|
556
|
+
"""Words from the caller as the recognizer hears them; the final one becomes turn.user."""
|
|
557
|
+
|
|
558
|
+
text: str
|
|
559
|
+
final: bool
|
|
560
|
+
language: str | None = None
|
|
561
|
+
confidence: float | None = None
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
class VendorSwitched(WireModel):
|
|
565
|
+
"""A stage's vendor failed or came back; `serving` is the vendor the stage runs on now."""
|
|
566
|
+
|
|
567
|
+
stage: Literal["llm", "stt", "tts"]
|
|
568
|
+
vendor: str
|
|
569
|
+
model: str
|
|
570
|
+
available: bool
|
|
571
|
+
serving: str
|
|
572
|
+
serving_model: str
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
EVENTS: dict[str, type[WireModel]] = {
|
|
576
|
+
"agent.configured": AgentConfigured,
|
|
577
|
+
"agent.detached": AgentDetached,
|
|
578
|
+
"agent.draining": AgentDraining,
|
|
579
|
+
"agent.registered": AgentRegistered,
|
|
580
|
+
"agent.state": AgentStateChanged,
|
|
581
|
+
"agent.transcript": AgentTranscript,
|
|
582
|
+
"attention.answered": AttentionAnswered,
|
|
583
|
+
"attention.requested": AttentionRequested,
|
|
584
|
+
"call.attached": CallAttached,
|
|
585
|
+
"call.claimed": CallClaimed,
|
|
586
|
+
"call.dialing": CallDialing,
|
|
587
|
+
"call.ended": CallEnded,
|
|
588
|
+
"call.line": CallLine,
|
|
589
|
+
"call.ringing": CallRinging,
|
|
590
|
+
"call.score": CallScore,
|
|
591
|
+
"call.started": CallStarted,
|
|
592
|
+
"call.summary": CallSummary,
|
|
593
|
+
"call.transferred": CallTransferred,
|
|
594
|
+
"callback.requested": CallbackRequested,
|
|
595
|
+
"code.claimed": CodeClaimed,
|
|
596
|
+
"code.issued": CodeIssued,
|
|
597
|
+
"confirm.declined": ConfirmDeclined,
|
|
598
|
+
"confirm.granted": ConfirmGranted,
|
|
599
|
+
"confirm.request": ConfirmRequest,
|
|
600
|
+
"credits.exhausted": CreditsExhausted,
|
|
601
|
+
"custom": Custom,
|
|
602
|
+
"dev.request": DevRequest,
|
|
603
|
+
"docs.sources": DocsSources,
|
|
604
|
+
"dtmf.received": DtmfReceived,
|
|
605
|
+
"error": ErrorEvent,
|
|
606
|
+
"event.received": EventReceived,
|
|
607
|
+
"fleet.full": FleetFull,
|
|
608
|
+
"log.caught_up": LogCaughtUp,
|
|
609
|
+
"log.gap": LogGap,
|
|
610
|
+
"memory.ops": MemoryOps,
|
|
611
|
+
"message.taken": MessageTaken,
|
|
612
|
+
"message.waiting": MessageWaiting,
|
|
613
|
+
"metrics.avatar": AvatarMetrics,
|
|
614
|
+
"metrics.eot": EOTInferenceMetrics,
|
|
615
|
+
"metrics.eou": EOUMetrics,
|
|
616
|
+
"metrics.interruption": InterruptionMetrics,
|
|
617
|
+
"metrics.llm": LLMMetrics,
|
|
618
|
+
"metrics.realtime": RealtimeModelMetrics,
|
|
619
|
+
"metrics.stt": STTMetrics,
|
|
620
|
+
"metrics.tts": TTSMetrics,
|
|
621
|
+
"metrics.vad": VADMetrics,
|
|
622
|
+
"participant.joined": ParticipantJoined,
|
|
623
|
+
"participant.left": ParticipantLeft,
|
|
624
|
+
"participant.speaking": ParticipantSpeaking,
|
|
625
|
+
"pong": Pong,
|
|
626
|
+
"prompt.changed": PromptChanged,
|
|
627
|
+
"room.opened": RoomOpened,
|
|
628
|
+
"room.sent": RoomSent,
|
|
629
|
+
"state.changed": StateChanged,
|
|
630
|
+
"spend.unusual": SpendUnusual,
|
|
631
|
+
"supervisor.ended": SupervisorEnded,
|
|
632
|
+
"supervisor.released": SupervisorReleased,
|
|
633
|
+
"supervisor.said": SupervisorSaid,
|
|
634
|
+
"supervisor.took_over": SupervisorTookOver,
|
|
635
|
+
"supervisor.transferred": SupervisorTransferred,
|
|
636
|
+
"supervisor.whispered": SupervisorWhispered,
|
|
637
|
+
"tool.call": ToolCall,
|
|
638
|
+
"tool.result": ToolResult,
|
|
639
|
+
"tools.changed": ToolsChanged,
|
|
640
|
+
"track.published": TrackPublished,
|
|
641
|
+
"track.unpublished": TrackUnpublished,
|
|
642
|
+
"turn.agent": AgentTurnEnded,
|
|
643
|
+
"turn.user": UserTurnEnded,
|
|
644
|
+
"user.state": UserStateChanged,
|
|
645
|
+
"user.transcript": UserTranscript,
|
|
646
|
+
"vendor.switched": VendorSwitched,
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def event_of(entry: Entry) -> WireModel:
|
|
651
|
+
"""Return the entry's data as the model its type names; an unknown type is refused."""
|
|
652
|
+
model = EVENTS.get(entry.type)
|
|
653
|
+
if model is None:
|
|
654
|
+
raise DeclarationRefused(f"unknown event type: {entry.type}")
|
|
655
|
+
return model.read(entry.data, entry.type)
|
pinecall/wire/frames.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""The frames: the base every wire model shares, the log entry, the command, a whole log."""
|
|
2
|
+
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
|
6
|
+
|
|
7
|
+
from pinecall.domain.errors import DeclarationRefused
|
|
8
|
+
from pinecall.domain.names import JsonObject
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WireModel(BaseModel):
|
|
12
|
+
"""A shape on the wire: unknown keys are refused; a field builds by name or alias."""
|
|
13
|
+
|
|
14
|
+
model_config = ConfigDict(extra="forbid", validate_by_name=True, validate_by_alias=True)
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def read(cls, raw: JsonObject, what: str) -> Self:
|
|
18
|
+
"""Build the model from decoded JSON, raising DeclarationRefused with what did not fit."""
|
|
19
|
+
try:
|
|
20
|
+
return cls.model_validate(raw)
|
|
21
|
+
except ValidationError as error:
|
|
22
|
+
raise DeclarationRefused(f"{what}: {error}") from error
|
|
23
|
+
|
|
24
|
+
# The two below stay in pydantic's own parser and writer (Rust): on the paths a gateway runs
|
|
25
|
+
# per entry, decoding to a dict and reading it, or dumping and encoding, is what shows in its
|
|
26
|
+
# profile (21 % of a core at 100 calls, 2026-10-01).
|
|
27
|
+
@classmethod
|
|
28
|
+
def read_json(cls, raw: str | bytes, what: str) -> Self:
|
|
29
|
+
"""Build the model from JSON text, raising DeclarationRefused with what did not fit."""
|
|
30
|
+
try:
|
|
31
|
+
return cls.model_validate_json(raw)
|
|
32
|
+
except ValidationError as error:
|
|
33
|
+
raise DeclarationRefused(f"{what}: {error}") from error
|
|
34
|
+
|
|
35
|
+
def written(self) -> JsonObject:
|
|
36
|
+
"""Return the model as it goes on the wire: wire keys, absent fields absent, JSON values."""
|
|
37
|
+
return self.model_dump(mode="json", by_alias=True, exclude_unset=True)
|
|
38
|
+
|
|
39
|
+
def written_json(self) -> str:
|
|
40
|
+
"""Return the model as `written()` is, as JSON text in one step."""
|
|
41
|
+
return self.model_dump_json(by_alias=True, exclude_unset=True)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# seq is written before control returns, so two readers never disagree about order.
|
|
45
|
+
class Entry(WireModel):
|
|
46
|
+
"""One line of a call's log, or of an agent's log when call is null."""
|
|
47
|
+
|
|
48
|
+
seq: int
|
|
49
|
+
ts: float
|
|
50
|
+
call: str | None
|
|
51
|
+
agent: str
|
|
52
|
+
type: str
|
|
53
|
+
ephemeral: bool
|
|
54
|
+
data: JsonObject
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# The gateway answers with the events the command produces, or with an error naming the id.
|
|
58
|
+
class Command(WireModel):
|
|
59
|
+
"""One instruction from an app to the gateway over its WebSocket."""
|
|
60
|
+
|
|
61
|
+
type: str
|
|
62
|
+
agent: str
|
|
63
|
+
call: str | None
|
|
64
|
+
id: str | None = None
|
|
65
|
+
data: JsonObject
|