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,192 @@
|
|
|
1
|
+
"""A call's lookups: recall and search for one turn, on its log; its turns read for memory."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
import psycopg
|
|
10
|
+
|
|
11
|
+
from pinecall.domain.agent import AgentConfig
|
|
12
|
+
from pinecall.domain.call import CallContext
|
|
13
|
+
from pinecall.domain.errors import EmbedderUnreachable, WrongModel
|
|
14
|
+
from pinecall.domain.names import JsonObject
|
|
15
|
+
from pinecall.domain.org import Quotas
|
|
16
|
+
from pinecall.domain.scope import Scope
|
|
17
|
+
from pinecall.log.logs import Log
|
|
18
|
+
from pinecall.postgres.pool import Pool
|
|
19
|
+
from pinecall.retrieval import knowledge, memory
|
|
20
|
+
from pinecall.retrieval.embed import Embedder
|
|
21
|
+
from pinecall.retrieval.extraction import Heard, Spoken
|
|
22
|
+
from pinecall.retrieval.knowledge import SearchQuery
|
|
23
|
+
from pinecall.retrieval.memory import Recall, RecalledFact
|
|
24
|
+
from pinecall.wire.events import AgentTurnEnded, DocsSources, ErrorEvent, MemoryOps, UserTurnEnded
|
|
25
|
+
from pinecall.wire.frames import Entry
|
|
26
|
+
from pinecall.wire.parts import DocSource, MemoryFact, MemoryOp, PlatformTool
|
|
27
|
+
from pinecall.wire.rest.calls import LookupRequest
|
|
28
|
+
from pinecall.wire.rest.retrieval import FoundChunk, SearchFound
|
|
29
|
+
|
|
30
|
+
SKIPPED = "{tool} did not run for this turn: {why}"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class OnTheCall:
|
|
38
|
+
"""The call a lookup runs for: whose it is, how it arrived, what its agent declared, its log."""
|
|
39
|
+
|
|
40
|
+
scope: Scope
|
|
41
|
+
context: CallContext
|
|
42
|
+
config: AgentConfig
|
|
43
|
+
log: Log
|
|
44
|
+
# When the door answered: recency is measured from it.
|
|
45
|
+
now: datetime
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# The results reach the model inside a tool result and nowhere else. A lookup that cannot run is
|
|
49
|
+
# an entry and the empty shape: the turn never waits on it. Nothing is written when a tool is
|
|
50
|
+
# off: an empty memory.ops would say a recall ran, and an empty docs.sources reads to the
|
|
51
|
+
# grounded judge as a search that found nothing.
|
|
52
|
+
async def lookup(
|
|
53
|
+
pool: Pool,
|
|
54
|
+
embedder: Embedder | None,
|
|
55
|
+
call: OnTheCall,
|
|
56
|
+
request: LookupRequest,
|
|
57
|
+
*,
|
|
58
|
+
quotas: Quotas,
|
|
59
|
+
) -> JsonObject:
|
|
60
|
+
"""Recall or search for the call, written on its log; the empty shape when nothing may run."""
|
|
61
|
+
if embedder is None or not _switched_on(call, request.tool, quotas):
|
|
62
|
+
return _nothing_found(request.tool)
|
|
63
|
+
started = time.perf_counter()
|
|
64
|
+
try:
|
|
65
|
+
if request.tool == "recall":
|
|
66
|
+
return await _recalled(pool, embedder, call, request, started=started)
|
|
67
|
+
return await _searched(pool, embedder, call, request, started=started)
|
|
68
|
+
except (EmbedderUnreachable, WrongModel, psycopg.Error) as failed:
|
|
69
|
+
logger.warning("%s did not run for call %s", request.tool, call.context.call, exc_info=True)
|
|
70
|
+
why = SKIPPED.format(tool=request.tool, why=str(failed) or type(failed).__name__)
|
|
71
|
+
skipped = ErrorEvent(code=f"{request.tool}_skipped", message=why, recoverable=True)
|
|
72
|
+
await call.log.append("error", skipped.written())
|
|
73
|
+
return _nothing_found(request.tool)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def heard_in(
|
|
77
|
+
context: CallContext, config: AgentConfig, entries: Sequence[Entry], *, at: datetime
|
|
78
|
+
) -> Heard | None:
|
|
79
|
+
"""What the extraction model reads of a finished call; None when nothing is to be kept."""
|
|
80
|
+
contact = context.remembered_as
|
|
81
|
+
if contact is None or config.memory is None:
|
|
82
|
+
return None
|
|
83
|
+
return Heard(
|
|
84
|
+
contact=contact,
|
|
85
|
+
call=context.call,
|
|
86
|
+
channel=context.channel,
|
|
87
|
+
policy=config.memory,
|
|
88
|
+
tools=tuple(tool.name for tool in config.tools),
|
|
89
|
+
spoken=[spoken for entry in entries if (spoken := _spoken(entry)) is not None],
|
|
90
|
+
at=at,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# A memory at its cap is still read, since a cap is about keeping.
|
|
95
|
+
def _switched_on(call: OnTheCall, tool: PlatformTool, quotas: Quotas) -> bool:
|
|
96
|
+
if tool == "recall":
|
|
97
|
+
keeps = call.config.memory is not None and call.context.remembered_as is not None
|
|
98
|
+
return keeps and not quotas.switched_off("memory_facts")
|
|
99
|
+
return bool(call.config.bases) and not quotas.switched_off("knowledge_chunks")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# The contact is the one the platform knows, never one the model named.
|
|
103
|
+
async def _recalled(
|
|
104
|
+
pool: Pool, embedder: Embedder, call: OnTheCall, request: LookupRequest, *, started: float
|
|
105
|
+
) -> JsonObject:
|
|
106
|
+
contact = call.context.remembered_as or ""
|
|
107
|
+
query = _query(request)
|
|
108
|
+
wanted = Recall(contact=contact, query=query, at=call.now)
|
|
109
|
+
recalled = await memory.recall(pool, embedder, call.scope, wanted)
|
|
110
|
+
op = MemoryOp(
|
|
111
|
+
op="recall",
|
|
112
|
+
contact=contact,
|
|
113
|
+
query=query,
|
|
114
|
+
facts=[_fact(item) for item in recalled.facts],
|
|
115
|
+
took_ms=_since(started),
|
|
116
|
+
)
|
|
117
|
+
await call.log.append("memory.ops", MemoryOps(ops=[op], speech_id=request.speech_id).written())
|
|
118
|
+
return {
|
|
119
|
+
"facts": [
|
|
120
|
+
{
|
|
121
|
+
"text": " ".join(item.fact.text.split()),
|
|
122
|
+
"source": item.fact.taught_by,
|
|
123
|
+
"since": item.fact.valid_from.date().isoformat(),
|
|
124
|
+
}
|
|
125
|
+
for item in recalled.facts
|
|
126
|
+
if item.fact.text.strip()
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# Every base in one search, so their scores are comparable; the app may say how many it wants.
|
|
132
|
+
async def _searched(
|
|
133
|
+
pool: Pool, embedder: Embedder, call: OnTheCall, request: LookupRequest, *, started: float
|
|
134
|
+
) -> JsonObject:
|
|
135
|
+
bases = call.config.bases
|
|
136
|
+
query = _query(request)
|
|
137
|
+
wanted = request.input.get("k")
|
|
138
|
+
k = wanted if isinstance(wanted, int) and wanted > 0 else max(docs.k for docs in bases)
|
|
139
|
+
floors = {docs.base: docs.min_score or 0.0 for docs in bases}
|
|
140
|
+
searched = await knowledge.search(
|
|
141
|
+
pool, embedder, call.scope, SearchQuery(query=query, k=k, bases=floors)
|
|
142
|
+
)
|
|
143
|
+
sources = DocsSources(
|
|
144
|
+
query=query,
|
|
145
|
+
sources=[
|
|
146
|
+
DocSource(
|
|
147
|
+
id=item.id,
|
|
148
|
+
base=item.base,
|
|
149
|
+
path=item.path,
|
|
150
|
+
heading=item.heading,
|
|
151
|
+
score=item.score,
|
|
152
|
+
excerpt=item.text,
|
|
153
|
+
)
|
|
154
|
+
for item in searched.found
|
|
155
|
+
],
|
|
156
|
+
took_ms=_since(started),
|
|
157
|
+
speech_id=request.speech_id,
|
|
158
|
+
)
|
|
159
|
+
await call.log.append("docs.sources", sources.written())
|
|
160
|
+
found = [
|
|
161
|
+
FoundChunk(path=item.path, heading=item.heading, text=item.text) for item in searched.found
|
|
162
|
+
]
|
|
163
|
+
return SearchFound(chunks=found).written()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _nothing_found(tool: PlatformTool) -> JsonObject:
|
|
167
|
+
if tool == "recall":
|
|
168
|
+
return {"facts": []}
|
|
169
|
+
return SearchFound(chunks=[]).written()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _query(request: LookupRequest) -> str:
|
|
173
|
+
return str(request.input.get("query") or "")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _fact(item: RecalledFact) -> MemoryFact:
|
|
177
|
+
fact = item.fact
|
|
178
|
+
return MemoryFact(
|
|
179
|
+
id=fact.id, text=fact.text, category=fact.category, score=item.score, source=fact.taught_by
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _spoken(entry: Entry) -> Spoken | None:
|
|
184
|
+
if entry.type == "turn.user":
|
|
185
|
+
return Spoken(role="user", text=UserTurnEnded.model_validate(entry.data).text)
|
|
186
|
+
if entry.type == "turn.agent":
|
|
187
|
+
return Spoken(role="agent", text=AgentTurnEnded.model_validate(entry.data).text)
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _since(started: float) -> float:
|
|
192
|
+
return (time.perf_counter() - started) * 1000
|