quantalogic 0.51.0__tar.gz → 0.52.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.
- {quantalogic-0.51.0 → quantalogic-0.52.0}/PKG-INFO +89 -2
- {quantalogic-0.51.0 → quantalogic-0.52.0}/README.md +88 -1
- {quantalogic-0.51.0 → quantalogic-0.52.0}/pyproject.toml +1 -1
- quantalogic-0.52.0/quantalogic/flow/__init__.py +40 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/flow/flow_extractor.py +32 -103
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/flow/flow_generator.py +6 -2
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/flow/flow_manager.py +33 -24
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/flow/flow_manager_schema.py +2 -3
- quantalogic-0.52.0/quantalogic/flow/flow_mermaid.py +240 -0
- quantalogic-0.52.0/quantalogic/flow/flow_validator.py +335 -0
- quantalogic-0.52.0/quantalogic/flow/flow_yaml.md +490 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/__init__.py +3 -2
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/tool.py +129 -3
- quantalogic-0.51.0/quantalogic/flow/__init__.py +0 -23
- quantalogic-0.51.0/quantalogic/flow/flow_yaml.md +0 -506
- {quantalogic-0.51.0 → quantalogic-0.52.0}/LICENSE +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/agent.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/agent_config.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/agent_factory.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/coding_agent.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/config.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/console_print_events.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/console_print_token.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/create_custom_agent.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/docs_cli.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/event_emitter.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/flow/flow.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/generative_model.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/get_model_info.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/interactive_text_editor.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/main.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/memory.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/model_info.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/model_info_list.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/model_info_litellm.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/model_names.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/memory_compaction_prompt.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/observation_response_format.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/repeated_tool_call_error.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/system_prompt.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/task_prompt.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/task_summary_prompt.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/tools_prompt.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts/variables_prompt.j2 +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/prompts.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/quantlitellm.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/search_agent.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/agent_server.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/models.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/routes.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/state.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/static/js/event_visualizer.js +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/static/js/quantalogic.js +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/server/templates/index.html +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/task_file_reader.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/task_runner.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tool_manager.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/agent_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/composio/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/composio/composio.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/database/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/database/generate_database_report_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/database/sql_query_tool_advanced.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/markdown_to_docx_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/markdown_to_epub_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/markdown_to_html_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/markdown_to_latex_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/markdown_to_pdf_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/document_tools/markdown_to_pptx_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/download_http_file_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/duckduckgo_search_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/edit_whole_content_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/elixir_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/execute_bash_command_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/alpha_vantage_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/ccxt_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/finance_llm_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/google_finance.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/market_intelligence_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/technical_analysis_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/tradingview_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/finance/yahoo_finance.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/git/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/git/bitbucket_clone_repo_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/git/bitbucket_operations_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/git/clone_repo_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/git/git_operations_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/google_packages/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/google_packages/google_news_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/grep_app_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/image_generation/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/image_generation/dalle_e.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/input_question_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/jinja_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/c_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/cpp_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/go_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/java_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/javascript_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/python_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/rust_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/scala_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/language_handlers/typescript_handler.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/list_directory_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/llm_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/llm_vision_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/markitdown_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/nasa_packages/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/nasa_packages/models.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/nasa_packages/nasa_apod_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/nasa_packages/nasa_neows_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/nasa_packages/services.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/nodejs_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/presentation_tools/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/presentation_tools/presentation_llm_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/product_hunt/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/product_hunt/product_hunt_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/product_hunt/services.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/python_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/rag_tool/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/rag_tool/document_metadata.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/rag_tool/query_response.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/rag_tool/rag_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/rag_tool/rag_tool_beta.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/read_file_block_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/read_file_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/read_html_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/replace_in_file_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/ripgrep_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/safe_python_interpreter_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/search_definition_names.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/sequence_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/serpapi_search_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/sql_query_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/task_complete_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/unified_diff_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/utilities/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/utilities/csv_processor_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/utilities/download_file_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/utilities/mermaid_validator_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/utils/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/utils/create_sample_database.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/utils/generate_database_report.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/wikipedia_search_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/tools/write_file_tool.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/__init__.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/ask_user_validation.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/async_utils.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/check_version.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/download_http_file.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/get_all_models.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/get_coding_environment.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/get_environment.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/get_quantalogic_rules_content.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/git_ls.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/lm_studio_model_info.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/python_interpreter.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/read_file.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/read_http_text_content.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/utils/xml_utility.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/version.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/version_check.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/welcome_message.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/xml_parser.py +0 -0
- {quantalogic-0.51.0 → quantalogic-0.52.0}/quantalogic/xml_tool_parser.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: quantalogic
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.52.0
|
4
4
|
Summary: QuantaLogic ReAct Agents
|
5
5
|
Author: Raphaël MANSUY
|
6
6
|
Author-email: raphael.mansuy@gmail.com
|
@@ -88,6 +88,8 @@ At [QuantaLogic](https://www.quantalogic.app), we spotted a black hole: amazing
|
|
88
88
|
- [Quick Start](#quick-start)
|
89
89
|
- [ReAct Framework: Dynamic Agents](#react-framework-dynamic-agents)
|
90
90
|
- [Flow Module: Structured Workflows](#flow-module-structured-workflows)
|
91
|
+
- 📘 **[Workflow YAML DSL Specification](./quantalogic/flow/flow_yaml.md)**: Comprehensive guide to defining powerful, structured workflows using our Domain-Specific Language
|
92
|
+
- 📚 **[Flow YAML Documentation](https://quantalogic.github.io/quantalogic/flow/flow_yaml)**: Dive into the official documentation for a deeper understanding of Flow YAML and its applications
|
91
93
|
- [ReAct vs. Flow: Pick Your Power](#react-vs-flow-pick-your-power)
|
92
94
|
- [Using the CLI](#using-the-cli)
|
93
95
|
- [Examples That Spark Joy](#examples-that-spark-joy)
|
@@ -357,7 +359,15 @@ Perfect for coding, debugging, or answering wild questions on the fly.
|
|
357
359
|
|
358
360
|
## Flow Module: Structured Workflows
|
359
361
|
|
360
|
-
The **Flow module** is your architect—building workflows that hum with precision. It
|
362
|
+
The **Flow module** is your architect—building workflows that hum with precision. It's all about nodes, transitions, and a steady rhythm, ideal for repeatable missions.
|
363
|
+
|
364
|
+
🔍 **Want to dive deeper?** Check out our comprehensive [Workflow YAML DSL Specification](./quantalogic/flow/flow_yaml.md), a detailed guide that walks you through defining powerful, structured workflows. From basic node configurations to complex transition logic, this documentation is your roadmap to mastering workflow design with QuantaLogic.
|
365
|
+
|
366
|
+
📚 **For a deeper understanding of Flow YAML and its applications, please refer to the official [Flow YAML Documentation](https://quantalogic.github.io/quantalogic/flow/flow_yaml).**
|
367
|
+
|
368
|
+
The Flow YAML documentation provides a comprehensive overview of the Flow YAML language, including its syntax, features, and best practices. It's a valuable resource for anyone looking to create complex workflows with QuantaLogic.
|
369
|
+
|
370
|
+
Additionally, the Flow YAML documentation includes a number of examples and tutorials to help you get started with creating your own workflows. These examples cover a range of topics, from simple workflows to more complex scenarios, and are designed to help you understand how to use Flow YAML to create powerful and flexible workflows.
|
361
371
|
|
362
372
|
### The Building Blocks
|
363
373
|
- **Nodes**: Tasks like functions or LLM calls.
|
@@ -629,6 +639,83 @@ mypy quantalogic # Check types
|
|
629
639
|
ruff check quantalogic # Lint it
|
630
640
|
```
|
631
641
|
|
642
|
+
### Create Custom Tools
|
643
|
+
The `create_tool()` function transforms any Python function into a reusable Tool:
|
644
|
+
|
645
|
+
```python
|
646
|
+
from quantalogic.tools import create_tool
|
647
|
+
|
648
|
+
def weather_lookup(city: str, country: str = "US") -> dict:
|
649
|
+
"""Retrieve current weather for a given location.
|
650
|
+
|
651
|
+
Args:
|
652
|
+
city: Name of the city to look up
|
653
|
+
country: Two-letter country code (default: US)
|
654
|
+
|
655
|
+
Returns:
|
656
|
+
Dictionary with weather information
|
657
|
+
"""
|
658
|
+
# Implement weather lookup logic here
|
659
|
+
return {"temperature": 22, "condition": "Sunny"}
|
660
|
+
|
661
|
+
# Convert the function to a Tool
|
662
|
+
weather_tool = create_tool(weather_lookup)
|
663
|
+
|
664
|
+
# Now you can use it as a Tool
|
665
|
+
print(weather_tool.to_markdown()) # Generate tool documentation
|
666
|
+
result = weather_tool.execute(city="New York") # Execute as a tool
|
667
|
+
```
|
668
|
+
|
669
|
+
#### Using Custom Tools with ReAct Agent
|
670
|
+
|
671
|
+
Here's how to integrate custom tools with a ReAct Agent:
|
672
|
+
|
673
|
+
```python
|
674
|
+
from quantalogic import Agent
|
675
|
+
from quantalogic.tools import create_tool, PythonTool
|
676
|
+
|
677
|
+
# Create a custom stock price lookup tool
|
678
|
+
def get_stock_price(symbol: str) -> str:
|
679
|
+
"""Get the current price of a stock by its ticker symbol.
|
680
|
+
|
681
|
+
Args:
|
682
|
+
symbol: Stock ticker symbol (e.g., AAPL, MSFT)
|
683
|
+
|
684
|
+
Returns:
|
685
|
+
Current stock price information
|
686
|
+
"""
|
687
|
+
# In a real implementation, you would fetch from an API
|
688
|
+
prices = {"AAPL": 185.92, "MSFT": 425.27, "GOOGL": 175.43}
|
689
|
+
if symbol in prices:
|
690
|
+
return f"{symbol} is currently trading at ${prices[symbol]}"
|
691
|
+
return f"Could not find price for {symbol}"
|
692
|
+
|
693
|
+
# Create an agent with standard and custom tools
|
694
|
+
agent = Agent(
|
695
|
+
model_name="gpt-4o",
|
696
|
+
tools=[
|
697
|
+
PythonTool(), # Standard Python execution tool
|
698
|
+
create_tool(get_stock_price) # Custom stock price tool
|
699
|
+
]
|
700
|
+
)
|
701
|
+
|
702
|
+
# The agent can now use both tools to solve tasks
|
703
|
+
result = agent.solve_task(
|
704
|
+
"Write a Python function to calculate investment growth, "
|
705
|
+
"then analyze Apple stock's current price"
|
706
|
+
)
|
707
|
+
|
708
|
+
print(result)
|
709
|
+
```
|
710
|
+
|
711
|
+
In this example, the agent can seamlessly use both the standard `PythonTool` and your custom stock price lookup tool to complete the task.
|
712
|
+
|
713
|
+
Key features of `create_tool()`:
|
714
|
+
- 🔧 Automatically converts functions to Tools
|
715
|
+
- 📝 Extracts metadata from function signature and docstring
|
716
|
+
- 🔍 Supports both synchronous and asynchronous functions
|
717
|
+
- 🛠️ Generates tool documentation and validation
|
718
|
+
|
632
719
|
---
|
633
720
|
|
634
721
|
## Contributing
|
@@ -42,6 +42,8 @@ At [QuantaLogic](https://www.quantalogic.app), we spotted a black hole: amazing
|
|
42
42
|
- [Quick Start](#quick-start)
|
43
43
|
- [ReAct Framework: Dynamic Agents](#react-framework-dynamic-agents)
|
44
44
|
- [Flow Module: Structured Workflows](#flow-module-structured-workflows)
|
45
|
+
- 📘 **[Workflow YAML DSL Specification](./quantalogic/flow/flow_yaml.md)**: Comprehensive guide to defining powerful, structured workflows using our Domain-Specific Language
|
46
|
+
- 📚 **[Flow YAML Documentation](https://quantalogic.github.io/quantalogic/flow/flow_yaml)**: Dive into the official documentation for a deeper understanding of Flow YAML and its applications
|
45
47
|
- [ReAct vs. Flow: Pick Your Power](#react-vs-flow-pick-your-power)
|
46
48
|
- [Using the CLI](#using-the-cli)
|
47
49
|
- [Examples That Spark Joy](#examples-that-spark-joy)
|
@@ -311,7 +313,15 @@ Perfect for coding, debugging, or answering wild questions on the fly.
|
|
311
313
|
|
312
314
|
## Flow Module: Structured Workflows
|
313
315
|
|
314
|
-
The **Flow module** is your architect—building workflows that hum with precision. It
|
316
|
+
The **Flow module** is your architect—building workflows that hum with precision. It's all about nodes, transitions, and a steady rhythm, ideal for repeatable missions.
|
317
|
+
|
318
|
+
🔍 **Want to dive deeper?** Check out our comprehensive [Workflow YAML DSL Specification](./quantalogic/flow/flow_yaml.md), a detailed guide that walks you through defining powerful, structured workflows. From basic node configurations to complex transition logic, this documentation is your roadmap to mastering workflow design with QuantaLogic.
|
319
|
+
|
320
|
+
📚 **For a deeper understanding of Flow YAML and its applications, please refer to the official [Flow YAML Documentation](https://quantalogic.github.io/quantalogic/flow/flow_yaml).**
|
321
|
+
|
322
|
+
The Flow YAML documentation provides a comprehensive overview of the Flow YAML language, including its syntax, features, and best practices. It's a valuable resource for anyone looking to create complex workflows with QuantaLogic.
|
323
|
+
|
324
|
+
Additionally, the Flow YAML documentation includes a number of examples and tutorials to help you get started with creating your own workflows. These examples cover a range of topics, from simple workflows to more complex scenarios, and are designed to help you understand how to use Flow YAML to create powerful and flexible workflows.
|
315
325
|
|
316
326
|
### The Building Blocks
|
317
327
|
- **Nodes**: Tasks like functions or LLM calls.
|
@@ -583,6 +593,83 @@ mypy quantalogic # Check types
|
|
583
593
|
ruff check quantalogic # Lint it
|
584
594
|
```
|
585
595
|
|
596
|
+
### Create Custom Tools
|
597
|
+
The `create_tool()` function transforms any Python function into a reusable Tool:
|
598
|
+
|
599
|
+
```python
|
600
|
+
from quantalogic.tools import create_tool
|
601
|
+
|
602
|
+
def weather_lookup(city: str, country: str = "US") -> dict:
|
603
|
+
"""Retrieve current weather for a given location.
|
604
|
+
|
605
|
+
Args:
|
606
|
+
city: Name of the city to look up
|
607
|
+
country: Two-letter country code (default: US)
|
608
|
+
|
609
|
+
Returns:
|
610
|
+
Dictionary with weather information
|
611
|
+
"""
|
612
|
+
# Implement weather lookup logic here
|
613
|
+
return {"temperature": 22, "condition": "Sunny"}
|
614
|
+
|
615
|
+
# Convert the function to a Tool
|
616
|
+
weather_tool = create_tool(weather_lookup)
|
617
|
+
|
618
|
+
# Now you can use it as a Tool
|
619
|
+
print(weather_tool.to_markdown()) # Generate tool documentation
|
620
|
+
result = weather_tool.execute(city="New York") # Execute as a tool
|
621
|
+
```
|
622
|
+
|
623
|
+
#### Using Custom Tools with ReAct Agent
|
624
|
+
|
625
|
+
Here's how to integrate custom tools with a ReAct Agent:
|
626
|
+
|
627
|
+
```python
|
628
|
+
from quantalogic import Agent
|
629
|
+
from quantalogic.tools import create_tool, PythonTool
|
630
|
+
|
631
|
+
# Create a custom stock price lookup tool
|
632
|
+
def get_stock_price(symbol: str) -> str:
|
633
|
+
"""Get the current price of a stock by its ticker symbol.
|
634
|
+
|
635
|
+
Args:
|
636
|
+
symbol: Stock ticker symbol (e.g., AAPL, MSFT)
|
637
|
+
|
638
|
+
Returns:
|
639
|
+
Current stock price information
|
640
|
+
"""
|
641
|
+
# In a real implementation, you would fetch from an API
|
642
|
+
prices = {"AAPL": 185.92, "MSFT": 425.27, "GOOGL": 175.43}
|
643
|
+
if symbol in prices:
|
644
|
+
return f"{symbol} is currently trading at ${prices[symbol]}"
|
645
|
+
return f"Could not find price for {symbol}"
|
646
|
+
|
647
|
+
# Create an agent with standard and custom tools
|
648
|
+
agent = Agent(
|
649
|
+
model_name="gpt-4o",
|
650
|
+
tools=[
|
651
|
+
PythonTool(), # Standard Python execution tool
|
652
|
+
create_tool(get_stock_price) # Custom stock price tool
|
653
|
+
]
|
654
|
+
)
|
655
|
+
|
656
|
+
# The agent can now use both tools to solve tasks
|
657
|
+
result = agent.solve_task(
|
658
|
+
"Write a Python function to calculate investment growth, "
|
659
|
+
"then analyze Apple stock's current price"
|
660
|
+
)
|
661
|
+
|
662
|
+
print(result)
|
663
|
+
```
|
664
|
+
|
665
|
+
In this example, the agent can seamlessly use both the standard `PythonTool` and your custom stock price lookup tool to complete the task.
|
666
|
+
|
667
|
+
Key features of `create_tool()`:
|
668
|
+
- 🔧 Automatically converts functions to Tools
|
669
|
+
- 📝 Extracts metadata from function signature and docstring
|
670
|
+
- 🔍 Supports both synchronous and asynchronous functions
|
671
|
+
- 🛠️ Generates tool documentation and validation
|
672
|
+
|
586
673
|
---
|
587
674
|
|
588
675
|
## Contributing
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""
|
2
|
+
Flow Package Initialization
|
3
|
+
|
4
|
+
This module initializes the flow package and provides package-level imports.
|
5
|
+
Now supports nested workflows for hierarchical flow definitions.
|
6
|
+
|
7
|
+
Key Visualization Utilities:
|
8
|
+
- generate_mermaid_diagram(): Convert workflow definitions to visual Mermaid flowcharts
|
9
|
+
- Supports pastel-colored node styling
|
10
|
+
- Generates interactive, readable workflow diagrams
|
11
|
+
- Handles complex workflows with multiple node types
|
12
|
+
|
13
|
+
- Generates descriptive labels
|
14
|
+
- Supports conditional node detection
|
15
|
+
"""
|
16
|
+
|
17
|
+
from loguru import logger
|
18
|
+
|
19
|
+
# Expose key components for easy importing
|
20
|
+
from .flow import Nodes, Workflow, WorkflowEngine
|
21
|
+
from .flow_extractor import extract_workflow_from_file
|
22
|
+
from .flow_generator import generate_executable_script
|
23
|
+
from .flow_manager import WorkflowManager
|
24
|
+
from .flow_mermaid import generate_mermaid_diagram
|
25
|
+
from .flow_validator import validate_workflow_definition
|
26
|
+
|
27
|
+
# Define which symbols are exported when using `from flow import *`
|
28
|
+
__all__ = [
|
29
|
+
"WorkflowManager",
|
30
|
+
"Nodes",
|
31
|
+
"Workflow",
|
32
|
+
"WorkflowEngine",
|
33
|
+
"generate_mermaid_diagram",
|
34
|
+
"extract_workflow_from_file",
|
35
|
+
"generate_executable_script",
|
36
|
+
"validate_workflow_definition"
|
37
|
+
]
|
38
|
+
|
39
|
+
# Package-level logger configuration
|
40
|
+
logger.info("Initializing Quantalogic Flow Package")
|
@@ -3,6 +3,7 @@ import os
|
|
3
3
|
|
4
4
|
from loguru import logger
|
5
5
|
|
6
|
+
from quantalogic.flow.flow_generator import generate_executable_script # Import from flow_generator
|
6
7
|
from quantalogic.flow.flow_manager import WorkflowManager # Added for YAML saving
|
7
8
|
from quantalogic.flow.flow_manager_schema import (
|
8
9
|
FunctionDefinition,
|
@@ -396,7 +397,7 @@ class WorkflowExtractor(ast.NodeVisitor):
|
|
396
397
|
"sub_workflow": WorkflowStructure(
|
397
398
|
start=sub_extractor.start_node,
|
398
399
|
transitions=[
|
399
|
-
TransitionDefinition(
|
400
|
+
TransitionDefinition(from_node=t[0], to_node=t[1], condition=t[2]) for t in sub_extractor.transitions
|
400
401
|
],
|
401
402
|
),
|
402
403
|
"inputs": list(inputs.keys()),
|
@@ -494,7 +495,7 @@ def extract_workflow_from_file(file_path):
|
|
494
495
|
|
495
496
|
# Construct TransitionDefinition objects
|
496
497
|
transitions = [
|
497
|
-
TransitionDefinition(
|
498
|
+
TransitionDefinition(from_node=from_node, to_node=to_node, condition=cond)
|
498
499
|
for from_node, to_node, cond in extractor.transitions
|
499
500
|
]
|
500
501
|
|
@@ -509,95 +510,7 @@ def extract_workflow_from_file(file_path):
|
|
509
510
|
return workflow_def, extractor.global_vars
|
510
511
|
|
511
512
|
|
512
|
-
|
513
|
-
"""
|
514
|
-
Generate an executable Python script from a WorkflowDefinition with global variables.
|
515
|
-
|
516
|
-
Args:
|
517
|
-
workflow_def: The WorkflowDefinition object containing the workflow details.
|
518
|
-
global_vars: Dictionary of global variables extracted from the source file.
|
519
|
-
output_file: The path where the executable script will be written.
|
520
|
-
|
521
|
-
The generated script includes:
|
522
|
-
- A shebang using `uv run` for environment management.
|
523
|
-
- Metadata specifying the required Python version and dependencies.
|
524
|
-
- Global variables from the original script.
|
525
|
-
- Embedded functions included directly in the script.
|
526
|
-
- Workflow instantiation using direct chaining syntax.
|
527
|
-
- A default initial_context matching the example.
|
528
|
-
"""
|
529
|
-
with open(output_file, "w") as f:
|
530
|
-
# Write the shebang and metadata
|
531
|
-
f.write("#!/usr/bin/env -S uv run\n")
|
532
|
-
f.write("# /// script\n")
|
533
|
-
f.write('# requires-python = ">=3.12"\n')
|
534
|
-
f.write("# dependencies = [\n")
|
535
|
-
f.write('# "loguru",\n')
|
536
|
-
f.write('# "litellm",\n')
|
537
|
-
f.write('# "pydantic>=2.0",\n')
|
538
|
-
f.write('# "anyio",\n')
|
539
|
-
f.write('# "quantalogic>=0.35",\n')
|
540
|
-
f.write('# "jinja2",\n')
|
541
|
-
f.write('# "instructor[litellm]",\n') # Kept for potential structured LLM support
|
542
|
-
f.write("# ]\n")
|
543
|
-
f.write("# ///\n\n")
|
544
|
-
|
545
|
-
# Write necessary imports
|
546
|
-
f.write("import anyio\n")
|
547
|
-
f.write("from typing import List\n")
|
548
|
-
f.write("from loguru import logger\n")
|
549
|
-
f.write("from quantalogic.flow import Nodes, Workflow\n\n")
|
550
|
-
|
551
|
-
# Write global variables
|
552
|
-
for var_name, value in global_vars.items():
|
553
|
-
f.write(f"{var_name} = {repr(value)}\n")
|
554
|
-
f.write("\n")
|
555
|
-
|
556
|
-
# Embed functions from workflow_def
|
557
|
-
for func_name, func_def in workflow_def.functions.items():
|
558
|
-
if func_def.type == "embedded":
|
559
|
-
if func_def.code is not None:
|
560
|
-
f.write(func_def.code + "\n\n")
|
561
|
-
else:
|
562
|
-
f.write("\n\n")
|
563
|
-
|
564
|
-
# Define workflow using chaining syntax
|
565
|
-
f.write("# Define the workflow using simplified syntax with automatic node registration\n")
|
566
|
-
f.write("workflow = (\n")
|
567
|
-
f.write(f' Workflow("{workflow_def.workflow.start}")\n')
|
568
|
-
for trans in workflow_def.workflow.transitions:
|
569
|
-
_from_node = trans.from_
|
570
|
-
to_node = trans.to
|
571
|
-
condition = trans.condition or "None"
|
572
|
-
if condition != "None":
|
573
|
-
# Ensure condition is formatted as a lambda if not already
|
574
|
-
if not condition.startswith("lambda ctx:"):
|
575
|
-
condition = f"lambda ctx: {condition}"
|
576
|
-
f.write(f' .then("{to_node}", condition={condition})\n')
|
577
|
-
for observer in workflow_def.observers:
|
578
|
-
f.write(f" .add_observer({observer})\n")
|
579
|
-
f.write(")\n\n")
|
580
|
-
|
581
|
-
# Main asynchronous function to run the workflow
|
582
|
-
f.write("async def main():\n")
|
583
|
-
f.write(' """Main function to run the story generation workflow."""\n')
|
584
|
-
f.write(" initial_context = {\n")
|
585
|
-
f.write(' "genre": "science fiction",\n')
|
586
|
-
f.write(' "num_chapters": 3,\n')
|
587
|
-
f.write(' "chapters": [],\n')
|
588
|
-
f.write(' "completed_chapters": 0,\n')
|
589
|
-
f.write(' "style": "descriptive"\n')
|
590
|
-
f.write(" } # Customize initial_context as needed\n")
|
591
|
-
f.write(" engine = workflow.build()\n")
|
592
|
-
f.write(" result = await engine.run(initial_context)\n")
|
593
|
-
f.write(' logger.info(f"Workflow result: {result}")\n\n')
|
594
|
-
|
595
|
-
# Entry point to execute the main function
|
596
|
-
f.write('if __name__ == "__main__":\n')
|
597
|
-
f.write(" anyio.run(main)\n")
|
598
|
-
|
599
|
-
# Set executable permissions (rwxr-xr-x)
|
600
|
-
os.chmod(output_file, 0o755)
|
513
|
+
# The generate_executable_script function has been moved to flow_generator.py
|
601
514
|
|
602
515
|
|
603
516
|
def print_workflow_definition(workflow_def):
|
@@ -638,11 +551,11 @@ def print_workflow_definition(workflow_def):
|
|
638
551
|
print("Transitions:")
|
639
552
|
for trans in workflow_def.workflow.transitions:
|
640
553
|
condition_str = f" [Condition: {trans.condition}]" if trans.condition else ""
|
641
|
-
if isinstance(trans.
|
642
|
-
for to_node in trans.
|
643
|
-
print(f"- {trans.
|
554
|
+
if isinstance(trans.to_node, list):
|
555
|
+
for to_node in trans.to_node:
|
556
|
+
print(f"- {trans.from_node} -> {to_node}{condition_str}")
|
644
557
|
else:
|
645
|
-
print(f"- {trans.
|
558
|
+
print(f"- {trans.from_node} -> {trans.to_node}{condition_str}")
|
646
559
|
|
647
560
|
print("\n#### Observers:")
|
648
561
|
for observer in workflow_def.observers:
|
@@ -650,23 +563,39 @@ def print_workflow_definition(workflow_def):
|
|
650
563
|
|
651
564
|
|
652
565
|
def main():
|
653
|
-
"""Demonstrate
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
566
|
+
"""Demonstrate extracting a workflow from a Python file and saving it to YAML."""
|
567
|
+
import argparse
|
568
|
+
import sys
|
569
|
+
|
570
|
+
parser = argparse.ArgumentParser(description='Extract workflow from a Python file')
|
571
|
+
parser.add_argument('file_path', nargs='?', default="examples/qflow/story_generator_agent.py",
|
572
|
+
help='Path to the Python file containing the workflow')
|
573
|
+
parser.add_argument('--output', '-o', default="./generated_workflow.py",
|
574
|
+
help='Output path for the executable Python script')
|
575
|
+
parser.add_argument('--yaml', '-y', default="workflow_definition.yaml",
|
576
|
+
help='Output path for the YAML workflow definition')
|
577
|
+
|
578
|
+
args = parser.parse_args()
|
579
|
+
file_path = args.file_path
|
580
|
+
output_file_python = args.output
|
581
|
+
yaml_output_path = args.yaml
|
582
|
+
|
583
|
+
if not os.path.exists(file_path):
|
584
|
+
logger.error(f"File '{file_path}' not found. Please provide a valid file path.")
|
585
|
+
logger.info("Example usage: python -m quantalogic.flow.flow_extractor path/to/your/workflow_file.py")
|
586
|
+
sys.exit(1)
|
587
|
+
|
659
588
|
try:
|
660
589
|
workflow_def, global_vars = extract_workflow_from_file(file_path)
|
661
590
|
logger.info(f"Successfully extracted workflow from '{file_path}'")
|
662
591
|
print_workflow_definition(workflow_def)
|
663
592
|
generate_executable_script(workflow_def, global_vars, output_file_python)
|
593
|
+
logger.info(f"Executable script generated at '{output_file_python}'")
|
594
|
+
|
664
595
|
# Save the workflow to a YAML file
|
665
596
|
manager = WorkflowManager(workflow_def)
|
666
597
|
manager.save_to_yaml(yaml_output_path)
|
667
598
|
logger.info(f"Workflow saved to YAML file '{yaml_output_path}'")
|
668
|
-
except FileNotFoundError:
|
669
|
-
logger.error(f"File '{file_path}' not found. Please ensure it exists in the specified directory.")
|
670
599
|
except Exception as e:
|
671
600
|
logger.error(f"Failed to parse or save workflow from '{file_path}': {e}")
|
672
601
|
|
@@ -60,14 +60,18 @@ def generate_executable_script(workflow_def: WorkflowDefinition, global_vars: di
|
|
60
60
|
f.write("workflow = (\n")
|
61
61
|
f.write(f' Workflow("{workflow_def.workflow.start}")\n')
|
62
62
|
for trans in workflow_def.workflow.transitions:
|
63
|
-
_from_node = trans.
|
64
|
-
to_node = trans.
|
63
|
+
_from_node = trans.from_node
|
64
|
+
to_node = trans.to_node
|
65
65
|
condition = trans.condition or "None"
|
66
66
|
if condition != "None":
|
67
67
|
# Ensure condition is formatted as a lambda if not already
|
68
68
|
if not condition.startswith("lambda ctx:"):
|
69
69
|
condition = f"lambda ctx: {condition}"
|
70
70
|
f.write(f' .then("{to_node}", condition={condition})\n')
|
71
|
+
# Add observers if any exist in the workflow definition
|
72
|
+
if hasattr(workflow_def, 'observers'):
|
73
|
+
for observer in workflow_def.observers:
|
74
|
+
f.write(f" .add_observer({observer})\n")
|
71
75
|
f.write(")\n\n")
|
72
76
|
|
73
77
|
# Main asynchronous function to run the workflow
|
@@ -65,7 +65,7 @@ class WorkflowManager:
|
|
65
65
|
self.workflow.workflow.transitions = [
|
66
66
|
t
|
67
67
|
for t in self.workflow.workflow.transitions
|
68
|
-
if t.
|
68
|
+
if t.from_node != name and (isinstance(t.to_node, str) or name not in t.to_node)
|
69
69
|
]
|
70
70
|
if self.workflow.workflow.start == name:
|
71
71
|
self.workflow.workflow.start = None
|
@@ -99,27 +99,36 @@ class WorkflowManager:
|
|
99
99
|
|
100
100
|
def add_transition(
|
101
101
|
self,
|
102
|
-
|
103
|
-
|
102
|
+
from_node: str,
|
103
|
+
to_node: Union[str, List[str]],
|
104
104
|
condition: Optional[str] = None,
|
105
|
+
strict: bool = True,
|
105
106
|
) -> None:
|
106
|
-
"""Add a transition between nodes
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
107
|
+
"""Add a transition between nodes.
|
108
|
+
|
109
|
+
Args:
|
110
|
+
from_node: Source node name
|
111
|
+
to_node: Target node name or list of target node names
|
112
|
+
condition: Optional condition for the transition
|
113
|
+
strict: If True, validates that all nodes exist before adding the transition.
|
114
|
+
If False, allows adding transitions to non-existent nodes.
|
115
|
+
"""
|
116
|
+
if strict:
|
117
|
+
if from_node not in self.workflow.nodes:
|
118
|
+
raise ValueError(f"Source node '{from_node}' does not exist")
|
119
|
+
if isinstance(to_node, str):
|
120
|
+
if to_node not in self.workflow.nodes:
|
121
|
+
raise ValueError(f"Target node '{to_node}' does not exist")
|
122
|
+
else:
|
123
|
+
for t in to_node:
|
124
|
+
if t not in self.workflow.nodes:
|
125
|
+
raise ValueError(f"Target node '{t}' does not exist")
|
116
126
|
# Create TransitionDefinition with named parameters
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
transition = TransitionDefinition.model_validate(transition_dict)
|
127
|
+
transition = TransitionDefinition(
|
128
|
+
from_node=from_node,
|
129
|
+
to_node=to_node,
|
130
|
+
condition=condition
|
131
|
+
)
|
123
132
|
self.workflow.workflow.transitions.append(transition)
|
124
133
|
|
125
134
|
def set_start_node(self, name: str) -> None:
|
@@ -281,8 +290,8 @@ class WorkflowManager:
|
|
281
290
|
sub_workflows[node_name] = sub_wf
|
282
291
|
added_sub_nodes = set()
|
283
292
|
for trans in node_def.sub_workflow.transitions:
|
284
|
-
from_node = trans.
|
285
|
-
to_nodes = [trans.
|
293
|
+
from_node = trans.from_node
|
294
|
+
to_nodes = [trans.to_node] if isinstance(trans.to_node, str) else trans.to_node
|
286
295
|
if from_node not in added_sub_nodes:
|
287
296
|
sub_wf.node(from_node)
|
288
297
|
added_sub_nodes.add(from_node)
|
@@ -362,8 +371,8 @@ class WorkflowManager:
|
|
362
371
|
|
363
372
|
added_nodes = set()
|
364
373
|
for trans in self.workflow.workflow.transitions:
|
365
|
-
from_node = trans.
|
366
|
-
to_nodes = [trans.
|
374
|
+
from_node = trans.from_node
|
375
|
+
to_nodes = [trans.to_node] if isinstance(trans.to_node, str) else trans.to_node
|
367
376
|
if from_node not in added_nodes and from_node not in sub_workflows:
|
368
377
|
wf.node(from_node)
|
369
378
|
added_nodes.add(from_node)
|
@@ -441,7 +450,7 @@ def main():
|
|
441
450
|
manager.add_node(name="start", function="greet")
|
442
451
|
manager.add_node(name="end", function="farewell")
|
443
452
|
manager.set_start_node("start")
|
444
|
-
manager.add_transition(
|
453
|
+
manager.add_transition(from_node="start", to_node="end")
|
445
454
|
manager.add_observer("monitor") # Add the observer
|
446
455
|
manager.save_to_yaml("workflow.yaml")
|
447
456
|
new_manager = WorkflowManager()
|
@@ -128,12 +128,11 @@ class NodeDefinition(BaseModel):
|
|
128
128
|
class TransitionDefinition(BaseModel):
|
129
129
|
"""Definition of a transition between nodes."""
|
130
130
|
|
131
|
-
|
131
|
+
from_node: str = Field(
|
132
132
|
...,
|
133
133
|
description="Source node name for the transition.",
|
134
|
-
alias="from", # Supports YAML aliasing
|
135
134
|
)
|
136
|
-
|
135
|
+
to_node: Union[str, List[str]] = Field(
|
137
136
|
..., description="Target node(s). A string for sequential, a list for parallel execution."
|
138
137
|
)
|
139
138
|
condition: Optional[str] = Field(
|