fastworkflow 2.17.7__tar.gz → 2.17.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.
Potentially problematic release.
This version of fastworkflow might be problematic. Click here for more details.
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/PKG-INFO +1 -1
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/parameter_extraction.py +81 -5
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/chat_session.py +14 -3
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/command_metadata_api.py +50 -0
- fastworkflow-2.17.9/fastworkflow/intent_clarification_agent.py +132 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/README.md +26 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/__main__.py +2 -1
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/jwt_manager.py +8 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/utils.py +29 -3
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/train/__main__.py +1 -1
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/react.py +13 -1
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/signatures.py +49 -31
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/workflow_agent.py +78 -4
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/pyproject.toml +1 -1
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/LICENSE +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/README.md +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/.DS_Store +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_commands/.gitkeep +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/abort.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/go_up.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/reset_context.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_is_current_context.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/command_context_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/_workflows/command_metadata_extraction/intent_detection.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/__main__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/ast_class_extractor.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/class_analysis_structures.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/cli_specification.md +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/command_dependency_resolver.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/command_file_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/command_file_template.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/command_import_utils.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/command_stub_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/context_folder_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/context_model_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/dependency_manager.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/dir_scanner.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/documentation_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/genai_postprocessor.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/inheritance_block_regenerator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/libcst_transformers.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/navigator_stub_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/pydantic_model_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/build/utterance_generator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/cache_matching.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/cli.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/command_context_model.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/command_directory.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/command_executor.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/command_interfaces.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/command_routing.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/docs/context_modules_prd.txt +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/extended_workflow_example/README.md +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/extended_workflow_example/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/extended_workflow_example/_commands/generate_report.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/extended_workflow_example/_commands/startup.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/extended_workflow_example/simple_workflow_template.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/extended_workflow_example/workflow_inheritance_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/fastworkflow.env +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/fastworkflow.passwords.env +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/hello_world/_commands/README.md +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/hello_world/_commands/add_two_numbers.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/hello_world/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/hello_world/application/add_two_numbers.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_1/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_1/_commands/send_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_1/application/send_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_2/_commands/User/send_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_2/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_2/_commands/startup.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_2/application/user.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_3/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_3/_commands/User/send_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_3/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_3/_commands/initialize_user.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_3/application/user.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/_ChatRoom.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/add_user.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/broadcast_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/get_current_user.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/list_users.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/set_current_user.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/_PremiumUser.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/User/_User.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/User/send_message.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/_commands/set_root_context.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/application/chatroom.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/application/user.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/context_hierarchy_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/messaging_app_4/startup_action.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/calculate.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/cancel_pending_order.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/exchange_delivered_order_items.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_email.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/get_order_details.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/get_product_details.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/get_user_details.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/list_all_product_types.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_address.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_items.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_payment.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/modify_user_address.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/return_delivered_order_items.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/_commands/transfer_to_human_agents.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/retail_data/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/retail_data/orders.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/retail_data/products.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/retail_data/users.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/calculate.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/cancel_pending_order.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/exchange_delivered_order_items.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_email.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/get_order_details.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/get_product_details.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/get_user_details.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/list_all_product_types.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_address.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_items.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_payment.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/modify_user_address.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/return_delivered_order_items.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/think.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/tool.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/tools/transfer_to_human_agents.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/retail_workflow/workflow_description.txt +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/_WorkItem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/add_child_workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/go_to_workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/mark_as_complete.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_first_child_workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_last_child_workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_next_workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_previous_workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_all_child_workitems.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_child_workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/show_schema.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/_commands/startup.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/application/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/application/workitem.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/simple_workflow_template.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/examples/simple_workflow_template/startup_action.json +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/mcp_server.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/model_pipeline_training.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/refine/__main__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run/__main__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/conversation_store.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/mcp_specific.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/run_fastapi_mcp/redoc_2_standalone_html.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/train/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/train/generate_synthetic.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/user_message_queues.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/__init__.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/chat_adapter.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/command_dependency_graph.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/context_utils.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/dspy_cache_utils.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/dspy_logger.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/dspy_utils.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/env.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/fuzzy_match.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/generate_param_examples.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/logging.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/parameterize_func_decorator.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/pydantic_model_2_dspy_signature_class.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/python_utils.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/utils/startup_progress.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.9}/fastworkflow/workflow.py +0 -0
- {fastworkflow-2.17.7 → fastworkflow-2.17.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.17.
|
|
3
|
+
Version: 2.17.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
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import sys
|
|
3
|
+
import re
|
|
3
4
|
from typing import Dict, List, Optional
|
|
4
5
|
|
|
5
6
|
from pydantic import BaseModel
|
|
7
|
+
from pydantic_core import PydanticUndefined
|
|
6
8
|
|
|
7
9
|
import fastworkflow
|
|
8
10
|
from fastworkflow.utils.logging import logger
|
|
@@ -59,11 +61,29 @@ class ParameterExtraction:
|
|
|
59
61
|
if stored_params:
|
|
60
62
|
new_params = self._extract_and_merge_missing_parameters(stored_params, self.command)
|
|
61
63
|
else:
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
self.
|
|
66
|
-
|
|
64
|
+
# Check if we're in agentic mode (not assistant mode command)
|
|
65
|
+
is_agentic_mode = (
|
|
66
|
+
"is_assistant_mode_command" not in self.cme_workflow.context
|
|
67
|
+
and "run_as_agent" in self.app_workflow.context
|
|
68
|
+
and self.app_workflow.context["run_as_agent"]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if is_agentic_mode:
|
|
72
|
+
# Try regex-based extraction first in agentic mode
|
|
73
|
+
new_params = self._extract_parameters_from_xml(self.command, command_parameters_class)
|
|
74
|
+
|
|
75
|
+
# If regex extraction fails, fall back to LLM-based extraction
|
|
76
|
+
if new_params is None:
|
|
77
|
+
new_params = input_for_param_extraction.extract_parameters(
|
|
78
|
+
command_parameters_class,
|
|
79
|
+
self.command_name,
|
|
80
|
+
app_workflow_folderpath)
|
|
81
|
+
else:
|
|
82
|
+
# Use LLM-based extraction for assistant mode
|
|
83
|
+
new_params = input_for_param_extraction.extract_parameters(
|
|
84
|
+
command_parameters_class,
|
|
85
|
+
self.command_name,
|
|
86
|
+
app_workflow_folderpath)
|
|
67
87
|
|
|
68
88
|
is_valid, error_msg, suggestions, missing_invalid_fields = \
|
|
69
89
|
input_for_param_extraction.validate_parameters(
|
|
@@ -272,6 +292,62 @@ class ParameterExtraction:
|
|
|
272
292
|
# Construct model without validation
|
|
273
293
|
return default_params.__class__.model_construct(**params_data)
|
|
274
294
|
|
|
295
|
+
@staticmethod
|
|
296
|
+
def _extract_parameters_from_xml(command: str, command_parameters_class: type[BaseModel]) -> Optional[BaseModel]:
|
|
297
|
+
"""
|
|
298
|
+
Extract parameters from XML-formatted command using regex.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
BaseModel instance with extracted parameters, or None if parsing fails
|
|
302
|
+
"""
|
|
303
|
+
field_names = list(command_parameters_class.model_fields.keys())
|
|
304
|
+
|
|
305
|
+
# If no parameters are defined, return empty model immediately
|
|
306
|
+
if not field_names:
|
|
307
|
+
return command_parameters_class.model_construct()
|
|
308
|
+
|
|
309
|
+
extracted_data = {}
|
|
310
|
+
|
|
311
|
+
# Try to extract each parameter using XML tags
|
|
312
|
+
for field_name in field_names:
|
|
313
|
+
# Look for <field_name>value</field_name> pattern
|
|
314
|
+
pattern = rf'<{re.escape(field_name)}>(.+?)</{re.escape(field_name)}>'
|
|
315
|
+
if match := re.search(pattern, command, re.DOTALL):
|
|
316
|
+
parameter_value = match[1].strip()
|
|
317
|
+
extracted_data[field_name] = parameter_value
|
|
318
|
+
|
|
319
|
+
# Check if we extracted values for ALL fields (safest criteria for LLM fallback)
|
|
320
|
+
all_fields_extracted = len(extracted_data) == len(field_names)
|
|
321
|
+
|
|
322
|
+
# Check if agent used example values
|
|
323
|
+
if all_fields_extracted:
|
|
324
|
+
for field_name, extracted_value in extracted_data.items():
|
|
325
|
+
field_info = command_parameters_class.model_fields[field_name]
|
|
326
|
+
examples = getattr(field_info, "examples", None)
|
|
327
|
+
if examples and extracted_value in examples:
|
|
328
|
+
all_fields_extracted = False
|
|
329
|
+
break
|
|
330
|
+
|
|
331
|
+
if all_fields_extracted:
|
|
332
|
+
# Initialize all fields with their default values (if they exist) or None
|
|
333
|
+
params_data = {}
|
|
334
|
+
for field_name in field_names:
|
|
335
|
+
field_info = command_parameters_class.model_fields[field_name]
|
|
336
|
+
if field_info.default is not PydanticUndefined:
|
|
337
|
+
params_data[field_name] = field_info.default
|
|
338
|
+
elif field_info.default_factory is not None:
|
|
339
|
+
params_data[field_name] = field_info.default_factory()
|
|
340
|
+
else:
|
|
341
|
+
params_data[field_name] = None
|
|
342
|
+
|
|
343
|
+
# Update with extracted values
|
|
344
|
+
params_data |= extracted_data
|
|
345
|
+
|
|
346
|
+
# Construct model without validation
|
|
347
|
+
return command_parameters_class.model_construct(**params_data)
|
|
348
|
+
|
|
349
|
+
return None
|
|
350
|
+
|
|
275
351
|
@staticmethod
|
|
276
352
|
def _extract_and_merge_missing_parameters(stored_params: BaseModel, command: str):
|
|
277
353
|
"""
|
|
@@ -133,7 +133,8 @@ class ChatSession:
|
|
|
133
133
|
|
|
134
134
|
# Initialize agent-related attributes
|
|
135
135
|
self._run_as_agent = run_as_agent
|
|
136
|
-
self._workflow_tool_agent = None
|
|
136
|
+
self._workflow_tool_agent = None
|
|
137
|
+
self._intent_clarification_agent = None
|
|
137
138
|
|
|
138
139
|
# Create the command metadata extraction workflow with a unique ID
|
|
139
140
|
self._cme_workflow = fastworkflow.Workflow.create(
|
|
@@ -272,13 +273,23 @@ class ChatSession:
|
|
|
272
273
|
self._current_workflow.context["run_as_agent"] = True
|
|
273
274
|
|
|
274
275
|
# Initialize the workflow tool agent
|
|
275
|
-
from fastworkflow.workflow_agent import initialize_workflow_tool_agent
|
|
276
|
+
from fastworkflow.workflow_agent import initialize_workflow_tool_agent
|
|
276
277
|
self._workflow_tool_agent = initialize_workflow_tool_agent(self)
|
|
277
278
|
|
|
279
|
+
# Initialize the intent clarification agent
|
|
280
|
+
from fastworkflow.intent_clarification_agent import initialize_intent_clarification_agent
|
|
281
|
+
self._intent_clarification_agent = initialize_intent_clarification_agent(self)
|
|
282
|
+
|
|
278
283
|
@property
|
|
279
284
|
def workflow_tool_agent(self):
|
|
280
285
|
"""Get the workflow tool agent for agent mode."""
|
|
281
286
|
return self._workflow_tool_agent
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def intent_clarification_agent(self):
|
|
290
|
+
"""Get the intent clarification agent for agent mode."""
|
|
291
|
+
return self._intent_clarification_agent
|
|
292
|
+
|
|
282
293
|
@property
|
|
283
294
|
def cme_workflow(self) -> fastworkflow.Workflow:
|
|
284
295
|
"""Get the command metadata extraction workflow."""
|
|
@@ -647,7 +658,7 @@ class ChatSession:
|
|
|
647
658
|
def _extract_conversation_summary(self,
|
|
648
659
|
user_query: str, workflow_actions: list[dict[str, str]], final_agent_response: str) -> str:
|
|
649
660
|
"""
|
|
650
|
-
Summarizes conversation based on original user query, workflow actions and
|
|
661
|
+
Summarizes conversation based on original user query, workflow actions and agent response.
|
|
651
662
|
Returns the conversation summary and the log entry
|
|
652
663
|
"""
|
|
653
664
|
# Lets log everything to a file called action_log.jsonl, if it exists
|
|
@@ -613,6 +613,56 @@ class CommandMetadataAPI:
|
|
|
613
613
|
|
|
614
614
|
return "\n".join(combined_lines)
|
|
615
615
|
|
|
616
|
+
@staticmethod
|
|
617
|
+
def get_suggested_commands_metadata(
|
|
618
|
+
subject_workflow_path: str,
|
|
619
|
+
cme_workflow_path: str,
|
|
620
|
+
active_context_name: str,
|
|
621
|
+
suggested_command_names: list[str],
|
|
622
|
+
for_agents: bool = False,
|
|
623
|
+
) -> str:
|
|
624
|
+
"""
|
|
625
|
+
Get metadata display text for ONLY the suggested commands.
|
|
626
|
+
|
|
627
|
+
Args:
|
|
628
|
+
subject_workflow_path: Path to the subject workflow
|
|
629
|
+
cme_workflow_path: Path to the CME workflow
|
|
630
|
+
active_context_name: Active context name
|
|
631
|
+
suggested_command_names: List of suggested command names (can be short names or qualified)
|
|
632
|
+
for_agents: Include agent-specific fields
|
|
633
|
+
|
|
634
|
+
Returns:
|
|
635
|
+
YAML-like formatted string with metadata for suggested commands only
|
|
636
|
+
"""
|
|
637
|
+
if not suggested_command_names:
|
|
638
|
+
return "No suggested commands provided."
|
|
639
|
+
|
|
640
|
+
parts: list[str] = []
|
|
641
|
+
for suggested_cmd in suggested_command_names:
|
|
642
|
+
# Try to get metadata for this command
|
|
643
|
+
# The command name could be qualified (Context/command) or short (command)
|
|
644
|
+
if part := CommandMetadataAPI.get_command_display_text_for_command(
|
|
645
|
+
subject_workflow_path=subject_workflow_path,
|
|
646
|
+
cme_workflow_path=cme_workflow_path,
|
|
647
|
+
active_context_name=active_context_name,
|
|
648
|
+
qualified_command_name=suggested_cmd,
|
|
649
|
+
for_agents=for_agents,
|
|
650
|
+
):
|
|
651
|
+
parts.append(part)
|
|
652
|
+
|
|
653
|
+
if not parts:
|
|
654
|
+
return "No metadata available for suggested commands."
|
|
655
|
+
|
|
656
|
+
# Combine all parts with header
|
|
657
|
+
combined_lines: list[str] = ["Suggested commands with metadata:"]
|
|
658
|
+
for idx, text in enumerate(parts):
|
|
659
|
+
lines = text.splitlines()
|
|
660
|
+
if idx > 0:
|
|
661
|
+
combined_lines.append("") # Blank line separator
|
|
662
|
+
combined_lines.extend(lines)
|
|
663
|
+
|
|
664
|
+
return "\n".join(combined_lines)
|
|
665
|
+
|
|
616
666
|
@staticmethod
|
|
617
667
|
def get_command_display_text_for_command(
|
|
618
668
|
subject_workflow_path: str,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Intent detection error state agent module for fastWorkflow.
|
|
3
|
+
Specialized agent for handling intent detection errors.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import dspy
|
|
8
|
+
|
|
9
|
+
import fastworkflow
|
|
10
|
+
from fastworkflow.utils.react import fastWorkflowReAct
|
|
11
|
+
from fastworkflow.command_metadata_api import CommandMetadataAPI
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class IntentClarificationAgentSignature(dspy.Signature):
|
|
15
|
+
"""
|
|
16
|
+
Handle intent detection errors by clarifying user intent.
|
|
17
|
+
You are provided with:
|
|
18
|
+
1. The workflow agent's inputs and trajectory - showing what the agent has been trying to do
|
|
19
|
+
2. Suggested commands metadata (for intent ambiguity) or empty (for intent misunderstanding - use show_available_commands tool)
|
|
20
|
+
|
|
21
|
+
Review the agent trajectory to understand the context and what led to this error.
|
|
22
|
+
If suggested_commands_metadata is provided, review it carefully to understand each command's purpose, parameters, and usage.
|
|
23
|
+
If suggested_commands_metadata is empty, use the show_available_commands tool to get the full list of available commands.
|
|
24
|
+
IMPORTANT: When clarifying intent, preserve ALL parameters from the original command.
|
|
25
|
+
Use available tools to resolve ambiguous or misunderstood commands. Use the ask_user tool ONLY as a last resort. Return the complete clarified command with the correct name and all original parameters.
|
|
26
|
+
"""
|
|
27
|
+
original_command = dspy.InputField(desc="The original command with all parameters that caused the error.")
|
|
28
|
+
error_message = dspy.InputField(desc="The intent detection error message from the workflow.")
|
|
29
|
+
agent_inputs = dspy.InputField(desc="The original inputs to the workflow agent.")
|
|
30
|
+
agent_trajectory = dspy.InputField(desc="The workflow agent's trajectory showing all actions taken so far leading to this error.")
|
|
31
|
+
suggested_commands_metadata = dspy.InputField(desc="Metadata for suggested commands (for ambiguity clarification), or empty string (for misunderstanding - use show_available_commands tool instead).")
|
|
32
|
+
clarified_command = dspy.OutputField(desc="The complete command with correct command name AND all original parameters preserved.")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _show_available_commands(chat_session: fastworkflow.ChatSession) -> str:
|
|
36
|
+
"""
|
|
37
|
+
Show available commands to help resolve intent detection errors.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
chat_session: The chat session instance
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List of available commands
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
current_workflow = chat_session.get_active_workflow()
|
|
47
|
+
return CommandMetadataAPI.get_command_display_text(
|
|
48
|
+
subject_workflow_path=current_workflow.folderpath,
|
|
49
|
+
cme_workflow_path=fastworkflow.get_internal_workflow_path("command_metadata_extraction"),
|
|
50
|
+
active_context_name=current_workflow.current_command_context_name,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _ask_user_for_clarification(
|
|
55
|
+
clarification_request: str,
|
|
56
|
+
chat_session: fastworkflow.ChatSession
|
|
57
|
+
) -> str:
|
|
58
|
+
"""
|
|
59
|
+
Ask user for clarification when intent is unclear.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
clarification_request: The question to ask the user
|
|
63
|
+
chat_session: The chat session instance
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
User's response
|
|
67
|
+
"""
|
|
68
|
+
command_output = fastworkflow.CommandOutput(
|
|
69
|
+
command_responses=[fastworkflow.CommandResponse(response=clarification_request)],
|
|
70
|
+
workflow_name=chat_session.get_active_workflow().folderpath.split('/')[-1]
|
|
71
|
+
)
|
|
72
|
+
chat_session.command_output_queue.put(command_output)
|
|
73
|
+
|
|
74
|
+
user_response = chat_session.user_message_queue.get()
|
|
75
|
+
|
|
76
|
+
# Log to action.jsonl (shared with main agent)
|
|
77
|
+
with open("action.jsonl", "a", encoding="utf-8") as f:
|
|
78
|
+
agent_user_dialog = {
|
|
79
|
+
"intent_clarification_agent": True,
|
|
80
|
+
"agent_query": clarification_request,
|
|
81
|
+
"user_response": user_response
|
|
82
|
+
}
|
|
83
|
+
f.write(json.dumps(agent_user_dialog, ensure_ascii=False) + "\n")
|
|
84
|
+
|
|
85
|
+
return user_response
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def initialize_intent_clarification_agent(
|
|
89
|
+
chat_session: fastworkflow.ChatSession,
|
|
90
|
+
max_iters: int = 20
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
Initialize a specialized agent for handling intent detection errors.
|
|
94
|
+
This agent has a limited tool set and shares traces with the main execution agent.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
chat_session: The chat session instance
|
|
98
|
+
max_iters: Maximum iterations for the agent (default: 10)
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
DSPy ReAct agent configured for intent detection error handling
|
|
102
|
+
"""
|
|
103
|
+
if not chat_session:
|
|
104
|
+
raise ValueError("chat_session cannot be null")
|
|
105
|
+
|
|
106
|
+
def show_available_commands() -> str:
|
|
107
|
+
"""
|
|
108
|
+
Show all available commands to help resolve intent ambiguity.
|
|
109
|
+
"""
|
|
110
|
+
return _show_available_commands(chat_session)
|
|
111
|
+
|
|
112
|
+
def ask_user(clarification_request: str) -> str:
|
|
113
|
+
"""
|
|
114
|
+
Ask the user for clarification when the intent is unclear.
|
|
115
|
+
Use this as a last resort when you cannot determine the correct command.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
clarification_request: Clear question to ask the user
|
|
119
|
+
"""
|
|
120
|
+
return _ask_user_for_clarification(clarification_request, chat_session)
|
|
121
|
+
|
|
122
|
+
# Limited tool set for intent detection errors
|
|
123
|
+
tools = [
|
|
124
|
+
show_available_commands,
|
|
125
|
+
ask_user,
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
return fastWorkflowReAct(
|
|
129
|
+
IntentClarificationAgentSignature,
|
|
130
|
+
tools=tools,
|
|
131
|
+
max_iters=max_iters,
|
|
132
|
+
)
|
|
@@ -82,6 +82,32 @@ uvicorn services.run_fastapi.main:app --workflow_path /path/to/workflow --expect
|
|
|
82
82
|
- Tokens without valid signatures are rejected
|
|
83
83
|
- Recommended for production deployments in untrusted environments
|
|
84
84
|
|
|
85
|
+
### Token Access in Workflow Context
|
|
86
|
+
|
|
87
|
+
JWT tokens are automatically passed to workflows via the `workflow_context` parameter as `http_bearer_token`. This allows workflows to access the bearer token for making authenticated API calls or forwarding authentication.
|
|
88
|
+
|
|
89
|
+
**Important notes:**
|
|
90
|
+
- The token is **only available to authenticated endpoints** (those using `get_session_and_ensure_runtime` dependency)
|
|
91
|
+
- The token is stored in the workflow context dictionary under the key `http_bearer_token`
|
|
92
|
+
- Token is **automatically updated** on every authenticated request, ensuring workflows always have the current valid token
|
|
93
|
+
- Token expiration is **automatically verified** by `verify_token()` in both secure mode (`--expect_encrypted_jwt` flag) and trusted network mode
|
|
94
|
+
- In secure mode: Full cryptographic signature verification + expiration checking
|
|
95
|
+
- In trusted network mode: Expiration checking is performed (signature verification disabled)
|
|
96
|
+
- Tokens should be treated as sensitive data and handled securely in workflows
|
|
97
|
+
- The `/initialize` endpoint is unauthenticated and does NOT provide a token to the workflow context; tokens are only available after calling `/initialize` and using the returned token in subsequent requests
|
|
98
|
+
|
|
99
|
+
**Example usage in workflow:**
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
# In workflow code
|
|
103
|
+
workflow_context = self._context # Gets the workflow_context
|
|
104
|
+
bearer_token = workflow_context.get('http_bearer_token')
|
|
105
|
+
|
|
106
|
+
# Use token for API calls
|
|
107
|
+
headers = {"Authorization": f"Bearer {bearer_token}"}
|
|
108
|
+
response = requests.get("https://api.example.com/data", headers=headers)
|
|
109
|
+
```
|
|
110
|
+
|
|
85
111
|
## API Endpoints (REST)
|
|
86
112
|
|
|
87
113
|
### `POST /initialize`
|
|
@@ -126,7 +126,8 @@ async def get_session_and_ensure_runtime(
|
|
|
126
126
|
workflow_path=ARGS.workflow_path,
|
|
127
127
|
context=json.loads(ARGS.context) if ARGS.context else None,
|
|
128
128
|
startup_command=ARGS.startup_command,
|
|
129
|
-
startup_action=fastworkflow.Action(**json.loads(ARGS.startup_action)) if ARGS.startup_action else None
|
|
129
|
+
startup_action=fastworkflow.Action(**json.loads(ARGS.startup_action)) if ARGS.startup_action else None,
|
|
130
|
+
http_bearer_token=session.http_bearer_token
|
|
130
131
|
)
|
|
131
132
|
|
|
132
133
|
return session
|
|
@@ -269,6 +269,14 @@ def verify_token(token: str, expected_type: str = "access") -> dict:
|
|
|
269
269
|
logger.warning(f"Token decoding failed: {e}")
|
|
270
270
|
raise JWTError(f"Failed to decode token: {e}") from e
|
|
271
271
|
|
|
272
|
+
# Manually check expiration even in unverified mode
|
|
273
|
+
if exp_timestamp := payload.get("exp"):
|
|
274
|
+
import time
|
|
275
|
+
current_time = int(time.time())
|
|
276
|
+
if exp_timestamp < current_time:
|
|
277
|
+
logger.warning(f"Token expired: exp={exp_timestamp}, now={current_time}")
|
|
278
|
+
raise JWTError("Token has expired")
|
|
279
|
+
|
|
272
280
|
# Validate token type for consistency (outside try-except to allow JWTError to propagate)
|
|
273
281
|
if payload.get("type") != expected_type:
|
|
274
282
|
raise JWTError(f"Invalid token type: expected {expected_type}, got {payload.get('type')}")
|
|
@@ -42,6 +42,7 @@ class SessionData(BaseModel):
|
|
|
42
42
|
issued_at: int # Unix timestamp
|
|
43
43
|
expires_at: int # Unix timestamp
|
|
44
44
|
jti: str # JWT ID (unique token identifier)
|
|
45
|
+
http_bearer_token: Optional[str] = None # The actual JWT token string for workflow context access
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
class InvokeRequest(BaseModel):
|
|
@@ -174,13 +175,14 @@ def get_session_from_jwt(
|
|
|
174
175
|
try:
|
|
175
176
|
payload = verify_token(token, expected_type="access")
|
|
176
177
|
|
|
177
|
-
# Extract session data from payload
|
|
178
|
+
# Extract session data from payload, including the token for workflow context
|
|
178
179
|
return SessionData(
|
|
179
180
|
user_id=payload["sub"],
|
|
180
181
|
token_type=payload["type"],
|
|
181
182
|
issued_at=payload["iat"],
|
|
182
183
|
expires_at=payload["exp"],
|
|
183
|
-
jti=payload["jti"]
|
|
184
|
+
jti=payload["jti"],
|
|
185
|
+
http_bearer_token=token # Store the actual token for workflow access
|
|
184
186
|
)
|
|
185
187
|
|
|
186
188
|
except JWTError as e:
|
|
@@ -204,7 +206,8 @@ async def ensure_user_runtime_exists(
|
|
|
204
206
|
context: Optional[dict] = None,
|
|
205
207
|
startup_command: Optional[str] = None,
|
|
206
208
|
startup_action: Optional['fastworkflow.Action'] = None,
|
|
207
|
-
stream_format: str = "ndjson"
|
|
209
|
+
stream_format: str = "ndjson",
|
|
210
|
+
http_bearer_token: Optional[str] = None
|
|
208
211
|
) -> None:
|
|
209
212
|
"""
|
|
210
213
|
Ensure a user runtime exists in the session manager. If not, create it.
|
|
@@ -220,6 +223,7 @@ async def ensure_user_runtime_exists(
|
|
|
220
223
|
startup_command: Optional startup command
|
|
221
224
|
startup_action: Optional startup action
|
|
222
225
|
stream_format: Stream format preference ("ndjson" or "sse", default "ndjson")
|
|
226
|
+
http_bearer_token: Optional JWT token to update in workflow context
|
|
223
227
|
|
|
224
228
|
Raises:
|
|
225
229
|
HTTPException: If session creation fails
|
|
@@ -228,8 +232,30 @@ async def ensure_user_runtime_exists(
|
|
|
228
232
|
existing_runtime = await session_manager.get_session(user_id)
|
|
229
233
|
if existing_runtime:
|
|
230
234
|
logger.debug(f"Session for user_id {user_id} already exists, skipping creation")
|
|
235
|
+
|
|
236
|
+
# Update the workflow's context with the current token if provided
|
|
237
|
+
if http_bearer_token and existing_runtime.chat_session:
|
|
238
|
+
active_workflow = existing_runtime.chat_session.get_active_workflow()
|
|
239
|
+
if active_workflow and active_workflow.context:
|
|
240
|
+
# Update the workflow's context with the current token
|
|
241
|
+
# Note: We mutate the dictionary in-place (no setter call), which means:
|
|
242
|
+
# 1. The change is immediate and visible to workflow code
|
|
243
|
+
# 2. The workflow is NOT marked dirty (won't persist to disk)
|
|
244
|
+
# 3. This is intentional for JWT tokens - we don't want to persist sensitive tokens
|
|
245
|
+
active_workflow.context['http_bearer_token'] = http_bearer_token
|
|
246
|
+
logger.debug(f"Updated http_bearer_token in workflow context for user_id {user_id}")
|
|
247
|
+
|
|
231
248
|
return
|
|
232
249
|
|
|
250
|
+
# Prepare workflow context, ensuring http_bearer_token is available
|
|
251
|
+
if http_bearer_token:
|
|
252
|
+
if context:
|
|
253
|
+
# Add or replace http_bearer_token in the context
|
|
254
|
+
context['http_bearer_token'] = http_bearer_token
|
|
255
|
+
else:
|
|
256
|
+
# Initialize context with http_bearer_token
|
|
257
|
+
context = {'http_bearer_token': http_bearer_token}
|
|
258
|
+
|
|
233
259
|
logger.info(f"Creating new session for user_id: {user_id}")
|
|
234
260
|
|
|
235
261
|
# Resolve conversation store base folder from SPEEDDICT_FOLDERNAME/user_conversations
|
|
@@ -114,7 +114,7 @@ def _get_commands_with_parameters(json_path):
|
|
|
114
114
|
command_directory = json.load(f)
|
|
115
115
|
|
|
116
116
|
# Extract the command metadata
|
|
117
|
-
commands_metadata = command_directory.get("
|
|
117
|
+
commands_metadata = command_directory.get("map_command_2_metadata", {})
|
|
118
118
|
|
|
119
119
|
# Initialize result dictionary
|
|
120
120
|
commands_with_parameters = {}
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
from typing import TYPE_CHECKING, Any, Callable, Literal
|
|
3
3
|
|
|
4
4
|
from litellm import ContextWindowExceededError
|
|
5
|
+
from litellm import exceptions as litellm_exceptions
|
|
5
6
|
|
|
6
7
|
import dspy
|
|
7
8
|
from dspy.adapters.types.tool import Tool
|
|
@@ -41,6 +42,7 @@ class fastWorkflowReAct(Module):
|
|
|
41
42
|
super().__init__()
|
|
42
43
|
self.signature = signature = ensure_signature(signature)
|
|
43
44
|
self.max_iters = max_iters
|
|
45
|
+
self.iteration_counter = 0
|
|
44
46
|
|
|
45
47
|
tools = [t if isinstance(t, Tool) else Tool(t) for t in tools]
|
|
46
48
|
tools = {tool.name: tool for tool in tools}
|
|
@@ -105,7 +107,8 @@ class fastWorkflowReAct(Module):
|
|
|
105
107
|
|
|
106
108
|
trajectory = {}
|
|
107
109
|
max_iters = input_args.pop("max_iters", self.max_iters)
|
|
108
|
-
|
|
110
|
+
idx = 0
|
|
111
|
+
while True:
|
|
109
112
|
try:
|
|
110
113
|
pred = self._call_with_potential_trajectory_truncation(self.react, trajectory, **input_args)
|
|
111
114
|
except ValueError as err:
|
|
@@ -126,6 +129,12 @@ class fastWorkflowReAct(Module):
|
|
|
126
129
|
if pred.next_tool_name == "finish":
|
|
127
130
|
break
|
|
128
131
|
|
|
132
|
+
idx += 1 # this is the counter for the index of the entire trajectory
|
|
133
|
+
self.iteration_counter += 1 # this counter just determines the number of times we run the react agent and it's reset everytime we call the user for clarification
|
|
134
|
+
if self.iteration_counter >= max_iters:
|
|
135
|
+
logger.warning("Max iterations reached")
|
|
136
|
+
break
|
|
137
|
+
|
|
129
138
|
extract = self._call_with_potential_trajectory_truncation(self.extract, trajectory, **input_args)
|
|
130
139
|
return dspy.Prediction(trajectory=trajectory, **extract)
|
|
131
140
|
|
|
@@ -161,6 +170,9 @@ class fastWorkflowReAct(Module):
|
|
|
161
170
|
**input_args,
|
|
162
171
|
trajectory=self._format_trajectory(trajectory),
|
|
163
172
|
)
|
|
173
|
+
except litellm_exceptions.BadRequestError:
|
|
174
|
+
logger.warning("Trajectory exceeded the context window, truncating the oldest tool call information.")
|
|
175
|
+
trajectory = self.truncate_trajectory(trajectory)
|
|
164
176
|
except ContextWindowExceededError:
|
|
165
177
|
logger.warning("Trajectory exceeded the context window, truncating the oldest tool call information.")
|
|
166
178
|
trajectory = self.truncate_trajectory(trajectory)
|
|
@@ -407,7 +407,7 @@ Today's date is {today}.
|
|
|
407
407
|
parsed = ast.literal_eval(text)
|
|
408
408
|
if isinstance(parsed, list):
|
|
409
409
|
return parsed
|
|
410
|
-
#
|
|
410
|
+
# Comma-separated values
|
|
411
411
|
if "," in text:
|
|
412
412
|
parts = [p.strip() for p in text.split(",")]
|
|
413
413
|
cleaned = [
|
|
@@ -415,6 +415,12 @@ Today's date is {today}.
|
|
|
415
415
|
for p in parts
|
|
416
416
|
]
|
|
417
417
|
return cleaned
|
|
418
|
+
# Single value - treat as a list with one element
|
|
419
|
+
if text:
|
|
420
|
+
# Remove quotes if present
|
|
421
|
+
if len(text) >= 2 and ((text[0] == text[-1] == '"') or (text[0] == text[-1] == "'")):
|
|
422
|
+
return [text[1:-1]]
|
|
423
|
+
return [text]
|
|
418
424
|
return None
|
|
419
425
|
|
|
420
426
|
def _coerce_scalar(expected_type: Type[Any], val: Any) -> Tuple[bool, Optional[Any]]:
|
|
@@ -511,7 +517,48 @@ Today's date is {today}.
|
|
|
511
517
|
|
|
512
518
|
if valid_by_type:
|
|
513
519
|
if corrected_value is not None:
|
|
514
|
-
|
|
520
|
+
pattern = next(
|
|
521
|
+
(meta.pattern
|
|
522
|
+
for meta in getattr(field_info, "metadata", [])
|
|
523
|
+
if hasattr(meta, "pattern")),
|
|
524
|
+
None,
|
|
525
|
+
)
|
|
526
|
+
if pattern and field_value is not None and field_value != NOT_FOUND:
|
|
527
|
+
invalid_value = None
|
|
528
|
+
if hasattr(field_info, "json_schema_extra") and field_info.json_schema_extra:
|
|
529
|
+
invalid_value = field_info.json_schema_extra.get("invalid_value")
|
|
530
|
+
|
|
531
|
+
# if invalid_value and field_value == invalid_value:
|
|
532
|
+
# invalid_fields.append(f"{field_name} '{field_value}'")
|
|
533
|
+
# pattern_str = str(pattern)
|
|
534
|
+
# examples = getattr(field_info, "examples", [])
|
|
535
|
+
# example = examples[0] if examples else ""
|
|
536
|
+
# all_suggestions[field_name] = [f"Please use the format matching pattern {pattern_str} (e.g., {example})"]
|
|
537
|
+
# is_valid = False
|
|
538
|
+
|
|
539
|
+
# else:
|
|
540
|
+
pattern_regex = re.compile(pattern)
|
|
541
|
+
if not pattern_regex.fullmatch(str(field_value)):
|
|
542
|
+
invalid_fields.append(f"{field_name} '{field_value}'")
|
|
543
|
+
pattern_str = str(pattern)
|
|
544
|
+
examples = getattr(field_info, "examples", [])
|
|
545
|
+
example = examples[0] if examples else ""
|
|
546
|
+
|
|
547
|
+
invalid_fields.append(f"{field_name} '{field_value}'")
|
|
548
|
+
all_suggestions[field_name] = [f"Please use the format matching pattern {pattern_str} (e.g., {example})"]
|
|
549
|
+
is_valid = False
|
|
550
|
+
else:
|
|
551
|
+
try:
|
|
552
|
+
setattr(cmd_parameters, field_name, corrected_value)
|
|
553
|
+
except Exception as e:
|
|
554
|
+
logger.critical(f"Failed to set attribute {field_name} with value {corrected_value}")
|
|
555
|
+
raise e
|
|
556
|
+
else:
|
|
557
|
+
try:
|
|
558
|
+
setattr(cmd_parameters, field_name, corrected_value)
|
|
559
|
+
except Exception as e:
|
|
560
|
+
logger.critical(f"Failed to set attribute {field_name} with value {corrected_value}")
|
|
561
|
+
raise e
|
|
515
562
|
else:
|
|
516
563
|
invalid_fields.append(f"{field_name} '{field_value}'")
|
|
517
564
|
all_suggestions[field_name] = build_type_suggestion()
|
|
@@ -539,35 +586,6 @@ Today's date is {today}.
|
|
|
539
586
|
missing_fields.append(field_name)
|
|
540
587
|
is_valid = False
|
|
541
588
|
|
|
542
|
-
pattern = next(
|
|
543
|
-
(meta.pattern
|
|
544
|
-
for meta in getattr(field_info, "metadata", [])
|
|
545
|
-
if hasattr(meta, "pattern")),
|
|
546
|
-
None,
|
|
547
|
-
)
|
|
548
|
-
if pattern and field_value is not None and field_value != NOT_FOUND:
|
|
549
|
-
invalid_value = None
|
|
550
|
-
if hasattr(field_info, "json_schema_extra") and field_info.json_schema_extra:
|
|
551
|
-
invalid_value = field_info.json_schema_extra.get("invalid_value")
|
|
552
|
-
|
|
553
|
-
if invalid_value and field_value == invalid_value:
|
|
554
|
-
invalid_fields.append(f"{field_name} '{field_value}'")
|
|
555
|
-
pattern_str = str(pattern)
|
|
556
|
-
examples = getattr(field_info, "examples", [])
|
|
557
|
-
example = examples[0] if examples else ""
|
|
558
|
-
all_suggestions[field_name] = [f"Please use the format matching pattern {pattern_str} (e.g., {example})"]
|
|
559
|
-
is_valid = False
|
|
560
|
-
|
|
561
|
-
else:
|
|
562
|
-
pattern_regex = re.compile(pattern)
|
|
563
|
-
if not pattern_regex.fullmatch(str(field_value)):
|
|
564
|
-
invalid_fields.append(f"{field_name} '{field_value}'")
|
|
565
|
-
pattern_str = str(pattern)
|
|
566
|
-
examples = getattr(field_info, "examples", [])
|
|
567
|
-
example = examples[0] if examples else ""
|
|
568
|
-
all_suggestions[field_name] = [f"Please use the format matching pattern {pattern_str} (e.g., {example})"]
|
|
569
|
-
is_valid = False
|
|
570
|
-
|
|
571
589
|
for field_name, field_info in type(cmd_parameters).model_fields.items():
|
|
572
590
|
field_value = getattr(cmd_parameters, field_name, None)
|
|
573
591
|
|