dv-pipecat-flows 0.0.0.dev2766__tar.gz → 0.0.0.dev2786__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 (128) hide show
  1. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/AGENTS.md +17 -0
  2. {dv_pipecat_flows-0.0.0.dev2766/src/dv_pipecat_flows.egg-info → dv_pipecat_flows-0.0.0.dev2786}/PKG-INFO +1 -1
  3. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786/src/dv_pipecat_flows.egg-info}/PKG-INFO +1 -1
  4. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/dv_pipecat_flows.egg-info/scm_file_list.json +8 -2
  5. dv_pipecat_flows-0.0.0.dev2786/src/dv_pipecat_flows.egg-info/scm_version.json +8 -0
  6. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/processors/router_mode_guard.py +0 -11
  7. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/processors/user_turn_observer.py +11 -32
  8. dv_pipecat_flows-0.0.0.dev2766/src/dv_pipecat_flows.egg-info/scm_version.json +0 -8
  9. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.agents/skills/loki-logs/SKILL.md +0 -0
  10. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.agents/skills/loki-logs/query-reference.md +0 -0
  11. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.claude/skills/loki-logs/SKILL.md +0 -0
  12. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.claude/skills/loki-logs/query-reference.md +0 -0
  13. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.gitattributes +0 -0
  14. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.gitignore +0 -0
  15. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.pre-commit-config.yaml +0 -0
  16. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.python-version +0 -0
  17. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/.readthedocs.yaml +0 -0
  18. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/CHANGELOG.md +0 -0
  19. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/CLAUDE.md +0 -0
  20. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/CONTRIBUTING.md +0 -0
  21. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/LICENSE +0 -0
  22. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/MANIFEST.in +0 -0
  23. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/README.md +0 -0
  24. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/.env.example +0 -0
  25. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/.gitignore +0 -0
  26. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/README.md +0 -0
  27. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/ARCHITECTURE.md +0 -0
  28. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/CODE_MAP.md +0 -0
  29. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/CONFIG.md +0 -0
  30. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/CONTRACTS.md +0 -0
  31. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/COPILOT_TODO.md +0 -0
  32. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/DISTRIBUTION.md +0 -0
  33. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/HLD.md +0 -0
  34. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/METHODS.md +0 -0
  35. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/docs/VOICE_E2E.md +0 -0
  36. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/ANALYSIS_stagehand_vs_hybrid.md +0 -0
  37. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/RESULTS.md +0 -0
  38. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/_env.py +0 -0
  39. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/bench.py +0 -0
  40. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/bench_browseruse.py +0 -0
  41. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/bench_stagehand.py +0 -0
  42. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/inspect_browseruse.py +0 -0
  43. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/inspect_browseruse_showcase.py +0 -0
  44. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/inspect_stagehand_prompt.py +0 -0
  45. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/pages/checkout.html +0 -0
  46. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/pages/dashboard.html +0 -0
  47. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/pages/signup.html +0 -0
  48. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/results_browseruse.json +0 -0
  49. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/results_perception.json +0 -0
  50. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/results_stagehand.json +0 -0
  51. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/tasks.py +0 -0
  52. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/try_hybrid.py +0 -0
  53. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/try_hybrid_multistep.py +0 -0
  54. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/eval/try_hybrid_oneshot.py +0 -0
  55. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/pyproject.toml +0 -0
  56. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/scripts/dev-stack.sh +0 -0
  57. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/browser_copilot/uv.lock +0 -0
  58. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/dev-requirements.txt +0 -0
  59. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/docker-compose.dev.yml +0 -0
  60. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/.eslintrc.json +0 -0
  61. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/.prettierrc +0 -0
  62. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/css/tailwind.css +0 -0
  63. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/examples/food_ordering.json +0 -0
  64. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/examples/movie_explorer.json +0 -0
  65. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/examples/patient_intake.json +0 -0
  66. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/examples/restaurant_reservation.json +0 -0
  67. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/examples/travel_planner.json +0 -0
  68. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/favicon.png +0 -0
  69. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/favicon.svg +0 -0
  70. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/index.html +0 -0
  71. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/editor/canvas.js +0 -0
  72. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/editor/editorState.js +0 -0
  73. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/editor/sidePanel.js +0 -0
  74. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/editor/toolbar.js +0 -0
  75. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/main.js +0 -0
  76. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/nodes/baseNode.js +0 -0
  77. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/nodes/endNode.js +0 -0
  78. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/nodes/flowNode.js +0 -0
  79. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/nodes/functionNode.js +0 -0
  80. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/nodes/index.js +0 -0
  81. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/nodes/mergeNode.js +0 -0
  82. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/nodes/startNode.js +0 -0
  83. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/types.js +0 -0
  84. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/utils/export.js +0 -0
  85. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/utils/helpers.js +0 -0
  86. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/utils/import.js +0 -0
  87. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/js/utils/validation.js +0 -0
  88. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/jsdoc.json +0 -0
  89. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/package-lock.json +0 -0
  90. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/package.json +0 -0
  91. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/postcss.config.cjs +0 -0
  92. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/public/favicon.png +0 -0
  93. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/public/favicon.svg +0 -0
  94. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/tailwind.config.cjs +0 -0
  95. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/vercel.json +0 -0
  96. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/editor/vite.config.js +0 -0
  97. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/engine_primitives_plan.md +0 -0
  98. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/env.example +0 -0
  99. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/images/food-ordering-flow.png +0 -0
  100. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/pipecat-flows.png +0 -0
  101. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/pipecat_upgrade.md +0 -0
  102. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/pyproject.toml +0 -0
  103. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/remote-asterisk-code/README.md +0 -0
  104. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/remote-asterisk-code/extensions.conf +0 -0
  105. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/remote-asterisk-code/rtp.conf +0 -0
  106. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/requirements.txt +0 -0
  107. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/scripts/azure_latency_probe.py +0 -0
  108. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/scripts/check-pypi-package.py +0 -0
  109. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/scripts/fix-ruff.sh +0 -0
  110. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/scripts/langfuse_export.py +0 -0
  111. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/scripts/pre-commit.sh +0 -0
  112. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/setup.cfg +0 -0
  113. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/dv_pipecat_flows.egg-info/SOURCES.txt +0 -0
  114. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/dv_pipecat_flows.egg-info/dependency_links.txt +0 -0
  115. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/dv_pipecat_flows.egg-info/requires.txt +0 -0
  116. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/dv_pipecat_flows.egg-info/top_level.txt +0 -0
  117. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/__init__.py +0 -0
  118. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/actions.py +0 -0
  119. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/adapters.py +0 -0
  120. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/condition_evaluator.py +0 -0
  121. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/exceptions.py +0 -0
  122. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/flow_validator.py +0 -0
  123. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/manager.py +0 -0
  124. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/processors/__init__.py +0 -0
  125. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/processors/speak_interruption_guard.py +0 -0
  126. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/router_mode.py +0 -0
  127. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/src/pipecat_flows/types.py +0 -0
  128. {dv_pipecat_flows-0.0.0.dev2766 → dv_pipecat_flows-0.0.0.dev2786}/uv.lock +0 -0
