fastworkflow 2.13.5__py3-none-any.whl → 2.14.1__py3-none-any.whl
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.
- fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py +1 -1
- fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/go_up.py +1 -1
- fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/reset_context.py +1 -1
- fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py +98 -166
- fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +7 -3
- fastworkflow/build/genai_postprocessor.py +143 -149
- fastworkflow/chat_session.py +42 -11
- fastworkflow/command_metadata_api.py +794 -0
- fastworkflow/command_routing.py +4 -1
- fastworkflow/examples/fastworkflow.env +1 -1
- fastworkflow/examples/fastworkflow.passwords.env +1 -0
- fastworkflow/examples/hello_world/_commands/add_two_numbers.py +1 -0
- fastworkflow/examples/retail_workflow/_commands/calculate.py +67 -0
- fastworkflow/examples/retail_workflow/_commands/cancel_pending_order.py +4 -1
- fastworkflow/examples/retail_workflow/_commands/exchange_delivered_order_items.py +13 -1
- fastworkflow/examples/retail_workflow/_commands/find_user_id_by_email.py +6 -1
- fastworkflow/examples/retail_workflow/_commands/find_user_id_by_name_zip.py +6 -1
- fastworkflow/examples/retail_workflow/_commands/get_order_details.py +22 -10
- fastworkflow/examples/retail_workflow/_commands/get_product_details.py +12 -4
- fastworkflow/examples/retail_workflow/_commands/get_user_details.py +21 -5
- fastworkflow/examples/retail_workflow/_commands/list_all_product_types.py +4 -1
- fastworkflow/examples/retail_workflow/_commands/modify_pending_order_address.py +3 -0
- fastworkflow/examples/retail_workflow/_commands/modify_pending_order_items.py +12 -0
- fastworkflow/examples/retail_workflow/_commands/modify_pending_order_payment.py +7 -1
- fastworkflow/examples/retail_workflow/_commands/modify_user_address.py +3 -0
- fastworkflow/examples/retail_workflow/_commands/return_delivered_order_items.py +10 -1
- fastworkflow/examples/retail_workflow/_commands/transfer_to_human_agents.py +1 -1
- fastworkflow/examples/retail_workflow/tools/calculate.py +1 -1
- fastworkflow/mcp_server.py +52 -44
- fastworkflow/run/__main__.py +9 -5
- fastworkflow/run_agent/__main__.py +8 -8
- fastworkflow/run_agent/agent_module.py +6 -16
- fastworkflow/utils/command_dependency_graph.py +130 -143
- fastworkflow/utils/dspy_utils.py +11 -0
- fastworkflow/utils/signatures.py +7 -0
- fastworkflow/workflow_agent.py +186 -0
- {fastworkflow-2.13.5.dist-info → fastworkflow-2.14.1.dist-info}/METADATA +12 -3
- {fastworkflow-2.13.5.dist-info → fastworkflow-2.14.1.dist-info}/RECORD +41 -40
- fastworkflow/agent_integration.py +0 -239
- fastworkflow/examples/retail_workflow/_commands/parameter_dependency_graph.json +0 -36
- {fastworkflow-2.13.5.dist-info → fastworkflow-2.14.1.dist-info}/LICENSE +0 -0
- {fastworkflow-2.13.5.dist-info → fastworkflow-2.14.1.dist-info}/WHEEL +0 -0
- {fastworkflow-2.13.5.dist-info → fastworkflow-2.14.1.dist-info}/entry_points.txt +0 -0
fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py
CHANGED
|
@@ -41,7 +41,7 @@ class ResponseGenerator:
|
|
|
41
41
|
fully_qualified_command_names = (
|
|
42
42
|
set(cme_command_names) |
|
|
43
43
|
set(subject_crd.get_command_names(app_workflow.current_command_context_name))
|
|
44
|
-
)
|
|
44
|
+
)
|
|
45
45
|
|
|
46
46
|
valid_command_names = [
|
|
47
47
|
fully_qualified_command_name.split('/')[-1]
|
|
@@ -6,7 +6,7 @@ from fastworkflow.train.generate_synthetic import generate_diverse_utterances
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Signature: # noqa: D101
|
|
9
|
-
"""Change context to the parent of the current context."""
|
|
9
|
+
"""Change context to the parent of the current context. This could change the commands that are available."""
|
|
10
10
|
|
|
11
11
|
plain_utterances = [
|
|
12
12
|
"go up",
|
fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/reset_context.py
CHANGED
|
@@ -5,7 +5,7 @@ from fastworkflow.train.generate_synthetic import generate_diverse_utterances
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class Signature: # noqa: D101
|
|
8
|
-
"""Reset the current context to the global context (*)."""
|
|
8
|
+
"""Reset the current context to the global context (*). This could change the commands that are available."""
|
|
9
9
|
plain_utterances = [
|
|
10
10
|
"reset context",
|
|
11
11
|
"clear context",
|
fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py
CHANGED
|
@@ -2,15 +2,16 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Dict, Any
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
import json
|
|
8
8
|
|
|
9
9
|
import fastworkflow
|
|
10
|
-
|
|
11
|
-
from fastworkflow.command_routing import RoutingDefinition
|
|
12
10
|
from fastworkflow.train.generate_synthetic import generate_diverse_utterances
|
|
11
|
+
from fastworkflow.command_metadata_api import CommandMetadataAPI
|
|
12
|
+
|
|
13
13
|
class Signature:
|
|
14
|
+
"""List all the commands available in the current context along with their metadata"""
|
|
14
15
|
class Output(BaseModel):
|
|
15
16
|
valid_command_names: list[str]
|
|
16
17
|
|
|
@@ -38,154 +39,95 @@ class Signature:
|
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class ResponseGenerator:
|
|
41
|
-
def _get_enhanced_command_info(self, workflow: fastworkflow.Workflow) -> Dict[str, Any]:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if fq_cmd == "wildcard":
|
|
84
|
-
continue
|
|
85
|
-
|
|
86
|
-
# Check if command has utterances
|
|
87
|
-
utterance_meta = (
|
|
88
|
-
subject_crd.command_directory.get_utterance_metadata(fq_cmd) or
|
|
89
|
-
cme_crd.command_directory.get_utterance_metadata(fq_cmd)
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
if not utterance_meta:
|
|
93
|
-
continue
|
|
94
|
-
|
|
95
|
-
cmd_name = fq_cmd.split("/")[-1]
|
|
96
|
-
|
|
97
|
-
# Get command signature information if available
|
|
98
|
-
signature_info = {}
|
|
99
|
-
with contextlib.suppress(Exception):
|
|
100
|
-
# Try to get command module for signature extraction
|
|
101
|
-
cmd_module = None
|
|
102
|
-
try:
|
|
103
|
-
cmd_module = subject_crd.get_command_module(fq_cmd)
|
|
104
|
-
except Exception:
|
|
105
|
-
with contextlib.suppress(Exception):
|
|
106
|
-
cmd_module = cme_crd.get_command_module(fq_cmd)
|
|
107
|
-
|
|
108
|
-
if cmd_module and hasattr(cmd_module, 'Signature'):
|
|
109
|
-
sig_class = cmd_module.Signature
|
|
110
|
-
if hasattr(sig_class, 'Input'):
|
|
111
|
-
input_class = sig_class.Input
|
|
112
|
-
signature_info["inputs"] = [
|
|
113
|
-
{
|
|
114
|
-
"name": field_name,
|
|
115
|
-
"type": str(field_info.annotation),
|
|
116
|
-
"description": field_info.description or "",
|
|
117
|
-
"examples": getattr(field_info, 'examples', []),
|
|
118
|
-
"default": str(field_info.default) if field_info.default else None
|
|
119
|
-
}
|
|
120
|
-
for field_name, field_info in input_class.model_fields.items()
|
|
121
|
-
]
|
|
122
|
-
|
|
123
|
-
# Get plain utterances if available
|
|
124
|
-
if hasattr(sig_class, 'plain_utterances'):
|
|
125
|
-
signature_info["plain_utterances"] = sig_class.plain_utterances
|
|
126
|
-
# Get docstring from utterance metadata if available
|
|
127
|
-
docstring = ""
|
|
128
|
-
if hasattr(utterance_meta, 'docstring'):
|
|
129
|
-
docstring = utterance_meta.docstring or ""
|
|
130
|
-
|
|
131
|
-
commands.append({
|
|
132
|
-
"qualified_name": fq_cmd,
|
|
133
|
-
"name": cmd_name,
|
|
134
|
-
"signature_docstring": docstring,
|
|
135
|
-
**signature_info
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
"context": context_info,
|
|
140
|
-
"commands": sorted(commands, key=lambda x: x["name"])
|
|
141
|
-
}
|
|
42
|
+
# def _get_enhanced_command_info(self, workflow: fastworkflow.Workflow) -> Dict[str, Any]:
|
|
43
|
+
# """
|
|
44
|
+
# Get enhanced command information for agent mode.
|
|
45
|
+
# Returns structured JSON with context info, command details, etc.
|
|
46
|
+
# """
|
|
47
|
+
# app_workflow = workflow.context["app_workflow"]
|
|
48
|
+
|
|
49
|
+
# # Get the current context information
|
|
50
|
+
# context_info = {
|
|
51
|
+
# "name": app_workflow.current_command_context_name,
|
|
52
|
+
# "display_name": app_workflow.current_command_context_displayname,
|
|
53
|
+
# "description": "",
|
|
54
|
+
# "inheritance": [],
|
|
55
|
+
# "containment": []
|
|
56
|
+
# }
|
|
57
|
+
|
|
58
|
+
# # Try to load context hierarchy information if available
|
|
59
|
+
# with contextlib.suppress(Exception):
|
|
60
|
+
# inheritance_path = Path(app_workflow.folderpath) / "context_inheritance_model.json"
|
|
61
|
+
# if inheritance_path.exists():
|
|
62
|
+
# with open(inheritance_path) as f:
|
|
63
|
+
# inheritance_data = json.load(f)
|
|
64
|
+
# context_info["inheritance"] = inheritance_data.get(context_info["name"], [])
|
|
65
|
+
|
|
66
|
+
# containment_path = Path(app_workflow.folderpath) / "context_containment_model.json"
|
|
67
|
+
# if containment_path.exists():
|
|
68
|
+
# with open(containment_path) as f:
|
|
69
|
+
# containment_data = json.load(f)
|
|
70
|
+
# context_info["containment"] = containment_data.get(context_info["name"], [])
|
|
71
|
+
|
|
72
|
+
# from fastworkflow.command_metadata_api import CommandMetadataAPI
|
|
73
|
+
|
|
74
|
+
# meta = CommandMetadataAPI.get_enhanced_command_info(
|
|
75
|
+
# subject_workflow_path=app_workflow.folderpath,
|
|
76
|
+
# cme_workflow_path=workflow.folderpath,
|
|
77
|
+
# active_context_name=app_workflow.current_command_context_name,
|
|
78
|
+
# )
|
|
79
|
+
|
|
80
|
+
# return {
|
|
81
|
+
# "context": context_info,
|
|
82
|
+
# "commands": meta.get("commands", [])
|
|
83
|
+
# }
|
|
142
84
|
|
|
143
|
-
def _process_command(
|
|
144
|
-
|
|
145
|
-
) -> Signature.Output:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
85
|
+
# def _process_command(
|
|
86
|
+
# self, workflow: fastworkflow.Workflow
|
|
87
|
+
# ) -> Signature.Output:
|
|
88
|
+
# """
|
|
89
|
+
# Provides helpful information about this type of work-item.
|
|
90
|
+
# If the workitem_path is not provided, it provides information about the current work-item.
|
|
91
|
+
|
|
92
|
+
# :param input: The input parameters for the function.
|
|
93
|
+
# """
|
|
94
|
+
# app_workflow = workflow.context["app_workflow"]
|
|
95
|
+
# subject_crd = fastworkflow.RoutingRegistry.get_definition(
|
|
96
|
+
# app_workflow.folderpath)
|
|
155
97
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
98
|
+
# crd = fastworkflow.RoutingRegistry.get_definition(
|
|
99
|
+
# workflow.folderpath)
|
|
100
|
+
# cme_command_names = crd.get_command_names('IntentDetection')
|
|
101
|
+
|
|
102
|
+
# # ------------------------------------------------------------------
|
|
103
|
+
# # Build the union of command names that *should* be visible, then
|
|
104
|
+
# # filter out any command that (a) is the special wildcard helper or
|
|
105
|
+
# # (b) has no user-facing utterances in *any* command directory.
|
|
106
|
+
# # ------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
# candidate_commands: set[str] = (
|
|
109
|
+
# set(cme_command_names)
|
|
110
|
+
# | set(subject_crd.get_command_names(app_workflow.current_command_context_name))
|
|
111
|
+
# )
|
|
112
|
+
|
|
113
|
+
# def _has_utterances(fq_cmd: str) -> bool:
|
|
114
|
+
# """Return True if *fq_cmd* has at least one utterance definition in
|
|
115
|
+
# either the subject workflow or the CME workflow."""
|
|
116
|
+
# return (
|
|
117
|
+
# subject_crd.command_directory.get_utterance_metadata(fq_cmd) is not None
|
|
118
|
+
# or crd.command_directory.get_utterance_metadata(fq_cmd) is not None
|
|
119
|
+
# )
|
|
120
|
+
|
|
121
|
+
# visible_commands = [
|
|
122
|
+
# fq_cmd for fq_cmd in candidate_commands
|
|
123
|
+
# if fq_cmd != "wildcard" and _has_utterances(fq_cmd)
|
|
124
|
+
# ]
|
|
125
|
+
|
|
126
|
+
# valid_command_names = [
|
|
127
|
+
# cmd.split("/")[-1] for cmd in sorted(visible_commands)
|
|
128
|
+
# ]
|
|
129
|
+
|
|
130
|
+
# return Signature.Output(valid_command_names=valid_command_names)
|
|
189
131
|
|
|
190
132
|
def __call__(
|
|
191
133
|
self,
|
|
@@ -198,23 +140,13 @@ class ResponseGenerator:
|
|
|
198
140
|
if fastworkflow.chat_session:
|
|
199
141
|
is_agent_mode = fastworkflow.chat_session.run_as_agent
|
|
200
142
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
# Include the current context display name in the header so callers see
|
|
210
|
-
# which context is active (e.g., "TodoListManager" or "global/*").
|
|
211
|
-
app_workflow = workflow.context["app_workflow"]
|
|
212
|
-
context_name_for_display = app_workflow.current_command_context_displayname
|
|
213
|
-
response_body = "\n".join(output.valid_command_names)
|
|
214
|
-
response = (
|
|
215
|
-
f"Commands available in the current context: {context_name_for_display}\n"
|
|
216
|
-
f"{response_body}\n"
|
|
217
|
-
)
|
|
143
|
+
app_workflow = workflow.context["app_workflow"]
|
|
144
|
+
response = CommandMetadataAPI.get_command_display_text(
|
|
145
|
+
subject_workflow_path=app_workflow.folderpath,
|
|
146
|
+
cme_workflow_path=workflow.folderpath,
|
|
147
|
+
active_context_name=app_workflow.current_command_context_name,
|
|
148
|
+
for_agents=is_agent_mode,
|
|
149
|
+
)
|
|
218
150
|
|
|
219
151
|
return fastworkflow.CommandOutput(
|
|
220
152
|
workflow_id=workflow.id,
|
|
@@ -80,7 +80,7 @@ class CommandNamePrediction:
|
|
|
80
80
|
valid_command_names = (
|
|
81
81
|
set(cme_command_names) |
|
|
82
82
|
set(app_crd.get_command_names(command_context_name))
|
|
83
|
-
)
|
|
83
|
+
)
|
|
84
84
|
|
|
85
85
|
command_name_dict = {
|
|
86
86
|
fully_qualified_command_name.split('/')[-1]: fully_qualified_command_name
|
|
@@ -338,7 +338,11 @@ class ParameterExtraction:
|
|
|
338
338
|
|
|
339
339
|
stored_params = self._get_stored_parameters(self.cme_workflow)
|
|
340
340
|
|
|
341
|
-
|
|
341
|
+
self.command = self.command.replace(self.command_name, "").strip()
|
|
342
|
+
|
|
343
|
+
input_for_param_extraction = InputForParamExtraction.create(
|
|
344
|
+
self.app_workflow, self.command_name,
|
|
345
|
+
self.command)
|
|
342
346
|
|
|
343
347
|
if stored_params:
|
|
344
348
|
_, _, _, stored_missing_fields = self._extract_missing_fields(input_for_param_extraction, self.app_workflow, self.command_name, stored_params)
|
|
@@ -372,7 +376,7 @@ class ParameterExtraction:
|
|
|
372
376
|
if params_str := self._format_parameters_for_display(merged_params):
|
|
373
377
|
error_msg = f"Extracted parameters so far:\n{params_str}\n\n{error_msg}"
|
|
374
378
|
|
|
375
|
-
error_msg += "\nEnter 'abort'
|
|
379
|
+
error_msg += "\nEnter 'abort' to get out of this error state and/or execute a different command."
|
|
376
380
|
error_msg += "\nEnter 'you misunderstood' if the wrong command was executed."
|
|
377
381
|
return self.Output(
|
|
378
382
|
parameters_are_valid=False,
|