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,453 @@
|
|
|
1
|
+
"""The bodies of the eval doors: a golden, a case, a suite, a run, a replay, a persona, a judge."""
|
|
2
|
+
|
|
3
|
+
from datetime import date
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from pinecall.domain.agent import RunsOn
|
|
9
|
+
from pinecall.domain.names import Env, Json, JsonObject
|
|
10
|
+
from pinecall.wire.frames import WireModel
|
|
11
|
+
from pinecall.wire.parts import EndReason, ModelConfig, ScoreVerdict
|
|
12
|
+
from pinecall.wire.rest.calls import SessionScore
|
|
13
|
+
|
|
14
|
+
# How a Spanish-speaking agent addresses the caller.
|
|
15
|
+
type Register = Literal["tu", "usted"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# `failed` is the run itself; a golden that failed is a score, not a status.
|
|
19
|
+
type RunStatus = Literal["running", "done", "failed"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# A call that broke is kept `pending` at hang-up; a person approves it into the nightly or
|
|
23
|
+
# dismisses it. A case kept by hand is approved from the start.
|
|
24
|
+
type CaseStatus = Literal["pending", "approved", "dismissed"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ── a golden ──
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EventStep(WireModel):
|
|
31
|
+
"""A fact of the tenant's backend injected into a golden, after the caller turn it names."""
|
|
32
|
+
|
|
33
|
+
# 0 is before the caller's first line.
|
|
34
|
+
after_turn: int = 0
|
|
35
|
+
name: str
|
|
36
|
+
data: JsonObject = Field(default_factory=dict[str, Json])
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Expect(WireModel):
|
|
40
|
+
"""What a golden expects; each field set is one judge."""
|
|
41
|
+
|
|
42
|
+
tools: list[str] = Field(default_factory=list[str])
|
|
43
|
+
# Catches an action taken before consent, which a list of phrases cannot.
|
|
44
|
+
not_tools: list[str] = Field(default_factory=list[str])
|
|
45
|
+
not_said: list[str] = Field(default_factory=list[str], alias="not")
|
|
46
|
+
says: list[str] = Field(default_factory=list[str])
|
|
47
|
+
# Any one of these is enough: an expectation with several right answers.
|
|
48
|
+
says_any: list[str] = Field(default_factory=list[str])
|
|
49
|
+
# Every price, hour, date and name the agent stated is in the call's evidence.
|
|
50
|
+
grounded: bool = False
|
|
51
|
+
# A field named `register` would shadow a pydantic attribute.
|
|
52
|
+
addressed_as: Register | None = Field(default=None, alias="register")
|
|
53
|
+
# With `events` only: whether the agent takes the fact up.
|
|
54
|
+
replies: bool | None = None
|
|
55
|
+
# Hang-up judges asked of the golden's call by name: the one field a model may answer.
|
|
56
|
+
judges: list[str] = Field(default_factory=list[str])
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Golden(WireModel):
|
|
60
|
+
"""One scripted conversation: the state it opens in, the caller's lines, what is expected."""
|
|
61
|
+
|
|
62
|
+
name: str
|
|
63
|
+
state: JsonObject = Field(default_factory=dict[str, Json])
|
|
64
|
+
# The caller's lines, each one a turn the box pays the agent's model for: forty at most.
|
|
65
|
+
input: list[str] = Field(default_factory=list[str], max_length=40)
|
|
66
|
+
# Answered to `recall` for this call only; the memory table is never written.
|
|
67
|
+
memory: list[str] = Field(default_factory=list[str])
|
|
68
|
+
events: list[EventStep] = Field(default_factory=list[EventStep])
|
|
69
|
+
# The day the model is told it is; the real one when absent.
|
|
70
|
+
today: date | None = None
|
|
71
|
+
expect: Expect = Field(default_factory=Expect)
|
|
72
|
+
# The call `pinecall runs promote` made it from; no judge reads it.
|
|
73
|
+
promoted_from: str | None = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# ── a suite, its run, and the matrix it is scored into ──
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class RunSuiteRequest(WireModel):
|
|
80
|
+
"""POST /v1/evals/run, the body: the agent, its goldens and cases, the models, a version."""
|
|
81
|
+
|
|
82
|
+
agent: str
|
|
83
|
+
# Each golden is a call whose simulated caller the box pays for: two hundred at most, and two
|
|
84
|
+
# hundred calls in all once the cases join and every model plays each (the door counts them).
|
|
85
|
+
goldens: list[Golden] = Field(default_factory=list[Golden], max_length=200)
|
|
86
|
+
# Cases of the org's dataset by name; a case held out is played only when named here.
|
|
87
|
+
cases: list[str] = Field(default_factory=list[str], max_length=200)
|
|
88
|
+
# Every case of the agent's that is not held out: the nightly run.
|
|
89
|
+
dataset: bool = False
|
|
90
|
+
# A version of the agent's settings in the app's scope to run instead of the one standing.
|
|
91
|
+
version: int | None = Field(default=None, ge=1)
|
|
92
|
+
# Empty: the model the agent declares. Each one plays every golden again: eight at most.
|
|
93
|
+
models: list[ModelConfig] = Field(default_factory=list[ModelConfig], max_length=8)
|
|
94
|
+
# The app socket to drive, as `WS /v1/chat?app=`; absent, the newest that holds the agent.
|
|
95
|
+
app: str | None = None
|
|
96
|
+
# Each golden said out loud, through the world's fleet.
|
|
97
|
+
voice: bool = False
|
|
98
|
+
interferer_db: float | None = None
|
|
99
|
+
packet_loss: float = Field(default=0.0, ge=0, le=1)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class OpenedCall(WireModel):
|
|
103
|
+
"""The call one golden opened under one model."""
|
|
104
|
+
|
|
105
|
+
golden: str
|
|
106
|
+
model: str
|
|
107
|
+
call: str
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class JudgeScore(WireModel):
|
|
111
|
+
"""One judge's verdict on one golden under one model."""
|
|
112
|
+
|
|
113
|
+
metric: str
|
|
114
|
+
# livekit's scoring: a pass is 1, unsure is 0.5, a fail is 0.
|
|
115
|
+
score: float
|
|
116
|
+
passed: bool
|
|
117
|
+
reason: str
|
|
118
|
+
criteria: str
|
|
119
|
+
judge_calls: int
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# `asked` is set on a cell that did not hold, and null when the run kept no requests.
|
|
123
|
+
class ScoreRow(WireModel):
|
|
124
|
+
"""Every judge's verdict on one golden under one model, and the call's own summary."""
|
|
125
|
+
|
|
126
|
+
model: str
|
|
127
|
+
golden: str
|
|
128
|
+
scores: list[JudgeScore]
|
|
129
|
+
summary: JsonObject | None
|
|
130
|
+
asked: list[JsonObject] | None = None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class Failure(WireModel):
|
|
134
|
+
"""One judge that did not hold, and the cell it broke on."""
|
|
135
|
+
|
|
136
|
+
model: str
|
|
137
|
+
golden: str
|
|
138
|
+
metric: str
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ScoreMatrix(WireModel):
|
|
142
|
+
"""Models by goldens: the axes in the order the cells came, one row per cell."""
|
|
143
|
+
|
|
144
|
+
models: list[str]
|
|
145
|
+
goldens: list[str]
|
|
146
|
+
metrics: list[str]
|
|
147
|
+
# A count, not a price: a judge's tokens are billed with the rest of the model usage.
|
|
148
|
+
judge_calls: int
|
|
149
|
+
runs: list[ScoreRow]
|
|
150
|
+
failures: list[Failure]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class EvalRunResponse(WireModel):
|
|
154
|
+
"""An eval run: the calls it opened, the matrix judged so far, and why it stopped early."""
|
|
155
|
+
|
|
156
|
+
id: str
|
|
157
|
+
agent: str
|
|
158
|
+
started_at: float
|
|
159
|
+
finished_at: float | None
|
|
160
|
+
status: RunStatus
|
|
161
|
+
calls: list[OpenedCall]
|
|
162
|
+
matrix: ScoreMatrix | None
|
|
163
|
+
error: str | None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class EvalRunList(WireModel):
|
|
167
|
+
"""GET /v1/evals/runs: the runs of the key's org and world, newest first."""
|
|
168
|
+
|
|
169
|
+
runs: list[EvalRunResponse]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# ── the org's dataset: real calls kept as goldens ──
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class PromoteCaseRequest(WireModel):
|
|
176
|
+
"""POST /v1/evals/cases, the body: the finished call, its name, what it expects, held out."""
|
|
177
|
+
|
|
178
|
+
call: str
|
|
179
|
+
name: str = Field(min_length=1)
|
|
180
|
+
expect: Expect = Field(default_factory=Expect)
|
|
181
|
+
# Left out of a nightly run, read only when a run names it.
|
|
182
|
+
held_out: bool = False
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class BrokeOn(WireModel):
|
|
186
|
+
"""A judge that broke on the call a case was kept from, and why."""
|
|
187
|
+
|
|
188
|
+
judge: str
|
|
189
|
+
reason: str
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class EvalCase(WireModel):
|
|
193
|
+
"""One case of the org's dataset: the golden a real call made, whose, from where, by whom."""
|
|
194
|
+
|
|
195
|
+
id: str
|
|
196
|
+
agent: str
|
|
197
|
+
name: str
|
|
198
|
+
golden: Golden
|
|
199
|
+
source_call: str
|
|
200
|
+
source_env: Env
|
|
201
|
+
held_out: bool
|
|
202
|
+
author: str
|
|
203
|
+
created_at: float
|
|
204
|
+
status: CaseStatus = "approved"
|
|
205
|
+
# The judges that broke at hang-up; empty for a case a person kept from a call that held.
|
|
206
|
+
broke: list[BrokeOn] = Field(default_factory=list[BrokeOn])
|
|
207
|
+
# The settings version the call ran on, None where its corner had set nothing.
|
|
208
|
+
source_version: int | None = None
|
|
209
|
+
# Written into the repository as a golden: the nightly plays the file, not the case.
|
|
210
|
+
kept_in_repo: bool = False
|
|
211
|
+
decided_by: str | None = None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class EvalCaseList(WireModel):
|
|
215
|
+
"""GET /v1/evals/cases: the org's cases, by agent and name; how many wait, of how many."""
|
|
216
|
+
|
|
217
|
+
cases: list[EvalCase]
|
|
218
|
+
pending: int = 0
|
|
219
|
+
# Past it a broken call is judged as ever and not kept: the inbox is for reading.
|
|
220
|
+
pending_at_most: int = 0
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class CaseDecision(WireModel):
|
|
224
|
+
"""PATCH /v1/evals/cases/{id}, the body: what a person decided; each field set is written."""
|
|
225
|
+
|
|
226
|
+
status: CaseStatus | None = None
|
|
227
|
+
held_out: bool | None = None
|
|
228
|
+
kept_in_repo: bool | None = None
|
|
229
|
+
# With `dismissed`: the judge that should have held, written as a calibration label.
|
|
230
|
+
judge_was_wrong: str | None = Field(default=None, min_length=1)
|
|
231
|
+
note: str | None = None
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ── the judge judged ──
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class LabelRequest(WireModel):
|
|
238
|
+
"""POST /v1/evals/calibration, the body: what one judge should have answered on a call."""
|
|
239
|
+
|
|
240
|
+
call: str
|
|
241
|
+
judge: str = Field(min_length=1)
|
|
242
|
+
held: bool
|
|
243
|
+
note: str | None = None
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class JudgeAgreement(WireModel):
|
|
247
|
+
"""One judge against the labels people gave: how many, compared, agreed, and trusted."""
|
|
248
|
+
|
|
249
|
+
judge: str
|
|
250
|
+
labelled: int
|
|
251
|
+
compared: int
|
|
252
|
+
agreed: int
|
|
253
|
+
rate: float | None
|
|
254
|
+
# None until enough labels were compared to say; False is a judge under its line.
|
|
255
|
+
trusted: bool | None
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class Calibration(WireModel):
|
|
259
|
+
"""GET /v1/evals/calibration: each judge's agreement, and the line it is held to."""
|
|
260
|
+
|
|
261
|
+
judges: list[JudgeAgreement]
|
|
262
|
+
labels_to_judge: int
|
|
263
|
+
agrees_at_least: float
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# ── a finished call checked again ──
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class ReplayCallRequest(WireModel):
|
|
270
|
+
"""POST /v1/evals/replay/{call}, the body: the words banned and the latency budget."""
|
|
271
|
+
|
|
272
|
+
banned: list[str] = Field(default_factory=list[str])
|
|
273
|
+
budget: dict[str, float] = Field(default_factory=dict[str, float])
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class CheckVerdict(WireModel):
|
|
277
|
+
"""One code check's answer: its name, its status and why."""
|
|
278
|
+
|
|
279
|
+
check: str
|
|
280
|
+
status: ScoreVerdict
|
|
281
|
+
detail: str
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class ReplayCallResponse(WireModel):
|
|
285
|
+
"""POST /v1/evals/replay/{call}, the answer: the six checks and whether none broke."""
|
|
286
|
+
|
|
287
|
+
call: str
|
|
288
|
+
agent: str
|
|
289
|
+
passed: bool
|
|
290
|
+
verdicts: list[CheckVerdict]
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# ── the simulated caller ──
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class CallerPersona(WireModel):
|
|
297
|
+
"""A persona as the caller doors take it: who to play, and on what."""
|
|
298
|
+
|
|
299
|
+
name: str = ""
|
|
300
|
+
# Bounded: each is a model's prompt, on keys the box may lend.
|
|
301
|
+
goal: str = Field(max_length=4000)
|
|
302
|
+
style: str = Field(max_length=4000)
|
|
303
|
+
facts: JsonObject = Field(default_factory=dict[str, Json])
|
|
304
|
+
llm: str | None = None
|
|
305
|
+
tts: str | None = None
|
|
306
|
+
voice: str | None = None
|
|
307
|
+
# For the persona judge alone: the model playing the caller is never told it.
|
|
308
|
+
accepts_when: str = ""
|
|
309
|
+
declines_when: str = ""
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class Spoken(WireModel):
|
|
313
|
+
"""One turn of the call as the caller heard it."""
|
|
314
|
+
|
|
315
|
+
who: Literal["agent", "caller"]
|
|
316
|
+
said: str = Field(max_length=4000)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class NextLineRequest(WireModel):
|
|
320
|
+
"""POST /v1/evals/caller, the body: the persona, the call so far, the turns left."""
|
|
321
|
+
|
|
322
|
+
persona: CallerPersona
|
|
323
|
+
# Two turns for each of the forty a call may take, and no more.
|
|
324
|
+
heard: list[Spoken] = Field(default_factory=list[Spoken], max_length=80)
|
|
325
|
+
turns_left: int = Field(default=1, ge=0, le=40)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class NextLineResponse(WireModel):
|
|
329
|
+
"""The caller's next line, and whether it hangs up after it."""
|
|
330
|
+
|
|
331
|
+
say: str
|
|
332
|
+
hangup: bool = False
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class PlaceVoiceCallRequest(WireModel):
|
|
336
|
+
"""POST /v1/evals/voice, the body: the room, the agent, the persona, and the line."""
|
|
337
|
+
|
|
338
|
+
# Minted by the client, as the box mints one (`call_` and a word), so it tails the log before
|
|
339
|
+
# the call starts; the door takes it only for a call nobody opened yet.
|
|
340
|
+
call: str = Field(pattern=r"^call_[A-Za-z0-9]{1,64}$")
|
|
341
|
+
agent: str
|
|
342
|
+
persona: CallerPersona
|
|
343
|
+
# The caller's lines, each paid for by the box (its model and its voice): forty at most.
|
|
344
|
+
turns: int = Field(default=6, ge=1, le=40)
|
|
345
|
+
# dB of a background voice under the caller's; absent is a clean line.
|
|
346
|
+
interferer_db: float | None = None
|
|
347
|
+
packet_loss: float = Field(default=0.0, ge=0, le=1)
|
|
348
|
+
# The state the agent opens the call in: the persona's, as `pinecall simulate` reads it.
|
|
349
|
+
state: JsonObject = Field(default_factory=dict[str, Json])
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class PlaceVoiceCallResponse(WireModel):
|
|
353
|
+
"""POST /v1/evals/voice, the answer: how many lines the caller said, and on what line."""
|
|
354
|
+
|
|
355
|
+
call: str
|
|
356
|
+
turns: int
|
|
357
|
+
line: str
|
|
358
|
+
# What the caller's lines and voice cost, in US dollars, by the box's rates.
|
|
359
|
+
caller_cost_usd: float = 0.0
|
|
360
|
+
# The caller hung up because its call had spent the providers row's `caller.ceiling_usd`.
|
|
361
|
+
stopped_at_ceiling: bool = False
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# ── an agent's personas ──
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
class PersonaRequest(WireModel):
|
|
368
|
+
"""PUT /v1/agents/{slug}/personas/{name}, the body: the caller written whole."""
|
|
369
|
+
|
|
370
|
+
about: str = ""
|
|
371
|
+
goal: str
|
|
372
|
+
style: str
|
|
373
|
+
facts: dict[str, str] = Field(default_factory=dict[str, str])
|
|
374
|
+
state: JsonObject = Field(default_factory=dict[str, Json])
|
|
375
|
+
llm: str | None = None
|
|
376
|
+
tts: str | None = None
|
|
377
|
+
voice: str | None = None
|
|
378
|
+
accepts_when: str | None = None
|
|
379
|
+
declines_when: str | None = None
|
|
380
|
+
# The name this caller had, when the write renames it.
|
|
381
|
+
was: str | None = None
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class PersonaRow(WireModel):
|
|
385
|
+
"""One caller of the agent as the list shows it."""
|
|
386
|
+
|
|
387
|
+
name: str
|
|
388
|
+
about: str
|
|
389
|
+
goal: str
|
|
390
|
+
style: str
|
|
391
|
+
facts: dict[str, str]
|
|
392
|
+
state: JsonObject
|
|
393
|
+
llm: str | None
|
|
394
|
+
tts: str | None
|
|
395
|
+
voice: str | None
|
|
396
|
+
accepts_when: str
|
|
397
|
+
declines_when: str
|
|
398
|
+
author: str
|
|
399
|
+
set_at: float
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class PersonaList(WireModel):
|
|
403
|
+
"""GET /v1/agents/{slug}/personas: the agent's callers, by name."""
|
|
404
|
+
|
|
405
|
+
personas: list[PersonaRow]
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class PersonaRunRow(WireModel):
|
|
409
|
+
"""One call a persona made: when, how long, how it ended, what it cost, the judges' score."""
|
|
410
|
+
|
|
411
|
+
call: str
|
|
412
|
+
agent: str
|
|
413
|
+
started_at: float
|
|
414
|
+
ended_at: float | None
|
|
415
|
+
turns: int
|
|
416
|
+
end_reason: EndReason | None
|
|
417
|
+
outcome: str | None
|
|
418
|
+
cost_usd: float | None
|
|
419
|
+
score: SessionScore | None
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class PersonaRunList(WireModel):
|
|
423
|
+
"""GET /v1/agents/{slug}/personas/{name}/runs: a page of the persona's calls, newest first."""
|
|
424
|
+
|
|
425
|
+
runs: list[PersonaRunRow]
|
|
426
|
+
total: int
|
|
427
|
+
next: str | None
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
# ── an agent's own judges ──
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
class JudgeRequest(WireModel):
|
|
434
|
+
"""PUT /v1/agents/{slug}/judges/{name}, the body: the question, and which calls it reads."""
|
|
435
|
+
|
|
436
|
+
question: str
|
|
437
|
+
runs_on: RunsOn = "every-call"
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class JudgeRow(WireModel):
|
|
441
|
+
"""One of the agent's own judges as the list shows it."""
|
|
442
|
+
|
|
443
|
+
name: str
|
|
444
|
+
question: str
|
|
445
|
+
runs_on: RunsOn
|
|
446
|
+
author: str
|
|
447
|
+
set_at: float
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class JudgeList(WireModel):
|
|
451
|
+
"""GET /v1/agents/{slug}/judges: the agent's own judges, by name."""
|
|
452
|
+
|
|
453
|
+
judges: list[JudgeRow]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""The bodies of the fleet doors: a worker's heartbeat and what the gateway answers it."""
|
|
2
|
+
|
|
3
|
+
from pydantic import ConfigDict
|
|
4
|
+
|
|
5
|
+
from pinecall.wire.frames import WireModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HeartbeatRequest(WireModel):
|
|
9
|
+
"""A worker's report: its fleet, its name, what it holds and how loaded it is."""
|
|
10
|
+
|
|
11
|
+
fleet: str
|
|
12
|
+
worker: str
|
|
13
|
+
# The name the worker registered under with LiveKit: the one a dispatch reaches it by; null
|
|
14
|
+
# until LiveKit registered it, so no call is offered to a worker nobody can give it to.
|
|
15
|
+
agent_name: str | None = None
|
|
16
|
+
active: int
|
|
17
|
+
# Measured slots; null when the worker is gated on its machine's CPU.
|
|
18
|
+
max_jobs: int | None
|
|
19
|
+
load: float
|
|
20
|
+
draining: bool
|
|
21
|
+
# Its last minute, as its calls' jobs told it; absent from a worker of an older release.
|
|
22
|
+
ended: int | None = None
|
|
23
|
+
failed: int | None = None
|
|
24
|
+
errors: int | None = None
|
|
25
|
+
turns: int | None = None
|
|
26
|
+
first_audio_p95_s: float | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HeartbeatResponse(WireModel):
|
|
30
|
+
"""The answer to a heartbeat: whether this worker is cordoned, and its fleet full."""
|
|
31
|
+
|
|
32
|
+
cordoned: bool
|
|
33
|
+
full: bool
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FleetTotals(WireModel):
|
|
37
|
+
"""One fleet over the workers heard from lately."""
|
|
38
|
+
|
|
39
|
+
fleet: str
|
|
40
|
+
workers: int
|
|
41
|
+
active: int
|
|
42
|
+
seats: int
|
|
43
|
+
free: int
|
|
44
|
+
accepting: int
|
|
45
|
+
full: bool
|
|
46
|
+
# Rooms with a caller that no worker opened yet, offered by the gateway (gateway/dispatching/).
|
|
47
|
+
waiting: int = 0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class WorkerStatus(WireModel):
|
|
51
|
+
"""One worker as its last heartbeat left it, and whether the operator cordoned it."""
|
|
52
|
+
|
|
53
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
54
|
+
|
|
55
|
+
fleet: str
|
|
56
|
+
worker: str
|
|
57
|
+
# The name the worker registered under with LiveKit: the one a dispatch reaches it by.
|
|
58
|
+
agent_name: str | None = None
|
|
59
|
+
active: int
|
|
60
|
+
max_jobs: int | None
|
|
61
|
+
load: float
|
|
62
|
+
draining: bool
|
|
63
|
+
cordoned: bool
|
|
64
|
+
seen_at: float
|
|
65
|
+
ended: int | None = None
|
|
66
|
+
failed: int | None = None
|
|
67
|
+
errors: int | None = None
|
|
68
|
+
turns: int | None = None
|
|
69
|
+
first_audio_p95_s: float | None = None
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""The bodies of the hosting doors: hosted apps, releases, secrets, and the runner's report."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
from pinecall.domain.names import Env
|
|
8
|
+
from pinecall.wire.frames import WireModel
|
|
9
|
+
|
|
10
|
+
# A runner is deployed apart from its gateway, and must outlive a gateway newer than itself: what
|
|
11
|
+
# the gateway answers it is read leniently, and every field added to it carries a default.
|
|
12
|
+
READ_BY_AN_OLDER_RUNNER = ConfigDict(extra="ignore", validate_by_name=True, validate_by_alias=True)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HostedAppRow(WireModel):
|
|
16
|
+
"""One app the box hosts for the org: its newest release, and how the runner left it."""
|
|
17
|
+
|
|
18
|
+
name: str
|
|
19
|
+
# The newest release, null before the first.
|
|
20
|
+
release: int | None
|
|
21
|
+
# The release serving the app now, null while none is.
|
|
22
|
+
live_release: int | None
|
|
23
|
+
# Why the newest release did not build or start, null when it did or is still on its way.
|
|
24
|
+
failed_why: str | None
|
|
25
|
+
# A person stopped it: nothing runs, and its releases and token stay.
|
|
26
|
+
stopped: bool
|
|
27
|
+
created_by: str
|
|
28
|
+
created_at: float
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class HostedAppList(WireModel):
|
|
32
|
+
"""GET /v1/hosted: the apps the box hosts for the org in the world, by name."""
|
|
33
|
+
|
|
34
|
+
apps: list[HostedAppRow]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ReleaseRow(WireModel):
|
|
38
|
+
"""One release of a hosted app: its number, its sources' sha256 and size, who sent it."""
|
|
39
|
+
|
|
40
|
+
name: str
|
|
41
|
+
release: int
|
|
42
|
+
sha256: str
|
|
43
|
+
bytes: int
|
|
44
|
+
author: str
|
|
45
|
+
note: str
|
|
46
|
+
created_at: float
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ReleaseList(WireModel):
|
|
50
|
+
"""GET /v1/hosted/{name}/releases: the app's releases, newest first."""
|
|
51
|
+
|
|
52
|
+
releases: list[ReleaseRow]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class RollbackRequest(WireModel):
|
|
56
|
+
"""POST /v1/hosted/{name}/rollback: which release's sources go again, as the next release."""
|
|
57
|
+
|
|
58
|
+
release: int = Field(ge=1)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class AppLogsResponse(WireModel):
|
|
62
|
+
"""GET /v1/hosted/{name}/logs: the last lines the runner read, and which process, and when."""
|
|
63
|
+
|
|
64
|
+
name: str
|
|
65
|
+
host: str | None
|
|
66
|
+
lines: str
|
|
67
|
+
# Null before the runner has sent any: asking is what makes it send them.
|
|
68
|
+
at: float | None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ServedRow(WireModel):
|
|
72
|
+
"""The time one app served on one UTC day."""
|
|
73
|
+
|
|
74
|
+
org: str
|
|
75
|
+
env: Env
|
|
76
|
+
name: str
|
|
77
|
+
day: str
|
|
78
|
+
seconds: float
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ServedPage(WireModel):
|
|
82
|
+
"""GET /v1/hosted/usage and GET /v1/ops/hosted-usage: the time apps served, per day."""
|
|
83
|
+
|
|
84
|
+
since: str
|
|
85
|
+
until: str
|
|
86
|
+
rows: list[ServedRow]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class PutSecretRequest(WireModel):
|
|
90
|
+
"""PUT /v1/secrets/{name}, the body: the value, which no door ever answers back."""
|
|
91
|
+
|
|
92
|
+
value: str
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class SecretRow(WireModel):
|
|
96
|
+
"""One of the org's secrets as the list shows it: never its value."""
|
|
97
|
+
|
|
98
|
+
name: str
|
|
99
|
+
set_by: str
|
|
100
|
+
set_at: float
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class SecretList(WireModel):
|
|
104
|
+
"""GET /v1/secrets: the org's secrets in the world, by name."""
|
|
105
|
+
|
|
106
|
+
secrets: list[SecretRow]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# ── the runner ──
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class RunnerReport(WireModel):
|
|
113
|
+
"""What the runner says of one app: the host it answers for went live, or failed and why."""
|
|
114
|
+
|
|
115
|
+
org: str
|
|
116
|
+
name: str
|
|
117
|
+
host: str
|
|
118
|
+
state: Literal["live", "failed"]
|
|
119
|
+
why: str = Field(default="", max_length=2000)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class AppLogs(WireModel):
|
|
123
|
+
"""The last lines of one app's process, as the runner read them from its container."""
|
|
124
|
+
|
|
125
|
+
org: str
|
|
126
|
+
name: str
|
|
127
|
+
host: str
|
|
128
|
+
lines: str = Field(max_length=256 * 1024)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class RunnerHeartbeatRequest(WireModel):
|
|
132
|
+
"""POST /v1/runner/heartbeat: the runner's name, and what changed since its last."""
|
|
133
|
+
|
|
134
|
+
runner: str
|
|
135
|
+
reports: list[RunnerReport] = Field(default_factory=list[RunnerReport])
|
|
136
|
+
logs: list[AppLogs] = Field(default_factory=list[AppLogs])
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class WantedApp(WireModel):
|
|
140
|
+
"""One app the runner is to have running: a release, under the host name its process takes."""
|
|
141
|
+
|
|
142
|
+
model_config = READ_BY_AN_OLDER_RUNNER
|
|
143
|
+
|
|
144
|
+
org: str
|
|
145
|
+
name: str
|
|
146
|
+
release: int
|
|
147
|
+
sha256: str
|
|
148
|
+
host: str
|
|
149
|
+
# Whether an app socket of the org says it runs on that host: the release is serving.
|
|
150
|
+
registered: bool
|
|
151
|
+
# Whether this host was already reported failed: the runner leaves it alone.
|
|
152
|
+
failed: bool
|
|
153
|
+
# Whether a person asked for its logs lately: the runner sends them with its next beat.
|
|
154
|
+
logs_wanted: bool = False
|
|
155
|
+
# The host that last went live: it serves until the one wanted registers, and is the one
|
|
156
|
+
# started again when its process exits.
|
|
157
|
+
live_host: str | None = None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class RunnerHeartbeatResponse(WireModel):
|
|
161
|
+
"""The answer to a heartbeat: the runner's world, and every app of it that has a release."""
|
|
162
|
+
|
|
163
|
+
model_config = READ_BY_AN_OLDER_RUNNER
|
|
164
|
+
|
|
165
|
+
world: Env
|
|
166
|
+
apps: list[WantedApp]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class AppEnvironment(WireModel):
|
|
170
|
+
"""GET /v1/runner/apps/{org}/{name}/environment: what the app's process is started with."""
|
|
171
|
+
|
|
172
|
+
model_config = READ_BY_AN_OLDER_RUNNER
|
|
173
|
+
|
|
174
|
+
environment: dict[str, str]
|