@@ -125,6 +125,23 @@ Example `call_config`:
125
125
  - `tests/test_manager.py`, `tests/test_actions.py`, etc., cover FlowManager and ActionManager edge cases (context resets, action ordering, function registration). Extend these tests when adding new features.
126
126
  - Logging: see the dedicated section below for Loguru usage and the custom logger configuration.
127
127
 
128
+ ## Metrics & Observability
129
+
130
+ - **SDK**: `ringg-telemetry` (metrics-only facade over OpenTelemetry). Initialised once per process in the FastAPI lifespan — `ringg-bot/server.py` — gated on `ENABLE_METRICS`. Export path is OTLP/gRPC → the in-cluster `otel-gateway` → **Google Managed Prometheus**. Tracing is a *separate* stack (Langfuse, `ringg-bot/utils/tracing.py`); the two are unrelated.
131
+ - **Declare instruments in one place**: the registry block near the bottom of `ringg-bot/utils/metrics_collector.py`. Module-level handles (`ringg_metrics.histogram/counter/gauge`), created at import, resolved lazily. Keep the cardinality justification in a comment next to each — the `labels=(...)` tuple is a hard allowlist, and the SDK **silently drops** any label not in it.
132
+ - **Record from a lazily-importing, swallow-all helper** when the call site is on a per-turn hot path — see `_record_redis_latency` in `ringg-bot/ringg_processors/llm_cache.py` and `_record_router_metrics` in `ringg-bot/utils/language_detection.py`. Telemetry must never raise into a live call, and the import must not drag the metrics stack into eval scripts.
133
+ - **Names**: the SDK prefixes everything with `ringg.` and GMP rewrites it for PromQL — `ringg.language.router.duration` + `unit="s"` becomes `ringg_language_router_duration_seconds_{bucket,count,sum}`; a counter with `unit="1"` becomes `..._total`. A non-standard unit gets appended as a suffix, so use `"1"` even for money (put "USD" in the description).
134
+ - **Dashboards as code**: `k8s/monitoring/dashboards/` — one JSON per dashboard plus a README with the `gcloud monitoring dashboards create/update` commands and the assigned resource ids.
135
+ - **Tests**: `ringg_telemetry.testing.install_test_provider()` / `uninstall_test_provider()` route handles to an in-memory reader. See `ringg-bot/tests/test_metrics_bridge.py` and `tests/test_language_router_metrics.py`.
136
+
137
+ ## Data Residency
138
+
139
+ - Some regions forbid their call data leaving regional storage. The single definition of that rule is `is_data_residency_restricted(region)` in `ringg-bot/utils/artifact_store.py` (currently `{"ksa"}`). **Consult it before any new write to the long-lived artifact bucket** — `RUNTIME_CONFIG_BUCKET` is US-located.
140
+ - Restricted regions **skip** the write entirely: there is no fallback bucket and no re-routing. Applies to the runtime-config snapshot, the tool-call snapshot and the language-router samples (`utils/generic_functions/cleanup.py`, `utils/language_sample_store.py`). The data still reaches the region's own backend via the `call_completion` POST, and transcripts/recordings still go to the regional bucket via `utils/transcript._get_gcs_bucket`.
141
+ - `region` is a **per-call** value (`RunConfig.region`, set by the backend), not a pod env var. On the two artifact uploaders it is keyword-only and *required* precisely so a new call site cannot become a violation by omission.
142
+ - Skipping the upload must not skip local cleanup: `LOCAL_STORAGE_DIR` is the pod's `/tmp` and the pod outlives thousands of calls, so anything buffered on disk still has to be unlinked on the skip path.
143
+ - Where feasible, gate at *capture* time too, not just upload time — e.g. `bot.py` passes `collect_samples=not is_data_residency_restricted(_region)` so raw user utterances are never written to `/tmp` for a restricted call.
144
+
128
145
  ## Logging
