lionagi 0.14.3__tar.gz → 0.14.5__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 (286) hide show
  1. {lionagi-0.14.3 → lionagi-0.14.5}/PKG-INFO +7 -3
  2. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/branch_operations.rst +1 -3
  3. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/adapters/async_postgres_adapter.py +172 -0
  4. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/concurrency/__init__.py +25 -1
  5. lionagi-0.14.5/lionagi/libs/concurrency/patterns.py +259 -0
  6. lionagi-0.14.5/lionagi/libs/concurrency/primitives.py +290 -0
  7. lionagi-0.14.5/lionagi/libs/concurrency/resource_tracker.py +182 -0
  8. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/concurrency/task.py +4 -2
  9. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/generic/pile.py +16 -38
  10. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/generic/processor.py +53 -26
  11. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/rate_limited_processor.py +53 -35
  12. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/session/branch.py +0 -5
  13. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/utils.py +56 -174
  14. lionagi-0.14.5/lionagi/version.py +1 -0
  15. {lionagi-0.14.3 → lionagi-0.14.5}/pyproject.toml +8 -3
  16. {lionagi-0.14.3 → lionagi-0.14.5}/uv.lock +12 -5
  17. lionagi-0.14.3/lionagi/libs/concurrency/patterns.py +0 -252
  18. lionagi-0.14.3/lionagi/libs/concurrency/primitives.py +0 -242
  19. lionagi-0.14.3/lionagi/version.py +0 -1
  20. {lionagi-0.14.3 → lionagi-0.14.5}/.coveragerc +0 -0
  21. {lionagi-0.14.3 → lionagi-0.14.5}/.env.example +0 -0
  22. {lionagi-0.14.3 → lionagi-0.14.5}/.github/FUNDING.yml +0 -0
  23. {lionagi-0.14.3 → lionagi-0.14.5}/.github/dependabot.yml +0 -0
  24. {lionagi-0.14.3 → lionagi-0.14.5}/.github/workflows/ci.yml +0 -0
  25. {lionagi-0.14.3 → lionagi-0.14.5}/.github/workflows/codeql.yml +0 -0
  26. {lionagi-0.14.3 → lionagi-0.14.5}/.github/workflows/docs.yml +0 -0
  27. {lionagi-0.14.3 → lionagi-0.14.5}/.github/workflows/release.yml +0 -0
  28. {lionagi-0.14.3 → lionagi-0.14.5}/.gitignore +0 -0
  29. {lionagi-0.14.3 → lionagi-0.14.5}/.pre-commit-config.yaml +0 -0
  30. {lionagi-0.14.3 → lionagi-0.14.5}/.python-version +0 -0
  31. {lionagi-0.14.3 → lionagi-0.14.5}/CODE_OF_CONDUCT.md +0 -0
  32. {lionagi-0.14.3 → lionagi-0.14.5}/CONTRIBUTING.md +0 -0
  33. {lionagi-0.14.3 → lionagi-0.14.5}/LICENSE +0 -0
  34. {lionagi-0.14.3 → lionagi-0.14.5}/README.md +0 -0
  35. {lionagi-0.14.3 → lionagi-0.14.5}/assets/operation_builder.gif +0 -0
  36. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/001_branch_converse.ipynb +0 -0
  37. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/002_branch_interact.ipynb +0 -0
  38. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/003_branch_info.ipynb +0 -0
  39. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/004_conversation_patterns.ipynb +0 -0
  40. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/005_react_basics.ipynb +0 -0
  41. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/006_operation_graphs_claim_extraction.ipynb +0 -0
  42. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/data/002_comedian.json +0 -0
  43. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/data/002_critic.json +0 -0
  44. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/data/006_lion_proof_ch2.md +0 -0
  45. {lionagi-0.14.3 → lionagi-0.14.5}/cookbooks/using_claude_code.py +0 -0
  46. {lionagi-0.14.3 → lionagi-0.14.5}/docs/Makefile +0 -0
  47. {lionagi-0.14.3 → lionagi-0.14.5}/docs/_static/custom.css +0 -0
  48. {lionagi-0.14.3 → lionagi-0.14.5}/docs/_templates/layout.html +0 -0
  49. {lionagi-0.14.3 → lionagi-0.14.5}/docs/conf.py +0 -0
  50. {lionagi-0.14.3 → lionagi-0.14.5}/docs/index.rst +0 -0
  51. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/action.rst +0 -0
  52. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/adapter.rst +0 -0
  53. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/branch.rst +0 -0
  54. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/concepts.rst +0 -0
  55. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/element_id.rst +0 -0
  56. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/event.rst +0 -0
  57. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/form.rst +0 -0
  58. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/graph.rst +0 -0
  59. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/index.rst +0 -0
  60. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/instruct.rst +0 -0
  61. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/lib_file.rst +0 -0
  62. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/lib_nested.rst +0 -0
  63. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/lib_package.rst +0 -0
  64. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/lib_schema.rst +0 -0
  65. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/lib_validate.rst +0 -0
  66. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/log.rst +0 -0
  67. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/mail.rst +0 -0
  68. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/message.rst +0 -0
  69. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/models.rst +0 -0
  70. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/operative_step.rst +0 -0
  71. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/pile.rst +0 -0
  72. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/processor.rst +0 -0
  73. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/progression.rst +0 -0
  74. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/service.rst +0 -0
  75. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/session.rst +0 -0
  76. {lionagi-0.14.3 → lionagi-0.14.5}/docs/modules/utils.rst +0 -0
  77. {lionagi-0.14.3 → lionagi-0.14.5}/docs/tutorials/get_started.rst +0 -0
  78. {lionagi-0.14.3 → lionagi-0.14.5}/docs/tutorials/get_started_pt2.rst +0 -0
  79. {lionagi-0.14.3 → lionagi-0.14.5}/docs/tutorials/get_started_pt3.rst +0 -0
  80. {lionagi-0.14.3 → lionagi-0.14.5}/docs/tutorials/index.rst +0 -0
  81. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/__init__.py +0 -0
  82. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/_class_registry.py +0 -0
  83. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/_errors.py +0 -0
  84. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/_types.py +0 -0
  85. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/adapters/__init__.py +0 -0
  86. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/adapters/postgres_model_adapter.py +0 -0
  87. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/config.py +0 -0
  88. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/__init__.py +0 -0
  89. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/action.py +0 -0
  90. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/base.py +0 -0
  91. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/code.py +0 -0
  92. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/file.py +0 -0
  93. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/instruct.py +0 -0
  94. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/reason.py +0 -0
  95. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/fields/research.py +0 -0
  96. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/__init__.py +0 -0
  97. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/concurrency/cancel.py +0 -0
  98. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/concurrency/errors.py +0 -0
  99. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/__init__.py +0 -0
  100. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/chunk.py +0 -0
  101. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/concat.py +0 -0
  102. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/concat_files.py +0 -0
  103. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/file_ops.py +0 -0
  104. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/params.py +0 -0
  105. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/process.py +0 -0
  106. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/file/save.py +0 -0
  107. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/__init__.py +0 -0
  108. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/flatten.py +0 -0
  109. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/nfilter.py +0 -0
  110. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/nget.py +0 -0
  111. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/ninsert.py +0 -0
  112. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/nmerge.py +0 -0
  113. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/npop.py +0 -0
  114. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/nset.py +0 -0
  115. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/unflatten.py +0 -0
  116. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/nested/utils.py +0 -0
  117. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/package/__init__.py +0 -0
  118. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/package/imports.py +0 -0
  119. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/package/management.py +0 -0
  120. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/package/params.py +0 -0
  121. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/package/system.py +0 -0
  122. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/parse.py +0 -0
  123. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/schema/__init__.py +0 -0
  124. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/schema/as_readable.py +0 -0
  125. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/schema/extract_code_block.py +0 -0
  126. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/schema/extract_docstring.py +0 -0
  127. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/schema/function_to_schema.py +0 -0
  128. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/schema/json_schema.py +0 -0
  129. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/schema/load_pydantic_model_from_schema.py +0 -0
  130. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/__init__.py +0 -0
  131. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/base.py +0 -0
  132. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/llmlingua.py +0 -0
  133. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/perplexity.py +0 -0
  134. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/symbolic_compress_context.py +0 -0
  135. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang.py +0 -0
  136. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/base.py +0 -0
  137. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/abstract_algebra.toml +0 -0
  138. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/category_theory.toml +0 -0
  139. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/complex_analysis.toml +0 -0
  140. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/framework_options.json +0 -0
  141. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/group_theory.toml +0 -0
  142. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/math_logic.toml +0 -0
  143. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/reflective_patterns.toml +0 -0
  144. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/set_theory.toml +0 -0
  145. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/frameworks/topology_fundamentals.toml +0 -0
  146. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/mapping/lion_emoji_mapping.toml +0 -0
  147. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/mapping/python_math_mapping.toml +0 -0
  148. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/mapping/rust_chinese_mapping.toml +0 -0
  149. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/resources/utility/base_synthlang_system_prompt.toml +0 -0
  150. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/synthlang_/translate_to_synthlang.py +0 -0
  151. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/token_transform/types.py +0 -0
  152. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/validate/__init__.py +0 -0
  153. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/validate/common_field_validators.py +0 -0
  154. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/validate/fuzzy_match_keys.py +0 -0
  155. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/validate/fuzzy_validate_mapping.py +0 -0
  156. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/validate/string_similarity.py +0 -0
  157. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/libs/validate/validate_boolean.py +0 -0
  158. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/models/__init__.py +0 -0
  159. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/models/field_model.py +0 -0
  160. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/models/hashable_model.py +0 -0
  161. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/models/model_params.py +0 -0
  162. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/models/note.py +0 -0
  163. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/models/operable_model.py +0 -0
  164. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/models/schema_model.py +0 -0
  165. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/ReAct/ReAct.py +0 -0
  166. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/ReAct/__init__.py +0 -0
  167. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/ReAct/utils.py +0 -0
  168. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/__init__.py +0 -0
  169. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/_act/__init__.py +0 -0
  170. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/_act/act.py +0 -0
  171. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/brainstorm/__init__.py +0 -0
  172. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/brainstorm/brainstorm.py +0 -0
  173. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/brainstorm/prompt.py +0 -0
  174. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/builder.py +0 -0
  175. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/chat/__init__.py +0 -0
  176. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/chat/chat.py +0 -0
  177. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/communicate/__init__.py +0 -0
  178. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/communicate/communicate.py +0 -0
  179. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/flow.py +0 -0
  180. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/instruct/__init__.py +0 -0
  181. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/instruct/instruct.py +0 -0
  182. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/interpret/__init__.py +0 -0
  183. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/interpret/interpret.py +0 -0
  184. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/manager.py +0 -0
  185. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/node.py +0 -0
  186. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/operate/__init__.py +0 -0
  187. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/operate/operate.py +0 -0
  188. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/parse/__init__.py +0 -0
  189. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/parse/parse.py +0 -0
  190. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/plan/__init__.py +0 -0
  191. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/plan/plan.py +0 -0
  192. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/plan/prompt.py +0 -0
  193. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/select/__init__.py +0 -0
  194. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/select/select.py +0 -0
  195. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/select/utils.py +0 -0
  196. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/translate/__init__.py +0 -0
  197. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/translate/translate.py +0 -0
  198. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/types.py +0 -0
  199. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/operations/utils.py +0 -0
  200. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/__init__.py +0 -0
  201. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/_concepts.py +0 -0
  202. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/action/__init__.py +0 -0
  203. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/action/function_calling.py +0 -0
  204. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/action/manager.py +0 -0
  205. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/action/tool.py +0 -0
  206. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/forms/__init__.py +0 -0
  207. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/forms/base.py +0 -0
  208. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/forms/flow.py +0 -0
  209. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/forms/form.py +0 -0
  210. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/forms/report.py +0 -0
  211. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/generic/__init__.py +0 -0
  212. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/generic/element.py +0 -0
  213. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/generic/event.py +0 -0
  214. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/generic/log.py +0 -0
  215. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/generic/progression.py +0 -0
  216. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/graph/__init__.py +0 -0
  217. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/graph/edge.py +0 -0
  218. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/graph/graph.py +0 -0
  219. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/graph/node.py +0 -0
  220. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/mail/__init__.py +0 -0
  221. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/mail/exchange.py +0 -0
  222. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/mail/mail.py +0 -0
  223. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/mail/mailbox.py +0 -0
  224. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/mail/manager.py +0 -0
  225. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/mail/package.py +0 -0
  226. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/__init__.py +0 -0
  227. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/action_request.py +0 -0
  228. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/action_response.py +0 -0
  229. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/assistant_response.py +0 -0
  230. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/base.py +0 -0
  231. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/instruction.py +0 -0
  232. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/manager.py +0 -0
  233. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/message.py +0 -0
  234. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/system.py +0 -0
  235. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/templates/README.md +0 -0
  236. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/templates/action_request.jinja2 +0 -0
  237. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/templates/action_response.jinja2 +0 -0
  238. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -0
  239. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -0
  240. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/templates/system_message.jinja2 +0 -0
  241. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -0
  242. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/operatives/__init__.py +0 -0
  243. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/operatives/operative.py +0 -0
  244. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/operatives/step.py +0 -0
  245. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/protocols/types.py +0 -0
  246. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/py.typed +0 -0
  247. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/__init__.py +0 -0
  248. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/__init__.py +0 -0
  249. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/api_calling.py +0 -0
  250. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/endpoint.py +0 -0
  251. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/endpoint_config.py +0 -0
  252. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/header_factory.py +0 -0
  253. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/match_endpoint.py +0 -0
  254. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/__init__.py +0 -0
  255. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/anthropic_.py +0 -0
  256. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/claude_code_.py +0 -0
  257. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/claude_code_cli.py +0 -0
  258. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/exa_.py +0 -0
  259. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/oai_.py +0 -0
  260. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/ollama_.py +0 -0
  261. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/connections/providers/perplexity_.py +0 -0
  262. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/imodel.py +0 -0
  263. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/manager.py +0 -0
  264. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/resilience.py +0 -0
  265. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/third_party/README.md +0 -0
  266. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/third_party/__init__.py +0 -0
  267. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/third_party/anthropic_models.py +0 -0
  268. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/third_party/exa_models.py +0 -0
  269. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/third_party/openai_models.py +0 -0
  270. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/third_party/pplx_models.py +0 -0
  271. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/token_calculator.py +0 -0
  272. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/service/types.py +0 -0
  273. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/session/__init__.py +0 -0
  274. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/session/prompts.py +0 -0
  275. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/session/session.py +0 -0
  276. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/settings.py +0 -0
  277. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/tools/__init__.py +0 -0
  278. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/tools/base.py +0 -0
  279. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/tools/file/__init__.py +0 -0
  280. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/tools/file/reader.py +0 -0
  281. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/tools/memory/tools.py +0 -0
  282. {lionagi-0.14.3 → lionagi-0.14.5}/lionagi/tools/types.py +0 -0
  283. {lionagi-0.14.3 → lionagi-0.14.5}/main.py +0 -0
  284. {lionagi-0.14.3 → lionagi-0.14.5}/scripts/README.md +0 -0
  285. {lionagi-0.14.3 → lionagi-0.14.5}/scripts/concat.py +0 -0
  286. {lionagi-0.14.3 → lionagi-0.14.5}/scripts/config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lionagi
3
- Version: 0.14.3
3
+ Version: 0.14.5
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
@@ -224,8 +224,7 @@ Requires-Dist: aiohttp>=3.12.0
224
224
  Requires-Dist: anyio>=4.8.0
225
225
  Requires-Dist: backoff>=2.2.1
226
226
  Requires-Dist: jinja2>=3.1.0
227
- Requires-Dist: json-repair>=0.47.8
228
- Requires-Dist: matplotlib>=3.9.0
227
+ Requires-Dist: json-repair>=0.40.0
229
228
  Requires-Dist: pillow>=11.0.0
230
229
  Requires-Dist: psutil>=7.0.0
231
230
  Requires-Dist: pydantic-settings>=2.8.0
@@ -239,6 +238,8 @@ Requires-Dist: claude-code-sdk>=0.0.14; extra == 'all'
239
238
  Requires-Dist: datamodel-code-generator>=0.31.2; extra == 'all'
240
239
  Requires-Dist: docling>=2.15.1; extra == 'all'
241
240
  Requires-Dist: fastmcp>=2.10.5; extra == 'all'
241
+ Requires-Dist: matplotlib>=3.7.0; extra == 'all'
242
+ Requires-Dist: networkx>=3.0.0; extra == 'all'
242
243
  Requires-Dist: ollama>=0.4.0; extra == 'all'
243
244
  Requires-Dist: pydapter[postgres]; extra == 'all'
244
245
  Requires-Dist: rich>=13.0.0; extra == 'all'
@@ -248,6 +249,9 @@ Provides-Extra: docs
248
249
  Requires-Dist: furo>=2024.8.6; extra == 'docs'
249
250
  Requires-Dist: sphinx-autobuild>=2024.10.3; extra == 'docs'
250
251
  Requires-Dist: sphinx>=8.1.3; extra == 'docs'
252
+ Provides-Extra: graph
253
+ Requires-Dist: matplotlib>=3.7.0; extra == 'graph'
254
+ Requires-Dist: networkx>=3.0.0; extra == 'graph'
251
255
  Provides-Extra: lint
252
256
  Requires-Dist: black[jupyter]>=24.10.0; extra == 'lint'
253
257
  Requires-Dist: isort>=5.13.2; extra == 'lint'
@@ -64,7 +64,7 @@ of action requests. Optionally retries, logs errors, or merges results.
64
64
  This is best used for **explicit function calls** triggered from user
65
65
  instructions or system logic.
66
66
 
67
- .. method:: Branch.act(action_request, *, suppress_errors=True, sanitize_input=False, unique_input=False, num_retries=0, initial_delay=0, retry_delay=0, backoff_factor=1, retry_default=UNDEFINED, retry_timeout=None, retry_timing=False, max_concurrent=None, throttle_period=None, flatten=True, dropna=True, unique_output=False, flatten_tuple_set=False)
67
+ .. method:: Branch.act(action_request, *, suppress_errors=True, sanitize_input=False, unique_input=False, num_retries=0, initial_delay=0, retry_delay=0, backoff_factor=1, retry_default=UNDEFINED, retry_timeout=None, max_concurrent=None, throttle_period=None, flatten=True, dropna=True, unique_output=False, flatten_tuple_set=False)
68
68
 
69
69
  Public, potentially batched, asynchronous interface to run one or multiple action requests.
70
70
 
@@ -90,8 +90,6 @@ instructions or system logic.
90
90
  Fallback value if all retries fail (if suppressing errors)
91
91
  retry_timeout : float | None, default=None
92
92
  Overall timeout for all attempts (None = no limit)
93
- retry_timing : bool, default=False
94
- If True, track time used for retries
95
93
  max_concurrent : int | None, default=None
96
94
  Maximum concurrent tasks (if batching)
97
95
  throttle_period : float | None, default=None
@@ -188,3 +188,175 @@ class LionAGIAsyncPostgresAdapter(AsyncPostgresAdapter[T]):
188
188
  return [cls._serialize_datetime_recursive(item) for item in obj]
189
189
  else:
190
190
  return obj
191
+
192
+ @classmethod
193
+ async def from_obj(
194
+ cls,
195
+ node_cls: type[T],
196
+ obj: Any,
197
+ /,
198
+ *,
199
+ adapt_meth: str = "from_dict",
200
+ many: bool = True,
201
+ **kw,
202
+ ) -> T | list[T] | None:
203
+ """
204
+ Read lionagi Node(s) from database with automatic data reconstruction.
205
+
206
+ Handles:
207
+ 1. Database querying with filters
208
+ 2. Reverse metadata field mapping (node_metadata → metadata)
209
+ 3. Reverse data serialization (ISO strings → datetime objects)
210
+ 4. Node object reconstruction
211
+
212
+ Args:
213
+ node_cls: The Node class to instantiate
214
+ obj: Database connection parameters (dict with dsn, table, etc.)
215
+ adapt_meth: Adaptation method (unused but required by pydapter)
216
+ many: Whether to return list or single object
217
+ **kw: Additional query parameters (where, limit, order_by)
218
+
219
+ Returns:
220
+ Single Node, list of Nodes, or None if no results found
221
+ """
222
+ try:
223
+ # Merge obj parameters with kw parameters
224
+ if isinstance(obj, dict):
225
+ params = {**obj, **kw}
226
+ else:
227
+ params = kw
228
+
229
+ # Validate required parameters
230
+ engine_url = params.get("dsn") or params.get("engine_url")
231
+ table = params.get("table")
232
+
233
+ if not engine_url or not table:
234
+ raise ValueError(
235
+ "Missing required 'dsn' and 'table' parameters"
236
+ )
237
+
238
+ # Build query
239
+ engine = create_async_engine(engine_url, future=True)
240
+ async with engine.begin() as conn:
241
+ meta = sa.MetaData()
242
+ meta.bind = conn
243
+ table_obj = cls._table(meta, table)
244
+
245
+ # Build SELECT query
246
+ query = sa.select(table_obj)
247
+
248
+ # Add WHERE conditions if provided
249
+ where_conditions = params.get("where")
250
+ if where_conditions:
251
+ if isinstance(where_conditions, dict):
252
+ # Convert dict to column conditions
253
+ for col_name, value in where_conditions.items():
254
+ if hasattr(table_obj.c, col_name):
255
+ query = query.where(
256
+ getattr(table_obj.c, col_name) == value
257
+ )
258
+ else:
259
+ # Assume it's already a SQLAlchemy condition
260
+ query = query.where(where_conditions)
261
+
262
+ # Add ordering if provided
263
+ order_by = params.get("order_by")
264
+ if order_by:
265
+ if isinstance(order_by, str):
266
+ if hasattr(table_obj.c, order_by):
267
+ query = query.order_by(
268
+ getattr(table_obj.c, order_by)
269
+ )
270
+ else:
271
+ query = query.order_by(order_by)
272
+
273
+ # Add limit if provided
274
+ limit = params.get("limit")
275
+ if limit:
276
+ query = query.limit(limit)
277
+
278
+ # Execute query
279
+ result = await conn.execute(query)
280
+ rows = result.fetchall()
281
+
282
+ # Use many parameter from params if provided, otherwise use method parameter
283
+ return_many = params.get("many", many)
284
+
285
+ if not rows:
286
+ return [] if return_many else None
287
+
288
+ # Convert database rows back to Node objects
289
+ nodes = []
290
+ for row in rows:
291
+ # Convert row to dict
292
+ row_dict = dict(row._mapping)
293
+
294
+ # Apply reverse lionagi data transformations
295
+ node_data = cls._reverse_lionagi_data(row_dict)
296
+
297
+ # Create Node instance
298
+ node = node_cls(**node_data)
299
+ nodes.append(node)
300
+
301
+ if return_many:
302
+ return nodes
303
+ else:
304
+ return nodes[-1] if nodes else None
305
+
306
+ except Exception as e:
307
+ raise QueryError(
308
+ f"Error reading from lionagi async adapter: {e}",
309
+ adapter="lionagi_async_pg",
310
+ ) from e
311
+
312
+ @classmethod
313
+ def _reverse_lionagi_data(cls, row_data: dict) -> dict:
314
+ """
315
+ Reverse lionagi data transformations from database storage.
316
+
317
+ Handles:
318
+ 1. Database field mapping (node_metadata → metadata)
319
+ 2. ISO string → datetime objects in content
320
+ 3. Proper lionagi Node field structure
321
+ """
322
+ # Create a copy to avoid modifying original
323
+ data = row_data.copy()
324
+
325
+ # Reverse field mapping: node_metadata → metadata
326
+ if "node_metadata" in data:
327
+ data["metadata"] = data.pop("node_metadata")
328
+
329
+ # Reverse datetime serialization in content
330
+ if "content" in data and isinstance(data["content"], dict):
331
+ data["content"] = cls._deserialize_datetime_recursive(
332
+ data["content"]
333
+ )
334
+
335
+ return data
336
+
337
+ @classmethod
338
+ def _deserialize_datetime_recursive(cls, obj: Any) -> Any:
339
+ """Recursively convert ISO datetime strings back to datetime objects."""
340
+ if isinstance(obj, str):
341
+ # Try to parse as ISO datetime string
342
+ try:
343
+ # Check if it looks like an ISO datetime string
344
+ if "T" in obj and (
345
+ obj.endswith("Z")
346
+ or "+" in obj[-10:]
347
+ or obj.count(":") >= 2
348
+ ):
349
+ return datetime.fromisoformat(obj.replace("Z", "+00:00"))
350
+ except (ValueError, AttributeError):
351
+ # Not a datetime string, return as-is
352
+ pass
353
+ return obj
354
+ elif isinstance(obj, dict):
355
+ return {
356
+ k: cls._deserialize_datetime_recursive(v)
357
+ for k, v in obj.items()
358
+ }
359
+ elif isinstance(obj, list):
360
+ return [cls._deserialize_datetime_recursive(item) for item in obj]
361
+ else:
362
+ return obj
@@ -1,4 +1,4 @@
1
- """Structured concurrency primitives.
1
+ """Structured concurrency primitives for pynector.
2
2
 
3
3
  This module provides structured concurrency primitives using AnyIO,
4
4
  which allows for consistent behavior across asyncio and trio backends.
@@ -6,7 +6,21 @@ which allows for consistent behavior across asyncio and trio backends.
6
6
 
7
7
  from .cancel import CancelScope, fail_after, move_on_after
8
8
  from .errors import get_cancelled_exc_class, shield
9
+ from .patterns import (
10
+ ConnectionPool,
11
+ WorkerPool,
12
+ parallel_requests,
13
+ retry_with_timeout,
14
+ )
9
15
  from .primitives import CapacityLimiter, Condition, Event, Lock, Semaphore
16
+ from .resource_tracker import (
17
+ ResourceTracker,
18
+ cleanup_check,
19
+ get_global_tracker,
20
+ resource_leak_detector,
21
+ track_resource,
22
+ untrack_resource,
23
+ )
10
24
  from .task import TaskGroup, create_task_group
11
25
 
12
26
  __all__ = [
@@ -15,6 +29,10 @@ __all__ = [
15
29
  "CancelScope",
16
30
  "move_on_after",
17
31
  "fail_after",
32
+ "ConnectionPool",
33
+ "WorkerPool",
34
+ "parallel_requests",
35
+ "retry_with_timeout",
18
36
  "Lock",
19
37
  "Semaphore",
20
38
  "CapacityLimiter",
@@ -22,4 +40,10 @@ __all__ = [
22
40
  "Condition",
23
41
  "get_cancelled_exc_class",
24
42
  "shield",
43
+ "ResourceTracker",
44
+ "resource_leak_detector",
45
+ "track_resource",
46
+ "untrack_resource",
47
+ "cleanup_check",
48
+ "get_global_tracker",
25
49
  ]
@@ -0,0 +1,259 @@
1
+ """Common concurrency patterns for structured concurrency."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from collections.abc import Awaitable, Callable
7
+ from types import TracebackType
8
+ from typing import Any, TypeVar
9
+
10
+ import anyio
11
+
12
+ from .cancel import move_on_after
13
+ from .primitives import CapacityLimiter, Lock
14
+ from .resource_tracker import track_resource, untrack_resource
15
+ from .task import create_task_group
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ T = TypeVar("T")
20
+ R = TypeVar("R")
21
+ Response = TypeVar("Response")
22
+
23
+
24
+ class ConnectionPool:
25
+ """A pool of reusable connections."""
26
+
27
+ def __init__(
28
+ self,
29
+ max_connections: int,
30
+ connection_factory: Callable[[], Awaitable[T]],
31
+ ):
32
+ """Initialize a new connection pool."""
33
+ if max_connections < 1:
34
+ raise ValueError("max_connections must be >= 1")
35
+ if not callable(connection_factory):
36
+ raise ValueError("connection_factory must be callable")
37
+
38
+ self._connection_factory = connection_factory
39
+ self._limiter = CapacityLimiter(max_connections)
40
+ self._connections: list[T] = []
41
+ self._lock = Lock()
42
+
43
+ track_resource(self, f"ConnectionPool-{id(self)}", "ConnectionPool")
44
+
45
+ def __del__(self):
46
+ """Clean up resource tracking."""
47
+ try:
48
+ untrack_resource(self)
49
+ except Exception:
50
+ pass
51
+
52
+ async def acquire(self) -> T:
53
+ """Acquire a connection from the pool."""
54
+ await self._limiter.acquire()
55
+
56
+ try:
57
+ async with self._lock:
58
+ if self._connections:
59
+ return self._connections.pop()
60
+
61
+ # No pooled connection available, create new one
62
+ return await self._connection_factory()
63
+ except Exception:
64
+ self._limiter.release()
65
+ raise
66
+
67
+ async def release(self, connection: T) -> None:
68
+ """Release a connection back to the pool."""
69
+ try:
70
+ async with self._lock:
71
+ self._connections.append(connection)
72
+ finally:
73
+ self._limiter.release()
74
+
75
+ async def __aenter__(self) -> ConnectionPool[T]:
76
+ """Enter the connection pool context."""
77
+ return self
78
+
79
+ async def __aexit__(
80
+ self,
81
+ exc_type: type[BaseException] | None,
82
+ exc_val: BaseException | None,
83
+ exc_tb: TracebackType | None,
84
+ ) -> None:
85
+ """Exit the connection pool context."""
86
+ # Clean up any remaining connections
87
+ async with self._lock:
88
+ self._connections.clear()
89
+
90
+
91
+ async def parallel_requests(
92
+ inputs: list[str],
93
+ func: Callable[[str], Awaitable[Response]],
94
+ max_concurrency: int = 10,
95
+ ) -> list[Response]:
96
+ """Execute requests in parallel with controlled concurrency.
97
+
98
+ Args:
99
+ inputs: List of inputs
100
+ fetch_func: Async function
101
+ max_concurrency: Maximum number of concurrent requests
102
+
103
+ Returns:
104
+ List of responses in the same order as inputs
105
+ """
106
+ if not inputs:
107
+ return []
108
+
109
+ results: list[Response | None] = [None] * len(inputs)
110
+
111
+ async def bounded_fetch(
112
+ semaphore: anyio.Semaphore, idx: int, url: str
113
+ ) -> None:
114
+ async with semaphore:
115
+ results[idx] = await func(url)
116
+
117
+ try:
118
+ async with create_task_group() as tg:
119
+ semaphore = anyio.Semaphore(max_concurrency)
120
+
121
+ for i, inp in enumerate(inputs):
122
+ await tg.start_soon(bounded_fetch, semaphore, i, inp)
123
+ except BaseException as e:
124
+ # Re-raise the first exception directly instead of ExceptionGroup
125
+ if hasattr(e, "exceptions") and e.exceptions:
126
+ raise e.exceptions[0]
127
+ else:
128
+ raise
129
+
130
+ return results # type: ignore
131
+
132
+
133
+ async def retry_with_timeout(
134
+ func: Callable[[], Awaitable[T]],
135
+ max_retries: int = 3,
136
+ timeout: float = 30.0,
137
+ backoff_factor: float = 1.0,
138
+ ) -> T:
139
+ """Retry an async function with exponential backoff and timeout.
140
+
141
+ Args:
142
+ func: The async function to retry
143
+ max_retries: Maximum number of retries
144
+ timeout: Timeout for each attempt
145
+ backoff_factor: Multiplier for exponential backoff
146
+
147
+ Returns:
148
+ The result of the successful function call
149
+
150
+ Raises:
151
+ Exception: The last exception raised by the function
152
+ """
153
+ last_exception = None
154
+
155
+ for attempt in range(max_retries):
156
+ try:
157
+ with move_on_after(timeout) as cancel_scope:
158
+ result = await func()
159
+ if not cancel_scope.cancelled_caught:
160
+ return result
161
+ else:
162
+ raise TimeoutError(f"Function timed out after {timeout}s")
163
+ except Exception as e:
164
+ last_exception = e
165
+ if attempt < max_retries - 1:
166
+ delay = backoff_factor * (2**attempt)
167
+ await anyio.sleep(delay)
168
+ continue
169
+
170
+ if last_exception:
171
+ raise last_exception
172
+ else:
173
+ raise RuntimeError("Retry failed without capturing exception")
174
+
175
+
176
+ class WorkerPool:
177
+ """A pool of worker tasks that process items from a queue."""
178
+
179
+ def __init__(
180
+ self, num_workers: int, worker_func: Callable[[Any], Awaitable[None]]
181
+ ):
182
+ """Initialize a new worker pool."""
183
+ if num_workers < 1:
184
+ raise ValueError("num_workers must be >= 1")
185
+ if not callable(worker_func):
186
+ raise ValueError("worker_func must be callable")
187
+
188
+ self._num_workers = num_workers
189
+ self._worker_func = worker_func
190
+ self._queue = anyio.create_memory_object_stream(1000)
191
+ self._task_group = None
192
+
193
+ track_resource(self, f"WorkerPool-{id(self)}", "WorkerPool")
194
+
195
+ def __del__(self):
196
+ """Clean up resource tracking."""
197
+ try:
198
+ untrack_resource(self)
199
+ except Exception:
200
+ pass
201
+
202
+ async def start(self) -> None:
203
+ """Start the worker pool."""
204
+ if self._task_group is not None:
205
+ raise RuntimeError("Worker pool is already started")
206
+
207
+ self._task_group = create_task_group()
208
+ await self._task_group.__aenter__()
209
+
210
+ # Start worker tasks
211
+ for i in range(self._num_workers):
212
+ await self._task_group.start_soon(self._worker_loop)
213
+
214
+ async def stop(self) -> None:
215
+ """Stop the worker pool."""
216
+ if self._task_group is None:
217
+ return
218
+
219
+ # Close the queue to signal workers to stop
220
+ await self._queue[0].aclose()
221
+
222
+ # Wait for all workers to finish
223
+ try:
224
+ await self._task_group.__aexit__(None, None, None)
225
+ finally:
226
+ self._task_group = None
227
+
228
+ async def submit(self, item: Any) -> None:
229
+ """Submit an item for processing."""
230
+ if self._task_group is None:
231
+ raise RuntimeError("Worker pool is not started")
232
+ await self._queue[0].send(item)
233
+
234
+ async def _worker_loop(self) -> None:
235
+ """Main loop for worker tasks."""
236
+ try:
237
+ async with self._queue[1]:
238
+ async for item in self._queue[1]:
239
+ try:
240
+ await self._worker_func(item)
241
+ except Exception as e:
242
+ logger.error(f"Worker error processing item: {e}")
243
+ except anyio.ClosedResourceError:
244
+ # Queue was closed, worker should exit gracefully
245
+ pass
246
+
247
+ async def __aenter__(self) -> WorkerPool:
248
+ """Enter the worker pool context."""
249
+ await self.start()
250
+ return self
251
+
252
+ async def __aexit__(
253
+ self,
254
+ exc_type: type[BaseException] | None,
255
+ exc_val: BaseException | None,
256
+ exc_tb: TracebackType | None,
257
+ ) -> None:
258
+ """Exit the worker pool context."""
259
+ await self.stop()