dv-pipecat-flows 0.0.26.dev142__tar.gz → 0.0.26.dev156__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.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.gitignore +5 -0
  2. {dv_pipecat_flows-0.0.26.dev142/src/dv_pipecat_flows.egg-info → dv_pipecat_flows-0.0.26.dev156}/PKG-INFO +1 -1
  3. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156/src/dv_pipecat_flows.egg-info}/PKG-INFO +1 -1
  4. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/dv_pipecat_flows.egg-info/scm_file_list.json +4 -0
  5. dv_pipecat_flows-0.0.26.dev156/src/dv_pipecat_flows.egg-info/scm_version.json +8 -0
  6. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/manager.py +128 -0
  7. dv_pipecat_flows-0.0.26.dev142/src/dv_pipecat_flows.egg-info/scm_version.json +0 -8
  8. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.agents/skills/pipecat-logs/SKILL.md +0 -0
  9. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.agents/skills/pipecat-logs/query-reference.md +0 -0
  10. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.claude/skills/pipecat-logs/SKILL.md +0 -0
  11. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.claude/skills/pipecat-logs/query-reference.md +0 -0
  12. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.gitattributes +0 -0
  13. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.pre-commit-config.yaml +0 -0
  14. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.python-version +0 -0
  15. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/.readthedocs.yaml +0 -0
  16. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/AGENTS.md +0 -0
  17. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/CHANGELOG.md +0 -0
  18. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/CLAUDE.md +0 -0
  19. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/CONTRIBUTING.md +0 -0
  20. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/LICENSE +0 -0
  21. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/MANIFEST.in +0 -0
  22. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/README.md +0 -0
  23. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/.env.example +0 -0
  24. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/.gitignore +0 -0
  25. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/README.md +0 -0
  26. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/ARCHITECTURE.md +0 -0
  27. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/CODE_MAP.md +0 -0
  28. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/CONFIG.md +0 -0
  29. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/CONTRACTS.md +0 -0
  30. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/COPILOT_TODO.md +0 -0
  31. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/DISTRIBUTION.md +0 -0
  32. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/HLD.md +0 -0
  33. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/METHODS.md +0 -0
  34. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/docs/VOICE_E2E.md +0 -0
  35. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/ANALYSIS_stagehand_vs_hybrid.md +0 -0
  36. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/RESULTS.md +0 -0
  37. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/_env.py +0 -0
  38. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/bench.py +0 -0
  39. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/bench_browseruse.py +0 -0
  40. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/bench_stagehand.py +0 -0
  41. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/inspect_browseruse.py +0 -0
  42. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/inspect_browseruse_showcase.py +0 -0
  43. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/inspect_stagehand_prompt.py +0 -0
  44. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/pages/checkout.html +0 -0
  45. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/pages/dashboard.html +0 -0
  46. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/pages/signup.html +0 -0
  47. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/results_browseruse.json +0 -0
  48. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/results_perception.json +0 -0
  49. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/results_stagehand.json +0 -0
  50. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/tasks.py +0 -0
  51. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/try_hybrid.py +0 -0
  52. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/try_hybrid_multistep.py +0 -0
  53. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/eval/try_hybrid_oneshot.py +0 -0
  54. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/pyproject.toml +0 -0
  55. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/scripts/dev-stack.sh +0 -0
  56. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/browser_copilot/uv.lock +0 -0
  57. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/dev-requirements.txt +0 -0
  58. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/docker-compose.dev.yml +0 -0
  59. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/.eslintrc.json +0 -0
  60. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/.prettierrc +0 -0
  61. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/css/tailwind.css +0 -0
  62. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/examples/food_ordering.json +0 -0
  63. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/examples/movie_explorer.json +0 -0
  64. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/examples/patient_intake.json +0 -0
  65. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/examples/restaurant_reservation.json +0 -0
  66. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/examples/travel_planner.json +0 -0
  67. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/favicon.png +0 -0
  68. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/favicon.svg +0 -0
  69. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/index.html +0 -0
  70. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/editor/canvas.js +0 -0
  71. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/editor/editorState.js +0 -0
  72. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/editor/sidePanel.js +0 -0
  73. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/editor/toolbar.js +0 -0
  74. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/main.js +0 -0
  75. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/nodes/baseNode.js +0 -0
  76. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/nodes/endNode.js +0 -0
  77. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/nodes/flowNode.js +0 -0
  78. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/nodes/functionNode.js +0 -0
  79. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/nodes/index.js +0 -0
  80. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/nodes/mergeNode.js +0 -0
  81. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/nodes/startNode.js +0 -0
  82. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/types.js +0 -0
  83. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/utils/export.js +0 -0
  84. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/utils/helpers.js +0 -0
  85. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/utils/import.js +0 -0
  86. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/js/utils/validation.js +0 -0
  87. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/jsdoc.json +0 -0
  88. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/package-lock.json +0 -0
  89. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/package.json +0 -0
  90. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/postcss.config.cjs +0 -0
  91. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/public/favicon.png +0 -0
  92. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/public/favicon.svg +0 -0
  93. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/tailwind.config.cjs +0 -0
  94. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/vercel.json +0 -0
  95. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/editor/vite.config.js +0 -0
  96. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/engine_primitives_plan.md +0 -0
  97. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/env.example +0 -0
  98. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/images/food-ordering-flow.png +0 -0
  99. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/pipecat-flows.png +0 -0
  100. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/pipecat_upgrade.md +0 -0
  101. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/pyproject.toml +0 -0
  102. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/remote-asterisk-code/README.md +0 -0
  103. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/remote-asterisk-code/extensions.conf +0 -0
  104. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/remote-asterisk-code/rtp.conf +0 -0
  105. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/requirements.txt +0 -0
  106. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/scripts/build_uae_pipecat_secret_blob.py +0 -0
  107. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/scripts/check-ar-package.py +0 -0
  108. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/scripts/fix-ruff.sh +0 -0
  109. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/scripts/pre-commit.sh +0 -0
  110. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/scripts/test_build_uae_pipecat_secret_blob.py +0 -0
  111. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/setup.cfg +0 -0
  112. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/dv_pipecat_flows.egg-info/SOURCES.txt +0 -0
  113. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/dv_pipecat_flows.egg-info/dependency_links.txt +0 -0
  114. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/dv_pipecat_flows.egg-info/requires.txt +0 -0
  115. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/dv_pipecat_flows.egg-info/top_level.txt +0 -0
  116. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/__init__.py +0 -0
  117. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/actions.py +0 -0
  118. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/adapters.py +0 -0
  119. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/condition_evaluator.py +0 -0
  120. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/exceptions.py +0 -0
  121. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/flow_validator.py +0 -0
  122. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/processors/__init__.py +0 -0
  123. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/processors/router_mode_guard.py +0 -0
  124. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/processors/speak_interruption_guard.py +0 -0
  125. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/processors/user_turn_observer.py +0 -0
  126. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/router_mode.py +0 -0
  127. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/src/pipecat_flows/types.py +0 -0
  128. {dv_pipecat_flows-0.0.26.dev142 → dv_pipecat_flows-0.0.26.dev156}/uv.lock +0 -0
@@ -187,3 +187,8 @@ MANIFEST
187
187
 
188
188
  # Asterisk configs
189
189
  remote-asterisk-code/pjsip.conf
190
+
191
+
192
+ # local files
193
+ scratch/
194
+ tmp/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dv-pipecat-flows
3
- Version: 0.0.26.dev142
3
+ Version: 0.0.26.dev156
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.26.dev142
3
+ Version: 0.0.26.dev156
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
@@ -385,6 +385,7 @@
385
385
  "ringg-bot/tests/conftest.py",
386
386
  "ringg-bot/tests/test_abandoned_call_report.py",
387
387
  "ringg-bot/tests/test_action_call_variables.py",
388
+ "ringg-bot/tests/test_anthropic_llm.py",
388
389
  "ringg-bot/tests/test_app_metrics.py",
389
390
  "ringg-bot/tests/test_arabic_amount.py",
390
391
  "ringg-bot/tests/test_artifact_store.py",
@@ -506,6 +507,7 @@
506
507
  "ringg-bot/tests/test_regional_vendor_endpoints.py",
507
508
  "ringg-bot/tests/test_regression_monitor_sync.py",
508
509
  "ringg-bot/tests/test_replay_kb_flag.py",
510
+ "ringg-bot/tests/test_replay_mock_websocket.py",
509
511
  "ringg-bot/tests/test_ringg_stt_mapping.py",
