fastworkflow 2.17.5__tar.gz → 2.17.7__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.5 → fastworkflow-2.17.7}/PKG-INFO +1 -1
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/chat_session.py +15 -2
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/README.md +29 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/__main__.py +13 -11
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/jwt_manager.py +83 -16
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/utils.py +7 -7
- fastworkflow-2.17.7/fastworkflow/utils/chat_adapter.py +99 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/react.py +7 -3
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/pyproject.toml +1 -1
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/LICENSE +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/README.md +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/.DS_Store +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_commands/.gitkeep +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/abort.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/go_up.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/reset_context.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_is_current_context.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/command_context_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/intent_detection.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/_workflows/command_metadata_extraction/parameter_extraction.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/__main__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/ast_class_extractor.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/class_analysis_structures.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/cli_specification.md +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/command_dependency_resolver.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/command_file_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/command_file_template.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/command_import_utils.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/command_stub_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/context_folder_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/context_model_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/dependency_manager.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/dir_scanner.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/documentation_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/genai_postprocessor.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/inheritance_block_regenerator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/libcst_transformers.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/navigator_stub_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/pydantic_model_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/utterance_generator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/cache_matching.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/cli.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/command_context_model.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/command_directory.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/command_executor.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/command_interfaces.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/command_metadata_api.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/command_routing.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/docs/context_modules_prd.txt +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/extended_workflow_example/README.md +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/extended_workflow_example/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/extended_workflow_example/_commands/generate_report.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/extended_workflow_example/_commands/startup.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/extended_workflow_example/simple_workflow_template.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/extended_workflow_example/workflow_inheritance_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/fastworkflow.env +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/fastworkflow.passwords.env +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/hello_world/_commands/README.md +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/hello_world/_commands/add_two_numbers.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/hello_world/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/hello_world/application/add_two_numbers.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_1/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_1/_commands/send_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_1/application/send_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_2/_commands/User/send_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_2/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_2/_commands/startup.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_2/application/user.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_3/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_3/_commands/User/send_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_3/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_3/_commands/initialize_user.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_3/application/user.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/_ChatRoom.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/add_user.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/broadcast_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/get_current_user.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/list_users.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/set_current_user.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/_PremiumUser.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/User/_User.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/User/send_message.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/_commands/set_root_context.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/application/chatroom.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/application/user.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/context_hierarchy_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/messaging_app_4/startup_action.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/calculate.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/cancel_pending_order.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/exchange_delivered_order_items.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_email.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/get_order_details.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/get_product_details.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/get_user_details.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/list_all_product_types.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_address.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_items.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_payment.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/modify_user_address.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/return_delivered_order_items.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/_commands/transfer_to_human_agents.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/context_inheritance_model.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/retail_data/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/retail_data/orders.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/retail_data/products.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/retail_data/users.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/calculate.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/cancel_pending_order.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/exchange_delivered_order_items.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_email.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/get_order_details.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/get_product_details.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/get_user_details.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/list_all_product_types.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_address.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_items.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_payment.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/modify_user_address.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/return_delivered_order_items.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/think.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/tool.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/tools/transfer_to_human_agents.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/retail_workflow/workflow_description.txt +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/_WorkItem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/add_child_workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/go_to_workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/mark_as_complete.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_first_child_workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_last_child_workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_next_workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_previous_workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_all_child_workitems.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_child_workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/show_schema.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/_commands/startup.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/application/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/application/workitem.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/simple_workflow_template.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/simple_workflow_template/startup_action.json +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/mcp_server.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/model_pipeline_training.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/refine/__main__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run/__main__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/conversation_store.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/mcp_specific.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/run_fastapi_mcp/redoc_2_standalone_html.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/train/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/train/__main__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/train/generate_synthetic.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/user_message_queues.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/__init__.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/command_dependency_graph.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/context_utils.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/dspy_cache_utils.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/dspy_logger.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/dspy_utils.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/env.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/fuzzy_match.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/generate_param_examples.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/logging.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/parameterize_func_decorator.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/pydantic_model_2_dspy_signature_class.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/python_utils.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/signatures.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/utils/startup_progress.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/workflow.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/workflow_agent.py +0 -0
- {fastworkflow-2.17.5 → fastworkflow-2.17.7}/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.7
|
|
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
|
|
@@ -467,15 +467,28 @@ class ChatSession:
|
|
|
467
467
|
self
|
|
468
468
|
)
|
|
469
469
|
|
|
470
|
+
# Get available commands for current context and pass to agent.
|
|
471
|
+
# The CommandsSystemPreludeAdapter will inject these commands into the system
|
|
472
|
+
# message, keeping them out of the trajectory to avoid token bloat while still
|
|
473
|
+
# providing context-specific command info.
|
|
474
|
+
from fastworkflow.workflow_agent import _what_can_i_do
|
|
475
|
+
available_commands = _what_can_i_do(self)
|
|
476
|
+
|
|
470
477
|
lm = dspy_utils.get_lm("LLM_AGENT", "LITELLM_API_KEY_AGENT")
|
|
471
478
|
from dspy.utils.exceptions import AdapterParseError
|
|
479
|
+
from fastworkflow.utils.chat_adapter import CommandsSystemPreludeAdapter
|
|
480
|
+
|
|
481
|
+
# Use CommandsSystemPreludeAdapter specifically for workflow agent calls
|
|
482
|
+
agent_adapter = CommandsSystemPreludeAdapter()
|
|
483
|
+
|
|
472
484
|
# Retry logic for AdapterParseError
|
|
473
485
|
max_retries = 2
|
|
474
486
|
for attempt in range(max_retries):
|
|
475
487
|
try:
|
|
476
|
-
with dspy.context(lm=lm, adapter=
|
|
488
|
+
with dspy.context(lm=lm, adapter=agent_adapter):
|
|
477
489
|
agent_result = self._workflow_tool_agent(
|
|
478
|
-
user_query=command_info_and_refined_message_with_todolist
|
|
490
|
+
user_query=command_info_and_refined_message_with_todolist,
|
|
491
|
+
available_commands=available_commands
|
|
479
492
|
)
|
|
480
493
|
break # Success, exit retry loop
|
|
481
494
|
except AdapterParseError as _:
|
|
@@ -47,12 +47,41 @@ Configure in your environment (loaded at process startup via CLI args or env loa
|
|
|
47
47
|
| Variable | Description | Required | Default |
|
|
48
48
|
|----------|-------------|----------|---------|
|
|
49
49
|
| `SPEEDDICT_FOLDERNAME` | Base folder for workflow contexts and conversation storage | Yes | - |
|
|
50
|
+
| `--expect_encrypted_jwt` | Enable full JWT signature verification (pass flag to require signed tokens) | No | False (no verification by default) |
|
|
50
51
|
|
|
51
52
|
Notes:
|
|
52
53
|
- Conversation DBs are stored under `SPEEDDICT_FOLDERNAME/user_conversations` (directory is auto-created).
|
|
53
54
|
- `/conversations` now accepts a `limit` query parameter (default `20`).
|
|
54
55
|
- Shutdown waits up to 30 seconds for active turns (hard-coded).
|
|
55
56
|
|
|
57
|
+
## JWT Verification Modes
|
|
58
|
+
|
|
59
|
+
### Default Behavior: No Signature Verification
|
|
60
|
+
By default, the service does NOT verify JWT signatures, accepting unsigned tokens for trusted internal networks where JWT is used purely for data transport:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uvicorn services.run_fastapi.main:app --workflow_path /path/to/workflow
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Use cases** (no verification):
|
|
67
|
+
- Controlled internal networks with network-level security
|
|
68
|
+
- Systems where JWT carries non-sensitive routing information
|
|
69
|
+
- Trusted environments where data transport is the primary concern
|
|
70
|
+
|
|
71
|
+
The service logs a warning on startup when running in this mode.
|
|
72
|
+
|
|
73
|
+
### Secure Mode: Enable Signature Verification
|
|
74
|
+
For production deployments requiring full RSA signature verification:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uvicorn services.run_fastapi.main:app --workflow_path /path/to/workflow --expect_encrypted_jwt
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Secure mode** (with `--expect_encrypted_jwt` flag):
|
|
81
|
+
- JWT tokens are cryptographically verified using RSA signatures
|
|
82
|
+
- Tokens without valid signatures are rejected
|
|
83
|
+
- Recommended for production deployments in untrusted environments
|
|
84
|
+
|
|
56
85
|
## API Endpoints (REST)
|
|
57
86
|
|
|
58
87
|
### `POST /initialize`
|
|
@@ -61,6 +61,7 @@ from .jwt_manager import (
|
|
|
61
61
|
create_access_token,
|
|
62
62
|
create_refresh_token,
|
|
63
63
|
verify_token,
|
|
64
|
+
set_jwt_verification_mode,
|
|
64
65
|
JWT_ACCESS_TOKEN_EXPIRE_MINUTES
|
|
65
66
|
)
|
|
66
67
|
|
|
@@ -148,6 +149,9 @@ async def lifespan(_app: FastAPI):
|
|
|
148
149
|
if ARGS.passwords_file_path:
|
|
149
150
|
env_vars.update(dotenv_values(ARGS.passwords_file_path))
|
|
150
151
|
fastworkflow.init(env_vars=env_vars)
|
|
152
|
+
|
|
153
|
+
# Configure JWT verification mode based on CLI parameter
|
|
154
|
+
set_jwt_verification_mode(ARGS.expect_encrypted_jwt)
|
|
151
155
|
|
|
152
156
|
async def _active_turn_user_ids() -> list[str]:
|
|
153
157
|
active: list[str] = []
|
|
@@ -219,6 +223,8 @@ def load_args():
|
|
|
219
223
|
parser.add_argument("--project_folderpath", required=False)
|
|
220
224
|
parser.add_argument("--port", type=int, default=8000, help="Port to run the server on (default: 8000)")
|
|
221
225
|
parser.add_argument("--host", default="0.0.0.0", help="Host to bind the server to (default: 0.0.0.0)")
|
|
226
|
+
parser.add_argument("--expect_encrypted_jwt", action="store_true", default=False,
|
|
227
|
+
help="Enable JWT signature verification (default: unsigned tokens accepted for trusted networks)")
|
|
222
228
|
return parser.parse_args()
|
|
223
229
|
|
|
224
230
|
ARGS = load_args()
|
|
@@ -251,13 +257,11 @@ def custom_openapi():
|
|
|
251
257
|
|
|
252
258
|
# Enhance the auto-generated Bearer token security scheme with better documentation
|
|
253
259
|
# The HTTPBearer dependency in utils.py creates the base scheme, we just improve it
|
|
254
|
-
if "components" in openapi_schema and "securitySchemes" in openapi_schema["components"]:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
"Enter ONLY the token (Swagger UI automatically adds 'Bearer ' prefix)"
|
|
260
|
-
)
|
|
260
|
+
if "components" in openapi_schema and "securitySchemes" in openapi_schema["components"] and "BearerAuth" in openapi_schema["components"]["securitySchemes"]:
|
|
261
|
+
openapi_schema["components"]["securitySchemes"]["BearerAuth"]["description"] = (
|
|
262
|
+
"JWT access token from /initialize or /refresh_token endpoint. "
|
|
263
|
+
"Enter ONLY the token (Swagger UI automatically adds 'Bearer ' prefix)"
|
|
264
|
+
)
|
|
261
265
|
|
|
262
266
|
# Apply security globally to all endpoints except public ones
|
|
263
267
|
for path, path_item in openapi_schema["paths"].items():
|
|
@@ -265,10 +269,8 @@ def custom_openapi():
|
|
|
265
269
|
if path in ["/initialize", "/refresh_token", "/", "/admin/dump_all_conversations", "/admin/generate_mcp_token"]:
|
|
266
270
|
continue
|
|
267
271
|
for method in path_item:
|
|
268
|
-
if method in ["get", "post", "put", "delete", "patch"]:
|
|
269
|
-
|
|
270
|
-
if "security" not in path_item[method]:
|
|
271
|
-
path_item[method]["security"] = [{"BearerAuth": []}]
|
|
272
|
+
if method in ["get", "post", "put", "delete", "patch"] and "security" not in path_item[method]:
|
|
273
|
+
path_item[method]["security"] = [{"BearerAuth": []}]
|
|
272
274
|
|
|
273
275
|
app.openapi_schema = openapi_schema
|
|
274
276
|
return app.openapi_schema
|
|
@@ -34,6 +34,9 @@ PUBLIC_KEY_PATH = os.path.join(KEYS_DIR, "public_key.pem")
|
|
|
34
34
|
_private_key: Optional[str] = None
|
|
35
35
|
_public_key: Optional[str] = None
|
|
36
36
|
|
|
37
|
+
# Flag to control JWT verification behavior (set from CLI)
|
|
38
|
+
EXPECT_ENCRYPTED_JWT = True # Default to secure mode
|
|
39
|
+
|
|
37
40
|
|
|
38
41
|
def ensure_keys_directory() -> None:
|
|
39
42
|
"""Create jwt_keys directory if it doesn't exist."""
|
|
@@ -129,19 +132,42 @@ def load_or_generate_keys() -> tuple[str, str]:
|
|
|
129
132
|
return _private_key, _public_key
|
|
130
133
|
|
|
131
134
|
|
|
135
|
+
def set_jwt_verification_mode(expect_encrypted: bool) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Configure JWT verification mode for trusted network scenarios.
|
|
138
|
+
|
|
139
|
+
When expect_encrypted=False, JWT tokens are decoded without signature verification.
|
|
140
|
+
This mode is ONLY suitable for trusted internal networks where JWT is used for
|
|
141
|
+
data transport rather than security.
|
|
142
|
+
|
|
143
|
+
WARNING: Disabling signature verification allows any client to forge tokens.
|
|
144
|
+
Only use in controlled environments.
|
|
145
|
+
"""
|
|
146
|
+
global EXPECT_ENCRYPTED_JWT
|
|
147
|
+
EXPECT_ENCRYPTED_JWT = expect_encrypted
|
|
148
|
+
if not expect_encrypted:
|
|
149
|
+
logger.warning(
|
|
150
|
+
"JWT signature verification DISABLED. "
|
|
151
|
+
"Tokens will be accepted without cryptographic validation. "
|
|
152
|
+
"Only use in trusted internal networks."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
132
156
|
def create_access_token(user_id: str, expires_days: int | None = None) -> str:
|
|
133
157
|
"""
|
|
134
158
|
Create a JWT access token for a user.
|
|
135
159
|
|
|
160
|
+
Behavior depends on EXPECT_ENCRYPTED_JWT flag:
|
|
161
|
+
- If True: Creates a signed token using RSA algorithm
|
|
162
|
+
- If False: Creates an unsigned token for trusted network use
|
|
163
|
+
|
|
136
164
|
Args:
|
|
137
165
|
user_id: User identifier
|
|
138
166
|
expires_days: Optional custom expiration in days. If None, uses JWT_ACCESS_TOKEN_EXPIRE_MINUTES (default 60 minutes).
|
|
139
167
|
|
|
140
168
|
Returns:
|
|
141
|
-
str: Encoded JWT access token
|
|
169
|
+
str: Encoded JWT access token (signed or unsigned based on EXPECT_ENCRYPTED_JWT)
|
|
142
170
|
"""
|
|
143
|
-
private_key, _ = load_or_generate_keys()
|
|
144
|
-
|
|
145
171
|
now = datetime.now(timezone.utc)
|
|
146
172
|
if expires_days is not None:
|
|
147
173
|
expire = now + timedelta(days=expires_days)
|
|
@@ -159,8 +185,17 @@ def create_access_token(user_id: str, expires_days: int | None = None) -> str:
|
|
|
159
185
|
"aud": JWT_AUDIENCE # Audience
|
|
160
186
|
}
|
|
161
187
|
|
|
162
|
-
|
|
163
|
-
|
|
188
|
+
if EXPECT_ENCRYPTED_JWT:
|
|
189
|
+
# Secure mode: create signed token
|
|
190
|
+
private_key, _ = load_or_generate_keys()
|
|
191
|
+
token = jwt.encode(payload, private_key, algorithm=JWT_ALGORITHM)
|
|
192
|
+
logger.debug(f"Created signed access token for user_id: {user_id}, expires: {expire.isoformat()}")
|
|
193
|
+
else:
|
|
194
|
+
# Trusted network mode: create unsigned token using HS256 with empty key
|
|
195
|
+
# This creates a JWT that can be decoded without verification
|
|
196
|
+
token = jwt.encode(payload, "", algorithm="HS256")
|
|
197
|
+
logger.debug(f"Created unsigned access token for user_id: {user_id}, expires: {expire.isoformat()}")
|
|
198
|
+
|
|
164
199
|
return token
|
|
165
200
|
|
|
166
201
|
|
|
@@ -168,14 +203,16 @@ def create_refresh_token(user_id: str) -> str:
|
|
|
168
203
|
"""
|
|
169
204
|
Create a JWT refresh token for a user.
|
|
170
205
|
|
|
206
|
+
Behavior depends on EXPECT_ENCRYPTED_JWT flag:
|
|
207
|
+
- If True: Creates a signed token using RSA algorithm
|
|
208
|
+
- If False: Creates an unsigned token for trusted network use
|
|
209
|
+
|
|
171
210
|
Args:
|
|
172
211
|
user_id: User identifier
|
|
173
212
|
|
|
174
213
|
Returns:
|
|
175
|
-
str: Encoded JWT refresh token
|
|
214
|
+
str: Encoded JWT refresh token (signed or unsigned based on EXPECT_ENCRYPTED_JWT)
|
|
176
215
|
"""
|
|
177
|
-
private_key, _ = load_or_generate_keys()
|
|
178
|
-
|
|
179
216
|
now = datetime.now(timezone.utc)
|
|
180
217
|
expire = now + timedelta(days=JWT_REFRESH_TOKEN_EXPIRE_DAYS)
|
|
181
218
|
|
|
@@ -190,15 +227,29 @@ def create_refresh_token(user_id: str) -> str:
|
|
|
190
227
|
"aud": JWT_AUDIENCE # Audience
|
|
191
228
|
}
|
|
192
229
|
|
|
193
|
-
|
|
194
|
-
|
|
230
|
+
if EXPECT_ENCRYPTED_JWT:
|
|
231
|
+
# Secure mode: create signed token
|
|
232
|
+
private_key, _ = load_or_generate_keys()
|
|
233
|
+
token = jwt.encode(payload, private_key, algorithm=JWT_ALGORITHM)
|
|
234
|
+
logger.debug(f"Created signed refresh token for user_id: {user_id}, expires: {expire.isoformat()}")
|
|
235
|
+
else:
|
|
236
|
+
# Trusted network mode: create unsigned token using HS256 with empty key
|
|
237
|
+
# This creates a JWT that can be decoded without verification
|
|
238
|
+
token = jwt.encode(payload, "", algorithm="HS256")
|
|
239
|
+
logger.debug(f"Created unsigned refresh token for user_id: {user_id}, expires: {expire.isoformat()}")
|
|
240
|
+
|
|
195
241
|
return token
|
|
196
242
|
|
|
197
243
|
|
|
198
244
|
def verify_token(token: str, expected_type: str = "access") -> dict:
|
|
245
|
+
# sourcery skip: extract-duplicate-method
|
|
199
246
|
"""
|
|
200
247
|
Verify and decode a JWT token.
|
|
201
248
|
|
|
249
|
+
Behavior depends on EXPECT_ENCRYPTED_JWT flag:
|
|
250
|
+
- If True (default): Full cryptographic verification with signature check
|
|
251
|
+
- If False: Extract payload without verification (trusted network mode)
|
|
252
|
+
|
|
202
253
|
Args:
|
|
203
254
|
token: JWT token string
|
|
204
255
|
expected_type: Expected token type ("access" or "refresh")
|
|
@@ -209,8 +260,25 @@ def verify_token(token: str, expected_type: str = "access") -> dict:
|
|
|
209
260
|
Raises:
|
|
210
261
|
JWTError: If token is invalid, expired, or type mismatch
|
|
211
262
|
"""
|
|
263
|
+
if not EXPECT_ENCRYPTED_JWT:
|
|
264
|
+
# Trusted network mode: decode without verification (accepts both unsigned and signed tokens)
|
|
265
|
+
try:
|
|
266
|
+
# Use unverified decoding - works for any JWT regardless of algorithm or signing
|
|
267
|
+
payload = jwt.get_unverified_claims(token)
|
|
268
|
+
except Exception as e:
|
|
269
|
+
logger.warning(f"Token decoding failed: {e}")
|
|
270
|
+
raise JWTError(f"Failed to decode token: {e}") from e
|
|
271
|
+
|
|
272
|
+
# Validate token type for consistency (outside try-except to allow JWTError to propagate)
|
|
273
|
+
if payload.get("type") != expected_type:
|
|
274
|
+
raise JWTError(f"Invalid token type: expected {expected_type}, got {payload.get('type')}")
|
|
275
|
+
|
|
276
|
+
logger.debug(f"Token decoded (unverified mode): user_id={payload.get('sub')}, type={expected_type}")
|
|
277
|
+
return payload
|
|
278
|
+
|
|
279
|
+
# Standard mode: full verification (existing code)
|
|
212
280
|
_, public_key = load_or_generate_keys()
|
|
213
|
-
|
|
281
|
+
|
|
214
282
|
try:
|
|
215
283
|
# Decode and verify token
|
|
216
284
|
payload = jwt.decode(
|
|
@@ -220,14 +288,14 @@ def verify_token(token: str, expected_type: str = "access") -> dict:
|
|
|
220
288
|
issuer=JWT_ISSUER,
|
|
221
289
|
audience=JWT_AUDIENCE
|
|
222
290
|
)
|
|
223
|
-
|
|
291
|
+
|
|
224
292
|
# Verify token type
|
|
225
293
|
if payload.get("type") != expected_type:
|
|
226
294
|
raise JWTError(f"Invalid token type: expected {expected_type}, got {payload.get('type')}")
|
|
227
|
-
|
|
295
|
+
|
|
228
296
|
logger.debug(f"Token verified successfully: user_id={payload.get('sub')}, type={expected_type}")
|
|
229
297
|
return payload
|
|
230
|
-
|
|
298
|
+
|
|
231
299
|
except JWTError as e:
|
|
232
300
|
logger.warning(f"Token verification failed: {e}")
|
|
233
301
|
raise
|
|
@@ -247,8 +315,7 @@ def get_token_expiry(token: str) -> Optional[datetime]:
|
|
|
247
315
|
try:
|
|
248
316
|
# Decode without verification (just to inspect claims)
|
|
249
317
|
payload = jwt.get_unverified_claims(token)
|
|
250
|
-
exp_timestamp
|
|
251
|
-
if exp_timestamp:
|
|
318
|
+
if exp_timestamp := payload.get("exp"):
|
|
252
319
|
return datetime.fromtimestamp(exp_timestamp, tz=timezone.utc)
|
|
253
320
|
except Exception as e:
|
|
254
321
|
logger.debug(f"Failed to get token expiry: {e}")
|
|
@@ -169,11 +169,11 @@ def get_session_from_jwt(
|
|
|
169
169
|
"""
|
|
170
170
|
# Extract token from credentials (already validated by HTTPBearer)
|
|
171
171
|
token = credentials.credentials
|
|
172
|
-
|
|
172
|
+
|
|
173
173
|
# Verify and decode token
|
|
174
174
|
try:
|
|
175
175
|
payload = verify_token(token, expected_type="access")
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
# Extract session data from payload
|
|
178
178
|
return SessionData(
|
|
179
179
|
user_id=payload["sub"],
|
|
@@ -182,19 +182,19 @@ def get_session_from_jwt(
|
|
|
182
182
|
expires_at=payload["exp"],
|
|
183
183
|
jti=payload["jti"]
|
|
184
184
|
)
|
|
185
|
-
|
|
185
|
+
|
|
186
186
|
except JWTError as e:
|
|
187
187
|
raise HTTPException(
|
|
188
188
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
189
189
|
detail=f"Invalid or expired token: {str(e)}",
|
|
190
|
-
headers={"WWW-Authenticate": "Bearer"}
|
|
191
|
-
)
|
|
190
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
191
|
+
) from e
|
|
192
192
|
except KeyError as e:
|
|
193
193
|
raise HTTPException(
|
|
194
194
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
195
195
|
detail=f"Token missing required claim: {str(e)}",
|
|
196
|
-
headers={"WWW-Authenticate": "Bearer"}
|
|
197
|
-
)
|
|
196
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
197
|
+
) from e
|
|
198
198
|
|
|
199
199
|
|
|
200
200
|
async def ensure_user_runtime_exists(
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ChatAdapter wrapper for injecting context-specific available commands into system messages.
|
|
3
|
+
|
|
4
|
+
Design Overview:
|
|
5
|
+
---------------
|
|
6
|
+
This module implements a ChatAdapter wrapper that dynamically injects workflow command information
|
|
7
|
+
into the system message at runtime, avoiding the need to rebuild ReAct agent modules per context.
|
|
8
|
+
|
|
9
|
+
Key Benefits:
|
|
10
|
+
- Single shared agent: No per-context module caching required
|
|
11
|
+
- Dynamic updates: Commands refresh per call based on current workflow context
|
|
12
|
+
- Token efficiency: Commands appear in system (not repeated in trajectory/history)
|
|
13
|
+
- Zero rebuild cost: Signature and modules remain stable across context changes
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
------
|
|
17
|
+
The adapter is used specifically for workflow agent calls via dspy.context():
|
|
18
|
+
|
|
19
|
+
from fastworkflow.utils.chat_adapter import CommandsSystemPreludeAdapter
|
|
20
|
+
|
|
21
|
+
agent_adapter = CommandsSystemPreludeAdapter()
|
|
22
|
+
available_commands = _what_can_i_do(chat_session)
|
|
23
|
+
|
|
24
|
+
with dspy.context(lm=lm, adapter=agent_adapter):
|
|
25
|
+
agent_result = agent(
|
|
26
|
+
user_query="...",
|
|
27
|
+
available_commands=available_commands
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
The adapter intercepts the format call and prepends commands to the system message,
|
|
31
|
+
keeping them out of the trajectory to prevent token bloat across iterations.
|
|
32
|
+
This scoped approach ensures the adapter only affects workflow agent calls, not other
|
|
33
|
+
DSPy operations in the system.
|
|
34
|
+
"""
|
|
35
|
+
import dspy
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CommandsSystemPreludeAdapter(dspy.ChatAdapter):
|
|
39
|
+
"""
|
|
40
|
+
Wraps a base DSPy ChatAdapter to inject available commands into the system message.
|
|
41
|
+
|
|
42
|
+
This adapter intercepts the render process and prepends a "Available commands" section
|
|
43
|
+
to the system message when `available_commands` is present in inputs. This ensures
|
|
44
|
+
commands are visible to the model at each step without being added to the trajectory
|
|
45
|
+
or conversation history.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
base: The underlying ChatAdapter to wrap. Defaults to dspy.ChatAdapter() if None.
|
|
49
|
+
title: The header text for the commands section. Defaults to "Available commands".
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> import dspy
|
|
53
|
+
>>> from fastworkflow.utils.chat_adapter import CommandsSystemPreludeAdapter
|
|
54
|
+
>>> dspy.settings.adapter = CommandsSystemPreludeAdapter()
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, base: dspy.ChatAdapter | None = None, title: str = "Available commands"):
|
|
58
|
+
super().__init__()
|
|
59
|
+
self.base = base or dspy.ChatAdapter()
|
|
60
|
+
self.title = title
|
|
61
|
+
|
|
62
|
+
def format(self, signature, demos, inputs):
|
|
63
|
+
"""
|
|
64
|
+
Format the inputs for the model, injecting available_commands into system message.
|
|
65
|
+
|
|
66
|
+
This method wraps the base adapter's format method and modifies the result
|
|
67
|
+
to include available commands in the system message if present in inputs.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
signature: The DSPy signature defining the task
|
|
71
|
+
demos: List of demonstration examples
|
|
72
|
+
inputs: Dictionary of input values, may include 'available_commands'
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Formatted messages with commands injected into system message
|
|
76
|
+
"""
|
|
77
|
+
# Call the base adapter's format method
|
|
78
|
+
formatted = self.base.format(signature, demos, inputs)
|
|
79
|
+
|
|
80
|
+
# Check if available_commands is in inputs
|
|
81
|
+
cmds = inputs.get("available_commands")
|
|
82
|
+
if not cmds:
|
|
83
|
+
return formatted
|
|
84
|
+
|
|
85
|
+
# Inject commands into the system message
|
|
86
|
+
prelude = f"{self.title}:\n{cmds}".strip()
|
|
87
|
+
|
|
88
|
+
# Formatted output is a list of messages, first may be system
|
|
89
|
+
# Find and modify the system message, or prepend one
|
|
90
|
+
if formatted and formatted[0].get("role") == "system":
|
|
91
|
+
# Prepend to existing system message
|
|
92
|
+
existing_content = formatted[0].get("content", "")
|
|
93
|
+
formatted[0]["content"] = f"{prelude}\n\n{existing_content}".strip()
|
|
94
|
+
else:
|
|
95
|
+
# No system message exists, prepend one
|
|
96
|
+
formatted.insert(0, {"role": "system", "content": prelude})
|
|
97
|
+
|
|
98
|
+
return formatted
|
|
99
|
+
|
|
@@ -67,13 +67,17 @@ class fastWorkflowReAct(Module):
|
|
|
67
67
|
args={},
|
|
68
68
|
)
|
|
69
69
|
|
|
70
|
-
for idx, tool in enumerate(tools.values())
|
|
71
|
-
instr.append(f"({idx + 1}) {tool}")
|
|
70
|
+
instr.extend(f"({idx + 1}) {tool}" for idx, tool in enumerate(tools.values()))
|
|
72
71
|
instr.append("When providing `next_tool_args`, the value inside the field must be in JSON format")
|
|
73
72
|
|
|
73
|
+
# Build the ReAct signature with trajectory and available_commands inputs.
|
|
74
|
+
# available_commands is injected into system message by CommandsSystemPreludeAdapter
|
|
75
|
+
# (see fastworkflow/utils/chat_adapter.py) and is NOT included in the trajectory
|
|
76
|
+
# formatting to avoid token bloat across iterations.
|
|
74
77
|
react_signature = (
|
|
75
78
|
dspy.Signature({**signature.input_fields}, "\n".join(instr))
|
|
76
79
|
.append("trajectory", dspy.InputField(), type_=str)
|
|
80
|
+
.append("available_commands", dspy.InputField(), type_=str)
|
|
77
81
|
.append("next_thought", dspy.OutputField(), type_=str)
|
|
78
82
|
.append("next_tool_name", dspy.OutputField(), type_=Literal[tuple(tools.keys())])
|
|
79
83
|
.append("next_tool_args", dspy.OutputField(), type_=dict[str, Any])
|
|
@@ -82,7 +86,7 @@ class fastWorkflowReAct(Module):
|
|
|
82
86
|
fallback_signature = dspy.Signature(
|
|
83
87
|
{**signature.input_fields, **signature.output_fields},
|
|
84
88
|
signature.instructions,
|
|
85
|
-
).append("trajectory", dspy.InputField(), type_=str)
|
|
89
|
+
).append("trajectory", dspy.InputField(), type_=str).append("available_commands", dspy.InputField(), type_=str)
|
|
86
90
|
|
|
87
91
|
self.tools = tools
|
|
88
92
|
self.react = dspy.Predict(react_signature)
|
|
@@ -9,7 +9,7 @@ repository = "https://github.com/radiantlogicinc/fastworkflow"
|
|
|
9
9
|
|
|
10
10
|
[tool.poetry]
|
|
11
11
|
name = "fastworkflow"
|
|
12
|
-
version = "2.17.
|
|
12
|
+
version = "2.17.7"
|
|
13
13
|
description = "A framework for rapidly building large-scale, deterministic, interactive workflows with a fault-tolerant, conversational UX"
|
|
14
14
|
authors = ["Dhar Rawal <drawal@radiantlogic.com>"]
|
|
15
15
|
license = "Apache-2.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/command_dependency_resolver.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/build/inheritance_block_regenerator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastworkflow-2.17.5 → fastworkflow-2.17.7}/fastworkflow/examples/fastworkflow.passwords.env
RENAMED
|
File without changes
|