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