fastworkflow 2.15.7__tar.gz → 2.15.9__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.
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/PKG-INFO +7 -16
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/README.md +6 -15
- fastworkflow-2.15.9/fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +163 -0
- fastworkflow-2.15.9/fastworkflow/_workflows/command_metadata_extraction/intent_detection.py +360 -0
- fastworkflow-2.15.9/fastworkflow/_workflows/command_metadata_extraction/parameter_extraction.py +315 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/cli.py +4 -173
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/fastworkflow.env +1 -1
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/run/__main__.py +27 -31
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/signatures.py +1 -1
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/pyproject.toml +1 -1
- fastworkflow-2.15.7/fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +0 -797
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/LICENSE +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/.DS_Store +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_commands/.gitkeep +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/abort.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/go_up.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/reset_context.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_is_current_context.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/_workflows/command_metadata_extraction/command_context_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/__main__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/ast_class_extractor.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/class_analysis_structures.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/cli_specification.md +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/command_dependency_resolver.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/command_file_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/command_file_template.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/command_import_utils.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/command_stub_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/context_folder_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/context_model_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/dependency_manager.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/dir_scanner.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/documentation_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/genai_postprocessor.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/inheritance_block_regenerator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/libcst_transformers.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/navigator_stub_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/pydantic_model_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/build/utterance_generator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/cache_matching.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/chat_session.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/command_context_model.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/command_directory.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/command_executor.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/command_interfaces.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/command_metadata_api.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/command_routing.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/docs/context_modules_prd.txt +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/extended_workflow_example/README.md +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/extended_workflow_example/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/extended_workflow_example/_commands/generate_report.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/extended_workflow_example/_commands/startup.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/extended_workflow_example/simple_workflow_template.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/extended_workflow_example/workflow_inheritance_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/fastworkflow.passwords.env +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/hello_world/_commands/README.md +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/hello_world/_commands/add_two_numbers.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/hello_world/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/hello_world/application/add_two_numbers.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_1/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_1/_commands/send_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_1/application/send_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_2/_commands/User/send_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_2/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_2/_commands/startup.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_2/application/user.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_3/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_3/_commands/User/send_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_3/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_3/_commands/initialize_user.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_3/application/user.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/_ChatRoom.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/add_user.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/broadcast_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/get_current_user.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/list_users.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/set_current_user.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/_PremiumUser.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/User/_User.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/User/send_message.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/_commands/set_root_context.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/application/chatroom.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/application/user.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/context_hierarchy_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/messaging_app_4/startup_action.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/calculate.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/cancel_pending_order.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/exchange_delivered_order_items.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_email.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/get_order_details.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/get_product_details.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/get_user_details.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/list_all_product_types.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_address.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_items.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_payment.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/modify_user_address.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/return_delivered_order_items.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/_commands/transfer_to_human_agents.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/context_inheritance_model.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/retail_data/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/retail_data/orders.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/retail_data/products.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/retail_data/users.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/calculate.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/cancel_pending_order.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/exchange_delivered_order_items.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_email.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/get_order_details.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/get_product_details.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/get_user_details.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/list_all_product_types.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_address.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_items.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_payment.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/modify_user_address.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/return_delivered_order_items.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/think.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/tool.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/tools/transfer_to_human_agents.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/retail_workflow/workflow_description.txt +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/_WorkItem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/add_child_workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/go_to_workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/mark_as_complete.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_first_child_workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_last_child_workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_next_workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_previous_workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_all_child_workitems.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_child_workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/show_schema.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/_commands/startup.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/application/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/application/workitem.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/simple_workflow_template.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/examples/simple_workflow_template/startup_action.json +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/mcp_server.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/model_pipeline_training.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/refine/__main__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/run/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/run_agent/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/run_agent/__main__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/run_agent/agent_module.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/train/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/train/__main__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/train/generate_synthetic.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/user_message_queues.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/__init__.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/command_dependency_graph.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/context_utils.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/dspy_cache_utils.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/dspy_logger.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/dspy_utils.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/env.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/fuzzy_match.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/generate_param_examples.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/logging.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/parameterize_func_decorator.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/pydantic_model_2_dspy_signature_class.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/python_utils.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/react.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/utils/startup_progress.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/workflow.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/workflow_agent.py +0 -0
- {fastworkflow-2.15.7 → fastworkflow-2.15.9}/fastworkflow/workflow_inheritance_model.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastworkflow
|
|
3
|
-
Version: 2.15.
|
|
3
|
+
Version: 2.15.9
|
|
4
4
|
Summary: A framework for rapidly building large-scale, deterministic, interactive workflows with a fault-tolerant, conversational UX
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: fastworkflow,ai,workflow,llm,openai
|
|
@@ -50,7 +50,7 @@ While [DSPy](https://dspy.ai) ([Why DSPy](https://x.com/lateinteraction/status/1
|
|
|
50
50
|
|
|
51
51
|
### Why fastWorkflow?
|
|
52
52
|
|
|
53
|
-
- ✅ **Unlimited Tool Scaling**: fastworkflow
|
|
53
|
+
- ✅ **Unlimited Tool Scaling**: fastworkflow organizes tools into context hierarchies so use any number of tools without sacrificing performance or efficiency
|
|
54
54
|
- ✅ **Cost-Effective Performance**: fastWorkFlow with small, free models can match the quality of large expensive models
|
|
55
55
|
- ✅ **Reliable Tool Execution**: fastworkflow validation pipeline virtually eliminates incorrect tool calling or parameter extraction, ensuring a reliable tool response
|
|
56
56
|
- ✅ **Adaptive Learning**: 1-shot learning from intent detection mistakes. It learns your conversational vocabulary as you interact with it
|
|
@@ -202,14 +202,14 @@ LITELLM_API_KEY_PLANNER=your-mistral-api-key
|
|
|
202
202
|
LITELLM_API_KEY_AGENT=your-mistral-api-key
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
-
You can get a free API key from [Mistral AI](https://mistral.ai)
|
|
205
|
+
You can get a free API key from [Mistral AI](https://mistral.ai) for the mistral small model. Or a free API key from [OpenRouter](https://openrouter.ai/openai/gpt-oss-20b:free) for the GPT-OSS-20B:free model. You can use different models for different LLM roles in the same workflow if you wish.
|
|
206
206
|
|
|
207
207
|
### Step 3: Train the Example
|
|
208
208
|
|
|
209
209
|
Train the intent-detection models for the workflow:
|
|
210
210
|
|
|
211
211
|
```sh
|
|
212
|
-
fastworkflow examples
|
|
212
|
+
fastworkflow train ./examples/hello_world ./examples/fastworkflow.env ./examples/fastworkflow.passwords.env
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
This step builds the NLP models that help the workflow understand user commands.
|
|
@@ -219,7 +219,7 @@ This step builds the NLP models that help the workflow understand user commands.
|
|
|
219
219
|
Once training is complete, run the interactive assistant:
|
|
220
220
|
|
|
221
221
|
```sh
|
|
222
|
-
fastworkflow examples
|
|
222
|
+
fastworkflow run ./examples/hello_world ./examples/fastworkflow.env ./examples/fastworkflow.passwords.env
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
You will be greeted with a `User >` prompt. Try it out by asking "what can you do?" or "add 49 + 51"!
|
|
@@ -240,12 +240,6 @@ fastworkflow examples list
|
|
|
240
240
|
|
|
241
241
|
# Fetch an example to your local directory
|
|
242
242
|
fastworkflow examples fetch <example_name>
|
|
243
|
-
|
|
244
|
-
# Train an example workflow
|
|
245
|
-
fastworkflow examples train <example_name>
|
|
246
|
-
|
|
247
|
-
# Run an example workflow
|
|
248
|
-
fastworkflow examples run <example_name>
|
|
249
243
|
```
|
|
250
244
|
|
|
251
245
|
### Workflow Operations
|
|
@@ -261,11 +255,8 @@ fastworkflow train <workflow_dir> <env_file> <passwords_file>
|
|
|
261
255
|
fastworkflow run <workflow_dir> <env_file> <passwords_file>
|
|
262
256
|
```
|
|
263
257
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
```sh
|
|
267
|
-
fastworkflow run <workflow_dir> <env_file> <passwords_file> --run_as_agent
|
|
268
|
-
```
|
|
258
|
+
> [!tip]
|
|
259
|
+
> **Deterministic execution:** Prefix a natural language command with `/` to execute it deterministically (non‑agentic) during an interactive run.
|
|
269
260
|
|
|
270
261
|
Each command has additional options that can be viewed with the `--help` flag:
|
|
271
262
|
|
|
@@ -17,7 +17,7 @@ While [DSPy](https://dspy.ai) ([Why DSPy](https://x.com/lateinteraction/status/1
|
|
|
17
17
|
|
|
18
18
|
### Why fastWorkflow?
|
|
19
19
|
|
|
20
|
-
- ✅ **Unlimited Tool Scaling**: fastworkflow
|
|
20
|
+
- ✅ **Unlimited Tool Scaling**: fastworkflow organizes tools into context hierarchies so use any number of tools without sacrificing performance or efficiency
|
|
21
21
|
- ✅ **Cost-Effective Performance**: fastWorkFlow with small, free models can match the quality of large expensive models
|
|
22
22
|
- ✅ **Reliable Tool Execution**: fastworkflow validation pipeline virtually eliminates incorrect tool calling or parameter extraction, ensuring a reliable tool response
|
|
23
23
|
- ✅ **Adaptive Learning**: 1-shot learning from intent detection mistakes. It learns your conversational vocabulary as you interact with it
|
|
@@ -169,14 +169,14 @@ LITELLM_API_KEY_PLANNER=your-mistral-api-key
|
|
|
169
169
|
LITELLM_API_KEY_AGENT=your-mistral-api-key
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
You can get a free API key from [Mistral AI](https://mistral.ai)
|
|
172
|
+
You can get a free API key from [Mistral AI](https://mistral.ai) for the mistral small model. Or a free API key from [OpenRouter](https://openrouter.ai/openai/gpt-oss-20b:free) for the GPT-OSS-20B:free model. You can use different models for different LLM roles in the same workflow if you wish.
|
|
173
173
|
|
|
174
174
|
### Step 3: Train the Example
|
|
175
175
|
|
|
176
176
|
Train the intent-detection models for the workflow:
|
|
177
177
|
|
|
178
178
|
```sh
|
|
179
|
-
fastworkflow examples
|
|
179
|
+
fastworkflow train ./examples/hello_world ./examples/fastworkflow.env ./examples/fastworkflow.passwords.env
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
This step builds the NLP models that help the workflow understand user commands.
|
|
@@ -186,7 +186,7 @@ This step builds the NLP models that help the workflow understand user commands.
|
|
|
186
186
|
Once training is complete, run the interactive assistant:
|
|
187
187
|
|
|
188
188
|
```sh
|
|
189
|
-
fastworkflow examples
|
|
189
|
+
fastworkflow run ./examples/hello_world ./examples/fastworkflow.env ./examples/fastworkflow.passwords.env
|
|
190
190
|
```
|
|
191
191
|
|
|
192
192
|
You will be greeted with a `User >` prompt. Try it out by asking "what can you do?" or "add 49 + 51"!
|
|
@@ -207,12 +207,6 @@ fastworkflow examples list
|
|
|
207
207
|
|
|
208
208
|
# Fetch an example to your local directory
|
|
209
209
|
fastworkflow examples fetch <example_name>
|
|
210
|
-
|
|
211
|
-
# Train an example workflow
|
|
212
|
-
fastworkflow examples train <example_name>
|
|
213
|
-
|
|
214
|
-
# Run an example workflow
|
|
215
|
-
fastworkflow examples run <example_name>
|
|
216
210
|
```
|
|
217
211
|
|
|
218
212
|
### Workflow Operations
|
|
@@ -228,11 +222,8 @@ fastworkflow train <workflow_dir> <env_file> <passwords_file>
|
|
|
228
222
|
fastworkflow run <workflow_dir> <env_file> <passwords_file>
|
|
229
223
|
```
|
|
230
224
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
```sh
|
|
234
|
-
fastworkflow run <workflow_dir> <env_file> <passwords_file> --run_as_agent
|
|
235
|
-
```
|
|
225
|
+
> [!tip]
|
|
226
|
+
> **Deterministic execution:** Prefix a natural language command with `/` to execute it deterministically (non‑agentic) during an interactive run.
|
|
236
227
|
|
|
237
228
|
Each command has additional options that can be viewed with the `--help` flag:
|
|
238
229
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import fastworkflow
|
|
2
|
+
from fastworkflow import Action, CommandOutput, CommandResponse, NLUPipelineStage
|
|
3
|
+
from fastworkflow.command_executor import CommandExecutor
|
|
4
|
+
|
|
5
|
+
from ..intent_detection import CommandNamePrediction
|
|
6
|
+
from ..parameter_extraction import ParameterExtraction
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Signature:
|
|
10
|
+
plain_utterances = [
|
|
11
|
+
"3",
|
|
12
|
+
"france",
|
|
13
|
+
"16.7,.002",
|
|
14
|
+
"John Doe, 56, 281-995-6423",
|
|
15
|
+
"/path/to/my/object",
|
|
16
|
+
"id=3636",
|
|
17
|
+
"25.73 and Howard St",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def generate_utterances(workflow: fastworkflow.Workflow, command_name: str) -> list[str]:
|
|
22
|
+
return [
|
|
23
|
+
command_name.split('/')[-1].lower().replace('_', ' ')
|
|
24
|
+
] + Signature.plain_utterances
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ResponseGenerator:
|
|
28
|
+
def __call__(
|
|
29
|
+
self,
|
|
30
|
+
workflow: fastworkflow.Workflow,
|
|
31
|
+
command: str,
|
|
32
|
+
) -> CommandOutput: # sourcery skip: hoist-if-from-if
|
|
33
|
+
app_workflow = workflow.context["app_workflow"] # type: fastworkflow.Workflow
|
|
34
|
+
cmd_ctxt_obj_name = app_workflow.current_command_context_name
|
|
35
|
+
nlu_pipeline_stage = workflow.context.get(
|
|
36
|
+
"NLU_Pipeline_Stage",
|
|
37
|
+
NLUPipelineStage.INTENT_DETECTION)
|
|
38
|
+
|
|
39
|
+
predictor = CommandNamePrediction(workflow)
|
|
40
|
+
cnp_output = predictor.predict(cmd_ctxt_obj_name, command, nlu_pipeline_stage)
|
|
41
|
+
|
|
42
|
+
if cnp_output.error_msg:
|
|
43
|
+
workflow_context = workflow.context
|
|
44
|
+
workflow_context["NLU_Pipeline_Stage"] = NLUPipelineStage.INTENT_AMBIGUITY_CLARIFICATION
|
|
45
|
+
workflow_context["command"] = command
|
|
46
|
+
workflow.context = workflow_context
|
|
47
|
+
return CommandOutput(
|
|
48
|
+
command_responses=[
|
|
49
|
+
CommandResponse(
|
|
50
|
+
response=(
|
|
51
|
+
f"Ambiguous intent error for command '{command}'\n"
|
|
52
|
+
f"{cnp_output.error_msg}"
|
|
53
|
+
),
|
|
54
|
+
success=False
|
|
55
|
+
)
|
|
56
|
+
]
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
if nlu_pipeline_stage == NLUPipelineStage.INTENT_DETECTION and \
|
|
60
|
+
cnp_output.command_name != 'ErrorCorrection/you_misunderstood':
|
|
61
|
+
workflow_context = workflow.context
|
|
62
|
+
workflow_context["command"] = command
|
|
63
|
+
workflow.context = workflow_context
|
|
64
|
+
|
|
65
|
+
if cnp_output.is_cme_command:
|
|
66
|
+
workflow_context = workflow.context
|
|
67
|
+
if cnp_output.command_name == 'ErrorCorrection/you_misunderstood':
|
|
68
|
+
workflow_context["NLU_Pipeline_Stage"] = NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION
|
|
69
|
+
workflow_context["command"] = command
|
|
70
|
+
else:
|
|
71
|
+
workflow.end_command_processing()
|
|
72
|
+
workflow.context = workflow_context
|
|
73
|
+
|
|
74
|
+
startup_action = Action(
|
|
75
|
+
command_name=cnp_output.command_name,
|
|
76
|
+
command=command,
|
|
77
|
+
)
|
|
78
|
+
command_output = CommandExecutor.perform_action(workflow, startup_action)
|
|
79
|
+
command_output.command_responses[0].artifacts["command_handled"] = True
|
|
80
|
+
# Set the additional attributes
|
|
81
|
+
command_output.command_name = cnp_output.command_name
|
|
82
|
+
return command_output
|
|
83
|
+
|
|
84
|
+
if nlu_pipeline_stage in {
|
|
85
|
+
NLUPipelineStage.INTENT_DETECTION,
|
|
86
|
+
NLUPipelineStage.INTENT_AMBIGUITY_CLARIFICATION,
|
|
87
|
+
NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION
|
|
88
|
+
}:
|
|
89
|
+
app_workflow.command_context_for_response_generation = \
|
|
90
|
+
app_workflow.current_command_context
|
|
91
|
+
|
|
92
|
+
if cnp_output.command_name is None:
|
|
93
|
+
while not cnp_output.command_name and \
|
|
94
|
+
app_workflow.command_context_for_response_generation is not None and \
|
|
95
|
+
not app_workflow.is_command_context_for_response_generation_root:
|
|
96
|
+
app_workflow.command_context_for_response_generation = \
|
|
97
|
+
app_workflow.get_parent(app_workflow.command_context_for_response_generation)
|
|
98
|
+
cnp_output = predictor.predict(
|
|
99
|
+
fastworkflow.Workflow.get_command_context_name(app_workflow.command_context_for_response_generation),
|
|
100
|
+
command, nlu_pipeline_stage)
|
|
101
|
+
|
|
102
|
+
if cnp_output.command_name is None:
|
|
103
|
+
if nlu_pipeline_stage == NLUPipelineStage.INTENT_DETECTION:
|
|
104
|
+
# out of scope commands
|
|
105
|
+
workflow_context = workflow.context
|
|
106
|
+
workflow_context["NLU_Pipeline_Stage"] = \
|
|
107
|
+
NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION
|
|
108
|
+
workflow_context["command"] = command
|
|
109
|
+
workflow.context = workflow_context
|
|
110
|
+
|
|
111
|
+
startup_action = Action(
|
|
112
|
+
command_name='ErrorCorrection/you_misunderstood',
|
|
113
|
+
command=command,
|
|
114
|
+
)
|
|
115
|
+
command_output = CommandExecutor.perform_action(workflow, startup_action)
|
|
116
|
+
command_output.command_responses[0].artifacts["command_handled"] = True
|
|
117
|
+
return command_output
|
|
118
|
+
|
|
119
|
+
return CommandOutput(
|
|
120
|
+
command_responses=[
|
|
121
|
+
CommandResponse(
|
|
122
|
+
response=cnp_output.error_msg,
|
|
123
|
+
success=False
|
|
124
|
+
)
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# move to the parameter extraction stage
|
|
129
|
+
workflow_context = workflow.context
|
|
130
|
+
workflow_context["NLU_Pipeline_Stage"] = NLUPipelineStage.PARAMETER_EXTRACTION
|
|
131
|
+
workflow.context = workflow_context
|
|
132
|
+
|
|
133
|
+
command_name = cnp_output.command_name
|
|
134
|
+
extractor = ParameterExtraction(workflow, app_workflow, command_name, command)
|
|
135
|
+
pe_output = extractor.extract()
|
|
136
|
+
if not pe_output.parameters_are_valid:
|
|
137
|
+
return CommandOutput(
|
|
138
|
+
command_name = command_name,
|
|
139
|
+
command_responses=[
|
|
140
|
+
CommandResponse(
|
|
141
|
+
response=(
|
|
142
|
+
f"PARAMETER EXTRACTION ERROR FOR COMMAND '{command_name}'\n"
|
|
143
|
+
f"{pe_output.error_msg}"
|
|
144
|
+
),
|
|
145
|
+
success=False
|
|
146
|
+
)
|
|
147
|
+
]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
workflow.end_command_processing()
|
|
151
|
+
|
|
152
|
+
return CommandOutput(
|
|
153
|
+
command_responses=[
|
|
154
|
+
CommandResponse(
|
|
155
|
+
response="",
|
|
156
|
+
artifacts={
|
|
157
|
+
"command": command,
|
|
158
|
+
"command_name": command_name,
|
|
159
|
+
"cmd_parameters": pe_output.cmd_parameters,
|
|
160
|
+
},
|
|
161
|
+
)
|
|
162
|
+
]
|
|
163
|
+
)
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import os
|
|
3
|
+
from collections import Counter
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from speedict import Rdict
|
|
8
|
+
|
|
9
|
+
import fastworkflow
|
|
10
|
+
from fastworkflow.utils.logging import logger
|
|
11
|
+
from fastworkflow import NLUPipelineStage
|
|
12
|
+
from fastworkflow.cache_matching import cache_match, store_utterance_cache
|
|
13
|
+
from fastworkflow.model_pipeline_training import (
|
|
14
|
+
CommandRouter
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from fastworkflow.utils.fuzzy_match import find_best_matches
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CommandNamePrediction:
|
|
21
|
+
class Output(BaseModel):
|
|
22
|
+
command_name: Optional[str] = None
|
|
23
|
+
error_msg: Optional[str] = None
|
|
24
|
+
is_cme_command: bool = False
|
|
25
|
+
|
|
26
|
+
def __init__(self, cme_workflow: fastworkflow.Workflow):
|
|
27
|
+
self.cme_workflow = cme_workflow
|
|
28
|
+
self.app_workflow = cme_workflow.context["app_workflow"]
|
|
29
|
+
self.app_workflow_folderpath = self.app_workflow.folderpath
|
|
30
|
+
self.app_workflow_id = self.app_workflow.id
|
|
31
|
+
|
|
32
|
+
self.convo_path = os.path.join(self.app_workflow_folderpath, "___convo_info")
|
|
33
|
+
self.cache_path = self._get_cache_path(self.app_workflow_id, self.convo_path)
|
|
34
|
+
self.path = self._get_cache_path_cache(self.convo_path)
|
|
35
|
+
|
|
36
|
+
def predict(self, command_context_name: str, command: str, nlu_pipeline_stage: NLUPipelineStage) -> "CommandNamePrediction.Output":
|
|
37
|
+
# sourcery skip: extract-duplicate-method
|
|
38
|
+
|
|
39
|
+
model_artifact_path = f"{self.app_workflow_folderpath}/___command_info/{command_context_name}"
|
|
40
|
+
command_router = CommandRouter(model_artifact_path)
|
|
41
|
+
|
|
42
|
+
# Re-use the already-built ModelPipeline attached to the router
|
|
43
|
+
# instead of instantiating a fresh one. This avoids reloading HF
|
|
44
|
+
# checkpoints and transferring tensors each time we see a new
|
|
45
|
+
# message for the same context.
|
|
46
|
+
modelpipeline = command_router.modelpipeline
|
|
47
|
+
|
|
48
|
+
crd = fastworkflow.RoutingRegistry.get_definition(
|
|
49
|
+
self.cme_workflow.folderpath)
|
|
50
|
+
cme_command_names = crd.get_command_names('IntentDetection')
|
|
51
|
+
|
|
52
|
+
valid_command_names = set()
|
|
53
|
+
if nlu_pipeline_stage == NLUPipelineStage.INTENT_AMBIGUITY_CLARIFICATION:
|
|
54
|
+
valid_command_names = self._get_suggested_commands(self.path)
|
|
55
|
+
elif nlu_pipeline_stage in (
|
|
56
|
+
NLUPipelineStage.INTENT_DETECTION, NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION):
|
|
57
|
+
app_crd = fastworkflow.RoutingRegistry.get_definition(
|
|
58
|
+
self.app_workflow_folderpath)
|
|
59
|
+
valid_command_names = (
|
|
60
|
+
set(cme_command_names) |
|
|
61
|
+
set(app_crd.get_command_names(command_context_name))
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
command_name_dict = {
|
|
65
|
+
fully_qualified_command_name.split('/')[-1]: fully_qualified_command_name
|
|
66
|
+
for fully_qualified_command_name in valid_command_names
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if nlu_pipeline_stage == NLUPipelineStage.INTENT_AMBIGUITY_CLARIFICATION:
|
|
70
|
+
# what_can_i_do is special in INTENT_AMBIGUITY_CLARIFICATION
|
|
71
|
+
# We will not predict, just match plain utterances with exact or fuzzy match
|
|
72
|
+
command_name_dict |= {
|
|
73
|
+
plain_utterance: 'IntentDetection/what_can_i_do'
|
|
74
|
+
for plain_utterance in crd.command_directory.map_command_2_utterance_metadata[
|
|
75
|
+
'IntentDetection/what_can_i_do'
|
|
76
|
+
].plain_utterances
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if nlu_pipeline_stage != NLUPipelineStage.INTENT_DETECTION:
|
|
80
|
+
# abort is special.
|
|
81
|
+
# We will not predict, just match plain utterances with exact or fuzzy match
|
|
82
|
+
command_name_dict |= {
|
|
83
|
+
plain_utterance: 'ErrorCorrection/abort'
|
|
84
|
+
for plain_utterance in crd.command_directory.map_command_2_utterance_metadata[
|
|
85
|
+
'ErrorCorrection/abort'
|
|
86
|
+
].plain_utterances
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if nlu_pipeline_stage != NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION:
|
|
90
|
+
# you_misunderstood is special.
|
|
91
|
+
# We will not predict, just match plain utterances with exact or fuzzy match
|
|
92
|
+
command_name_dict |= {
|
|
93
|
+
plain_utterance: 'ErrorCorrection/you_misunderstood'
|
|
94
|
+
for plain_utterance in crd.command_directory.map_command_2_utterance_metadata[
|
|
95
|
+
'ErrorCorrection/you_misunderstood'
|
|
96
|
+
].plain_utterances
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# See if the command starts with a command name followed by a space
|
|
100
|
+
tentative_command_name = command.split(" ", 1)[0]
|
|
101
|
+
normalized_command_name = tentative_command_name.lower()
|
|
102
|
+
command_name = None
|
|
103
|
+
if normalized_command_name in command_name_dict:
|
|
104
|
+
command_name = normalized_command_name
|
|
105
|
+
command = command.replace(f"{tentative_command_name}", "").strip().replace(" ", " ")
|
|
106
|
+
else:
|
|
107
|
+
# Use Levenshtein distance for fuzzy matching with the full command part after @
|
|
108
|
+
best_matched_commands, _ = find_best_matches(
|
|
109
|
+
command.replace(" ", "_"),
|
|
110
|
+
command_name_dict.keys(),
|
|
111
|
+
threshold=0.3 # Adjust threshold as needed
|
|
112
|
+
)
|
|
113
|
+
if best_matched_commands:
|
|
114
|
+
command_name = best_matched_commands[0]
|
|
115
|
+
|
|
116
|
+
if nlu_pipeline_stage == NLUPipelineStage.INTENT_DETECTION:
|
|
117
|
+
if not command_name:
|
|
118
|
+
if cache_result := cache_match(self.path, command, modelpipeline, 0.85):
|
|
119
|
+
command_name = cache_result
|
|
120
|
+
else:
|
|
121
|
+
predictions=command_router.predict(command)
|
|
122
|
+
# predictions = majority_vote_predictions(command_router, command)
|
|
123
|
+
|
|
124
|
+
if len(predictions)==1:
|
|
125
|
+
command_name = predictions[0].split('/')[-1]
|
|
126
|
+
else:
|
|
127
|
+
# If confidence is low, treat as ambiguous command (type 1)
|
|
128
|
+
error_msg = self._formulate_ambiguous_command_error_message(
|
|
129
|
+
predictions, "run_as_agent" in self.app_workflow.context)
|
|
130
|
+
|
|
131
|
+
# Store suggested commands
|
|
132
|
+
self._store_suggested_commands(self.path, predictions, 1)
|
|
133
|
+
return CommandNamePrediction.Output(error_msg=error_msg)
|
|
134
|
+
|
|
135
|
+
elif nlu_pipeline_stage in (
|
|
136
|
+
NLUPipelineStage.INTENT_AMBIGUITY_CLARIFICATION,
|
|
137
|
+
NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION
|
|
138
|
+
) and not command_name:
|
|
139
|
+
command_name = "what_can_i_do"
|
|
140
|
+
|
|
141
|
+
if not command_name or command_name == "wildcard":
|
|
142
|
+
fully_qualified_command_name=None
|
|
143
|
+
is_cme_command=False
|
|
144
|
+
else:
|
|
145
|
+
fully_qualified_command_name = command_name_dict[command_name]
|
|
146
|
+
is_cme_command=(
|
|
147
|
+
fully_qualified_command_name in cme_command_names or
|
|
148
|
+
fully_qualified_command_name in crd.get_command_names('ErrorCorrection')
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
nlu_pipeline_stage
|
|
153
|
+
in (
|
|
154
|
+
NLUPipelineStage.INTENT_AMBIGUITY_CLARIFICATION,
|
|
155
|
+
NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION,
|
|
156
|
+
)
|
|
157
|
+
and not fully_qualified_command_name.endswith('abort')
|
|
158
|
+
and not fully_qualified_command_name.endswith('what_can_i_do')
|
|
159
|
+
and not fully_qualified_command_name.endswith('you_misunderstood')
|
|
160
|
+
):
|
|
161
|
+
command = self.cme_workflow.context["command"]
|
|
162
|
+
store_utterance_cache(self.path, command, command_name, modelpipeline)
|
|
163
|
+
|
|
164
|
+
return CommandNamePrediction.Output(
|
|
165
|
+
command_name=fully_qualified_command_name,
|
|
166
|
+
is_cme_command=is_cme_command
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def _get_cache_path(workflow_id, convo_path):
|
|
171
|
+
"""
|
|
172
|
+
Generate cache file path based on workflow ID
|
|
173
|
+
"""
|
|
174
|
+
base_dir = convo_path
|
|
175
|
+
# Create directory if it doesn't exist
|
|
176
|
+
os.makedirs(base_dir, exist_ok=True)
|
|
177
|
+
return os.path.join(base_dir, f"{workflow_id}.db")
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def _get_cache_path_cache(convo_path):
|
|
181
|
+
"""
|
|
182
|
+
Generate cache file path based on workflow ID
|
|
183
|
+
"""
|
|
184
|
+
base_dir = convo_path
|
|
185
|
+
# Create directory if it doesn't exist
|
|
186
|
+
os.makedirs(base_dir, exist_ok=True)
|
|
187
|
+
return os.path.join(base_dir, "cache.db")
|
|
188
|
+
|
|
189
|
+
# Store the suggested commands with the flag type
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _store_suggested_commands(cache_path, command_list, flag_type):
|
|
192
|
+
"""
|
|
193
|
+
Store the list of suggested commands for the constrained selection
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
cache_path: Path to the cache database
|
|
197
|
+
command_list: List of suggested commands
|
|
198
|
+
flag_type: Type of constraint (1=ambiguous, 2=misclassified)
|
|
199
|
+
"""
|
|
200
|
+
db = Rdict(cache_path)
|
|
201
|
+
try:
|
|
202
|
+
db["suggested_commands"] = command_list
|
|
203
|
+
db["flag_type"] = flag_type
|
|
204
|
+
finally:
|
|
205
|
+
db.close()
|
|
206
|
+
|
|
207
|
+
# Get the suggested commands
|
|
208
|
+
@staticmethod
|
|
209
|
+
def _get_suggested_commands(cache_path):
|
|
210
|
+
"""
|
|
211
|
+
Get the list of suggested commands for the constrained selection
|
|
212
|
+
"""
|
|
213
|
+
db = Rdict(cache_path)
|
|
214
|
+
try:
|
|
215
|
+
return db.get("suggested_commands", [])
|
|
216
|
+
finally:
|
|
217
|
+
db.close()
|
|
218
|
+
|
|
219
|
+
@staticmethod
|
|
220
|
+
def _get_count(cache_path):
|
|
221
|
+
db = Rdict(cache_path)
|
|
222
|
+
try:
|
|
223
|
+
return db.get("utterance_count", 0) # Default to 0 if key doesn't exist
|
|
224
|
+
finally:
|
|
225
|
+
db.close()
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def _print_db_contents(cache_path):
|
|
229
|
+
db = Rdict(cache_path)
|
|
230
|
+
try:
|
|
231
|
+
print("All keys in database:", list(db.keys()))
|
|
232
|
+
for key in db.keys():
|
|
233
|
+
print(f"Key: {key}, Value: {db[key]}")
|
|
234
|
+
finally:
|
|
235
|
+
db.close()
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def _store_utterance(cache_path, utterance, label):
|
|
239
|
+
"""
|
|
240
|
+
Store utterance in existing or new database
|
|
241
|
+
Returns: The utterance count used
|
|
242
|
+
"""
|
|
243
|
+
# Open the database (creates if doesn't exist)
|
|
244
|
+
db = Rdict(cache_path)
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
# Get existing counter or initialize to 0
|
|
248
|
+
utterance_count = db.get("utterance_count", 0)
|
|
249
|
+
|
|
250
|
+
# Create and store the utterance entry
|
|
251
|
+
utterance_data = {
|
|
252
|
+
"utterance": utterance,
|
|
253
|
+
"label": label
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
db[utterance_count] = utterance_data
|
|
257
|
+
|
|
258
|
+
# Increment and store the counter
|
|
259
|
+
utterance_count += 1
|
|
260
|
+
db["utterance_count"] = utterance_count
|
|
261
|
+
|
|
262
|
+
return utterance_count - 1 # Return the count used for this utterance
|
|
263
|
+
|
|
264
|
+
finally:
|
|
265
|
+
# Always close the database
|
|
266
|
+
db.close()
|
|
267
|
+
|
|
268
|
+
# Function to read from database
|
|
269
|
+
@staticmethod
|
|
270
|
+
def _read_utterance(cache_path, utterance_id):
|
|
271
|
+
"""
|
|
272
|
+
Read a specific utterance from the database
|
|
273
|
+
"""
|
|
274
|
+
db = Rdict(cache_path)
|
|
275
|
+
try:
|
|
276
|
+
return db.get(utterance_id)['utterance']
|
|
277
|
+
finally:
|
|
278
|
+
db.close()
|
|
279
|
+
|
|
280
|
+
@staticmethod
|
|
281
|
+
def _formulate_ambiguous_command_error_message(
|
|
282
|
+
route_choice_list: list[str], run_as_agent: bool) -> str:
|
|
283
|
+
command_list = (
|
|
284
|
+
"\n".join([
|
|
285
|
+
f"{route_choice.split('/')[-1].lower()}"
|
|
286
|
+
for route_choice in route_choice_list if route_choice != 'wildcard'
|
|
287
|
+
])
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
"The command is ambiguous. "
|
|
292
|
+
+ (
|
|
293
|
+
"Choose the correct command name from these possible options and update your command:\n"
|
|
294
|
+
if run_as_agent
|
|
295
|
+
else "Please choose a command name from these possible options:\n"
|
|
296
|
+
)
|
|
297
|
+
+ f"{command_list}\n\nor type 'what can i do' to see all commands\n"
|
|
298
|
+
+ ("or type 'abort' to cancel" if run_as_agent else '')
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# TODO - generation is deterministic. They all return the same answer
|
|
303
|
+
# TODO - Need 'temperature' for intent detection pipeline
|
|
304
|
+
def majority_vote_predictions(command_router, command: str, n_predictions: int = 5) -> list[str]:
|
|
305
|
+
"""
|
|
306
|
+
Generate N prediction sets in parallel and return the set that wins the majority vote.
|
|
307
|
+
|
|
308
|
+
This function improves prediction reliability by running multiple parallel predictions
|
|
309
|
+
and selecting the most common result through majority voting. This helps reduce
|
|
310
|
+
the impact of random variations in model predictions.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
command_router: The CommandRouter instance to use for predictions
|
|
314
|
+
command: The input command string
|
|
315
|
+
n_predictions: Number of parallel predictions to generate (default: 5)
|
|
316
|
+
Can be configured via N_PARALLEL_PREDICTIONS environment variable
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
The prediction set that received the majority vote. Falls back to a single
|
|
320
|
+
prediction if all parallel predictions fail.
|
|
321
|
+
|
|
322
|
+
Note:
|
|
323
|
+
Uses ThreadPoolExecutor with max_workers limited to min(n_predictions, 10)
|
|
324
|
+
to avoid overwhelming the system with too many concurrent threads.
|
|
325
|
+
"""
|
|
326
|
+
def get_single_prediction():
|
|
327
|
+
"""Helper function to get a single prediction"""
|
|
328
|
+
return command_router.predict(command)
|
|
329
|
+
|
|
330
|
+
# Generate N predictions in parallel
|
|
331
|
+
prediction_sets = []
|
|
332
|
+
with ThreadPoolExecutor(max_workers=min(n_predictions, 10)) as executor:
|
|
333
|
+
# Submit all prediction tasks
|
|
334
|
+
futures = [executor.submit(get_single_prediction) for _ in range(n_predictions)]
|
|
335
|
+
|
|
336
|
+
# Collect results as they complete
|
|
337
|
+
for future in as_completed(futures):
|
|
338
|
+
try:
|
|
339
|
+
prediction_set = future.result()
|
|
340
|
+
prediction_sets.append(prediction_set)
|
|
341
|
+
except Exception as e:
|
|
342
|
+
logger.warning(f"Prediction failed: {e}")
|
|
343
|
+
# Continue with other predictions even if one fails
|
|
344
|
+
|
|
345
|
+
if not prediction_sets:
|
|
346
|
+
# Fallback to single prediction if all parallel predictions failed
|
|
347
|
+
logger.warning("All parallel predictions failed, falling back to single prediction")
|
|
348
|
+
return command_router.predict(command)
|
|
349
|
+
|
|
350
|
+
# Convert lists to tuples so they can be hashed and counted
|
|
351
|
+
prediction_tuples = [tuple(sorted(pred_set)) for pred_set in prediction_sets]
|
|
352
|
+
|
|
353
|
+
# Count occurrences of each unique prediction set
|
|
354
|
+
vote_counts = Counter(prediction_tuples)
|
|
355
|
+
|
|
356
|
+
# Get the prediction set with the most votes
|
|
357
|
+
winning_tuple = vote_counts.most_common(1)[0][0]
|
|
358
|
+
|
|
359
|
+
# Convert back to list and return
|
|
360
|
+
return list(winning_tuple)
|