lionagi 0.6.1__py3-none-any.whl → 0.7.0__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 (48) hide show
  1. lionagi/libs/token_transform/__init__.py +0 -0
  2. lionagi/libs/token_transform/llmlingua.py +1 -0
  3. lionagi/libs/token_transform/perplexity.py +439 -0
  4. lionagi/libs/token_transform/synthlang.py +409 -0
  5. lionagi/operations/ReAct/ReAct.py +126 -0
  6. lionagi/operations/ReAct/utils.py +28 -0
  7. lionagi/operations/__init__.py +1 -9
  8. lionagi/operations/_act/act.py +73 -0
  9. lionagi/operations/chat/__init__.py +3 -0
  10. lionagi/operations/chat/chat.py +173 -0
  11. lionagi/operations/communicate/__init__.py +0 -0
  12. lionagi/operations/communicate/communicate.py +167 -0
  13. lionagi/operations/instruct/__init__.py +3 -0
  14. lionagi/operations/instruct/instruct.py +29 -0
  15. lionagi/operations/interpret/__init__.py +3 -0
  16. lionagi/operations/interpret/interpret.py +40 -0
  17. lionagi/operations/operate/__init__.py +3 -0
  18. lionagi/operations/operate/operate.py +189 -0
  19. lionagi/operations/parse/__init__.py +3 -0
  20. lionagi/operations/parse/parse.py +125 -0
  21. lionagi/operations/select/__init__.py +0 -4
  22. lionagi/operations/select/select.py +11 -30
  23. lionagi/operations/select/utils.py +13 -2
  24. lionagi/operations/translate/__init__.py +0 -0
  25. lionagi/operations/translate/translate.py +47 -0
  26. lionagi/operations/types.py +16 -0
  27. lionagi/operatives/action/manager.py +20 -21
  28. lionagi/operatives/strategies/__init__.py +3 -0
  29. lionagi/session/branch.py +1098 -929
  30. lionagi/version.py +1 -1
  31. {lionagi-0.6.1.dist-info → lionagi-0.7.0.dist-info}/METADATA +1 -1
  32. {lionagi-0.6.1.dist-info → lionagi-0.7.0.dist-info}/RECORD +45 -26
  33. lionagi/libs/compress/models.py +0 -66
  34. lionagi/libs/compress/utils.py +0 -69
  35. lionagi/operations/select/prompt.py +0 -5
  36. /lionagi/{libs/compress → operations/ReAct}/__init__.py +0 -0
  37. /lionagi/operations/{strategies → _act}/__init__.py +0 -0
  38. /lionagi/{operations → operatives}/strategies/base.py +0 -0
  39. /lionagi/{operations → operatives}/strategies/concurrent.py +0 -0
  40. /lionagi/{operations → operatives}/strategies/concurrent_chunk.py +0 -0
  41. /lionagi/{operations → operatives}/strategies/concurrent_sequential_chunk.py +0 -0
  42. /lionagi/{operations → operatives}/strategies/params.py +0 -0
  43. /lionagi/{operations → operatives}/strategies/sequential.py +0 -0
  44. /lionagi/{operations → operatives}/strategies/sequential_chunk.py +0 -0
  45. /lionagi/{operations → operatives}/strategies/sequential_concurrent_chunk.py +0 -0
  46. /lionagi/{operations → operatives}/strategies/utils.py +0 -0
  47. {lionagi-0.6.1.dist-info → lionagi-0.7.0.dist-info}/WHEEL +0 -0
  48. {lionagi-0.6.1.dist-info → lionagi-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,125 @@
1
+ # Copyright (c) 2023 - 2024, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from typing import TYPE_CHECKING, Any, Literal
6
+
7
+ from pydantic import BaseModel
8
+
9
+ from lionagi.libs.validate.fuzzy_validate_mapping import fuzzy_validate_mapping
10
+ from lionagi.operatives.types import Operative
11
+ from lionagi.utils import breakdown_pydantic_annotation
12
+
13
+ if TYPE_CHECKING:
14
+ from lionagi.session.branch import Branch
15
+
16
+
17
+ async def parse(
18
+ branch: "Branch",
19
+ text: str,
20
+ handle_validation: Literal[
21
+ "raise", "return_value", "return_none"
22
+ ] = "return_value",
23
+ max_retries: int = 3,
24
+ request_type: type[BaseModel] = None,
25
+ operative: Operative = None,
26
+ similarity_algo="jaro_winkler",
27
+ similarity_threshold: float = 0.85,
28
+ fuzzy_match: bool = True,
29
+ handle_unmatched: Literal[
30
+ "ignore", "raise", "remove", "fill", "force"
31
+ ] = "force",
32
+ fill_value: Any = None,
33
+ fill_mapping: dict[str, Any] | None = None,
34
+ strict: bool = False,
35
+ suppress_conversion_errors: bool = False,
36
+ response_format=None,
37
+ ):
38
+ """Attempts to parse text into a structured Pydantic model.
39
+
40
+ Uses optional fuzzy matching to handle partial or unclear fields.
41
+
42
+ Args:
43
+ text (str): The raw text to parse.
44
+ handle_validation (Literal["raise","return_value","return_none"]):
45
+ What to do if parsing fails. Defaults to "return_value".
46
+ max_retries (int):
47
+ How many times to retry parsing if it fails.
48
+ request_type (type[BaseModel], optional):
49
+ The Pydantic model to parse into.
50
+ operative (Operative, optional):
51
+ If provided, uses its model and max_retries setting.
52
+ similarity_algo (str):
53
+ The similarity algorithm for fuzzy field matching.
54
+ similarity_threshold (float):
55
+ A threshold for fuzzy matching (0.0 - 1.0).
56
+ fuzzy_match (bool):
57
+ If True, tries to match unrecognized keys to known ones.
58
+ handle_unmatched (Literal["ignore","raise","remove","fill","force"]):
59
+ How to handle unmatched fields.
60
+ fill_value (Any):
61
+ A default value used when fill is needed.
62
+ fill_mapping (dict[str, Any] | None):
63
+ A mapping from field -> fill value override.
64
+ strict (bool):
65
+ If True, raises errors on ambiguous fields or data types.
66
+ suppress_conversion_errors (bool):
67
+ If True, logs or ignores errors during data conversion.
68
+
69
+ Returns:
70
+ BaseModel | Any | None:
71
+ The parsed model instance, or a dict/string/None depending
72
+ on the handling mode.
73
+ """
74
+ _should_try = True
75
+ num_try = 0
76
+ response_model = text
77
+ if operative is not None:
78
+ max_retries = operative.max_retries
79
+ response_format = operative.request_type
80
+
81
+ while (
82
+ _should_try
83
+ and num_try < max_retries
84
+ and not isinstance(response_model, BaseModel)
85
+ ):
86
+ num_try += 1
87
+ _, res = await branch.chat(
88
+ instruction="reformat text into specified model",
89
+ guidane="follow the required response format, using the model schema as a guide",
90
+ context=[{"text_to_format": text}],
91
+ response_format=response_format or request_type,
92
+ sender=branch.user,
93
+ recipient=branch.id,
94
+ imodel=branch.parse_model,
95
+ return_ins_res_message=True,
96
+ )
97
+ if operative is not None:
98
+ response_model = operative.update_response_model(res.response)
99
+ else:
100
+ response_model = fuzzy_validate_mapping(
101
+ res.response,
102
+ breakdown_pydantic_annotation(request_type),
103
+ similarity_algo=similarity_algo,
104
+ similarity_threshold=similarity_threshold,
105
+ fuzzy_match=fuzzy_match,
106
+ handle_unmatched=handle_unmatched,
107
+ fill_value=fill_value,
108
+ fill_mapping=fill_mapping,
109
+ strict=strict,
110
+ suppress_conversion_errors=suppress_conversion_errors,
111
+ )
112
+ response_model = request_type.model_validate(response_model)
113
+
114
+ if not isinstance(response_model, BaseModel):
115
+ match handle_validation:
116
+ case "return_value":
117
+ return response_model
118
+ case "return_none":
119
+ return None
120
+ case "raise":
121
+ raise ValueError(
122
+ "Failed to parse response into request format"
123
+ )
124
+
125
+ return response_model
@@ -1,7 +1,3 @@
1
1
  # Copyright (c) 2023 - 2024, HaiyangLi <quantocean.li at gmail dot com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
-
5
- from .select import select
6
-
7
- __all__ = ["select"]
@@ -2,56 +2,37 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
-
6
5
  from enum import Enum
7
- from typing import Any
6
+ from typing import TYPE_CHECKING, Any
8
7
 
9
- from pydantic import BaseModel, Field
8
+ from pydantic import BaseModel
10
9
 
11
10
  from lionagi.operatives.types import Instruct
12
- from lionagi.session.branch import Branch
13
-
14
- from .prompt import PROMPT
15
- from .utils import parse_selection, parse_to_representation
16
11
 
12
+ from .utils import SelectionModel
17
13
 
18
- class SelectionModel(BaseModel):
19
- """Model representing the selection output."""
20
-
21
- selected: list[Any] = Field(default_factory=list)
14
+ if TYPE_CHECKING:
15
+ from lionagi.session.branch import Branch
22
16
 
23
17
 
24
18
  async def select(
19
+ branch: "Branch",
25
20
  instruct: Instruct | dict[str, Any],
26
21
  choices: list[str] | type[Enum] | dict[str, Any],
27
22
  max_num_selections: int = 1,
28
- branch: Branch | None = None,
29
23
  branch_kwargs: dict[str, Any] | None = None,
30
24
  return_branch: bool = False,
31
25
  verbose: bool = False,
32
26
  **kwargs: Any,
33
- ) -> SelectionModel | tuple[SelectionModel, Branch]:
34
- """Perform a selection operation from given choices.
35
-
36
- Args:
37
- instruct: Instruction model or dictionary.
38
- choices: Options to select from.
39
- max_num_selections: Maximum selections allowed.
40
- branch: Existing branch or None to create a new one.
41
- branch_kwargs: Additional arguments for branch creation.
42
- return_branch: If True, return the branch with the selection.
43
- verbose: Whether to enable verbose output.
44
- **kwargs: Additional keyword arguments.
45
-
46
- Returns:
47
- A SelectionModel instance, optionally with the branch.
48
- """
27
+ ) -> SelectionModel | tuple[SelectionModel, "Branch"]:
49
28
  if verbose:
50
29
  print(f"Starting selection with up to {max_num_selections} choices.")
51
30
 
31
+ from .utils import SelectionModel, parse_selection, parse_to_representation
32
+
52
33
  branch = branch or Branch(**(branch_kwargs or {}))
53
34
  selections, contents = parse_to_representation(choices)
54
- prompt = PROMPT.format(
35
+ prompt = SelectionModel.PROMPT.format(
55
36
  max_num_selections=max_num_selections, choices=selections
56
37
  )
57
38
 
@@ -73,7 +54,7 @@ async def select(
73
54
  instruct["context"] = context
74
55
 
75
56
  response_model: SelectionModel = await branch.operate(
76
- operative_model=SelectionModel,
57
+ response_format=SelectionModel,
77
58
  **kwargs,
78
59
  **instruct,
79
60
  )
@@ -4,14 +4,25 @@
4
4
 
5
5
  import inspect
6
6
  from enum import Enum
7
- from typing import Any
7
+ from typing import Any, ClassVar
8
8
 
9
- from pydantic import BaseModel, JsonValue
9
+ from pydantic import BaseModel, Field, JsonValue
10
10
 
11
11
  from lionagi.libs.validate.string_similarity import string_similarity
12
12
  from lionagi.utils import is_same_dtype
13
13
 
14
14
 
15
+ # TODO: Make select a field to be added into a model, much like reason and action
16
+ class SelectionModel(BaseModel):
17
+ """Model representing the selection output."""
18
+
19
+ PROMPT: ClassVar[str] = (
20
+ "Please select up to {max_num_selections} items from the following list {choices}. Provide the selection(s) into appropriate field in format required, and no comments from you"
21
+ )
22
+
23
+ selected: list[Any] = Field(default_factory=list)
24
+
25
+
15
26
  def parse_to_representation(
16
27
  choices: Enum | dict | list | tuple | set,
17
28
  ) -> tuple[list[str], JsonValue]:
File without changes
@@ -0,0 +1,47 @@
1
+ from typing import TYPE_CHECKING, Literal
2
+
3
+ from lionagi.service.imodel import iModel
4
+
5
+ if TYPE_CHECKING:
6
+ from lionagi.session.branch import Branch
7
+
8
+
9
+ async def translate(
10
+ branch: "Branch",
11
+ text: str,
12
+ technique: Literal["SynthLang"] = "SynthLang",
13
+ technique_kwargs: dict = None,
14
+ compress: bool = False,
15
+ chat_model: iModel = None,
16
+ compress_model: iModel = None,
17
+ compression_ratio: float = 0.2,
18
+ compress_kwargs=None,
19
+ verbose: bool = True,
20
+ new_branch: bool = True,
21
+ **kwargs,
22
+ ):
23
+ if technique == "SynthLang":
24
+ from lionagi.libs.token_transform.synthlang import (
25
+ translate_to_synthlang,
26
+ )
27
+
28
+ if not technique_kwargs:
29
+ technique_kwargs = {}
30
+ if not technique_kwargs.get("template_name"):
31
+ technique_kwargs["template_name"] = "symbolic_systems"
32
+
33
+ technique_kwargs = {**technique_kwargs, **kwargs}
34
+
35
+ return await translate_to_synthlang(
36
+ text=text,
37
+ compress=compress,
38
+ chat_model=chat_model or branch.chat_model,
39
+ compress_model=compress_model,
40
+ compression_ratio=compression_ratio,
41
+ compress_kwargs=compress_kwargs,
42
+ verbose=verbose,
43
+ branch=branch if not new_branch else None,
44
+ **technique_kwargs,
45
+ )
46
+
47
+ raise ValueError(f"Technique {technique} is not supported.")
@@ -3,11 +3,27 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
  from .brainstorm.brainstorm import brainstorm
6
+ from .chat.chat import chat
7
+ from .communicate.communicate import communicate
8
+ from .instruct.instruct import instruct
9
+ from .interpret.interpret import interpret
10
+ from .operate.operate import operate
11
+ from .parse.parse import parse
6
12
  from .plan.plan import plan
13
+ from .ReAct.ReAct import ReAct
7
14
  from .select.select import select
15
+ from .translate.translate import translate
8
16
 
9
17
  __all__ = (
10
18
  "brainstorm",
11
19
  "plan",
12
20
  "select",
21
+ "chat",
22
+ "communicate",
23
+ "instruct",
24
+ "interpret",
25
+ "operate",
26
+ "parse",
27
+ "ReAct",
28
+ "translate",
13
29
  )
@@ -5,8 +5,7 @@
5
5
  from typing import Any
6
6
 
7
7
  from lionagi.protocols._concepts import Manager
8
- from lionagi.protocols.generic.event import EventStatus, Execution
9
- from lionagi.protocols.generic.log import Log
8
+ from lionagi.protocols.generic.event import Execution
10
9
  from lionagi.protocols.messages.action_request import ActionRequest
11
10
  from lionagi.utils import to_list
12
11
 
@@ -113,19 +112,28 @@ class ActionManager(Manager):
113
112
  ]
114
113
 
115
114
  def match_tool(
116
- self, action_request: ActionRequest | ActionRequestModel
115
+ self, action_request: ActionRequest | ActionRequestModel | dict
117
116
  ) -> FunctionCalling:
118
- if not isinstance(action_request, ActionRequest | ActionRequestModel):
117
+ if not isinstance(
118
+ action_request, ActionRequest | ActionRequestModel | dict
119
+ ):
119
120
  raise TypeError(f"Unsupported type {type(action_request)}")
120
121
 
121
- tool = self.registry.get(action_request.function, None)
122
- if not isinstance(tool, Tool):
123
- raise ValueError(
124
- f"Function {action_request.function} is not registered."
125
- )
126
- return FunctionCalling(
127
- func_tool=tool, arguments=action_request.arguments
122
+ func = (
123
+ action_request["function"]
124
+ if isinstance(action_request, dict)
125
+ else action_request.function
126
+ )
127
+ args = (
128
+ action_request["arguments"]
129
+ if isinstance(action_request, dict)
130
+ else action_request.arguments
128
131
  )
132
+ tool = self.registry.get(func, None)
133
+
134
+ if not isinstance(tool, Tool):
135
+ raise ValueError(f"Function {func} is not registered.")
136
+ return FunctionCalling(func_tool=tool, arguments=args)
129
137
 
130
138
  async def invoke(
131
139
  self, func_call: ActionRequestModel | ActionRequest
@@ -148,16 +156,7 @@ class ActionManager(Manager):
148
156
  Raises:
149
157
  ValueError: If function not registered or call format invalid.
150
158
  """
151
- try:
152
- function_calling = self.match_tool(func_call)
153
- except ValueError as e:
154
- return Log(
155
- content={
156
- "event_type": "function_call",
157
- "status": EventStatus.FAILED,
158
- "error": str(e),
159
- }
160
- )
159
+ function_calling = self.match_tool(func_call)
161
160
  await function_calling.invoke()
162
161
  return function_calling
163
162
 
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2023 - 2024, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0