pinecall 0.1.0__tar.gz

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.
Files changed (831) hide show
  1. pinecall-0.1.0/.claude/skills/add-a-vendor/SKILL.md +105 -0
  2. pinecall-0.1.0/.claude/skills/debug-a-call/SKILL.md +77 -0
  3. pinecall-0.1.0/.claude/skills/deploy/SKILL.md +95 -0
  4. pinecall-0.1.0/.env.example +256 -0
  5. pinecall-0.1.0/.github/workflows/ci.yml +100 -0
  6. pinecall-0.1.0/.github/workflows/release.yml +101 -0
  7. pinecall-0.1.0/.gitignore +24 -0
  8. pinecall-0.1.0/.python-version +1 -0
  9. pinecall-0.1.0/.squawk.toml +22 -0
  10. pinecall-0.1.0/ARCHITECTURE.md +376 -0
  11. pinecall-0.1.0/CHANGELOG.md +558 -0
  12. pinecall-0.1.0/CLAUDE.md +126 -0
  13. pinecall-0.1.0/CONTRIBUTING.md +14 -0
  14. pinecall-0.1.0/LICENSE +202 -0
  15. pinecall-0.1.0/Makefile +195 -0
  16. pinecall-0.1.0/PKG-INFO +237 -0
  17. pinecall-0.1.0/README.md +201 -0
  18. pinecall-0.1.0/docs/a-box-in-production.md +486 -0
  19. pinecall-0.1.0/docs/design/pinecall-v2-design.html +1012 -0
  20. pinecall-0.1.0/docs/design/research-synthesis.md +137 -0
  21. pinecall-0.1.0/docs/design/voice-stack-graph.md +684 -0
  22. pinecall-0.1.0/docs/from-zero.md +516 -0
  23. pinecall-0.1.0/docs/images/dark/admin-fleet.png +0 -0
  24. pinecall-0.1.0/docs/images/dark/admin-orgs.png +0 -0
  25. pinecall-0.1.0/docs/images/dark/admin-routes.png +0 -0
  26. pinecall-0.1.0/docs/images/dark/admin-usage.png +0 -0
  27. pinecall-0.1.0/docs/images/dark/agents.png +0 -0
  28. pinecall-0.1.0/docs/images/dark/calls.png +0 -0
  29. pinecall-0.1.0/docs/images/dark/chat.png +0 -0
  30. pinecall-0.1.0/docs/images/dark/evals.png +0 -0
  31. pinecall-0.1.0/docs/images/dark/keys.png +0 -0
  32. pinecall-0.1.0/docs/images/dark/knowledge.png +0 -0
  33. pinecall-0.1.0/docs/images/dark/live.png +0 -0
  34. pinecall-0.1.0/docs/images/dark/memory.png +0 -0
  35. pinecall-0.1.0/docs/images/dark/numbers.png +0 -0
  36. pinecall-0.1.0/docs/images/dark/pipeline.png +0 -0
  37. pinecall-0.1.0/docs/images/dark/providers.png +0 -0
  38. pinecall-0.1.0/docs/images/dark/sessions.png +0 -0
  39. pinecall-0.1.0/docs/images/dark/talk.png +0 -0
  40. pinecall-0.1.0/docs/images/dark/team.png +0 -0
  41. pinecall-0.1.0/docs/images/dark/usage.png +0 -0
  42. pinecall-0.1.0/docs/images/light/admin-fleet.png +0 -0
  43. pinecall-0.1.0/docs/images/light/admin-orgs.png +0 -0
  44. pinecall-0.1.0/docs/images/light/admin-routes.png +0 -0
  45. pinecall-0.1.0/docs/images/light/admin-usage.png +0 -0
  46. pinecall-0.1.0/docs/images/light/agents.png +0 -0
  47. pinecall-0.1.0/docs/images/light/calls.png +0 -0
  48. pinecall-0.1.0/docs/images/light/chat.png +0 -0
  49. pinecall-0.1.0/docs/images/light/evals.png +0 -0
  50. pinecall-0.1.0/docs/images/light/keys.png +0 -0
  51. pinecall-0.1.0/docs/images/light/knowledge.png +0 -0
  52. pinecall-0.1.0/docs/images/light/live.png +0 -0
  53. pinecall-0.1.0/docs/images/light/memory.png +0 -0
  54. pinecall-0.1.0/docs/images/light/numbers.png +0 -0
  55. pinecall-0.1.0/docs/images/light/pipeline.png +0 -0
  56. pinecall-0.1.0/docs/images/light/providers.png +0 -0
  57. pinecall-0.1.0/docs/images/light/sessions.png +0 -0
  58. pinecall-0.1.0/docs/images/light/talk.png +0 -0
  59. pinecall-0.1.0/docs/images/light/team.png +0 -0
  60. pinecall-0.1.0/docs/images/light/usage.png +0 -0
  61. pinecall-0.1.0/docs/multi-tenancy.md +286 -0
  62. pinecall-0.1.0/docs/protocol/dev-verbs.md +42 -0
  63. pinecall-0.1.0/docs/protocol/every-door.md +68 -0
  64. pinecall-0.1.0/docs/protocol/gateway-api.md +400 -0
  65. pinecall-0.1.0/docs/protocol/numbers.md +96 -0
  66. pinecall-0.1.0/docs/protocol/operator-api.md +389 -0
  67. pinecall-0.1.0/docs/protocol/people.md +88 -0
  68. pinecall-0.1.0/docs/protocol/pipeline-api.md +56 -0
  69. pinecall-0.1.0/docs/protocol/projections.md +79 -0
  70. pinecall-0.1.0/docs/protocol/provider-keys.md +35 -0
  71. pinecall-0.1.0/docs/protocol/tokens.md +148 -0
  72. pinecall-0.1.0/docs/reports/ms-8.md +197 -0
  73. pinecall-0.1.0/docs/retrieval/spec.md +225 -0
  74. pinecall-0.1.0/docs/scaling.md +172 -0
  75. pinecall-0.1.0/docs/security/prompt-injection.md +230 -0
  76. pinecall-0.1.0/docs/the-fleet.html +152 -0
  77. pinecall-0.1.0/docs/the-runtime-cli.md +393 -0
  78. pinecall-0.1.0/infra/README.md +134 -0
  79. pinecall-0.1.0/infra/box/Makefile +168 -0
  80. pinecall-0.1.0/infra/box/README.md +398 -0
  81. pinecall-0.1.0/infra/box/caddy/Caddyfile +21 -0
  82. pinecall-0.1.0/infra/box/caddy/pinecall.conf +8 -0
  83. pinecall-0.1.0/infra/box/cloud-init.yaml +62 -0
  84. pinecall-0.1.0/infra/box/containers/pinecall-livekit.container +32 -0
  85. pinecall-0.1.0/infra/box/containers/pinecall-postgres.container +31 -0
  86. pinecall-0.1.0/infra/box/containers/pinecall-postgres.volume +3 -0
  87. pinecall-0.1.0/infra/box/containers/pinecall-redis.container +21 -0
  88. pinecall-0.1.0/infra/box/containers/pinecall-sip.container +32 -0
  89. pinecall-0.1.0/infra/box/containers/pinecall-tei.container +41 -0
  90. pinecall-0.1.0/infra/box/containers/pinecall-tei.volume +5 -0
  91. pinecall-0.1.0/infra/box/containers/pinecall.network +4 -0
  92. pinecall-0.1.0/infra/box/livekit.yaml +24 -0
  93. pinecall-0.1.0/infra/box/nftables.conf +89 -0
  94. pinecall-0.1.0/infra/box/pinecall-fleet.service +43 -0
  95. pinecall-0.1.0/infra/box/pinecall-gateway.service +99 -0
  96. pinecall-0.1.0/infra/box/pinecall-operator-key.service +32 -0
  97. pinecall-0.1.0/infra/box/pinecall-overflow.service +33 -0
  98. pinecall-0.1.0/infra/box/pinecall-postgres-image.service +14 -0
  99. pinecall-0.1.0/infra/box/pinecall-secrets.service +16 -0
  100. pinecall-0.1.0/infra/box/pinecall-worker-key.service +34 -0
  101. pinecall-0.1.0/infra/box/pinecall-worker.service +92 -0
  102. pinecall-0.1.0/infra/box/sip.yaml +29 -0
  103. pinecall-0.1.0/infra/box/sysusers.d/pinecall.conf +5 -0
  104. pinecall-0.1.0/infra/box/tmpfiles.d/pinecall.conf +22 -0
  105. pinecall-0.1.0/infra/compose/.env.example +24 -0
  106. pinecall-0.1.0/infra/compose/dev.yml +203 -0
  107. pinecall-0.1.0/infra/compose/livekit.yaml +29 -0
  108. pinecall-0.1.0/infra/compose/postgres/Dockerfile +41 -0
  109. pinecall-0.1.0/infra/compose/postgres/init.sql +3 -0
  110. pinecall-0.1.0/infra/compose/sip.yaml +22 -0
  111. pinecall-0.1.0/infra/fleet/README.md +60 -0
  112. pinecall-0.1.0/infra/fleet/aws +37 -0
  113. pinecall-0.1.0/infra/fleet/gcp +34 -0
  114. pinecall-0.1.0/infra/fleet/hetzner +29 -0
  115. pinecall-0.1.0/infra/seed/routes.json +14 -0
  116. pinecall-0.1.0/infra/tools/.ruff.toml +10 -0
  117. pinecall-0.1.0/infra/tools/carrier_cidrs.py +37 -0
  118. pinecall-0.1.0/infra/tools/pyrightconfig.json +14 -0
  119. pinecall-0.1.0/infra/tools/sip_call.py +138 -0
  120. pinecall-0.1.0/infra/tools/sip_probe.py +77 -0
  121. pinecall-0.1.0/infra/tools/tests/conftest.py +9 -0
  122. pinecall-0.1.0/infra/tools/tests/test_carrier_cidrs.py +55 -0
  123. pinecall-0.1.0/infra/tools/tests/test_twilio_trunk.py +193 -0
  124. pinecall-0.1.0/infra/tools/twilio_rest.py +61 -0
  125. pinecall-0.1.0/infra/tools/twilio_trunk.py +337 -0
  126. pinecall-0.1.0/pyproject.toml +175 -0
  127. pinecall-0.1.0/scripts/bootstrap +6 -0
  128. pinecall-0.1.0/scripts/console +23 -0
  129. pinecall-0.1.0/scripts/format +7 -0
  130. pinecall-0.1.0/scripts/generate-env-example +12 -0
  131. pinecall-0.1.0/scripts/lint +14 -0
  132. pinecall-0.1.0/scripts/lint-migrations +28 -0
  133. pinecall-0.1.0/scripts/refresh-prices +11 -0
  134. pinecall-0.1.0/scripts/refresh_prices.py +96 -0
  135. pinecall-0.1.0/scripts/test +8 -0
  136. pinecall-0.1.0/scripts/the-version +18 -0
  137. pinecall-0.1.0/src/pinecall/__init__.py +9 -0
  138. pinecall-0.1.0/src/pinecall/_env_example.py +38 -0
  139. pinecall-0.1.0/src/pinecall/_env_files.py +31 -0
  140. pinecall-0.1.0/src/pinecall/_exceptions.py +5 -0
  141. pinecall-0.1.0/src/pinecall/_settings.py +387 -0
  142. pinecall-0.1.0/src/pinecall/_vendor_keys.py +283 -0
  143. pinecall-0.1.0/src/pinecall/_version.py +4 -0
  144. pinecall-0.1.0/src/pinecall/api/__init__.py +1 -0
  145. pinecall-0.1.0/src/pinecall/api/_deps.py +384 -0
  146. pinecall-0.1.0/src/pinecall/api/_live.py +246 -0
  147. pinecall-0.1.0/src/pinecall/api/_operator.py +50 -0
  148. pinecall-0.1.0/src/pinecall/api/_refusals.py +40 -0
  149. pinecall-0.1.0/src/pinecall/api/_serving.py +49 -0
  150. pinecall-0.1.0/src/pinecall/api/agents/__init__.py +1 -0
  151. pinecall-0.1.0/src/pinecall/api/agents/dev.py +177 -0
  152. pinecall-0.1.0/src/pinecall/api/agents/doors.py +136 -0
  153. pinecall-0.1.0/src/pinecall/api/agents/endpoints.py +164 -0
  154. pinecall-0.1.0/src/pinecall/api/agents/handlers.py +120 -0
  155. pinecall-0.1.0/src/pinecall/api/agents/holding.py +71 -0
  156. pinecall-0.1.0/src/pinecall/api/agents/on_a_call.py +146 -0
  157. pinecall-0.1.0/src/pinecall/api/agents/provider_keys.py +28 -0
  158. pinecall-0.1.0/src/pinecall/api/agents/registry.py +399 -0
  159. pinecall-0.1.0/src/pinecall/api/agents/socket.py +240 -0
  160. pinecall-0.1.0/src/pinecall/api/app.py +312 -0
  161. pinecall-0.1.0/src/pinecall/api/calls/__init__.py +1 -0
  162. pinecall-0.1.0/src/pinecall/api/calls/chat.py +236 -0
  163. pinecall-0.1.0/src/pinecall/api/calls/commands.py +52 -0
  164. pinecall-0.1.0/src/pinecall/api/calls/events.py +395 -0
  165. pinecall-0.1.0/src/pinecall/api/calls/listing.py +86 -0
  166. pinecall-0.1.0/src/pinecall/api/calls/lookup.py +45 -0
  167. pinecall-0.1.0/src/pinecall/api/calls/opening.py +98 -0
  168. pinecall-0.1.0/src/pinecall/api/calls/recording.py +46 -0
  169. pinecall-0.1.0/src/pinecall/api/calls/sink.py +281 -0
  170. pinecall-0.1.0/src/pinecall/api/calls/state.py +48 -0
  171. pinecall-0.1.0/src/pinecall/api/calls/tools.py +85 -0
  172. pinecall-0.1.0/src/pinecall/api/contacts.py +124 -0
  173. pinecall-0.1.0/src/pinecall/api/discovery.py +41 -0
  174. pinecall-0.1.0/src/pinecall/api/evals/__init__.py +1 -0
  175. pinecall-0.1.0/src/pinecall/api/evals/attachment.py +83 -0
  176. pinecall-0.1.0/src/pinecall/api/evals/caller.py +31 -0
  177. pinecall-0.1.0/src/pinecall/api/evals/conversation.py +179 -0
  178. pinecall-0.1.0/src/pinecall/api/evals/listening.py +93 -0
  179. pinecall-0.1.0/src/pinecall/api/evals/replay.py +75 -0
  180. pinecall-0.1.0/src/pinecall/api/evals/runner.py +329 -0
  181. pinecall-0.1.0/src/pinecall/api/evals/runs.py +124 -0
  182. pinecall-0.1.0/src/pinecall/api/evals/scoring.py +128 -0
  183. pinecall-0.1.0/src/pinecall/api/evals/settling.py +43 -0
  184. pinecall-0.1.0/src/pinecall/api/evals/spoken.py +96 -0
  185. pinecall-0.1.0/src/pinecall/api/evals/voice.py +100 -0
  186. pinecall-0.1.0/src/pinecall/api/extraction.py +97 -0
  187. pinecall-0.1.0/src/pinecall/api/fleet.py +157 -0
  188. pinecall-0.1.0/src/pinecall/api/floor.py +51 -0
  189. pinecall-0.1.0/src/pinecall/api/keys.py +117 -0
  190. pinecall-0.1.0/src/pinecall/api/knowledge.py +136 -0
  191. pinecall-0.1.0/src/pinecall/api/listen.py +24 -0
  192. pinecall-0.1.0/src/pinecall/api/login.py +196 -0
  193. pinecall-0.1.0/src/pinecall/api/managed.py +100 -0
  194. pinecall-0.1.0/src/pinecall/api/members.py +266 -0
  195. pinecall-0.1.0/src/pinecall/api/numbers.py +362 -0
  196. pinecall-0.1.0/src/pinecall/api/orgs.py +274 -0
  197. pinecall-0.1.0/src/pinecall/api/pages.py +84 -0
  198. pinecall-0.1.0/src/pinecall/api/pairing.py +88 -0
  199. pinecall-0.1.0/src/pinecall/api/pipeline.py +56 -0
  200. pinecall-0.1.0/src/pinecall/api/pipeline_report.py +168 -0
  201. pinecall-0.1.0/src/pinecall/api/provider_keys.py +99 -0
  202. pinecall-0.1.0/src/pinecall/api/providers.py +95 -0
  203. pinecall-0.1.0/src/pinecall/api/routes.py +114 -0
  204. pinecall-0.1.0/src/pinecall/api/signup.py +115 -0
  205. pinecall-0.1.0/src/pinecall/api/supervise/__init__.py +1 -0
  206. pinecall-0.1.0/src/pinecall/api/supervise/aiming.py +148 -0
  207. pinecall-0.1.0/src/pinecall/api/supervise/verbs.py +51 -0
  208. pinecall-0.1.0/src/pinecall/api/supervise_seat.py +28 -0
  209. pinecall-0.1.0/src/pinecall/api/tokens.py +184 -0
  210. pinecall-0.1.0/src/pinecall/api/usage.py +103 -0
  211. pinecall-0.1.0/src/pinecall/api/whatsapp/__init__.py +1 -0
  212. pinecall-0.1.0/src/pinecall/api/whatsapp/doors.py +38 -0
  213. pinecall-0.1.0/src/pinecall/api/whatsapp/threads.py +236 -0
  214. pinecall-0.1.0/src/pinecall/api/whatsapp/webhook.py +119 -0
  215. pinecall-0.1.0/src/pinecall/api/whoami.py +100 -0
  216. pinecall-0.1.0/src/pinecall/auth/__init__.py +21 -0
  217. pinecall-0.1.0/src/pinecall/auth/bearer.py +30 -0
  218. pinecall-0.1.0/src/pinecall/auth/codes.py +62 -0
  219. pinecall-0.1.0/src/pinecall/auth/invitations.py +30 -0
  220. pinecall-0.1.0/src/pinecall/auth/keys.py +378 -0
  221. pinecall-0.1.0/src/pinecall/auth/members.py +390 -0
  222. pinecall-0.1.0/src/pinecall/auth/pairing.py +115 -0
  223. pinecall-0.1.0/src/pinecall/auth/passwords.py +37 -0
  224. pinecall-0.1.0/src/pinecall/auth/scopes.py +240 -0
  225. pinecall-0.1.0/src/pinecall/auth/throttle.py +43 -0
  226. pinecall-0.1.0/src/pinecall/cli/__init__.py +87 -0
  227. pinecall-0.1.0/src/pinecall/cli/box/__init__.py +5 -0
  228. pinecall-0.1.0/src/pinecall/cli/box/verbs.py +136 -0
  229. pinecall-0.1.0/src/pinecall/cli/columns.py +12 -0
  230. pinecall-0.1.0/src/pinecall/cli/doctor/__init__.py +5 -0
  231. pinecall-0.1.0/src/pinecall/cli/doctor/probes.py +58 -0
  232. pinecall-0.1.0/src/pinecall/cli/doctor/verbs.py +339 -0
  233. pinecall-0.1.0/src/pinecall/cli/fleet/__init__.py +5 -0
  234. pinecall-0.1.0/src/pinecall/cli/fleet/verbs.py +206 -0
  235. pinecall-0.1.0/src/pinecall/cli/gateway.py +36 -0
  236. pinecall-0.1.0/src/pinecall/cli/init.py +106 -0
  237. pinecall-0.1.0/src/pinecall/cli/keys/__init__.py +5 -0
  238. pinecall-0.1.0/src/pinecall/cli/keys/verbs.py +193 -0
  239. pinecall-0.1.0/src/pinecall/cli/migrate.py +103 -0
  240. pinecall-0.1.0/src/pinecall/cli/operator.py +125 -0
  241. pinecall-0.1.0/src/pinecall/cli/orgs/__init__.py +5 -0
  242. pinecall-0.1.0/src/pinecall/cli/orgs/verbs.py +340 -0
  243. pinecall-0.1.0/src/pinecall/cli/providers/__init__.py +5 -0
  244. pinecall-0.1.0/src/pinecall/cli/providers/verbs.py +61 -0
  245. pinecall-0.1.0/src/pinecall/cli/routes/__init__.py +5 -0
  246. pinecall-0.1.0/src/pinecall/cli/routes/verbs.py +187 -0
  247. pinecall-0.1.0/src/pinecall/cli/sessions/__init__.py +5 -0
  248. pinecall-0.1.0/src/pinecall/cli/sessions/render.py +163 -0
  249. pinecall-0.1.0/src/pinecall/cli/sessions/source.py +55 -0
  250. pinecall-0.1.0/src/pinecall/cli/sessions/verbs.py +266 -0
  251. pinecall-0.1.0/src/pinecall/cli/worker.py +148 -0
  252. pinecall-0.1.0/src/pinecall/evals/__init__.py +76 -0
  253. pinecall-0.1.0/src/pinecall/evals/answers.py +39 -0
  254. pinecall-0.1.0/src/pinecall/evals/bridge.py +186 -0
  255. pinecall-0.1.0/src/pinecall/evals/caller.py +151 -0
  256. pinecall-0.1.0/src/pinecall/evals/calling.py +298 -0
  257. pinecall-0.1.0/src/pinecall/evals/case.py +117 -0
  258. pinecall-0.1.0/src/pinecall/evals/checks/__init__.py +1 -0
  259. pinecall-0.1.0/src/pinecall/evals/checks/consent.py +48 -0
  260. pinecall-0.1.0/src/pinecall/evals/checks/errors.py +24 -0
  261. pinecall-0.1.0/src/pinecall/evals/checks/latency.py +41 -0
  262. pinecall-0.1.0/src/pinecall/evals/checks/register.py +43 -0
  263. pinecall-0.1.0/src/pinecall/evals/checks/replayed.py +90 -0
  264. pinecall-0.1.0/src/pinecall/evals/checks/verdict.py +44 -0
  265. pinecall-0.1.0/src/pinecall/evals/goldens.py +79 -0
  266. pinecall-0.1.0/src/pinecall/evals/headless.py +104 -0
  267. pinecall-0.1.0/src/pinecall/evals/judges/__init__.py +1 -0
  268. pinecall-0.1.0/src/pinecall/evals/judges/asking.py +86 -0
  269. pinecall-0.1.0/src/pinecall/evals/judges/consent.py +53 -0
  270. pinecall-0.1.0/src/pinecall/evals/judges/expected.py +201 -0
  271. pinecall-0.1.0/src/pinecall/evals/judges/grounded.py +237 -0
  272. pinecall-0.1.0/src/pinecall/evals/judges/model.py +77 -0
  273. pinecall-0.1.0/src/pinecall/evals/judges/policy.py +53 -0
  274. pinecall-0.1.0/src/pinecall/evals/judges/register.py +68 -0
  275. pinecall-0.1.0/src/pinecall/evals/line.py +89 -0
  276. pinecall-0.1.0/src/pinecall/evals/matrix.py +144 -0
  277. pinecall-0.1.0/src/pinecall/evals/polling.py +22 -0
  278. pinecall-0.1.0/src/pinecall/evals/remembering.py +33 -0
  279. pinecall-0.1.0/src/pinecall/evals/report.py +120 -0
  280. pinecall-0.1.0/src/pinecall/evals/runs.py +179 -0
  281. pinecall-0.1.0/src/pinecall/evals/score.py +160 -0
  282. pinecall-0.1.0/src/pinecall/evals/speech.py +94 -0
  283. pinecall-0.1.0/src/pinecall/evals/transcript.py +39 -0
  284. pinecall-0.1.0/src/pinecall/evals/verdicts.py +75 -0
  285. pinecall-0.1.0/src/pinecall/extensions/__init__.py +6 -0
  286. pinecall-0.1.0/src/pinecall/extensions/loading.py +43 -0
  287. pinecall-0.1.0/src/pinecall/extensions/points.py +28 -0
  288. pinecall-0.1.0/src/pinecall/fleet/__init__.py +37 -0
  289. pinecall-0.1.0/src/pinecall/fleet/clouds.py +88 -0
  290. pinecall-0.1.0/src/pinecall/fleet/decisions.py +168 -0
  291. pinecall-0.1.0/src/pinecall/fleet/loop.py +104 -0
  292. pinecall-0.1.0/src/pinecall/fleet/roster.py +151 -0
  293. pinecall-0.1.0/src/pinecall/gateway/admin/assets/index-C3WMXdfL.css +1 -0
  294. pinecall-0.1.0/src/pinecall/gateway/admin/assets/index-DR1mxoAh.js +58 -0
  295. pinecall-0.1.0/src/pinecall/gateway/admin/index.html +20 -0
  296. pinecall-0.1.0/src/pinecall/gateway/console/assets/index-DCTnou52.css +1 -0
  297. pinecall-0.1.0/src/pinecall/gateway/console/assets/index-dOcE4usU.js +99 -0
  298. pinecall-0.1.0/src/pinecall/gateway/console/index.html +20 -0
  299. pinecall-0.1.0/src/pinecall/gateway/console/logo-mark.png +0 -0
  300. pinecall-0.1.0/src/pinecall/knowledge/__init__.py +6 -0
  301. pinecall-0.1.0/src/pinecall/knowledge/chunking.py +111 -0
  302. pinecall-0.1.0/src/pinecall/knowledge/protocol.py +69 -0
  303. pinecall-0.1.0/src/pinecall/knowledge/scoring.py +87 -0
  304. pinecall-0.1.0/src/pinecall/knowledge/store.py +267 -0
  305. pinecall-0.1.0/src/pinecall/log/__init__.py +33 -0
  306. pinecall-0.1.0/src/pinecall/log/entry.py +38 -0
  307. pinecall-0.1.0/src/pinecall/log/fanout.py +108 -0
  308. pinecall-0.1.0/src/pinecall/log/filters.py +67 -0
  309. pinecall-0.1.0/src/pinecall/log/latencies.py +67 -0
  310. pinecall-0.1.0/src/pinecall/log/logs.py +139 -0
  311. pinecall-0.1.0/src/pinecall/log/pii.py +133 -0
  312. pinecall-0.1.0/src/pinecall/log/projection.py +244 -0
  313. pinecall-0.1.0/src/pinecall/log/reduce.py +396 -0
  314. pinecall-0.1.0/src/pinecall/log/replay.py +166 -0
  315. pinecall-0.1.0/src/pinecall/log/room.py +69 -0
  316. pinecall-0.1.0/src/pinecall/log/snapshots.py +43 -0
  317. pinecall-0.1.0/src/pinecall/log/store/__init__.py +21 -0
  318. pinecall-0.1.0/src/pinecall/log/store/memory.py +145 -0
  319. pinecall-0.1.0/src/pinecall/log/store/migrating.py +205 -0
  320. pinecall-0.1.0/src/pinecall/log/store/pool.py +26 -0
  321. pinecall-0.1.0/src/pinecall/log/store/postgres.py +373 -0
  322. pinecall-0.1.0/src/pinecall/log/store/protocol.py +94 -0
  323. pinecall-0.1.0/src/pinecall/log/usage.py +134 -0
  324. pinecall-0.1.0/src/pinecall/log/wording.py +25 -0
  325. pinecall-0.1.0/src/pinecall/log/writers.py +126 -0
  326. pinecall-0.1.0/src/pinecall/lookups/__init__.py +5 -0
  327. pinecall-0.1.0/src/pinecall/lookups/answers.py +44 -0
  328. pinecall-0.1.0/src/pinecall/lookups/entries.py +69 -0
  329. pinecall-0.1.0/src/pinecall/lookups/service.py +242 -0
  330. pinecall-0.1.0/src/pinecall/memory/__init__.py +11 -0
  331. pinecall-0.1.0/src/pinecall/memory/extraction.py +231 -0
  332. pinecall-0.1.0/src/pinecall/memory/goldens.py +226 -0
  333. pinecall-0.1.0/src/pinecall/memory/pgvector.py +324 -0
  334. pinecall-0.1.0/src/pinecall/memory/protocol.py +105 -0
  335. pinecall-0.1.0/src/pinecall/memory/ranking.py +48 -0
  336. pinecall-0.1.0/src/pinecall/memory/scoring.py +99 -0
  337. pinecall-0.1.0/src/pinecall/migrations/0001_call_log.sql +62 -0
  338. pinecall-0.1.0/src/pinecall/migrations/0002_api_keys.sql +16 -0
  339. pinecall-0.1.0/src/pinecall/migrations/0003_routes.sql +19 -0
  340. pinecall-0.1.0/src/pinecall/migrations/0004_eval_runs.sql +22 -0
  341. pinecall-0.1.0/src/pinecall/migrations/0005_tokens.sql +20 -0
  342. pinecall-0.1.0/src/pinecall/migrations/0006_orgs.sql +64 -0
  343. pinecall-0.1.0/src/pinecall/migrations/0007_provider_keys.sql +20 -0
  344. pinecall-0.1.0/src/pinecall/migrations/0008_memory.sql +46 -0
  345. pinecall-0.1.0/src/pinecall/migrations/0009_knowledge.sql +49 -0
  346. pinecall-0.1.0/src/pinecall/migrations/0010_memory_model.sql +14 -0
  347. pinecall-0.1.0/src/pinecall/migrations/0011_feature_quotas.sql +14 -0
  348. pinecall-0.1.0/src/pinecall/migrations/0012_pipeline_overrides.sql +26 -0
  349. pinecall-0.1.0/src/pinecall/migrations/0013_environments.sql +39 -0
  350. pinecall-0.1.0/src/pinecall/migrations/0014_members.sql +38 -0
  351. pinecall-0.1.0/src/pinecall/migrations/0015_carriers.sql +20 -0
  352. pinecall-0.1.0/src/pinecall/migrations/0016_managed_numbers.sql +11 -0
  353. pinecall-0.1.0/src/pinecall/migrations/0017_provider_scope.sql +16 -0
  354. pinecall-0.1.0/src/pinecall/migrations/0018_worlds_in_the_data.sql +50 -0
  355. pinecall-0.1.0/src/pinecall/migrations/0019_seats.sql +13 -0
  356. pinecall-0.1.0/src/pinecall/migrations/0020_operators.sql +18 -0
  357. pinecall-0.1.0/src/pinecall/migrations/0021_a_developers_own.sql +48 -0
  358. pinecall-0.1.0/src/pinecall/migrations/0022_a_tts_knob.sql +17 -0
  359. pinecall-0.1.0/src/pinecall/migrations/0023_sandbox.sql +42 -0
  360. pinecall-0.1.0/src/pinecall/migrations/migrations.lock +11 -0
  361. pinecall-0.1.0/src/pinecall/orgs/__init__.py +1 -0
  362. pinecall-0.1.0/src/pinecall/orgs/admission.py +135 -0
  363. pinecall-0.1.0/src/pinecall/orgs/carriers.py +144 -0
  364. pinecall-0.1.0/src/pinecall/orgs/meter.py +46 -0
  365. pinecall-0.1.0/src/pinecall/orgs/table.py +198 -0
  366. pinecall-0.1.0/src/pinecall/orgs/turned.py +62 -0
  367. pinecall-0.1.0/src/pinecall/orgs/vault.py +154 -0
  368. pinecall-0.1.0/src/pinecall/providers/__init__.py +1 -0
  369. pinecall-0.1.0/src/pinecall/providers/_inference.py +51 -0
  370. pinecall-0.1.0/src/pinecall/providers/blocks.py +79 -0
  371. pinecall-0.1.0/src/pinecall/providers/catalog.py +303 -0
  372. pinecall-0.1.0/src/pinecall/providers/declaration.py +231 -0
  373. pinecall-0.1.0/src/pinecall/providers/embed/__init__.py +109 -0
  374. pinecall-0.1.0/src/pinecall/providers/embed/perplexity.py +193 -0
  375. pinecall-0.1.0/src/pinecall/providers/embed/tei.py +100 -0
  376. pinecall-0.1.0/src/pinecall/providers/embed/wire.py +63 -0
  377. pinecall-0.1.0/src/pinecall/providers/embedder.py +89 -0
  378. pinecall-0.1.0/src/pinecall/providers/knocks.py +39 -0
  379. pinecall-0.1.0/src/pinecall/providers/llm/__init__.py +5 -0
  380. pinecall-0.1.0/src/pinecall/providers/llm/anthropic.py +20 -0
  381. pinecall-0.1.0/src/pinecall/providers/llm/livekit.py +14 -0
  382. pinecall-0.1.0/src/pinecall/providers/llm/openai.py +18 -0
  383. pinecall-0.1.0/src/pinecall/providers/models.py +59 -0
  384. pinecall-0.1.0/src/pinecall/providers/overrides.py +233 -0
  385. pinecall-0.1.0/src/pinecall/providers/pipeline.py +108 -0
  386. pinecall-0.1.0/src/pinecall/providers/plugin.py +150 -0
  387. pinecall-0.1.0/src/pinecall/providers/prices.py +240 -0
  388. pinecall-0.1.0/src/pinecall/providers/published.py +71 -0
  389. pinecall-0.1.0/src/pinecall/providers/published_prices.json +5344 -0
  390. pinecall-0.1.0/src/pinecall/providers/registry.py +193 -0
  391. pinecall-0.1.0/src/pinecall/providers/standing.py +40 -0
  392. pinecall-0.1.0/src/pinecall/providers/stt/__init__.py +20 -0
  393. pinecall-0.1.0/src/pinecall/providers/stt/deepgram.py +26 -0
  394. pinecall-0.1.0/src/pinecall/providers/stt/livekit.py +23 -0
  395. pinecall-0.1.0/src/pinecall/providers/stt/soniox.py +38 -0
  396. pinecall-0.1.0/src/pinecall/providers/tts/__init__.py +5 -0
  397. pinecall-0.1.0/src/pinecall/providers/tts/elevenlabs.py +57 -0
  398. pinecall-0.1.0/src/pinecall/providers/tts/livekit.py +26 -0
  399. pinecall-0.1.0/src/pinecall/providers/tts/voices.py +113 -0
  400. pinecall-0.1.0/src/pinecall/providers/usage.py +40 -0
  401. pinecall-0.1.0/src/pinecall/routes/__init__.py +1 -0
  402. pinecall-0.1.0/src/pinecall/routes/answering.py +89 -0
  403. pinecall-0.1.0/src/pinecall/routes/table.py +253 -0
  404. pinecall-0.1.0/src/pinecall/routes/trunks.py +160 -0
  405. pinecall-0.1.0/src/pinecall/routes/twilio.py +236 -0
  406. pinecall-0.1.0/src/pinecall/session/__init__.py +1 -0
  407. pinecall-0.1.0/src/pinecall/session/asking.py +71 -0
  408. pinecall-0.1.0/src/pinecall/session/clock.py +54 -0
  409. pinecall-0.1.0/src/pinecall/session/declaring.py +57 -0
  410. pinecall-0.1.0/src/pinecall/session/first_entries.py +41 -0
  411. pinecall-0.1.0/src/pinecall/session/greeting.py +33 -0
  412. pinecall-0.1.0/src/pinecall/session/knowing.py +22 -0
  413. pinecall-0.1.0/src/pinecall/session/lookups.py +284 -0
  414. pinecall-0.1.0/src/pinecall/session/pending.py +72 -0
  415. pinecall-0.1.0/src/pinecall/session/remembering.py +48 -0
  416. pinecall-0.1.0/src/pinecall/session/scoring.py +21 -0
  417. pinecall-0.1.0/src/pinecall/session/supervising.py +39 -0
  418. pinecall-0.1.0/src/pinecall/session/text/__init__.py +1 -0
  419. pinecall-0.1.0/src/pinecall/session/text/agent.py +148 -0
  420. pinecall-0.1.0/src/pinecall/session/text/measure.py +92 -0
  421. pinecall-0.1.0/src/pinecall/session/text/running.py +45 -0
  422. pinecall-0.1.0/src/pinecall/session/text/session.py +328 -0
  423. pinecall-0.1.0/src/pinecall/session/text/supervising.py +94 -0
  424. pinecall-0.1.0/src/pinecall/session/text/turns.py +144 -0
  425. pinecall-0.1.0/src/pinecall/session/visibility.py +46 -0
  426. pinecall-0.1.0/src/pinecall/session/voice/__init__.py +5 -0
  427. pinecall-0.1.0/src/pinecall/session/voice/agent.py +124 -0
  428. pinecall-0.1.0/src/pinecall/session/voice/barge_in.py +63 -0
  429. pinecall-0.1.0/src/pinecall/session/voice/commands.py +252 -0
  430. pinecall-0.1.0/src/pinecall/session/voice/dead_end.py +23 -0
  431. pinecall-0.1.0/src/pinecall/session/voice/events.py +257 -0
  432. pinecall-0.1.0/src/pinecall/session/voice/hanging_up.py +75 -0
  433. pinecall-0.1.0/src/pinecall/session/voice/hearing.py +59 -0
  434. pinecall-0.1.0/src/pinecall/session/voice/kit.py +24 -0
  435. pinecall-0.1.0/src/pinecall/session/voice/metrics.py +161 -0
  436. pinecall-0.1.0/src/pinecall/session/voice/platform.py +44 -0
  437. pinecall-0.1.0/src/pinecall/session/voice/reading_back.py +21 -0
  438. pinecall-0.1.0/src/pinecall/session/voice/room/__init__.py +7 -0
  439. pinecall-0.1.0/src/pinecall/session/voice/room/datachannel.py +248 -0
  440. pinecall-0.1.0/src/pinecall/session/voice/room/facts.py +210 -0
  441. pinecall-0.1.0/src/pinecall/session/voice/room/holding.py +50 -0
  442. pinecall-0.1.0/src/pinecall/session/voice/room/invite.py +40 -0
  443. pinecall-0.1.0/src/pinecall/session/voice/room/mute.py +50 -0
  444. pinecall-0.1.0/src/pinecall/session/voice/room/remove.py +21 -0
  445. pinecall-0.1.0/src/pinecall/session/voice/room/send.py +22 -0
  446. pinecall-0.1.0/src/pinecall/session/voice/session.py +173 -0
  447. pinecall-0.1.0/src/pinecall/session/voice/sip.py +83 -0
  448. pinecall-0.1.0/src/pinecall/session/voice/supervising.py +139 -0
  449. pinecall-0.1.0/src/pinecall/session/voice/tools.py +114 -0
  450. pinecall-0.1.0/src/pinecall/session/voice/transfer.py +74 -0
  451. pinecall-0.1.0/src/pinecall/session/voice/voice.py +391 -0
  452. pinecall-0.1.0/src/pinecall/session/voice/writing.py +70 -0
  453. pinecall-0.1.0/src/pinecall/tokens/__init__.py +1 -0
  454. pinecall-0.1.0/src/pinecall/tokens/ledger.py +107 -0
  455. pinecall-0.1.0/src/pinecall/tokens/room.py +49 -0
  456. pinecall-0.1.0/src/pinecall/tokens/seating.py +62 -0
  457. pinecall-0.1.0/src/pinecall/tokens/spending.py +37 -0
  458. pinecall-0.1.0/src/pinecall/types/__init__.py +152 -0
  459. pinecall-0.1.0/src/pinecall/types/agent.py +157 -0
  460. pinecall-0.1.0/src/pinecall/types/call.py +93 -0
  461. pinecall-0.1.0/src/pinecall/types/carrier.py +88 -0
  462. pinecall-0.1.0/src/pinecall/types/channel.py +19 -0
  463. pinecall-0.1.0/src/pinecall/types/consent.py +128 -0
  464. pinecall-0.1.0/src/pinecall/types/counting.py +11 -0
  465. pinecall-0.1.0/src/pinecall/types/dispatch.py +28 -0
  466. pinecall-0.1.0/src/pinecall/types/fusion.py +32 -0
  467. pinecall-0.1.0/src/pinecall/types/goldens.py +60 -0
  468. pinecall-0.1.0/src/pinecall/types/json.py +6 -0
  469. pinecall-0.1.0/src/pinecall/types/key.py +120 -0
  470. pinecall-0.1.0/src/pinecall/types/knowledge.py +90 -0
  471. pinecall-0.1.0/src/pinecall/types/lookup.py +102 -0
  472. pinecall-0.1.0/src/pinecall/types/member.py +100 -0
  473. pinecall-0.1.0/src/pinecall/types/org.py +117 -0
  474. pinecall-0.1.0/src/pinecall/types/prompt.py +99 -0
  475. pinecall-0.1.0/src/pinecall/types/provider_keys.py +15 -0
  476. pinecall-0.1.0/src/pinecall/types/refused.py +8 -0
  477. pinecall-0.1.0/src/pinecall/types/route.py +68 -0
  478. pinecall-0.1.0/src/pinecall/types/token.py +139 -0
  479. pinecall-0.1.0/src/pinecall/types/tool.py +74 -0
  480. pinecall-0.1.0/src/pinecall/whatsapp/__init__.py +1 -0
  481. pinecall-0.1.0/src/pinecall/whatsapp/graph.py +97 -0
  482. pinecall-0.1.0/src/pinecall/whatsapp/inbound.py +130 -0
  483. pinecall-0.1.0/src/pinecall/whatsapp/meta.py +9 -0
  484. pinecall-0.1.0/src/pinecall/whatsapp/routing.py +44 -0
  485. pinecall-0.1.0/src/pinecall/whatsapp/sending.py +47 -0
  486. pinecall-0.1.0/src/pinecall/whatsapp/signing.py +23 -0
  487. pinecall-0.1.0/src/pinecall/worker/__init__.py +22 -0
  488. pinecall-0.1.0/src/pinecall/worker/client.py +227 -0
  489. pinecall-0.1.0/src/pinecall/worker/commanding.py +56 -0
  490. pinecall-0.1.0/src/pinecall/worker/entry.py +200 -0
  491. pinecall-0.1.0/src/pinecall/worker/heartbeat.py +91 -0
  492. pinecall-0.1.0/src/pinecall/worker/load.py +120 -0
  493. pinecall-0.1.0/src/pinecall/worker/main.py +140 -0
  494. pinecall-0.1.0/src/pinecall/worker/overflow.py +172 -0
  495. pinecall-0.1.0/src/pinecall/worker/recordings.py +78 -0
  496. pinecall-0.1.0/src/pinecall/worker/router.py +111 -0
  497. pinecall-0.1.0/src/pinecall/worker/seat.py +92 -0
  498. pinecall-0.1.0/tests/__init__.py +1 -0
  499. pinecall-0.1.0/tests/api/__init__.py +1 -0
  500. pinecall-0.1.0/tests/api/agents/__init__.py +1 -0
  501. pinecall-0.1.0/tests/api/agents/test_dev_relay.py +156 -0
  502. pinecall-0.1.0/tests/api/agents/test_registry.py +325 -0
  503. pinecall-0.1.0/tests/api/agents/test_socket.py +240 -0
  504. pinecall-0.1.0/tests/api/agents/test_the_line.py +218 -0
  505. pinecall-0.1.0/tests/api/agents/test_the_team_sees_every_corner.py +107 -0
  506. pinecall-0.1.0/tests/api/agents/test_two_developers.py +352 -0
  507. pinecall-0.1.0/tests/api/agents/test_two_worlds.py +122 -0
  508. pinecall-0.1.0/tests/api/calls/__init__.py +1 -0
  509. pinecall-0.1.0/tests/api/calls/test_chat.py +370 -0
  510. pinecall-0.1.0/tests/api/calls/test_events.py +294 -0
  511. pinecall-0.1.0/tests/api/calls/test_listing.py +81 -0
  512. pinecall-0.1.0/tests/api/calls/test_live_reader.py +178 -0
  513. pinecall-0.1.0/tests/api/calls/test_lookup.py +144 -0
  514. pinecall-0.1.0/tests/api/calls/test_opening.py +62 -0
  515. pinecall-0.1.0/tests/api/calls/test_overrides.py +55 -0
  516. pinecall-0.1.0/tests/api/calls/test_recording.py +82 -0
  517. pinecall-0.1.0/tests/api/calls/test_state.py +227 -0
  518. pinecall-0.1.0/tests/api/calls/test_text.py +342 -0
  519. pinecall-0.1.0/tests/api/calls/test_the_orgs_own_key.py +55 -0
  520. pinecall-0.1.0/tests/api/calls/test_who_is_calling.py +45 -0
  521. pinecall-0.1.0/tests/api/carriers.py +107 -0
  522. pinecall-0.1.0/tests/api/conftest.py +396 -0
  523. pinecall-0.1.0/tests/api/evals/__init__.py +1 -0
  524. pinecall-0.1.0/tests/api/evals/conftest.py +143 -0
  525. pinecall-0.1.0/tests/api/evals/test_a_golden_opens_mid_call.py +65 -0
  526. pinecall-0.1.0/tests/api/evals/test_replay_door.py +78 -0
  527. pinecall-0.1.0/tests/api/evals/test_the_app_that_left.py +125 -0
  528. pinecall-0.1.0/tests/api/evals/test_the_caller_door.py +111 -0
  529. pinecall-0.1.0/tests/api/evals/test_the_costless_judge.py +142 -0
  530. pinecall-0.1.0/tests/api/evals/test_the_day_a_golden_runs_on.py +73 -0
  531. pinecall-0.1.0/tests/api/evals/test_the_run_door.py +310 -0
  532. pinecall-0.1.0/tests/api/evals/test_the_runs_list.py +89 -0
  533. pinecall-0.1.0/tests/api/evals/test_when_a_spoken_caller_may_hang_up.py +86 -0
  534. pinecall-0.1.0/tests/api/fake_graph.py +24 -0
  535. pinecall-0.1.0/tests/api/orgs/__init__.py +1 -0
  536. pinecall-0.1.0/tests/api/orgs/test_moving_an_agent.py +140 -0
  537. pinecall-0.1.0/tests/api/orgs/test_ops_orgs.py +142 -0
  538. pinecall-0.1.0/tests/api/orgs/test_provider_keys.py +311 -0
  539. pinecall-0.1.0/tests/api/orgs/test_quotas.py +144 -0
  540. pinecall-0.1.0/tests/api/orgs/test_the_operators_own_doors.py +109 -0
  541. pinecall-0.1.0/tests/api/orgs/test_two_orgs_never_cross.py +158 -0
  542. pinecall-0.1.0/tests/api/orgs/test_usage_door.py +91 -0
  543. pinecall-0.1.0/tests/api/people.py +49 -0
  544. pinecall-0.1.0/tests/api/pipeline/__init__.py +1 -0
  545. pinecall-0.1.0/tests/api/pipeline/conftest.py +44 -0
  546. pinecall-0.1.0/tests/api/pipeline/test_pipeline_doors.py +232 -0
  547. pinecall-0.1.0/tests/api/pipeline/test_pipeline_without_a_key.py +29 -0
  548. pinecall-0.1.0/tests/api/policy.py +16 -0
  549. pinecall-0.1.0/tests/api/routes/__init__.py +1 -0
  550. pinecall-0.1.0/tests/api/routes/test_doors.py +140 -0
  551. pinecall-0.1.0/tests/api/supervise/__init__.py +1 -0
  552. pinecall-0.1.0/tests/api/supervise/test_the_seat_is_named.py +82 -0
  553. pinecall-0.1.0/tests/api/supervise/test_verbs_door.py +271 -0
  554. pinecall-0.1.0/tests/api/supervise/test_verbs_on_a_text_call.py +292 -0
  555. pinecall-0.1.0/tests/api/talking.py +145 -0
  556. pinecall-0.1.0/tests/api/test_a_person_does_not_hold_production.py +117 -0
  557. pinecall-0.1.0/tests/api/test_buying_numbers.py +143 -0
  558. pinecall-0.1.0/tests/api/test_contacts.py +178 -0
  559. pinecall-0.1.0/tests/api/test_extraction.py +134 -0
  560. pinecall-0.1.0/tests/api/test_fleet_doors.py +129 -0
  561. pinecall-0.1.0/tests/api/test_knowledge.py +193 -0
  562. pinecall-0.1.0/tests/api/test_many_apps.py +257 -0
  563. pinecall-0.1.0/tests/api/test_members_and_login.py +238 -0
  564. pinecall-0.1.0/tests/api/test_no_provider_key_in_the_log.py +219 -0
  565. pinecall-0.1.0/tests/api/test_numbers.py +259 -0
  566. pinecall-0.1.0/tests/api/test_ops_keys.py +150 -0
  567. pinecall-0.1.0/tests/api/test_pairing.py +170 -0
  568. pinecall-0.1.0/tests/api/test_providers.py +63 -0
  569. pinecall-0.1.0/tests/api/test_pushing_into_a_capped_plan.py +113 -0
  570. pinecall-0.1.0/tests/api/test_refusals.py +80 -0
  571. pinecall-0.1.0/tests/api/test_scopes_at_the_doors.py +197 -0
  572. pinecall-0.1.0/tests/api/test_seating_a_team.py +105 -0
  573. pinecall-0.1.0/tests/api/test_served_call.py +216 -0
  574. pinecall-0.1.0/tests/api/test_signup.py +168 -0
  575. pinecall-0.1.0/tests/api/test_the_orgs_floor.py +129 -0
  576. pinecall-0.1.0/tests/api/test_the_orgs_own_keys.py +193 -0
  577. pinecall-0.1.0/tests/api/test_the_orgs_tables.py +131 -0
  578. pinecall-0.1.0/tests/api/test_the_pages_are_served.py +192 -0
  579. pinecall-0.1.0/tests/api/test_two_worlds_never_cross.py +224 -0
  580. pinecall-0.1.0/tests/api/test_whoami.py +63 -0
  581. pinecall-0.1.0/tests/api/test_worker_doors.py +178 -0
  582. pinecall-0.1.0/tests/api/tokens/__init__.py +1 -0
  583. pinecall-0.1.0/tests/api/tokens/test_listen_door.py +90 -0
  584. pinecall-0.1.0/tests/api/tokens/test_spending.py +91 -0
  585. pinecall-0.1.0/tests/api/tokens/test_supervise_door.py +69 -0
  586. pinecall-0.1.0/tests/api/tokens/test_the_door.py +248 -0
  587. pinecall-0.1.0/tests/api/whatsapp/__init__.py +1 -0
  588. pinecall-0.1.0/tests/api/whatsapp/conftest.py +201 -0
  589. pinecall-0.1.0/tests/api/whatsapp/test_routing.py +52 -0
  590. pinecall-0.1.0/tests/api/whatsapp/test_the_desk_holds_a_thread.py +154 -0
  591. pinecall-0.1.0/tests/api/whatsapp/test_the_door.py +327 -0
  592. pinecall-0.1.0/tests/auth/__init__.py +1 -0
  593. pinecall-0.1.0/tests/auth/test_codes.py +41 -0
  594. pinecall-0.1.0/tests/auth/test_keys.py +251 -0
  595. pinecall-0.1.0/tests/auth/test_members.py +88 -0
  596. pinecall-0.1.0/tests/auth/test_passwords.py +48 -0
  597. pinecall-0.1.0/tests/auth/test_postgres_members.py +67 -0
  598. pinecall-0.1.0/tests/auth/test_scopes.py +38 -0
  599. pinecall-0.1.0/tests/auth/test_throttle.py +31 -0
  600. pinecall-0.1.0/tests/auth/test_tokens.py +160 -0
  601. pinecall-0.1.0/tests/cli/__init__.py +1 -0
  602. pinecall-0.1.0/tests/cli/box/__init__.py +1 -0
  603. pinecall-0.1.0/tests/cli/box/test_verbs.py +99 -0
  604. pinecall-0.1.0/tests/cli/conftest.py +5 -0
  605. pinecall-0.1.0/tests/cli/doctor/__init__.py +1 -0
  606. pinecall-0.1.0/tests/cli/doctor/reading.py +29 -0
  607. pinecall-0.1.0/tests/cli/doctor/test_verbs.py +354 -0
  608. pinecall-0.1.0/tests/cli/doctor/test_which_keys_are_honoured.py +26 -0
  609. pinecall-0.1.0/tests/cli/keys/__init__.py +1 -0
  610. pinecall-0.1.0/tests/cli/keys/conftest.py +60 -0
  611. pinecall-0.1.0/tests/cli/keys/test_verbs.py +120 -0
  612. pinecall-0.1.0/tests/cli/orgs/__init__.py +1 -0
  613. pinecall-0.1.0/tests/cli/orgs/conftest.py +60 -0
  614. pinecall-0.1.0/tests/cli/orgs/test_verbs.py +139 -0
  615. pinecall-0.1.0/tests/cli/routes/__init__.py +1 -0
  616. pinecall-0.1.0/tests/cli/routes/conftest.py +60 -0
  617. pinecall-0.1.0/tests/cli/routes/test_verbs.py +111 -0
  618. pinecall-0.1.0/tests/cli/sessions/__init__.py +1 -0
  619. pinecall-0.1.0/tests/cli/sessions/test_postgres.py +74 -0
  620. pinecall-0.1.0/tests/cli/sessions/test_render.py +185 -0
  621. pinecall-0.1.0/tests/cli/sessions/test_verbs.py +206 -0
  622. pinecall-0.1.0/tests/cli/test_dispatcher.py +111 -0
  623. pinecall-0.1.0/tests/cli/test_init.py +116 -0
  624. pinecall-0.1.0/tests/cli/test_migrate.py +42 -0
  625. pinecall-0.1.0/tests/cli/test_migrate_key.py +67 -0
  626. pinecall-0.1.0/tests/cli/test_worker.py +246 -0
  627. pinecall-0.1.0/tests/conftest.py +86 -0
  628. pinecall-0.1.0/tests/evals/__init__.py +1 -0
  629. pinecall-0.1.0/tests/evals/checks/__init__.py +1 -0
  630. pinecall-0.1.0/tests/evals/checks/conftest.py +43 -0
  631. pinecall-0.1.0/tests/evals/checks/test_checks.py +83 -0
  632. pinecall-0.1.0/tests/evals/checks/test_consent.py +48 -0
  633. pinecall-0.1.0/tests/evals/clinica.py +55 -0
  634. pinecall-0.1.0/tests/evals/conversations.py +65 -0
  635. pinecall-0.1.0/tests/evals/fakes.py +106 -0
  636. pinecall-0.1.0/tests/evals/fixtures/clinica-norte/declaration.json +103 -0
  637. pinecall-0.1.0/tests/evals/fixtures/clinica-norte/state-0.txt +38 -0
  638. pinecall-0.1.0/tests/evals/fixtures/clinica-norte/state-1.txt +47 -0
  639. pinecall-0.1.0/tests/evals/fixtures/clinica-norte/state-2.txt +38 -0
  640. pinecall-0.1.0/tests/evals/logs/booking-before-the-yes.json +10 -0
  641. pinecall-0.1.0/tests/evals/logs/booking-confirmed-by-somebody-else.json +10 -0
  642. pinecall-0.1.0/tests/evals/logs/booking-confirmed.json +11 -0
  643. pinecall-0.1.0/tests/evals/logs/booking-with-no-gate.json +8 -0
  644. pinecall-0.1.0/tests/evals/logs.py +85 -0
  645. pinecall-0.1.0/tests/evals/measuring.py +18 -0
  646. pinecall-0.1.0/tests/evals/test_a_golden_that_remembers.py +59 -0
  647. pinecall-0.1.0/tests/evals/test_a_simulated_call_hangs_up.py +98 -0
  648. pinecall-0.1.0/tests/evals/test_bridge.py +141 -0
  649. pinecall-0.1.0/tests/evals/test_clinica_turns.py +129 -0
  650. pinecall-0.1.0/tests/evals/test_consent.py +94 -0
  651. pinecall-0.1.0/tests/evals/test_expected.py +204 -0
  652. pinecall-0.1.0/tests/evals/test_goldens.py +45 -0
  653. pinecall-0.1.0/tests/evals/test_grounded.py +208 -0
  654. pinecall-0.1.0/tests/evals/test_matrix.py +106 -0
  655. pinecall-0.1.0/tests/evals/test_register.py +58 -0
  656. pinecall-0.1.0/tests/evals/test_report.py +85 -0
  657. pinecall-0.1.0/tests/evals/test_the_callers_voice.py +44 -0
  658. pinecall-0.1.0/tests/evals/test_the_degraded_line.py +129 -0
  659. pinecall-0.1.0/tests/evals/test_the_goldens.py +47 -0
  660. pinecall-0.1.0/tests/evals/test_the_public_surface.py +63 -0
  661. pinecall-0.1.0/tests/evals/test_the_sources_the_gateway_writes.py +49 -0
  662. pinecall-0.1.0/tests/evals/test_the_verdict_on_a_call.py +180 -0
  663. pinecall-0.1.0/tests/evals/test_the_vocabulary.py +56 -0
  664. pinecall-0.1.0/tests/evals/test_when_the_caller_says_its_next_line.py +65 -0
  665. pinecall-0.1.0/tests/extensions/__init__.py +1 -0
  666. pinecall-0.1.0/tests/extensions/test_the_points.py +73 -0
  667. pinecall-0.1.0/tests/fleet/__init__.py +1 -0
  668. pinecall-0.1.0/tests/fleet/test_clouds.py +65 -0
  669. pinecall-0.1.0/tests/fleet/test_decisions.py +154 -0
  670. pinecall-0.1.0/tests/fleet/test_loop.py +95 -0
  671. pinecall-0.1.0/tests/fleet/test_roster.py +109 -0
  672. pinecall-0.1.0/tests/knowledge/__init__.py +1 -0
  673. pinecall-0.1.0/tests/knowledge/files.py +24 -0
  674. pinecall-0.1.0/tests/knowledge/test_chunking.py +103 -0
  675. pinecall-0.1.0/tests/knowledge/test_scoring.py +89 -0
  676. pinecall-0.1.0/tests/knowledge/test_store.py +289 -0
  677. pinecall-0.1.0/tests/knowledge/test_the_push.py +88 -0
  678. pinecall-0.1.0/tests/log/__init__.py +1 -0
  679. pinecall-0.1.0/tests/log/_sigkill_writer.py +19 -0
  680. pinecall-0.1.0/tests/log/test_0021_against_rows.py +108 -0
  681. pinecall-0.1.0/tests/log/test_an_unreadable_entry.py +64 -0
  682. pinecall-0.1.0/tests/log/test_fanout.py +92 -0
  683. pinecall-0.1.0/tests/log/test_filters.py +52 -0
  684. pinecall-0.1.0/tests/log/test_latencies.py +52 -0
  685. pinecall-0.1.0/tests/log/test_logs.py +98 -0
  686. pinecall-0.1.0/tests/log/test_migrating.py +183 -0
  687. pinecall-0.1.0/tests/log/test_migrations.py +43 -0
  688. pinecall-0.1.0/tests/log/test_pii.py +104 -0
  689. pinecall-0.1.0/tests/log/test_postgres.py +111 -0
  690. pinecall-0.1.0/tests/log/test_reduce.py +240 -0
  691. pinecall-0.1.0/tests/log/test_replay.py +137 -0
  692. pinecall-0.1.0/tests/log/test_sigkill.py +65 -0
  693. pinecall-0.1.0/tests/log/test_store.py +185 -0
  694. pinecall-0.1.0/tests/log/test_usage.py +125 -0
  695. pinecall-0.1.0/tests/log/test_wording.py +24 -0
  696. pinecall-0.1.0/tests/lookups/__init__.py +1 -0
  697. pinecall-0.1.0/tests/lookups/fakes.py +349 -0
  698. pinecall-0.1.0/tests/lookups/test_end_to_end.py +115 -0
  699. pinecall-0.1.0/tests/lookups/test_service.py +326 -0
  700. pinecall-0.1.0/tests/memory/__init__.py +1 -0
  701. pinecall-0.1.0/tests/memory/conftest.py +115 -0
  702. pinecall-0.1.0/tests/memory/facts.py +31 -0
  703. pinecall-0.1.0/tests/memory/test_extraction.py +140 -0
  704. pinecall-0.1.0/tests/memory/test_goldens.py +198 -0
  705. pinecall-0.1.0/tests/memory/test_one_developers_own.py +69 -0
  706. pinecall-0.1.0/tests/memory/test_pgvector.py +344 -0
  707. pinecall-0.1.0/tests/memory/test_ranking.py +67 -0
  708. pinecall-0.1.0/tests/memory/test_scoring.py +101 -0
  709. pinecall-0.1.0/tests/migrations.py +74 -0
  710. pinecall-0.1.0/tests/orgs/__init__.py +1 -0
  711. pinecall-0.1.0/tests/orgs/test_a_turned_knob_survives_a_restart.py +89 -0
  712. pinecall-0.1.0/tests/orgs/test_carriers.py +68 -0
  713. pinecall-0.1.0/tests/orgs/test_orgs_migration.py +167 -0
  714. pinecall-0.1.0/tests/orgs/test_provider_keys_migration.py +110 -0
  715. pinecall-0.1.0/tests/orgs/test_the_quotas_table.py +80 -0
  716. pinecall-0.1.0/tests/postgres.py +137 -0
  717. pinecall-0.1.0/tests/providers/__init__.py +1 -0
  718. pinecall-0.1.0/tests/providers/embed/__init__.py +1 -0
  719. pinecall-0.1.0/tests/providers/embed/test_perplexity.py +271 -0
  720. pinecall-0.1.0/tests/providers/embed/test_tei.py +116 -0
  721. pinecall-0.1.0/tests/providers/embed/test_the_builder.py +96 -0
  722. pinecall-0.1.0/tests/providers/test_a_turned_knob_is_written_through.py +44 -0
  723. pinecall-0.1.0/tests/providers/test_blocks.py +310 -0
  724. pinecall-0.1.0/tests/providers/test_catalog.py +96 -0
  725. pinecall-0.1.0/tests/providers/test_declaration.py +45 -0
  726. pinecall-0.1.0/tests/providers/test_models.py +144 -0
  727. pinecall-0.1.0/tests/providers/test_pipeline.py +72 -0
  728. pinecall-0.1.0/tests/providers/test_plugin.py +65 -0
  729. pinecall-0.1.0/tests/providers/test_prices.py +195 -0
  730. pinecall-0.1.0/tests/providers/test_provider_keys.py +102 -0
  731. pinecall-0.1.0/tests/providers/test_published.py +52 -0
  732. pinecall-0.1.0/tests/providers/test_registry.py +104 -0
  733. pinecall-0.1.0/tests/providers/test_stt.py +89 -0
  734. pinecall-0.1.0/tests/providers/test_tts.py +93 -0
  735. pinecall-0.1.0/tests/providers/test_voices.py +50 -0
  736. pinecall-0.1.0/tests/providers/vendors/__init__.py +12 -0
  737. pinecall-0.1.0/tests/providers/vendors/acme.py +12 -0
  738. pinecall-0.1.0/tests/routes/__init__.py +1 -0
  739. pinecall-0.1.0/tests/routes/test_answering.py +62 -0
  740. pinecall-0.1.0/tests/routes/test_postgres_table.py +109 -0
  741. pinecall-0.1.0/tests/routes/test_table.py +57 -0
  742. pinecall-0.1.0/tests/routes/test_trunks.py +33 -0
  743. pinecall-0.1.0/tests/routes/test_twilio.py +148 -0
  744. pinecall-0.1.0/tests/session/__init__.py +1 -0
  745. pinecall-0.1.0/tests/session/fake_llm.py +168 -0
  746. pinecall-0.1.0/tests/session/test_a_run_never_greets.py +36 -0
  747. pinecall-0.1.0/tests/session/test_asking.py +157 -0
  748. pinecall-0.1.0/tests/session/test_clock.py +67 -0
  749. pinecall-0.1.0/tests/session/test_first_entries.py +47 -0
  750. pinecall-0.1.0/tests/session/test_greeting.py +114 -0
  751. pinecall-0.1.0/tests/session/test_lookups.py +334 -0
  752. pinecall-0.1.0/tests/session/test_remembering.py +58 -0
  753. pinecall-0.1.0/tests/session/test_visibility.py +59 -0
  754. pinecall-0.1.0/tests/session/text/__init__.py +1 -0
  755. pinecall-0.1.0/tests/session/text/test_llm_live.py +57 -0
  756. pinecall-0.1.0/tests/session/text/test_lookups.py +180 -0
  757. pinecall-0.1.0/tests/session/text/test_metrics_passthrough.py +115 -0
  758. pinecall-0.1.0/tests/session/text/test_prompt.py +65 -0
  759. pinecall-0.1.0/tests/session/text/test_running.py +102 -0
  760. pinecall-0.1.0/tests/session/text/test_session.py +107 -0
  761. pinecall-0.1.0/tests/session/voice/__init__.py +1 -0
  762. pinecall-0.1.0/tests/session/voice/fakes.py +214 -0
  763. pinecall-0.1.0/tests/session/voice/room/__init__.py +1 -0
  764. pinecall-0.1.0/tests/session/voice/room/fakes.py +293 -0
  765. pinecall-0.1.0/tests/session/voice/room/test_datachannel.py +265 -0
  766. pinecall-0.1.0/tests/session/voice/room/test_facts.py +183 -0
  767. pinecall-0.1.0/tests/session/voice/room/test_verbs.py +168 -0
  768. pinecall-0.1.0/tests/session/voice/silence.py +85 -0
  769. pinecall-0.1.0/tests/session/voice/test_agent.py +106 -0
  770. pinecall-0.1.0/tests/session/voice/test_barge_in.py +33 -0
  771. pinecall-0.1.0/tests/session/voice/test_commands.py +177 -0
  772. pinecall-0.1.0/tests/session/voice/test_events.py +268 -0
  773. pinecall-0.1.0/tests/session/voice/test_hanging_up.py +78 -0
  774. pinecall-0.1.0/tests/session/voice/test_hearing.py +54 -0
  775. pinecall-0.1.0/tests/session/voice/test_kit.py +37 -0
  776. pinecall-0.1.0/tests/session/voice/test_lookups.py +259 -0
  777. pinecall-0.1.0/tests/session/voice/test_metrics.py +214 -0
  778. pinecall-0.1.0/tests/session/voice/test_prompt.py +130 -0
  779. pinecall-0.1.0/tests/session/voice/test_reading_back.py +70 -0
  780. pinecall-0.1.0/tests/session/voice/test_session.py +185 -0
  781. pinecall-0.1.0/tests/session/voice/test_sip.py +88 -0
  782. pinecall-0.1.0/tests/session/voice/test_supervising.py +236 -0
  783. pinecall-0.1.0/tests/session/voice/test_tools.py +141 -0
  784. pinecall-0.1.0/tests/session/voice/test_transfer.py +116 -0
  785. pinecall-0.1.0/tests/session/voice/test_voice.py +242 -0
  786. pinecall-0.1.0/tests/session/voice/test_whisper_changes_the_next_line.py +137 -0
  787. pinecall-0.1.0/tests/session/voice/test_writing.py +39 -0
  788. pinecall-0.1.0/tests/test_box_packages.py +210 -0
  789. pinecall-0.1.0/tests/test_env_example.py +40 -0
  790. pinecall-0.1.0/tests/test_isolation.py +153 -0
  791. pinecall-0.1.0/tests/test_layout.py +112 -0
  792. pinecall-0.1.0/tests/test_one_definition_per_thing.py +33 -0
  793. pinecall-0.1.0/tests/test_settings.py +190 -0
  794. pinecall-0.1.0/tests/test_settings_from_credentials.py +47 -0
  795. pinecall-0.1.0/tests/test_the_public_surface.py +47 -0
  796. pinecall-0.1.0/tests/test_vectors.py +38 -0
  797. pinecall-0.1.0/tests/tokens/__init__.py +1 -0
  798. pinecall-0.1.0/tests/tokens/test_ledger.py +31 -0
  799. pinecall-0.1.0/tests/tokens/test_postgres_ledger.py +48 -0
  800. pinecall-0.1.0/tests/tree.py +89 -0
  801. pinecall-0.1.0/tests/types/__init__.py +1 -0
  802. pinecall-0.1.0/tests/types/test_agent.py +115 -0
  803. pinecall-0.1.0/tests/types/test_call.py +85 -0
  804. pinecall-0.1.0/tests/types/test_carrier.py +36 -0
  805. pinecall-0.1.0/tests/types/test_consent.py +126 -0
  806. pinecall-0.1.0/tests/types/test_fusion.py +32 -0
  807. pinecall-0.1.0/tests/types/test_goldens.py +57 -0
  808. pinecall-0.1.0/tests/types/test_member.py +63 -0
  809. pinecall-0.1.0/tests/types/test_org.py +67 -0
  810. pinecall-0.1.0/tests/types/test_prompt.py +85 -0
  811. pinecall-0.1.0/tests/types/test_route.py +47 -0
  812. pinecall-0.1.0/tests/types/test_token.py +82 -0
  813. pinecall-0.1.0/tests/types/test_tool.py +87 -0
  814. pinecall-0.1.0/tests/types/test_wire_agreement.py +89 -0
  815. pinecall-0.1.0/tests/vectors.py +56 -0
  816. pinecall-0.1.0/tests/whatsapp/__init__.py +1 -0
  817. pinecall-0.1.0/tests/whatsapp/bodies.py +81 -0
  818. pinecall-0.1.0/tests/whatsapp/test_inbound.py +61 -0
  819. pinecall-0.1.0/tests/whatsapp/test_signing.py +38 -0
  820. pinecall-0.1.0/tests/worker/__init__.py +1 -0
  821. pinecall-0.1.0/tests/worker/fakes.py +119 -0
  822. pinecall-0.1.0/tests/worker/test_client.py +220 -0
  823. pinecall-0.1.0/tests/worker/test_entry.py +250 -0
  824. pinecall-0.1.0/tests/worker/test_heartbeat.py +84 -0
  825. pinecall-0.1.0/tests/worker/test_load.py +126 -0
  826. pinecall-0.1.0/tests/worker/test_main.py +208 -0
  827. pinecall-0.1.0/tests/worker/test_overflow.py +43 -0
  828. pinecall-0.1.0/tests/worker/test_recordings.py +109 -0
  829. pinecall-0.1.0/tests/worker/test_router.py +117 -0
  830. pinecall-0.1.0/tests/worker/test_seat.py +110 -0
  831. pinecall-0.1.0/uv.lock +4734 -0
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: add-a-vendor
3
+ description: Add an LLM, STT or TTS vendor the way this tree does it — a catalog row, a key, and the four places that are still by hand. Use when adding a provider, a model row, a price, or a provider key.
4
+ ---
5
+
6
+ # Adding a vendor
7
+
8
+ **Almost always, there is nothing to add.** `providers/catalog.py` already holds every vendor
9
+ livekit-agents ships a plugin for, `providers/plugin.py` builds any of them out of the plugin's own
10
+ constructor signature, and `_vendor_keys.py` holds a field for each. Check first:
11
+
12
+ ```bash
13
+ uv run pinecall-runtime providers --does tts | grep -i <the vendor>
14
+ ```
15
+
16
+ - **`ready`** — it runs today. Nothing to do.
17
+ - **`install`** — the row is right and the plugin is not on this box: add its extra to
18
+ `[project.optional-dependencies] providers` in `pyproject.toml`, `uv lock`, deploy. Use
19
+ `providers-big` when it drags a cloud SDK (boto3, azure-cognitiveservices-speech,
20
+ google-cloud-*, onnxruntime) — that is a decision a box makes, never a default.
21
+ - **`no key`** — `printf '%s' <the key> | make secret NAME=<ITS_VARIABLE>` from the checkout, then
22
+ `make restart`. The variable is in the `providers` table's own column.
23
+ - **not listed at all** — livekit added a plugin since. One row, below.
24
+
25
+ ## A row in the catalog
26
+
27
+ `providers/catalog.py`, alphabetical, positional in the order the fields are declared:
28
+
29
+ ```python
30
+ (Provider("acme", ("stt", "tts"), "ACME_API_KEY", ("acmeai",), "what a person is choosing"),)
31
+ ```
32
+
33
+ - `does` is what the plugin's own `__all__` exports — `tests/providers/test_catalog.py` checks the
34
+ row against the plugin and fails if they disagree.
35
+ - `env` is the variable **the plugin itself reads**, spelled the vendor's way. `None` when the
36
+ vendor has no single-string credential (AWS's chain, Google's service account, RTZR's pair):
37
+ BYOK then does not apply to it and nothing anywhere asks for one.
38
+ - An alias must **never** also be a model name of that vendor. A bare word that names a vendor IS
39
+ the vendor at the pipeline door, so `sonic`, `octave`, `sonar`, `mist`, `nova` and `aura` are
40
+ deliberately not aliases — every one of them is something a person could type meaning the model.
41
+
42
+ Then **one field in `_vendor_keys.py`**, named the variable lowercased. That rule is the whole of
43
+ the key wiring: `catalog.settings_field_of` reads it, the doctor reads it, the vault accepts the
44
+ vendor, `.env.example` and the box's units list it. `tests/providers/test_provider_keys.py` fails
45
+ while a row has no field.
46
+
47
+ ## A tuned file — only when this build has an OPINION
48
+
49
+ A file under `providers/llm/`, `stt/` or `tts/` exists to **override a plugin default**: a model a
50
+ phone line should not wait for, an endpointing number, a model this build forbids. Five of them do.
51
+ If there is nothing to override, do not write one — the generic path is not a fallback, it is the
52
+ normal case, and a file that only forwards `model=` and `api_key=` is one more thing to go stale.
53
+
54
+ ```python
55
+ """Acme: one line saying what the plugin is, and the one default this repo overrides."""
56
+
57
+ from livekit.plugins import acme
58
+
59
+ from pinecall.providers.llm import VENDORS
60
+ from pinecall.providers.registry import Asked, Chat, a_key
61
+
62
+ DEFAULT_MODEL = "acme-fast" # the plugin's own is acme-large (acme/llm.py:67)
63
+
64
+
65
+ @VENDORS.registers("acme")
66
+ def build(asked: Asked) -> Chat:
67
+ return acme.LLM(model=asked.model or DEFAULT_MODEL, api_key=a_key("acme", asked))
68
+ ```
69
+
70
+ ## The four places that are still by hand
71
+
72
+ 1. `providers/knocks.py` — the vendor's cheapest authenticated GET, so `doctor` knocks and
73
+ `make deploy` refuses a dead key by name. **Only a URL you have opened with a live key and
74
+ watched answer 200.** A guessed one 404s a perfectly good key and refuses a box that was fine;
75
+ a vendor with no row is simply not knocked, which is honest.
76
+ 2. `providers/prices.py` — a row only where you read the vendor's own page, with `AS_OF` beside
77
+ it. Everything else is priced by `published_prices.json`
78
+ (`scripts/refresh-prices`, mahimailabs/voice-prices), and the hand-read table wins where the
79
+ two disagree — Soniox is the row that says why.
80
+ 3. `infra/box/*.service` and `WORKER_CREDENTIALS` in the `Makefile` — one `ImportCredential=` line
81
+ each and one name, when the vendor has an env.
82
+ 4. `tests/conftest.py` — a dead sentinel, if a suite needs to construct that vendor.
83
+
84
+ Then `scripts/generate-env-example` (a test fails while it drifts), and `CHANGELOG.md`.
85
+
86
+ ## What the tests will refuse
87
+
88
+ - A vendor SDK import anywhere but `providers/` (`tests/test_isolation.py`, `VENDOR_SDKS`).
89
+ - A catalog row that disagrees with its plugin's `__all__` (`tests/providers/test_catalog.py`).
90
+ - A catalogued vendor with no settings field (`tests/providers/test_provider_keys.py`).
91
+ - A second definition of a default model, a header name, a URL: `grep` first
92
+ (`tests/test_one_definition_per_thing.py`).
93
+ - Two files in one directory one letter apart (`openai.py` next to `openia.py`).
94
+ - A public name added to `pinecall.providers` without editing `tests/test_the_public_surface.py`.
95
+
96
+ ## Verify
97
+
98
+ ```bash
99
+ scripts/format && scripts/lint && uv run pytest -m unit -q # three times: it is shuffled
100
+ uv run pinecall-runtime providers --does stt # the row, and what it wants
101
+ uv run pinecall-runtime doctor # the knock, with a real key in .env
102
+ ```
103
+
104
+ An STT vendor is verified on a spoken call (`debug-a-call`): the transcript entries name the
105
+ vendor in `metrics.stt`. A TTS vendor: `tts_node_ttfb` on `turn.agent`.
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: debug-a-call
3
+ description: Read a call's log to find what went wrong, and drive a simulated voice or text call at a gateway (local or a box) to reproduce it. Use for "the agent said nothing", a 503 from simulate, a slow turn, a tool that never answered, or proving a worker takes calls.
4
+ ---
5
+
6
+ # Debugging a call
7
+
8
+ The log is the truth: every event of a call, with a `seq`, in `call_log`. Read it before reading
9
+ any process's stdout.
10
+
11
+ ## Read the log
12
+
13
+ ```bash
14
+ uv run pinecall-runtime sessions list # newest first, off Postgres, gateway up or not
15
+ uv run pinecall-runtime sessions show <call> # entry by entry, every metric under its turn
16
+ uv run pinecall-runtime sessions tail <call> # as it happens (polls)
17
+ uv run pinecall-runtime sessions recording <call> # where the audio went (RECORD=1)
18
+ ```
19
+
20
+ What to look for, in order: `call.started` (channel, direction) → `turn.user` / `turn.agent`
21
+ with their metrics (`e2e_latency`, `llm_node_ttft`, `tts_node_ttfb`) → `tool.call` /
22
+ `tool.result` (did the app answer? `timeout_s` on the ToolSpec) → `metrics.*` errors, the
23
+ session's own `error` entries → `call.summary` (usage, cost) → `call.score` (the judges: `panel`
24
+ is who RAN, `judges` who ANSWERED; `not_judged` means the session was built without a Scorer).
25
+
26
+ A call with `turn.user` and no `turn.agent`: the LLM or TTS — see the worker's journal. A call
27
+ with no `turn.user`: the STT, or the seat (`worker/seat.py`: which participant is the caller).
28
+
29
+ ## Drive a call
30
+
31
+ From an example in the agents repo, against whichever gateway you name:
32
+
33
+ ```bash
34
+ cd ~/pinecall-v2/agents/examples/clinica-norte
35
+ PINECALL_URL=https://box.pinecall.io node ../../bin/pinecall.js whoami # which key, from where
36
+ PINECALL_URL=… node ../../bin/pinecall.js simulate --persona apurado --turns 3 # text
37
+ PINECALL_URL=… node ../../bin/pinecall.js simulate --persona apurado --voice --judge # voice
38
+ node ../../bin/pinecall.js personas list
39
+ ```
40
+
41
+ `--voice` mounts the class in THIS terminal (it serves the call the runtime opens), asks the
42
+ gateway to put a synthetic caller on a real line, and prints each turn with its latencies; with
43
+ `--judge`, `call.score` read back. The key comes from `PINECALL_API_KEY` in the env or
44
+ `~/.pinecall/credentials`; `PINECALL_URL` picks the gateway (otherwise the local dev file).
45
+
46
+ What a `503` from simulate means:
47
+ - `this box has no speech tool` — the GATEWAY's box lacks `espeak-ng` (macOS: `say`). It is in
48
+ `PACKAGES`; `make deploy` installs it.
49
+ - `no agent joined room … in 20s: is pinecall-runtime worker up?` — no worker took the job:
50
+ none registered on the SFU, or the one that did crashed on the job (its journal has a traceback:
51
+ `NoProvider: <vendor> has no API key in this process` is a missing or empty credential).
52
+
53
+ ## Which worker took it
54
+
55
+ ```bash
56
+ ssh <hub> 'sudo podman logs --since 10m pinecall-livekit 2>&1 | grep -E "worker registered|job"'
57
+ ssh <worker> "sudo journalctl -u pinecall-worker --since '$START' -o cat" \
58
+ | grep -iE "received job|pipeline is live|error|exception" \
59
+ | sed -E 's/(key|secret|token)[=:] *[^ ]+/\1=***/Ig'
60
+ ```
61
+
62
+ `received job request` names `job_id`, `dispatch_id`, `room`; `the pipeline is live N s after
63
+ the job arrived` is the number to watch (0.5 s on the box). To prove a call lands on ONE worker,
64
+ stop the others first (`sudo systemctl stop pinecall-worker` on the hub), never guess from load.
65
+
66
+ ## Local
67
+
68
+ `uv run pinecall-runtime doctor` first. `unset PINECALL_API_KEY` before `pinecall run` against a
69
+ dev-key gateway (it is ignored out loud; a bare 403 is that). Native Postgres on the laptop:
70
+ `DATABASE_URL=…@[::1]:5432/…`. A `.env` the runtime ignored is never silent: the doctor's first
71
+ line says which file it read.
72
+
73
+ ## Never
74
+
75
+ Print a key or a token from a journal, a `.env`, or `~/.pinecall/credentials` (the `sed` above
76
+ on every journal read). Run `simulate` against production with a persona that books: `consent`
77
+ is judged, but the tool still runs in the app you mounted.
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: deploy
3
+ description: Deploy this checkout to a box (hub, worker, or one machine) and verify it, without a secret ever reaching a screen. Use for make deploy, a second box, rotating a vendor key, or when the doctor refuses a key.
4
+ ---
5
+
6
+ # Deploying a box
7
+
8
+ The deploy is the root `Makefile`: rsync + ssh + make + curl, from a checkout, to the account
9
+ cloud-init made. Nothing on the box can clone; nothing here uses shipway. The box's identity is
10
+ `deploy.local.mk` beside the Makefile (git-ignored): `BOX`, `DOMAIN`, optional `SSH_KEY`.
11
+
12
+ ## NEVER
13
+
14
+ - Print a secret: not a credential's value, not a key from the shell, not a line of a journal
15
+ that carries one. Compare credentials by fingerprint (below). Pipe every journal read through
16
+ `sed -E 's/(key|secret|token)[=:] *[^ ]+/\1=***/Ig'`.
17
+ - Fix the box by hand and stop there. An `apt install`, a credential rewritten with
18
+ `systemd-creds`, a unit edited in `/etc`: each is at most a probe. The same fix goes in the
19
+ tree (`PACKAGES` in `infra/box/Makefile`, `make secret`, a doctor check, the unit file) and the
20
+ box re-converges through `make deploy` before the turn ends.
21
+ - Restart a container (`pinecall-livekit`, `-sip`, `-postgres`, `-redis`) during a call. The
22
+ deploy never does; a changed `.container` is restarted between two calls, by a person.
23
+ - Run `shipway deploy` anywhere near this tree. `~/pinecall/v2/shipway.yml` points at the same box.
24
+ - Leave `PINECALL_API_KEY` exported while running the deploy or anything that opens a socket:
25
+ `unset PINECALL_API_KEY` first.
26
+
27
+ ## The path
28
+
29
+ ```bash
30
+ unset PINECALL_API_KEY
31
+ make deploy # sync → install → restart → doctor
32
+ make deploy BOX=deploy@<ip> DOMAIN=<domain> # another box, same tree
33
+ ```
34
+
35
+ What each step proves: `sync` — the two directories the box needs; `install` — every file of
36
+ `infra/box/` where systemd reads it, the packages the box is missing, the role's units enabled and
37
+ the others' disabled, `uv sync --frozen` as the service user; `restart` — by role: `all` restarts
38
+ gateway → health through Caddy → worker; `hub` restarts the gateway only (`systemctl restart`
39
+ would START a worker the role disabled); `worker` restarts its one unit; `doctor` — the runtime's
40
+ doctor as the units see the box, every vendor knocked with the key the box holds.
41
+
42
+ A deploy that ends in `✗ provider keys answer refused ELEVEN_API_KEY (HTTP 401)` is a dead key,
43
+ not a deploy failure: rotate it (below), `make restart`, `make doctor`.
44
+
45
+ ## Fingerprints, never values
46
+
47
+ ```bash
48
+ ssh <box> "sudo systemd-creds decrypt --name=$N /etc/credstore.encrypted/$N - | sha256sum | cut -c1-12; \
49
+ sudo systemd-creds decrypt --name=$N /etc/credstore.encrypted/$N - | wc -c"
50
+ ```
51
+
52
+ Two boxes agree when the twelve characters and the byte count agree. `e3b0c44298fc` is the
53
+ sha256 of the EMPTY string: a credential written from an unset shell variable. The shell may call
54
+ a key differently from the runtime (`ELEVENLABS_API_KEY` vs `ELEVEN_API_KEY`); `.env.example`
55
+ is the runtime's list.
56
+
57
+ ## Rotating a vendor key
58
+
59
+ ```bash
60
+ printf '%s' "$ELEVENLABS_API_KEY" | make secret NAME=ELEVEN_API_KEY # the value on stdin, on no screen
61
+ make restart && make doctor
62
+ make worker-secrets WORKER=deploy@<worker-ip> # every worker takes the hub's copy
63
+ ```
64
+
65
+ `box secrets` (the generated ones: LiveKit pair, Postgres, ops, vault) never rotates; `box secret
66
+ <NAME>` replaces in place.
67
+
68
+ ## A second box
69
+
70
+ The hub keeps the gateway and the media plane (`PINECALL_ROLE=hub` in its `/etc/pinecall/box.env`);
71
+ each worker is `PINECALL_ROLE=worker` with `LIVEKIT_URL=wss://<hub-domain>`,
72
+ `PINECALL_GATEWAY_URL=https://<hub-domain>`, `PINECALL_MAX_JOBS=<measured>` — cloud-init writes
73
+ those lines. Order: `make worker-secrets WORKER=…` · `make deploy BOX=<worker> DOMAIN=<hub>` · the
74
+ hub's SFU log says `worker registered` with a new `AW_` id · one voice call lands there (the
75
+ `debug-a-call` skill) · only then the hub's role flips to `hub` and `make deploy` runs on it.
76
+
77
+ ## Reading the box
78
+
79
+ ```bash
80
+ make status # every unit and container, one line each
81
+ make logs UNIT=worker # gateway (default) · worker · caddy
82
+ ssh <box> 'sudo podman logs --since 5m pinecall-livekit 2>&1 | grep -E "worker registered|job"'
83
+ ssh <box> "sudo journalctl -u pinecall-worker --since '$START' -o cat" | sed -E 's/(key|secret|token)[=:] *[^ ]+/\1=***/Ig'
84
+ ```
85
+
86
+ ## Gotchas
87
+
88
+ - A fresh box has `/opt/pinecall/app` and nothing under it; `sync` makes the two directories.
89
+ - The fence lives in `prerouting` (raw priority) because DNAT'd container traffic never hits
90
+ `input`, and it must accept udp/tcp 53 from `podman*` or containers cannot resolve each other.
91
+ - A credential file is named EXACTLY as the credential, no extension; `ImportCredential=` accepts
92
+ a trailing glob only (`PINECALL_*`).
93
+ - The units run `/opt/pinecall/venv/bin/pinecall-runtime`, never `uv run`.
94
+ - `livekit` on the hub reads a worker's load every 0.5 s and stops routing at 0.7 of it:
95
+ `PINECALL_MAX_JOBS` is one under the measured ceiling.
@@ -0,0 +1,256 @@
1
+ # The only .env ever committed, and it is GENERATED: `scripts/generate-env-example` renders it
2
+ # from the aliases src/pinecall/_settings.py declares, and a test fails when the two drift.
3
+ # Copy it to .env and fill what you have. A real environment variable wins over the file,
4
+ # and a name this runtime does not read is ignored, never an error.
5
+
6
+ # Anthropic, llm. Every provider key keeps the vendor's own variable name.
7
+ ANTHROPIC_API_KEY=
8
+
9
+ # OpenAI, llm, stt and tts. The other LLM a call may run on.
10
+ OPENAI_API_KEY=
11
+
12
+ # Soniox, stt and tts. A call needs one key of each role: llm, stt, tts.
13
+ SONIOX_API_KEY=
14
+
15
+ # Deepgram, stt and tts. Flux and Nova for the ears, Aura for the voice.
16
+ DEEPGRAM_API_KEY=
17
+
18
+ # ElevenLabs, stt and tts. The voice this runtime speaks with by default.
19
+ ELEVEN_API_KEY=
20
+
21
+ # AssemblyAI, stt. Universal-Streaming.
22
+ ASSEMBLYAI_API_KEY=
23
+
24
+ # Async AI, tts.
25
+ ASYNCAI_API_KEY=
26
+
27
+ # Azure Speech, stt and tts. Needs AZURE_SPEECH_REGION beside the key.
28
+ AZURE_SPEECH_KEY=
29
+
30
+ # Baseten, llm, stt and tts. Open models on Baseten's own endpoints.
31
+ BASETEN_API_KEY=
32
+
33
+ # Bland, tts.
34
+ BLAND_API_KEY=
35
+
36
+ # Camb.ai, tts.
37
+ CAMB_API_KEY=
38
+
39
+ # Cartesia, stt and tts. Sonic, and Ink-Whisper for the ears.
40
+ CARTESIA_API_KEY=
41
+
42
+ # Cerebras, llm.
43
+ CEREBRAS_API_KEY=
44
+
45
+ # Naver CLOVA, stt. Needs CLOVA_STT_INVOKE_URL beside the secret.
46
+ CLOVA_STT_SECRET_KEY=
47
+
48
+ # fal, stt. Wizper. Batch, not a live socket.
49
+ FAL_KEY=
50
+
51
+ # Fireworks AI, stt.
52
+ FIREWORKS_API_KEY=
53
+
54
+ # Fish Audio, tts.
55
+ FISH_API_KEY=
56
+
57
+ # Gladia, stt.
58
+ GLADIA_API_KEY=
59
+
60
+ # Gnani, stt and tts.
61
+ GNANI_API_KEY=
62
+
63
+ # Google, llm, stt and tts. Gemini on the key; Speech-to-Text and Text-to-Speech want a service account.
64
+ GOOGLE_API_KEY=
65
+
66
+ # Gradium, stt and tts.
67
+ GRADIUM_API_KEY=
68
+
69
+ # Groq, llm, stt and tts. Whisper and open models, fast.
70
+ GROQ_API_KEY=
71
+
72
+ # Hume, tts.
73
+ HUME_API_KEY=
74
+
75
+ # Inworld, stt and tts.
76
+ INWORLD_API_KEY=
77
+
78
+ # LMNT, tts.
79
+ LMNT_API_KEY=
80
+
81
+ # MiniMax, tts.
82
+ MINIMAX_API_KEY=
83
+
84
+ # Mistral AI, llm, stt and tts.
85
+ MISTRAL_API_KEY=
86
+
87
+ # Murf, tts.
88
+ MURF_API_KEY=
89
+
90
+ # Neuphonic, tts.
91
+ NEUPHONIC_API_KEY=
92
+
93
+ # NVIDIA Riva, stt and tts.
94
+ NVIDIA_API_KEY=
95
+
96
+ # Palabra, stt and tts.
97
+ PALABRA_API_KEY=
98
+
99
+ # Perplexity, llm. Sonar, and the embedder: the contextual model and the flat.
100
+ PERPLEXITY_API_KEY=
101
+
102
+ # Resemble AI, tts.
103
+ RESEMBLE_API_KEY=
104
+
105
+ # Respeecher, tts.
106
+ RESPEECHER_API_KEY=
107
+
108
+ # Rime, tts.
109
+ RIME_API_KEY=
110
+
111
+ # Sarvam AI, llm, stt and tts. The Indian languages, all three jobs on one account.
112
+ SARVAM_API_KEY=
113
+
114
+ # Simplismart, stt and tts.
115
+ SIMPLISMART_API_KEY=
116
+
117
+ # slng, stt and tts.
118
+ SLNG_API_KEY=
119
+
120
+ # Smallest AI, stt and tts.
121
+ SMALLEST_API_KEY=
122
+
123
+ # Speechify, tts.
124
+ SPEECHIFY_API_KEY=
125
+
126
+ # Speechmatics, stt and tts.
127
+ SPEECHMATICS_API_KEY=
128
+
129
+ # Spitch, stt and tts. The African languages.
130
+ SPITCH_API_KEY=
131
+
132
+ # Upliftai, tts.
133
+ UPLIFTAI_API_KEY=
134
+
135
+ # Vakyam, tts.
136
+ VAKYAM_API_KEY=
137
+
138
+ # xAI, stt and tts. Grok speaks and hears here; Grok the LLM is reached through livekit or openai.
139
+ XAI_API_KEY=
140
+
141
+ # OpenRouter, an embedder: the other way to the flat model, no contextual door.
142
+ OPENROUTER_API_KEY=
143
+
144
+ # The box's own WhatsApp Cloud API token, used for an org that brought none.
145
+ WHATSAPP_ACCESS_TOKEN=
146
+
147
+ # LiveKit: the media plane both processes talk to. One port serves ws and http.
148
+ LIVEKIT_URL=ws://127.0.0.1:7880
149
+
150
+ # The LiveKit API key, as the LiveKit server's own config declares it.
151
+ LIVEKIT_API_KEY=
152
+
153
+ # Its secret. It also signs the call tokens, so there is no second one.
154
+ LIVEKIT_API_SECRET=
155
+
156
+ # The LiveKit URL a browser is told to join. Unset, it hears LIVEKIT_URL.
157
+ LIVEKIT_PUBLIC_URL=
158
+
159
+ # The box's Twilio account, for the numbers it buys for a tenant.
160
+ TWILIO_ACCOUNT_SID=
161
+
162
+ # An API key SID on that account, revocable on its own; else the account SID.
163
+ TWILIO_API_KEY=
164
+
165
+ # The API key's secret, or the account's auth token when no key is set.
166
+ TWILIO_API_SECRET=
167
+
168
+ # The box's public name: where a carrier sends a call. Unset, nothing imports.
169
+ PINECALL_DOMAIN=
170
+
171
+ # Postgres 17 with pgvector and pg_textsearch: the one stateful service.
172
+ DATABASE_URL=postgresql://pinecall:pinecall@127.0.0.1:5432/pinecall
173
+
174
+ # TEI, the embedder. 8081, because the gateway serves 8080 on the same host.
175
+ TEI_URL=http://127.0.0.1:8081
176
+
177
+ # Who embeds: tei · perplexity · openrouter. The other two need their API key.
178
+ EMBED_PROVIDER=tei
179
+
180
+ # The embedding model. Unset: BAAI/bge-m3 · pplx-embed-context-v1-0.6b · perplexity/pplx-embed-v1-0.6b, by provider.
181
+ EMBED_MODEL=
182
+
183
+ # Where it is asked. Unset: the provider's own door, and TEI_URL for TEI.
184
+ EMBED_BASE_URL=
185
+
186
+ # The Meta app's App Secret: every webhook body is HMAC-SHA256-signed with it. Unset, the WhatsApp door is closed.
187
+ PINECALL_WHATSAPP_APP_SECRET=
188
+
189
+ # The word Meta echoes back when the webhook is subscribed. Choose it; type it in the Meta app.
190
+ PINECALL_WHATSAPP_VERIFY_TOKEN=
191
+
192
+ # Whether a call's audio is kept at all. 0, false, no and off all mean no.
193
+ RECORD=true
194
+
195
+ # Where a kept recording lands, absolute or relative to the working directory.
196
+ PINECALL_RECORDINGS=recordings
197
+
198
+ # The gateway a worker's job asks. Read in the job process, never the parent.
199
+ PINECALL_GATEWAY_URL=http://127.0.0.1:8080
200
+
201
+ # The agent a job that names none is for.
202
+ PINECALL_AGENT=
203
+
204
+ # Calls this worker holds at once, measured on its machine. Unset: gate on CPU.
205
+ PINECALL_MAX_JOBS=
206
+
207
+ # Where the worker's own health server binds, on loopback. Not 8080 or 8081.
208
+ PINECALL_WORKER_HTTP_PORT=8082
209
+
210
+ # What this worker is called in its heartbeats. Unset: the short hostname.
211
+ PINECALL_WORKER_NAME=
212
+
213
+ # What the overflow agent says when the fleet is full, before it hangs up.
214
+ PINECALL_OVERFLOW_SAYS=En este momento todas nuestras líneas están ocupadas. Hemos tomado nota de su número y le devolveremos la llamada en cuanto se libere una. Gracias por su paciencia.
215
+
216
+ # The app socket a job's call claims. Unset, the call takes the newest holder.
217
+ PINECALL_APP=
218
+
219
+ # What this box runs: all · hub · worker. The doctor asks after what it has.
220
+ PINECALL_ROLE=all
221
+
222
+ # How short a member's password may be. 0 is no rule.
223
+ PINECALL_MIN_PASSWORD=8
224
+
225
+ # The key /v1/ops/* is authenticated by. Unset, the operator API is closed.
226
+ PINECALL_OPS_KEY=
227
+
228
+ # Pinecall's hosted gateway: a plan, billed.
229
+ PINECALL_CLOUD=false
230
+
231
+ # Whether a stranger may make an org at this gateway. Off unless you say.
232
+ PINECALL_SIGNUP=false
233
+
234
+ # Packages that plug a policy into the runtime's points, comma separated.
235
+ PINECALL_EXTENSIONS=
236
+
237
+ # The org key the worker knocks its gateway with, as `keys issue` printed it.
238
+ PINECALL_WORKER_KEY=
239
+
240
+ # A Fernet key, generated once on the box by `pinecall-runtime box secrets`: a tenant's own provider keys are encrypted under it. Unset, the provider-key doors answer 503.
241
+ PINECALL_VAULT_KEY=
242
+
243
+ # How much both processes say: DEBUG, INFO, WARNING or ERROR.
244
+ PINECALL_LOG_LEVEL=INFO
245
+
246
+ # What judging one call may spend on a model, in euros. Zero: no judge asks.
247
+ PINECALL_JUDGE_CEILING_EUR=0.002
248
+
249
+ # What a spoken turn waits for the recall and search it started while the caller was still talking, in ms. It is silence on the line, so it is small.
250
+ PINECALL_VOICE_LOOKUP_BUDGET_MS=250
251
+
252
+ # What a written turn waits for recall and search, in ms. A written caller sends a whole message, so the lookup only starts at the end — and nobody hears the wait.
253
+ PINECALL_TEXT_LOOKUP_BUDGET_MS=3000
254
+
255
+ # What a hang-up waits for memory to be written, in s. Past it the call seals.
256
+ PINECALL_REMEMBER_BUDGET_S=8.0
@@ -0,0 +1,100 @@
1
+ # The gate: lint, then both test rings, on every push and every pull request. One job, because the
2
+ # whole check is a few minutes and a matrix would only make a failure harder to read.
3
+ #
4
+ # Two repositories, laid out the way a laptop has them: `pyproject.toml` names
5
+ # `../protocol/python` as an editable path dependency, so the second checkout is what makes the
6
+ # wire resolvable — until pinecall-protocol is published and the line becomes a version range.
7
+ name: ci
8
+
9
+ on:
10
+ push:
11
+ branches: ["**"]
12
+ pull_request:
13
+ workflow_dispatch:
14
+ # release.yml corre ESTE archivo como su gate, en vez de repetir los pasos: el gate del release
15
+ # y el de cada push son el mismo, o uno de los dos miente.
16
+ workflow_call:
17
+
18
+ permissions:
19
+ contents: read
20
+
21
+ concurrency:
22
+ group: ci-${{ github.ref }}
23
+ cancel-in-progress: true
24
+
25
+ jobs:
26
+ check:
27
+ runs-on: ubuntu-latest
28
+ # Most of these minutes are the Postgres image: pgvector and pg_textsearch are compiled from
29
+ # source, because the image with both is this repository's own and is built, not published.
30
+ #
31
+ # `max_connections=300` on that container: pytest-xdist opens a worker per core and each one
32
+ # of them a pool, and a runner has more cores than a laptop — the default hundred ran out and
33
+ # answered `sorry, too many clients already`.
34
+ timeout-minutes: 30
35
+
36
+ env:
37
+ DATABASE_URL: postgresql://pinecall:pinecall@127.0.0.1:5432/pinecall
38
+
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ with:
42
+ path: runtime
43
+
44
+ - uses: actions/checkout@v4
45
+ with:
46
+ repository: pinecall/protocol
47
+ path: protocol
48
+
49
+ # What a person does with `docker compose -f infra/compose/dev.yml up -d`, in one step.
50
+ #
51
+ # The schema needs BOTH extensions — `halfvec` in 0008 and 0009, a `bm25` index in 0009 —
52
+ # so the stock image cannot run a migration at all: it stops at `type "halfvec" does not
53
+ # exist`. It is started as a STEP and not as a service, because a `services:` block starts
54
+ # before any step runs and so cannot wait for a build. The two CREATE EXTENSIONs are here
55
+ # because no migration creates one — the same two a person types once by hand.
56
+ - name: postgres with the two extensions the schema needs
57
+ run: |
58
+ docker build -q -t pinecall/postgres:ci runtime/infra/compose/postgres
59
+ docker run -d --name postgres -p 5432:5432 \
60
+ -e POSTGRES_USER=pinecall -e POSTGRES_PASSWORD=pinecall -e POSTGRES_DB=pinecall \
61
+ pinecall/postgres:ci -c max_connections=300
62
+ # NOT pg_isready: postgres's entrypoint runs initdb behind a TEMPORARY server on the
63
+ # same socket, so `accepting connections` is true a second before POSTGRES_DB exists
64
+ # and the next line dies with `database "pinecall" does not exist`. Wait for the thing
65
+ # that actually matters — the database answering a query as the user.
66
+ for _ in $(seq 1 90); do
67
+ docker exec postgres psql -U pinecall -d pinecall -c 'select 1' >/dev/null 2>&1 && break
68
+ sleep 1
69
+ done
70
+ docker exec postgres psql -U pinecall -d pinecall \
71
+ -c "CREATE EXTENSION IF NOT EXISTS vector" \
72
+ -c "CREATE EXTENSION IF NOT EXISTS pg_textsearch"
73
+
74
+ - uses: astral-sh/setup-uv@v5
75
+ with:
76
+ enable-cache: true
77
+ cache-dependency-glob: runtime/uv.lock
78
+
79
+ # `--extra runtime` carries livekit-agents and its plugins; `--extra providers` the vendor
80
+ # SDKs the provider tables construct. The dev group holds the checkers and squawk.
81
+ - run: uv sync --locked --extra runtime --extra providers --group dev
82
+ working-directory: runtime
83
+
84
+ # ruff, pyright, mypy, then squawk over the migrations that have not landed.
85
+ - run: scripts/lint
86
+ working-directory: runtime
87
+
88
+ # `public`, migrated, which is what a dev box has and what the app's own pool reads. The
89
+ # Postgres ring gives itself a schema per worker and migrates that; the API tests do not —
90
+ # they build the real app, whose lifespan opens a pool the moment DATABASE_URL answers, and
91
+ # a database that answers with no tables in it is `relation "pipeline_overrides" does not
92
+ # exist` a hundred and sixty times. On a laptop this is invisible: `public` was migrated
93
+ # months ago, or nothing answers at all.
94
+ - run: uv run pinecall-runtime migrate up
95
+ working-directory: runtime
96
+
97
+ # Ring 0 and the Postgres ring: no key, no model, no network. A test that needs a real
98
+ # ANTHROPIC_API_KEY skips itself by name rather than failing.
99
+ - run: scripts/test
100
+ working-directory: runtime