Skip to content

refactor(pjsip): extract answer policy module and consolidate production env - #27

Merged
volschin merged 4 commits into
mainfrom
refactor/answer-policy-and-env-cleanup
Aug 1, 2026
Merged

refactor(pjsip): extract answer policy module and consolidate production env#27
volschin merged 4 commits into
mainfrom
refactor/answer-policy-and-env-cleanup

Conversation

@volschin

@volschin volschin commented Aug 1, 2026

Copy link
Copy Markdown
Owner

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 from pjsip_poc.py. Also completes the PoC/production environment split: production now reads a single .env, and .env.pjsip-poc is PoC-only.

Changes

  • agent/answer_policy.py (new)DelayedAnswerService (delayed answer, max call duration, lifecycle logging) plus caller_id_from_uri(), moved from agent/pjsip_poc.py.
  • Caller-ID logging — the answer-policy lifecycle now logs the normalized caller ID on ring/answer/hangup, which makes TRUSTED_CALLERS mismatches diagnosable from the logs.
  • agent/pjsip.py — imports the shared service and drops its duplicate caller_id_from_uri.
  • compose.yml — production stack no longer loads .env.pjsip-poc; PJSIP/FRITZ!Box settings live in .env.
  • Teststests/test_answer_policy.py (new) takes over the answer-policy cases from tests/test_pjsip_poc.py, plus 4 new caller-ID logging/extraction tests.
  • Docs.env.example, .env.pjsip-poc.example, README.md, docs/pjsip-migration.md, CLAUDE.md updated for the single-.env production model; TODO.md records two live-call findings (TTS 503 dropping a turn, caller-ID format vs TRUSTED_CALLERS).

Deployment note (breaking for existing deploys)

compose.yml no longer reads .env.pjsip-poc. FRITZ!Box SIP credentials and PJSIP tuning keys must be present in .env before deploying this branch, or startup fails with fritzbox_sip_username must not be empty.

Testing

  • venv/bin/pytest -q → 314 passed
  • Deployed to the production container: SIP registration OK, no startup errors, two live inbound calls confirmed caller-ID extraction end to end.

volschin added 4 commits July 31, 2026 18:13
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.
@volschin
volschin merged commit 4095549 into main Aug 1, 2026
5 checks passed
@volschin
volschin deleted the refactor/answer-policy-and-env-cleanup branch August 1, 2026 03:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant