lionagi 0.17.11__py3-none-any.whl → 0.18.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 (52) hide show
  1. lionagi/libs/schema/minimal_yaml.py +98 -0
  2. lionagi/ln/types.py +32 -5
  3. lionagi/models/field_model.py +9 -0
  4. lionagi/operations/ReAct/ReAct.py +474 -237
  5. lionagi/operations/ReAct/utils.py +3 -0
  6. lionagi/operations/act/act.py +206 -0
  7. lionagi/operations/chat/chat.py +130 -114
  8. lionagi/operations/communicate/communicate.py +101 -42
  9. lionagi/operations/flow.py +4 -4
  10. lionagi/operations/interpret/interpret.py +65 -20
  11. lionagi/operations/operate/operate.py +212 -106
  12. lionagi/operations/parse/parse.py +170 -142
  13. lionagi/operations/select/select.py +78 -17
  14. lionagi/operations/select/utils.py +1 -1
  15. lionagi/operations/types.py +119 -23
  16. lionagi/protocols/generic/log.py +3 -2
  17. lionagi/protocols/messages/__init__.py +27 -0
  18. lionagi/protocols/messages/action_request.py +86 -184
  19. lionagi/protocols/messages/action_response.py +73 -131
  20. lionagi/protocols/messages/assistant_response.py +130 -159
  21. lionagi/protocols/messages/base.py +26 -18
  22. lionagi/protocols/messages/instruction.py +281 -625
  23. lionagi/protocols/messages/manager.py +112 -62
  24. lionagi/protocols/messages/message.py +87 -197
  25. lionagi/protocols/messages/system.py +52 -123
  26. lionagi/protocols/types.py +0 -2
  27. lionagi/service/connections/endpoint.py +0 -8
  28. lionagi/service/connections/providers/oai_.py +29 -94
  29. lionagi/service/connections/providers/ollama_.py +3 -2
  30. lionagi/service/hooks/hooked_event.py +2 -2
  31. lionagi/service/third_party/claude_code.py +3 -2
  32. lionagi/service/third_party/openai_models.py +433 -0
  33. lionagi/session/branch.py +170 -178
  34. lionagi/session/session.py +3 -9
  35. lionagi/tools/file/reader.py +2 -2
  36. lionagi/version.py +1 -1
  37. {lionagi-0.17.11.dist-info → lionagi-0.18.0.dist-info}/METADATA +1 -2
  38. {lionagi-0.17.11.dist-info → lionagi-0.18.0.dist-info}/RECORD +41 -49
  39. lionagi/operations/_act/act.py +0 -86
  40. lionagi/protocols/messages/templates/README.md +0 -28
  41. lionagi/protocols/messages/templates/action_request.jinja2 +0 -5
  42. lionagi/protocols/messages/templates/action_response.jinja2 +0 -9
  43. lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -6
  44. lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -61
  45. lionagi/protocols/messages/templates/system_message.jinja2 +0 -11
  46. lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -7
  47. lionagi/service/connections/providers/types.py +0 -28
  48. lionagi/service/third_party/openai_model_names.py +0 -198
  49. lionagi/service/types.py +0 -58
  50. /lionagi/operations/{_act → act}/__init__.py +0 -0
  51. {lionagi-0.17.11.dist-info → lionagi-0.18.0.dist-info}/WHEEL +0 -0
  52. {lionagi-0.17.11.dist-info → lionagi-0.18.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import orjson
6
+ import yaml
7
+
8
+ # --- YAML Dumper with minimal, readable settings --------------------------------
9
+
10
+
11
+ class MinimalDumper(yaml.SafeDumper):
12
+ # Disable anchors/aliases (&id001, *id001) for repeated objects.
13
+ def ignore_aliases(self, data: Any) -> bool: # type: ignore[override]
14
+ return True
15
+
16
+
17
+ def _represent_str(dumper: yaml.SafeDumper, data: str):
18
+ # Use block scalars for multiline text; plain style otherwise.
19
+ if "\n" in data:
20
+ return dumper.represent_scalar(
21
+ "tag:yaml.org,2002:str", data, style="|"
22
+ )
23
+ return dumper.represent_scalar("tag:yaml.org,2002:str", data)
24
+
25
+
26
+ MinimalDumper.add_representer(str, _represent_str)
27
+
28
+ # --- Optional pruning of empty values -------------------------------------------
29
+
30
+
31
+ def _is_empty(x: Any) -> bool:
32
+ """
33
+ Define 'empty' for pruning. Keeps 0 and False.
34
+ - None or '' (after strip) are empty
35
+ - Empty containers are empty
36
+ """
37
+ if x is None:
38
+ return True
39
+ if isinstance(x, str):
40
+ return x.strip() == ""
41
+ if isinstance(x, dict):
42
+ return len(x) == 0
43
+ if isinstance(x, (list, tuple, set)):
44
+ return len(x) == 0
45
+ # Keep numbers (including 0) and booleans (including False)
46
+ return False
47
+
48
+
49
+ def _prune(x: Any) -> Any:
50
+ """Recursively remove empty leaves and empty containers produced thereby."""
51
+ if isinstance(x, dict):
52
+ pruned = {k: _prune(v) for k, v in x.items() if not _is_empty(v)}
53
+ # Remove keys that became empty after recursion
54
+ return {k: v for k, v in pruned.items() if not _is_empty(v)}
55
+ if isinstance(x, list):
56
+ pruned_list = [_prune(v) for v in x if not _is_empty(v)]
57
+ return [v for v in pruned_list if not _is_empty(v)]
58
+ if isinstance(x, tuple):
59
+ pruned_list = [_prune(v) for v in x if not _is_empty(v)]
60
+ return tuple(v for v in pruned_list if not _is_empty(v))
61
+ if isinstance(x, set):
62
+ pruned_set = {_prune(v) for v in x if not _is_empty(v)}
63
+ return {v for v in pruned_set if not _is_empty(v)}
64
+ return x
65
+
66
+
67
+ # --- Public API ------------------------------------------------------------------
68
+
69
+
70
+ def minimal_yaml(
71
+ value: Any,
72
+ *,
73
+ drop_empties: bool = True,
74
+ indent: int = 2,
75
+ line_width: int = 2**31 - 1, # avoid PyYAML inserting line-wraps
76
+ sort_keys: bool = False,
77
+ ) -> str:
78
+ """
79
+ Convert any Python value (dict/list/scalars) to a minimal, readable YAML string.
80
+ - Lists -> YAML sequences with '- '
81
+ - Dicts -> 'key: value' mappings
82
+ - Multiline strings -> block scalars (|)
83
+ - Optional pruning of empty values (keeps 0 and False)
84
+ - No aliases/anchors
85
+ """
86
+ if isinstance(value, str):
87
+ value = orjson.loads(value)
88
+
89
+ data = _prune(value) if drop_empties else value
90
+ return yaml.dump(
91
+ data,
92
+ Dumper=MinimalDumper,
93
+ default_flow_style=False, # block style
94
+ sort_keys=sort_keys, # preserve insertion order
95
+ allow_unicode=True,
96
+ indent=indent,
97
+ width=line_width,
98
+ )
lionagi/ln/types.py CHANGED
@@ -232,10 +232,13 @@ class Params:
232
232
  dict_.update(kw_)
233
233
  return dict_
234
234
 
235
- def to_dict(self) -> dict[str, str]:
235
+ def to_dict(self, exclude: set[str] = None) -> dict[str, str]:
236
236
  data = {}
237
+ exclude = exclude or set()
237
238
  for k in self.allowed():
238
- if not self._is_sentinel(v := getattr(self, k, Undefined)):
239
+ if k not in exclude and not self._is_sentinel(
240
+ v := getattr(self, k, Undefined)
241
+ ):
239
242
  data[k] = v
240
243
  return data
241
244
 
@@ -249,6 +252,12 @@ class Params:
249
252
  return False
250
253
  return hash(self) == hash(other)
251
254
 
255
+ def with_updates(self, **kwargs: Any) -> DataClass:
256
+ """Return a new instance with updated fields."""
257
+ dict_ = self.to_dict()
258
+ dict_.update(kwargs)
259
+ return type(self)(**dict_)
260
+
252
261
 
253
262
  @dataclass(slots=True)
254
263
  class DataClass:
@@ -296,11 +305,13 @@ class DataClass:
296
305
  for k in self.allowed():
297
306
  _validate_strict(k)
298
307
 
299
- def to_dict(self) -> dict[str, str]:
308
+ def to_dict(self, exclude: set[str] = None) -> dict[str, str]:
300
309
  data = {}
301
- print(self.allowed())
310
+ exclude = exclude or set()
302
311
  for k in type(self).allowed():
303
- if not self._is_sentinel(v := getattr(self, k)):
312
+ if k not in exclude and not self._is_sentinel(
313
+ v := getattr(self, k)
314
+ ):
304
315
  data[k] = v
305
316
  return data
306
317
 
@@ -311,6 +322,22 @@ class DataClass:
311
322
  return True
312
323
  return is_sentinel(value)
313
324
 
325
+ def with_updates(self, **kwargs: Any) -> DataClass:
326
+ """Return a new instance with updated fields."""
327
+ dict_ = self.to_dict()
328
+ dict_.update(kwargs)
329
+ return type(self)(**dict_)
330
+
331
+ def __hash__(self) -> int:
332
+ from ._hash import hash_dict
333
+
334
+ return hash_dict(self.to_dict())
335
+
336
+ def __eq__(self, other: Any) -> bool:
337
+ if not isinstance(other, DataClass):
338
+ return False
339
+ return hash(self) == hash(other)
340
+
314
341
 
315
342
  KeysLike = Sequence[str] | KeysDict
316
343
 
@@ -513,6 +513,15 @@ class FieldModel(Params):
513
513
  # These are FieldTemplate markers, don't pass to FieldInfo
514
514
  pass
515
515
  else:
516
+ # Filter out unserializable objects from json_schema_extra
517
+ # to avoid Pydantic serialization errors when generating JSON schema
518
+ from pydantic import BaseModel
519
+
520
+ # Skip model classes and other unserializable types
521
+ if isinstance(meta.value, type):
522
+ # Skip type objects (including model classes) - they can't be serialized
523
+ continue
524
+
516
525
  # Any other metadata goes in json_schema_extra
517
526
  if "json_schema_extra" not in field_kwargs:
518
527
  field_kwargs["json_schema_extra"] = {}