510
512
  "ringg-bot/tests/test_router_model_client.py",
511
513
  "ringg-bot/tests/test_router_shadow.py",
@@ -529,6 +531,7 @@
529
531
  "ringg-bot/tests/test_simulate_agent_config.py",
530
532
  "ringg-bot/tests/test_simulate_batch.py",
531
533
  "ringg-bot/tests/test_simulate_progress.py",
534
+ "ringg-bot/tests/test_simulation_action_widget_guard.py",
532
535
  "ringg-bot/tests/test_single_node_kb_id_resolution.py",
533
536
  "ringg-bot/tests/test_stall_watchdog.py",
534
537
  "ringg-bot/tests/test_status_router.py",
@@ -571,6 +574,7 @@
571
574
  "ringg-bot/transports/factory.py",
572
575
  "ringg-bot/utils/action_call_handler.py",
573
576
  "ringg-bot/utils/analyzer_pool.py",
577
+ "ringg-bot/utils/anthropic_llm.py",
574
578
  "ringg-bot/utils/api_calls.py",
575
579
  "ringg-bot/utils/app_metrics.py",
576
580
  "ringg-bot/utils/arabic_amount.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.0.25",
3
+ "distance": 156,
4
+ "node": "g7ace808e",
5
+ "dirty": false,
6
+ "branch": "dv-stage",
7
+ "node_date": "2026-10-09"
8
+ }
@@ -53,6 +53,9 @@ from utils.collect_prompts import collect_prompt
53
53
  from pipecat.frames.frames import (
54
54
  DTMFUpdateSettingsFrame,
55
55
  FunctionCallResultProperties,
56
+ InterruptionFrame,
57
+ LLMFullResponseEndFrame,
58
+ LLMFullResponseStartFrame,
56
59
  LLMMessagesAppendFrame,
57
60
  LLMMessagesUpdateFrame,
58
61
  LLMRunFrame,
@@ -177,6 +180,50 @@ def _paired_transition_call_ids(
177
180
  return call_ids & result_ids
178
181
 
179
182
 
183
+ # Appended after the carried conversation when the node prompt is folded into the
184
+ # system prompt, so the context still ends on a customer-side turn. Without it Claude
185
+ # sees the bot's own last message as the end of the conversation and replies with
186
+ # nothing after a node change (measured on the PB chatbot replay, 9 Oct 2026).
187
+ CONTINUE_NUDGE = (
188
+ "Continue the conversation: act on the customer's latest message using the "
189
+ "instructions in the system prompt above. Do not reply to this note or mention it."
190
+ )
191
+
192
+
193
+ def _system_text(content: Any) -> str:
194
+ if isinstance(content, str):
195
+ return content
196
+ if isinstance(content, list):
197
+ return "\n".join(
198
+ part.get("text", "") for part in content if isinstance(part, dict) and part.get("text")
199
+ )
200
+ return ""
201
+
202
+
203
+ def fold_system_messages(messages: List[dict]) -> List[dict]:
204
+ """Put every system message into one leading system message for Claude.
205
+
206
+ The conversation keeps its order after it, and a system-role nudge closes the
207
+ context: the adapters turn that into a user message (merged into the customer's
208
+ turn when it is last), so the request ends on the customer's side. The nudge is
209
+ a system message so that the next RESET_WITH_CONVERSATION drops it instead of
210
+ carrying it as something the customer said. A context with no conversation is
211
+ left as one system message, as before.
212
+ """
213
+ system_parts = [
214
+ text
215
+ for m in messages
216
+ if m.get("role") == "system" and (text := _system_text(m.get("content")))
217
+ ]
218
+ if not system_parts:
219
+ return messages
220
+ rest = [m for m in messages if m.get("role") != "system"]
221
+ folded = [{"role": "system", "content": "\n\n".join(system_parts)}, *rest]
222
+ if rest:
223
+ folded.append({"role": "system", "content": CONTINUE_NUDGE})
224
+ return folded
225
+
226
+
180
227
  def extract_conversation_messages(
181
228
  messages: List[dict], transition_names: Optional[Set[str]] = None
182
229
  ) -> List[dict]:
@@ -265,6 +312,11 @@ def extract_conversation_messages(
265
312
  # drained the terminal frame. Coarse on purpose — nothing waits on it.
266
313
  _CALL_END_POLL_INTERVAL_S = 5.0
267
314
 
315
+ # How long a RESET_WITH_CONVERSATION carry waits for the reply in flight to be
316
+ # saved (see FlowManager._wait_for_reply_saved). In a chat session the end frame
317
+ # lands within milliseconds of the transition.
318
+ _REPLY_SAVE_TIMEOUT_S = 1.5
319
+
268
320
  # How long to let an utterance that straddled the wait ceiling finish
269
321
  # committing. The caller started speaking just before the ceiling fired, so STT
270
322
  # has a final in flight; deciding anything before it lands means classifying
@@ -453,6 +505,29 @@ class FlowManager:
453
505
  # Signature: is_injected(message) -> bool
454
506
  self.injected_message_filter: Optional[Callable[[Any], bool]] = None
455
507
 
508
+ # Chat sessions: before RESET_WITH_CONVERSATION carries the conversation into
509
+ # the next node, wait for the assistant aggregator to save the reply in flight,
510
+ # so text the LLM wrote alongside the transition is carried too (see
511
+ # `_wait_for_reply_saved`). Off by default: in a voice call the reply is saved
512
+ # only once it has been spoken, so waiting would hold the hop for the speech.
513
+ self.carry_waits_for_reply: bool = False
514
+ # Claude (Anthropic API / Bedrock): both take one system prompt at the top, and
515
+ # their adapters turn every later system message into a user message merged
516
+ # with the customer's last turn. A node's task prompt placed after the carried
517
+ # conversation then reads to Claude as something the customer said, and Claude
518
+ # answers the instructions instead of the customer. When set, a context replace
519
+ # folds every system message into one leading system message and ends on a
520
+ # short nudge (see `fold_system_messages`). Off by default: GPT reads a trailing
521
+ # system message as an instruction, which is why the task prompt sits there.
522
+ self.fold_task_into_system: bool = False
523
+ # Replies the assistant aggregator has started but not yet saved, counted from
524
+ # its own frame events (`_track_assistant_reply`); `_reply_saved` is set while
525
+ # there are none.
526
+ self._replies_in_flight = 0
527
+ self._reply_saved = asyncio.Event()
528
+ self._reply_saved.set()
529
+ self._watch_assistant_replies()
530
+
456
531
  # Wired in `initialize()` once the pipeline is up. Tracks the timestamp
457
532
  # of the latest UserStoppedSpeakingFrame so router-mode nodes can gate
458
533
  # their LLM classification on "user has actually replied" instead of
@@ -5078,6 +5153,54 @@ In all of these cases, you can provide a `name` in your new node's config for de
5078
5153
  resolved.append(message)
5079
5154
  return resolved
5080
5155
 
5156
+ def _watch_assistant_replies(self) -> None:
5157
+ """Follow the assistant aggregator's replies through its on_after_process_frame event."""
5158
+ assistant = self._context_aggregator.assistant() if self._context_aggregator else None
5159
+ add_event_handler = getattr(assistant, "add_event_handler", None)
5160
+ if add_event_handler is None:
5161
+ return
5162
+ try:
5163
+ add_event_handler("on_after_process_frame", self._track_assistant_reply)
5164
+ except Exception as exc: # never let reply tracking break flow setup
5165
+ logger.debug(f"Not tracking assistant replies: {exc}")
5166
+
5167
+ async def _track_assistant_reply(self, _aggregator: Any, frame: Any) -> None:
5168
+ """Count replies in flight. The aggregator saves a reply's text to the context
5169
+ while processing its LLMFullResponseEndFrame, so after that frame it is saved."""
5170
+ if isinstance(frame, LLMFullResponseStartFrame):
5171
+ self._replies_in_flight += 1
5172
+ self._reply_saved.clear()
5173
+ elif isinstance(frame, (LLMFullResponseEndFrame, InterruptionFrame)):
5174
+ if isinstance(frame, InterruptionFrame):
5175
+ self._replies_in_flight = 0
5176
+ else:
5177
+ self._replies_in_flight = max(0, self._replies_in_flight - 1)
5178
+ if not self._replies_in_flight:
5179
+ self._reply_saved.set()
5180
+
5181
+ async def _wait_for_reply_saved(self) -> None:
5182
+ """Wait, briefly, until the assistant aggregator has saved the reply in flight.
5183
+
5184
+ A transition fires on its function-call result, a SystemFrame that overtakes
5185
+ the reply's queued text and its LLMFullResponseEndFrame. The aggregator adds
5186
+ that text to the context only at the end frame, so a carry taken before it
5187
+ leaves out what the LLM wrote alongside the transition ("Plans that work well
5188
+ for NRIs -") and the LLMMessagesUpdateFrame then erases it from the live
5189
+ context: the customer saw it, the LLM never does, and the next node re-routes
5190
+ on a turn that was already answered (Policybazaar chat, Oct 2026).
5191
+
5192
+ Capped, so a lost end frame only delays the hop.
5193
+ """
5194
+ if self._reply_saved.is_set():
5195
+ return
5196
+ try:
5197
+ await asyncio.wait_for(self._reply_saved.wait(), timeout=_REPLY_SAVE_TIMEOUT_S)
5198
+ except asyncio.TimeoutError:
5199
+ logger.warning(
5200
+ f"Reply still in flight after {_REPLY_SAVE_TIMEOUT_S}s; carrying the "
5201
+ "conversation without it"
5202
+ )
5203
+
5081
5204
  async def _update_llm_context(
5082
5205
  self,
5083
5206
  role_messages: Optional[List[dict]],
@@ -5156,6 +5279,8 @@ In all of these cases, you can provide a `name` in your new node's config for de
5156
5279
  # prompt and tool-call/tool-result logs are dropped. Placed after
5157
5280
  # role_messages (the new system instruction) and before task
5158
5281
  # messages, mirroring the RESET_WITH_SUMMARY layout.
5282
+ if self.carry_waits_for_reply:
5283
+ await self._wait_for_reply_saved()
5159
5284
  carried = extract_conversation_messages(
5160
5285
  self._without_injected(
5161
5286
  self._context_aggregator.user()._context.get_messages()
@@ -5191,6 +5316,9 @@ In all of these cases, you can provide a `name` in your new node's config for de
5191
5316
  except Exception as exc: # never let a provider break a transition
5192
5317
  logger.warning(f"sticky_context_provider failed: {exc}")
5193
5318
 
5319
+ if is_replace and self.fold_task_into_system:
5320
+ messages = fold_system_messages(messages)
5321
+
5194
5322
  frame_type = LLMMessagesUpdateFrame if is_replace else LLMMessagesAppendFrame
5195
5323
 
5196
5324
  await self._task.queue_frames(
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.0.25",
3
- "distance": 142,
4
- "node": "g5494275d",
5
- "dirty": false,
6
- "branch": "dv-stage",
7
- "node_date": "2026-10-07"
8
- }