lionagi 0.17.11__py3-none-any.whl → 0.18.1__py3-none-any.whl

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 (109) hide show
  1. lionagi/_errors.py +0 -5
  2. lionagi/fields.py +83 -0
  3. lionagi/libs/schema/minimal_yaml.py +98 -0
  4. lionagi/ln/__init__.py +3 -1
  5. lionagi/ln/concurrency/primitives.py +4 -4
  6. lionagi/ln/concurrency/task.py +1 -0
  7. lionagi/ln/types.py +32 -5
  8. lionagi/models/field_model.py +21 -4
  9. lionagi/models/hashable_model.py +2 -3
  10. lionagi/operations/ReAct/ReAct.py +475 -238
  11. lionagi/operations/ReAct/utils.py +3 -0
  12. lionagi/operations/act/act.py +206 -0
  13. lionagi/operations/builder.py +5 -7
  14. lionagi/operations/chat/chat.py +130 -114
  15. lionagi/operations/communicate/communicate.py +101 -42
  16. lionagi/operations/fields.py +380 -0
  17. lionagi/operations/flow.py +8 -10
  18. lionagi/operations/interpret/interpret.py +65 -20
  19. lionagi/operations/node.py +4 -4
  20. lionagi/operations/operate/operate.py +216 -108
  21. lionagi/{protocols/operatives → operations/operate}/operative.py +4 -5
  22. lionagi/{protocols/operatives → operations/operate}/step.py +34 -39
  23. lionagi/operations/parse/parse.py +170 -142
  24. lionagi/operations/select/select.py +79 -18
  25. lionagi/operations/select/utils.py +8 -2
  26. lionagi/operations/types.py +119 -23
  27. lionagi/protocols/action/manager.py +5 -6
  28. lionagi/protocols/contracts.py +2 -2
  29. lionagi/protocols/generic/__init__.py +22 -0
  30. lionagi/protocols/generic/element.py +36 -127
  31. lionagi/protocols/generic/log.py +3 -2
  32. lionagi/protocols/generic/pile.py +9 -10
  33. lionagi/protocols/generic/progression.py +23 -22
  34. lionagi/protocols/graph/edge.py +6 -5
  35. lionagi/protocols/ids.py +6 -49
  36. lionagi/protocols/messages/__init__.py +29 -0
  37. lionagi/protocols/messages/action_request.py +86 -184
  38. lionagi/protocols/messages/action_response.py +73 -131
  39. lionagi/protocols/messages/assistant_response.py +130 -159
  40. lionagi/protocols/messages/base.py +31 -22
  41. lionagi/protocols/messages/instruction.py +280 -625
  42. lionagi/protocols/messages/manager.py +112 -62
  43. lionagi/protocols/messages/message.py +87 -197
  44. lionagi/protocols/messages/system.py +52 -123
  45. lionagi/protocols/types.py +1 -13
  46. lionagi/service/connections/__init__.py +3 -0
  47. lionagi/service/connections/endpoint.py +0 -8
  48. lionagi/service/connections/providers/claude_code_cli.py +3 -2
  49. lionagi/service/connections/providers/oai_.py +29 -94
  50. lionagi/service/connections/providers/ollama_.py +3 -2
  51. lionagi/service/hooks/_types.py +1 -1
  52. lionagi/service/hooks/_utils.py +1 -1
  53. lionagi/service/hooks/hook_event.py +3 -8
  54. lionagi/service/hooks/hook_registry.py +5 -5
  55. lionagi/service/hooks/hooked_event.py +63 -3
  56. lionagi/service/imodel.py +24 -20
  57. lionagi/service/third_party/claude_code.py +3 -3
  58. lionagi/service/third_party/openai_models.py +435 -0
  59. lionagi/service/token_calculator.py +1 -94
  60. lionagi/session/branch.py +190 -400
  61. lionagi/session/session.py +8 -99
  62. lionagi/tools/file/reader.py +2 -2
  63. lionagi/version.py +1 -1
  64. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/METADATA +6 -6
  65. lionagi-0.18.1.dist-info/RECORD +164 -0
  66. lionagi/fields/__init__.py +0 -47
  67. lionagi/fields/action.py +0 -188
  68. lionagi/fields/base.py +0 -153
  69. lionagi/fields/code.py +0 -239
  70. lionagi/fields/file.py +0 -234
  71. lionagi/fields/instruct.py +0 -135
  72. lionagi/fields/reason.py +0 -55
  73. lionagi/fields/research.py +0 -52
  74. lionagi/operations/_act/act.py +0 -86
  75. lionagi/operations/brainstorm/__init__.py +0 -2
  76. lionagi/operations/brainstorm/brainstorm.py +0 -498
  77. lionagi/operations/brainstorm/prompt.py +0 -11
  78. lionagi/operations/instruct/__init__.py +0 -2
  79. lionagi/operations/instruct/instruct.py +0 -28
  80. lionagi/operations/plan/__init__.py +0 -6
  81. lionagi/operations/plan/plan.py +0 -386
  82. lionagi/operations/plan/prompt.py +0 -25
  83. lionagi/operations/utils.py +0 -45
  84. lionagi/protocols/forms/__init__.py +0 -2
  85. lionagi/protocols/forms/base.py +0 -85
  86. lionagi/protocols/forms/flow.py +0 -79
  87. lionagi/protocols/forms/form.py +0 -86
  88. lionagi/protocols/forms/report.py +0 -48
  89. lionagi/protocols/mail/__init__.py +0 -2
  90. lionagi/protocols/mail/exchange.py +0 -220
  91. lionagi/protocols/mail/mail.py +0 -51
  92. lionagi/protocols/mail/mailbox.py +0 -103
  93. lionagi/protocols/mail/manager.py +0 -218
  94. lionagi/protocols/mail/package.py +0 -101
  95. lionagi/protocols/messages/templates/README.md +0 -28
  96. lionagi/protocols/messages/templates/action_request.jinja2 +0 -5
  97. lionagi/protocols/messages/templates/action_response.jinja2 +0 -9
  98. lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -6
  99. lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -61
  100. lionagi/protocols/messages/templates/system_message.jinja2 +0 -11
  101. lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -7
  102. lionagi/protocols/operatives/__init__.py +0 -2
  103. lionagi/service/connections/providers/types.py +0 -28
  104. lionagi/service/third_party/openai_model_names.py +0 -198
  105. lionagi/service/types.py +0 -58
  106. lionagi-0.17.11.dist-info/RECORD +0 -199
  107. /lionagi/operations/{_act → act}/__init__.py +0 -0
  108. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/WHEEL +0 -0
  109. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/licenses/LICENSE +0 -0
lionagi/session/branch.py CHANGED
@@ -2,52 +2,48 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  from collections.abc import AsyncGenerator, Callable
5
- from typing import TYPE_CHECKING, Any, Literal, Optional
5
+ from typing import TYPE_CHECKING, Any, Literal
6
6
 
7
7
  from pydantic import BaseModel, Field, JsonValue, PrivateAttr, field_serializer
8
8
 
9
9
  from lionagi.config import settings
10
+ from lionagi.ln import AlcallParams
10
11
  from lionagi.ln.types import Unset
11
12
  from lionagi.models.field_model import FieldModel
12
- from lionagi.operations.flow import AlcallParams
13
+ from lionagi.operations.fields import Instruct
13
14
  from lionagi.operations.manager import OperationManager
15
+ from lionagi.protocols._concepts import Relational
14
16
  from lionagi.protocols.action.manager import ActionManager
15
17
  from lionagi.protocols.action.tool import FuncTool, Tool, ToolRef
16
- from lionagi.protocols.types import (
18
+ from lionagi.protocols.generic import (
17
19
  ID,
18
- MESSAGE_FIELDS,
20
+ DataLogger,
21
+ DataLoggerConfig,
22
+ Element,
23
+ Log,
24
+ Pile,
25
+ Progression,
26
+ )
27
+ from lionagi.protocols.messages import (
19
28
  ActionRequest,
20
29
  ActionResponse,
21
30
  AssistantResponse,
22
- Communicatable,
23
- Element,
24
- IDType,
25
31
  Instruction,
26
- Log,
27
- LogManager,
28
- LogManagerConfig,
29
- Mail,
30
- Mailbox,
31
32
  MessageManager,
32
33
  MessageRole,
33
- PackageCategory,
34
- Pile,
35
- Progression,
36
- Relational,
37
34
  RoledMessage,
38
35
  SenderRecipient,
39
36
  System,
40
37
  )
41
38
  from lionagi.service.connections.endpoint import Endpoint
42
- from lionagi.service.types import iModel, iModelManager
39
+ from lionagi.service.manager import iModel, iModelManager
43
40
  from lionagi.tools.base import LionTool
44
41
  from lionagi.utils import copy
45
42
 
46
43
  from .prompts import LION_SYSTEM_MESSAGE
47
44
 
48
45
  if TYPE_CHECKING:
49
- from lionagi.fields import Instruct
50
- from lionagi.protocols.operatives.operative import Operative
46
+ from lionagi.operations.operate.operative import Operative
51
47
 
52
48
 
53
49
  __all__ = ("Branch",)
@@ -56,7 +52,7 @@ __all__ = ("Branch",)
56
52
  _DEFAULT_ALCALL_PARAMS = None
57
53
 
58
54
 
59
- class Branch(Element, Communicatable, Relational):
55
+ class Branch(Element, Relational):
60
56
  """
61
57
  Manages a conversation 'branch' with messages, tools, and iModels.
62
58
 
@@ -65,20 +61,17 @@ class Branch(Element, Communicatable, Relational):
65
61
  - Registers and invokes tools/actions (`ActionManager`).
66
62
  - Manages model instances (`iModelManager`).
67
63
  - Logs activity (`LogManager`).
68
- - Communicates via mailboxes (`Mailbox`).
69
64
 
70
65
  **Key responsibilities**:
71
66
  - Storing and organizing messages, including system instructions, user instructions, and model responses.
72
67
  - Handling asynchronous or synchronous execution of LLM calls and tool invocations.
73
- - Providing a consistent interface for operate,” chat,” communicate,” parse,” etc.
68
+ - Providing a consistent interface for "operate," "chat," "communicate," "parse," etc.
74
69
 
75
70
  Attributes:
76
71
  user (SenderRecipient | None):
77
72
  The user or "owner" of this branch (often tied to a session).
78
73
  name (str | None):
79
74
  A human-readable name for this branch.
80
- mailbox (Mailbox):
81
- A mailbox for sending and receiving `Package` objects to/from other branches.
82
75
 
83
76
  Note:
84
77
  Actual implementations for chat, parse, operate, etc., are referenced
@@ -101,16 +94,10 @@ class Branch(Element, Communicatable, Relational):
101
94
  description="A human-readable name of the branch (optional).",
102
95
  )
103
96
 
104
- mailbox: Mailbox = Field(
105
- default_factory=Mailbox,
106
- exclude=True,
107
- description="Mailbox for cross-branch or external communication.",
108
- )
109
-
110
97
  _message_manager: MessageManager | None = PrivateAttr(None)
111
98
  _action_manager: ActionManager | None = PrivateAttr(None)
112
99
  _imodel_manager: iModelManager | None = PrivateAttr(None)
113
- _log_manager: LogManager | None = PrivateAttr(None)
100
+ _log_manager: DataLogger | None = PrivateAttr(None)
114
101
  _operation_manager: OperationManager | None = PrivateAttr(None)
115
102
 
116
103
  def __init__(
@@ -125,7 +112,7 @@ class Branch(Element, Communicatable, Relational):
125
112
  parse_model: iModel | dict = None,
126
113
  imodel: iModel = None, # deprecated, alias of chat_model
127
114
  tools: FuncTool | list[FuncTool] = None, # ActionManager kwargs
128
- log_config: LogManagerConfig | dict = None, # LogManager kwargs
115
+ log_config: DataLoggerConfig | dict = None, # LogManager kwargs
129
116
  system_datetime: bool | str = None,
130
117
  system_template=None,
131
118
  system_template_context: dict = None,
@@ -194,11 +181,11 @@ class Branch(Element, Communicatable, Relational):
194
181
  system = f"Developer Prompt: {str(system)}" if system else ""
195
182
  system = (LION_SYSTEM_MESSAGE + "\n\n" + system).strip()
196
183
 
184
+ # Note: system_template and system_template_context are deprecated
185
+ # Template rendering has been removed from the message system
197
186
  self._message_manager.add_message(
198
187
  system=system,
199
188
  system_datetime=system_datetime,
200
- template=system_template,
201
- template_context=system_template_context,
202
189
  recipient=self.id,
203
190
  sender=system_sender or self.user or MessageRole.SYSTEM,
204
191
  )
@@ -229,10 +216,10 @@ class Branch(Element, Communicatable, Relational):
229
216
  # --- LogManager ---
230
217
  if log_config:
231
218
  if isinstance(log_config, dict):
232
- log_config = LogManagerConfig(**log_config)
233
- self._log_manager = LogManager.from_config(log_config, logs=logs)
219
+ log_config = DataLoggerConfig(**log_config)
220
+ self._log_manager = DataLogger.from_config(log_config, logs=logs)
234
221
  else:
235
- self._log_manager = LogManager(**settings.LOG_CONFIG, logs=logs)
222
+ self._log_manager = DataLogger(**settings.LOG_CONFIG, logs=logs)
236
223
 
237
224
  self._operation_manager = OperationManager()
238
225
 
@@ -414,6 +401,7 @@ class Branch(Element, Communicatable, Relational):
414
401
  pd.DataFrame: Each row represents a message, with columns defined by MESSAGE_FIELDS.
415
402
  """
416
403
  from lionagi.protocols.generic.pile import Pile
404
+ from lionagi.protocols.messages.base import MESSAGE_FIELDS
417
405
 
418
406
  if progression is None:
419
407
  progression = self.msgs.progression
@@ -426,170 +414,6 @@ class Branch(Element, Communicatable, Relational):
426
414
  p = Pile(collections=msgs)
427
415
  return p.to_df(columns=MESSAGE_FIELDS)
428
416
 
429
- # -------------------------------------------------------------------------
430
- # Mailbox Send / Receive
431
- # -------------------------------------------------------------------------
432
- def send(
433
- self,
434
- recipient: IDType,
435
- category: Optional["PackageCategory"],
436
- item: Any,
437
- request_source: IDType | None = None,
438
- ) -> None:
439
- """
440
- Sends a `Package` (wrapped in a `Mail` object) to a specified recipient.
441
-
442
- Args:
443
- recipient (IDType):
444
- ID of the recipient branch or component.
445
- category (PackageCategory | None):
446
- The category/type of the package (e.g., 'message', 'tool', 'imodel').
447
- item (Any):
448
- The payload to send (e.g., a message, tool reference, model, etc.).
449
- request_source (IDType | None):
450
- The ID that prompted or requested this send operation (optional).
451
- """
452
- from lionagi.protocols.mail.package import Package
453
-
454
- package = Package(
455
- category=category,
456
- item=item,
457
- request_source=request_source,
458
- )
459
-
460
- mail = Mail(
461
- sender=self.id,
462
- recipient=recipient,
463
- package=package,
464
- )
465
- self.mailbox.append_out(mail)
466
-
467
- def receive(
468
- self,
469
- sender: IDType,
470
- message: bool = False,
471
- tool: bool = False,
472
- imodel: bool = False,
473
- ) -> None:
474
- """
475
- Retrieves and processes mail from a given sender according to the specified flags.
476
-
477
- Args:
478
- sender (IDType):
479
- The ID of the mail sender.
480
- message (bool):
481
- If `True`, process packages categorized as "message".
482
- tool (bool):
483
- If `True`, process packages categorized as "tool".
484
- imodel (bool):
485
- If `True`, process packages categorized as "imodel".
486
-
487
- Raises:
488
- ValueError: If no mail exists from the specified sender,
489
- or if a package is invalid for the chosen category.
490
- """
491
- sender = ID.get_id(sender)
492
- if sender not in self.mailbox.pending_ins.keys():
493
- raise ValueError(f"No mail or package found from sender: {sender}")
494
-
495
- skipped_requests = Progression()
496
- while self.mailbox.pending_ins[sender]:
497
- mail_id = self.mailbox.pending_ins[sender].popleft()
498
- mail: Mail = self.mailbox.pile_[mail_id]
499
-
500
- if mail.category == "message" and message:
501
- if not isinstance(mail.package.item, RoledMessage):
502
- raise ValueError(
503
- "Invalid message package: The item must be a `RoledMessage`."
504
- )
505
- new_message = mail.package.item.clone()
506
- new_message.sender = mail.sender
507
- new_message.recipient = self.id
508
- self.msgs.messages.include(new_message)
509
- self.mailbox.pile_.pop(mail_id)
510
-
511
- elif mail.category == "tool" and tool:
512
- if not isinstance(mail.package.item, Tool):
513
- raise ValueError(
514
- "Invalid tool package: The item must be a `Tool` instance."
515
- )
516
- self._action_manager.register_tools(mail.package.item)
517
- self.mailbox.pile_.pop(mail_id)
518
-
519
- elif mail.category == "imodel" and imodel:
520
- if not isinstance(mail.package.item, iModel):
521
- raise ValueError(
522
- "Invalid iModel package: The item must be an `iModel` instance."
523
- )
524
- self._imodel_manager.register_imodel(
525
- mail.package.item.name or "chat", mail.package.item
526
- )
527
- self.mailbox.pile_.pop(mail_id)
528
-
529
- else:
530
- # If the category doesn't match the flags or is unhandled
531
- skipped_requests.append(mail)
532
-
533
- # Requeue any skipped mail
534
- self.mailbox.pending_ins[sender] = skipped_requests
535
- if len(self.mailbox.pending_ins[sender]) == 0:
536
- self.mailbox.pending_ins.pop(sender)
537
-
538
- async def asend(
539
- self,
540
- recipient: IDType,
541
- category: PackageCategory | None,
542
- package: Any,
543
- request_source: IDType | None = None,
544
- ):
545
- """
546
- Async version of `send()`.
547
-
548
- Args:
549
- recipient (IDType):
550
- ID of the recipient branch or component.
551
- category (PackageCategory | None):
552
- The category/type of the package.
553
- package (Any):
554
- The item(s) to send (message/tool/model).
555
- request_source (IDType | None):
556
- The origin request ID (if any).
557
- """
558
- async with self.mailbox.pile_:
559
- self.send(recipient, category, package, request_source)
560
-
561
- async def areceive(
562
- self,
563
- sender: IDType,
564
- message: bool = False,
565
- tool: bool = False,
566
- imodel: bool = False,
567
- ) -> None:
568
- """
569
- Async version of `receive()`.
570
-
571
- Args:
572
- sender (IDType):
573
- The ID of the mail sender.
574
- message (bool):
575
- If `True`, process packages categorized as "message".
576
- tool (bool):
577
- If `True`, process packages categorized as "tool".
578
- imodel (bool):
579
- If `True`, process packages categorized as "imodel".
580
- """
581
- async with self.mailbox.pile_:
582
- self.receive(sender, message, tool, imodel)
583
-
584
- def receive_all(self) -> None:
585
- """
586
- Receives mail from all known senders without filtering.
587
-
588
- (Duplicate method included in your snippet; you may unify or remove.)
589
- """
590
- for key in self.mailbox.pending_ins:
591
- self.receive(key)
592
-
593
417
  def connect(
594
418
  self,
595
419
  provider: str = None,
@@ -632,7 +456,7 @@ class Branch(Element, Communicatable, Relational):
632
456
  async def _connect(**kwargs):
633
457
  """connect to an api endpoint"""
634
458
  api_call = await imodel.invoke(**kwargs)
635
- self._log_manager.log(Log.create(api_call))
459
+ self._log_manager.log(api_call)
636
460
  return api_call.response
637
461
 
638
462
  _connect.__name__ = name or imodel.endpoint.name
@@ -760,6 +584,7 @@ class Branch(Element, Communicatable, Relational):
760
584
  image_detail: Literal["low", "high", "auto"] = None,
761
585
  plain_content: str = None,
762
586
  return_ins_res_message: bool = False,
587
+ include_token_usage_to_model: bool = False,
763
588
  **kwargs,
764
589
  ) -> tuple[Instruction, AssistantResponse]:
765
590
  """
@@ -809,25 +634,27 @@ class Branch(Element, Communicatable, Relational):
809
634
  tuple[Instruction, AssistantResponse]:
810
635
  The `Instruction` object and the final `AssistantResponse`.
811
636
  """
812
- from lionagi.operations.chat.chat import chat
637
+ from lionagi.operations.chat.chat import ChatParam, chat
813
638
 
814
639
  return await chat(
815
640
  self,
816
641
  instruction=instruction,
817
- guidance=guidance,
818
- context=context,
819
- sender=sender,
820
- recipient=recipient,
821
- request_fields=request_fields,
822
- response_format=response_format,
823
- progression=progression,
824
- imodel=imodel or kwargs.pop("chat_model", None) or self.chat_model,
825
- tool_schemas=tool_schemas,
826
- images=images,
827
- image_detail=image_detail,
828
- plain_content=plain_content,
642
+ chat_param=ChatParam(
643
+ guidance=guidance,
644
+ context=context,
645
+ sender=sender or self.user or "user",
646
+ recipient=recipient or self.id,
647
+ response_format=response_format or request_fields,
648
+ progression=progression,
649
+ tool_schemas=tool_schemas or [],
650
+ images=images or [],
651
+ image_detail=image_detail or "auto",
652
+ plain_content=plain_content or "",
653
+ include_token_usage_to_model=include_token_usage_to_model,
654
+ imodel=imodel or self.chat_model,
655
+ imodel_kw=kwargs,
656
+ ),
829
657
  return_ins_res_message=return_ins_res_message,
830
- **kwargs,
831
658
  )
832
659
 
833
660
  async def parse(
@@ -889,25 +716,15 @@ class Branch(Element, Communicatable, Relational):
889
716
  BaseModel | dict | str | None:
890
717
  Parsed model instance, or a fallback based on `handle_validation`.
891
718
  """
892
- from lionagi.operations.parse.parse import parse
893
719
 
894
- return await parse(
895
- self,
896
- text=text,
897
- handle_validation=handle_validation,
898
- max_retries=max_retries,
899
- request_type=request_type,
900
- operative=operative,
901
- similarity_algo=similarity_algo,
902
- similarity_threshold=similarity_threshold,
903
- fuzzy_match=fuzzy_match,
904
- handle_unmatched=handle_unmatched,
905
- fill_value=fill_value,
906
- fill_mapping=fill_mapping,
907
- strict=strict,
908
- suppress_conversion_errors=suppress_conversion_errors,
909
- response_format=response_format,
910
- )
720
+ _pms = {
721
+ k: v
722
+ for k, v in locals().items()
723
+ if k not in ("self", "_pms") and v is not None
724
+ }
725
+ from lionagi.operations.parse.parse import parse, prepare_parse_kws
726
+
727
+ return await parse(self, **prepare_parse_kws(self, **_pms))
911
728
 
912
729
  async def operate(
913
730
  self,
@@ -1026,39 +843,18 @@ class Branch(Element, Communicatable, Relational):
1026
843
  - If both `operative_model` and `response_format` or `request_model` are given.
1027
844
  - If the LLM's response cannot be parsed into the expected format and `handle_validation='raise'`.
1028
845
  """
1029
- from lionagi.operations.operate.operate import operate
1030
-
1031
- return await operate(
1032
- self,
1033
- instruct=instruct,
1034
- instruction=instruction,
1035
- guidance=guidance,
1036
- context=context,
1037
- sender=sender,
1038
- recipient=recipient,
1039
- progression=progression,
1040
- chat_model=chat_model,
1041
- invoke_actions=invoke_actions,
1042
- tool_schemas=tool_schemas,
1043
- images=images,
1044
- image_detail=image_detail,
1045
- parse_model=parse_model,
1046
- skip_validation=skip_validation,
1047
- tools=tools,
1048
- operative=operative,
1049
- response_format=response_format,
1050
- actions=actions,
1051
- reason=reason,
1052
- call_params=call_params,
1053
- action_strategy=action_strategy,
1054
- verbose_action=verbose_action,
1055
- field_models=field_models,
1056
- exclude_fields=exclude_fields,
1057
- handle_validation=handle_validation,
1058
- include_token_usage_to_model=include_token_usage_to_model,
1059
- **kwargs,
846
+ _pms = {
847
+ k: v
848
+ for k, v in locals().items()
849
+ if k not in ("self", "_pms") and v is not None
850
+ }
851
+ from lionagi.operations.operate.operate import (
852
+ operate,
853
+ prepare_operate_kw,
1060
854
  )
1061
855
 
856
+ return await operate(self, **prepare_operate_kw(self, **_pms))
857
+
1062
858
  async def communicate(
1063
859
  self,
1064
860
  instruction: Instruction | JsonValue = None,
@@ -1132,44 +928,19 @@ class Branch(Element, Communicatable, Relational):
1132
928
  - A dict of the requested fields,
1133
929
  - or `None` if parsing fails and `handle_validation='return_none'`.
1134
930
  """
1135
- from lionagi.operations.communicate.communicate import communicate
931
+ _pms = {
932
+ k: v
933
+ for k, v in locals().items()
934
+ if k not in ("self", "_pms", "kwargs") and v is not None
935
+ }
936
+ _pms.update(kwargs)
1136
937
 
1137
- return await communicate(
1138
- self,
1139
- instruction=instruction,
1140
- guidance=guidance,
1141
- context=context,
1142
- plain_content=plain_content,
1143
- sender=sender,
1144
- recipient=recipient,
1145
- progression=progression,
1146
- response_format=response_format,
1147
- request_fields=request_fields,
1148
- chat_model=chat_model,
1149
- parse_model=parse_model,
1150
- skip_validation=skip_validation,
1151
- images=images,
1152
- image_detail=image_detail,
1153
- num_parse_retries=num_parse_retries,
1154
- clear_messages=clear_messages,
1155
- include_token_usage_to_model=include_token_usage_to_model,
1156
- **kwargs,
938
+ from lionagi.operations.communicate.communicate import (
939
+ communicate,
940
+ prepare_communicate_kw,
1157
941
  )
1158
942
 
1159
- async def _act(
1160
- self,
1161
- action_request: ActionRequest | BaseModel | dict,
1162
- suppress_errors: bool,
1163
- verbose_action: bool,
1164
- ) -> ActionResponse:
1165
- from lionagi.operations._act.act import _act
1166
-
1167
- return await _act(
1168
- branch=self,
1169
- action_request=action_request,
1170
- suppress_errors=suppress_errors,
1171
- verbose_action=verbose_action,
1172
- )
943
+ return await communicate(self, **prepare_communicate_kw(self, **_pms))
1173
944
 
1174
945
  async def act(
1175
946
  self,
@@ -1180,54 +951,15 @@ class Branch(Element, Communicatable, Relational):
1180
951
  suppress_errors: bool = True,
1181
952
  call_params: AlcallParams = None,
1182
953
  ) -> list[ActionResponse]:
1183
- global _DEFAULT_ALCALL_PARAMS
1184
- if call_params is None:
1185
- if _DEFAULT_ALCALL_PARAMS is None:
1186
- _DEFAULT_ALCALL_PARAMS = AlcallParams(output_dropna=True)
1187
- call_params = _DEFAULT_ALCALL_PARAMS
1188
-
1189
- kw = {
1190
- "suppress_errors": suppress_errors,
1191
- "verbose_action": verbose_action,
1192
- }
1193
-
1194
- match strategy:
1195
- case "concurrent":
1196
- return await self._concurrent_act(
1197
- action_request, call_params, **kw
1198
- )
1199
- case "sequential":
1200
- return await self._sequential_act(action_request, **kw)
1201
- case _:
1202
- raise ValueError(
1203
- "Invalid strategy. Choose 'concurrent' or 'sequential'."
1204
- )
1205
954
 
1206
- async def _concurrent_act(
1207
- self,
1208
- action_request: ActionRequest | BaseModel | dict,
1209
- call_params: AlcallParams = None,
1210
- **kwargs,
1211
- ) -> list:
1212
- return await call_params(action_request, self._act, **kwargs)
955
+ _pms = {
956
+ k: v
957
+ for k, v in locals().items()
958
+ if k not in ("self", "_pms") and v is not None
959
+ }
960
+ from lionagi.operations.act.act import act, prepare_act_kw
1213
961
 
1214
- async def _sequential_act(
1215
- self,
1216
- action_request: ActionRequest | BaseModel | dict,
1217
- suppress_errors: bool = True,
1218
- verbose_action: bool = False,
1219
- ) -> list:
1220
- action_request = (
1221
- action_request
1222
- if isinstance(action_request, list)
1223
- else [action_request]
1224
- )
1225
- results = []
1226
- for req in action_request:
1227
- results.append(
1228
- await self._act(req, verbose_action, suppress_errors)
1229
- )
1230
- return results
962
+ return await act(self, **prepare_act_kw(self, **_pms))
1231
963
 
1232
964
  async def interpret(
1233
965
  self,
@@ -1275,43 +1007,20 @@ class Branch(Element, Communicatable, Relational):
1275
1007
  # refined might be "Explain step-by-step how to set up a marketing analytics
1276
1008
  # pipeline to track campaign performance..."
1277
1009
  """
1278
- from lionagi.operations.interpret.interpret import interpret
1279
-
1280
- return await interpret(
1281
- self,
1282
- text=text,
1283
- domain=domain,
1284
- style=style,
1285
- interpret_model=interpret_model,
1286
- **kwargs,
1287
- )
1288
-
1289
- async def instruct(
1290
- self,
1291
- instruct: "Instruct",
1292
- /,
1293
- **kwargs,
1294
- ):
1295
- """
1296
- A convenience method that chooses between `operate()` and `communicate()`
1297
- based on the contents of an `Instruct` object.
1298
-
1299
- If the `Instruct` indicates tool usage or advanced response format,
1300
- `operate()` is used. Otherwise, it defaults to `communicate()`.
1301
1010
 
1302
- Args:
1303
- instruct (Instruct):
1304
- An object containing `instruction`, `guidance`, `context`, etc.
1305
- **kwargs:
1306
- Additional args forwarded to `operate()` or `communicate()`.
1011
+ _pms = {
1012
+ k: v
1013
+ for k, v in locals().items()
1014
+ if k not in ("self", "_pms", "kwargs") and v is not None
1015
+ }
1016
+ _pms.update(kwargs)
1307
1017
 
1308
- Returns:
1309
- Any:
1310
- The result of the underlying call (structured object, raw text, etc.).
1311
- """
1312
- from lionagi.operations.instruct.instruct import instruct as _ins
1018
+ from lionagi.operations.interpret.interpret import (
1019
+ interpret,
1020
+ prepare_interpret_kw,
1021
+ )
1313
1022
 
1314
- return await _ins(self, instruct, **kwargs)
1023
+ return await interpret(self, **prepare_interpret_kw(self, **_pms))
1315
1024
 
1316
1025
  async def ReAct(
1317
1026
  self,
@@ -1413,6 +1122,13 @@ class Branch(Element, Communicatable, Relational):
1413
1122
  """
1414
1123
  from lionagi.operations.ReAct.ReAct import ReAct
1415
1124
 
1125
+ # Remove potential duplicate parameters from kwargs
1126
+ kwargs_filtered = {
1127
+ k: v
1128
+ for k, v in kwargs.items()
1129
+ if k not in {"verbose_analysis", "verbose_action"}
1130
+ }
1131
+
1416
1132
  return await ReAct(
1417
1133
  self,
1418
1134
  instruct,
@@ -1438,7 +1154,7 @@ class Branch(Element, Communicatable, Relational):
1438
1154
  reasoning_effort=reasoning_effort,
1439
1155
  display_as=display_as,
1440
1156
  include_token_usage_to_model=include_token_usage_to_model,
1441
- **kwargs,
1157
+ **kwargs_filtered,
1442
1158
  )
1443
1159
 
1444
1160
  async def ReActStream(
@@ -1466,40 +1182,114 @@ class Branch(Element, Communicatable, Relational):
1466
1182
  include_token_usage_to_model: bool = True,
1467
1183
  **kwargs,
1468
1184
  ) -> AsyncGenerator:
1185
+ from lionagi.ln.fuzzy import FuzzyMatchKeysParams
1469
1186
  from lionagi.operations.ReAct.ReAct import ReActStream
1187
+ from lionagi.operations.ReAct.utils import ReActAnalysis
1188
+ from lionagi.operations.types import (
1189
+ ActionParam,
1190
+ ChatParam,
1191
+ InterpretParam,
1192
+ ParseParam,
1193
+ )
1194
+
1195
+ # Convert Instruct to dict if needed
1196
+ instruct_dict = (
1197
+ instruct.to_dict()
1198
+ if isinstance(instruct, Instruct)
1199
+ else dict(instruct)
1200
+ )
1201
+
1202
+ # Build InterpretContext if interpretation requested
1203
+ intp_param = None
1204
+ if interpret:
1205
+ intp_param = InterpretParam(
1206
+ domain=interpret_domain or "general",
1207
+ style=interpret_style or "concise",
1208
+ sample_writing=interpret_sample or "",
1209
+ imodel=interpret_model or analysis_model or self.chat_model,
1210
+ imodel_kw=interpret_kwargs or {},
1211
+ )
1212
+
1213
+ # Build ChatContext
1214
+ chat_param = ChatParam(
1215
+ guidance=instruct_dict.get("guidance"),
1216
+ context=instruct_dict.get("context"),
1217
+ sender=self.user or "user",
1218
+ recipient=self.id,
1219
+ response_format=None,
1220
+ progression=None,
1221
+ tool_schemas=tool_schemas or [],
1222
+ images=[],
1223
+ image_detail="auto",
1224
+ plain_content="",
1225
+ include_token_usage_to_model=include_token_usage_to_model,
1226
+ imodel=analysis_model or self.chat_model,
1227
+ imodel_kw=kwargs,
1228
+ )
1229
+
1230
+ # Build ActionContext
1231
+ action_param = None
1232
+ if tools is not None or tool_schemas is not None:
1233
+ from lionagi.operations.act.act import _get_default_call_params
1234
+
1235
+ action_param = ActionParam(
1236
+ action_call_params=_get_default_call_params(),
1237
+ tools=tools or True,
1238
+ strategy="concurrent",
1239
+ suppress_errors=True,
1240
+ verbose_action=False,
1241
+ )
1242
+
1243
+ # Build ParseContext
1244
+ from lionagi.operations.parse.parse import get_default_call
1245
+
1246
+ parse_param = ParseParam(
1247
+ response_format=ReActAnalysis,
1248
+ fuzzy_match_params=FuzzyMatchKeysParams(),
1249
+ handle_validation="return_value",
1250
+ alcall_params=get_default_call(),
1251
+ imodel=analysis_model or self.chat_model,
1252
+ imodel_kw={},
1253
+ )
1254
+
1255
+ # Response context for final answer
1256
+ resp_ctx = response_kwargs or {}
1257
+ if response_format:
1258
+ resp_ctx["response_format"] = response_format
1470
1259
 
1471
1260
  async for result in ReActStream(
1472
1261
  self,
1473
- instruct,
1474
- interpret=interpret,
1475
- interpret_domain=interpret_domain,
1476
- interpret_style=interpret_style,
1477
- interpret_sample=interpret_sample,
1478
- interpret_model=interpret_model,
1479
- interpret_kwargs=interpret_kwargs,
1480
- tools=tools,
1481
- tool_schemas=tool_schemas,
1482
- response_format=response_format,
1262
+ instruction=instruct_dict.get("instruction", str(instruct)),
1263
+ chat_param=chat_param,
1264
+ action_param=action_param,
1265
+ parse_param=parse_param,
1266
+ intp_param=intp_param,
1267
+ resp_ctx=resp_ctx,
1268
+ reasoning_effort=reasoning_effort,
1269
+ reason=True,
1270
+ field_models=None,
1271
+ handle_validation="return_value",
1272
+ invoke_actions=True,
1273
+ clear_messages=False,
1483
1274
  intermediate_response_options=intermediate_response_options,
1484
1275
  intermediate_listable=intermediate_listable,
1485
- reasoning_effort=reasoning_effort,
1486
- extension_allowed=extension_allowed,
1276
+ intermediate_nullable=False,
1487
1277
  max_extensions=max_extensions,
1488
- response_kwargs=response_kwargs,
1489
- analysis_model=analysis_model,
1490
- verbose_analysis=True,
1278
+ extension_allowed=extension_allowed,
1279
+ verbose_analysis=verbose,
1491
1280
  display_as=display_as,
1492
1281
  verbose_length=verbose_length,
1493
- include_token_usage_to_model=include_token_usage_to_model,
1494
- **kwargs,
1282
+ continue_after_failed_response=False,
1495
1283
  ):
1496
- analysis, str_ = result
1497
1284
  if verbose:
1285
+ analysis, str_ = result
1498
1286
  from lionagi.libs.schema.as_readable import as_readable
1499
1287
 
1500
1288
  str_ += "\n---------\n"
1501
1289
  as_readable(str_, md=True, display_str=True)
1502
- yield analysis
1290
+ yield analysis
1291
+ else:
1292
+ yield result
1503
1293
 
1504
1294
 
1505
1295
  # File: lionagi/session/branch.py