fastworkflow 2.18.3__tar.gz → 2.19.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.
Files changed (186) hide show
  1. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/PKG-INFO +1 -1
  2. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/__init__.py +10 -0
  3. fastworkflow-2.19.0/fastworkflow/active_workflow.py +52 -0
  4. fastworkflow-2.19.0/fastworkflow/chat_session.py +546 -0
  5. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/intent_clarification_agent.py +18 -3
  6. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/mcp_server.py +36 -7
  7. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/model_pipeline_training.py +53 -0
  8. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run/__main__.py +19 -0
  9. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/react.py +108 -15
  10. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/workflow_agent.py +68 -31
  11. fastworkflow-2.19.0/fastworkflow/workflow_execution_context.py +678 -0
  12. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/pyproject.toml +1 -1
  13. fastworkflow-2.18.3/fastworkflow/chat_session.py +0 -857
  14. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/LICENSE +0 -0
  15. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/README.md +0 -0
  16. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/.DS_Store +0 -0
  17. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_commands/.gitkeep +0 -0
  18. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/__init__.py +0 -0
  19. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/__init__.py +0 -0
  20. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/abort.py +0 -0
  21. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py +0 -0
  22. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/go_up.py +0 -0
  23. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/reset_context.py +0 -0
  24. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py +0 -0
  25. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_is_current_context.py +0 -0
  26. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/__init__.py +0 -0
  27. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +0 -0
  28. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/command_context_model.json +0 -0
  29. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/intent_detection.py +0 -0
  30. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/_workflows/command_metadata_extraction/parameter_extraction.py +0 -0
  31. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/__main__.py +0 -0
  32. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/ast_class_extractor.py +0 -0
  33. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/class_analysis_structures.py +0 -0
  34. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/cli_specification.md +0 -0
  35. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/command_dependency_resolver.py +0 -0
  36. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/command_file_generator.py +0 -0
  37. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/command_file_template.py +0 -0
  38. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/command_import_utils.py +0 -0
  39. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/command_stub_generator.py +0 -0
  40. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/context_folder_generator.py +0 -0
  41. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/context_model_generator.py +0 -0
  42. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/dependency_manager.py +0 -0
  43. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/dir_scanner.py +0 -0
  44. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/documentation_generator.py +0 -0
  45. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/genai_postprocessor.py +0 -0
  46. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/inheritance_block_regenerator.py +0 -0
  47. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/libcst_transformers.py +0 -0
  48. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/navigator_stub_generator.py +0 -0
  49. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/pydantic_model_generator.py +0 -0
  50. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/build/utterance_generator.py +0 -0
  51. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/cache_matching.py +0 -0
  52. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/cli.py +0 -0
  53. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/command_context_model.py +0 -0
  54. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/command_directory.py +0 -0
  55. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/command_executor.py +0 -0
  56. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/command_interfaces.py +0 -0
  57. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/command_metadata_api.py +0 -0
  58. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/command_routing.py +0 -0
  59. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/docs/context_modules_prd.txt +0 -0
  60. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/extended_workflow_example/README.md +0 -0
  61. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/extended_workflow_example/_commands/WorkItem/get_status.py +0 -0
  62. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/extended_workflow_example/_commands/generate_report.py +0 -0
  63. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/extended_workflow_example/_commands/startup.py +0 -0
  64. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/extended_workflow_example/simple_workflow_template.json +0 -0
  65. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/extended_workflow_example/workflow_inheritance_model.json +0 -0
  66. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/fastworkflow.env +0 -0
  67. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/fastworkflow.passwords.env +0 -0
  68. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/hello_world/_commands/README.md +0 -0
  69. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/hello_world/_commands/add_two_numbers.py +0 -0
  70. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/hello_world/_commands/context_inheritance_model.json +0 -0
  71. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/hello_world/application/add_two_numbers.py +0 -0
  72. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_1/_commands/context_inheritance_model.json +0 -0
  73. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_1/_commands/send_message.py +0 -0
  74. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_1/application/send_message.py +0 -0
  75. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_2/_commands/User/send_message.py +0 -0
  76. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_2/_commands/context_inheritance_model.json +0 -0
  77. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_2/_commands/startup.py +0 -0
  78. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_2/application/user.py +0 -0
  79. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_3/_commands/PremiumUser/send_priority_message.py +0 -0
  80. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_3/_commands/User/send_message.py +0 -0
  81. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_3/_commands/context_inheritance_model.json +0 -0
  82. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_3/_commands/initialize_user.py +0 -0
  83. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_3/application/user.py +0 -0
  84. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/_ChatRoom.py +0 -0
  85. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/add_user.py +0 -0
  86. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/broadcast_message.py +0 -0
  87. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/get_current_user.py +0 -0
  88. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/list_users.py +0 -0
  89. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/ChatRoom/set_current_user.py +0 -0
  90. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/_PremiumUser.py +0 -0
  91. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/PremiumUser/send_priority_message.py +0 -0
  92. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/User/_User.py +0 -0
  93. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/User/send_message.py +0 -0
  94. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/context_inheritance_model.json +0 -0
  95. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/_commands/set_root_context.py +0 -0
  96. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/application/chatroom.py +0 -0
  97. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/application/user.py +0 -0
  98. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/context_hierarchy_model.json +0 -0
  99. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/messaging_app_4/startup_action.json +0 -0
  100. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/calculate.py +0 -0
  101. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/cancel_pending_order.py +0 -0
  102. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/exchange_delivered_order_items.py +0 -0
  103. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_email.py +0 -0
  104. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/find_user_id_by_name_zip.py +0 -0
  105. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/get_order_details.py +0 -0
  106. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/get_product_details.py +0 -0
  107. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/get_user_details.py +0 -0
  108. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/list_all_product_types.py +0 -0
  109. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_address.py +0 -0
  110. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_items.py +0 -0
  111. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/modify_pending_order_payment.py +0 -0
  112. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/modify_user_address.py +0 -0
  113. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/return_delivered_order_items.py +0 -0
  114. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/_commands/transfer_to_human_agents.py +0 -0
  115. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/context_inheritance_model.json +0 -0
  116. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/retail_data/__init__.py +0 -0
  117. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/retail_data/orders.json +0 -0
  118. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/retail_data/products.json +0 -0
  119. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/retail_data/users.json +0 -0
  120. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/calculate.py +0 -0
  121. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/cancel_pending_order.py +0 -0
  122. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/exchange_delivered_order_items.py +0 -0
  123. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_email.py +0 -0
  124. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/find_user_id_by_name_zip.py +0 -0
  125. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/get_order_details.py +0 -0
  126. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/get_product_details.py +0 -0
  127. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/get_user_details.py +0 -0
  128. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/list_all_product_types.py +0 -0
  129. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_address.py +0 -0
  130. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_items.py +0 -0
  131. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/modify_pending_order_payment.py +0 -0
  132. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/modify_user_address.py +0 -0
  133. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/return_delivered_order_items.py +0 -0
  134. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/think.py +0 -0
  135. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/tool.py +0 -0
  136. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/tools/transfer_to_human_agents.py +0 -0
  137. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/retail_workflow/workflow_description.txt +0 -0
  138. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/__init__.py +0 -0
  139. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/_WorkItem.py +0 -0
  140. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/add_child_workitem.py +0 -0
  141. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/get_status.py +0 -0
  142. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/go_to_workitem.py +0 -0
  143. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/mark_as_complete.py +0 -0
  144. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_first_child_workitem.py +0 -0
  145. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_last_child_workitem.py +0 -0
  146. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_next_workitem.py +0 -0
  147. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/move_to_previous_workitem.py +0 -0
  148. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_all_child_workitems.py +0 -0
  149. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/remove_child_workitem.py +0 -0
  150. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/WorkItem/show_schema.py +0 -0
  151. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/_commands/startup.py +0 -0
  152. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/application/__init__.py +0 -0
  153. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/application/workitem.py +0 -0
  154. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/simple_workflow_template.json +0 -0
  155. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/examples/simple_workflow_template/startup_action.json +0 -0
  156. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/refine/__main__.py +0 -0
  157. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run/__init__.py +0 -0
  158. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/README.md +0 -0
  159. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/__init__.py +0 -0
  160. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/__main__.py +0 -0
  161. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/conversation_store.py +0 -0
  162. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/jwt_manager.py +0 -0
  163. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/mcp_specific.py +0 -0
  164. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/redoc_2_standalone_html.py +0 -0
  165. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/run_fastapi_mcp/utils.py +0 -0
  166. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/train/__init__.py +0 -0
  167. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/train/__main__.py +0 -0
  168. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/train/generate_synthetic.py +0 -0
  169. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/user_message_queues.py +0 -0
  170. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/__init__.py +0 -0
  171. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/chat_adapter.py +0 -0
  172. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/context_utils.py +0 -0
  173. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/dspy_cache_utils.py +0 -0
  174. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/dspy_logger.py +0 -0
  175. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/dspy_utils.py +0 -0
  176. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/env.py +0 -0
  177. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/fuzzy_match.py +0 -0
  178. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/generate_param_examples.py +0 -0
  179. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/logging.py +0 -0
  180. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/parameterize_func_decorator.py +0 -0
  181. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/pydantic_model_2_dspy_signature_class.py +0 -0
  182. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/python_utils.py +0 -0
  183. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/signatures.py +0 -0
  184. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/utils/startup_progress.py +0 -0
  185. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/workflow.py +0 -0
  186. {fastworkflow-2.18.3 → fastworkflow-2.19.0}/fastworkflow/workflow_inheritance_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastworkflow
3
- Version: 2.18.3
3
+ Version: 2.19.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
@@ -224,3 +224,13 @@ def get_workflow_id(workflow_id_str: str) -> int:
224
224
 
225
225
  from .workflow import Workflow as Workflow
226
226
  from .chat_session import ChatSession as ChatSession
227
+ from .active_workflow import (
228
+ get_active_workflow,
229
+ push_active_workflow,
230
+ pop_active_workflow,
231
+ clear_workflow_stack,
232
+ )
233
+ from .workflow_execution_context import (
234
+ WorkflowExecutionContext,
235
+ CommandCancelledError,
236
+ )
@@ -0,0 +1,52 @@
1
+ """
2
+ Context-scoped active workflow stack (per OS thread / asyncio task).
3
+
4
+ Replaces the per-ChatSession deque for resolving the current app workflow during
5
+ command execution and child-workflow nesting.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from contextvars import ContextVar
11
+ from typing import Optional
12
+
13
+ import fastworkflow
14
+ from fastworkflow.utils.logging import logger
15
+
16
+ # Immutable tuple used as a stack; each push/pop replaces the ContextVar value.
17
+ _workflow_stack_var: ContextVar[tuple[fastworkflow.Workflow, ...]] = ContextVar(
18
+ "active_workflow_stack",
19
+ default=(),
20
+ )
21
+
22
+
23
+ def get_active_workflow() -> Optional[fastworkflow.Workflow]:
24
+ """Get the currently active workflow (top of stack)."""
25
+ stack = _workflow_stack_var.get()
26
+ return stack[-1] if stack else None
27
+
28
+
29
+ def push_active_workflow(workflow: fastworkflow.Workflow) -> None:
30
+ """Push a workflow onto the context-local stack."""
31
+ stack = _workflow_stack_var.get()
32
+ new_stack = stack + (workflow,)
33
+ _workflow_stack_var.set(new_stack)
34
+ logger.debug(f"Workflow stack: {[w.id for w in new_stack]}")
35
+
36
+
37
+ def pop_active_workflow() -> Optional[fastworkflow.Workflow]:
38
+ """Pop a workflow from the context-local stack."""
39
+ stack = _workflow_stack_var.get()
40
+ if not stack:
41
+ return None
42
+ workflow = stack[-1]
43
+ new_stack = stack[:-1]
44
+ _workflow_stack_var.set(new_stack)
45
+ logger.debug(f"Workflow stack after pop: {[w.id for w in new_stack]}")
46
+ return workflow
47
+
48
+
49
+ def clear_workflow_stack() -> None:
50
+ """Clear the entire workflow stack for this context."""
51
+ _workflow_stack_var.set(())
52
+ logger.debug("Workflow stack cleared")
@@ -0,0 +1,546 @@
1
+ from enum import Enum
2
+ from queue import Empty, Queue
3
+ from threading import Thread
4
+ from typing import Optional
5
+ import contextlib
6
+ from pathlib import Path
7
+ import os
8
+ import time
9
+
10
+ import dspy
11
+ from dspy.clients import litellm
12
+
13
+ import fastworkflow
14
+ from fastworkflow import active_workflow
15
+ from fastworkflow.workflow_execution_context import WorkflowExecutionContext
16
+ from fastworkflow.utils.logging import logger
17
+ from fastworkflow.model_pipeline_training import CommandRouter
18
+ from fastworkflow.utils.startup_progress import StartupProgress
19
+
20
+
21
+ class SessionStatus(Enum):
22
+ STARTING = "STARTING"
23
+ RUNNING = "RUNNING"
24
+ STOPPING = "STOPPING"
25
+ STOPPED = "STOPPED"
26
+
27
+ class ChatWorker(Thread):
28
+ def __init__(self, chat_session: "ChatSession"):
29
+ super().__init__()
30
+ self.chat_session = chat_session
31
+ self.daemon = True
32
+
33
+ def run(self):
34
+ """Process messages for the root workflow"""
35
+ try:
36
+ self.chat_session._status = SessionStatus.RUNNING
37
+ workflow = self.chat_session._current_workflow
38
+ if workflow:
39
+ logger.debug(f"Started root workflow {workflow.id}")
40
+
41
+ # Run the workflow loop
42
+ self.chat_session._run_workflow_loop()
43
+
44
+ finally:
45
+ self.chat_session._status = SessionStatus.STOPPED
46
+ # Ensure workflow is popped if thread terminates unexpectedly
47
+ if self.chat_session.get_active_workflow() is not None:
48
+ self.chat_session.pop_active_workflow()
49
+
50
+ class ChatSession:
51
+ def get_active_workflow(self) -> Optional[fastworkflow.Workflow]:
52
+ """Get the currently active workflow.
53
+
54
+ Returns the top of the context-local stack when set (e.g. during a
55
+ message turn or explicit push); otherwise falls back to the bound app
56
+ workflow so callers on other threads see the session's workflow.
57
+ """
58
+ workflow = active_workflow.get_active_workflow()
59
+ if workflow is not None:
60
+ return workflow
61
+ return self._core.app_workflow if self._core else None
62
+
63
+ def push_active_workflow(self, workflow: fastworkflow.Workflow) -> None:
64
+ """Push a workflow onto the context-local stack."""
65
+ active_workflow.push_active_workflow(workflow)
66
+
67
+ def pop_active_workflow(self) -> Optional[fastworkflow.Workflow]:
68
+ """Pop a workflow from the context-local stack."""
69
+ return active_workflow.pop_active_workflow()
70
+
71
+ def clear_workflow_stack(self) -> None:
72
+ """Clear the entire workflow stack for this context."""
73
+ active_workflow.clear_workflow_stack()
74
+
75
+ def stop_workflow(self) -> None:
76
+ """
77
+ Stop the current workflow and clear the workflow stack.
78
+ This method is called when starting a new root workflow to ensure
79
+ the previous workflow is properly stopped and resources are cleaned up.
80
+ """
81
+ # Set status to stopping to signal the workflow loop to exit
82
+ self._status = SessionStatus.STOPPING
83
+
84
+ # Wait for the chat worker thread to finish if it exists
85
+ if self._chat_worker and self._chat_worker.is_alive():
86
+ self._chat_worker.join(timeout=5.0) # Wait up to 5 seconds
87
+ if self._chat_worker.is_alive():
88
+ logger.warning("Chat worker thread did not terminate within timeout")
89
+
90
+ # Clear the workflow stack
91
+ self.clear_workflow_stack()
92
+
93
+ # Reset status to stopped
94
+ self._status = SessionStatus.STOPPED
95
+
96
+ # Clear current workflow reference
97
+ self._current_workflow = None
98
+
99
+ logger.debug("Workflow stopped and workflow stack cleared")
100
+
101
+ def __init__(
102
+ self,
103
+ run_as_agent: bool = False,
104
+ ask_user_timeout: Optional[float] = None,
105
+ ):
106
+ """
107
+ Initialize a chat session.
108
+
109
+ Args:
110
+ run_as_agent: If True, use agent mode (DSPy-based tool selection).
111
+ If False (default), use traditional command execution.
112
+ ask_user_timeout: Topology B (suspend/resume) safety net only; it has
113
+ NO effect here. ChatSession is Topology A (blocking
114
+ CLI worker) and always waits for the human on
115
+ ask_user. Kept for API compatibility with the core.
116
+
117
+ A chat session can run multiple workflows that share the same message queues.
118
+ Use start_workflow() to start a specific workflow within this session.
119
+ """
120
+ self._core = WorkflowExecutionContext(
121
+ run_as_agent=run_as_agent,
122
+ ask_user_timeout=ask_user_timeout,
123
+ )
124
+
125
+ # Create queues for user messages and command outputs (CLI transport)
126
+ self._user_message_queue = Queue()
127
+ self._command_output_queue = Queue()
128
+ self._command_trace_queue = Queue()
129
+ self._core.set_transport_queues(
130
+ user_message_queue=self._user_message_queue,
131
+ command_output_queue=self._command_output_queue,
132
+ command_trace_queue=self._command_trace_queue,
133
+ )
134
+
135
+ self._status = SessionStatus.STOPPED
136
+ self._chat_worker = None
137
+ self._current_workflow = None
138
+ self._keep_alive = False
139
+
140
+ from fastworkflow.command_executor import CommandExecutor
141
+ self._CommandExecutor = CommandExecutor
142
+
143
+ # this intializes the conversation traces file name also
144
+ # which is necessary when starting a brand new chat session
145
+ self.clear_conversation_history()
146
+
147
+ def start_workflow(self,
148
+ workflow_folderpath: str,
149
+ workflow_id_str: Optional[str] = None,
150
+ parent_workflow_id: Optional[int] = None,
151
+ workflow_context: dict = None,
152
+ startup_command: str = "",
153
+ startup_action: Optional[fastworkflow.Action] = None,
154
+ keep_alive: bool = False,
155
+ project_folderpath: Optional[str] = None
156
+ ) -> Optional[fastworkflow.CommandOutput]:
157
+ """
158
+ Create and start a workflow within this chat session.
159
+
160
+ Args:
161
+ workflow_folderpath: The folder containing the fastworkflow Workflow
162
+ workflow_id_str: Arbitrary key used to persist the workflow state
163
+ parent_workflow_id: Persist this workflow under a parent workflow
164
+ workflow_context: The starting context for the workflow.
165
+ startup_command: Optional command to execute on startup
166
+ startup_action: Optional action to execute on startup
167
+ keep_alive: Whether to keep the chat session alive after workflow completion
168
+
169
+ Returns:
170
+ CommandOutput for non-keep_alive workflows, None otherwise
171
+ """
172
+ if startup_command and startup_action:
173
+ raise ValueError("Cannot provide both startup_command and startup_action")
174
+
175
+ litellm.drop_params = True # See https://docs.litellm.ai/docs/completion/drop_params
176
+
177
+ # Create the workflow
178
+ workflow = fastworkflow.Workflow.create(
179
+ workflow_folderpath,
180
+ workflow_id_str=workflow_id_str,
181
+ parent_workflow_id=parent_workflow_id,
182
+ workflow_context=workflow_context,
183
+ project_folderpath=project_folderpath
184
+ )
185
+
186
+ self._current_workflow = workflow
187
+ self._status = SessionStatus.STOPPED
188
+ self._startup_command = startup_command
189
+
190
+ if startup_action and startup_action.workflow_id is None:
191
+ startup_action.workflow_id = workflow.id
192
+ self._startup_action = startup_action
193
+ self._keep_alive = False if parent_workflow_id else keep_alive
194
+
195
+ # Check if we need to stop the current workflow
196
+ # Stop if this is a new root workflow (no parent, keep_alive=True)
197
+ current_workflow = self.get_active_workflow()
198
+ if (current_workflow and
199
+ parent_workflow_id is None and
200
+ self._keep_alive):
201
+ logger.info(f"Stopping current workflow {current_workflow.id} to start new root workflow {workflow.id}")
202
+ self.stop_workflow()
203
+
204
+ # ------------------------------------------------------------
205
+ # Eager warm-up of CommandRouter / ModelPipeline
206
+ # ------------------------------------------------------------
207
+ # Loading transformer checkpoints and moving them to device is
208
+ # expensive (~1 s). We do it here *once* for every model artifact
209
+ # directory so that the first user message does not pay the cost.
210
+ try:
211
+ command_info_root = Path(workflow.folderpath) / "___command_info"
212
+ if command_info_root.is_dir():
213
+ subdirs = [d for d in command_info_root.iterdir() if d.is_dir()]
214
+
215
+ # Tell the progress bar how many extra steps we are going to
216
+ # perform (one per directory plus one for the wildcard "*").
217
+ StartupProgress.add_total(len(subdirs) + 1)
218
+
219
+ for subdir in subdirs:
220
+ # Instantiating CommandRouter triggers ModelPipeline
221
+ # construction and caches it process-wide.
222
+ with contextlib.suppress(Exception):
223
+ CommandRouter(str(subdir))
224
+ StartupProgress.advance(f"Warm-up {subdir.name}")
225
+
226
+ # Also warm-up the global-context artefacts, which live in a
227
+ # pseudo-folder named '*' in some workflows.
228
+ with contextlib.suppress(Exception):
229
+ CommandRouter(str(command_info_root / '*'))
230
+ StartupProgress.advance("Warm-up global")
231
+ except Exception as warm_err: # pragma: no cover – warm-up must never fail
232
+ logger.debug(f"Model warm-up skipped due to error: {warm_err}")
233
+
234
+ self._core.bind_app_workflow(workflow)
235
+ self._core.keep_alive = self._keep_alive
236
+
237
+ # Start the workflow
238
+ if self._status != SessionStatus.STOPPED:
239
+ raise RuntimeError("Workflow already started")
240
+
241
+ self._status = SessionStatus.STARTING
242
+
243
+ # Agent + MCP tooling initialize lazily on first agent message (after contextvar push)
244
+
245
+ command_output = None
246
+ if self._keep_alive:
247
+ # Root workflow gets a worker thread
248
+ self._chat_worker = ChatWorker(self)
249
+ self._chat_worker.start()
250
+ else:
251
+ # Child workflows run their loop in the current thread
252
+ self._status = SessionStatus.RUNNING
253
+ command_output = self._run_workflow_loop()
254
+
255
+ return command_output
256
+
257
+ @property
258
+ def workflow_tool_agent(self):
259
+ """Get the workflow tool agent for agent mode."""
260
+ return self._core.workflow_tool_agent
261
+
262
+ @property
263
+ def intent_clarification_agent(self):
264
+ """Get the intent clarification agent for agent mode."""
265
+ return self._core.intent_clarification_agent
266
+
267
+ @property
268
+ def cme_workflow(self) -> fastworkflow.Workflow:
269
+ """Get the command metadata extraction workflow."""
270
+ return self._core.cme_workflow
271
+
272
+ @property
273
+ def app_workflow(self) -> Optional[fastworkflow.Workflow]:
274
+ """Get the bound application workflow."""
275
+ return self._core.app_workflow
276
+
277
+ @property
278
+ def run_as_agent(self) -> bool:
279
+ """Check if running in agent mode."""
280
+ return self._core.run_as_agent
281
+
282
+ @property
283
+ def ask_user_timeout(self) -> Optional[float]:
284
+ """Seconds to wait on ask_user before cancelling (None = block forever)."""
285
+ return self._core.ask_user_timeout
286
+
287
+ @property
288
+ def user_message_queue(self) -> Queue:
289
+ return self._user_message_queue
290
+
291
+ @property
292
+ def command_output_queue(self) -> Queue:
293
+ return self._command_output_queue
294
+
295
+ @property
296
+ def command_trace_queue(self) -> Queue:
297
+ return self._command_trace_queue
298
+
299
+ @property
300
+ def workflow_is_complete(self) -> bool:
301
+ workflow = self._core.app_workflow
302
+ return workflow.is_complete if workflow else True
303
+
304
+ @workflow_is_complete.setter
305
+ def workflow_is_complete(self, value: bool) -> None:
306
+ if workflow := self._core.app_workflow:
307
+ workflow.is_complete = value
308
+
309
+ @property
310
+ def conversation_history(self) -> dspy.History:
311
+ """Return the conversation history."""
312
+ return self._core.conversation_history
313
+
314
+ @property
315
+ def _conversation_history(self) -> dspy.History:
316
+ return self._core.conversation_history
317
+
318
+ @_conversation_history.setter
319
+ def _conversation_history(self, value: dspy.History) -> None:
320
+ self._core._conversation_history = value
321
+
322
+ # def clear_conversation_history(self, trace_filename_suffix: Optional[str] = None) -> None:
323
+ def clear_conversation_history(self) -> None:
324
+ """
325
+ Clear the conversation history.
326
+ This resets the conversation history to an empty state.
327
+ """
328
+ self._core.clear_conversation_history()
329
+ # Filename for conversation traces
330
+ # if trace_filename_suffix:
331
+ # self._conversation_traces_file_name: str = (
332
+ # f"conversation_traces_{trace_filename_suffix}"
333
+ # )
334
+ # else:
335
+ # self._conversation_traces_file_name: str = (
336
+ # f"conversation_traces_{datetime.now().strftime('%m_%d_%Y:%H_%M_%S')}.jsonl"
337
+ # )
338
+
339
+ def _run_workflow_loop(self) -> Optional[fastworkflow.CommandOutput]:
340
+ """
341
+ Run the workflow message processing loop.
342
+ For child workflows (keep_alive=False):
343
+ - Returns final CommandOutput when workflow completes
344
+ - All outputs (success or failure) are sent to queue during processing
345
+ """
346
+ last_output = None
347
+ workflow = self._current_workflow
348
+
349
+ try:
350
+ # Handle startup command/action
351
+ if self._startup_command:
352
+ last_output = self._core.process_message(self._startup_command)
353
+ elif self._startup_action:
354
+ last_output = self._core.process_action(self._startup_action)
355
+
356
+ while (
357
+ not self.workflow_is_complete or self._keep_alive
358
+ ) and self._status != SessionStatus.STOPPING:
359
+ try:
360
+ message = self.user_message_queue.get()
361
+
362
+ if isinstance(message, fastworkflow.Action):
363
+ last_output = self._core.process_action(message)
364
+ else:
365
+ last_output = self._core.process_message(message)
366
+
367
+ except Empty:
368
+ continue
369
+
370
+ # Return final output for child workflows, regardless of success/failure
371
+ if not self._keep_alive:
372
+ return last_output
373
+
374
+ finally:
375
+ self._status = SessionStatus.STOPPED
376
+ if self.get_active_workflow() is not None:
377
+ self.clear_workflow_stack()
378
+ logger.debug(f"Workflow {workflow.id if workflow else 'unknown'} completed")
379
+
380
+ return None
381
+
382
+ # def _is_mcp_tool_call(self, message: str) -> bool:
383
+ # """Detect if message is an MCP tool call JSON"""
384
+ # try:
385
+ # data = json.loads(message)
386
+ # return data.get("type") == "mcp_tool_call"
387
+ # except (json.JSONDecodeError, AttributeError):
388
+ # return False
389
+
390
+ # def _process_mcp_tool_call(self, message: str) -> fastworkflow.CommandOutput:
391
+ # # sourcery skip: class-extract-method, extract-method
392
+ # """Process an MCP tool call message"""
393
+ # workflow = self.get_active_workflow()
394
+
395
+ # try:
396
+ # # Parse JSON message
397
+ # data = json.loads(message)
398
+ # tool_call_data = data["tool_call"]
399
+
400
+ # # Create MCPToolCall object
401
+ # tool_call = fastworkflow.MCPToolCall(
402
+ # name=tool_call_data["name"],
403
+ # arguments=tool_call_data["arguments"]
404
+ # )
405
+
406
+ # # Execute via command executor
407
+ # mcp_result = self._CommandExecutor.perform_mcp_tool_call(
408
+ # workflow,
409
+ # tool_call,
410
+ # command_context=workflow.current_command_context_name
411
+ # )
412
+
413
+ # # Convert MCPToolResult back to CommandOutput for consistency
414
+ # command_output = self._convert_mcp_result_to_command_output(mcp_result)
415
+
416
+ # # Put in output queue if needed
417
+ # if (not command_output.success or self._keep_alive) and self.command_output_queue:
418
+ # self.command_output_queue.put(command_output)
419
+
420
+ # # Flush on successful or failed tool call – state may have changed.
421
+ # if workflow := self.get_active_workflow():
422
+ # workflow.flush()
423
+
424
+ # return command_output
425
+
426
+ # except Exception as e:
427
+ # logger.error(f"Error processing MCP tool call: {e}. Tool call content: {message}")
428
+ # return self._process_message(message) # process as a message
429
+
430
+ # def _convert_mcp_result_to_command_output(self, mcp_result: fastworkflow.MCPToolResult) -> fastworkflow.CommandOutput:
431
+ # """Convert MCPToolResult to CommandOutput for compatibility"""
432
+ # command_response = fastworkflow.CommandResponse(
433
+ # response=mcp_result.content[0].text if mcp_result.content else "No response",
434
+ # success=not mcp_result.isError
435
+ # )
436
+
437
+ # command_output = fastworkflow.CommandOutput(command_responses=[command_response])
438
+ # command_output._mcp_source = mcp_result # Mark for special formatting
439
+ # return command_output
440
+
441
+ def _process_message(self, message: str) -> fastworkflow.CommandOutput:
442
+ """Back-compat shim: delegate single-message execution to the core."""
443
+ return self._core.process_message(message)
444
+
445
+ def _process_agent_message(self, message: str) -> fastworkflow.CommandOutput:
446
+ """Back-compat shim: delegate agent-message execution to the core."""
447
+ return self._core.process_message(message)
448
+
449
+ def _process_action(self, action: fastworkflow.Action) -> fastworkflow.CommandOutput:
450
+ """Back-compat shim: delegate action execution to the core."""
451
+ return self._core.process_action(action)
452
+
453
+ def profile_invoke_command(self, message: str):
454
+ """
455
+ Profile the invoke_command method with detailed focus on performance issues.
456
+
457
+ Args:
458
+ message: The message to process
459
+ output_file: Name of the profile output file
460
+
461
+ Returns:
462
+ The result of the invoke_command call
463
+ """
464
+ from datetime import datetime
465
+
466
+ # Generate a unique filename with timestamp
467
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
468
+ output_file = f"invoke_command_{timestamp}.prof"
469
+
470
+ import cProfile
471
+ import pstats
472
+ import io
473
+ import time
474
+
475
+ # Create a Profile object
476
+ profiler = cProfile.Profile()
477
+
478
+ # Enable profiling
479
+ profiler.enable()
480
+
481
+ # Execute invoke_command and time it
482
+ start_time = time.time()
483
+ if self._core.app_workflow:
484
+ self.push_active_workflow(self._core.app_workflow)
485
+ try:
486
+ result = self._CommandExecutor.invoke_command(self, message)
487
+ finally:
488
+ if self._core.app_workflow:
489
+ self.pop_active_workflow()
490
+ elapsed = time.time() - start_time
491
+
492
+ # Disable profiling
493
+ profiler.disable()
494
+
495
+ # Save profile results to file
496
+ profiler.dump_stats(output_file)
497
+ print(f"\nProfile data saved to {os.path.abspath(output_file)}")
498
+ print(f"invoke_command execution took {elapsed:.4f} seconds")
499
+
500
+ # Create summary report
501
+ report_file = f"{os.path.splitext(output_file)[0]}_report.txt"
502
+ with open(report_file, "w") as f:
503
+ # Overall summary by cumulative time
504
+ s = io.StringIO()
505
+ ps = pstats.Stats(profiler, stream=s)
506
+ ps.sort_stats('cumulative').print_stats(30)
507
+ f.write(f"=== CUMULATIVE TIME SUMMARY (TOP 30) === Execution time: {elapsed:.4f}s\n")
508
+ f.write(s.getvalue())
509
+ f.write("\n\n")
510
+
511
+ # Internal time summary
512
+ s = io.StringIO()
513
+ ps = pstats.Stats(profiler, stream=s)
514
+ ps.sort_stats('time').print_stats(30)
515
+ f.write("=== INTERNAL TIME SUMMARY (TOP 30) ===\n")
516
+ f.write(s.getvalue())
517
+ f.write("\n\n")
518
+
519
+ # Most called functions
520
+ s = io.StringIO()
521
+ ps = pstats.Stats(profiler, stream=s)
522
+ ps.sort_stats('calls').print_stats(30)
523
+ f.write("=== MOST CALLED FUNCTIONS (TOP 30) ===\n")
524
+ f.write(s.getvalue())
525
+
526
+ # Focus areas for issues 3-7
527
+ focus_areas = [
528
+ ('lock_contention', ['lock', 'acquire', 'release'], 'time'),
529
+ ('model_operations', ['torch', 'nn', 'model'], 'cumulative'),
530
+ ('command_extraction', ['wildcard.py', 'extract', 'predict'], 'cumulative'),
531
+ ('file_io', ['_get_sessiondb_folderpath', '_load', '_save'], 'cumulative'),
532
+ ('frequent_operations', ['startswith', 'isinstance', 'get'], 'calls')
533
+ ]
534
+
535
+ for name, patterns, sort_by in focus_areas:
536
+ f.write(f"\n\n=== {name.upper()} ===\n")
537
+ for pattern in patterns:
538
+ s = io.StringIO()
539
+ ps = pstats.Stats(profiler, stream=s)
540
+ ps.sort_stats(sort_by).print_stats(pattern, 10)
541
+ f.write(f"\nPattern: '{pattern}'\n")
542
+ f.write(s.getvalue())
543
+
544
+ print(f"Detailed report saved to {os.path.abspath(report_file)}")
545
+
546
+ return result
@@ -7,6 +7,8 @@ import json
7
7
  import dspy
8
8
 
9
9
  import fastworkflow
10
+ from fastworkflow.utils.logging import logger
11
+ from fastworkflow.workflow_execution_context import CommandCancelledError
10
12
  from fastworkflow.utils.react import fastWorkflowReAct
11
13
  from fastworkflow.command_metadata_api import CommandMetadataAPI
12
14
 
@@ -64,13 +66,26 @@ def _ask_user_for_clarification(
64
66
  Returns:
65
67
  User's response
66
68
  """
