fastworkflow 3.1.2__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/PKG-INFO +99 -4
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/README.md +98 -3
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/__init__.py +17 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/intent_detection.py +70 -2
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/parameter_extraction.py +45 -2
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/chat_session.py +12 -1
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/cli.py +63 -3
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/command_executor.py +69 -0
- fastworkflow-3.2.0/fastworkflow/conversation_labeling.py +131 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/distillation.py +24 -30
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/fastworkflow.env +14 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/kvstore.py +1 -1
- fastworkflow-3.2.0/fastworkflow/metrics.py +65 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/model_pipeline_training.py +27 -9
- fastworkflow-3.2.0/fastworkflow/observability_store.py +1862 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run/__main__.py +40 -1
- fastworkflow-3.2.0/fastworkflow/run_chatbot/__init__.py +10 -0
- fastworkflow-3.2.0/fastworkflow/run_chatbot/launcher.py +504 -0
- fastworkflow-3.2.0/fastworkflow/run_chatbot/server.py +1297 -0
- fastworkflow-3.2.0/fastworkflow/run_chatbot/static/index.html +2820 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/README.md +7 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/__main__.py +478 -140
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/mcp_specific.py +7 -1
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/server_memory.py +97 -12
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/turns.py +166 -32
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/utils.py +263 -127
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/build-task-benchmarks/SKILL.md +260 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/build-task-benchmarks/reference.md +340 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/debug-workflow-conversations/SKILL.md +188 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/debug-workflow-conversations/reference.md +272 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/declare-parameter-producers/SKILL.md +137 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/design-context-models/SKILL.md +215 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/design-context-models/reference.md +174 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/detect-duplicate-capabilities/SKILL.md +199 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/evaluate-intent-routing/SKILL.md +183 -0
- {fastworkflow-3.1.2/fastworkflow/docs → fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows}/integrate-chat-agent/SKILL.md +16 -0
- {fastworkflow-3.1.2/fastworkflow/docs → fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows}/integrate-chat-agent/reference.md +3 -1
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/resolve-parameter-values/SKILL.md +156 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/supply-training-personas/SKILL.md +188 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/train-and-publish-models/SKILL.md +226 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/train-and-publish-models/reference.md +177 -0
- fastworkflow-3.2.0/fastworkflow/skills_for_coding_fastworkflows/validate-command-parameters/SKILL.md +165 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/state_paths.py +22 -1
- fastworkflow-3.2.0/fastworkflow/tracing.py +417 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/__main__.py +20 -0
- fastworkflow-3.2.0/fastworkflow/train/metrics_persistence.py +242 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/turn.py +9 -0
- fastworkflow-3.2.0/fastworkflow/utils/dspy_logger.py +582 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/dspy_utils.py +12 -2
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/react.py +87 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/signatures.py +55 -5
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/workflow.py +5 -4
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/workflow_agent.py +109 -46
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/workflow_execution_context.py +634 -24
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/pyproject.toml +5 -2
- fastworkflow-3.1.2/fastworkflow/docs/context_modules_prd.txt +0 -41
- fastworkflow-3.1.2/fastworkflow/run_fastapi_mcp/conversation_store.py +0 -697
- fastworkflow-3.1.2/fastworkflow/utils/dspy_logger.py +0 -188
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/LICENSE +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/.DS_Store +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_commands/.gitkeep +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/abort.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/go_up.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/reset_context.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_is_current_context.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/_workflows/command_metadata_extraction/command_context_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/active_workflow.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/__main__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/ast_class_extractor.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/class_analysis_structures.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/cli_specification.md +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/command_dependency_resolver.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/command_file_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/command_file_template.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/command_import_utils.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/command_stub_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/context_folder_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/context_model_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/dependency_manager.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/dir_scanner.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/documentation_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/genai_postprocessor.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/inheritance_block_regenerator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/libcst_transformers.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/navigator_stub_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/pydantic_model_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/build/utterance_generator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/cache_matching.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/checkpoint_store.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/command_context_model.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/command_directory.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/command_interfaces.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/command_metadata_api.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/command_routing.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/conversation_history_io.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/extended_workflow_example/README.md +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/extended_workflow_example/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/extended_workflow_example/_commands/generate_report.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/extended_workflow_example/_commands/startup.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/extended_workflow_example/simple_workflow_template.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/extended_workflow_example/workflow_inheritance_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/fastworkflow.passwords.env +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/hello_world/_commands/README.md +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/hello_world/_commands/add_two_numbers.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/hello_world/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/hello_world/application/add_two_numbers.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_1/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_1/_commands/send_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_1/application/send_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_2/_commands/User/_User.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_2/_commands/User/send_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_2/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_2/_commands/startup.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_2/application/user.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_3/_commands/PremiumUser/_PremiumUser.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_3/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_3/_commands/User/_User.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_3/_commands/User/send_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_3/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_3/_commands/initialize_user.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_3/application/user.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/_ChatRoom.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/add_user.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/broadcast_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/get_current_user.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/list_users.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/set_current_user.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/_PremiumUser.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/send_priority_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/User/_User.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/User/send_message.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/context_inheritance_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/_commands/set_root_context.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/application/chatroom.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/application/user.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/context_hierarchy_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/messaging_app_4/startup_action.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/calculate.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/cancel_pending_order.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/exchange_delivered_order_items.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_email.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/get_order_details.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/get_product_details.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/get_user_details.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/list_all_product_types.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_address.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_items.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_payment.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/modify_user_address.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/return_delivered_order_items.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/_commands/transfer_to_human_agents.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/context_inheritance_model.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/retail_data/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/retail_data/orders.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/retail_data/products.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/retail_data/users.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/calculate.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/cancel_pending_order.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/exchange_delivered_order_items.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_email.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_name_zip.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/get_order_details.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/get_product_details.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/get_user_details.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/list_all_product_types.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_address.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_items.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_payment.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/modify_user_address.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/return_delivered_order_items.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/think.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/tool.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/tools/transfer_to_human_agents.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/retail_workflow/workflow_description.txt +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/_WorkItem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/add_child_workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/get_status.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/go_to_workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/mark_as_complete.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_first_child_workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_last_child_workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_next_workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_previous_workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_all_child_workitems.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_child_workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/show_schema.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/_commands/startup.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/application/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/application/workitem.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/simple_workflow_template.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/examples/simple_workflow_template/startup_action.json +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/intent_clarification_agent.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/mcp_server.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/nlu_labels.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/refine/__main__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/checkpoint.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/jwt_manager.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/run_fastapi_mcp/redoc_2_standalone_html.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/serialization_hooks.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/session_state_store.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/state_serialization.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/storage_keys.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/artifact_versioning.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/class_balance.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/determinism.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/duplicate_detection.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/generate_synthetic.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/heldout_evaluation.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/param_example_cache.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/personas.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/selective_training.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/training_report.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/train/utterance_cache.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/user_message_queues.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/__init__.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/chat_adapter.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/context_utils.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/dspy_cache_utils.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/env.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/fuzzy_match.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/generate_param_examples.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/insights_loader.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/logging.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/parameterize_func_decorator.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/pydantic_model_2_dspy_signature_class.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/python_utils.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/utils/startup_progress.py +0 -0
- {fastworkflow-3.1.2 → fastworkflow-3.2.0}/fastworkflow/workflow_inheritance_model.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastworkflow
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
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
|
|
@@ -167,6 +167,7 @@ fastWorkflow was benchmarked on [Tau Bench](https://github.com/sierra-research/t
|
|
|
167
167
|
- [Quick Start: run an example in 5 minutes](#quick-start-run-an-example-in-5-minutes)
|
|
168
168
|
- [AI-enable your own app (without restructuring it)](#ai-enable-your-own-app-without-restructuring-it)
|
|
169
169
|
- [How complex workflows scale: context hierarchies](#how-complex-workflows-scale-context-hierarchies)
|
|
170
|
+
- [Chat with it and debug it: `run_chatbot`](#chat-with-it-and-debug-it-run_chatbot)
|
|
170
171
|
- [Production deployment](#production-deployment)
|
|
171
172
|
- [Developer FAQ](#developer-faq)
|
|
172
173
|
- [Key concepts (going deeper)](#key-concepts-going-deeper)
|
|
@@ -238,10 +239,10 @@ class OrderService:
|
|
|
238
239
|
|
|
239
240
|
### Recommended: let a coding agent wrap it for you
|
|
240
241
|
|
|
241
|
-
The fastest path for a non-trivial app is the **[integrate-chat-agent](./fastworkflow/
|
|
242
|
+
The fastest path for a non-trivial app is the **[integrate-chat-agent](./fastworkflow/skills_for_coding_fastworkflows/integrate-chat-agent) skill** with Cursor or Claude Code:
|
|
242
243
|
|
|
243
244
|
```text
|
|
244
|
-
Open fastworkflow/
|
|
245
|
+
Open fastworkflow/skills_for_coding_fastworkflows/integrate-chat-agent/SKILL.md
|
|
245
246
|
Prompt: "Integrate a fastWorkflow chat agent for OrderService in orders.py"
|
|
246
247
|
```
|
|
247
248
|
|
|
@@ -352,6 +353,88 @@ This is what lets small models stay accurate as your app grows — and what keep
|
|
|
352
353
|
|
|
353
354
|
---
|
|
354
355
|
|
|
356
|
+
## Chat with it and debug it: `run_chatbot`
|
|
357
|
+
|
|
358
|
+
When a turn does the wrong thing, the question is almost always *which stage* got it wrong: did intent detection pick the wrong command, did parameter extraction mis-fill an argument, or did the command itself fail? `run_chatbot` answers that without a debugger.
|
|
359
|
+
|
|
360
|
+
Every run records what it did to a per-workflow SQLite database, `observability.sqlite3`, under `FASTWORKFLOW_STATE_ROOT/workflows/<workflow-id>/`. `run_chatbot` is a local browser UI over the workflow: a chat client for driving new turns, and a trace viewer for everything that already happened.
|
|
361
|
+
|
|
362
|
+
```sh
|
|
363
|
+
fastworkflow run_chatbot
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
One command opens the workflow picker, then starts the selected workflow's FastAPI server for you (loopback-only, stopped when the chatbot exits), picks a free port, and connects the chat — no server URL, token, channel, workflow path, or env-file CLI arguments to fill in. Env files are auto-detected in the workflow directory and, for bundled workflows, the shared `examples/` directory. If either is missing, choose existing files in the browser or create owner-only workflow-local copies from the bundled templates.
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
fastWorkflow Chatbot
|
|
370
|
+
pick a workflow in the browser (bundled examples
|
|
371
|
+
and local folders are listed; you can browse anywhere).
|
|
372
|
+
|
|
373
|
+
Open in your browser:
|
|
374
|
+
|
|
375
|
+
http://127.0.0.1:8901/?token=Bg4-uLpLdETJAsYbGzXbfRQdAEhitS6p8podPXmK25g
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Use that printed URL. It carries a one-off token, and requests without it are refused — so one you leave running is not readable by anything else on the machine. Pass `--server-port` to skip spawning and use a FastAPI server already running on that port. The page opens with a workflow picker: bundled examples plus a directory browser, with a **Switch workflow** button to change later.
|
|
379
|
+
|
|
380
|
+
### Debug mode: what a turn actually did
|
|
381
|
+
|
|
382
|
+
Debug mode nests turns directly beneath their conversations (with a separate conversation-less group when needed), and opens any turn into a span tree — the nested record of the stages that ran, each with its duration, status, and attributes:
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
fw.turn the whole logical turn (stable across ask_user suspensions)
|
|
386
|
+
├── fw.planner.plan the agent's plan for the turn (fw.planner.replan on a re-plan)
|
|
387
|
+
│ └── fw.llm.call the planner's LLM request/response
|
|
388
|
+
└── fw.agent.execute the ReAct loop as a whole (attempts, final answer)
|
|
389
|
+
└── fw.agent.step one reasoning step: thought, chosen tool, observation
|
|
390
|
+
├── fw.llm.call the step's reasoning LLM call (+ provider-native reasoning)
|
|
391
|
+
└── fw.agent.tool_call the agent invoking a command
|
|
392
|
+
└── fw.command.execute resolution + your business logic running
|
|
393
|
+
├── fw.nlu.intent which matching layer decided, classifier
|
|
394
|
+
│ confidence vs threshold, candidates on ambiguity
|
|
395
|
+
└── fw.nlu.param_extraction extraction method, missing/invalid fields,
|
|
396
|
+
│ db_lookup outcomes, validation-hook verdict
|
|
397
|
+
└── fw.llm.call the LLM extraction call, when one ran
|
|
398
|
+
fw.ask_user a clarifying question, and how long the user took
|
|
399
|
+
|
|
400
|
+
(Deterministic "/"-mode turns skip the planner/agent layers: fw.command.execute
|
|
401
|
+
sits directly under fw.turn.)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
The diagnosis lives in the attributes of `fw.command.execute`. It records `raw_command` (the natural-language command the agent sent), `command_name` (what intent detection *resolved* it to), the extracted `parameters`, the `response_text`, and `success`. Comparing the first two is the whole trick:
|
|
405
|
+
|
|
406
|
+
- **`raw_command` was reasonable but `command_name` is the wrong command** → an intent-detection miss. Add seed utterances for that command and retrain.
|
|
407
|
+
- **`command_name` is right but `parameters` are wrong** → a parameter-extraction miss. Strengthen the `Field(description=…, examples=[…])` in that command's Signature.
|
|
408
|
+
- **Both right, span status is `error`** → the bug is in your own code, and `response_text` usually says how.
|
|
409
|
+
|
|
410
|
+
Two turn-level fields are worth knowing because they are deliberately independent. `status` is the turn's lifecycle (`completed`, `failed`, …) and `success` means every command in it succeeded. A turn that is `completed` but not `success` is the case worth hunting: a command failed and the agent wrote a confident answer over it.
|
|
411
|
+
|
|
412
|
+
Coding agents can run this diagnosis directly against the database: the wheel-shipped [`debug-workflow-conversations`](fastworkflow/skills_for_coding_fastworkflows/debug-workflow-conversations/SKILL.md) skill carries the failure-triage decision tree, the full schema and span-attribute contract, and the routing table from each diagnosis to the fastWorkflow feature (and companion skill) that fixes it.
|
|
413
|
+
|
|
414
|
+
The **Health** view reports the background writer's state and the database size, which is how you tell "no spans recorded" apart from "spans dropped under load."
|
|
415
|
+
|
|
416
|
+
### Chat: drive the workflow from the browser
|
|
417
|
+
|
|
418
|
+
The Chat tab is connected the moment the workflow's server is ready (the first start loads models, so give it a moment — the page tells you what it is waiting on). Every turn is recorded like any other, so you can send a message and immediately open its span tree via its **view trace** link or the Debug tab.
|
|
419
|
+
|
|
420
|
+
Messages go to `/invoke_agent`; prefix one with `/` to force deterministic execution via `/invoke_assistant`, the same convention as the CLI prompt. **New conversation** in the header archives the current thread and starts a fresh one, like the CLI's `//new`. Sessions are single-developer by design: the chatbot manages one fixed private channel (`chatbot`), so there is nothing multi-user to configure and your history is in one place across launches.
|
|
421
|
+
|
|
422
|
+
There is deliberately no box for a startup command or a per-session context: the session is driven by what you type, so pre-seeding it from a chat form makes no sense. Those stay launch-time decisions — `run_fastapi_mcp --startup_command`, or the `/initialize` request fields if you are calling the API yourself.
|
|
423
|
+
|
|
424
|
+
A spawned server always binds `127.0.0.1`, has CORS pinned to loopback origins, and is stopped when the chatbot exits (SIGTERM included). It runs with unsigned dev JWTs — fine for a loopback-only dev server whose tokens the chatbot mints itself; pass `--expect-encrypted-jwt` to require signed tokens instead (then paste one in the chat's Advanced panel). Already have a server running? Launch with `--server-port <port>` and connect via the Advanced panel.
|
|
425
|
+
|
|
426
|
+
Insights distillation (`fastworkflow run --generate_insights`) stays CLI-only by design, as do bind address and env-file choices for externally managed servers. The full capability comparison is in [`docs/run_chatbot_cli_parity.md`](docs/run_chatbot_cli_parity.md).
|
|
427
|
+
|
|
428
|
+
### Retention and erasure
|
|
429
|
+
|
|
430
|
+
Trace data grows. Spans and artifacts are pruned beyond a retention horizon; **turn records and conversations are never deleted by a default prune**, so history survives.
|
|
431
|
+
|
|
432
|
+
Use **Clear conversations** in Debug mode for an explicit, confirmed reset. It removes conversation labels, turns, spans, artifacts, feedback, and legacy per-channel conversation files. Training runs, writer diagnostics, and monotonic conversation counters survive, so identities are never reused after a clear.
|
|
433
|
+
|
|
434
|
+
To turn recording off entirely, set `FW_OBSERVABILITY=0`.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
355
438
|
## Production deployment
|
|
356
439
|
|
|
357
440
|
### Pattern 1 — host it as a FastAPI service (recommended)
|
|
@@ -488,7 +571,7 @@ Deep-dive articles:
|
|
|
488
571
|
|
|
489
572
|
## Architecture overview
|
|
490
573
|
|
|
491
|
-
fastWorkflow separates **build-time**, **train-time**, and **run-time**. At build-time you create a command interface from your code (recommended via the [integrate-chat-agent](./fastworkflow/
|
|
574
|
+
fastWorkflow separates **build-time**, **train-time**, and **run-time**. At build-time you create a command interface from your code (recommended via the [integrate-chat-agent](./fastworkflow/skills_for_coding_fastworkflows/integrate-chat-agent) skill). `train` builds the NLP models; `run` executes the workflow. Your existing code is never modified — fastWorkflow sits as a layer on top.
|
|
492
575
|
|
|
493
576
|
```mermaid
|
|
494
577
|
graph LR
|
|
@@ -601,6 +684,12 @@ fastworkflow run <workflow_dir> <env_file> <passwords_file> --assistant # dete
|
|
|
601
684
|
fastworkflow run <workflow_dir> <env_file> <passwords_file> \
|
|
602
685
|
--startup_command "your command" --keep_alive False
|
|
603
686
|
|
|
687
|
+
# Browser chatbot + observability viewer (`run` is also spelled `run_cli`);
|
|
688
|
+
# workflow, env files, and trace maintenance are chosen in the browser
|
|
689
|
+
fastworkflow run_chatbot
|
|
690
|
+
fastworkflow run_chatbot --server-port 8000 # pin the spawned server's port
|
|
691
|
+
fastworkflow run_chatbot --expect-encrypted-jwt # require signed tokens
|
|
692
|
+
|
|
604
693
|
# Host as a FastAPI/MCP service
|
|
605
694
|
python -m fastworkflow.run_fastapi_mcp --workflow_path ./wf --port 8000
|
|
606
695
|
```
|
|
@@ -630,6 +719,10 @@ Two files per workflow (templates ship with `fastworkflow examples fetch`).
|
|
|
630
719
|
| `LITELLM_PROXY_API_BASE` | LiteLLM Proxy URL | with `litellm_proxy/` models | *not set* |
|
|
631
720
|
| `INTENT_DETECTION_TINY_MODEL` | HF id for the small intent model | `train` (optional) | `google/bert_uncased_L-4_H-128_A-2` |
|
|
632
721
|
| `INTENT_DETECTION_LARGE_MODEL` | HF id for the large intent model | `train` (optional) | `distilbert-base-uncased` |
|
|
722
|
+
| `FW_OBSERVABILITY` | Master switch for trace recording. `0` disables it. On by default for `run`/`run_fastapi_mcp`; off by default when embedding the core as a library | Optional | `1` (fastWorkflow entry points) |
|
|
723
|
+
| `FW_OBS_RETENTION_DAYS` | Age beyond which the automatic prune (run at recorder startup) drops spans/artifacts (turn records are exempt) | Optional | `30` |
|
|
724
|
+
| `FW_OBS_DB_MAX_BYTES` | Size cap; the automatic prune evicts oldest spans first while over it | Optional | `1073741824` (1 GiB) |
|
|
725
|
+
| `FW_OBS_CAPTURE_TRACEBACKS` | Persist exception tracebacks as artifacts. Off by default because tracebacks can carry sensitive values | Optional | `0` |
|
|
633
726
|
|
|
634
727
|
### `fastworkflow.passwords.env`
|
|
635
728
|
|
|
@@ -668,6 +761,8 @@ When a model uses the `litellm_proxy/` prefix, the per-role keys are ignored and
|
|
|
668
761
|
|
|
669
762
|
> **Commands not recognized** — a command module with an import/syntax error won't load and won't appear as an intent. Check your `_commands/*.py` files.
|
|
670
763
|
|
|
764
|
+
> **The agent did something unexpected** — open the chatbot (`fastworkflow run_chatbot`, then pick the workflow) and read the turn's span tree. It shows which command intent detection chose, what parameters were extracted, and whether your command failed — usually enough to identify the stage at fault before reaching for a debugger.
|
|
765
|
+
|
|
671
766
|
> [!tip]
|
|
672
767
|
> To debug command files, set up a VSCode `launch.json` with `justMyCode: false`, add breakpoints, and run in debug mode.
|
|
673
768
|
|
|
@@ -113,6 +113,7 @@ fastWorkflow was benchmarked on [Tau Bench](https://github.com/sierra-research/t
|
|
|
113
113
|
- [Quick Start: run an example in 5 minutes](#quick-start-run-an-example-in-5-minutes)
|
|
114
114
|
- [AI-enable your own app (without restructuring it)](#ai-enable-your-own-app-without-restructuring-it)
|
|
115
115
|
- [How complex workflows scale: context hierarchies](#how-complex-workflows-scale-context-hierarchies)
|
|
116
|
+
- [Chat with it and debug it: `run_chatbot`](#chat-with-it-and-debug-it-run_chatbot)
|
|
116
117
|
- [Production deployment](#production-deployment)
|
|
117
118
|
- [Developer FAQ](#developer-faq)
|
|
118
119
|
- [Key concepts (going deeper)](#key-concepts-going-deeper)
|
|
@@ -184,10 +185,10 @@ class OrderService:
|
|
|
184
185
|
|
|
185
186
|
### Recommended: let a coding agent wrap it for you
|
|
186
187
|
|
|
187
|
-
The fastest path for a non-trivial app is the **[integrate-chat-agent](./fastworkflow/
|
|
188
|
+
The fastest path for a non-trivial app is the **[integrate-chat-agent](./fastworkflow/skills_for_coding_fastworkflows/integrate-chat-agent) skill** with Cursor or Claude Code:
|
|
188
189
|
|
|
189
190
|
```text
|
|
190
|
-
Open fastworkflow/
|
|
191
|
+
Open fastworkflow/skills_for_coding_fastworkflows/integrate-chat-agent/SKILL.md
|
|
191
192
|
Prompt: "Integrate a fastWorkflow chat agent for OrderService in orders.py"
|
|
192
193
|
```
|
|
193
194
|
|
|
@@ -298,6 +299,88 @@ This is what lets small models stay accurate as your app grows — and what keep
|
|
|
298
299
|
|
|
299
300
|
---
|
|
300
301
|
|
|
302
|
+
## Chat with it and debug it: `run_chatbot`
|
|
303
|
+
|
|
304
|
+
When a turn does the wrong thing, the question is almost always *which stage* got it wrong: did intent detection pick the wrong command, did parameter extraction mis-fill an argument, or did the command itself fail? `run_chatbot` answers that without a debugger.
|
|
305
|
+
|
|
306
|
+
Every run records what it did to a per-workflow SQLite database, `observability.sqlite3`, under `FASTWORKFLOW_STATE_ROOT/workflows/<workflow-id>/`. `run_chatbot` is a local browser UI over the workflow: a chat client for driving new turns, and a trace viewer for everything that already happened.
|
|
307
|
+
|
|
308
|
+
```sh
|
|
309
|
+
fastworkflow run_chatbot
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
One command opens the workflow picker, then starts the selected workflow's FastAPI server for you (loopback-only, stopped when the chatbot exits), picks a free port, and connects the chat — no server URL, token, channel, workflow path, or env-file CLI arguments to fill in. Env files are auto-detected in the workflow directory and, for bundled workflows, the shared `examples/` directory. If either is missing, choose existing files in the browser or create owner-only workflow-local copies from the bundled templates.
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
fastWorkflow Chatbot
|
|
316
|
+
pick a workflow in the browser (bundled examples
|
|
317
|
+
and local folders are listed; you can browse anywhere).
|
|
318
|
+
|
|
319
|
+
Open in your browser:
|
|
320
|
+
|
|
321
|
+
http://127.0.0.1:8901/?token=Bg4-uLpLdETJAsYbGzXbfRQdAEhitS6p8podPXmK25g
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Use that printed URL. It carries a one-off token, and requests without it are refused — so one you leave running is not readable by anything else on the machine. Pass `--server-port` to skip spawning and use a FastAPI server already running on that port. The page opens with a workflow picker: bundled examples plus a directory browser, with a **Switch workflow** button to change later.
|
|
325
|
+
|
|
326
|
+
### Debug mode: what a turn actually did
|
|
327
|
+
|
|
328
|
+
Debug mode nests turns directly beneath their conversations (with a separate conversation-less group when needed), and opens any turn into a span tree — the nested record of the stages that ran, each with its duration, status, and attributes:
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
fw.turn the whole logical turn (stable across ask_user suspensions)
|
|
332
|
+
├── fw.planner.plan the agent's plan for the turn (fw.planner.replan on a re-plan)
|
|
333
|
+
│ └── fw.llm.call the planner's LLM request/response
|
|
334
|
+
└── fw.agent.execute the ReAct loop as a whole (attempts, final answer)
|
|
335
|
+
└── fw.agent.step one reasoning step: thought, chosen tool, observation
|
|
336
|
+
├── fw.llm.call the step's reasoning LLM call (+ provider-native reasoning)
|
|
337
|
+
└── fw.agent.tool_call the agent invoking a command
|
|
338
|
+
└── fw.command.execute resolution + your business logic running
|
|
339
|
+
├── fw.nlu.intent which matching layer decided, classifier
|
|
340
|
+
│ confidence vs threshold, candidates on ambiguity
|
|
341
|
+
└── fw.nlu.param_extraction extraction method, missing/invalid fields,
|
|
342
|
+
│ db_lookup outcomes, validation-hook verdict
|
|
343
|
+
└── fw.llm.call the LLM extraction call, when one ran
|
|
344
|
+
fw.ask_user a clarifying question, and how long the user took
|
|
345
|
+
|
|
346
|
+
(Deterministic "/"-mode turns skip the planner/agent layers: fw.command.execute
|
|
347
|
+
sits directly under fw.turn.)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The diagnosis lives in the attributes of `fw.command.execute`. It records `raw_command` (the natural-language command the agent sent), `command_name` (what intent detection *resolved* it to), the extracted `parameters`, the `response_text`, and `success`. Comparing the first two is the whole trick:
|
|
351
|
+
|
|
352
|
+
- **`raw_command` was reasonable but `command_name` is the wrong command** → an intent-detection miss. Add seed utterances for that command and retrain.
|
|
353
|
+
- **`command_name` is right but `parameters` are wrong** → a parameter-extraction miss. Strengthen the `Field(description=…, examples=[…])` in that command's Signature.
|
|
354
|
+
- **Both right, span status is `error`** → the bug is in your own code, and `response_text` usually says how.
|
|
355
|
+
|
|
356
|
+
Two turn-level fields are worth knowing because they are deliberately independent. `status` is the turn's lifecycle (`completed`, `failed`, …) and `success` means every command in it succeeded. A turn that is `completed` but not `success` is the case worth hunting: a command failed and the agent wrote a confident answer over it.
|
|
357
|
+
|
|
358
|
+
Coding agents can run this diagnosis directly against the database: the wheel-shipped [`debug-workflow-conversations`](fastworkflow/skills_for_coding_fastworkflows/debug-workflow-conversations/SKILL.md) skill carries the failure-triage decision tree, the full schema and span-attribute contract, and the routing table from each diagnosis to the fastWorkflow feature (and companion skill) that fixes it.
|
|
359
|
+
|
|
360
|
+
The **Health** view reports the background writer's state and the database size, which is how you tell "no spans recorded" apart from "spans dropped under load."
|
|
361
|
+
|
|
362
|
+
### Chat: drive the workflow from the browser
|
|
363
|
+
|
|
364
|
+
The Chat tab is connected the moment the workflow's server is ready (the first start loads models, so give it a moment — the page tells you what it is waiting on). Every turn is recorded like any other, so you can send a message and immediately open its span tree via its **view trace** link or the Debug tab.
|
|
365
|
+
|
|
366
|
+
Messages go to `/invoke_agent`; prefix one with `/` to force deterministic execution via `/invoke_assistant`, the same convention as the CLI prompt. **New conversation** in the header archives the current thread and starts a fresh one, like the CLI's `//new`. Sessions are single-developer by design: the chatbot manages one fixed private channel (`chatbot`), so there is nothing multi-user to configure and your history is in one place across launches.
|
|
367
|
+
|
|
368
|
+
There is deliberately no box for a startup command or a per-session context: the session is driven by what you type, so pre-seeding it from a chat form makes no sense. Those stay launch-time decisions — `run_fastapi_mcp --startup_command`, or the `/initialize` request fields if you are calling the API yourself.
|
|
369
|
+
|
|
370
|
+
A spawned server always binds `127.0.0.1`, has CORS pinned to loopback origins, and is stopped when the chatbot exits (SIGTERM included). It runs with unsigned dev JWTs — fine for a loopback-only dev server whose tokens the chatbot mints itself; pass `--expect-encrypted-jwt` to require signed tokens instead (then paste one in the chat's Advanced panel). Already have a server running? Launch with `--server-port <port>` and connect via the Advanced panel.
|
|
371
|
+
|
|
372
|
+
Insights distillation (`fastworkflow run --generate_insights`) stays CLI-only by design, as do bind address and env-file choices for externally managed servers. The full capability comparison is in [`docs/run_chatbot_cli_parity.md`](docs/run_chatbot_cli_parity.md).
|
|
373
|
+
|
|
374
|
+
### Retention and erasure
|
|
375
|
+
|
|
376
|
+
Trace data grows. Spans and artifacts are pruned beyond a retention horizon; **turn records and conversations are never deleted by a default prune**, so history survives.
|
|
377
|
+
|
|
378
|
+
Use **Clear conversations** in Debug mode for an explicit, confirmed reset. It removes conversation labels, turns, spans, artifacts, feedback, and legacy per-channel conversation files. Training runs, writer diagnostics, and monotonic conversation counters survive, so identities are never reused after a clear.
|
|
379
|
+
|
|
380
|
+
To turn recording off entirely, set `FW_OBSERVABILITY=0`.
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
301
384
|
## Production deployment
|
|
302
385
|
|
|
303
386
|
### Pattern 1 — host it as a FastAPI service (recommended)
|
|
@@ -434,7 +517,7 @@ Deep-dive articles:
|
|
|
434
517
|
|
|
435
518
|
## Architecture overview
|
|
436
519
|
|
|
437
|
-
fastWorkflow separates **build-time**, **train-time**, and **run-time**. At build-time you create a command interface from your code (recommended via the [integrate-chat-agent](./fastworkflow/
|
|
520
|
+
fastWorkflow separates **build-time**, **train-time**, and **run-time**. At build-time you create a command interface from your code (recommended via the [integrate-chat-agent](./fastworkflow/skills_for_coding_fastworkflows/integrate-chat-agent) skill). `train` builds the NLP models; `run` executes the workflow. Your existing code is never modified — fastWorkflow sits as a layer on top.
|
|
438
521
|
|
|
439
522
|
```mermaid
|
|
440
523
|
graph LR
|
|
@@ -547,6 +630,12 @@ fastworkflow run <workflow_dir> <env_file> <passwords_file> --assistant # dete
|
|
|
547
630
|
fastworkflow run <workflow_dir> <env_file> <passwords_file> \
|
|
548
631
|
--startup_command "your command" --keep_alive False
|
|
549
632
|
|
|
633
|
+
# Browser chatbot + observability viewer (`run` is also spelled `run_cli`);
|
|
634
|
+
# workflow, env files, and trace maintenance are chosen in the browser
|
|
635
|
+
fastworkflow run_chatbot
|
|
636
|
+
fastworkflow run_chatbot --server-port 8000 # pin the spawned server's port
|
|
637
|
+
fastworkflow run_chatbot --expect-encrypted-jwt # require signed tokens
|
|
638
|
+
|
|
550
639
|
# Host as a FastAPI/MCP service
|
|
551
640
|
python -m fastworkflow.run_fastapi_mcp --workflow_path ./wf --port 8000
|
|
552
641
|
```
|
|
@@ -576,6 +665,10 @@ Two files per workflow (templates ship with `fastworkflow examples fetch`).
|
|
|
576
665
|
| `LITELLM_PROXY_API_BASE` | LiteLLM Proxy URL | with `litellm_proxy/` models | *not set* |
|
|
577
666
|
| `INTENT_DETECTION_TINY_MODEL` | HF id for the small intent model | `train` (optional) | `google/bert_uncased_L-4_H-128_A-2` |
|
|
578
667
|
| `INTENT_DETECTION_LARGE_MODEL` | HF id for the large intent model | `train` (optional) | `distilbert-base-uncased` |
|
|
668
|
+
| `FW_OBSERVABILITY` | Master switch for trace recording. `0` disables it. On by default for `run`/`run_fastapi_mcp`; off by default when embedding the core as a library | Optional | `1` (fastWorkflow entry points) |
|
|
669
|
+
| `FW_OBS_RETENTION_DAYS` | Age beyond which the automatic prune (run at recorder startup) drops spans/artifacts (turn records are exempt) | Optional | `30` |
|
|
670
|
+
| `FW_OBS_DB_MAX_BYTES` | Size cap; the automatic prune evicts oldest spans first while over it | Optional | `1073741824` (1 GiB) |
|
|
671
|
+
| `FW_OBS_CAPTURE_TRACEBACKS` | Persist exception tracebacks as artifacts. Off by default because tracebacks can carry sensitive values | Optional | `0` |
|
|
579
672
|
|
|
580
673
|
### `fastworkflow.passwords.env`
|
|
581
674
|
|
|
@@ -614,6 +707,8 @@ When a model uses the `litellm_proxy/` prefix, the per-role keys are ignored and
|
|
|
614
707
|
|
|
615
708
|
> **Commands not recognized** — a command module with an import/syntax error won't load and won't appear as an intent. Check your `_commands/*.py` files.
|
|
616
709
|
|
|
710
|
+
> **The agent did something unexpected** — open the chatbot (`fastworkflow run_chatbot`, then pick the workflow) and read the turn's span tree. It shows which command intent detection chose, what parameters were extracted, and whether your command failed — usually enough to identify the stage at fault before reaching for a debugger.
|
|
711
|
+
|
|
617
712
|
> [!tip]
|
|
618
713
|
> To debug command files, set up a VSCode `launch.json` with `justMyCode: false`, add breakpoints, and run in debug mode.
|
|
619
714
|
|
|
@@ -63,6 +63,9 @@ class CommandTraceEvent:
|
|
|
63
63
|
response_text: str | None
|
|
64
64
|
success: bool | None
|
|
65
65
|
timestamp_ms: int
|
|
66
|
+
# Logical-turn correlation key (additive, observability design §3.1 [X7]).
|
|
67
|
+
# None only for events emitted outside any logical turn.
|
|
68
|
+
turn_key: str | None = None
|
|
66
69
|
|
|
67
70
|
class CommandOutput(BaseModel):
|
|
68
71
|
"""The result of one command execution.
|
|
@@ -343,3 +346,17 @@ _turn_types_namespace = {
|
|
|
343
346
|
}
|
|
344
347
|
TurnOutput.model_rebuild(_types_namespace=_turn_types_namespace)
|
|
345
348
|
TurnResult.model_rebuild(_types_namespace=_turn_types_namespace)
|
|
349
|
+
|
|
350
|
+
# Observability sinks (fastworkflow.tracing / fastworkflow.metrics, both
|
|
351
|
+
# stdlib-only). Embedders implement TraceSink/MetricsSink and wire them via
|
|
352
|
+
# WorkflowExecutionContext (observability design §3.1).
|
|
353
|
+
from fastworkflow.tracing import (
|
|
354
|
+
Span as Span,
|
|
355
|
+
TraceSink as TraceSink,
|
|
356
|
+
NoOpTraceSink as NoOpTraceSink,
|
|
357
|
+
)
|
|
358
|
+
from fastworkflow.metrics import (
|
|
359
|
+
MetricsSink as MetricsSink,
|
|
360
|
+
NoOpMetricsSink as NoOpMetricsSink,
|
|
361
|
+
LoggingMetricsSink as LoggingMetricsSink,
|
|
362
|
+
)
|
|
@@ -7,7 +7,7 @@ from pydantic import BaseModel
|
|
|
7
7
|
|
|
8
8
|
import fastworkflow
|
|
9
9
|
from fastworkflow.utils.logging import logger
|
|
10
|
-
from fastworkflow import NLUPipelineStage
|
|
10
|
+
from fastworkflow import NLUPipelineStage, tracing
|
|
11
11
|
from fastworkflow.cache_matching import cache_match, store_utterance_cache
|
|
12
12
|
from fastworkflow.kvstore import KVStore
|
|
13
13
|
from fastworkflow.model_pipeline_training import (
|
|
@@ -40,6 +40,60 @@ class CommandNamePrediction:
|
|
|
40
40
|
self.path = self._get_cache_path_cache(self.convo_path)
|
|
41
41
|
|
|
42
42
|
def predict(self, command_context_name: str, command: str, nlu_pipeline_stage: NLUPipelineStage) -> "CommandNamePrediction.Output":
|
|
43
|
+
"""Predict, wrapped in a ``fw.nlu.intent`` span (D3 as amended).
|
|
44
|
+
|
|
45
|
+
One span per prediction attempt — the wildcard command's parent-chain
|
|
46
|
+
walk calls this once per context, and each attempt is recorded with
|
|
47
|
+
the context it ran against. The span carries which matching layer
|
|
48
|
+
decided (exact prefix / fuzzy pre-match / embedding cache /
|
|
49
|
+
classifier), the classifier's confidence and threshold when it ran,
|
|
50
|
+
and the candidate set on an ambiguity. Emission never affects the
|
|
51
|
+
prediction: the helpers no-op without a bound host/sink.
|
|
52
|
+
"""
|
|
53
|
+
host = tracing.current_host()
|
|
54
|
+
span = tracing.start_span(
|
|
55
|
+
host,
|
|
56
|
+
tracing.SPAN_NLU_INTENT,
|
|
57
|
+
attributes={
|
|
58
|
+
"context": command_context_name,
|
|
59
|
+
"stage": nlu_pipeline_stage.name,
|
|
60
|
+
"utterance": command,
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
nlu_trace: dict = {}
|
|
64
|
+
try:
|
|
65
|
+
output = self._predict_impl(
|
|
66
|
+
command_context_name, command, nlu_pipeline_stage, nlu_trace
|
|
67
|
+
)
|
|
68
|
+
except BaseException:
|
|
69
|
+
tracing.end_span(
|
|
70
|
+
host, span, status=tracing.STATUS_ERROR, attributes=nlu_trace
|
|
71
|
+
)
|
|
72
|
+
raise
|
|
73
|
+
tracing.end_span(
|
|
74
|
+
host,
|
|
75
|
+
span,
|
|
76
|
+
status=tracing.STATUS_OK,
|
|
77
|
+
attributes={
|
|
78
|
+
**nlu_trace,
|
|
79
|
+
"command_name": output.command_name,
|
|
80
|
+
"is_cme_command": output.is_cme_command,
|
|
81
|
+
"ambiguous": output.error_msg is not None,
|
|
82
|
+
# None = no local prediction; the caller walks up the context
|
|
83
|
+
# chain (or files a misunderstanding) — exactly the routing
|
|
84
|
+
# signal a debugging agent needs.
|
|
85
|
+
"resolved": output.command_name is not None,
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
return output
|
|
89
|
+
|
|
90
|
+
def _predict_impl(
|
|
91
|
+
self,
|
|
92
|
+
command_context_name: str,
|
|
93
|
+
command: str,
|
|
94
|
+
nlu_pipeline_stage: NLUPipelineStage,
|
|
95
|
+
nlu_trace: dict,
|
|
96
|
+
) -> "CommandNamePrediction.Output":
|
|
43
97
|
# sourcery skip: extract-duplicate-method
|
|
44
98
|
|
|
45
99
|
model_artifact_path = f"{self.app_workflow_folderpath}/___command_info/{command_context_name}"
|
|
@@ -109,6 +163,7 @@ class CommandNamePrediction:
|
|
|
109
163
|
if normalized_command_name in command_name_dict:
|
|
110
164
|
command_name = normalized_command_name
|
|
111
165
|
command = command.replace(f"{tentative_command_name}", "").strip().replace(" ", " ")
|
|
166
|
+
nlu_trace["matcher_layer"] = "exact_prefix"
|
|
112
167
|
else:
|
|
113
168
|
# Use Levenshtein distance for fuzzy matching with the full command part after @
|
|
114
169
|
# No match is ([], None), never (None, None) — len() here is safe.
|
|
@@ -133,16 +188,24 @@ class CommandNamePrediction:
|
|
|
133
188
|
f"utterance '{command}' in context '{command_context_name}'. "
|
|
134
189
|
"Deferring to the classifier instead of picking one."
|
|
135
190
|
)
|
|
191
|
+
nlu_trace["fuzzy_prematch_tie"] = [str(c) for c in best_matched_commands]
|
|
136
192
|
elif best_matched_commands:
|
|
137
193
|
command_name = best_matched_commands[0]
|
|
194
|
+
nlu_trace["matcher_layer"] = "fuzzy_prematch"
|
|
138
195
|
|
|
139
196
|
if nlu_pipeline_stage == NLUPipelineStage.INTENT_DETECTION:
|
|
140
197
|
if not command_name:
|
|
141
198
|
if cache_result := cache_match(self.path, command, modelpipeline, 0.85):
|
|
142
199
|
command_name = cache_result
|
|
200
|
+
nlu_trace["matcher_layer"] = "embedding_cache"
|
|
201
|
+
nlu_trace["cache_similarity_threshold"] = 0.85
|
|
143
202
|
else:
|
|
144
|
-
predictions=
|
|
203
|
+
predictions, classifier_details = (
|
|
204
|
+
command_router.predict_with_details(command)
|
|
205
|
+
)
|
|
145
206
|
# predictions = majority_vote_predictions(command_router, command)
|
|
207
|
+
nlu_trace["matcher_layer"] = "classifier"
|
|
208
|
+
nlu_trace["classifier"] = classifier_details
|
|
146
209
|
|
|
147
210
|
if len(predictions)==1:
|
|
148
211
|
command_name = predictions[0].split('/')[-1]
|
|
@@ -158,11 +221,15 @@ class CommandNamePrediction:
|
|
|
158
221
|
"will be prompted with the local candidates only and the "
|
|
159
222
|
"'this belongs to an ancestor context' signal is dropped."
|
|
160
223
|
)
|
|
224
|
+
nlu_trace["escalation_labels_discarded"] = [
|
|
225
|
+
str(label) for label in escalation_signals
|
|
226
|
+
]
|
|
161
227
|
|
|
162
228
|
error_msg = self._formulate_ambiguous_command_error_message(
|
|
163
229
|
predictions, "run_as_agent" in self.app_workflow.context)
|
|
164
230
|
|
|
165
231
|
# Store suggested commands
|
|
232
|
+
nlu_trace["candidates"] = [str(p) for p in predictions]
|
|
166
233
|
self._store_suggested_commands(self.path, predictions, 1)
|
|
167
234
|
return CommandNamePrediction.Output(error_msg=error_msg)
|
|
168
235
|
|
|
@@ -171,6 +238,7 @@ class CommandNamePrediction:
|
|
|
171
238
|
NLUPipelineStage.INTENT_MISUNDERSTANDING_CLARIFICATION
|
|
172
239
|
) and not command_name:
|
|
173
240
|
command_name = "what can i do?"
|
|
241
|
+
nlu_trace["matcher_layer"] = "clarification_default"
|
|
174
242
|
|
|
175
243
|
fully_qualified_command_name = self.resolve_fully_qualified_command_name(
|
|
176
244
|
command_name, command_name_dict)
|
|
@@ -10,7 +10,7 @@ from pydantic_core import PydanticUndefined
|
|
|
10
10
|
|
|
11
11
|
import fastworkflow
|
|
12
12
|
from fastworkflow.utils.logging import logger
|
|
13
|
-
from fastworkflow import ModuleType
|
|
13
|
+
from fastworkflow import ModuleType, tracing
|
|
14
14
|
|
|
15
15
|
from fastworkflow.utils.signatures import InputForParamExtraction
|
|
16
16
|
|
|
@@ -58,6 +58,43 @@ class ParameterExtraction:
|
|
|
58
58
|
self.command = command
|
|
59
59
|
|
|
60
60
|
def extract(self) -> "ParameterExtraction.Output":
|
|
61
|
+
"""Extract, wrapped in a ``fw.nlu.param_extraction`` span (D3 as
|
|
62
|
+
amended). The span records the extraction method (stored-merge /
|
|
63
|
+
xml-regex / llm), whether this is a NOT_FOUND retry round, and the
|
|
64
|
+
STRUCTURED validation outcome from ``validate_parameters`` —
|
|
65
|
+
missing/invalid fields, per-field db_lookup events, and the
|
|
66
|
+
validate_extracted_parameters verdict. A failed extraction is a
|
|
67
|
+
conversational state, not a span error: status stays ok and
|
|
68
|
+
``parameters_valid`` carries the signal; only an exception marks the
|
|
69
|
+
span error. Emission never affects extraction (no-op without a host).
|
|
70
|
+
"""
|
|
71
|
+
host = tracing.current_host()
|
|
72
|
+
span = tracing.start_span(
|
|
73
|
+
host,
|
|
74
|
+
tracing.SPAN_NLU_PARAM_EXTRACTION,
|
|
75
|
+
command_name=self.command_name,
|
|
76
|
+
attributes={"command_name": self.command_name},
|
|
77
|
+
)
|
|
78
|
+
diagnostics: dict = {}
|
|
79
|
+
try:
|
|
80
|
+
output = self._extract_impl(diagnostics)
|
|
81
|
+
except BaseException:
|
|
82
|
+
tracing.end_span(
|
|
83
|
+
host, span, status=tracing.STATUS_ERROR, attributes=diagnostics
|
|
84
|
+
)
|
|
85
|
+
raise
|
|
86
|
+
tracing.end_span(
|
|
87
|
+
host,
|
|
88
|
+
span,
|
|
89
|
+
status=tracing.STATUS_OK,
|
|
90
|
+
attributes={
|
|
91
|
+
**diagnostics,
|
|
92
|
+
"parameters_valid": output.parameters_are_valid,
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
return output
|
|
96
|
+
|
|
97
|
+
def _extract_impl(self, diagnostics: dict) -> "ParameterExtraction.Output":
|
|
61
98
|
app_workflow_folderpath = self.app_workflow.folderpath
|
|
62
99
|
app_command_routing_definition = fastworkflow.RoutingRegistry.get_definition(app_workflow_folderpath)
|
|
63
100
|
|
|
@@ -78,8 +115,10 @@ class ParameterExtraction:
|
|
|
78
115
|
self.command)
|
|
79
116
|
|
|
80
117
|
# If we have missing fields (in parameter extraction error state), try to apply the command directly
|
|
118
|
+
diagnostics["retry_round"] = bool(stored_params)
|
|
81
119
|
if stored_params:
|
|
82
120
|
new_params = self._extract_and_merge_missing_parameters(stored_params, self.command)
|
|
121
|
+
diagnostics["extraction_method"] = "stored_merge"
|
|
83
122
|
else:
|
|
84
123
|
# Check if we're in agentic mode (not assistant mode command)
|
|
85
124
|
is_agentic_mode = (
|
|
@@ -91,6 +130,7 @@ class ParameterExtraction:
|
|
|
91
130
|
if is_agentic_mode:
|
|
92
131
|
# Try regex-based extraction first in agentic mode
|
|
93
132
|
new_params = self._extract_parameters_from_xml(self.command, command_parameters_class)
|
|
133
|
+
diagnostics["extraction_method"] = "xml_regex"
|
|
94
134
|
|
|
95
135
|
# If regex extraction fails, fall back to LLM-based extraction
|
|
96
136
|
if new_params is None:
|
|
@@ -98,16 +138,19 @@ class ParameterExtraction:
|
|
|
98
138
|
command_parameters_class,
|
|
99
139
|
self.command_name,
|
|
100
140
|
app_workflow_folderpath)
|
|
141
|
+
diagnostics["extraction_method"] = "llm"
|
|
101
142
|
else:
|
|
102
143
|
# Use LLM-based extraction for assistant mode
|
|
103
144
|
new_params = input_for_param_extraction.extract_parameters(
|
|
104
145
|
command_parameters_class,
|
|
105
146
|
self.command_name,
|
|
106
147
|
app_workflow_folderpath)
|
|
148
|
+
diagnostics["extraction_method"] = "llm"
|
|
107
149
|
|
|
108
150
|
is_valid, error_msg, suggestions, missing_invalid_fields = \
|
|
109
151
|
input_for_param_extraction.validate_parameters(
|
|
110
|
-
self.app_workflow, self.command_name, new_params
|
|
152
|
+
self.app_workflow, self.command_name, new_params,
|
|
153
|
+
diagnostics=diagnostics
|
|
111
154
|
)
|
|
112
155
|
|
|
113
156
|
# Set all the missing and invalid fields to appropriate sentinel values before storing
|
|
@@ -117,9 +117,15 @@ class ChatSession:
|
|
|
117
117
|
"""
|
|
118
118
|
self._core = WorkflowExecutionContext(
|
|
119
119
|
run_as_agent=run_as_agent,
|
|
120
|
-
mirror_action_log_to_file=True,
|
|
121
120
|
generate_insights=generate_insights,
|
|
122
121
|
)
|
|
122
|
+
# CLI identity [R17]: a synthetic per-session channel so CLI turns are
|
|
123
|
+
# attributable in the observability store (conversation ids are minted
|
|
124
|
+
# by the store from Phase 2).
|
|
125
|
+
from datetime import datetime, timezone
|
|
126
|
+
self._core.bind_observability_identity(
|
|
127
|
+
channel_id=f"cli:{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%S.%f')}Z"
|
|
128
|
+
)
|
|
123
129
|
|
|
124
130
|
# Create queues for user messages and command outputs (CLI transport)
|
|
125
131
|
self._user_message_queue = Queue()
|
|
@@ -371,8 +377,10 @@ class ChatSession:
|
|
|
371
377
|
# Handle startup command/action
|
|
372
378
|
if self._startup_command:
|
|
373
379
|
last_output = self._core._execute_message(self._startup_command)
|
|
380
|
+
self._core.finalize_turn_for_observability(last_output)
|
|
374
381
|
elif self._startup_action:
|
|
375
382
|
last_output = self._core.process_action(self._startup_action)
|
|
383
|
+
self._core.finalize_turn_for_observability(last_output)
|
|
376
384
|
|
|
377
385
|
while (
|
|
378
386
|
not self.workflow_is_complete or self._keep_alive
|
|
@@ -384,6 +392,9 @@ class ChatSession:
|
|
|
384
392
|
last_output = self._core.process_action(message)
|
|
385
393
|
else:
|
|
386
394
|
last_output = self._core._execute_message(message)
|
|
395
|
+
# Emit the turn record/root-span close for observability;
|
|
396
|
+
# the CLI transport itself still rides the queues.
|
|
397
|
+
self._core.finalize_turn_for_observability(last_output)
|
|
387
398
|
|
|
388
399
|
except Empty:
|
|
389
400
|
continue
|