129
146
 
130
147
  - We use Loguru for all application logging. Do not use the Python `logging` module in app code; always `from loguru import logger`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dv-pipecat-flows
3
- Version: 0.0.0.dev2766
3
+ Version: 0.0.0.dev2786
4
4
  Summary: Conversation Flow management for Pipecat AI applications
5
5
  License: BSD 2-Clause License
6
6
  Project-URL: Source, https://github.com/pipecat-ai/pipecat-flows
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dv-pipecat-flows
3
- Version: 0.0.0.dev2766
3
+ Version: 0.0.0.dev2786
4
4
  Summary: Conversation Flow management for Pipecat AI applications
5
5
  License: BSD 2-Clause License
6
6
  Project-URL: Source, https://github.com/pipecat-ai/pipecat-flows
@@ -211,6 +211,8 @@
211
211
  "k8s/livekit/values-prod.yaml",
212
212
  "k8s/livekit/values-stage.yaml",
213
213
  "k8s/livekit/values.yaml",
214
+ "k8s/monitoring/dashboards/README.md",
215
+ "k8s/monitoring/dashboards/ringg-language-router.json",
214
216
  "pipecat-flows.png",
215
217
  "pipecat_upgrade.md",
216
218
  "pyproject.toml",
@@ -359,6 +361,7 @@
359
361
  "ringg-bot/tests/conftest.py",
