lionagi 0.13.3__tar.gz → 0.13.4__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 (267) hide show
  1. {lionagi-0.13.3 → lionagi-0.13.4}/.github/workflows/ci.yml +6 -0
  2. {lionagi-0.13.3 → lionagi-0.13.4}/PKG-INFO +11 -8
  3. {lionagi-0.13.3 → lionagi-0.13.4}/README.md +9 -7
  4. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/config.py +1 -1
  5. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/__init__.py +2 -0
  6. lionagi-0.13.4/lionagi/operations/flow.py +436 -0
  7. lionagi-0.13.4/lionagi/operations/node.py +107 -0
  8. lionagi-0.13.4/lionagi/protocols/graph/__init__.py +9 -0
  9. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/claude_code_cli.py +1 -4
  10. lionagi-0.13.4/lionagi/session/__init__.py +8 -0
  11. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/session/session.py +47 -0
  12. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/settings.py +2 -2
  13. lionagi-0.13.4/lionagi/version.py +1 -0
  14. {lionagi-0.13.3 → lionagi-0.13.4}/pyproject.toml +2 -1
  15. {lionagi-0.13.3 → lionagi-0.13.4}/uv.lock +270 -1
  16. lionagi-0.13.3/lionagi/tools/__init__.py +0 -3
  17. lionagi-0.13.3/lionagi/tools/file/__init__.py +0 -3
  18. lionagi-0.13.3/lionagi/version.py +0 -1
  19. {lionagi-0.13.3 → lionagi-0.13.4}/.coveragerc +0 -0
  20. {lionagi-0.13.3 → lionagi-0.13.4}/.env.example +0 -0
  21. {lionagi-0.13.3 → lionagi-0.13.4}/.github/FUNDING.yml +0 -0
  22. {lionagi-0.13.3 → lionagi-0.13.4}/.github/dependabot.yml +0 -0
  23. {lionagi-0.13.3 → lionagi-0.13.4}/.github/workflows/codeql.yml +0 -0
  24. {lionagi-0.13.3 → lionagi-0.13.4}/.github/workflows/docs.yml +0 -0
  25. {lionagi-0.13.3 → lionagi-0.13.4}/.github/workflows/release.yml +0 -0
  26. {lionagi-0.13.3 → lionagi-0.13.4}/.gitignore +0 -0
  27. {lionagi-0.13.3 → lionagi-0.13.4}/.pre-commit-config.yaml +0 -0
  28. {lionagi-0.13.3 → lionagi-0.13.4}/.python-version +0 -0
  29. {lionagi-0.13.3 → lionagi-0.13.4}/CODE_OF_CONDUCT.md +0 -0
  30. {lionagi-0.13.3 → lionagi-0.13.4}/CONTRIBUTING.md +0 -0
  31. {lionagi-0.13.3 → lionagi-0.13.4}/LICENSE +0 -0
  32. {lionagi-0.13.3 → lionagi-0.13.4}/cookbooks/ch01_get_started.md +0 -0
  33. {lionagi-0.13.3 → lionagi-0.13.4}/cookbooks/ch02_concepts.md +0 -0
  34. {lionagi-0.13.3 → lionagi-0.13.4}/cookbooks/using_claude_code.py +0 -0
  35. {lionagi-0.13.3 → lionagi-0.13.4}/docs/Makefile +0 -0
  36. {lionagi-0.13.3 → lionagi-0.13.4}/docs/_static/custom.css +0 -0
  37. {lionagi-0.13.3 → lionagi-0.13.4}/docs/_templates/layout.html +0 -0
  38. {lionagi-0.13.3 → lionagi-0.13.4}/docs/conf.py +0 -0
  39. {lionagi-0.13.3 → lionagi-0.13.4}/docs/index.rst +0 -0
  40. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/action.rst +0 -0
  41. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/adapter.rst +0 -0
  42. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/branch.rst +0 -0
  43. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/branch_operations.rst +0 -0
  44. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/concepts.rst +0 -0
  45. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/element_id.rst +0 -0
  46. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/event.rst +0 -0
  47. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/form.rst +0 -0
  48. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/graph.rst +0 -0
  49. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/index.rst +0 -0
  50. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/instruct.rst +0 -0
  51. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/lib_file.rst +0 -0
  52. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/lib_nested.rst +0 -0
  53. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/lib_package.rst +0 -0
  54. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/lib_schema.rst +0 -0
  55. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/lib_validate.rst +0 -0
  56. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/log.rst +0 -0
  57. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/mail.rst +0 -0
  58. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/message.rst +0 -0
  59. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/models.rst +0 -0
  60. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/operative_step.rst +0 -0
  61. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/pile.rst +0 -0
  62. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/processor.rst +0 -0
  63. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/progression.rst +0 -0
  64. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/service.rst +0 -0
  65. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/session.rst +0 -0
  66. {lionagi-0.13.3 → lionagi-0.13.4}/docs/modules/utils.rst +0 -0
  67. {lionagi-0.13.3 → lionagi-0.13.4}/docs/tutorials/get_started.rst +0 -0
  68. {lionagi-0.13.3 → lionagi-0.13.4}/docs/tutorials/get_started_pt2.rst +0 -0
  69. {lionagi-0.13.3 → lionagi-0.13.4}/docs/tutorials/get_started_pt3.rst +0 -0
  70. {lionagi-0.13.3 → lionagi-0.13.4}/docs/tutorials/index.rst +0 -0
  71. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/__init__.py +0 -0
  72. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/_class_registry.py +0 -0
  73. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/_errors.py +0 -0
  74. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/_types.py +0 -0
  75. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/__init__.py +0 -0
  76. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/action.py +0 -0
  77. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/base.py +0 -0
  78. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/code.py +0 -0
  79. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/file.py +0 -0
  80. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/instruct.py +0 -0
  81. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/reason.py +0 -0
  82. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/fields/research.py +0 -0
  83. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/__init__.py +0 -0
  84. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/__init__.py +0 -0
  85. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/chunk.py +0 -0
  86. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/concat.py +0 -0
  87. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/concat_files.py +0 -0
  88. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/file_ops.py +0 -0
  89. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/params.py +0 -0
  90. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/process.py +0 -0
  91. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/file/save.py +0 -0
  92. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/__init__.py +0 -0
  93. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/flatten.py +0 -0
  94. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/nfilter.py +0 -0
  95. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/nget.py +0 -0
  96. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/ninsert.py +0 -0
  97. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/nmerge.py +0 -0
  98. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/npop.py +0 -0
  99. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/nset.py +0 -0
  100. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/unflatten.py +0 -0
  101. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/nested/utils.py +0 -0
  102. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/package/__init__.py +0 -0
  103. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/package/imports.py +0 -0
  104. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/package/management.py +0 -0
  105. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/package/params.py +0 -0
  106. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/package/system.py +0 -0
  107. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/parse.py +0 -0
  108. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/schema/__init__.py +0 -0
  109. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/schema/as_readable.py +0 -0
  110. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/schema/extract_code_block.py +0 -0
  111. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/schema/extract_docstring.py +0 -0
  112. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/schema/function_to_schema.py +0 -0
  113. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/schema/json_schema.py +0 -0
  114. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/schema/load_pydantic_model_from_schema.py +0 -0
  115. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/__init__.py +0 -0
  116. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/base.py +0 -0
  117. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/llmlingua.py +0 -0
  118. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/perplexity.py +0 -0
  119. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/symbolic_compress_context.py +0 -0
  120. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang.py +0 -0
  121. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/base.py +0 -0
  122. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/abstract_algebra.toml +0 -0
  123. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/category_theory.toml +0 -0
  124. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/complex_analysis.toml +0 -0
  125. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/framework_options.json +0 -0
  126. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/group_theory.toml +0 -0
  127. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/math_logic.toml +0 -0
  128. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/reflective_patterns.toml +0 -0
  129. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/set_theory.toml +0 -0
  130. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/frameworks/topology_fundamentals.toml +0 -0
  131. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/mapping/lion_emoji_mapping.toml +0 -0
  132. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/mapping/python_math_mapping.toml +0 -0
  133. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/mapping/rust_chinese_mapping.toml +0 -0
  134. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/resources/utility/base_synthlang_system_prompt.toml +0 -0
  135. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/synthlang_/translate_to_synthlang.py +0 -0
  136. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/token_transform/types.py +0 -0
  137. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/validate/__init__.py +0 -0
  138. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/validate/common_field_validators.py +0 -0
  139. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/validate/fuzzy_match_keys.py +0 -0
  140. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/validate/fuzzy_validate_mapping.py +0 -0
  141. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/validate/string_similarity.py +0 -0
  142. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/libs/validate/validate_boolean.py +0 -0
  143. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/models/__init__.py +0 -0
  144. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/models/field_model.py +0 -0
  145. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/models/hashable_model.py +0 -0
  146. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/models/model_params.py +0 -0
  147. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/models/note.py +0 -0
  148. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/models/operable_model.py +0 -0
  149. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/models/schema_model.py +0 -0
  150. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/ReAct/ReAct.py +0 -0
  151. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/ReAct/__init__.py +0 -0
  152. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/ReAct/utils.py +0 -0
  153. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/_act/__init__.py +0 -0
  154. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/_act/act.py +0 -0
  155. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/brainstorm/__init__.py +0 -0
  156. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/brainstorm/brainstorm.py +0 -0
  157. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/brainstorm/prompt.py +0 -0
  158. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/chat/__init__.py +0 -0
  159. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/chat/chat.py +0 -0
  160. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/communicate/__init__.py +0 -0
  161. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/communicate/communicate.py +0 -0
  162. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/instruct/__init__.py +0 -0
  163. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/instruct/instruct.py +0 -0
  164. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/interpret/__init__.py +0 -0
  165. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/interpret/interpret.py +0 -0
  166. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/manager.py +0 -0
  167. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/operate/__init__.py +0 -0
  168. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/operate/operate.py +0 -0
  169. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/parse/__init__.py +0 -0
  170. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/parse/parse.py +0 -0
  171. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/plan/__init__.py +0 -0
  172. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/plan/plan.py +0 -0
  173. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/plan/prompt.py +0 -0
  174. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/select/__init__.py +0 -0
  175. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/select/select.py +0 -0
  176. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/select/utils.py +0 -0
  177. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/translate/__init__.py +0 -0
  178. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/translate/translate.py +0 -0
  179. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/types.py +0 -0
  180. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/operations/utils.py +0 -0
  181. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/__init__.py +0 -0
  182. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/_concepts.py +0 -0
  183. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/action/__init__.py +0 -0
  184. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/action/function_calling.py +0 -0
  185. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/action/manager.py +0 -0
  186. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/action/tool.py +0 -0
  187. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/forms/__init__.py +0 -0
  188. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/forms/base.py +0 -0
  189. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/forms/flow.py +0 -0
  190. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/forms/form.py +0 -0
  191. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/forms/report.py +0 -0
  192. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/generic/__init__.py +0 -0
  193. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/generic/element.py +0 -0
  194. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/generic/event.py +0 -0
  195. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/generic/log.py +0 -0
  196. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/generic/pile.py +0 -0
  197. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/generic/processor.py +0 -0
  198. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/generic/progression.py +0 -0
  199. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/graph/edge.py +0 -0
  200. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/graph/graph.py +0 -0
  201. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/graph/node.py +0 -0
  202. {lionagi-0.13.3/lionagi/protocols/graph → lionagi-0.13.4/lionagi/protocols/mail}/__init__.py +0 -0
  203. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/mail/exchange.py +0 -0
  204. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/mail/mail.py +0 -0
  205. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/mail/mailbox.py +0 -0
  206. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/mail/manager.py +0 -0
  207. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/mail/package.py +0 -0
  208. {lionagi-0.13.3/lionagi/protocols/mail → lionagi-0.13.4/lionagi/protocols/messages}/__init__.py +0 -0
  209. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/action_request.py +0 -0
  210. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/action_response.py +0 -0
  211. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/assistant_response.py +0 -0
  212. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/base.py +0 -0
  213. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/instruction.py +0 -0
  214. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/manager.py +0 -0
  215. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/message.py +0 -0
  216. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/system.py +0 -0
  217. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/templates/README.md +0 -0
  218. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/templates/action_request.jinja2 +0 -0
  219. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/templates/action_response.jinja2 +0 -0
  220. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -0
  221. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -0
  222. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/templates/system_message.jinja2 +0 -0
  223. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -0
  224. {lionagi-0.13.3/lionagi/protocols/messages → lionagi-0.13.4/lionagi/protocols/operatives}/__init__.py +0 -0
  225. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/operatives/operative.py +0 -0
  226. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/operatives/step.py +0 -0
  227. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/protocols/types.py +0 -0
  228. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/py.typed +0 -0
  229. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/__init__.py +0 -0
  230. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/__init__.py +0 -0
  231. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/api_calling.py +0 -0
  232. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/endpoint.py +0 -0
  233. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/endpoint_config.py +0 -0
  234. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/header_factory.py +0 -0
  235. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/match_endpoint.py +0 -0
  236. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/__init__.py +0 -0
  237. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/anthropic_.py +0 -0
  238. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/claude_code_.py +0 -0
  239. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/exa_.py +0 -0
  240. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/oai_.py +0 -0
  241. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/ollama_.py +0 -0
  242. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/connections/providers/perplexity_.py +0 -0
  243. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/imodel.py +0 -0
  244. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/manager.py +0 -0
  245. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/rate_limited_processor.py +0 -0
  246. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/resilience.py +0 -0
  247. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/third_party/README.md +0 -0
  248. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/third_party/__init__.py +0 -0
  249. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/third_party/anthropic_models.py +0 -0
  250. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/third_party/exa_models.py +0 -0
  251. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/third_party/openai_models.py +0 -0
  252. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/third_party/pplx_models.py +0 -0
  253. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/token_calculator.py +0 -0
  254. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/service/types.py +0 -0
  255. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/session/branch.py +0 -0
  256. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/session/prompts.py +0 -0
  257. {lionagi-0.13.3/lionagi/protocols/operatives → lionagi-0.13.4/lionagi/tools}/__init__.py +0 -0
  258. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/tools/base.py +0 -0
  259. {lionagi-0.13.3/lionagi/session → lionagi-0.13.4/lionagi/tools/file}/__init__.py +0 -0
  260. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/tools/file/reader.py +0 -0
  261. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/tools/memory/tools.py +0 -0
  262. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/tools/types.py +0 -0
  263. {lionagi-0.13.3 → lionagi-0.13.4}/lionagi/utils.py +0 -0
  264. {lionagi-0.13.3 → lionagi-0.13.4}/main.py +0 -0
  265. {lionagi-0.13.3 → lionagi-0.13.4}/scripts/README.md +0 -0
  266. {lionagi-0.13.3 → lionagi-0.13.4}/scripts/concat.py +0 -0
  267. {lionagi-0.13.3 → lionagi-0.13.4}/scripts/config.py +0 -0
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [ main, develop ]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  jobs:
10
13
  test:
