dv-pipecat-flows 0.0.23.dev5__tar.gz → 0.0.23.dev7__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.dev5/src/dv_pipecat_flows.egg-info → dv_pipecat_flows-0.0.23.dev7}/PKG-INFO +2 -1
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/README.md +1 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7/src/dv_pipecat_flows.egg-info}/PKG-INFO +2 -1
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/manager.py +171 -20
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.agents/skills/loki-logs/SKILL.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.agents/skills/loki-logs/query-reference.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.claude/skills/loki-logs/SKILL.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.claude/skills/loki-logs/query-reference.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.gitattributes +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.gitignore +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.pre-commit-config.yaml +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.python-version +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.readthedocs.yaml +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/AGENTS.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/CHANGELOG.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/CLAUDE.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/CONTRIBUTING.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/LICENSE +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/MANIFEST.in +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/dev-requirements.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/docker-compose.dev.yml +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/.eslintrc.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/.prettierrc +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/css/tailwind.css +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/food_ordering.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/movie_explorer.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/patient_intake.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/restaurant_reservation.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/travel_planner.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/favicon.png +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/favicon.svg +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/index.html +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/editor/canvas.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/editor/editorState.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/editor/sidePanel.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/editor/toolbar.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/main.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/baseNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/endNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/flowNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/functionNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/index.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/mergeNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/startNode.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/types.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/utils/export.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/utils/helpers.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/utils/import.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/utils/validation.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/jsdoc.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/package-lock.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/package.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/postcss.config.cjs +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/public/favicon.png +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/public/favicon.svg +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/tailwind.config.cjs +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/vercel.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/vite.config.js +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/engine_primitives_plan.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/env.example +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/images/food-ordering-flow.png +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/pipecat-flows.png +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/pipecat_upgrade.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/pyproject.toml +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/remote-asterisk-code/README.md +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/remote-asterisk-code/extensions.conf +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/remote-asterisk-code/rtp.conf +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/requirements.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/scripts/check-pypi-package.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/scripts/fix-ruff.sh +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/scripts/pre-commit.sh +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/setup.cfg +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/dv_pipecat_flows.egg-info/SOURCES.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/dv_pipecat_flows.egg-info/dependency_links.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/dv_pipecat_flows.egg-info/requires.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/dv_pipecat_flows.egg-info/scm_file_list.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/dv_pipecat_flows.egg-info/scm_version.json +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/dv_pipecat_flows.egg-info/top_level.txt +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/__init__.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/actions.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/adapters.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/condition_evaluator.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/exceptions.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/flow_validator.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/processors/__init__.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/processors/router_mode_guard.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/processors/speak_interruption_guard.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/processors/user_turn_observer.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/router_mode.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/types.py +0 -0
- {dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/uv.lock +0 -0
{dv_pipecat_flows-0.0.23.dev5/src/dv_pipecat_flows.egg-info → dv_pipecat_flows-0.0.23.dev7}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dv-pipecat-flows
|
|
3
|
-
Version: 0.0.23.
|
|
3
|
+
Version: 0.0.23.dev7
|
|
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
|
|
@@ -309,3 +309,4 @@ We aim to review all contributions promptly and provide constructive feedback to
|
|
|
309
309
|
➡️ [Reach us on X](https://x.com/pipecat_ai)
|
|
310
310
|
|
|
311
311
|
|
|
312
|
+
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7/src/dv_pipecat_flows.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dv-pipecat-flows
|
|
3
|
-
Version: 0.0.23.
|
|
3
|
+
Version: 0.0.23.dev7
|
|
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
|
|
@@ -309,3 +309,4 @@ We aim to review all contributions promptly and provide constructive feedback to
|
|
|
309
309
|
➡️ [Reach us on X](https://x.com/pipecat_ai)
|
|
310
310
|
|
|
311
311
|
|
|
312
|
+
|
|
@@ -120,34 +120,139 @@ else:
|
|
|
120
120
|
LLMService = Any
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
the
|
|
132
|
-
|
|
123
|
+
def _paired_transition_call_ids(
|
|
124
|
+
messages: List[dict], transition_names: Set[str]
|
|
125
|
+
) -> Set[str]:
|
|
126
|
+
"""IDs of transition tool calls that have BOTH a call and a matching result.
|
|
127
|
+
|
|
128
|
+
Pairing is the whole point. Every provider rejects a dangling half: an
|
|
129
|
+
assistant ``tool_use``/``tool_calls`` entry with no result, or a result whose
|
|
130
|
+
call was filtered away, is a hard 400. Rather than emit and then repair, we
|
|
131
|
+
resolve the pairs up front and only keep ids that are complete on both sides
|
|
132
|
+
— so a transition still in flight when the context is rebuilt is simply left
|
|
133
|
+
out instead of corrupting the whole request.
|
|
133
134
|
"""
|
|
135
|
+
call_ids: Set[str] = set()
|
|
136
|
+
for message in messages:
|
|
137
|
+
if not isinstance(message, dict) or message.get("role") != "assistant":
|
|
138
|
+
continue
|
|
139
|
+
# OpenAI shape: {"tool_calls": [{"id", "function": {"name"}}]}
|
|
140
|
+
for tool_call in message.get("tool_calls") or []:
|
|
141
|
+
if not isinstance(tool_call, dict):
|
|
142
|
+
continue
|
|
143
|
+
name = (tool_call.get("function") or {}).get("name")
|
|
144
|
+
if name in transition_names and tool_call.get("id"):
|
|
145
|
+
call_ids.add(tool_call["id"])
|
|
146
|
+
# Anthropic shape: content parts of type "tool_use"
|
|
147
|
+
content = message.get("content")
|
|
148
|
+
if isinstance(content, list):
|
|
149
|
+
for part in content:
|
|
150
|
+
if (
|
|
151
|
+
isinstance(part, dict)
|
|
152
|
+
and part.get("type") == "tool_use"
|
|
153
|
+
and part.get("name") in transition_names
|
|
154
|
+
and part.get("id")
|
|
155
|
+
):
|
|
156
|
+
call_ids.add(part["id"])
|
|
157
|
+
|
|
158
|
+
result_ids: Set[str] = set()
|
|
159
|
+
for message in messages:
|
|
160
|
+
if not isinstance(message, dict):
|
|
161
|
+
continue
|
|
162
|
+
# OpenAI shape: a dedicated {"role": "tool", "tool_call_id": ...} message
|
|
163
|
+
if message.get("role") == "tool" and message.get("tool_call_id") in call_ids:
|
|
164
|
+
result_ids.add(message["tool_call_id"])
|
|
165
|
+
# Anthropic shape: "tool_result" parts ride inside a *user* message
|
|
166
|
+
content = message.get("content")
|
|
167
|
+
if isinstance(content, list):
|
|
168
|
+
for part in content:
|
|
169
|
+
if (
|
|
170
|
+
isinstance(part, dict)
|
|
171
|
+
and part.get("type") == "tool_result"
|
|
172
|
+
and part.get("tool_use_id") in call_ids
|
|
173
|
+
):
|
|
174
|
+
result_ids.add(part["tool_use_id"])
|
|
175
|
+
|
|
176
|
+
return call_ids & result_ids
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def extract_conversation_messages(
|
|
180
|
+
messages: List[dict], transition_names: Optional[Set[str]] = None
|
|
181
|
+
) -> List[dict]:
|
|
182
|
+
"""Keep the conversational turns, plus the transition tool calls.
|
|
183
|
+
|
|
184
|
+
Returns user/assistant messages carrying real spoken text, and — when
|
|
185
|
+
``transition_names`` is supplied — the ``tool_use``/``tool_result`` pairs for
|
|
186
|
+
the flow's transition functions. Everything else is dropped: system prompts,
|
|
187
|
+
and the tool traffic of KB lookups / API tools / widgets (a single ``query_kb``
|
|
188
|
+
result runs ~8 KB, and the node's own system prompt is the real cost anyway).
|
|
189
|
+
|
|
190
|
+
Keeping the transition call is not cosmetic. Without it the carried transcript
|
|
191
|
+
ends on the user turn that triggered the hop, which is indistinguishable from a
|
|
192
|
+
fresh message nobody has handled — so the target node re-routes on it and the
|
|
193
|
+
source node sends it straight back. That is the A->B->A ping-pong. The
|
|
194
|
+
preserved call tells the target it was routed here, by whom, and (via the
|
|
195
|
+
required ``transition_reason`` arg) why.
|
|
196
|
+
|
|
197
|
+
Handles both string content and provider content-part lists (Anthropic).
|
|
198
|
+
Used by the RESET_WITH_CONVERSATION strategy.
|
|
199
|
+
"""
|
|
200
|
+
transition_names = transition_names or set()
|
|
201
|
+
keep_ids = _paired_transition_call_ids(messages, transition_names)
|
|
202
|
+
|
|
134
203
|
kept: List[dict] = []
|
|
135
204
|
for message in messages:
|
|
136
|
-
if not isinstance(message, dict)
|
|
205
|
+
if not isinstance(message, dict):
|
|
206
|
+
continue
|
|
207
|
+
role = message.get("role")
|
|
208
|
+
if role not in ("user", "assistant", "tool"):
|
|
137
209
|
continue
|
|
138
210
|
content = message.get("content")
|
|
211
|
+
|
|
212
|
+
# OpenAI tool result — keep only if its call survived.
|
|
213
|
+
if role == "tool":
|
|
214
|
+
if message.get("tool_call_id") in keep_ids:
|
|
215
|
+
kept.append(message)
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
# Provider content-part list (Anthropic): text + surviving tool blocks.
|
|
139
219
|
if isinstance(content, list):
|
|
140
|
-
|
|
220
|
+
parts = [
|
|
141
221
|
part
|
|
142
222
|
for part in content
|
|
143
|
-
if isinstance(part, dict)
|
|
223
|
+
if isinstance(part, dict)
|
|
224
|
+
and (
|
|
225
|
+
(part.get("type") == "text" and part.get("text"))
|
|
226
|
+
or (part.get("type") == "tool_use" and part.get("id") in keep_ids)
|
|
227
|
+
or (
|
|
228
|
+
part.get("type") == "tool_result"
|
|
229
|
+
and part.get("tool_use_id") in keep_ids
|
|
230
|
+
)
|
|
231
|
+
)
|
|
144
232
|
]
|
|
145
|
-
if
|
|
146
|
-
|
|
147
|
-
|
|
233
|
+
if parts:
|
|
234
|
+
kept.append({"role": role, "content": parts})
|
|
235
|
+
continue
|
|
236
|
+
|
|
237
|
+
# OpenAI assistant turn carrying tool_calls.
|
|
238
|
+
tool_calls = [
|
|
239
|
+
tool_call
|
|
240
|
+
for tool_call in (message.get("tool_calls") or [])
|
|
241
|
+
if isinstance(tool_call, dict) and tool_call.get("id") in keep_ids
|
|
242
|
+
]
|
|
243
|
+
if tool_calls:
|
|
244
|
+
# An assistant turn may mix a KB call with a transition call; only the
|
|
245
|
+
# surviving calls come across, and their results were matched above.
|
|
246
|
+
kept.append(
|
|
247
|
+
{
|
|
248
|
+
"role": role,
|
|
249
|
+
"content": content if isinstance(content, str) and content.strip() else None,
|
|
250
|
+
"tool_calls": tool_calls,
|
|
251
|
+
}
|
|
252
|
+
)
|
|
148
253
|
elif isinstance(content, str) and content.strip():
|
|
149
|
-
kept.append({"role":
|
|
150
|
-
# else: empty content
|
|
254
|
+
kept.append({"role": role, "content": content})
|
|
255
|
+
# else: empty content and no surviving tool calls -> drop
|
|
151
256
|
return kept
|
|
152
257
|
|
|
153
258
|
|
|
@@ -219,6 +324,16 @@ class FlowManager:
|
|
|
219
324
|
# rationale + prompt_sha1 land in the transition tool-log args. Cleared
|
|
220
325
|
# by _set_node after read so it can't leak into an unrelated next hop.
|
|
221
326
|
self._last_router_pick: Optional[Dict[str, Any]] = None
|
|
327
|
+
# The LLM's own one-line rationale for the hop it just requested, taken
|
|
328
|
+
# from the `transition_reason` arg every transition function declares.
|
|
329
|
+
# Same lifecycle as _last_router_pick: stashed by the transition func,
|
|
330
|
+
# consumed + cleared by _set_node so it can't leak into the next hop.
|
|
331
|
+
self._last_transition_reason: Optional[str] = None
|
|
332
|
+
# Every transition function registered so far this call (populated by
|
|
333
|
+
# _register_function). RESET_WITH_CONVERSATION keeps these tool calls in
|
|
334
|
+
# the carried context and drops all other tool traffic — see
|
|
335
|
+
# extract_conversation_messages.
|
|
336
|
+
self._transition_function_names: Set[str] = set()
|
|
222
337
|
self._context_aggregator = context_aggregator
|
|
223
338
|
self._pending_transition: Optional[Dict[str, Any]] = None
|
|
224
339
|
# Result ("success"/"failure") of the most recent function action, set
|
|
@@ -698,6 +813,14 @@ class FlowManager:
|
|
|
698
813
|
function_start_time = time.time()
|
|
699
814
|
try:
|
|
700
815
|
logger.debug(f"Function called: {name}")
|
|
816
|
+
# Every transition function declares a required `transition_reason`
|
|
817
|
+
# (see call_helper._with_transition_reason). Stash it for _set_node,
|
|
818
|
+
# which tool-logs it and turns it into the handoff breadcrumb.
|
|
819
|
+
# Not written into state — it is per-hop rationale, not a variable.
|
|
820
|
+
reason = (params.arguments or {}).get("transition_reason")
|
|
821
|
+
if isinstance(reason, str) and reason.strip():
|
|
822
|
+
self._last_transition_reason = reason.strip()
|
|
823
|
+
|
|
701
824
|
# Flattened-router extract: copy LLM-supplied args into
|
|
702
825
|
# ``flow_manager.state`` BEFORE the transition resolves, so
|
|
703
826
|
# downstream nodes (e.g. Speak picking variant by
|
|
@@ -805,6 +928,22 @@ class FlowManager:
|
|
|
805
928
|
next_node = None
|
|
806
929
|
is_transition_only_function = True
|
|
807
930
|
|
|
931
|
+
# Record that THIS function moved the flow, so
|
|
932
|
+
# RESET_WITH_CONVERSATION keeps its tool call in the carried
|
|
933
|
+
# context (see extract_conversation_messages).
|
|
934
|
+
#
|
|
935
|
+
# Detected at CALL time, not registration time. ringg-bot bakes the
|
|
936
|
+
# target into the handler (``create_transition_handler`` returns
|
|
937
|
+
# ``({"status": "ok"}, target_node)``) and leaves
|
|
938
|
+
# ``FlowsFunctionSchema.transition_to`` unset — so a
|
|
939
|
+
# registration-time check reads None, never fires, and silently
|
|
940
|
+
# reduces the whole context filter to a no-op. A truthy
|
|
941
|
+
# ``next_node`` is the only reliable signal. Call-time is
|
|
942
|
+
# sufficient by construction: a transition's tool call can only be
|
|
943
|
+
# in the context if the transition actually ran.
|
|
944
|
+
if next_node:
|
|
945
|
+
self._transition_function_names.add(name)
|
|
946
|
+
|
|
808
947
|
logger.debug(
|
|
809
948
|
f"{'Transition-only function called for' if is_transition_only_function else 'Function handler completed for'} {name}"
|
|
810
949
|
)
|
|
@@ -1041,6 +1180,13 @@ class FlowManager:
|
|
|
1041
1180
|
Raises:
|
|
1042
1181
|
FlowError: If function registration fails
|
|
1043
1182
|
"""
|
|
1183
|
+
# A function that moves the flow is a transition function; everything else
|
|
1184
|
+
# (KB lookups, API tools, widgets) is not. Recorded cumulatively for the
|
|
1185
|
+
# whole call so RESET_WITH_CONVERSATION can tell the two apart in a context
|
|
1186
|
+
# it is filtering long after the source node's tools were swapped out.
|
|
1187
|
+
if transition_to or transition_callback:
|
|
1188
|
+
self._transition_function_names.add(name)
|
|
1189
|
+
|
|
1044
1190
|
if name not in self._current_functions:
|
|
1045
1191
|
try:
|
|
1046
1192
|
# Handle special token format (e.g. "__function__:function_name")
|
|
@@ -3414,6 +3560,8 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3414
3560
|
previous_node_id = self._current_node
|
|
3415
3561
|
router_pick = self._last_router_pick
|
|
3416
3562
|
self._last_router_pick = None
|
|
3563
|
+
transition_reason = self._last_transition_reason
|
|
3564
|
+
self._last_transition_reason = None
|
|
3417
3565
|
# In v2 deterministic lowering, ``FlowConfig.nodes`` is keyed by
|
|
3418
3566
|
# ``node.name`` (see calling/flow_builder_v2.py:214), and
|
|
3419
3567
|
# ``self._current_node`` stores that same dict key — so
|
|
@@ -3459,9 +3607,11 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
3459
3607
|
}
|
|
3460
3608
|
if router_pick:
|
|
3461
3609
|
transition_args.update(router_pick)
|
|
3610
|
+
if transition_reason:
|
|
3611
|
+
transition_args["transition_reason"] = transition_reason
|
|
3462
3612
|
logger.info(
|
|
3463
3613
|
f"[node_transition] from={previous_node_id} to={node_id} "
|
|
3464
|
-
f"router_pick={router_pick}"
|
|
3614
|
+
f"router_pick={router_pick} reason={transition_reason!r}"
|
|
3465
3615
|
)
|
|
3466
3616
|
transition_call_id = await start_tracking(
|
|
3467
3617
|
transcript_handler=transition_transcript_handler,
|
|
@@ -4181,7 +4331,8 @@ In all of these cases, you can provide a `name` in your new node's config for de
|
|
|
4181
4331
|
# role_messages (the new system instruction) and before task
|
|
4182
4332
|
# messages, mirroring the RESET_WITH_SUMMARY layout.
|
|
4183
4333
|
carried = extract_conversation_messages(
|
|
4184
|
-
self._context_aggregator.user()._context.get_messages()
|
|
4334
|
+
self._context_aggregator.user()._context.get_messages(),
|
|
4335
|
+
transition_names=self._transition_function_names,
|
|
4185
4336
|
)
|
|
4186
4337
|
messages.extend(carried)
|
|
4187
4338
|
logger.debug(f"Carried {len(carried)} conversation message(s) across transition")
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.agents/skills/loki-logs/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/.claude/skills/loki-logs/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/food_ordering.json
RENAMED
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/movie_explorer.json
RENAMED
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/patient_intake.json
RENAMED
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/examples/travel_planner.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/editor/editorState.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/editor/js/nodes/functionNode.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/remote-asterisk-code/README.md
RENAMED
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/remote-asterisk-code/extensions.conf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/exceptions.py
RENAMED
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/flow_validator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dv_pipecat_flows-0.0.23.dev5 → dv_pipecat_flows-0.0.23.dev7}/src/pipecat_flows/router_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|