refactor(pjsip): extract answer policy module and consolidate production env - #27
Merged
Merged
Conversation
The production transport imported DelayedAnswerService from agent.pjsip_poc,
so live call logs carried the agent.pjsip_poc logger name and PoC wording
("answered by PoC agent"), which reads as if the proof of concept were still
the running entry point. Move the policy into agent/answer_policy.py and use
neutral wording. caller_id_from_uri moves along with it so the slim PoC image
does not have to import the production dependency tree.
The policy now logs the caller number at every lifecycle step — offer, answer,
duration limit, cancel, hangup, capacity reject — so a call can be traced end
to end. Only the SIP user extracted by caller_id_from_uri is recorded, never
the raw remoteUri; a missing number logs as "unknown". This is a deliberate
reversal of the previous stance of keeping numbers out of application logs:
they now appear regardless of PJSIP_LOG_LEVEL. Documented in
docs/pjsip-migration.md and the env comments.
compose.yml no longer loads .env.pjsip-poc as a second env file. That file is
scoped to compose.pjsip-poc.yml; production reads .env alone, which already
carried the FRITZ!Box credentials. POC_MAX_CALL_SECONDS was inert in
production anyway (no Settings field) — production uses MAX_CALL_SECONDS.
Deployments must move PJSIP tuning keys into .env; the effective values are
unchanged.
A real call on 2026-07-31 lost one response turn when /v1/audio/speech returned 503 mid-conversation. The pipeline aborted the turn instead of retrying, so the caller heard silence. Track the agent-side retry, the server-side root cause, and the still-unverified external caller ID format.
An external call logged 015172420641, not E.164. _is_trusted does an exact string match, so an E.164 allowlist entry would fail closed with no signal. Caller-ID lifecycle logging is now verified for internal and external calls.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extracts the delayed-answer policy out of the signalling-only PoC module into a dedicated
agent/answer_policy.py, so production PJSIP and the PoC share one implementation instead of the production transport importing frompjsip_poc.py. Also completes the PoC/production environment split: production now reads a single.env, and.env.pjsip-pocis PoC-only.Changes
agent/answer_policy.py(new) —DelayedAnswerService(delayed answer, max call duration, lifecycle logging) pluscaller_id_from_uri(), moved fromagent/pjsip_poc.py.TRUSTED_CALLERSmismatches diagnosable from the logs.agent/pjsip.py— imports the shared service and drops its duplicatecaller_id_from_uri.compose.yml— production stack no longer loads.env.pjsip-poc; PJSIP/FRITZ!Box settings live in.env.tests/test_answer_policy.py(new) takes over the answer-policy cases fromtests/test_pjsip_poc.py, plus 4 new caller-ID logging/extraction tests..env.example,.env.pjsip-poc.example,README.md,docs/pjsip-migration.md,CLAUDE.mdupdated for the single-.envproduction model;TODO.mdrecords two live-call findings (TTS 503 dropping a turn, caller-ID format vsTRUSTED_CALLERS).Deployment note (breaking for existing deploys)
compose.ymlno longer reads.env.pjsip-poc. FRITZ!Box SIP credentials and PJSIP tuning keys must be present in.envbefore deploying this branch, or startup fails withfritzbox_sip_username must not be empty.Testing
venv/bin/pytest -q→ 314 passed