11
14
  runs-on: ubuntu-latest
@@ -52,6 +55,9 @@ jobs:
52
55
  needs: test
53
56
  runs-on: ubuntu-latest
54
57
  if: github.event_name == 'push' && github.ref == 'refs/heads/main'
58
+ permissions:
59
+ contents: read
60
+ packages: write
55
61
  steps:
56
62
  - uses: actions/checkout@v4
57
63
  - name: Set up Python
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lionagi
3
- Version: 0.13.3
3
+ Version: 0.13.4
4
4
  Summary: An Intelligence Operating System.
5
5
  Author-email: HaiyangLi <quantocean.li@gmail.com>, Liangbingyan Luo <llby_luo@outlook.com>
6
6
  License: Apache License
@@ -225,6 +225,7 @@ Requires-Dist: anyio>=4.8.0
225
225
  Requires-Dist: backoff>=2.2.1
226
226
  Requires-Dist: jinja2>=3.1.0
227
227
  Requires-Dist: json-repair>=0.47.8
228
+ Requires-Dist: matplotlib>=3.9.0
228
229
  Requires-Dist: pillow>=11.0.0
229
230
  Requires-Dist: psutil>=7.0.0
230
231
  Requires-Dist: pydantic-settings>=2.8.0
@@ -273,7 +274,7 @@ Description-Content-Type: text/markdown
273
274
 