360
362
  "ringg-bot/tests/test_action_call_variables.py",
361
363
  "ringg-bot/tests/test_app_metrics.py",
364
+ "ringg-bot/tests/test_artifact_store.py",
362
365
  "ringg-bot/tests/test_asterisk_ami_client.py",
363
366
  "ringg-bot/tests/test_asterisk_ari_transfer_message.py",
364
367
  "ringg-bot/tests/test_asterisk_v1_transfer_numbers.py",
@@ -391,6 +394,7 @@
391
394
  "ringg-bot/tests/test_job_reporting.py",
392
395
  "ringg-bot/tests/test_kb_planner.py",
393
396
  "ringg-bot/tests/test_language_map.py",
397
+ "ringg-bot/tests/test_language_router_metrics.py",
394
398
  "ringg-bot/tests/test_language_sample_store.py",
395
399
  "ringg-bot/tests/test_language_steering.py",
396
400
  "ringg-bot/tests/test_llm_cache.py",
@@ -410,12 +414,15 @@
410
414
  "ringg-bot/tests/test_on_call_sdk_context.py",
411
415
  "ringg-bot/tests/test_ozonetel_inbound.py",
412
416
  "ringg-bot/tests/test_ozonetel_transfer.py",
417
+ "ringg-bot/tests/test_rag_filter_operators.py",
418
+ "ringg-bot/tests/test_rag_filter_property_names.py",
413
419
  "ringg-bot/tests/test_realtime_external_tts.py",
414
420
  "ringg-bot/tests/test_recording_flush.py",
415
421
  "ringg-bot/tests/test_recording_stereo.py",
416
422
  "ringg-bot/tests/test_regression_monitor_sync.py",
417
423
  "ringg-bot/tests/test_replay_kb_flag.py",
418
424
  "ringg-bot/tests/test_runtime_config_language_normalization.py",
425
+ "ringg-bot/tests/test_runtime_config_upload.py",
419
426
  "ringg-bot/tests/test_sdk_executor_flatten.py",
420
427
  "ringg-bot/tests/test_sdk_tool_schema.py",
421
428
  "ringg-bot/tests/test_send_blocks_calendar.py",
@@ -625,6 +632,7 @@
625
632
  "tests/test_condition_evaluator.py",
626
633
  "tests/test_context_strategies.py",
627
634
  "tests/test_credentials_resolver.py",
635
+ "tests/test_deployment_scaling_chart.py",
628
636
  "tests/test_end_call_inactivity.py",
629
637
  "tests/test_end_call_interruptible.py",
630
638
  "tests/test_end_call_provider_dispatch.py",
@@ -639,10 +647,8 @@
639
647
  "tests/test_openai_realtime_transcript_filter.py",
640
648
  "tests/test_response_formatters.py",
641
649
  "tests/test_router_extract_schema.py",
642
- "tests/test_router_mode_guard.py",
643
650
  "tests/test_router_no_unclear.py",
644
651
  "tests/test_tts_split_cache.py",
645
- "tests/test_user_turn_observer.py",
646
652
  "tests/test_voicemail_detector.py",
647
653
  "uv.lock"
648
654
  ]
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.0.0.dev0",
3
+ "distance": 2786,
4
+ "node": "g4f35e028c126047efd7781c8674fba2a380c5e99",
5
+ "dirty": false,
6
+ "branch": "dv-stage",
7
+ "node_date": "2026-08-10"
8
+ }
@@ -48,7 +48,6 @@ from typing import Callable, Tuple, Type
48
48
 