69
+ user_queue = chat_session.user_message_queue
70
+ output_queue = chat_session.command_output_queue
71
+ if user_queue is None:
72
+ raise CommandCancelledError(
73
+ "ask_user requires a user_message_queue (not available in this context)"
74
+ )
75
+
76
+ active = chat_session.get_active_workflow()
77
+ workflow_name = active.folderpath.split("/")[-1] if active else ""
67
78
  command_output = fastworkflow.CommandOutput(
68
79
  command_responses=[fastworkflow.CommandResponse(response=clarification_request)],
69
- workflow_name=chat_session.get_active_workflow().folderpath.split('/')[-1]
80
+ workflow_name=workflow_name,
70
81
  )
71
- chat_session.command_output_queue.put(command_output)
82
+ if output_queue is not None:
83
+ output_queue.put(command_output)
72
84
 
73
- user_response = chat_session.user_message_queue.get()
85
+ # Topology A (blocking): block indefinitely for the human's answer. The nested
86
+ # intent-clarification ask_user is last-resort; in Topology B there is no queue
87
+ # and it aborts via CommandCancelledError above (no timeout involved).
88
+ user_response = user_queue.get()
74
89
 
75
90
  # Log to action.jsonl (shared with main agent)
76
91
  with open("action.jsonl", "a", encoding="utf-8") as f: