lionagi 0.5.5__py3-none-any.whl → 0.6.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (433) hide show
  1. lionagi/__init__.py +18 -24
  2. lionagi/{core/_class_registry.py → _class_registry.py} +51 -10
  3. lionagi/_errors.py +35 -0
  4. lionagi/libs/__init__.py +3 -0
  5. lionagi/libs/compress/__init__.py +3 -0
  6. lionagi/libs/compress/models.py +6 -2
  7. lionagi/libs/compress/utils.py +4 -16
  8. lionagi/libs/file/__init__.py +3 -0
  9. lionagi/libs/file/chunk.py +4 -0
  10. lionagi/libs/file/file_ops.py +4 -0
  11. lionagi/libs/file/params.py +4 -41
  12. lionagi/libs/file/process.py +4 -0
  13. lionagi/libs/file/save.py +5 -1
  14. lionagi/libs/{parse/flatten → nested}/flatten.py +4 -0
  15. lionagi/libs/{parse/nested → nested}/nfilter.py +4 -0
  16. lionagi/libs/{parse/nested → nested}/nget.py +6 -1
  17. lionagi/libs/{parse/nested → nested}/ninsert.py +5 -1
  18. lionagi/libs/{parse/nested → nested}/nmerge.py +4 -0
  19. lionagi/libs/{parse/nested → nested}/npop.py +5 -2
  20. lionagi/libs/{parse/nested → nested}/nset.py +6 -1
  21. lionagi/libs/{parse/flatten → nested}/unflatten.py +4 -0
  22. lionagi/libs/{parse/nested → nested}/utils.py +5 -1
  23. lionagi/libs/package/__init__.py +3 -0
  24. lionagi/libs/package/imports.py +6 -2
  25. lionagi/libs/package/management.py +7 -3
  26. lionagi/libs/package/params.py +4 -0
  27. lionagi/libs/package/system.py +4 -0
  28. lionagi/libs/parse.py +30 -0
  29. lionagi/libs/{parse/json → schema}/as_readable.py +10 -4
  30. lionagi/libs/{parse/string_parse/code_block.py → schema/extract_code_block.py} +4 -0
  31. lionagi/libs/{parse/string_parse/docstring.py → schema/extract_docstring.py} +4 -0
  32. lionagi/libs/{parse/string_parse/function_.py → schema/function_to_schema.py} +21 -9
  33. lionagi/libs/{parse/json/schema.py → schema/json_schema.py} +5 -1
  34. lionagi/libs/validate/common_field_validators.py +170 -0
  35. lionagi/libs/{parse/validate/keys.py → validate/fuzzy_match_keys.py} +42 -8
  36. lionagi/libs/{parse/validate/mapping.py → validate/fuzzy_validate_mapping.py} +41 -6
  37. lionagi/libs/{string_similarity/algorithms.py → validate/string_similarity.py} +115 -1
  38. lionagi/libs/{parse/validate/boolean.py → validate/validate_boolean.py} +42 -3
  39. lionagi/operations/__init__.py +13 -3
  40. lionagi/operations/brainstorm/__init__.py +3 -3
  41. lionagi/operations/brainstorm/brainstorm.py +33 -19
  42. lionagi/operations/brainstorm/prompt.py +4 -0
  43. lionagi/operations/plan/__init__.py +4 -0
  44. lionagi/operations/plan/plan.py +19 -16
  45. lionagi/operations/plan/prompt.py +4 -0
  46. lionagi/operations/select/__init__.py +4 -0
  47. lionagi/operations/select/prompt.py +4 -0
  48. lionagi/operations/select/select.py +2 -2
  49. lionagi/operations/select/utils.py +4 -4
  50. lionagi/{strategies → operations/strategies}/base.py +6 -2
  51. lionagi/{strategies → operations/strategies}/concurrent.py +8 -5
  52. lionagi/{strategies → operations/strategies}/concurrent_chunk.py +6 -3
  53. lionagi/{strategies → operations/strategies}/concurrent_sequential_chunk.py +8 -4
  54. lionagi/{strategies → operations/strategies}/params.py +26 -6
  55. lionagi/{strategies → operations/strategies}/sequential.py +6 -2
  56. lionagi/{strategies → operations/strategies}/sequential_chunk.py +7 -3
  57. lionagi/{strategies → operations/strategies}/sequential_concurrent_chunk.py +9 -4
  58. lionagi/{strategies → operations/strategies}/utils.py +6 -3
  59. lionagi/operations/types.py +13 -0
  60. lionagi/operations/utils.py +6 -3
  61. lionagi/operatives/action/function_calling.py +136 -0
  62. lionagi/operatives/action/manager.py +236 -0
  63. lionagi/operatives/action/request_response_model.py +90 -0
  64. lionagi/operatives/action/tool.py +141 -0
  65. lionagi/{protocols/operatives/action.py → operatives/action/utils.py} +52 -90
  66. lionagi/{core → operatives}/forms/base.py +9 -4
  67. lionagi/{core → operatives}/forms/form.py +8 -13
  68. lionagi/{core → operatives}/forms/report.py +5 -3
  69. lionagi/operatives/instruct/base.py +79 -0
  70. lionagi/operatives/instruct/instruct.py +105 -0
  71. lionagi/operatives/instruct/instruct_collection.py +52 -0
  72. lionagi/operatives/instruct/node.py +13 -0
  73. lionagi/{protocols/operatives → operatives/instruct}/prompts.py +0 -34
  74. lionagi/{protocols/operatives → operatives/instruct}/reason.py +14 -7
  75. lionagi/{core/models/__init__.py → operatives/manager.py} +5 -1
  76. lionagi/operatives/models/field_model.py +194 -0
  77. lionagi/operatives/models/model_params.py +307 -0
  78. lionagi/{core → operatives}/models/note.py +20 -28
  79. lionagi/{core → operatives}/models/operable_model.py +153 -71
  80. lionagi/{core → operatives}/models/schema_model.py +4 -3
  81. lionagi/{protocols/operatives → operatives}/operative.py +10 -7
  82. lionagi/{protocols/operatives → operatives}/step.py +67 -26
  83. lionagi/operatives/types.py +69 -0
  84. lionagi/protocols/_concepts.py +94 -0
  85. lionagi/protocols/adapters/adapter.py +23 -7
  86. lionagi/protocols/adapters/json_adapter.py +72 -14
  87. lionagi/protocols/adapters/pandas_/csv_adapter.py +50 -0
  88. lionagi/protocols/adapters/pandas_/excel_adapter.py +52 -0
  89. lionagi/protocols/adapters/pandas_/pd_dataframe_adapter.py +31 -0
  90. lionagi/protocols/adapters/pandas_/pd_series_adapter.py +17 -0
  91. lionagi/protocols/adapters/types.py +18 -0
  92. lionagi/protocols/generic/element.py +460 -0
  93. lionagi/protocols/generic/event.py +177 -0
  94. lionagi/protocols/generic/log.py +237 -0
  95. lionagi/{core → protocols}/generic/pile.py +193 -131
  96. lionagi/protocols/generic/processor.py +316 -0
  97. lionagi/protocols/generic/progression.py +500 -0
  98. lionagi/protocols/graph/edge.py +166 -0
  99. lionagi/protocols/graph/graph.py +290 -0
  100. lionagi/protocols/graph/node.py +125 -0
  101. lionagi/protocols/mail/exchange.py +116 -0
  102. lionagi/protocols/mail/mail.py +25 -0
  103. lionagi/protocols/mail/mailbox.py +47 -0
  104. lionagi/protocols/mail/manager.py +168 -0
  105. lionagi/protocols/mail/package.py +55 -0
  106. lionagi/protocols/messages/action_request.py +165 -0
  107. lionagi/protocols/messages/action_response.py +132 -0
  108. lionagi/{core/communication → protocols/messages}/assistant_response.py +55 -79
  109. lionagi/protocols/messages/base.py +73 -0
  110. lionagi/protocols/messages/instruction.py +582 -0
  111. lionagi/protocols/messages/manager.py +429 -0
  112. lionagi/protocols/messages/message.py +216 -0
  113. lionagi/protocols/messages/system.py +115 -0
  114. lionagi/protocols/messages/templates/assistant_response.jinja2 +6 -0
  115. lionagi/{core/communication → protocols/messages}/templates/instruction_message.jinja2 +2 -2
  116. lionagi/protocols/types.py +96 -0
  117. lionagi/service/__init__.py +1 -16
  118. lionagi/service/endpoints/base.py +517 -0
  119. lionagi/service/endpoints/chat_completion.py +102 -0
  120. lionagi/service/endpoints/match_endpoint.py +60 -0
  121. lionagi/service/endpoints/rate_limited_processor.py +146 -0
  122. lionagi/service/endpoints/token_calculator.py +209 -0
  123. lionagi/service/imodel.py +263 -96
  124. lionagi/service/manager.py +45 -0
  125. lionagi/service/providers/anthropic_/messages.py +64 -0
  126. lionagi/service/providers/groq_/chat_completions.py +56 -0
  127. lionagi/service/providers/openai_/chat_completions.py +62 -0
  128. lionagi/service/providers/openrouter_/chat_completions.py +62 -0
  129. lionagi/service/providers/perplexity_/__init__.py +3 -0
  130. lionagi/service/providers/perplexity_/chat_completions.py +40 -0
  131. lionagi/service/types.py +18 -0
  132. lionagi/session/__init__.py +3 -0
  133. lionagi/session/branch.py +1287 -0
  134. lionagi/session/session.py +296 -0
  135. lionagi/settings.py +62 -118
  136. lionagi/utils.py +2386 -0
  137. lionagi/version.py +1 -1
  138. {lionagi-0.5.5.dist-info → lionagi-0.6.1.dist-info}/METADATA +10 -9
  139. lionagi-0.6.1.dist-info/RECORD +169 -0
  140. lionagi/core/action/action_manager.py +0 -289
  141. lionagi/core/action/base.py +0 -109
  142. lionagi/core/action/function_calling.py +0 -153
  143. lionagi/core/action/tool.py +0 -202
  144. lionagi/core/action/types.py +0 -16
  145. lionagi/core/communication/action_request.py +0 -163
  146. lionagi/core/communication/action_response.py +0 -149
  147. lionagi/core/communication/base_mail.py +0 -49
  148. lionagi/core/communication/instruction.py +0 -376
  149. lionagi/core/communication/message.py +0 -286
  150. lionagi/core/communication/message_manager.py +0 -543
  151. lionagi/core/communication/system.py +0 -116
  152. lionagi/core/communication/templates/assistant_response.jinja2 +0 -2
  153. lionagi/core/communication/types.py +0 -27
  154. lionagi/core/communication/utils.py +0 -256
  155. lionagi/core/forms/types.py +0 -13
  156. lionagi/core/generic/component.py +0 -422
  157. lionagi/core/generic/edge.py +0 -163
  158. lionagi/core/generic/element.py +0 -199
  159. lionagi/core/generic/graph.py +0 -377
  160. lionagi/core/generic/log.py +0 -151
  161. lionagi/core/generic/log_manager.py +0 -320
  162. lionagi/core/generic/node.py +0 -11
  163. lionagi/core/generic/progression.py +0 -395
  164. lionagi/core/generic/types.py +0 -23
  165. lionagi/core/generic/utils.py +0 -53
  166. lionagi/core/models/base.py +0 -28
  167. lionagi/core/models/field_model.py +0 -145
  168. lionagi/core/models/model_params.py +0 -194
  169. lionagi/core/models/types.py +0 -19
  170. lionagi/core/session/branch.py +0 -130
  171. lionagi/core/session/branch_mixins.py +0 -581
  172. lionagi/core/session/session.py +0 -163
  173. lionagi/core/session/types.py +0 -8
  174. lionagi/core/typing/__init__.py +0 -9
  175. lionagi/core/typing/_concepts.py +0 -173
  176. lionagi/core/typing/_id.py +0 -104
  177. lionagi/core/typing/_pydantic.py +0 -33
  178. lionagi/core/typing/_typing.py +0 -54
  179. lionagi/integrations/_services.py +0 -17
  180. lionagi/integrations/anthropic_/AnthropicModel.py +0 -268
  181. lionagi/integrations/anthropic_/AnthropicService.py +0 -127
  182. lionagi/integrations/anthropic_/anthropic_max_output_token_data.yaml +0 -12
  183. lionagi/integrations/anthropic_/anthropic_price_data.yaml +0 -34
  184. lionagi/integrations/anthropic_/api_endpoints/api_request.py +0 -277
  185. lionagi/integrations/anthropic_/api_endpoints/data_models.py +0 -40
  186. lionagi/integrations/anthropic_/api_endpoints/match_response.py +0 -119
  187. lionagi/integrations/anthropic_/api_endpoints/messages/request/message_models.py +0 -14
  188. lionagi/integrations/anthropic_/api_endpoints/messages/request/request_body.py +0 -74
  189. lionagi/integrations/anthropic_/api_endpoints/messages/response/__init__.py +0 -0
  190. lionagi/integrations/anthropic_/api_endpoints/messages/response/content_models.py +0 -32
  191. lionagi/integrations/anthropic_/api_endpoints/messages/response/response_body.py +0 -101
  192. lionagi/integrations/anthropic_/api_endpoints/messages/response/usage_models.py +0 -25
  193. lionagi/integrations/anthropic_/version.py +0 -5
  194. lionagi/integrations/groq_/GroqModel.py +0 -325
  195. lionagi/integrations/groq_/GroqService.py +0 -156
  196. lionagi/integrations/groq_/api_endpoints/__init__.py +0 -0
  197. lionagi/integrations/groq_/api_endpoints/data_models.py +0 -187
  198. lionagi/integrations/groq_/api_endpoints/groq_request.py +0 -288
  199. lionagi/integrations/groq_/api_endpoints/match_response.py +0 -106
  200. lionagi/integrations/groq_/api_endpoints/response_utils.py +0 -105
  201. lionagi/integrations/groq_/groq_max_output_token_data.yaml +0 -21
  202. lionagi/integrations/groq_/groq_price_data.yaml +0 -58
  203. lionagi/integrations/groq_/groq_rate_limits.yaml +0 -105
  204. lionagi/integrations/groq_/version.py +0 -5
  205. lionagi/integrations/litellm_/imodel.py +0 -76
  206. lionagi/integrations/ollama_/OllamaModel.py +0 -244
  207. lionagi/integrations/ollama_/OllamaService.py +0 -142
  208. lionagi/integrations/ollama_/api_endpoints/api_request.py +0 -179
  209. lionagi/integrations/ollama_/api_endpoints/chat_completion/message_models.py +0 -31
  210. lionagi/integrations/ollama_/api_endpoints/chat_completion/request_body.py +0 -46
  211. lionagi/integrations/ollama_/api_endpoints/chat_completion/response_body.py +0 -67
  212. lionagi/integrations/ollama_/api_endpoints/chat_completion/tool_models.py +0 -49
  213. lionagi/integrations/ollama_/api_endpoints/completion/__init__.py +0 -0
  214. lionagi/integrations/ollama_/api_endpoints/completion/request_body.py +0 -72
  215. lionagi/integrations/ollama_/api_endpoints/completion/response_body.py +0 -59
  216. lionagi/integrations/ollama_/api_endpoints/data_models.py +0 -15
  217. lionagi/integrations/ollama_/api_endpoints/embedding/__init__.py +0 -0
  218. lionagi/integrations/ollama_/api_endpoints/embedding/request_body.py +0 -33
  219. lionagi/integrations/ollama_/api_endpoints/embedding/response_body.py +0 -29
  220. lionagi/integrations/ollama_/api_endpoints/match_data_model.py +0 -62
  221. lionagi/integrations/ollama_/api_endpoints/match_response.py +0 -190
  222. lionagi/integrations/ollama_/api_endpoints/model/copy_model.py +0 -13
  223. lionagi/integrations/ollama_/api_endpoints/model/create_model.py +0 -28
  224. lionagi/integrations/ollama_/api_endpoints/model/delete_model.py +0 -11
  225. lionagi/integrations/ollama_/api_endpoints/model/list_model.py +0 -60
  226. lionagi/integrations/ollama_/api_endpoints/model/pull_model.py +0 -34
  227. lionagi/integrations/ollama_/api_endpoints/model/push_model.py +0 -35
  228. lionagi/integrations/ollama_/api_endpoints/model/show_model.py +0 -36
  229. lionagi/integrations/ollama_/api_endpoints/option_models.py +0 -68
  230. lionagi/integrations/openai_/OpenAIModel.py +0 -419
  231. lionagi/integrations/openai_/OpenAIService.py +0 -435
  232. lionagi/integrations/openai_/__init__.py +0 -0
  233. lionagi/integrations/openai_/api_endpoints/__init__.py +0 -3
  234. lionagi/integrations/openai_/api_endpoints/api_request.py +0 -277
  235. lionagi/integrations/openai_/api_endpoints/audio/__init__.py +0 -9
  236. lionagi/integrations/openai_/api_endpoints/audio/speech_models.py +0 -34
  237. lionagi/integrations/openai_/api_endpoints/audio/transcription_models.py +0 -136
  238. lionagi/integrations/openai_/api_endpoints/audio/translation_models.py +0 -41
  239. lionagi/integrations/openai_/api_endpoints/audio/types.py +0 -41
  240. lionagi/integrations/openai_/api_endpoints/batch/__init__.py +0 -17
  241. lionagi/integrations/openai_/api_endpoints/batch/batch_models.py +0 -146
  242. lionagi/integrations/openai_/api_endpoints/batch/cancel_batch.py +0 -7
  243. lionagi/integrations/openai_/api_endpoints/batch/create_batch.py +0 -26
  244. lionagi/integrations/openai_/api_endpoints/batch/list_batch.py +0 -37
  245. lionagi/integrations/openai_/api_endpoints/batch/request_object_models.py +0 -65
  246. lionagi/integrations/openai_/api_endpoints/batch/retrieve_batch.py +0 -7
  247. lionagi/integrations/openai_/api_endpoints/batch/types.py +0 -4
  248. lionagi/integrations/openai_/api_endpoints/chat_completions/__init__.py +0 -1
  249. lionagi/integrations/openai_/api_endpoints/chat_completions/request/__init__.py +0 -39
  250. lionagi/integrations/openai_/api_endpoints/chat_completions/request/message_models.py +0 -121
  251. lionagi/integrations/openai_/api_endpoints/chat_completions/request/request_body.py +0 -221
  252. lionagi/integrations/openai_/api_endpoints/chat_completions/request/response_format.py +0 -71
  253. lionagi/integrations/openai_/api_endpoints/chat_completions/request/stream_options.py +0 -14
  254. lionagi/integrations/openai_/api_endpoints/chat_completions/request/tool_choice_models.py +0 -17
  255. lionagi/integrations/openai_/api_endpoints/chat_completions/request/tool_models.py +0 -54
  256. lionagi/integrations/openai_/api_endpoints/chat_completions/request/types.py +0 -18
  257. lionagi/integrations/openai_/api_endpoints/chat_completions/response/__init__.py +0 -0
  258. lionagi/integrations/openai_/api_endpoints/chat_completions/response/choice_models.py +0 -62
  259. lionagi/integrations/openai_/api_endpoints/chat_completions/response/function_models.py +0 -16
  260. lionagi/integrations/openai_/api_endpoints/chat_completions/response/log_prob_models.py +0 -47
  261. lionagi/integrations/openai_/api_endpoints/chat_completions/response/message_models.py +0 -25
  262. lionagi/integrations/openai_/api_endpoints/chat_completions/response/response_body.py +0 -99
  263. lionagi/integrations/openai_/api_endpoints/chat_completions/response/types.py +0 -8
  264. lionagi/integrations/openai_/api_endpoints/chat_completions/response/usage_models.py +0 -24
  265. lionagi/integrations/openai_/api_endpoints/chat_completions/util.py +0 -46
  266. lionagi/integrations/openai_/api_endpoints/data_models.py +0 -23
  267. lionagi/integrations/openai_/api_endpoints/embeddings/__init__.py +0 -3
  268. lionagi/integrations/openai_/api_endpoints/embeddings/request_body.py +0 -79
  269. lionagi/integrations/openai_/api_endpoints/embeddings/response_body.py +0 -67
  270. lionagi/integrations/openai_/api_endpoints/files/__init__.py +0 -11
  271. lionagi/integrations/openai_/api_endpoints/files/delete_file.py +0 -20
  272. lionagi/integrations/openai_/api_endpoints/files/file_models.py +0 -56
  273. lionagi/integrations/openai_/api_endpoints/files/list_files.py +0 -27
  274. lionagi/integrations/openai_/api_endpoints/files/retrieve_file.py +0 -9
  275. lionagi/integrations/openai_/api_endpoints/files/upload_file.py +0 -38
  276. lionagi/integrations/openai_/api_endpoints/fine_tuning/__init__.py +0 -37
  277. lionagi/integrations/openai_/api_endpoints/fine_tuning/cancel_jobs.py +0 -9
  278. lionagi/integrations/openai_/api_endpoints/fine_tuning/create_jobs.py +0 -133
  279. lionagi/integrations/openai_/api_endpoints/fine_tuning/fine_tuning_job_checkpoint_models.py +0 -58
  280. lionagi/integrations/openai_/api_endpoints/fine_tuning/fine_tuning_job_event_models.py +0 -31
  281. lionagi/integrations/openai_/api_endpoints/fine_tuning/fine_tuning_job_models.py +0 -140
  282. lionagi/integrations/openai_/api_endpoints/fine_tuning/list_fine_tuning_checkpoints.py +0 -51
  283. lionagi/integrations/openai_/api_endpoints/fine_tuning/list_fine_tuning_events.py +0 -42
  284. lionagi/integrations/openai_/api_endpoints/fine_tuning/list_fine_tuning_jobs.py +0 -31
  285. lionagi/integrations/openai_/api_endpoints/fine_tuning/retrieve_jobs.py +0 -9
  286. lionagi/integrations/openai_/api_endpoints/fine_tuning/training_format.py +0 -30
  287. lionagi/integrations/openai_/api_endpoints/images/__init__.py +0 -9
  288. lionagi/integrations/openai_/api_endpoints/images/image_edit_models.py +0 -69
  289. lionagi/integrations/openai_/api_endpoints/images/image_models.py +0 -56
  290. lionagi/integrations/openai_/api_endpoints/images/image_variation_models.py +0 -56
  291. lionagi/integrations/openai_/api_endpoints/images/response_body.py +0 -30
  292. lionagi/integrations/openai_/api_endpoints/match_data_model.py +0 -197
  293. lionagi/integrations/openai_/api_endpoints/match_response.py +0 -336
  294. lionagi/integrations/openai_/api_endpoints/models/__init__.py +0 -7
  295. lionagi/integrations/openai_/api_endpoints/models/delete_fine_tuned_model.py +0 -17
  296. lionagi/integrations/openai_/api_endpoints/models/models_models.py +0 -31
  297. lionagi/integrations/openai_/api_endpoints/models/retrieve_model.py +0 -9
  298. lionagi/integrations/openai_/api_endpoints/moderations/__init__.py +0 -3
  299. lionagi/integrations/openai_/api_endpoints/moderations/request_body.py +0 -20
  300. lionagi/integrations/openai_/api_endpoints/moderations/response_body.py +0 -139
  301. lionagi/integrations/openai_/api_endpoints/uploads/__init__.py +0 -19
  302. lionagi/integrations/openai_/api_endpoints/uploads/add_upload_part.py +0 -11
  303. lionagi/integrations/openai_/api_endpoints/uploads/cancel_upload.py +0 -7
  304. lionagi/integrations/openai_/api_endpoints/uploads/complete_upload.py +0 -18
  305. lionagi/integrations/openai_/api_endpoints/uploads/create_upload.py +0 -17
  306. lionagi/integrations/openai_/api_endpoints/uploads/uploads_models.py +0 -52
  307. lionagi/integrations/openai_/image_token_calculator/__init__.py +0 -0
  308. lionagi/integrations/openai_/image_token_calculator/image_token_calculator.py +0 -98
  309. lionagi/integrations/openai_/image_token_calculator/openai_image_token_data.yaml +0 -15
  310. lionagi/integrations/openai_/openai_max_output_token_data.yaml +0 -12
  311. lionagi/integrations/openai_/openai_price_data.yaml +0 -26
  312. lionagi/integrations/openai_/version.py +0 -1
  313. lionagi/integrations/pandas_/__init__.py +0 -24
  314. lionagi/integrations/pandas_/extend_df.py +0 -61
  315. lionagi/integrations/pandas_/read.py +0 -103
  316. lionagi/integrations/pandas_/remove_rows.py +0 -61
  317. lionagi/integrations/pandas_/replace_keywords.py +0 -65
  318. lionagi/integrations/pandas_/save.py +0 -131
  319. lionagi/integrations/pandas_/search_keywords.py +0 -69
  320. lionagi/integrations/pandas_/to_df.py +0 -196
  321. lionagi/integrations/pandas_/update_cells.py +0 -54
  322. lionagi/integrations/perplexity_/PerplexityModel.py +0 -274
  323. lionagi/integrations/perplexity_/PerplexityService.py +0 -118
  324. lionagi/integrations/perplexity_/api_endpoints/__init__.py +0 -0
  325. lionagi/integrations/perplexity_/api_endpoints/api_request.py +0 -171
  326. lionagi/integrations/perplexity_/api_endpoints/chat_completions/__init__.py +0 -0
  327. lionagi/integrations/perplexity_/api_endpoints/chat_completions/request/__init__.py +0 -0
  328. lionagi/integrations/perplexity_/api_endpoints/chat_completions/request/request_body.py +0 -121
  329. lionagi/integrations/perplexity_/api_endpoints/chat_completions/response/__init__.py +0 -0
  330. lionagi/integrations/perplexity_/api_endpoints/chat_completions/response/response_body.py +0 -146
  331. lionagi/integrations/perplexity_/api_endpoints/data_models.py +0 -63
  332. lionagi/integrations/perplexity_/api_endpoints/match_response.py +0 -26
  333. lionagi/integrations/perplexity_/perplexity_max_output_token_data.yaml +0 -3
  334. lionagi/integrations/perplexity_/perplexity_price_data.yaml +0 -10
  335. lionagi/integrations/perplexity_/version.py +0 -1
  336. lionagi/integrations/pydantic_/__init__.py +0 -8
  337. lionagi/integrations/pydantic_/break_down_annotation.py +0 -81
  338. lionagi/integrations/pydantic_/new_model.py +0 -208
  339. lionagi/libs/constants.py +0 -98
  340. lionagi/libs/file/path.py +0 -301
  341. lionagi/libs/file/types.py +0 -22
  342. lionagi/libs/func/__init__.py +0 -0
  343. lionagi/libs/func/async_calls/__init__.py +0 -24
  344. lionagi/libs/func/async_calls/alcall.py +0 -210
  345. lionagi/libs/func/async_calls/bcall.py +0 -130
  346. lionagi/libs/func/async_calls/mcall.py +0 -134
  347. lionagi/libs/func/async_calls/pcall.py +0 -149
  348. lionagi/libs/func/async_calls/rcall.py +0 -217
  349. lionagi/libs/func/async_calls/tcall.py +0 -114
  350. lionagi/libs/func/async_calls/ucall.py +0 -85
  351. lionagi/libs/func/decorators.py +0 -277
  352. lionagi/libs/func/lcall.py +0 -57
  353. lionagi/libs/func/params.py +0 -64
  354. lionagi/libs/func/throttle.py +0 -119
  355. lionagi/libs/func/types.py +0 -39
  356. lionagi/libs/func/utils.py +0 -96
  357. lionagi/libs/package/types.py +0 -26
  358. lionagi/libs/parse/__init__.py +0 -1
  359. lionagi/libs/parse/flatten/__init__.py +0 -9
  360. lionagi/libs/parse/flatten/params.py +0 -52
  361. lionagi/libs/parse/json/__init__.py +0 -27
  362. lionagi/libs/parse/json/extract.py +0 -102
  363. lionagi/libs/parse/json/parse.py +0 -179
  364. lionagi/libs/parse/json/to_json.py +0 -71
  365. lionagi/libs/parse/nested/__init__.py +0 -33
  366. lionagi/libs/parse/nested/to_flat_list.py +0 -64
  367. lionagi/libs/parse/params.py +0 -0
  368. lionagi/libs/parse/string_parse/__init__.py +0 -11
  369. lionagi/libs/parse/type_convert/__init__.py +0 -19
  370. lionagi/libs/parse/type_convert/params.py +0 -145
  371. lionagi/libs/parse/type_convert/to_dict.py +0 -333
  372. lionagi/libs/parse/type_convert/to_list.py +0 -186
  373. lionagi/libs/parse/type_convert/to_num.py +0 -358
  374. lionagi/libs/parse/type_convert/to_str.py +0 -195
  375. lionagi/libs/parse/types.py +0 -9
  376. lionagi/libs/parse/validate/__init__.py +0 -14
  377. lionagi/libs/parse/validate/params.py +0 -62
  378. lionagi/libs/parse/xml/__init__.py +0 -10
  379. lionagi/libs/parse/xml/convert.py +0 -56
  380. lionagi/libs/parse/xml/parser.py +0 -93
  381. lionagi/libs/string_similarity/__init__.py +0 -32
  382. lionagi/libs/string_similarity/matcher.py +0 -102
  383. lionagi/libs/string_similarity/utils.py +0 -15
  384. lionagi/libs/utils.py +0 -266
  385. lionagi/protocols/adapters/pandas_adapter.py +0 -96
  386. lionagi/protocols/configs/__init__.py +0 -0
  387. lionagi/protocols/configs/branch_config.py +0 -86
  388. lionagi/protocols/configs/id_config.py +0 -15
  389. lionagi/protocols/configs/imodel_config.py +0 -73
  390. lionagi/protocols/configs/log_config.py +0 -93
  391. lionagi/protocols/configs/retry_config.py +0 -29
  392. lionagi/protocols/configs/types.py +0 -15
  393. lionagi/protocols/operatives/instruct.py +0 -194
  394. lionagi/protocols/operatives/types.py +0 -19
  395. lionagi/protocols/registries/_component_registry.py +0 -23
  396. lionagi/protocols/registries/_pile_registry.py +0 -30
  397. lionagi/service/complete_request_info.py +0 -11
  398. lionagi/service/rate_limiter.py +0 -108
  399. lionagi/service/service.py +0 -41
  400. lionagi/service/service_match_util.py +0 -131
  401. lionagi/service/service_util.py +0 -72
  402. lionagi/service/token_calculator.py +0 -51
  403. lionagi/strategies/__init__.py +0 -0
  404. lionagi/strategies/types.py +0 -21
  405. lionagi-0.5.5.dist-info/RECORD +0 -374
  406. /lionagi/{core → libs/nested}/__init__.py +0 -0
  407. /lionagi/{core/action → libs/schema}/__init__.py +0 -0
  408. /lionagi/{core/communication → libs/validate}/__init__.py +0 -0
  409. /lionagi/{core/forms → operations/strategies}/__init__.py +0 -0
  410. /lionagi/{core/generic → operatives}/__init__.py +0 -0
  411. /lionagi/{core/session → operatives/action}/__init__.py +0 -0
  412. /lionagi/{integrations/anthropic_ → operatives/forms}/__init__.py +0 -0
  413. /lionagi/{core → operatives}/forms/utils.py +0 -0
  414. /lionagi/{integrations/anthropic_/api_endpoints → operatives/instruct}/__init__.py +0 -0
  415. /lionagi/{integrations/anthropic_/api_endpoints/messages → operatives/models}/__init__.py +0 -0
  416. /lionagi/{integrations → protocols/adapters/pandas_}/__init__.py +0 -0
  417. /lionagi/{integrations/anthropic_/api_endpoints/messages/request → protocols/generic}/__init__.py +0 -0
  418. /lionagi/{integrations/groq_ → protocols/graph}/__init__.py +0 -0
  419. /lionagi/{integrations/litellm_ → protocols/mail}/__init__.py +0 -0
  420. /lionagi/{integrations/ollama_ → protocols/messages}/__init__.py +0 -0
  421. /lionagi/{core/communication → protocols/messages}/templates/README.md +0 -0
  422. /lionagi/{core/communication → protocols/messages}/templates/action_request.jinja2 +0 -0
  423. /lionagi/{core/communication → protocols/messages}/templates/action_response.jinja2 +0 -0
  424. /lionagi/{core/communication → protocols/messages}/templates/system_message.jinja2 +0 -0
  425. /lionagi/{core/communication → protocols/messages}/templates/tool_schemas.jinja2 +0 -0
  426. /lionagi/{integrations/ollama_/api_endpoints → service/endpoints}/__init__.py +0 -0
  427. /lionagi/{integrations/ollama_/api_endpoints/chat_completion → service/providers}/__init__.py +0 -0
  428. /lionagi/{integrations/ollama_/api_endpoints/model → service/providers/anthropic_}/__init__.py +0 -0
  429. /lionagi/{integrations/perplexity_ → service/providers/groq_}/__init__.py +0 -0
  430. /lionagi/{protocols/operatives → service/providers/openai_}/__init__.py +0 -0
  431. /lionagi/{protocols/registries → service/providers/openrouter_}/__init__.py +0 -0
  432. {lionagi-0.5.5.dist-info → lionagi-0.6.1.dist-info}/WHEEL +0 -0
  433. {lionagi-0.5.5.dist-info → lionagi-0.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1287 @@
1
+ # Copyright (c) 2023 - 2024, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import logging
6
+ from typing import Any, Literal
7
+
8
+ import pandas as pd
9
+ from jinja2 import Template
10
+ from pydantic import BaseModel, Field, JsonValue, PrivateAttr
11
+
12
+ from lionagi.libs.validate.fuzzy_validate_mapping import fuzzy_validate_mapping
13
+ from lionagi.operatives.models.field_model import FieldModel
14
+ from lionagi.operatives.models.model_params import ModelParams
15
+ from lionagi.operatives.types import (
16
+ ActionManager,
17
+ ActionResponseModel,
18
+ FunctionCalling,
19
+ FuncTool,
20
+ Instruct,
21
+ Operative,
22
+ Step,
23
+ Tool,
24
+ ToolRef,
25
+ )
26
+ from lionagi.protocols.types import (
27
+ ID,
28
+ MESSAGE_FIELDS,
29
+ ActionRequest,
30
+ ActionResponse,
31
+ AssistantResponse,
32
+ Communicatable,
33
+ Element,
34
+ IDType,
35
+ Instruction,
36
+ Log,
37
+ LogManager,
38
+ LogManagerConfig,
39
+ Mail,
40
+ Mailbox,
41
+ MessageFlag,
42
+ MessageManager,
43
+ MessageRole,
44
+ Package,
45
+ PackageCategory,
46
+ Pile,
47
+ Progression,
48
+ Relational,
49
+ RoledMessage,
50
+ SenderRecipient,
51
+ System,
52
+ )
53
+ from lionagi.service import iModel, iModelManager
54
+ from lionagi.settings import Settings
55
+ from lionagi.utils import (
56
+ UNDEFINED,
57
+ alcall,
58
+ breakdown_pydantic_annotation,
59
+ copy,
60
+ )
61
+
62
+ __all__ = ("Branch",)
63
+
64
+
65
+ class Branch(Element, Communicatable, Relational):
66
+ """Manages a conversation 'branch' with messages, tools, and iModels.
67
+
68
+ The Branch class orchestrates message handling, model invocation,
69
+ action (tool) execution, logging, and mailbox-based communication.
70
+ It maintains references to a MessageManager, ActionManager, iModelManager,
71
+ and a LogManager, providing high-level methods for combined operations.
72
+
73
+ Attributes:
74
+ user (SenderRecipient | None):
75
+ The user or sender of this branch context (e.g., a session object).
76
+ name (str | None):
77
+ A human-readable name for this branch.
78
+ mailbox (Mailbox):
79
+ A mailbox for sending and receiving `Package`s to/from other
80
+ branches or components.
81
+ """
82
+
83
+ user: SenderRecipient | None = Field(
84
+ None,
85
+ description=(
86
+ "The user or sender of the branch, typically a session object or"
87
+ "an external user identifier. Please note that this is a distinct"
88
+ "concept from `user` parameter in LLM API calls."
89
+ ),
90
+ )
91
+
92
+ name: str | None = Field(
93
+ None,
94
+ description="A human readable name of the branch, if any.",
95
+ )
96
+
97
+ mailbox: Mailbox = Field(default_factory=Mailbox, exclude=True)
98
+
99
+ _message_manager: MessageManager | None = PrivateAttr(None)
100
+ _action_manager: ActionManager | None = PrivateAttr(None)
101
+ _imodel_manager: iModelManager | None = PrivateAttr(None)
102
+ _log_manager: LogManager | None = PrivateAttr(None)
103
+
104
+ def __init__(
105
+ self,
106
+ *,
107
+ user: SenderRecipient = None,
108
+ name: str | None = None,
109
+ messages: Pile[RoledMessage] = None, # message manager kwargs
110
+ system: System | JsonValue = None,
111
+ system_sender: SenderRecipient = None,
112
+ chat_model: iModel = None, # imodel manager kwargs
113
+ parse_model: iModel = None,
114
+ imodel: iModel = None, # deprecated, alias of chat_model
115
+ tools: FuncTool | list[FuncTool] = None, # action manager kwargs
116
+ log_config: LogManagerConfig | dict = None, # log manager kwargs
117
+ system_datetime: bool | str = None,
118
+ system_template: Template | str = None,
119
+ system_template_context: dict = None,
120
+ logs: Pile[Log] = None,
121
+ **kwargs,
122
+ ):
123
+ """Initializes a Branch with references to managers and mailbox.
124
+
125
+ Args:
126
+ user (SenderRecipient, optional):
127
+ The user or sender of the branch context.
128
+ name (str | None, optional):
129
+ A human-readable name for this branch.
130
+ messages (Pile[RoledMessage], optional):
131
+ Initial messages to seed the MessageManager.
132
+ system (System | JsonValue, optional):
133
+ A system message or data to configure system role.
134
+ system_sender (SenderRecipient, optional):
135
+ Sender to assign if the system message is added.
136
+ chat_model (iModel, optional):
137
+ The chat model used by iModelManager (if not provided,
138
+ falls back to defaults).
139
+ parse_model (iModel, optional):
140
+ The parse model used by iModelManager.
141
+ imodel (iModel, optional):
142
+ Deprecated. Alias for chat_model.
143
+ tools (FuncTool | list[FuncTool], optional):
144
+ Tools for the ActionManager.
145
+ log_config (LogManagerConfig | dict, optional):
146
+ Configuration for the LogManager.
147
+ system_datetime (bool | str, optional):
148
+ Whether to include timestamps in system messages.
149
+ system_template (Template | str, optional):
150
+ A Jinja2 template or template string for system messages.
151
+ system_template_context (dict, optional):
152
+ Context variables for the system template.
153
+ **kwargs: Additional parameters passed to the Element parent init.
154
+ """
155
+ super().__init__(user=user, name=name, **kwargs)
156
+
157
+ self._message_manager = MessageManager(messages=messages)
158
+ if any(
159
+ i is not None
160
+ for i in [system, system_sender, system_datetime, system_template]
161
+ ):
162
+
163
+ self._message_manager.add_message(
164
+ system=system,
165
+ system_datetime=system_datetime,
166
+ template=system_template,
167
+ template_context=system_template_context,
168
+ recipient=self.id,
169
+ sender=system_sender or self.user or MessageRole.SYSTEM,
170
+ )
171
+
172
+ chat_model = chat_model or imodel
173
+ if not chat_model:
174
+ chat_model = iModel(**Settings.iModel.CHAT)
175
+ if not parse_model:
176
+ parse_model = iModel(**Settings.iModel.PARSE)
177
+
178
+ if isinstance(chat_model, dict):
179
+ chat_model = iModel.from_dict(chat_model)
180
+ if isinstance(parse_model, dict):
181
+ parse_model = iModel.from_dict(parse_model)
182
+
183
+ self._imodel_manager = iModelManager(
184
+ chat=chat_model, parse=parse_model
185
+ )
186
+ self._action_manager = ActionManager(tools)
187
+
188
+ if log_config:
189
+ log_config = (
190
+ log_config
191
+ if isinstance(log_config, LogManagerConfig)
192
+ else LogManagerConfig(**log_config)
193
+ )
194
+ self._log_manager = LogManager.from_config(log_config, logs=logs)
195
+ else:
196
+ self._log_manager = LogManager(**Settings.Config.LOG, logs=logs)
197
+
198
+ @property
199
+ def system(self) -> System | None:
200
+ """System | None: The system message or configuration."""
201
+ return self._message_manager.system
202
+
203
+ @property
204
+ def msgs(self) -> MessageManager:
205
+ """MessageManager: Manages the conversation messages."""
206
+ return self._message_manager
207
+
208
+ @property
209
+ def acts(self) -> ActionManager:
210
+ """ActionManager: Manages available tools (actions)."""
211
+ return self._action_manager
212
+
213
+ @property
214
+ def mdls(self) -> iModelManager:
215
+ """iModelManager: Manages chat and parse models."""
216
+ return self._imodel_manager
217
+
218
+ @property
219
+ def messages(self) -> Pile[RoledMessage]:
220
+ """Pile[RoledMessage]: The collection of messages in this branch."""
221
+ return self._message_manager.messages
222
+
223
+ @property
224
+ def logs(self) -> Pile[Log]:
225
+ """Pile[Log]: The collection of log entries for this branch."""
226
+ return self._log_manager.logs
227
+
228
+ @property
229
+ def chat_model(self) -> iModel:
230
+ """iModel: The primary (chat) model in the iModelManager."""
231
+ return self._imodel_manager.chat
232
+
233
+ @chat_model.setter
234
+ def chat_model(self, value: iModel) -> None:
235
+ """Sets the chat model in the iModelManager.
236
+
237
+ Args:
238
+ value (iModel): The new chat model.
239
+ """
240
+ self._imodel_manager.register_imodel("chat", value)
241
+
242
+ @property
243
+ def parse_model(self) -> iModel:
244
+ """iModel: The parsing model in the iModelManager."""
245
+ return self._imodel_manager.parse
246
+
247
+ @parse_model.setter
248
+ def parse_model(self, value: iModel) -> None:
249
+ """Sets the parse model in the iModelManager.
250
+
251
+ Args:
252
+ value (iModel): The new parse model.
253
+ """
254
+ self._imodel_manager.register_imodel("parse", value)
255
+
256
+ @property
257
+ def tools(self) -> dict[str, Tool]:
258
+ """dict[str, Tool]: All tools registered in the ActionManager."""
259
+ return self._action_manager.registry
260
+
261
+ async def aclone(self, sender: ID.Ref = None) -> "Branch":
262
+ """Asynchronous clone of this Branch.
263
+
264
+ Args:
265
+ sender (ID.Ref, optional):
266
+ If provided, sets this sender ID on all cloned messages.
267
+
268
+ Returns:
269
+ Branch: A new branch instance with cloned messages.
270
+ """
271
+ async with self.msgs.messages:
272
+ return self.clone(sender)
273
+
274
+ async def operate(
275
+ self,
276
+ *,
277
+ instruct: Instruct = None,
278
+ instruction: Instruction | JsonValue = None,
279
+ guidance: JsonValue = None,
280
+ context: JsonValue = None,
281
+ sender: SenderRecipient = None,
282
+ recipient: SenderRecipient = None,
283
+ progression: Progression = None,
284
+ imodel: iModel = None, # deprecated, alias of chat_model
285
+ chat_model: iModel = None,
286
+ invoke_actions: bool = True,
287
+ tool_schemas: list[dict] = None,
288
+ images: list = None,
289
+ image_detail: Literal["low", "high", "auto"] = None,
290
+ parse_model: iModel = None,
291
+ skip_validation: bool = False,
292
+ tools: ToolRef = None,
293
+ operative: Operative = None,
294
+ response_format: type[
295
+ BaseModel
296
+ ] = None, # alias of operative.request_type
297
+ return_operative: bool = False,
298
+ actions: bool = False,
299
+ reason: bool = False,
300
+ action_kwargs: dict = None,
301
+ field_models: list[FieldModel] = None,
302
+ exclude_fields: list | dict | None = None,
303
+ request_params: ModelParams = None,
304
+ request_param_kwargs: dict = None,
305
+ response_params: ModelParams = None,
306
+ response_param_kwargs: dict = None,
307
+ handle_validation: Literal[
308
+ "raise", "return_value", "return_none"
309
+ ] = "return_value",
310
+ operative_model: type[BaseModel] = None,
311
+ request_model: type[BaseModel] = None,
312
+ **kwargs,
313
+ ) -> list | BaseModel | None | dict | str:
314
+ """Orchestrates an 'operate' flow with optional tool invocation.
315
+
316
+ This method creates or updates an Operative, sends an instruction
317
+ to the chat model, optionally parses the response, and invokes
318
+ requested tools if `invoke_actions` is True.
319
+
320
+ Args:
321
+ instruct (Instruct):
322
+ The instruction containing context, guidance, etc.
323
+ sender (SenderRecipient, optional):
324
+ The sender of this operation.
325
+ recipient (SenderRecipient, optional):
326
+ The recipient of this operation.
327
+ progression (Progression, optional):
328
+ Specific progression of messages to use.
329
+ imodel (iModel, optional):
330
+ Deprecated, alias for chat_model.
331
+ chat_model (iModel, optional):
332
+ The chat model to invoke.
333
+ invoke_actions (bool, optional):
334
+ Whether to call requested tools (actions).
335
+ tool_schemas (list[dict], optional):
336
+ Overridden schemas for the tools to be used.
337
+ images (list, optional):
338
+ Additional images for the model context.
339
+ image_detail (Literal["low", "high", "auto"], optional):
340
+ The level of detail for images, if relevant.
341
+ parse_model (iModel, optional):
342
+ The parse model for validating or refining responses.
343
+ skip_validation (bool, optional):
344
+ If True, skip post-response validation steps.
345
+ tools (ToolRef, optional):
346
+ Specific tools to make available if `invoke_actions` is True.
347
+ operative (Operative, optional):
348
+ The operative describing how to handle the response.
349
+ response_format (type[BaseModel], optional):
350
+ An expected response schema (alias of `operative.request_type`).
351
+ fuzzy_match_kwargs (dict, optional):
352
+ Settings for fuzzy validation if used.
353
+ operative_kwargs (dict, optional):
354
+ Additional arguments for creating an Operative if none is given.
355
+ **kwargs: Additional arguments passed to the model invocation.
356
+
357
+ Returns:
358
+ list | BaseModel | None | dict | str:
359
+ The final parsed response, or an Operative object, or the
360
+ string/dict if skipping validation or no tools needed.
361
+ """
362
+ if operative_model:
363
+ logging.warning(
364
+ "operative_model is deprecated. Use response_format instead."
365
+ )
366
+ if (
367
+ (operative_model and response_format)
368
+ or (operative_model and request_model)
369
+ or (response_format and request_model)
370
+ ):
371
+ raise ValueError(
372
+ "Cannot specify both operative_model and response_format"
373
+ "or operative_model and request_model as they are aliases"
374
+ "for the same parameter."
375
+ )
376
+
377
+ response_format = response_format or operative_model or request_model
378
+ chat_model = chat_model or imodel or self.chat_model
379
+ parse_model = parse_model or chat_model
380
+
381
+ if isinstance(instruct, dict):
382
+ instruct = Instruct(**instruct)
383
+
384
+ instruct = instruct or Instruct(
385
+ instruction=instruction,
386
+ guidance=guidance,
387
+ context=context,
388
+ )
389
+
390
+ if reason:
391
+ instruct.reason = True
392
+ if actions:
393
+ instruct.actions = True
394
+
395
+ operative: Operative = Step.request_operative(
396
+ request_params=request_params,
397
+ reason=instruct.reason,
398
+ actions=instruct.actions,
399
+ exclude_fields=exclude_fields,
400
+ base_type=response_format,
401
+ field_models=field_models,
402
+ **(request_param_kwargs or {}),
403
+ )
404
+ if instruct.actions:
405
+ tools = tools or True
406
+ if invoke_actions and tools:
407
+ tool_schemas = self.acts.get_tool_schema(tools)
408
+
409
+ ins, res = await self.invoke_chat(
410
+ instruction=instruct.instruction,
411
+ guidance=instruct.guidance,
412
+ context=instruct.context,
413
+ sender=sender,
414
+ recipient=recipient,
415
+ response_format=operative.request_type,
416
+ progression=progression,
417
+ imodel=chat_model,
418
+ images=images,
419
+ image_detail=image_detail,
420
+ tool_schemas=tool_schemas,
421
+ **kwargs,
422
+ )
423
+ self.msgs.add_message(instruction=ins)
424
+ self.msgs.add_message(assistant_response=res)
425
+
426
+ operative.response_str_dict = res.response
427
+ if skip_validation:
428
+ if return_operative:
429
+ return operative
430
+ return operative.response_str_dict
431
+
432
+ response_model = operative.update_response_model(res.response)
433
+
434
+ if not isinstance(response_model, BaseModel):
435
+ response_model = await self.parse(
436
+ text=res.response,
437
+ request_type=operative.request_type,
438
+ max_retries=operative.max_retries,
439
+ handle_validation="return_value",
440
+ )
441
+ operative.response_model = operative.update_response_model(
442
+ response_model
443
+ )
444
+
445
+ if not isinstance(response_model, BaseModel):
446
+ match handle_validation:
447
+ case "return_value":
448
+ return response_model
449
+ case "return_none":
450
+ return None
451
+ case "raise":
452
+ raise ValueError(
453
+ "Failed to parse response into request format"
454
+ )
455
+
456
+ if not invoke_actions:
457
+ return operative if return_operative else operative.response_model
458
+
459
+ if (
460
+ getattr(response_model, "action_required", None) is True
461
+ and getattr(response_model, "action_requests", None) is not None
462
+ ):
463
+ action_response_models = await self.invoke_action(
464
+ response_model.action_requests,
465
+ **(action_kwargs or {}),
466
+ )
467
+ operative = Step.respond_operative(
468
+ response_params=response_params,
469
+ operative=operative,
470
+ additional_data={"action_responses": action_response_models},
471
+ **(response_param_kwargs or {}),
472
+ )
473
+ return operative if return_operative else operative.response_model
474
+
475
+ async def parse(
476
+ self,
477
+ text: str,
478
+ handle_validation: Literal[
479
+ "raise", "return_value", "return_none"
480
+ ] = "return_value",
481
+ max_retries: int = 3,
482
+ request_type: type[BaseModel] = None,
483
+ operative: Operative = None,
484
+ similarity_algo="jaro_winkler",
485
+ similarity_threshold: float = 0.85,
486
+ fuzzy_match: bool = True,
487
+ handle_unmatched: Literal[
488
+ "ignore", "raise", "remove", "fill", "force"
489
+ ] = "force",
490
+ fill_value: Any = None,
491
+ fill_mapping: dict[str, Any] | None = None,
492
+ strict: bool = False,
493
+ suppress_conversion_errors: bool = False,
494
+ ):
495
+ """Attempts to parse text into a structured Pydantic model.
496
+
497
+ Uses optional fuzzy matching to handle partial or unclear fields.
498
+
499
+ Args:
500
+ text (str): The raw text to parse.
501
+ handle_validation (Literal["raise","return_value","return_none"]):
502
+ What to do if parsing fails. Defaults to "return_value".
503
+ max_retries (int):
504
+ How many times to retry parsing if it fails.
505
+ request_type (type[BaseModel], optional):
506
+ The Pydantic model to parse into.
507
+ operative (Operative, optional):
508
+ If provided, uses its model and max_retries setting.
509
+ similarity_algo (str):
510
+ The similarity algorithm for fuzzy field matching.
511
+ similarity_threshold (float):
512
+ A threshold for fuzzy matching (0.0 - 1.0).
513
+ fuzzy_match (bool):
514
+ If True, tries to match unrecognized keys to known ones.
515
+ handle_unmatched (Literal["ignore","raise","remove","fill","force"]):
516
+ How to handle unmatched fields.
517
+ fill_value (Any):
518
+ A default value used when fill is needed.
519
+ fill_mapping (dict[str, Any] | None):
520
+ A mapping from field -> fill value override.
521
+ strict (bool):
522
+ If True, raises errors on ambiguous fields or data types.
523
+ suppress_conversion_errors (bool):
524
+ If True, logs or ignores errors during data conversion.
525
+
526
+ Returns:
527
+ BaseModel | Any | None:
528
+ The parsed model instance, or a dict/string/None depending
529
+ on the handling mode.
530
+ """
531
+ _should_try = True
532
+ num_try = 0
533
+ response_model = text
534
+ if operative is not None:
535
+ max_retries = operative.max_retries
536
+ request_type = operative.request_type
537
+
538
+ while (
539
+ _should_try
540
+ and num_try < max_retries
541
+ and not isinstance(response_model, BaseModel)
542
+ ):
543
+ num_try += 1
544
+ _, res = await self.invoke_chat(
545
+ instruction="reformat text into specified model",
546
+ guidane="follow the required response format, using the model schema as a guide",
547
+ context=[{"text_to_format": text}],
548
+ response_format=request_type,
549
+ sender=self.user,
550
+ recipient=self.id,
551
+ imodel=self.parse_model,
552
+ )
553
+ if operative is not None:
554
+ response_model = operative.update_response_model(res.response)
555
+ else:
556
+ response_model = fuzzy_validate_mapping(
557
+ res.response,
558
+ breakdown_pydantic_annotation(request_type),
559
+ similarity_algo=similarity_algo,
560
+ similarity_threshold=similarity_threshold,
561
+ fuzzy_match=fuzzy_match,
562
+ handle_unmatched=handle_unmatched,
563
+ fill_value=fill_value,
564
+ fill_mapping=fill_mapping,
565
+ strict=strict,
566
+ suppress_conversion_errors=suppress_conversion_errors,
567
+ )
568
+ response_model = request_type.model_validate(response_model)
569
+
570
+ if not isinstance(response_model, BaseModel):
571
+ match handle_validation:
572
+ case "return_value":
573
+ return response_model
574
+ case "return_none":
575
+ return None
576
+ case "raise":
577
+ raise ValueError(
578
+ "Failed to parse response into request format"
579
+ )
580
+
581
+ return response_model
582
+
583
+ async def communicate(
584
+ self,
585
+ instruction: Instruction | JsonValue = None,
586
+ guidance: JsonValue = None,
587
+ context: JsonValue = None,
588
+ sender: SenderRecipient = None,
589
+ recipient: SenderRecipient = None,
590
+ progression: ID.IDSeq = None,
591
+ request_model: type[BaseModel] | BaseModel | None = None,
592
+ response_format: type[BaseModel] = None,
593
+ request_fields: dict | list[str] = None,
594
+ imodel: iModel = None, # alias of chat_model
595
+ chat_model: iModel = None,
596
+ parse_model: iModel = None,
597
+ skip_validation: bool = False,
598
+ images: list = None,
599
+ image_detail: Literal["low", "high", "auto"] = None,
600
+ num_parse_retries: int = 3,
601
+ fuzzy_match_kwargs: dict = None,
602
+ clear_messages: bool = False,
603
+ operative_model: type[BaseModel] = None,
604
+ **kwargs,
605
+ ):
606
+ """Handles a general 'communicate' flow without tool invocation.
607
+
608
+ Sends messages to the model, optionally parses the response, and
609
+ can handle simpler field-level validation.
610
+
611
+ Args:
612
+ instruction (Instruction | JsonValue, optional):
613
+ The main user query or context.
614
+ guidance (JsonValue, optional):
615
+ Additional LLM instructions.
616
+ context (JsonValue, optional):
617
+ Context data to pass to the LLM.
618
+ sender (SenderRecipient, optional):
619
+ The sender of this message.
620
+ recipient (SenderRecipient, optional):
621
+ The recipient of this message.
622
+ progression (ID.IDSeq, optional):
623
+ A custom progression of conversation messages.
624
+ request_model (type[BaseModel] | BaseModel | None, optional):
625
+ Model for structured responses.
626
+ response_format (type[BaseModel], optional):
627
+ Alias for request_model if both are not given simultaneously.
628
+ request_fields (dict | list[str], optional):
629
+ Simpler field-level mapping if no model is used.
630
+ imodel (iModel, optional):
631
+ Deprecated, alias for chat_model.
632
+ chat_model (iModel, optional):
633
+ Model used for the conversation.
634
+ parse_model (iModel, optional):
635
+ Model used for any parsing operation.
636
+ skip_validation (bool, optional):
637
+ If True, returns the raw response without further checks.
638
+ images (list, optional):
639
+ Additional images if relevant to the LLM context.
640
+ image_detail (Literal["low","high","auto"], optional):
641
+ Level of image detail if used.
642
+ num_parse_retries (int, optional):
643
+ Max times to retry parsing on failure (capped at 5).
644
+ fuzzy_match_kwargs (dict, optional):
645
+ Settings passed to the fuzzy validation function.
646
+ clear_messages (bool, optional):
647
+ If True, clears previously stored messages.
648
+ **kwargs:
649
+ Additional arguments for the LLM call.
650
+
651
+ Returns:
652
+ Any:
653
+ The raw string, a validated Pydantic model, or a dict
654
+ of requested fields, depending on the parameters.
655
+ """
656
+ if operative_model:
657
+ logging.warning(
658
+ "operative_model is deprecated. Use response_format instead."
659
+ )
660
+ if (
661
+ (operative_model and response_format)
662
+ or (operative_model and request_model)
663
+ or (response_format and request_model)
664
+ ):
665
+ raise ValueError(
666
+ "Cannot specify both operative_model and response_format"
667
+ "or operative_model and request_model as they are aliases"
668
+ "for the same parameter."
669
+ )
670
+
671
+ response_format = response_format or operative_model or request_model
672
+
673
+ imodel = imodel or chat_model or self.chat_model
674
+ parse_model = parse_model or self.parse_model
675
+
676
+ if clear_messages:
677
+ self.msgs.clear_messages()
678
+
679
+ if num_parse_retries > 5:
680
+ logging.warning(
681
+ f"Are you sure you want to retry {num_parse_retries} "
682
+ "times? lowering retry attempts to 5. Suggestion is under 3"
683
+ )
684
+ num_parse_retries = 5
685
+
686
+ ins, res = await self.invoke_chat(
687
+ instruction=instruction,
688
+ guidance=guidance,
689
+ context=context,
690
+ sender=sender,
691
+ recipient=recipient,
692
+ response_format=response_format,
693
+ progression=progression,
694
+ imodel=imodel,
695
+ images=images,
696
+ image_detail=image_detail,
697
+ **kwargs,
698
+ )
699
+ self.msgs.add_message(instruction=ins)
700
+ self.msgs.add_message(assistant_response=res)
701
+
702
+ if skip_validation:
703
+ return res.response
704
+
705
+ if response_format is not None:
706
+ return await self.parse(
707
+ text=res.response,
708
+ request_type=response_format,
709
+ max_retries=num_parse_retries,
710
+ **(fuzzy_match_kwargs or {}),
711
+ )
712
+
713
+ if request_fields is not None:
714
+ _d = fuzzy_validate_mapping(
715
+ res.response,
716
+ request_fields,
717
+ handle_unmatched="force",
718
+ fill_value=UNDEFINED,
719
+ )
720
+ return {k: v for k, v in _d.items() if v != UNDEFINED}
721
+
722
+ return res.response
723
+
724
+ async def invoke_action(
725
+ self,
726
+ action_request: list | ActionRequest | BaseModel | dict,
727
+ /,
728
+ suppress_errors: bool = False,
729
+ sanitize_input: bool = False,
730
+ unique_input: bool = False,
731
+ num_retries: int = 0,
732
+ initial_delay: float = 0,
733
+ retry_delay: float = 0,
734
+ backoff_factor: float = 1,
735
+ retry_default: Any = UNDEFINED,
736
+ retry_timeout: float | None = None,
737
+ retry_timing: bool = False,
738
+ max_concurrent: int | None = None,
739
+ throttle_period: float | None = None,
740
+ flatten: bool = True,
741
+ dropna: bool = True,
742
+ unique_output: bool = False,
743
+ flatten_tuple_set: bool = False,
744
+ ):
745
+ params = locals()
746
+ params.pop("self")
747
+ params.pop("action_request")
748
+ return await alcall(
749
+ action_request,
750
+ self._invoke_action,
751
+ **params,
752
+ )
753
+
754
+ async def _invoke_action(
755
+ self,
756
+ action_request: ActionRequest | BaseModel | dict,
757
+ suppress_errors: bool = False,
758
+ ) -> ActionResponse:
759
+ """Invokes a tool (action) asynchronously.
760
+
761
+ Args:
762
+ action_request (ActionRequest | BaseModel | dict):
763
+ Contains the function name (`function`) and arguments.
764
+ suppress_errors (bool, optional):
765
+ If True, logs errors instead of raising.
766
+
767
+ Returns:
768
+ ActionResponse: The result of the tool call.
769
+ """
770
+ try:
771
+ func, args = None, None
772
+ if isinstance(action_request, BaseModel):
773
+ if hasattr(action_request, "function") and hasattr(
774
+ action_request, "arguments"
775
+ ):
776
+ func = action_request.function
777
+ args = action_request.arguments
778
+ elif isinstance(action_request, dict):
779
+ if action_request.keys() >= {"function", "arguments"}:
780
+ func = action_request["function"]
781
+ args = action_request["arguments"]
782
+
783
+ func_call: FunctionCalling = await self._action_manager.invoke(
784
+ action_request
785
+ )
786
+ if isinstance(func_call, FunctionCalling):
787
+ self._log_manager.log(Log.create(func_call))
788
+
789
+ if not isinstance(action_request, ActionRequest):
790
+ action_request = ActionRequest.create(
791
+ function=func,
792
+ arguments=args,
793
+ sender=self.id,
794
+ recipient=func_call.func_tool.id,
795
+ )
796
+
797
+ if action_request not in self.messages:
798
+ self.msgs.add_message(action_request=action_request)
799
+
800
+ self.msgs.add_message(
801
+ action_request=action_request,
802
+ action_output=func_call.response,
803
+ )
804
+
805
+ return ActionResponseModel(
806
+ function=action_request.function,
807
+ arguments=action_request.arguments,
808
+ output=func_call.response,
809
+ )
810
+ if isinstance(func_call, Log):
811
+ self._log_manager.log(func_call)
812
+ return None
813
+
814
+ except Exception as e:
815
+ if suppress_errors:
816
+ logging.error(f"Error invoking action: {e}")
817
+ else:
818
+ raise e
819
+
820
+ async def invoke_chat(
821
+ self,
822
+ instruction=None,
823
+ guidance=None,
824
+ context=None,
825
+ sender=None,
826
+ recipient=None,
827
+ request_fields=None,
828
+ response_format: type[BaseModel] = None,
829
+ progression=None,
830
+ imodel: iModel = None,
831
+ tool_schemas=None,
832
+ images: list = None,
833
+ image_detail: Literal["low", "high", "auto"] = None,
834
+ **kwargs,
835
+ ) -> tuple[Instruction, AssistantResponse]:
836
+ """Invokes the chat model with the current conversation history.
837
+
838
+ This method constructs a sequence of messages from the stored
839
+ progression, merges any pending action responses into the context,
840
+ and calls the model. The result is then wrapped in an
841
+ AssistantResponse.
842
+
843
+ Args:
844
+ instruction (Any):
845
+ The main user instruction text or structured data.
846
+ guidance (Any):
847
+ Additional system or user guidance.
848
+ context (Any):
849
+ Context data to pass to the model.
850
+ sender (Any):
851
+ The user or entity sending this message.
852
+ recipient (Any):
853
+ The intended recipient of this message (default is self.id).
854
+ request_fields (Any):
855
+ A set of fields for partial validation (rarely used).
856
+ request_model (type[BaseModel], optional):
857
+ A specific Pydantic model to request from the LLM.
858
+ progression (Any):
859
+ The conversation flow or message ordering.
860
+ imodel (iModel, optional):
861
+ The chat model to use.
862
+ tool_schemas (Any, optional):
863
+ Additional schemas to pass if tools are invoked.
864
+ images (list, optional):
865
+ Optional list of images.
866
+ image_detail (Literal["low","high","auto"], optional):
867
+ The level of detail for images, if relevant.
868
+ **kwargs:
869
+ Additional model invocation parameters.
870
+
871
+ Returns:
872
+ tuple[Instruction, AssistantResponse]:
873
+ The instruction object (with context) and the final
874
+ AssistantResponse from the model call.
875
+ """
876
+ ins: Instruction = self.msgs.create_instruction(
877
+ instruction=instruction,
878
+ guidance=guidance,
879
+ context=context,
880
+ sender=sender or self.user or "user",
881
+ recipient=recipient or self.id,
882
+ response_format=response_format,
883
+ request_fields=request_fields,
884
+ images=images,
885
+ image_detail=image_detail,
886
+ tool_schemas=tool_schemas,
887
+ )
888
+
889
+ progression = progression or self.msgs.progression
890
+ messages: list[RoledMessage] = [
891
+ self.msgs.messages[i] for i in progression
892
+ ]
893
+
894
+ use_ins = None
895
+ _to_use = []
896
+ _action_responses: set[ActionResponse] = set()
897
+
898
+ for i in messages:
899
+ if isinstance(i, ActionResponse):
900
+ _action_responses.add(i)
901
+ if isinstance(i, AssistantResponse):
902
+ j = AssistantResponse(
903
+ role=i.role,
904
+ content=copy(i.content),
905
+ sender=i.sender,
906
+ recipient=i.recipient,
907
+ template=i.template,
908
+ )
909
+ _to_use.append(j)
910
+ if isinstance(i, Instruction):
911
+ j = Instruction(
912
+ role=i.role,
913
+ content=copy(i.content),
914
+ sender=i.sender,
915
+ recipient=i.recipient,
916
+ template=i.template,
917
+ )
918
+ j.tool_schemas = None
919
+ j.respond_schema_info = None
920
+ j.request_response_format = None
921
+
922
+ if _action_responses:
923
+ d_ = [k.content for k in _action_responses]
924
+ for z in d_:
925
+ if z not in j.context:
926
+ j.context.append(z)
927
+
928
+ _to_use.append(j)
929
+ _action_responses = set()
930
+ else:
931
+ _to_use.append(j)
932
+
933
+ messages = _to_use
934
+ if _action_responses:
935
+ j = ins.model_copy()
936
+ d_ = [k.content for k in _action_responses]
937
+ for z in d_:
938
+ if z not in j.context:
939
+ j.context.append(z)
940
+ use_ins = j
941
+
942
+ if messages and len(messages) > 1:
943
+ _msgs = [messages[0]]
944
+
945
+ for i in messages[1:]:
946
+ if isinstance(i, AssistantResponse):
947
+ if isinstance(_msgs[-1], AssistantResponse):
948
+ _msgs[-1].response = (
949
+ f"{_msgs[-1].response}\n\n{i.response}"
950
+ )
951
+ else:
952
+ _msgs.append(i)
953
+ else:
954
+ if isinstance(_msgs[-1], AssistantResponse):
955
+ _msgs.append(i)
956
+ messages = _msgs
957
+
958
+ if self.msgs.system and imodel.sequential_exchange:
959
+ messages = [msg for msg in messages if msg.role != "system"]
960
+ first_instruction = None
961
+
962
+ if len(messages) == 0:
963
+ first_instruction = ins.model_copy()
964
+ first_instruction.guidance = self.msgs.system.rendered + (
965
+ first_instruction.guidance or ""
966
+ )
967
+ messages.append(first_instruction)
968
+ elif len(messages) >= 1:
969
+ first_instruction = messages[0]
970
+ if not isinstance(first_instruction, Instruction):
971
+ raise ValueError(
972
+ "First message in progression must be an Instruction or System"
973
+ )
974
+ first_instruction = first_instruction.model_copy()
975
+ first_instruction.guidance = self.msgs.system.rendered + (
976
+ first_instruction.guidance or ""
977
+ )
978
+ messages[0] = first_instruction
979
+ messages.append(use_ins or ins)
980
+
981
+ else:
982
+ messages.append(use_ins or ins)
983
+
984
+ kwargs["messages"] = [i.chat_msg for i in messages]
985
+ imodel = imodel or self.chat_model
986
+
987
+ api_call = await imodel.invoke(**kwargs)
988
+ self._log_manager.log(Log.create(api_call))
989
+
990
+ res = AssistantResponse.create(
991
+ assistant_response=api_call.response,
992
+ sender=self.id,
993
+ recipient=self.user,
994
+ )
995
+
996
+ return ins, res
997
+
998
+ def clone(self, sender: ID.Ref = None) -> "Branch":
999
+ """Clones this Branch, creating a new instance with the same data.
1000
+
1001
+ Args:
1002
+ sender (ID.Ref, optional):
1003
+ If provided, sets this sender ID on the cloned messages.
1004
+ Otherwise, uses self.id.
1005
+
1006
+ Returns:
1007
+ Branch: A new branch with cloned messages and the same tools.
1008
+ """
1009
+ if sender is not None:
1010
+ if not ID.is_id(sender):
1011
+ raise ValueError(
1012
+ "Input value for branch.clone sender is not a valid sender"
1013
+ )
1014
+ sender = ID.get_id(sender)
1015
+
1016
+ system = self.msgs.system.clone() if self.msgs.system else None
1017
+ tools = (
1018
+ list(self._action_manager.registry.values())
1019
+ if self._action_manager.registry
1020
+ else None
1021
+ )
1022
+ branch_clone = Branch(
1023
+ system=system,
1024
+ user=self.user,
1025
+ messages=[i.clone() for i in self.msgs.messages],
1026
+ tools=tools,
1027
+ metadata={"clone_from": self},
1028
+ )
1029
+ for message in branch_clone.msgs.messages:
1030
+ message.sender = sender or self.id
1031
+ message.recipient = branch_clone.id
1032
+ return branch_clone
1033
+
1034
+ def to_df(self, *, progression: Progression = None) -> pd.DataFrame:
1035
+ """Converts messages in the branch to a Pandas DataFrame.
1036
+
1037
+ Args:
1038
+ progression (Progression, optional):
1039
+ A custom progression of messages to include. If None, uses
1040
+ the existing stored progression.
1041
+
1042
+ Returns:
1043
+ pd.DataFrame:
1044
+ A DataFrame containing message data for easy inspection
1045
+ or serialization.
1046
+ """
1047
+ if progression is None:
1048
+ progression = self.msgs.progression
1049
+
1050
+ msgs = [
1051
+ self.msgs.messages[i]
1052
+ for i in progression
1053
+ if i in self.msgs.messages
1054
+ ]
1055
+ p = Pile(collections=msgs)
1056
+ return p.to_df(columns=MESSAGE_FIELDS)
1057
+
1058
+ async def _instruct(self, instruct: Instruct, /, **kwargs) -> Any:
1059
+ """Convenience method for handling an 'Instruct'.
1060
+
1061
+ Checks if the instruct uses reserved kwargs for an 'operate' flow
1062
+ (e.g., actions and a response format). Otherwise, falls back to a
1063
+ simpler 'communicate' flow.
1064
+
1065
+ Args:
1066
+ instruct (Instruct):
1067
+ The instruction context and guidance.
1068
+ **kwargs:
1069
+ Additional arguments for operate or communicate.
1070
+
1071
+ Returns:
1072
+ Any: The result of the chosen flow, e.g., a validated response.
1073
+ """
1074
+ config = {**instruct.to_dict(), **kwargs}
1075
+ if any(i in config and config[i] for i in Instruct.reserved_kwargs):
1076
+ if "response_format" in config or "request_model" in config:
1077
+ return await self.operate(**config)
1078
+ for i in Instruct.reserved_kwargs:
1079
+ config.pop(i, None)
1080
+
1081
+ return await self.communicate(**config)
1082
+
1083
+ def send(
1084
+ self,
1085
+ recipient: IDType,
1086
+ category: PackageCategory | None,
1087
+ item: Any,
1088
+ request_source: IDType | None = None,
1089
+ ) -> None:
1090
+ """Sends a package (wrapped in Mail) to a specific recipient.
1091
+
1092
+ Args:
1093
+ recipient (IDType):
1094
+ The ID of the recipient entity.
1095
+ category (PackageCategory | None):
1096
+ The category of the package (e.g., 'message', 'tool', etc.).
1097
+ package (Any):
1098
+ The payload to send (could be a message, tool, model, etc.).
1099
+ request_source (IDType | None):
1100
+ The ID that requested this send (if any).
1101
+ """
1102
+ package = Package(
1103
+ category=category,
1104
+ item=item,
1105
+ request_source=request_source,
1106
+ )
1107
+
1108
+ mail = Mail(
1109
+ sender=self.id,
1110
+ recipient=recipient,
1111
+ package=package,
1112
+ )
1113
+ self.mailbox.append_out(mail)
1114
+
1115
+ def receive(
1116
+ self,
1117
+ sender: IDType,
1118
+ message: bool = False,
1119
+ tool: bool = False,
1120
+ imodel: bool = False,
1121
+ ) -> None:
1122
+ """Retrieves mail from a sender, processing it if enabled by parameters.
1123
+
1124
+ Args:
1125
+ sender (IDType):
1126
+ The ID of the sender.
1127
+ message (bool):
1128
+ If True, processes mails categorized as "message".
1129
+ tool (bool):
1130
+ If True, processes mails categorized as "tool".
1131
+ imodel (bool):
1132
+ If True, processes mails categorized as "imodel".
1133
+
1134
+ Raises:
1135
+ ValueError:
1136
+ If the sender doesn't exist or if the mail category is invalid
1137
+ for the chosen processing options.
1138
+ """
1139
+ skipped_requests = Progression()
1140
+ sender = ID.get_id(sender)
1141
+ if sender not in self.mailbox.pending_ins.keys():
1142
+ raise ValueError(f"No package from {sender}")
1143
+ while self.mailbox.pending_ins[sender]:
1144
+ mail_id = self.mailbox.pending_ins[sender].popleft()
1145
+ mail: Mail = self.mailbox.pile_[mail_id]
1146
+
1147
+ if mail.category == "message" and message:
1148
+ if not isinstance(mail.package.item, RoledMessage):
1149
+ raise ValueError("Invalid message format")
1150
+ new_message = mail.package.item.clone()
1151
+ new_message.sender = mail.sender
1152
+ new_message.recipient = self.id
1153
+ self.msgs.messages.include(new_message)
1154
+ self.mailbox.pile_.pop(mail_id)
1155
+
1156
+ elif mail.category == "tool" and tool:
1157
+ if not isinstance(mail.package.item, Tool):
1158
+ raise ValueError("Invalid tools format")
1159
+ self._action_manager.register_tools(mail.package.item)
1160
+ self.mailbox.pile_.pop(mail_id)
1161
+
1162
+ elif mail.category == "imodel" and imodel:
1163
+ if not isinstance(mail.package.item, iModel):
1164
+ raise ValueError("Invalid iModel format")
1165
+ self._imodel_manager.register_imodel(
1166
+ imodel.name or "chat", mail.package.item
1167
+ )
1168
+ self.mailbox.pile_.pop(mail_id)
1169
+
1170
+ else:
1171
+ skipped_requests.append(mail)
1172
+
1173
+ self.mailbox.pending_ins[sender] = skipped_requests
1174
+
1175
+ if len(self.mailbox.pending_ins[sender]) == 0:
1176
+ self.mailbox.pending_ins.pop(sender)
1177
+
1178
+ async def asend(
1179
+ self,
1180
+ recipient: IDType,
1181
+ category: PackageCategory | None,
1182
+ package: Any,
1183
+ request_source: IDType | None = None,
1184
+ ):
1185
+ """Asynchronous version of send().
1186
+
1187
+ Args:
1188
+ recipient (IDType): The ID of the recipient.
1189
+ category (PackageCategory | None): The category of the package.
1190
+ package (Any): The item(s) to send.
1191
+ request_source (IDType | None): The origin of this request.
1192
+ """
1193
+ async with self.mailbox.pile_:
1194
+ self.send(recipient, category, package, request_source)
1195
+
1196
+ async def areceive(
1197
+ self,
1198
+ sender: IDType,
1199
+ message: bool = False,
1200
+ tool: bool = False,
1201
+ imodel: bool = False,
1202
+ ) -> None:
1203
+ """Asynchronous version of receive().
1204
+
1205
+ Args:
1206
+ sender (IDType): The sender's ID.
1207
+ message (bool): Whether to process message packages.
1208
+ tool (bool): Whether to process tool packages.
1209
+ imodel (bool): Whether to process iModel packages.
1210
+ """
1211
+ async with self.mailbox.pile_:
1212
+ self.receive(sender, message, tool, imodel)
1213
+
1214
+ def receive_all(self) -> None:
1215
+ """Receives mail from all senders."""
1216
+ for key in list(self.mailbox.pending_ins.keys()):
1217
+ self.receive(key)
1218
+
1219
+ def to_dict(self):
1220
+ meta = {}
1221
+ if "clone_from" in self.metadata:
1222
+
1223
+ meta["clone_from"] = {
1224
+ "id": str(self.metadata["clone_from"].id),
1225
+ "user": str(self.metadata["clone_from"].user),
1226
+ "created_at": self.metadata["clone_from"].created_at,
1227
+ "progression": [
1228
+ str(i)
1229
+ for i in self.metadata["clone_from"].msgs.progression
1230
+ ],
1231
+ }
1232
+ meta.update(
1233
+ copy({k: v for k, v in self.metadata.items() if k != "clone_from"})
1234
+ )
1235
+
1236
+ dict_ = super().to_dict()
1237
+ dict_["messages"] = self.messages.to_dict()
1238
+ dict_["logs"] = self.logs.to_dict()
1239
+ dict_["chat_model"] = self.chat_model.to_dict()
1240
+ dict_["parse_model"] = self.parse_model.to_dict()
1241
+ if self.system:
1242
+ dict_["system"] = self.system.to_dict()
1243
+ dict_["log_config"] = self._log_manager._config.model_dump()
1244
+ dict_["metadata"] = meta
1245
+
1246
+ return dict_
1247
+
1248
+ @classmethod
1249
+ def from_dict(cls, data: dict):
1250
+ dict_ = {
1251
+ "messages": data.pop("messages", UNDEFINED),
1252
+ "logs": data.pop("logs", UNDEFINED),
1253
+ "chat_model": data.pop("chat_model", UNDEFINED),
1254
+ "parse_model": data.pop("parse_model", UNDEFINED),
1255
+ "system": data.pop("system", UNDEFINED),
1256
+ "log_config": data.pop("log_config", UNDEFINED),
1257
+ }
1258
+ params = {}
1259
+ for k, v in data.items():
1260
+ if isinstance(v, dict) and "id" in v:
1261
+ params.update(v)
1262
+ else:
1263
+ params[k] = v
1264
+
1265
+ params.update(dict_)
1266
+ return cls(**{k: v for k, v in params.items() if v is not UNDEFINED})
1267
+
1268
+ def receive_all(self) -> None:
1269
+ """Receives mail from all senders."""
1270
+ for key in self.mailbox.pending_ins:
1271
+ self.receive(key)
1272
+
1273
+ def flagged_messages(
1274
+ self,
1275
+ include_clone: bool = False,
1276
+ include_load: bool = False,
1277
+ ) -> None:
1278
+ flags = []
1279
+ if include_clone:
1280
+ flags.append(MessageFlag.MESSAGE_CLONE)
1281
+ if include_load:
1282
+ flags.append(MessageFlag.MESSAGE_LOAD)
1283
+ out = [i for i in self.messages if i._flag in flags]
1284
+ return Pile(collections=out, item_type=RoledMessage, strict_type=False)
1285
+
1286
+
1287
+ # File: lionagi/session/branch.py