dv-pipecat-flows 0.0.23.dev10__tar.gz → 0.0.23.dev11__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.
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.gitignore +0 -2
- {dv_pipecat_flows-0.0.23.dev10/src/dv_pipecat_flows.egg-info → dv_pipecat_flows-0.0.23.dev11}/PKG-INFO +1 -1
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11/src/dv_pipecat_flows.egg-info}/PKG-INFO +1 -1
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/dv_pipecat_flows.egg-info/SOURCES.txt +0 -36
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/dv_pipecat_flows.egg-info/scm_file_list.json +4 -67
- dv_pipecat_flows-0.0.23.dev11/src/dv_pipecat_flows.egg-info/scm_version.json +8 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/manager.py +84 -115
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/processors/router_mode_guard.py +11 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/processors/user_turn_observer.py +32 -11
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/.env.example +0 -13
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/.gitignore +0 -5
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/README.md +0 -71
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/ARCHITECTURE.md +0 -190
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/CODE_MAP.md +0 -254
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/CONFIG.md +0 -125
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/CONTRACTS.md +0 -189
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/COPILOT_TODO.md +0 -79
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/DISTRIBUTION.md +0 -73
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/HLD.md +0 -302
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/METHODS.md +0 -265
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/docs/VOICE_E2E.md +0 -52
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/ANALYSIS_stagehand_vs_hybrid.md +0 -113
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/RESULTS.md +0 -107
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/_env.py +0 -44
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/bench.py +0 -480
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/bench_browseruse.py +0 -312
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/bench_stagehand.py +0 -283
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/inspect_browseruse.py +0 -115
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/inspect_browseruse_showcase.py +0 -115
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/inspect_stagehand_prompt.py +0 -112
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/pages/checkout.html +0 -29
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/pages/dashboard.html +0 -25
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/pages/signup.html +0 -23
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/results_browseruse.json +0 -159
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/results_perception.json +0 -446
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/results_stagehand.json +0 -166
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/tasks.py +0 -104
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/try_hybrid.py +0 -105
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/try_hybrid_multistep.py +0 -149
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/eval/try_hybrid_oneshot.py +0 -133
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/pyproject.toml +0 -27
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/scripts/dev-stack.sh +0 -212
- dv_pipecat_flows-0.0.23.dev10/browser_copilot/uv.lock +0 -551
- dv_pipecat_flows-0.0.23.dev10/scripts/azure_latency_probe.py +0 -220
- dv_pipecat_flows-0.0.23.dev10/scripts/langfuse_export.py +0 -321
- dv_pipecat_flows-0.0.23.dev10/src/dv_pipecat_flows.egg-info/scm_version.json +0 -8
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.agents/skills/loki-logs/SKILL.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.agents/skills/loki-logs/query-reference.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.claude/skills/loki-logs/SKILL.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.claude/skills/loki-logs/query-reference.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.gitattributes +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.pre-commit-config.yaml +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.python-version +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/.readthedocs.yaml +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/AGENTS.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/CHANGELOG.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/CLAUDE.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/CONTRIBUTING.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/LICENSE +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/MANIFEST.in +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/README.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/dev-requirements.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/docker-compose.dev.yml +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/.eslintrc.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/.prettierrc +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/css/tailwind.css +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/examples/food_ordering.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/examples/movie_explorer.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/examples/patient_intake.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/examples/restaurant_reservation.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/examples/travel_planner.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/favicon.png +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/favicon.svg +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/index.html +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/editor/canvas.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/editor/editorState.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/editor/sidePanel.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/editor/toolbar.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/main.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/nodes/baseNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/nodes/endNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/nodes/flowNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/nodes/functionNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/nodes/index.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/nodes/mergeNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/nodes/startNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/types.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/utils/export.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/utils/helpers.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/utils/import.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/js/utils/validation.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/jsdoc.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/package-lock.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/package.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/postcss.config.cjs +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/public/favicon.png +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/public/favicon.svg +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/tailwind.config.cjs +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/vercel.json +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/editor/vite.config.js +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/engine_primitives_plan.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/env.example +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/images/food-ordering-flow.png +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/pipecat-flows.png +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/pipecat_upgrade.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/pyproject.toml +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/remote-asterisk-code/README.md +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/remote-asterisk-code/extensions.conf +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/remote-asterisk-code/rtp.conf +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/requirements.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/scripts/check-pypi-package.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/scripts/fix-ruff.sh +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/scripts/pre-commit.sh +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/setup.cfg +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/dv_pipecat_flows.egg-info/dependency_links.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/dv_pipecat_flows.egg-info/requires.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/dv_pipecat_flows.egg-info/top_level.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/__init__.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/actions.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/adapters.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/condition_evaluator.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/exceptions.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/flow_validator.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/processors/__init__.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/processors/speak_interruption_guard.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/router_mode.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/types.py +0 -0
- {dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/uv.lock +0 -0
|
@@ -23,40 +23,6 @@ uv.lock
|
|
|
23
23
|
.agents/skills/loki-logs/query-reference.md
|
|
24
24
|
.claude/skills/loki-logs/SKILL.md
|
|
25
25
|
.claude/skills/loki-logs/query-reference.md
|
|
26
|
-
browser_copilot/.env.example
|
|
27
|
-
browser_copilot/.gitignore
|
|
28
|
-
browser_copilot/README.md
|
|
29
|
-
browser_copilot/pyproject.toml
|
|
30
|
-
browser_copilot/uv.lock
|
|
31
|
-
browser_copilot/docs/ARCHITECTURE.md
|
|
32
|
-
browser_copilot/docs/CODE_MAP.md
|
|
33
|
-
browser_copilot/docs/CONFIG.md
|
|
34
|
-
browser_copilot/docs/CONTRACTS.md
|
|
35
|
-
browser_copilot/docs/COPILOT_TODO.md
|
|
36
|
-
browser_copilot/docs/DISTRIBUTION.md
|
|
37
|
-
browser_copilot/docs/HLD.md
|
|
38
|
-
browser_copilot/docs/METHODS.md
|
|
39
|
-
browser_copilot/docs/VOICE_E2E.md
|
|
40
|
-
browser_copilot/eval/ANALYSIS_stagehand_vs_hybrid.md
|
|
41
|
-
browser_copilot/eval/RESULTS.md
|
|
42
|
-
browser_copilot/eval/_env.py
|
|
43
|
-
browser_copilot/eval/bench.py
|
|
44
|
-
browser_copilot/eval/bench_browseruse.py
|
|
45
|
-
browser_copilot/eval/bench_stagehand.py
|
|
46
|
-
browser_copilot/eval/inspect_browseruse.py
|
|
47
|
-
browser_copilot/eval/inspect_browseruse_showcase.py
|
|
48
|
-
browser_copilot/eval/inspect_stagehand_prompt.py
|
|
49
|
-
browser_copilot/eval/results_browseruse.json
|
|
50
|
-
browser_copilot/eval/results_perception.json
|
|
51
|
-
browser_copilot/eval/results_stagehand.json
|
|
52
|
-
browser_copilot/eval/tasks.py
|
|
53
|
-
browser_copilot/eval/try_hybrid.py
|
|
54
|
-
browser_copilot/eval/try_hybrid_multistep.py
|
|
55
|
-
browser_copilot/eval/try_hybrid_oneshot.py
|
|
56
|
-
browser_copilot/eval/pages/checkout.html
|
|
57
|
-
browser_copilot/eval/pages/dashboard.html
|
|
58
|
-
browser_copilot/eval/pages/signup.html
|
|
59
|
-
browser_copilot/scripts/dev-stack.sh
|
|
60
26
|
editor/.eslintrc.json
|
|
61
27
|
editor/.prettierrc
|
|
62
28
|
editor/favicon.png
|
|
@@ -98,10 +64,8 @@ images/food-ordering-flow.png
|
|
|
98
64
|
remote-asterisk-code/README.md
|
|
99
65
|
remote-asterisk-code/extensions.conf
|
|
100
66
|
remote-asterisk-code/rtp.conf
|
|
101
|
-
scripts/azure_latency_probe.py
|
|
102
67
|
scripts/check-pypi-package.py
|
|
103
68
|
scripts/fix-ruff.sh
|
|
104
|
-
scripts/langfuse_export.py
|
|
105
69
|
scripts/pre-commit.sh
|
|
106
70
|
src/dv_pipecat_flows.egg-info/PKG-INFO
|
|
107
71
|
src/dv_pipecat_flows.egg-info/SOURCES.txt
|
|
@@ -40,40 +40,6 @@
|
|
|
40
40
|
"LICENSE",
|
|
41
41
|
"MANIFEST.in",
|
|
42
42
|
"README.md",
|
|
43
|
-
"browser_copilot/.env.example",
|
|
44
|
-
"browser_copilot/.gitignore",
|
|
45
|
-
"browser_copilot/README.md",
|
|
46
|
-
"browser_copilot/docs/ARCHITECTURE.md",
|
|
47
|
-
"browser_copilot/docs/CODE_MAP.md",
|
|
48
|
-
"browser_copilot/docs/CONFIG.md",
|
|
49
|
-
"browser_copilot/docs/CONTRACTS.md",
|
|
50
|
-
"browser_copilot/docs/COPILOT_TODO.md",
|
|
51
|
-
"browser_copilot/docs/DISTRIBUTION.md",
|
|
52
|
-
"browser_copilot/docs/HLD.md",
|
|
53
|
-
"browser_copilot/docs/METHODS.md",
|
|
54
|
-
"browser_copilot/docs/VOICE_E2E.md",
|
|
55
|
-
"browser_copilot/eval/ANALYSIS_stagehand_vs_hybrid.md",
|
|
56
|
-
"browser_copilot/eval/RESULTS.md",
|
|
57
|
-
"browser_copilot/eval/_env.py",
|
|
58
|
-
"browser_copilot/eval/bench.py",
|
|
59
|
-
"browser_copilot/eval/bench_browseruse.py",
|
|
60
|
-
"browser_copilot/eval/bench_stagehand.py",
|
|
61
|
-
"browser_copilot/eval/inspect_browseruse.py",
|
|
62
|
-
"browser_copilot/eval/inspect_browseruse_showcase.py",
|
|
63
|
-
"browser_copilot/eval/inspect_stagehand_prompt.py",
|
|
64
|
-
"browser_copilot/eval/pages/checkout.html",
|
|
65
|
-
"browser_copilot/eval/pages/dashboard.html",
|
|
66
|
-
"browser_copilot/eval/pages/signup.html",
|
|
67
|
-
"browser_copilot/eval/results_browseruse.json",
|
|
68
|
-
"browser_copilot/eval/results_perception.json",
|
|
69
|
-
"browser_copilot/eval/results_stagehand.json",
|
|
70
|
-
"browser_copilot/eval/tasks.py",
|
|
71
|
-
"browser_copilot/eval/try_hybrid.py",
|
|
72
|
-
"browser_copilot/eval/try_hybrid_multistep.py",
|
|
73
|
-
"browser_copilot/eval/try_hybrid_oneshot.py",
|
|
74
|
-
"browser_copilot/pyproject.toml",
|
|
75
|
-
"browser_copilot/scripts/dev-stack.sh",
|
|
76
|
-
"browser_copilot/uv.lock",
|
|
77
43
|
"dev-requirements.txt",
|
|
78
44
|
"docker-compose.dev.yml",
|
|
79
45
|
"docs/ASTERISK_CHAN_WEBSOCKET_MIGRATION.md",
|
|
@@ -86,7 +52,6 @@
|
|
|
86
52
|
"docs/Makefile",
|
|
87
53
|
"docs/OPENAI_RESPONSES_API_MIGRATION.md",
|
|
88
54
|
"docs/blockkit-agents-cdn-integration.md",
|
|
89
|
-
"docs/browser-copilot-rpc-contract.md",
|
|
90
55
|
"docs/build-docs.sh",
|
|
91
56
|
"docs/conf.py",
|
|
92
57
|
"docs/examples/pb_fintech_call_transfer_config.json",
|
|
@@ -191,13 +156,6 @@
|
|
|
191
156
|
"k8s/dv-pipecat/values-stage-asia.yaml",
|
|
192
157
|
"k8s/dv-pipecat/values-stage.yaml",
|
|
193
158
|
"k8s/dv-pipecat/values.yaml",
|
|
194
|
-
"k8s/langfuse/.gitignore",
|
|
195
|
-
"k8s/langfuse/.helmignore",
|
|
196
|
-
"k8s/langfuse/Chart.yaml",
|
|
197
|
-
"k8s/langfuse/README.md",
|
|
198
|
-
"k8s/langfuse/internal-lb.yaml",
|
|
199
|
-
"k8s/langfuse/managed-cert.yaml",
|
|
200
|
-
"k8s/langfuse/values.yaml",
|
|
201
159
|
"k8s/livekit/Chart.yaml",
|
|
202
160
|
"k8s/livekit/README.md",
|
|
203
161
|
"k8s/livekit/templates/_helpers.tpl",
|
|
@@ -220,15 +178,12 @@
|
|
|
220
178
|
"remote-asterisk-code/extensions.conf",
|
|
221
179
|
"remote-asterisk-code/rtp.conf",
|
|
222
180
|
"requirements.txt",
|
|
223
|
-
"ringg-bot/BROWSER_COPILOT_EVALUATION.md",
|
|
224
181
|
"ringg-bot/DEPLOYMENT.md",
|
|
225
182
|
"ringg-bot/Dockerfile",
|
|
226
|
-
"ringg-bot/LIVE_MEETING_AGENT.md",
|
|
227
183
|
"ringg-bot/PIPECAT_ARCHITECTURE.md",
|
|
228
184
|
"ringg-bot/PIPELINE_TESTING.md",
|
|
229
185
|
"ringg-bot/README.md",
|
|
230
186
|
"ringg-bot/assets/background/office_ambience_8k_mono.wav",
|
|
231
|
-
"ringg-bot/assets/tool_call_music_default.mp3",
|
|
232
187
|
"ringg-bot/backchannel.py",
|
|
233
188
|
"ringg-bot/bot.py",
|
|
234
189
|
"ringg-bot/bot_with_flows.py",
|
|
@@ -320,11 +275,8 @@
|
|
|
320
275
|
"ringg-bot/replay/tool_sandbox.py",
|
|
321
276
|
"ringg-bot/replay/tool_sandbox_single_node.py",
|
|
322
277
|
"ringg-bot/requirements.txt",
|
|
323
|
-
"ringg-bot/ringg_processors/copilot_contract.py",
|
|
324
|
-
"ringg-bot/ringg_processors/dom_context_injector.py",
|
|
325
278
|
"ringg-bot/ringg_processors/end_of_call_processor.py",
|
|
326
279
|
"ringg-bot/ringg_processors/function_call_filler_processor.py",
|
|
327
|
-
"ringg-bot/ringg_processors/function_call_music_processor.py",
|
|
328
280
|
"ringg-bot/ringg_processors/language_steering_processor.py",
|
|
329
281
|
"ringg-bot/ringg_processors/livekit_caption_observer.py",
|
|
330
282
|
"ringg-bot/ringg_processors/livekit_rpc_bridge.py",
|
|
@@ -370,29 +322,23 @@
|
|
|
370
322
|
"ringg-bot/tests/test_asteriskv2_transfer_numbers.py",
|
|
371
323
|
"ringg-bot/tests/test_asteriskv2_user_to_user.py",
|
|
372
324
|
"ringg-bot/tests/test_azure_region_routing.py",
|
|
373
|
-
"ringg-bot/tests/test_browser_action_suppression.py",
|
|
374
325
|
"ringg-bot/tests/test_call_transfer_strategies.py",
|
|
375
326
|
"ringg-bot/tests/test_chat_idle_activity_frame.py",
|
|
376
327
|
"ringg-bot/tests/test_chat_user_idle_processor.py",
|
|
377
328
|
"ringg-bot/tests/test_connection_watchdog.py",
|
|
378
|
-
"ringg-bot/tests/test_copilot_log_redaction.py",
|
|
379
329
|
"ringg-bot/tests/test_custom_vars_pipeline_parity.py",
|
|
380
330
|
"ringg-bot/tests/test_decimal_safe_aggregator.py",
|
|
381
331
|
"ringg-bot/tests/test_direct_sim_tool_records.py",
|
|
382
|
-
"ringg-bot/tests/test_dom_context_injector.py",
|
|
383
332
|
"ringg-bot/tests/test_dropped_turn_regeneration.py",
|
|
384
333
|
"ringg-bot/tests/test_end_call_schema.py",
|
|
334
|
+
"ringg-bot/tests/test_end_call_teardown.py",
|
|
385
335
|
"ringg-bot/tests/test_env_config_asterisk_ami.py",
|
|
386
|
-
"ringg-bot/tests/test_execute_plan.py",
|
|
387
|
-
"ringg-bot/tests/test_find_button_api_config.py",
|
|
388
336
|
"ringg-bot/tests/test_flow_query_kb.py",
|
|
389
337
|
"ringg-bot/tests/test_gcs_fetch.py",
|
|
390
338
|
"ringg-bot/tests/test_http_client_diagnostics.py",
|
|
391
339
|
"ringg-bot/tests/test_http_client_stall_diagnostics.py",
|
|
392
340
|
"ringg-bot/tests/test_inbound_call_data_and_custom_vars.py",
|
|
393
|
-
"ringg-bot/tests/test_inbound_gate_context_handoff.py",
|
|
394
341
|
"ringg-bot/tests/test_inbound_registration_race.py",
|
|
395
|
-
"ringg-bot/tests/test_inbound_resolve.py",
|
|
396
342
|
"ringg-bot/tests/test_job_reporting.py",
|
|
397
343
|
"ringg-bot/tests/test_kb_planner.py",
|
|
398
344
|
"ringg-bot/tests/test_language_map.py",
|
|
@@ -411,19 +357,17 @@
|
|
|
411
357
|
"ringg-bot/tests/test_merge_parallel_tool_responses.py",
|
|
412
358
|
"ringg-bot/tests/test_metrics_bridge.py",
|
|
413
359
|
"ringg-bot/tests/test_metrics_collector_llm.py",
|
|
360
|
+
"ringg-bot/tests/test_no_bot_calls_are_failed.py",
|
|
414
361
|
"ringg-bot/tests/test_on_call_api_pre_call_message.py",
|
|
415
362
|
"ringg-bot/tests/test_on_call_jq_metadata.py",
|
|
416
363
|
"ringg-bot/tests/test_on_call_sdk_context.py",
|
|
417
364
|
"ringg-bot/tests/test_ozonetel_inbound.py",
|
|
418
365
|
"ringg-bot/tests/test_ozonetel_transfer.py",
|
|
419
|
-
"ringg-bot/tests/test_rag_filter_operators.py",
|
|
420
|
-
"ringg-bot/tests/test_rag_filter_property_names.py",
|
|
421
366
|
"ringg-bot/tests/test_realtime_external_tts.py",
|
|
422
367
|
"ringg-bot/tests/test_recording_flush.py",
|
|
423
368
|
"ringg-bot/tests/test_recording_stereo.py",
|
|
424
369
|
"ringg-bot/tests/test_regression_monitor_sync.py",
|
|
425
370
|
"ringg-bot/tests/test_replay_kb_flag.py",
|
|
426
|
-
"ringg-bot/tests/test_runtime_config_language_normalization.py",
|
|
427
371
|
"ringg-bot/tests/test_runtime_config_upload.py",
|
|
428
372
|
"ringg-bot/tests/test_sdk_executor_flatten.py",
|
|
429
373
|
"ringg-bot/tests/test_sdk_tool_schema.py",
|
|
@@ -438,13 +382,11 @@
|
|
|
438
382
|
"ringg-bot/tests/test_simulate_batch.py",
|
|
439
383
|
"ringg-bot/tests/test_simulate_progress.py",
|
|
440
384
|
"ringg-bot/tests/test_summary_dispatcher.py",
|
|
441
|
-
"ringg-bot/tests/test_tool_call_music.py",
|
|
442
385
|
"ringg-bot/tests/test_tool_calls_upload.py",
|
|
443
386
|
"ringg-bot/tests/test_tool_executor_context.py",
|
|
444
387
|
"ringg-bot/tests/test_traffic_split.py",
|
|
445
388
|
"ringg-bot/tests/test_transcript_timestamp_format.py",
|
|
446
389
|
"ringg-bot/tests/test_transfer_custom_vars.py",
|
|
447
|
-
"ringg-bot/tests/test_transfer_summary_api_tool.py",
|
|
448
390
|
"ringg-bot/tests/test_tts_currency.py",
|
|
449
391
|
"ringg-bot/tests/test_variable_replacement.py",
|
|
450
392
|
"ringg-bot/tests/test_wandb_llm.py",
|
|
@@ -485,7 +427,6 @@
|
|
|
485
427
|
"ringg-bot/utils/decimal_safe_aggregator.py",
|
|
486
428
|
"ringg-bot/utils/deepgram_warmup.py",
|
|
487
429
|
"ringg-bot/utils/flow_tools/__init__.py",
|
|
488
|
-
"ringg-bot/utils/flow_tools/browser_actions.py",
|
|
489
430
|
"ringg-bot/utils/flow_tools/call_transfer.py",
|
|
490
431
|
"ringg-bot/utils/flow_tools/collect_long_input.py",
|
|
491
432
|
"ringg-bot/utils/flow_tools/dtmf_output.py",
|
|
@@ -556,12 +497,10 @@
|
|
|
556
497
|
"ringg-bot/utils/tts.py",
|
|
557
498
|
"ringg-bot/utils/twilio_async_client.py",
|
|
558
499
|
"ringg-bot/utils/warning_messages.py",
|
|
559
|
-
"ringg-bot/utils/widget_transcript.py",
|
|
560
500
|
"ringg-bot/utils/word_timestamp_logger.py",
|
|
561
501
|
"ringg-bot/voice_services/asterisk/asterisk_service.py",
|
|
562
502
|
"ringg-bot/voice_services/asterisk/router.py",
|
|
563
503
|
"ringg-bot/voice_services/asteriskv2/asterisk_v2_service.py",
|
|
564
|
-
"ringg-bot/voice_services/asteriskv2/inbound_resolve.py",
|
|
565
504
|
"ringg-bot/voice_services/asteriskv2/provider_transforms.py",
|
|
566
505
|
"ringg-bot/voice_services/asteriskv2/router.py",
|
|
567
506
|
"ringg-bot/voice_services/asteriskv2/websocket_handler.py",
|
|
@@ -605,10 +544,8 @@
|
|
|
605
544
|
"ringg-bot/voicemail_detector.py",
|
|
606
545
|
"ringg-bot/websocket/router.py",
|
|
607
546
|
"ringg-bot/websocket/websocket_service.py",
|
|
608
|
-
"scripts/azure_latency_probe.py",
|
|
609
547
|
"scripts/check-pypi-package.py",
|
|
610
548
|
"scripts/fix-ruff.sh",
|
|
611
|
-
"scripts/langfuse_export.py",
|
|
612
549
|
"scripts/pre-commit.sh",
|
|
613
550
|
"src/pipecat_flows/__init__.py",
|
|
614
551
|
"src/pipecat_flows/actions.py",
|
|
@@ -645,14 +582,14 @@
|
|
|
645
582
|
"tests/test_hamsa_stt_config.py",
|
|
646
583
|
"tests/test_helpers.py",
|
|
647
584
|
"tests/test_keypad_menu_node.py",
|
|
648
|
-
"tests/test_livekit_chat_flush.py",
|
|
649
585
|
"tests/test_manager.py",
|
|
650
586
|
"tests/test_openai_realtime_transcript_filter.py",
|
|
651
587
|
"tests/test_response_formatters.py",
|
|
652
588
|
"tests/test_router_extract_schema.py",
|
|
589
|
+
"tests/test_router_mode_guard.py",
|
|
653
590
|
"tests/test_router_no_unclear.py",
|
|
654
|
-
"tests/test_router_wait_idle.py",
|
|
655
591
|
"tests/test_tts_split_cache.py",
|
|
592
|
+
"tests/test_user_turn_observer.py",
|
|
656
593
|
"tests/test_voicemail_detector.py",
|
|
657
594
|
"uv.lock"
|
|
658
595
|
]
|
{dv_pipecat_flows-0.0.23.dev10 → dv_pipecat_flows-0.0.23.dev11}/src/pipecat_flows/manager.py
RENAMED
|
@@ -48,7 +48,7 @@ from typing import (
|
|
|
48
48
|
|
|
49
49
|
from loguru import logger
|
|
50
50
|
from utils.tracking_helpers import start_tracking, complete_tracking
|
|
51
|
-
from utils.collect_normalizers import
|
|
51
|
+
from utils.collect_normalizers import normalize_collect_value
|
|
52
52
|
from utils.collect_prompts import collect_prompt
|
|
53
53
|
from pipecat.frames.frames import (
|
|
54
54
|
DTMFUpdateSettingsFrame,
|
|
@@ -374,23 +374,6 @@ class FlowManager:
|
|
|
374
374
|
Callable[[Dict[str, Any]], Awaitable[None]]
|
|
375
375
|
] = None
|
|
376
376
|
|
|
377
|
-
# Optional hook for context that must SURVIVE a RESET* node transition.
|
|
378
|
-
# A RESET* strategy replaces the whole message list, dropping anything an
|
|
379
|
-
# external processor injected out-of-band (e.g. the browser co-pilot's
|
|
380
|
-
# ambient "[Browser context]" message, appended to the aggregator on each
|
|
381
|
-
# DOM update — not part of any node's messages). Given the freshly-built
|
|
382
|
-
# replacement list, the provider returns it with its own sticky messages
|
|
383
|
-
# re-applied: it both DROPS stale copies carried forward by
|
|
384
|
-
# RESET_WITH_CONVERSATION and APPENDS the current one, so there is no
|
|
385
|
-
# duplicate and no race (it rides the same update frame). Runs only on the
|
|
386
|
-
# full-replace path; APPEND already retains the live message. Default None
|
|
387
|
-
# (no-op) — non-copilot flows are unaffected.
|
|
388
|
-
#
|
|
389
|
-
# Signature: provider(messages: List[dict]) -> List[dict]
|
|
390
|
-
self.sticky_context_provider: Optional[
|
|
391
|
-
Callable[[List[dict]], List[dict]]
|
|
392
|
-
] = None
|
|
393
|
-
|
|
394
377
|
# Wired in `initialize()` once the pipeline is up. Tracks the timestamp
|
|
395
378
|
# of the latest UserStoppedSpeakingFrame so router-mode nodes can gate
|
|
396
379
|
# their LLM classification on "user has actually replied" instead of
|
|
@@ -1872,24 +1855,6 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
1872
1855
|
label="jump",
|
|
1873
1856
|
)
|
|
1874
1857
|
|
|
1875
|
-
def _last_context_role(self) -> Optional[str]:
|
|
1876
|
-
"""Role of the last message in the live LLM context, or None when no
|
|
1877
|
-
context aggregator / empty context. Used by the router wait gate to
|
|
1878
|
-
tell a real committed user turn apart from system-generated context
|
|
1879
|
-
pushes (e.g. the idle warning's LLM-path append)."""
|
|
1880
|
-
if not self._context_aggregator:
|
|
1881
|
-
return None
|
|
1882
|
-
ctx = self._context_aggregator.user()._context
|
|
1883
|
-
messages = (
|
|
1884
|
-
ctx.get_messages() if hasattr(ctx, "get_messages") else getattr(ctx, "messages", [])
|
|
1885
|
-
)
|
|
1886
|
-
if not messages:
|
|
1887
|
-
return None
|
|
1888
|
-
last = messages[-1]
|
|
1889
|
-
if isinstance(last, dict):
|
|
1890
|
-
return last.get("role")
|
|
1891
|
-
return getattr(last, "role", None)
|
|
1892
|
-
|
|
1893
1858
|
async def _run_router_node(
|
|
1894
1859
|
self, node_id: str, node_config: Dict[str, Any]
|
|
1895
1860
|
) -> Optional[str]:
|
|
@@ -1923,19 +1888,10 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
1923
1888
|
# `_run_router_node` captures a fresh entry_ts — the next iteration
|
|
1924
1889
|
# correctly waits for a NEW user reply instead of reclassifying the
|
|
1925
1890
|
# stale one that triggered the previous UNCLEAR.
|
|
1926
|
-
#
|
|
1927
|
-
# Silence handling is delegated to the user-idle processor: we re-arm
|
|
1928
|
-
# it for the duration of the wait (it was stopped at `_set_node`
|
|
1929
|
-
# entry) and block until the user actually replies. If they never do,
|
|
1930
|
-
# the idle processor's warning → end stages fire and end the call —
|
|
1931
|
-
# the router never classifies stale silence. The idle warning's
|
|
1932
|
-
# fixed-phrase TTSSpeakFrame passes RouterModeGuard; its LLM-path
|
|
1933
|
-
# fallback (realtime/chat-only/unsupported languages) is dropped by
|
|
1934
|
-
# the guard, so that warning is silent but the end stage still ends
|
|
1935
|
-
# the call.
|
|
1936
1891
|
observer = self._user_turn_observer
|
|
1937
1892
|
|
|
1938
|
-
|
|
1893
|
+
timeout_s = float(node_config.get("router_wait_for_user_timeout", 15.0))
|
|
1894
|
+
if observer is not None and timeout_s > 0:
|
|
1939
1895
|
entry_ts = time.monotonic()
|
|
1940
1896
|
if observer.last_user_stopped_ts >= entry_ts:
|
|
1941
1897
|
logger.debug(
|
|
@@ -1956,41 +1912,28 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
1956
1912
|
)
|
|
1957
1913
|
else:
|
|
1958
1914
|
logger.debug(
|
|
1959
|
-
f"Router '{node_id}': waiting for "
|
|
1960
|
-
f"UserStoppedSpeakingFrame
|
|
1915
|
+
f"Router '{node_id}': waiting up to {timeout_s}s for "
|
|
1916
|
+
f"UserStoppedSpeakingFrame"
|
|
1961
1917
|
)
|
|
1962
|
-
await self._task.queue_frame(StartUserIdleProcessorFrame())
|
|
1963
1918
|
try:
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
f"user reply); still waiting"
|
|
1982
|
-
)
|
|
1983
|
-
observer.event.clear()
|
|
1984
|
-
finally:
|
|
1985
|
-
# Restore the "idle stopped while the node is
|
|
1986
|
-
# processing" invariant for the classifier call.
|
|
1987
|
-
await self._task.queue_frame(
|
|
1988
|
-
StopUserIdleProcessorFrame()
|
|
1919
|
+
await asyncio.wait_for(
|
|
1920
|
+
observer.event.wait(), timeout=timeout_s
|
|
1921
|
+
)
|
|
1922
|
+
logger.debug(
|
|
1923
|
+
f"Router '{node_id}': user reply received after "
|
|
1924
|
+
f"{time.monotonic() - entry_ts:.2f}s"
|
|
1925
|
+
)
|
|
1926
|
+
except asyncio.TimeoutError:
|
|
1927
|
+
# User stayed silent. Don't raise — let the router
|
|
1928
|
+
# LLM classify whatever context is available; it
|
|
1929
|
+
# will most likely return UNCLEAR, which the
|
|
1930
|
+
# existing pick_branch_target machinery routes to
|
|
1931
|
+
# reprompt or exhausted_node.
|
|
1932
|
+
logger.info(
|
|
1933
|
+
f"Router '{node_id}': no user reply within "
|
|
1934
|
+
f"{timeout_s}s; proceeding (will likely route "
|
|
1935
|
+
f"to unclear/exhausted)"
|
|
1989
1936
|
)
|
|
1990
|
-
logger.debug(
|
|
1991
|
-
f"Router '{node_id}': user reply received after "
|
|
1992
|
-
f"{time.monotonic() - entry_ts:.2f}s"
|
|
1993
|
-
)
|
|
1994
1937
|
|
|
1995
1938
|
branches = node_config.get("router_branches") or []
|
|
1996
1939
|
extract = node_config.get("router_extract") or {}
|
|
@@ -2689,6 +2632,7 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2689
2632
|
"""
|
|
2690
2633
|
self._collect_readback = "" # grouped, language-matched read-back for the confirm
|
|
2691
2634
|
self._collect_reply = "" # LLM free reply for a conversational (non-digit) turn
|
|
2635
|
+
self._collect_repeat = False # LLM-decided: caller asked to hear the digits back
|
|
2692
2636
|
context_messages = [
|
|
2693
2637
|
{"role": "user", "content": turn}
|
|
2694
2638
|
for turn in turns
|
|
@@ -2732,6 +2676,14 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2732
2676
|
"the number; make no promises or commitments."
|
|
2733
2677
|
),
|
|
2734
2678
|
},
|
|
2679
|
+
"repeat_request": {
|
|
2680
|
+
"type": "boolean",
|
|
2681
|
+
"description": (
|
|
2682
|
+
"true ONLY if the caller's latest message asks to hear back the "
|
|
2683
|
+
"digits captured so far; false when they are offering to dictate "
|
|
2684
|
+
"again themselves."
|
|
2685
|
+
),
|
|
2686
|
+
},
|
|
2735
2687
|
},
|
|
2736
2688
|
)
|
|
2737
2689
|
try:
|
|
@@ -2749,6 +2701,11 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2749
2701
|
"empty and put a SHORT on-topic reply in `reply`: answer in one "
|
|
2750
2702
|
"sentence, then ask again for the number — stay on topic, make no "
|
|
2751
2703
|
"promises.\n"
|
|
2704
|
+
"Set `repeat_request` to true ONLY when the caller's latest message "
|
|
2705
|
+
"asks to hear back what has been captured so far ('repeat that', "
|
|
2706
|
+
"'what do you have?', 'दोबारा बोलिए'). An offer to dictate again "
|
|
2707
|
+
"('मैं फिर से बताती हूँ', 'I'll tell you again') is NOT a repeat "
|
|
2708
|
+
"request.\n"
|
|
2752
2709
|
"Digits: read each spoken number-word in the order spoken and join its "
|
|
2753
2710
|
"digits left to right — a digit-word adds one digit, 'double X' adds "
|
|
2754
2711
|
"XX, 'triple X' adds XXX (in any language, incl. डबल/ट्रिपल), and a lone "
|
|
@@ -2778,6 +2735,7 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2778
2735
|
reply = (extracted or {}).get("reply")
|
|
2779
2736
|
self._collect_readback = readback.strip() if isinstance(readback, str) else ""
|
|
2780
2737
|
self._collect_reply = reply.strip() if isinstance(reply, str) else ""
|
|
2738
|
+
self._collect_repeat = bool((extracted or {}).get("repeat_request"))
|
|
2781
2739
|
return re.sub(r"\D", "", value) if isinstance(value, str) else ""
|
|
2782
2740
|
|
|
2783
2741
|
async def _reextract_if_corrected(
|
|
@@ -2939,9 +2897,10 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2939
2897
|
# the CORRECTED number at once — instead of asking "what should it
|
|
2940
2898
|
# be?" and making them repeat what they just said. The idempotent
|
|
2941
2899
|
# extractor merges "no, the first part is 807" into 70823… →
|
|
2942
|
-
# 8072341303.
|
|
2943
|
-
#
|
|
2944
|
-
#
|
|
2900
|
+
# 8072341303. Only a plain rejection with no new digits falls
|
|
2901
|
+
# through to the re-ask — and starts FRESH (start_ts advanced), so
|
|
2902
|
+
# the rejected number's turns can't concatenate into the caller's
|
|
2903
|
+
# re-dictation (live call 2a3f6513). Bounded by ``corrections``.
|
|
2945
2904
|
while True:
|
|
2946
2905
|
confirmed = await self._confirm_collected(
|
|
2947
2906
|
node_id, getattr(self, "_collect_readback", "") or " ".join(buffer)
|
|
@@ -2951,6 +2910,10 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2951
2910
|
corrections += 1
|
|
2952
2911
|
if corrections >= max_attempts:
|
|
2953
2912
|
break
|
|
2913
|
+
# Snapshot the fresh-attempt boundary BEFORE the re-extract LLM
|
|
2914
|
+
# call: a caller who starts re-dictating during that await must
|
|
2915
|
+
# land INSIDE the new window, not be timed out of it.
|
|
2916
|
+
fresh_start = time.monotonic()
|
|
2954
2917
|
corrected = await self._reextract_if_corrected(
|
|
2955
2918
|
node_id, "phone", start_ts, normalized,
|
|
2956
2919
|
country_default=country_default,
|
|
@@ -2958,7 +2921,9 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2958
2921
|
if corrected is not None:
|
|
2959
2922
|
buffer, normalized = corrected
|
|
2960
2923
|
continue # re-read the corrected number, no "what should it be?"
|
|
2924
|
+
start_ts = fresh_start
|
|
2961
2925
|
buffer = ""
|
|
2926
|
+
echoed = ""
|
|
2962
2927
|
await self._task.queue_frame(
|
|
2963
2928
|
TTSSpeakFrame(text=collect_prompt(language, "restart_denied"))
|
|
2964
2929
|
)
|
|
@@ -2987,8 +2952,7 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
2987
2952
|
# correction during the echo phase ("no, the 3rd is 5": 7765 → 7755)
|
|
2988
2953
|
# changes the value but not the length, so a length check would mis-read
|
|
2989
2954
|
# the fix as "no progress" and burn a miss instead of echoing it below.
|
|
2990
|
-
|
|
2991
|
-
if buffer and turns and is_repeat_request(turns[-1]):
|
|
2955
|
+
if buffer and getattr(self, "_collect_repeat", False):
|
|
2992
2956
|
# Re-reads bypass the miss counter, so the absolute deadline is the
|
|
2993
2957
|
# only bound — without it a caller repeating "say it again" loops
|
|
2994
2958
|
# forever (a turn lands every cycle, never silence, never a miss).
|
|
@@ -3092,7 +3056,9 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3092
3056
|
if normalized:
|
|
3093
3057
|
# Same as phone: a "no, it's X" correction re-extracts over the turns
|
|
3094
3058
|
# we already have and re-reads the CORRECTED code at once; only a
|
|
3095
|
-
# plain rejection with no new digits asks "what should it be?"
|
|
3059
|
+
# plain rejection with no new digits asks "what should it be?" —
|
|
3060
|
+
# and starts FRESH (start_ts advanced) so the rejected code's turns
|
|
3061
|
+
# can't concatenate into the re-dictation (live call 2a3f6513).
|
|
3096
3062
|
while True:
|
|
3097
3063
|
confirmed = await self._confirm_collected(
|
|
3098
3064
|
node_id, getattr(self, "_collect_readback", "") or " ".join(normalized)
|
|
@@ -3102,6 +3068,8 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3102
3068
|
corrections += 1
|
|
3103
3069
|
if corrections >= max_attempts:
|
|
3104
3070
|
break
|
|
3071
|
+
# Boundary BEFORE the re-extract await (same race as phone above).
|
|
3072
|
+
fresh_start = time.monotonic()
|
|
3105
3073
|
corrected = await self._reextract_if_corrected(
|
|
3106
3074
|
node_id, collect_type, start_ts, normalized,
|
|
3107
3075
|
what="numeric code", num_digits=target,
|
|
@@ -3109,7 +3077,9 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3109
3077
|
if corrected is not None:
|
|
3110
3078
|
buffer, normalized = corrected
|
|
3111
3079
|
continue # re-read the corrected code, no "what should it be?"
|
|
3080
|
+
start_ts = fresh_start
|
|
3112
3081
|
buffer = ""
|
|
3082
|
+
echoed = ""
|
|
3113
3083
|
await self._task.queue_frame(
|
|
3114
3084
|
TTSSpeakFrame(text=collect_prompt(language, "restart_denied"))
|
|
3115
3085
|
)
|
|
@@ -3135,8 +3105,7 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3135
3105
|
# asked to hear it back, RE-READ the digits-so-far (no miss); else re-ask
|
|
3136
3106
|
# and count a miss. Compare by VALUE not length so a same-length
|
|
3137
3107
|
# correction during the echo phase counts as progress (echoed below).
|
|
3138
|
-
|
|
3139
|
-
if buffer and turns and is_repeat_request(turns[-1]):
|
|
3108
|
+
if buffer and getattr(self, "_collect_repeat", False):
|
|
3140
3109
|
# Re-reads bypass the miss counter, so the absolute deadline is the
|
|
3141
3110
|
# only bound — without it a caller repeating "say it again" loops
|
|
3142
3111
|
# forever (a turn lands every cycle, never silence, never a miss).
|
|
@@ -3196,7 +3165,8 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3196
3165
|
# knows WHAT is being confirmed. Works in any language.
|
|
3197
3166
|
answer = await self._extract_spoken_value(
|
|
3198
3167
|
node_id,
|
|
3199
|
-
"yes if the caller agrees the value is correct, no if they say it is
|
|
3168
|
+
"yes if the caller agrees the value is correct, no if they say it is "
|
|
3169
|
+
"wrong, repeat if they ask to hear the value read back again",
|
|
3200
3170
|
timeout_s=10.0,
|
|
3201
3171
|
system_prompt=(
|
|
3202
3172
|
"The assistant just read a value back to the caller and asked them "
|
|
@@ -3205,13 +3175,16 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3205
3175
|
"confirmed it is correct. If they point out ANY mistake or give a "
|
|
3206
3176
|
"different value — even while sounding agreeable (e.g. 'no, the "
|
|
3207
3177
|
"last one is 5 not 3', 'haan but change the first digit') — that "
|
|
3208
|
-
"is NOT confirmed; answer 'no'. Answer '
|
|
3209
|
-
"
|
|
3178
|
+
"is NOT confirmed; answer 'no'. Answer 'repeat' ONLY if they ask "
|
|
3179
|
+
"to HEAR the value again ('repeat that', 'दोबारा बोलिए') — a "
|
|
3180
|
+
"caller saying it is wrong and offering to dictate it again "
|
|
3181
|
+
"('नहीं, मैं फिर से बताती हूँ') means 'no', not 'repeat'. Answer "
|
|
3182
|
+
"'yes' only if they accept it exactly as read."
|
|
3210
3183
|
),
|
|
3211
3184
|
context_prefix=[
|
|
3212
3185
|
{"role": "assistant", "content": f"I have {readback}. Is that correct?"}
|
|
3213
3186
|
],
|
|
3214
|
-
enum_values=["yes", "no"],
|
|
3187
|
+
enum_values=["yes", "no", "repeat"],
|
|
3215
3188
|
)
|
|
3216
3189
|
# Hard guard: a genuine confirmation never contains digits, a correction
|
|
3217
3190
|
# ("no, the last is 5 not 3") always does. If the reply has any digit,
|
|
@@ -3224,17 +3197,18 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3224
3197
|
else ""
|
|
3225
3198
|
)
|
|
3226
3199
|
has_digit = any(ch.isdigit() for ch in reply)
|
|
3227
|
-
# A request to hear it again ("can you repeat?") is NOT a yes or a no —
|
|
3228
|
-
# re-read the value and DON'T burn a confirm attempt (bounded so a caller
|
|
3229
|
-
# stuck on "repeat" can't loop forever). A digit-carrying reply is a
|
|
3230
|
-
# correction, handled just below, so only a digit-free reply is a repeat.
|
|
3231
|
-
if not has_digit and is_repeat_request(reply):
|
|
3232
|
-
repeats += 1
|
|
3233
|
-
if repeats <= 3:
|
|
3234
|
-
continue
|
|
3235
3200
|
if has_digit:
|
|
3236
3201
|
return False
|
|
3237
3202
|
text = (answer or "").strip().lower()
|
|
3203
|
+
# A request to hear it again (LLM-decided — "मैं फिर से बताती हूँ" /
|
|
3204
|
+
# "I'll tell you again" classifies as 'no', not 'repeat'; the old
|
|
3205
|
+
# substring match looped on that false positive, live call 2a3f6513)
|
|
3206
|
+
# is NOT a yes or a no — re-read the value and DON'T burn a confirm
|
|
3207
|
+
# attempt (bounded so a caller stuck on "repeat" can't loop forever).
|
|
3208
|
+
if text.startswith("repeat"):
|
|
3209
|
+
repeats += 1
|
|
3210
|
+
if repeats <= 3:
|
|
3211
|
+
continue
|
|
3238
3212
|
if text.startswith("yes"):
|
|
3239
3213
|
return True
|
|
3240
3214
|
if text.startswith("no"):
|
|
@@ -4405,22 +4379,17 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
4405
4379
|
|
|
4406
4380
|
# For the first node or any RESET* strategy, replace the whole
|
|
4407
4381
|
# context; APPEND adds to it.
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
messages = self.sticky_context_provider(messages)
|
|
4420
|
-
except Exception as exc: # never let a provider break a transition
|
|
4421
|
-
logger.warning(f"sticky_context_provider failed: {exc}")
|
|
4422
|
-
|
|
4423
|
-
frame_type = LLMMessagesUpdateFrame if is_replace else LLMMessagesAppendFrame
|
|
4382
|
+
frame_type = (
|
|
4383
|
+
LLMMessagesUpdateFrame
|
|
4384
|
+
if self._current_node is None
|
|
4385
|
+
or update_config.strategy
|
|
4386
|
+
in [
|
|
4387
|
+
ContextStrategy.RESET,
|
|
4388
|
+
ContextStrategy.RESET_WITH_SUMMARY,
|
|
4389
|
+
ContextStrategy.RESET_WITH_CONVERSATION,
|
|
4390
|
+
]
|
|
4391
|
+
else LLMMessagesAppendFrame
|
|
4392
|
+
)
|
|
4424
4393
|
|
|
4425
4394
|
await self._task.queue_frames(
|
|
4426
4395
|
[frame_type(messages=messages), LLMSetToolsFrame(tools=functions)]
|