49
49
  from pipecat.frames.frames import (
50
50
  Frame,
51
- LLMContextFrame,
52
51
  LLMMessagesFrame,
53
52
  LLMRunFrame,
54
53
  LLMTextFrame,
@@ -64,16 +63,6 @@ logger = logging.getLogger(__name__)
64
63
  # race-free fix for the spurious-bot-text problem.
65
64
  _TRIGGER_FRAME_TYPES: Tuple[Type[Frame], ...] = (
66
65
  OpenAILLMContextFrame,
67
- # Universal context frame. `LLMContextFrame` and `OpenAILLMContextFrame` are
68
- # SIBLINGS (both extend Frame directly), so matching one does NOT match the
69
- # other — both must be listed. The pipeline picks between them from a single
70
- # switch in the bot: `context = LLMContext() if (_use_responses or
71
- # _use_universal_for_google) else OpenAILLMContext()`. Omitting this one let
72
- # the LLM fire during router-mode nodes on every Responses-API agent
73
- # (gpt-5.4-mini/nano are forced onto Responses regardless of the
74
- # `use_responses_api` toggle) and every google/gemma agent — the bot spoke
75
- # spurious freeform text instead of routing silently.
76
- LLMContextFrame,
77
66
  LLMMessagesFrame,
78
67
  LLMRunFrame,
79
68
  )
@@ -1,5 +1,4 @@
1
- """Observer that timestamps the user-turn-committed context frame
2
- (:class:`OpenAILLMContextFrame` **or** :class:`LLMContextFrame`) so router-mode
1
+ """Observer that timestamps :class:`OpenAILLMContextFrame` so router-mode
3
2
  nodes can detect "user turn is now committed to context" without reaching
4
3
  into the user context aggregator's private state.
5
4
 
@@ -8,27 +7,22 @@ Why an observer, not a FrameProcessor:
8
7
  public ``PipelineTask.add_observer`` API.
9
8
  * We only need a passive timestamp + an ``asyncio.Event`` — no frame routing.
10
9
 
11
- Why a context frame and not ``UserStoppedSpeakingFrame``:
10
+ Why ``OpenAILLMContextFrame`` and not ``UserStoppedSpeakingFrame``:
12
11
  * ``UserStoppedSpeakingFrame`` is raw VAD — it fires the instant the mic
13
12
  detects silence, BEFORE the STT final lands and BEFORE the user-context
14
13
  aggregator appends the turn. Router gates that release on this signal
15
14
  classify against an empty/stale context.
16
- * The context frame is pushed by ``LLMUserContextAggregator``
15
+ * ``OpenAILLMContextFrame`` is pushed by ``LLMUserContextAggregator``
17
16
  (``llm_response._process_aggregation``) only AFTER the final transcript
18
17
  is appended to the LLM context. It is the canonical "user turn is now
19
18
  in the context the router will read" event.
20
19
  * Router-mode never queues ``LLMRunFrame`` for the router node itself, so
21
- the only context frame produced during a router invocation comes from
22
- the aggregator's turn-complete path — no competing source.
23
-
24
- BOTH context-frame flavours must be matched (see
25
- ``_USER_TURN_COMMITTED_FRAME_TYPES``): the aggregator emits
26
- ``LLMContextFrame`` on the Responses-API / google / gemma paths and
27
- ``OpenAILLMContextFrame`` on the Chat Completions path.
20
+ the only ``OpenAILLMContextFrame`` produced during a router invocation
21
+ comes from the aggregator's turn-complete path — no competing source.
28
22
 
29
23
  State exposed:
30
24
  * ``last_user_context_pushed_ts``: monotonic time of the latest
31
- user-turn context frame (user turn committed to context).
25
+ ``OpenAILLMContextFrame`` (user turn committed to context).
32
26
  * ``last_user_stopped_ts``: backwards-compatible alias kept in sync with
33
27
  ``last_user_context_pushed_ts`` so existing callers keep working.
34
28
  * ``event``: ``asyncio.Event`` set on every such frame; the consumer is
@@ -44,41 +38,26 @@ from typing import List, Tuple
44
38
 
45
39
  from pipecat.frames.frames import (
46
40
  InterimTranscriptionFrame,
47
- LLMContextFrame,
48
41
  TranscriptionFrame,
49
42
  UserStartedSpeakingFrame,
50
43
  )
51
44
  from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContextFrame
52
45
  from pipecat.observers.base_observer import BaseObserver, FramePushed
53
46
 
54
- # Both context-frame flavours mean the same thing here: "the user's turn is now
55
- # committed to the context the router will read". Which one the aggregator emits
56
- # is decided by one switch in the bot:
57
- #
58
- # context = LLMContext() if (_use_responses or _use_universal_for_google)
59
- # else OpenAILLMContext()
60
- #
61
- # They are SIBLINGS (both extend Frame directly, neither subclasses the other),
62
- # so matching one does NOT match the other. Keying on only OpenAILLMContextFrame
63
- # left every Responses-API / google / gemma agent's router waiting the full
64
- # ``router_wait_for_user_timeout`` (15s) after the caller had already replied.
65
- _USER_TURN_COMMITTED_FRAME_TYPES = (OpenAILLMContextFrame, LLMContextFrame)
66
-
67
47
  # Bound the per-call transcript log so a very long call can't grow it without
68
48
  # limit; far more than any single Collect dictation needs.
69
49
  _MAX_TRANSCRIPT_LOG = 256
70
50
 
71
51
 
72
52
  class UserTurnObserver(BaseObserver):
73
- """Tracks the timestamp of the most recent user-turn context frame.
53
+ """Tracks the timestamp of the most recent ``OpenAILLMContextFrame``.
74
54
 
75
55
  Router-mode uses this to (a) detect a user reply already committed to
76
56
  context BEFORE the router was entered (barge-in during the preceding
77
57
  Speak's TTS) and (b) await the NEXT user reply when none is available.
78
58
 
79
- The observer is keyed on the context frame (either flavour, see
80
- ``_USER_TURN_COMMITTED_FRAME_TYPES``) rather than the raw VAD
81
- ``UserStoppedSpeakingFrame`` because only the former guarantees the
59
+ The observer is keyed on ``OpenAILLMContextFrame`` rather than the raw
60
+ VAD ``UserStoppedSpeakingFrame`` because only the former guarantees the
82
61
  user's transcript has been appended to the LLM context the router reads.
83
62
  """
84
63
 
@@ -114,7 +93,7 @@ class UserTurnObserver(BaseObserver):
114
93
 
115
94
  @property
116
95
  def event(self) -> asyncio.Event:
117
- """The asyncio.Event set on each user-turn context frame.
96
+ """The asyncio.Event set on each ``OpenAILLMContextFrame``.
118
97
 
119
98
  Callers MUST ``.clear()`` before awaiting if they want to wait for the
120
99
  NEXT frame (vs. immediately returning on a stale set state).
@@ -122,7 +101,7 @@ class UserTurnObserver(BaseObserver):
122
101
  return self._event
123
102
 
124
103
  async def on_push_frame(self, data: FramePushed) -> None:
125
- if isinstance(data.frame, _USER_TURN_COMMITTED_FRAME_TYPES):
104
+ if isinstance(data.frame, OpenAILLMContextFrame):
126
105
  now = time.monotonic()
127
106
  self.last_user_context_pushed_ts = now
128
107
  self.last_user_stopped_ts = now
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.0.0.dev0",
3
- "distance": 2766,
4
- "node": "g2f9ead0f53048f6c04d0647e37cff236bd770e14",
5
- "dirty": false,
6
- "branch": "dv-stage",
7
- "node_date": "2026-08-08"
8
- }