274
275
  # LION - Language InterOperable Network
275
276
 
276
- ## An Intelligence Operating System SDK
277
+ ## An Agentic Intelligence SDK
277
278
 
278
279
  LionAGI is a robust framework for orchestrating multi-step AI operations with
279
280
  precise control. Bring together multiple models, advanced ReAct reasoning, tool
@@ -409,19 +410,21 @@ LionAGI now supports Anthropic's [Claude Code SDK](https://github.com/anthropics
409
410
  from lionagi import iModel, Branch
410
411
 
411
412
  # Create a Claude Code model
412
- coder = iModel(
413
+ model = iModel(
413
414
  provider="claude_code",
414
- endpoint="code",
415
- model="claude-sonnet-4-20250514",
415
+ endpoint="query_cli",
416
+ model="sonnet",
416
417
  allowed_tools=["Write", "Read", "Edit"], # Control which tools Claude can use
418
+ permission_mode = "bypassPermissions", # Bypass tool permission checks (use with caution!),
419
+ verbose_output=True, # Enable detailed output for debugging
417
420
  )
418
421
 
419
422
  # Start a coding session
420
- branch = Branch(chat_model=coder)
421
- response = await branch.chat("Create a Python function to calculate fibonacci numbers")
423
+ branch = Branch(chat_model=model)
424
+ response = await branch.communicate("Explain the architecture of protocols, operations, and branch")
422
425
 
423
426
  # Claude Code maintains session context automatically
424
- response2 = await branch.chat("Now optimize it for performance")
427
+ response2 = await branch.communicate("how do these parts form lionagi system")
425
428
  ```
426
429
 
427
430
  Key features:
@@ -8,7 +8,7 @@
8
8
 
9
9
  # LION - Language InterOperable Network
10
10
 
11
- ## An Intelligence Operating System SDK
11
+ ## An Agentic Intelligence SDK
12
12
 
13
13
  LionAGI is a robust framework for orchestrating multi-step AI operations with
14
14
  precise control. Bring together multiple models, advanced ReAct reasoning, tool
@@ -144,19 +144,21 @@ LionAGI now supports Anthropic's [Claude Code SDK](https://github.com/anthropics
144
144
  from lionagi import iModel, Branch
145
145
 
146
146
  # Create a Claude Code model
147
- coder = iModel(
147
+ model = iModel(
148
148
  provider="claude_code",
149
- endpoint="code",
150
- model="claude-sonnet-4-20250514",
149
+ endpoint="query_cli",
150
+ model="sonnet",
151
151
  allowed_tools=["Write", "Read", "Edit"], # Control which tools Claude can use
152
+ permission_mode = "bypassPermissions", # Bypass tool permission checks (use with caution!),
153
+ verbose_output=True, # Enable detailed output for debugging
152
154
  )
153
155
 
154
156
  # Start a coding session
155
- branch = Branch(chat_model=coder)
156
- response = await branch.chat("Create a Python function to calculate fibonacci numbers")
157
+ branch = Branch(chat_model=model)
158
+ response = await branch.communicate("Explain the architecture of protocols, operations, and branch")
157
159
 
158
160
  # Claude Code maintains session context automatically
159
- response2 = await branch.chat("Now optimize it for performance")
161
+ response2 = await branch.communicate("how do these parts form lionagi system")
160
162
  ```
161
163
 
162
164
  Key features:
@@ -68,7 +68,7 @@ class AppSettings(BaseSettings, frozen=True):
68
68
  LIONAGI_EMBEDDING_MODEL: str = "text-embedding-3-small"
69
69
 
70
70
  LIONAGI_CHAT_PROVIDER: str = "openai"
71
- LIONAGI_CHAT_MODEL: str = "gpt-4o"
71
+ LIONAGI_CHAT_MODEL: str = "gpt-4.1-nano"
72
72
 
73
73
  # default storage
74
74
  LIONAGI_AUTO_STORE_EVENT: bool = False
@@ -2,4 +2,6 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ from .flow import flow
6
+ from .node import BranchOperations, Operation
5
7
  from .types import *
@@ -0,0 +1,436 @@
1
+ # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import asyncio
6
+ from typing import Any
7
+
8
+ from lionagi.operations.node import Operation
9
+ from lionagi.protocols.types import Graph, Node
10
+ from lionagi.session.branch import Branch
11
+ from lionagi.session.session import Session
12
+ from lionagi.utils import to_dict
13
+
14
+
15
+ async def flow(
16
+ branch: Branch,
17
+ graph: Graph,
18
+ *,
19
+ context: dict[str, Any] | None = None,
20
+ parallel: bool = True,
21
+ max_concurrent: int = 5,
22
+ verbose: bool = False,
23
+ session: Session | None = None,
24
+ ) -> dict[str, Any]:
25
+ """
26
+ Execute a graph-based workflow using the branch's operations.
27
+
28
+ For simple graphs, executes directly on the branch.
29
+ For parallel execution, uses session for coordination.
30
+
31
+ Args:
32
+ branch: The branch to execute operations on
33
+ graph: The workflow graph containing Operation nodes
34
+ context: Initial context
35
+ parallel: Whether to execute independent operations in parallel
36
+ max_concurrent: Max concurrent operations
37
+ verbose: Enable verbose logging
38
+ session: Optional session for multi-branch parallel execution
39
+
40
+ Returns:
41
+ Execution results with completed operations and final context
42
+ """
43
+ # Validate graph
44
+ if not graph.is_acyclic():
45
+ raise ValueError("Graph must be acyclic for flow execution")
46
+
47
+ # Simple sequential execution on single branch
48
+ if not parallel or max_concurrent == 1:
49
+ return await _execute_sequential(branch, graph, context, verbose)
50
+
51
+ # Parallel execution using session
52
+ if session is None:
53
+ # Create temporary session for this flow
54
+ from lionagi.session.session import Session
55
+
56
+ session = Session()
57
+ session.branches.include(branch)
58
+ session.default_branch = branch
59
+
60
+ return await _execute_parallel(
61
+ session, graph, context, max_concurrent, verbose
62
+ )
63
+
64
+
65
+ async def _execute_sequential(
66
+ branch: Branch, graph: Graph, context: dict[str, Any] | None, verbose: bool
67
+ ) -> dict[str, Any]:
68
+ """Execute graph sequentially on a single branch."""
69
+ completed = []
70
+ results = {}
71
+ execution_context = context or {}
72
+
73
+ # Get execution order (topological sort)
74
+ execution_order = _topological_sort(graph)
75
+
76
+ for node_id in execution_order:
77
+ node = graph.internal_nodes[node_id]
78
+
79
+ if not isinstance(node, Operation):
80
+ continue
81
+
82
+ # Check dependencies using set for fast lookup
83
+ completed_set = set(completed)
84
+
85
+ # Check if dependencies and conditions are satisfied
86
+ if not await _dependencies_satisfied_async(
87
+ node, graph, completed_set, results, execution_context
88
+ ):
89
+ continue
90
+
91
+ predecessors = graph.get_predecessors(node)
92
+
93
+ # Update operation context with predecessors
94
+ if predecessors:
95
+ pred_context = {}
96
+ for pred in predecessors:
97
+ if pred.id in results:
98
+ result = results[pred.id]
99
+ # Use to_dict for proper serialization of complex types only
100
+ if result is not None and not isinstance(
101
+ result, (str, int, float, bool)
102
+ ):
103
+ result = to_dict(result, recursive=True)
104
+ pred_context[f"{pred.id}_result"] = result
105
+
106
+ if "context" not in node.parameters:
107
+ node.parameters["context"] = pred_context
108
+ else:
109
+ node.parameters["context"].update(pred_context)
110
+
111
+ # Add execution context
112
+ if execution_context:
113
+ if "context" not in node.parameters:
114
+ node.parameters["context"] = execution_context.copy()
115
+ else:
116
+ node.parameters["context"].update(execution_context)
117
+
118
+ # Execute operation
119
+ if verbose:
120
+ print(f"Executing operation: {node.id}")
121
+
122
+ await node.invoke(branch)
123
+
124
+ completed.append(node.id)
125
+ results[node.id] = node.response
126
+
127
+ # Update execution context
128
+ if isinstance(node.response, dict) and "context" in node.response:
129
+ execution_context.update(node.response["context"])
130
+
131
+ return {
132
+ "completed_operations": completed,
133
+ "operation_results": results,
134
+ "final_context": execution_context,
135
+ }
136
+
137
+
138
+ async def _execute_parallel(
139
+ session: Session,
140
+ graph: Graph,
141
+ context: dict[str, Any] | None,
142
+ max_concurrent: int,
143
+ verbose: bool,
144
+ ) -> dict[str, Any]:
145
+ """Execute graph in parallel using multiple branches."""
146
+ results = {}
147
+ execution_context = context or {}
148
+ completed = [] # Track completed operations
149
+
150
+ # Get operation nodes in topological order
151
+ operation_nodes = []
152
+ execution_order = _topological_sort(graph)
153
+ for node_id in execution_order:
154
+ node = graph.internal_nodes.get(node_id)
155
+ if isinstance(node, Operation):
156
+ operation_nodes.append(node)
157
+
158
+ # Use session branches context manager for safe parallel execution
159
+ async with session.branches:
160
+ # Create a pool of worker branches
161
+ worker_branches = []
162
+ for i in range(min(max_concurrent, len(operation_nodes))):
163
+ if i == 0:
164
+ worker_branches.append(session.default_branch)
165
+ else:
166
+ worker_branches.append(session.split(session.default_branch))
167
+
168
+ # Process nodes in dependency order
169
+ remaining_nodes = {node.id for node in operation_nodes}
170
+ executing_tasks = {} # node_id -> asyncio.Task
171
+ blocked_nodes = set() # Nodes that have been checked and found blocked
172
+
173
+ max_iterations = 1000 # Prevent infinite loops
174
+ iteration = 0
175
+
176
+ while (
177
+ remaining_nodes or executing_tasks
178
+ ) and iteration < max_iterations:
179
+ iteration += 1
180
+
181
+ # Check for completed tasks
182
+ completed_in_round = []
183
+ for node_id, task in list(executing_tasks.items()):
184
+ if task.done():
185
+ try:
186
+ result = await task
187
+ results[node_id] = result
188
+ completed.append(node_id)
189
+ completed_in_round.append(node_id)
190
+ if verbose:
191
+ print(f"Completed operation: {node_id}")
192
+ except Exception as e:
193
+ if verbose:
194
+ print(f"Operation {node_id} failed: {e}")
195
+ results[node_id] = {"error": str(e)}
196
+ completed.append(node_id)
197
+ completed_in_round.append(node_id)
198
+ finally:
199
+ del executing_tasks[node_id]
200
+
201
+ # Remove completed from remaining
202
+ remaining_nodes -= set(completed_in_round)
203
+
204
+ # If new completions, clear blocked nodes to re-check
205
+ if completed_in_round:
206
+ blocked_nodes.clear()
207
+
208
+ # Find nodes ready to execute (skip already blocked nodes)
209
+ ready_nodes = []
210
+ completed_set = set(completed)
211
+ newly_blocked = []
212
+
213
+ for node in operation_nodes:
214
+ if (
215
+ node.id in remaining_nodes
216
+ and node.id not in executing_tasks
217
+ and node.id not in blocked_nodes
218
+ and len(executing_tasks) < max_concurrent
219
+ ):
220
+ if await _dependencies_satisfied_async(
221
+ node, graph, completed_set, results, execution_context
222
+ ):
223
+ ready_nodes.append(node)
224
+ else:
225
+ newly_blocked.append(node.id)
226
+
227
+ # Update blocked nodes
228
+ blocked_nodes.update(newly_blocked)
229
+
230
+ # If no ready nodes but we have remaining and no executing tasks, we're stuck
231
+ if not ready_nodes and remaining_nodes and not executing_tasks:
232
+ if verbose:
233
+ print(
234
+ f"Deadlock detected: {len(remaining_nodes)} nodes cannot execute"
235
+ )
236
+ remaining_node_names = [
237
+ n.operation
238
+ for n in operation_nodes
239
+ if n.id in remaining_nodes
240
+ ]
241
+ print(f"Remaining operations: {remaining_node_names}")
242
+ # Mark remaining nodes as failed
243
+ for node in operation_nodes:
244
+ if node.id in remaining_nodes:
245
+ results[node.id] = {
246
+ "error": "Blocked by unsatisfied conditions"
247
+ }
248
+ completed.append(node.id)
249
+ break
250
+
251
+ # Start execution for ready nodes
252
+ started_count = 0
253
+ for node in ready_nodes:
254
+ if len(executing_tasks) >= max_concurrent:
255
+ break
256
+
257
+ # Get an available branch (round-robin)
258
+ branch_idx = len(executing_tasks) % len(worker_branches)
259
+ node_branch = worker_branches[branch_idx]
260
+
261
+ # Check if node specifies a branch
262
+ branch_id = node.parameters.get("branch_id")
263
+ if branch_id:
264
+ try:
265
+ node_branch = session.branches[branch_id]
266
+ except:
267
+ pass # Use the selected worker branch
268
+
269
+ # Create task for this node
270
+ task = asyncio.create_task(
271
+ _execute_node_async(
272
+ node,
273
+ node_branch,
274
+ graph,
275
+ results,
276
+ execution_context,
277
+ verbose,
278
+ )
279
+ )
280
+ executing_tasks[node.id] = task
281
+ started_count += 1
282
+
283
+ if verbose:
284
+ branch_name = (
285
+ getattr(node_branch, "name", None) or node_branch.id
286
+ )
287
+ print(
288
+ f"Started operation {node.id} on branch: {branch_name}"
289
+ )
290
+
291
+ # If we started new tasks or have executing tasks, wait for some to complete
292
+ if started_count > 0 or executing_tasks:
293
+ # Wait for at least one task to complete before next iteration
294
+ if executing_tasks:
295
+ done, pending = await asyncio.wait(
296
+ executing_tasks.values(),
297
+ return_when=asyncio.FIRST_COMPLETED,
298
+ )
299
+ else:
300
+ await asyncio.sleep(0.01)
301
+ elif not remaining_nodes:
302
+ # All done
303
+ break
304
+
305
+ if iteration >= max_iterations:
306
+ raise RuntimeError(
307
+ f"Flow execution exceeded maximum iterations ({max_iterations})"
308
+ )
309
+
310
+ return {
311
+ "completed_operations": completed,
312
+ "operation_results": results,
313
+ "final_context": execution_context,
314
+ }
315
+
316
+
317
+ async def _execute_node_async(
318
+ node: Operation,
319
+ branch: Branch,
320
+ graph: Graph,
321
+ results: dict[str, Any],
322
+ execution_context: dict[str, Any],
323
+ verbose: bool,
324
+ ) -> Any:
325
+ """Execute a single node asynchronously."""
326
+ # Update operation context with predecessors
327
+ predecessors = graph.get_predecessors(node)
328
+ if predecessors:
329
+ pred_context = {}
330
+ for pred in predecessors:
331
+ if pred.id in results:
332
+ result = results[pred.id]
333
+ # Use to_dict for proper serialization of complex types only
334
+ if result is not None and not isinstance(
335
+ result, (str, int, float, bool)
336
+ ):
337
+ result = to_dict(result, recursive=True)
338
+ pred_context[f"{pred.id}_result"] = result
339
+
340
+ if "context" not in node.parameters:
341
+ node.parameters["context"] = pred_context
342
+ else:
343
+ node.parameters["context"].update(pred_context)
344
+
345
+ # Add execution context
346
+ if execution_context:
347
+ if "context" not in node.parameters:
348
+ node.parameters["context"] = execution_context.copy()
349
+ else:
350
+ node.parameters["context"].update(execution_context)
351
+
352
+ # Execute the operation
353
+ await node.invoke(branch)
354
+ result = node.response
355
+
356
+ # Update execution context if needed
357
+ if isinstance(result, dict) and "context" in result:
358
+ execution_context.update(result["context"])
359
+
360
+ return result
361
+
362
+
363
+ def _topological_sort(graph: Graph) -> list[str]:
364
+ """Get topological ordering of graph nodes."""
365
+ visited = set()
366
+ stack = []
367
+
368
+ def visit(node_id: str):
369
+ if node_id in visited:
370
+ return
371
+ visited.add(node_id)
372
+
373
+ successors = graph.get_successors(graph.internal_nodes[node_id])
374
+ for successor in successors:
375
+ visit(successor.id)
376
+
377
+ stack.append(node_id)
378
+
379
+ for node in graph.internal_nodes:
380
+ if node.id not in visited:
381
+ visit(node.id)
382
+
383
+ return stack[::-1]
384
+
385
+
386
+ async def _dependencies_satisfied_async(
387
+ node: Node,
388
+ graph: Graph,
389
+ completed: set[str],
390
+ results: dict[str, Any],
391
+ execution_context: dict[str, Any] | None = None,
392
+ ) -> bool:
393
+ """Check if node dependencies are satisfied and edge conditions pass."""
394
+ # Get all incoming edges to this node
395
+ incoming_edges = []
396
+ for edge in graph.internal_edges:
397
+ if edge.tail == node.id:
398
+ incoming_edges.append(edge)
399
+
400
+ # If no incoming edges, node can execute
401
+ if not incoming_edges:
402
+ return True
403
+
404
+ # Check each incoming edge
405
+ at_least_one_satisfied = False
406
+ for edge in incoming_edges:
407
+ # Check if predecessor is completed
408
+ if edge.head not in completed:
409
+ # If edge has no condition, we need to wait for predecessor
410
+ if not edge.condition:
411
+ continue
412
+ # If edge has condition but predecessor not complete, skip
413
+ continue
414
+
415
+ # Predecessor is completed
416
+ if edge.condition:
417
+ # Evaluate condition
418
+ # Get the result - don't use to_dict if it's already a simple type
419
+ result_value = results.get(edge.head)
420
+ if result_value is not None and not isinstance(
421
+ result_value, (str, int, float, bool)
422
+ ):
423
+ result_value = to_dict(result_value, recursive=True)
424
+
425
+ ctx = {"result": result_value, "context": execution_context or {}}
426
+ try:
427
+ if await edge.condition.apply(ctx):
428
+ at_least_one_satisfied = True
429
+ except Exception as e:
430
+ # Condition evaluation failed
431
+ continue
432
+ else:
433
+ # No condition, edge is satisfied
434
+ at_least_one_satisfied = True
435
+
436
+ return at_least_one_satisfied
@@ -0,0 +1,107 @@
1
+ import asyncio
2
+ import logging
3
+ from typing import Any, Literal
4
+ from uuid import UUID
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+ from lionagi.protocols.types import ID, Event, EventStatus, IDType, Node
9
+ from lionagi.session.branch import Branch
10
+
11
+ BranchOperations = Literal[
12
+ "chat",
13
+ "operate",
14
+ "communicate",
15
+ "parse",
16
+ "ReAct",
17
+ "select",
18
+ "translate",
19
+ "interpret",
20
+ "act",
21
+ "ReActStream",
22
+ "instruct",
23
+ ]
24
+
25
+ logger = logging.getLogger("operation")
26
+
27
+
28
+ class Operation(Node, Event):
29
+ operation: BranchOperations
30
+ parameters: dict[str, Any] | BaseModel = Field(
31
+ default_factory=dict, description="Parameters for the operation"
32
+ )
33
+
34
+ @property
35
+ def branch_id(self) -> IDType | None:
36
+ if a := self.metadata.get("branch_id"):
37
+ return ID.get_id(a)
38
+
39
+ @branch_id.setter
40
+ def branch_id(self, value: str | UUID | IDType | None):
41
+ if value is None:
42
+ self.metadata.pop("branch_id", None)
43
+ else:
44
+ self.metadata["branch_id"] = str(value)
45
+
46
+ @property
47
+ def graph_id(self) -> str | None:
48
+ return self.metadata.get("graph_id")
49
+
50
+ @graph_id.setter
51
+ def graph_id(self, value: str | UUID | IDType | None):
52
+ if value is None:
53
+ self.metadata.pop("graph_id", None)
54
+ else:
55
+ self.metadata["graph_id"] = str(value)
56
+
57
+ @property
58
+ def request(self) -> dict:
59
+ # Convert parameters to dict if it's a BaseModel
60
+ params = self.parameters
61
+ if hasattr(params, "model_dump"):
62
+ params = params.model_dump()
63
+ elif hasattr(params, "dict"):
64
+ params = params.dict()
65
+
66
+ return params if isinstance(params, dict) else {}
67
+
68
+ @property
69
+ def response(self):
70
+ """Get the response from the execution."""
71
+ return self.execution.response if self.execution else None
72
+
73
+ async def invoke(self, branch: Branch):
74
+ meth = getattr(branch, self.operation, None)
75
+ if meth is None:
76
+ raise ValueError(f"Unsupported operation type: {self.operation}")
77
+
78
+ start = asyncio.get_event_loop().time()
79
+
80
+ try:
81
+ self.execution.status = EventStatus.PROCESSING
82
+ self.branch_id = branch.id
83
+ response = await self._invoke(meth)
84
+
85
+ self.execution.response = response
86
+ self.execution.status = EventStatus.COMPLETED
87
+
88
+ except asyncio.CancelledError:
89
+ self.execution.error = "Operation cancelled"
90
+ self.execution.status = EventStatus.FAILED
91
+ raise
92
+
93
+ except Exception as e:
94
+ self.execution.error = str(e)
95
+ self.execution.status = EventStatus.FAILED
96
+ logger.error(f"Operation failed: {e}")
97
+
98
+ finally:
99
+ self.execution.duration = asyncio.get_event_loop().time() - start
100
+
101
+ async def _invoke(self, meth):
102
+ if self.operation == "ReActStream":
103
+ res = []
104
+ async for i in meth(**self.request):
105
+ res.append(i)
106
+ return res
107
+ return await meth(**self.request)
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from .edge import Edge, EdgeCondition
6
+ from .graph import Graph
7
+ from .node import Node
8
+
9
+ __all__ = ["Edge", "EdgeCondition", "Graph", "Node"]
@@ -196,10 +196,7 @@ def _pp_tool_use(tu: dict[str, Any], theme) -> None:
196
196
  def _pp_tool_result(tr: dict[str, Any], theme) -> None:
197
197
  body_preview = shorten(str(tr["content"]).replace("\n", " "), 130)
198
198
  status = "ERR" if tr.get("is_error") else "OK"
199
- body = (
200
- f"- 📄 Tool Result({tr['tool_use_id']}) - {status}\n\n"
201
- f"\tcontent: {body_preview}"
202
- )
199
+ body = f"- 📄 Tool Result({tr['tool_use_id']}) - {status}\n\n\tcontent: {body_preview}"
203
200
  print_readable(body, border=False, panel=False, theme=theme)
204
201
 
205
202