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

Sign up to get free protection for your applications and to get access to all the features.
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