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
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""The number doors' bodies: carriers, numbers, dialling out, a leg's trunk, consent, opt-outs."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from pinecall.domain.call import Route
|
|
8
|
+
from pinecall.domain.names import Channel, Env, RouteOrigin, StepState
|
|
9
|
+
from pinecall.wire.frames import WireModel
|
|
10
|
+
from pinecall.wire.parts import (
|
|
11
|
+
Projection,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# ── dialling out ──
|
|
15
|
+
|
|
16
|
+
type ConsentKind = Literal["express", "written", "opt_out"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DialResponse(WireModel):
|
|
20
|
+
"""POST /v1/agents/{slug}/dial, the answer."""
|
|
21
|
+
|
|
22
|
+
call: str
|
|
23
|
+
agent: str
|
|
24
|
+
to: str
|
|
25
|
+
from_: str = Field(alias="from")
|
|
26
|
+
env: Env
|
|
27
|
+
log_token: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DialGuards(WireModel):
|
|
31
|
+
"""GET /v1/carrier/outbound, the guards: what this org may dial and how often."""
|
|
32
|
+
|
|
33
|
+
dial_anywhere: bool
|
|
34
|
+
per_minute: int
|
|
35
|
+
per_day: int
|
|
36
|
+
max_duration_s: int
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class OutboundStatus(WireModel):
|
|
40
|
+
"""GET /v1/carrier/outbound, the answer."""
|
|
41
|
+
|
|
42
|
+
ready: bool
|
|
43
|
+
kind: str | None
|
|
44
|
+
from_numbers: list[str]
|
|
45
|
+
steps_missing: list[str]
|
|
46
|
+
guards: DialGuards
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ProvisionOutboundResponse(WireModel):
|
|
50
|
+
"""POST /v1/carrier/outbound, the answer: the plan, and whether it was carried out."""
|
|
51
|
+
|
|
52
|
+
steps: list[str]
|
|
53
|
+
dry_run: bool
|
|
54
|
+
ready: bool
|
|
55
|
+
trunk: str | None = None
|
|
56
|
+
address: str | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ── numbers and the accounts they live in ──
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class NetworkRow(WireModel):
|
|
63
|
+
"""A network a peer of the org calls from, and whether the box's operator admitted it."""
|
|
64
|
+
|
|
65
|
+
network: str
|
|
66
|
+
state: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CarrierRow(WireModel):
|
|
70
|
+
"""GET /v1/carrier: one account of the org, named, never its secret."""
|
|
71
|
+
|
|
72
|
+
kind: str
|
|
73
|
+
account: str
|
|
74
|
+
label: str = ""
|
|
75
|
+
# A SIP peer's networks, each waiting, approved or refused; none for an account with an API.
|
|
76
|
+
networks: list[NetworkRow] = Field(default_factory=list[NetworkRow])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class CatalogCarrier(WireModel):
|
|
80
|
+
"""A carrier the org may bring numbers through: automatic (its API) or guided (SIP terms)."""
|
|
81
|
+
|
|
82
|
+
kind: str
|
|
83
|
+
name: str
|
|
84
|
+
how: Literal["automatic", "guided"]
|
|
85
|
+
networks: list[str]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class CarrierCatalog(WireModel):
|
|
89
|
+
"""GET /v1/carriers/catalog: the carriers this box admits, and whether it sells numbers."""
|
|
90
|
+
|
|
91
|
+
carriers: list[CatalogCarrier]
|
|
92
|
+
sells: bool
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class CarrierList(WireModel):
|
|
96
|
+
"""GET /v1/carriers: every account of the org, oldest first."""
|
|
97
|
+
|
|
98
|
+
carriers: list[CarrierRow]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class OwnedNumberRow(WireModel):
|
|
102
|
+
"""A number an account owns, and whether this world imported it."""
|
|
103
|
+
|
|
104
|
+
number: str
|
|
105
|
+
name: str
|
|
106
|
+
imported: bool
|
|
107
|
+
account: str
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class AvailableNumbers(WireModel):
|
|
111
|
+
"""GET /v1/numbers/available: what the org's Twilio accounts own; a peer lists none."""
|
|
112
|
+
|
|
113
|
+
kind: str
|
|
114
|
+
numbers: list[OwnedNumberRow]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class NumberRow(WireModel):
|
|
118
|
+
"""GET /v1/numbers, one row: a number of the org, the agent it reaches, how it was written."""
|
|
119
|
+
|
|
120
|
+
route: Route
|
|
121
|
+
origin: RouteOrigin
|
|
122
|
+
# What a call to it does now, read off the tables (GET /v1/numbers/{number}/path says why).
|
|
123
|
+
rings: StepState = "ok"
|
|
124
|
+
# When a call to it last reached the box; null when none ever did.
|
|
125
|
+
last_call_at: float | None = None
|
|
126
|
+
# The carrier of the box's catalog a hooked number comes through.
|
|
127
|
+
via: str | None = None
|
|
128
|
+
# The org's account the number lives in (GET /v1/carriers names it); null for one with none.
|
|
129
|
+
account: str | None = None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class PathStep(WireModel):
|
|
133
|
+
"""One step of a call's way to its agent, what it does now, and what would make it work."""
|
|
134
|
+
|
|
135
|
+
step: Literal["carrier", "fence", "world", "agent"]
|
|
136
|
+
state: StepState
|
|
137
|
+
says: str
|
|
138
|
+
fix: str | None = None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class NumberPath(WireModel):
|
|
142
|
+
"""GET /v1/numbers/{number}/path: the four steps, the worst of them, and the last call."""
|
|
143
|
+
|
|
144
|
+
number: str
|
|
145
|
+
steps: list[PathStep]
|
|
146
|
+
rings: StepState
|
|
147
|
+
last_call_at: float | None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class ImportNumberResponse(WireModel):
|
|
151
|
+
"""POST /v1/numbers and /v1/numbers/buy: the route, one sentence a step, whether it ran."""
|
|
152
|
+
|
|
153
|
+
route: Route
|
|
154
|
+
steps: list[str]
|
|
155
|
+
dry_run: bool
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class ImportNumberRequest(WireModel):
|
|
159
|
+
"""POST /v1/numbers: a number of one of the org's accounts, or one the org hooks itself."""
|
|
160
|
+
|
|
161
|
+
number: str
|
|
162
|
+
agent: str
|
|
163
|
+
channel: Channel = "phone"
|
|
164
|
+
account: str | None = None
|
|
165
|
+
hooked: bool = False
|
|
166
|
+
networks: list[str] = Field(default_factory=list[str])
|
|
167
|
+
# A hooked number's carrier from the box's catalog (GET /v1/carriers/catalog).
|
|
168
|
+
via: str | None = None
|
|
169
|
+
move: bool = False
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class BuyNumberRequest(WireModel):
|
|
173
|
+
"""POST /v1/numbers/buy: a number wanted from the box's own account."""
|
|
174
|
+
|
|
175
|
+
# Two letters and digits alone: both ride a URL path the box signs with its own account.
|
|
176
|
+
country: str = Field(pattern=r"^[A-Za-z]{2}$")
|
|
177
|
+
area_code: str | None = Field(default=None, pattern=r"^[0-9]{1,6}$")
|
|
178
|
+
agent: str
|
|
179
|
+
channel: Channel = "phone"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class MoveNumberRequest(WireModel):
|
|
183
|
+
"""PUT /v1/numbers/{number}/env: the world the number answers in from now on."""
|
|
184
|
+
|
|
185
|
+
env: Env
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class Consent(WireModel):
|
|
189
|
+
"""The consent a call runs on: express or written, where it came from, the words, a proof."""
|
|
190
|
+
|
|
191
|
+
kind: Literal["express", "written"]
|
|
192
|
+
source: str = Field(min_length=1, max_length=200)
|
|
193
|
+
text: str | None = Field(default=None, max_length=2000)
|
|
194
|
+
evidence: str | None = Field(default=None, max_length=500)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class DialRequest(WireModel):
|
|
198
|
+
"""POST /v1/agents/{slug}/dial: the far end, the org's number it is shown, the consent."""
|
|
199
|
+
|
|
200
|
+
to: str
|
|
201
|
+
from_: str | None = Field(None, alias="from")
|
|
202
|
+
consent: Consent | None = None
|
|
203
|
+
# What the log token handed back reads the call through.
|
|
204
|
+
log: Projection = "tenant"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class LegTrunk(WireModel):
|
|
208
|
+
"""How the worker dials a leg: the host, its transport, the pair, the number shown."""
|
|
209
|
+
|
|
210
|
+
hostname: str
|
|
211
|
+
transport: Literal["auto", "udp", "tcp", "tls"]
|
|
212
|
+
username: str
|
|
213
|
+
password: str
|
|
214
|
+
shown: str
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class LegTrunkResponse(WireModel):
|
|
218
|
+
"""GET /v1/agents/{slug}/outbound-trunk: the leg's trunk, inline, after the guards."""
|
|
219
|
+
|
|
220
|
+
trunk: LegTrunk
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class ConsentRow(WireModel):
|
|
224
|
+
"""One fact about a number: a consent or an opt-out, by whom, from what, on which call."""
|
|
225
|
+
|
|
226
|
+
kind: ConsentKind
|
|
227
|
+
source: str
|
|
228
|
+
text: str | None
|
|
229
|
+
evidence: str | None
|
|
230
|
+
given_by: str
|
|
231
|
+
call: str | None
|
|
232
|
+
given_at: float
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class ConsentHistory(WireModel):
|
|
236
|
+
"""GET /v1/org/consents/{number}: what stands for the number, and every row, newest first."""
|
|
237
|
+
|
|
238
|
+
number: str
|
|
239
|
+
standing: Literal["consented", "opted_out", "unknown"]
|
|
240
|
+
rows: list[ConsentRow]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class OptedOut(WireModel):
|
|
244
|
+
"""A number on the org's do-not-call list: since when, from what, and who put it there."""
|
|
245
|
+
|
|
246
|
+
number: str
|
|
247
|
+
since: float
|
|
248
|
+
source: str
|
|
249
|
+
given_by: str
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class DoNotCall(WireModel):
|
|
253
|
+
"""GET /v1/org/dnc: the numbers whose newest row is an opt-out, newest first, a page."""
|
|
254
|
+
|
|
255
|
+
numbers: list[OptedOut]
|
|
256
|
+
next: str | None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class DoNotCallImport(WireModel):
|
|
260
|
+
"""POST /v1/org/dnc: numbers the org's own list or a Registry scrub says not to call."""
|
|
261
|
+
|
|
262
|
+
numbers: list[str] = Field(min_length=1, max_length=10000)
|
|
263
|
+
source: str = Field(min_length=1, max_length=200)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class DoNotCallImported(WireModel):
|
|
267
|
+
"""POST /v1/org/dnc, the answer: how many numbers joined the list, and the ones refused."""
|
|
268
|
+
|
|
269
|
+
added: int
|
|
270
|
+
refused: list[str]
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class RecordConsent(WireModel):
|
|
274
|
+
"""POST /v1/org/consents: one fact about a number, a consent given or an opt-out."""
|
|
275
|
+
|
|
276
|
+
number: str
|
|
277
|
+
kind: ConsentKind
|
|
278
|
+
source: str = Field(min_length=1, max_length=200)
|
|
279
|
+
text: str | None = Field(default=None, max_length=2000)
|
|
280
|
+
evidence: str | None = Field(default=None, max_length=500)
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""The bodies of the box's own doors: orgs and what they hold, their people and keys, the box."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from pinecall.domain.names import Channel, Env
|
|
8
|
+
from pinecall.domain.org import QuotaName
|
|
9
|
+
from pinecall.wire.frames import Entry, WireModel
|
|
10
|
+
from pinecall.wire.rest.accounts import MemberRow
|
|
11
|
+
from pinecall.wire.rest.fleet import FleetTotals, WorkerStatus
|
|
12
|
+
from pinecall.wire.rest.numbers import DialGuards
|
|
13
|
+
|
|
14
|
+
# `bought` on the box's account; `twilio`, `sip`, `whatsapp` the org's account it was imported
|
|
15
|
+
# from; `imported` from an account since forgotten; `hooked` pointed at the box by the org itself;
|
|
16
|
+
# `typed` a row the box's operator wrote.
|
|
17
|
+
type NumberCameIn = Literal["bought", "twilio", "sip", "whatsapp", "imported", "hooked", "typed"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CreateOrgRequest(WireModel):
|
|
21
|
+
"""POST /v1/ops/orgs: the slug, and the name people read; the slug when none."""
|
|
22
|
+
|
|
23
|
+
slug: str
|
|
24
|
+
name: str | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class OrgRow(WireModel):
|
|
28
|
+
"""One org: its minted id, its slug and its name."""
|
|
29
|
+
|
|
30
|
+
id: str
|
|
31
|
+
slug: str
|
|
32
|
+
name: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# The same shape read and written: a limit left out is none, and `lends` null lends everything.
|
|
36
|
+
class OrgQuotas(WireModel):
|
|
37
|
+
"""An org's limits in one world, by name; null is no limit, and `lends` comes back sorted."""
|
|
38
|
+
|
|
39
|
+
limits: dict[QuotaName, int | None] = Field(default_factory=dict[QuotaName, int | None])
|
|
40
|
+
budget_usd: int | None = None
|
|
41
|
+
lends: list[str] | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PutQuotasRequest(WireModel):
|
|
45
|
+
"""PUT /v1/ops/orgs/{named}/quotas: which world, and the whole set for it."""
|
|
46
|
+
|
|
47
|
+
env: Env
|
|
48
|
+
quotas: OrgQuotas
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class PutDiallingRequest(WireModel):
|
|
52
|
+
"""PUT /v1/ops/orgs/{named}/dialling: the guards whole; one left out is the default."""
|
|
53
|
+
|
|
54
|
+
dial_anywhere: bool | None = None
|
|
55
|
+
per_minute: int | None = None
|
|
56
|
+
per_day: int | None = None
|
|
57
|
+
max_duration_s: int | None = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class OrgHolding(WireModel):
|
|
61
|
+
"""What the org holds right now, across both worlds, against its stock quotas."""
|
|
62
|
+
|
|
63
|
+
memory_facts: int
|
|
64
|
+
knowledge_chunks: int
|
|
65
|
+
numbers: int
|
|
66
|
+
seats: int
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class OrgProfile(WireModel):
|
|
70
|
+
"""GET /v1/ops/orgs/{named}: one org, its quotas per world, its dial guards, its holdings."""
|
|
71
|
+
|
|
72
|
+
id: str
|
|
73
|
+
slug: str
|
|
74
|
+
name: str
|
|
75
|
+
quotas: dict[Env, OrgQuotas]
|
|
76
|
+
dialling: DialGuards
|
|
77
|
+
holding: OrgHolding
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class MoveAgentRequest(WireModel):
|
|
81
|
+
"""PUT /v1/ops/orgs/{named}/agents: the agent to move into this org."""
|
|
82
|
+
|
|
83
|
+
agent: str
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class AgentMovedResponse(WireModel):
|
|
87
|
+
"""The move: how many logs came, which numbers came, which stayed where they were."""
|
|
88
|
+
|
|
89
|
+
agent: str
|
|
90
|
+
org: str
|
|
91
|
+
logs: int
|
|
92
|
+
numbers: list[str]
|
|
93
|
+
stayed: list[str]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class IssueKeyRequest(WireModel):
|
|
97
|
+
"""POST /v1/ops/orgs/{named}/keys: a key of the org, in a world, with scopes and an owner."""
|
|
98
|
+
|
|
99
|
+
env: Env
|
|
100
|
+
label: str | None = None
|
|
101
|
+
scopes: list[str] | None = None
|
|
102
|
+
subject: str | None = None
|
|
103
|
+
name: str | None = None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class OrgMembersResponse(WireModel):
|
|
107
|
+
"""GET /v1/ops/orgs/{named}/members: the org's people, and how many hold a seat."""
|
|
108
|
+
|
|
109
|
+
members: list[MemberRow]
|
|
110
|
+
seated: int
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class OperatorRequest(WireModel):
|
|
114
|
+
"""PUT /v1/ops/orgs/{named}/members/{id}/operator: whether this member runs the box."""
|
|
115
|
+
|
|
116
|
+
operator: bool
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class SsoRequiredRequest(WireModel):
|
|
120
|
+
"""PUT /v1/ops/orgs/{named}/sso/required: whether a password may still open the org."""
|
|
121
|
+
|
|
122
|
+
required: bool
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class BoxMailResponse(WireModel):
|
|
126
|
+
"""GET and PUT /v1/ops/mail: the box's mailbox and where it came from, never the password."""
|
|
127
|
+
|
|
128
|
+
configured: bool
|
|
129
|
+
source: str | None
|
|
130
|
+
host: str | None
|
|
131
|
+
port: int | None
|
|
132
|
+
security: str | None
|
|
133
|
+
username: str | None
|
|
134
|
+
from_: str | None = Field(alias="from")
|
|
135
|
+
verified_at: str | None
|
|
136
|
+
last_error: str | None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class BoxProvider(WireModel):
|
|
140
|
+
"""One box-wide identity provider: whether it is wired, the client, the URI to register."""
|
|
141
|
+
|
|
142
|
+
configured: bool
|
|
143
|
+
client_id: str | None
|
|
144
|
+
redirect_uri: str
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class BoxSignInResponse(WireModel):
|
|
148
|
+
"""GET /v1/ops/signin: every provider the box could offer every org's people."""
|
|
149
|
+
|
|
150
|
+
google: BoxProvider
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class PutSignInRequest(WireModel):
|
|
154
|
+
"""PUT /v1/ops/signin/google: the OAuth client the operator made for this gateway."""
|
|
155
|
+
|
|
156
|
+
client_id: str
|
|
157
|
+
client_secret: str
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class PutBrandRequest(WireModel):
|
|
161
|
+
"""PUT /v1/ops/brand: a field left out keeps its value; an empty one goes to the default."""
|
|
162
|
+
|
|
163
|
+
name: str | None = None
|
|
164
|
+
logo_url: str | None = None
|
|
165
|
+
accent: str | None = None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class RouteRequest(WireModel):
|
|
169
|
+
"""POST /v1/ops/routes: which org's agent answers a number, on a channel, in a world."""
|
|
170
|
+
|
|
171
|
+
org: str
|
|
172
|
+
number: str
|
|
173
|
+
agent: str
|
|
174
|
+
channel: Channel
|
|
175
|
+
env: Env = "production"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class RouteRow(WireModel):
|
|
179
|
+
"""One route as the operator lists it."""
|
|
180
|
+
|
|
181
|
+
org: str
|
|
182
|
+
number: str
|
|
183
|
+
agent: str
|
|
184
|
+
channel: Channel
|
|
185
|
+
env: Env
|
|
186
|
+
managed: bool
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class BoxNumber(WireModel):
|
|
190
|
+
"""GET /v1/ops/numbers: a number the box answers at, how it came, and who picks it up."""
|
|
191
|
+
|
|
192
|
+
number: str
|
|
193
|
+
channel: Channel
|
|
194
|
+
org: str
|
|
195
|
+
env: Env
|
|
196
|
+
agent: str
|
|
197
|
+
came_in: NumberCameIn
|
|
198
|
+
# A process holds the agent in the org and world now: a call to the number is answered.
|
|
199
|
+
running: bool
|
|
200
|
+
# The catalog carrier a number the org hooked comes through, when it named one.
|
|
201
|
+
via: str | None = None
|
|
202
|
+
# False for a number the org hooked that waits for the operator: no call to it opens.
|
|
203
|
+
approved: bool = True
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class BoxCarrier(WireModel):
|
|
207
|
+
"""A carrier of the box's catalog: its published networks, and whether it is admitted."""
|
|
208
|
+
|
|
209
|
+
kind: str
|
|
210
|
+
name: str
|
|
211
|
+
# The box drives its API (lists, points, buys numbers); the others are SIP terms alone.
|
|
212
|
+
control: bool
|
|
213
|
+
networks: list[str]
|
|
214
|
+
source: str
|
|
215
|
+
read_on: str
|
|
216
|
+
admitted: bool
|
|
217
|
+
# The platform's own carrier: admitted always, its networks in the cloud firewall.
|
|
218
|
+
fixed: bool
|
|
219
|
+
# The numbers of every org that reach the box through it.
|
|
220
|
+
numbers: int
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class FenceOpening(WireModel):
|
|
224
|
+
"""A network 5060 opens to beyond Twilio's, and why: a carrier's kind, or an org's ask."""
|
|
225
|
+
|
|
226
|
+
network: str
|
|
227
|
+
reason: str
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class BoxFence(WireModel):
|
|
231
|
+
"""What the fence opens to beyond Twilio's, and every network the cloud's firewall admits."""
|
|
232
|
+
|
|
233
|
+
openings: list[FenceOpening]
|
|
234
|
+
# Twilio's first, then each opening: Terraform's `sip_sources` (`fence export`).
|
|
235
|
+
networks: list[str]
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class BoxCarriers(WireModel):
|
|
239
|
+
"""GET /v1/ops/carriers: the catalog, each carrier admitted or not, and the fence."""
|
|
240
|
+
|
|
241
|
+
carriers: list[BoxCarrier]
|
|
242
|
+
fence: BoxFence
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class RepointRequest(WireModel):
|
|
246
|
+
"""POST /v1/ops/sip/repoint: the names each world was reached at before, sent on from."""
|
|
247
|
+
|
|
248
|
+
former: dict[Env, list[str]] = Field(default_factory=dict[Env, list[str]])
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class RepointedTrunk(WireModel):
|
|
252
|
+
"""POST /v1/ops/sip/repoint: a trunk of an account sent on from a world's name to its SIP's."""
|
|
253
|
+
|
|
254
|
+
account: str
|
|
255
|
+
trunk: str
|
|
256
|
+
world: Env
|
|
257
|
+
was: str
|
|
258
|
+
now: str
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class AdmitCarrierRequest(WireModel):
|
|
262
|
+
"""PUT /v1/ops/carriers/{kind}: whether orgs may bring numbers through the carrier."""
|
|
263
|
+
|
|
264
|
+
admitted: bool
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class CarrierNetworkRow(WireModel):
|
|
268
|
+
"""A network an org asked 5060 to open to: who, for what, and the operator's answer."""
|
|
269
|
+
|
|
270
|
+
id: int
|
|
271
|
+
org: str
|
|
272
|
+
source: str
|
|
273
|
+
network: str
|
|
274
|
+
state: str
|
|
275
|
+
asked_at: float
|
|
276
|
+
decided_by: str | None
|
|
277
|
+
decided_at: float | None
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class FleetListed(WireModel):
|
|
281
|
+
"""GET /v1/ops/fleet: the box's time, every worker heard from, and each fleet summed."""
|
|
282
|
+
|
|
283
|
+
now: float
|
|
284
|
+
stale_after_s: float
|
|
285
|
+
workers: list[WorkerStatus]
|
|
286
|
+
totals: list[FleetTotals]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class FleetDemand(WireModel):
|
|
290
|
+
"""GET /v1/fleet/wanted: how many scaled workers the fleet wants, for KEDA."""
|
|
291
|
+
|
|
292
|
+
fleet: str
|
|
293
|
+
wanted: int
|
|
294
|
+
# The fleet's calls and the seats its workers hold now, for whoever reads why.
|
|
295
|
+
active: int
|
|
296
|
+
seats: int
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class BoxEvent(WireModel):
|
|
300
|
+
"""One frame of GET /v1/ops/events: an entry of some org's floor, whose, and in which world."""
|
|
301
|
+
|
|
302
|
+
org: str
|
|
303
|
+
env: Env | None = None
|
|
304
|
+
entry: Entry
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class TracebackCall(WireModel):
|
|
308
|
+
"""One phone call with the number: whose, which way, when, how it ended, whether erased."""
|
|
309
|
+
|
|
310
|
+
call: str
|
|
311
|
+
org: str | None
|
|
312
|
+
env: str | None
|
|
313
|
+
direction: str | None
|
|
314
|
+
from_number: str | None
|
|
315
|
+
to_number: str | None
|
|
316
|
+
started_at: float | None
|
|
317
|
+
ended_at: float | None
|
|
318
|
+
end_reason: str | None
|
|
319
|
+
# The call's log is gone and only its detail record is left (0016_call_records.sql).
|
|
320
|
+
erased: bool
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class TracebackDial(WireModel):
|
|
324
|
+
"""One dial to the number: whose, the call it placed, the number shown, who asked, the guard."""
|
|
325
|
+
|
|
326
|
+
org: str
|
|
327
|
+
env: str
|
|
328
|
+
agent: str
|
|
329
|
+
call: str | None
|
|
330
|
+
shown: str | None
|
|
331
|
+
asked_by: str
|
|
332
|
+
refused: str | None
|
|
333
|
+
at: float
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class Traceback(WireModel):
|
|
337
|
+
"""GET /v1/ops/traceback: what the box keeps of one number since a day, oldest first."""
|
|
338
|
+
|
|
339
|
+
number: str
|
|
340
|
+
since: float
|
|
341
|
+
calls: list[TracebackCall]
|
|
342
|
+
dials: list[TracebackDial]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""The bodies of the providers doors: the catalogue, the org's own keys, the voices, a sample."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pinecall.domain.names import JsonObject
|
|
6
|
+
from pinecall.wire.frames import WireModel
|
|
7
|
+
|
|
8
|
+
type Availability = Literal["yours", "offered", "bring your own", "broken"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
type Standing = Literal["ready", "no plugin", "no key", "its own"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# v1's row, which the console reads, and `availability` and `broken` beside it: `standing` is
|
|
15
|
+
# `availability` in v1's words, and the fields v1 read off a table in code are empty here.
|
|
16
|
+
class ProviderRow(WireModel):
|
|
17
|
+
"""One installed vendor: what it does, whose key runs it for this org, and why not."""
|
|
18
|
+
|
|
19
|
+
name: str
|
|
20
|
+
does: list[Literal["llm", "stt", "tts"]]
|
|
21
|
+
aliases: list[str]
|
|
22
|
+
note: str
|
|
23
|
+
standing: Standing
|
|
24
|
+
ready: bool
|
|
25
|
+
env: str | None
|
|
26
|
+
extra: str
|
|
27
|
+
voices_listed: bool
|
|
28
|
+
availability: Availability
|
|
29
|
+
broken: str | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Catalogue(WireModel):
|
|
33
|
+
"""GET /v1/providers: every vendor installed, the defaults per stage, the models named."""
|
|
34
|
+
|
|
35
|
+
providers: list[ProviderRow]
|
|
36
|
+
defaults: dict[str, str]
|
|
37
|
+
# The picker reads GET /v1/voices; no names are curated in code.
|
|
38
|
+
voices: list[str]
|
|
39
|
+
models: dict[str, list[str]]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class VendorsResponse(WireModel):
|
|
43
|
+
"""GET /v1/provider-keys: the vendors the org brought its own credentials for, never one."""
|
|
44
|
+
|
|
45
|
+
vendors: list[str]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# One key, or the whole object a plugin's constructor takes (`speech_key` and `speech_region`).
|
|
49
|
+
class ProviderKeyRequest(WireModel):
|
|
50
|
+
"""PUT /v1/provider-keys/{vendor}: the org's own credentials for one vendor."""
|
|
51
|
+
|
|
52
|
+
key: str | None = None
|
|
53
|
+
credentials: JsonObject | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ListedVoice(WireModel):
|
|
57
|
+
"""One voice as the picker shows it: the id the `voice` setting takes, and what to choose by."""
|
|
58
|
+
|
|
59
|
+
id: str
|
|
60
|
+
name: str
|
|
61
|
+
language: str
|
|
62
|
+
description: str
|
|
63
|
+
gender: str
|
|
64
|
+
country: str
|
|
65
|
+
accent: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class VoicesListed(WireModel):
|
|
69
|
+
"""GET /v1/voices?tts=&language=: a vendor's own voices, in the vendor's order."""
|
|
70
|
+
|
|
71
|
+
tts: str
|
|
72
|
+
language: str | None
|
|
73
|
+
voices: list[ListedVoice]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class VoiceSampleRequest(WireModel):
|
|
77
|
+
"""POST /v1/voices/sample: which vendor, voice, model and words to hear; the answer is WAV."""
|
|
78
|
+
|
|
79
|
+
tts: str
|
|
80
|
+
voice: str
|
|
81
|
+
model: str | None = None
|
|
82
|
+
language: str | None = None
|
|
83
|
+
text: str | None = None
|