lionagi 0.15.14__py3-none-any.whl → 0.16.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 (53) hide show
  1. lionagi/adapters/async_postgres_adapter.py +1 -1
  2. lionagi/libs/nested/ninsert.py +3 -6
  3. lionagi/libs/nested/nmerge.py +1 -2
  4. lionagi/libs/validate/fuzzy_match_keys.py +5 -182
  5. lionagi/libs/validate/string_similarity.py +6 -331
  6. lionagi/ln/__init__.py +56 -66
  7. lionagi/ln/_async_call.py +13 -10
  8. lionagi/ln/_hash.py +33 -8
  9. lionagi/ln/_list_call.py +2 -35
  10. lionagi/ln/_to_list.py +51 -28
  11. lionagi/ln/_utils.py +156 -0
  12. lionagi/ln/concurrency/__init__.py +39 -31
  13. lionagi/ln/concurrency/_compat.py +65 -0
  14. lionagi/ln/concurrency/cancel.py +92 -109
  15. lionagi/ln/concurrency/errors.py +17 -17
  16. lionagi/ln/concurrency/patterns.py +249 -206
  17. lionagi/ln/concurrency/primitives.py +257 -216
  18. lionagi/ln/concurrency/resource_tracker.py +42 -155
  19. lionagi/ln/concurrency/task.py +55 -73
  20. lionagi/ln/concurrency/throttle.py +3 -0
  21. lionagi/ln/concurrency/utils.py +1 -0
  22. lionagi/ln/fuzzy/__init__.py +15 -0
  23. lionagi/ln/{_extract_json.py → fuzzy/_extract_json.py} +22 -9
  24. lionagi/ln/{_fuzzy_json.py → fuzzy/_fuzzy_json.py} +14 -8
  25. lionagi/ln/fuzzy/_fuzzy_match.py +172 -0
  26. lionagi/ln/fuzzy/_fuzzy_validate.py +46 -0
  27. lionagi/ln/fuzzy/_string_similarity.py +332 -0
  28. lionagi/ln/{_models.py → types.py} +153 -4
  29. lionagi/operations/ReAct/utils.py +1 -2
  30. lionagi/operations/flow.py +2 -1
  31. lionagi/operations/operate/operate.py +26 -16
  32. lionagi/protocols/action/function_calling.py +1 -4
  33. lionagi/protocols/contracts.py +46 -0
  34. lionagi/protocols/generic/element.py +1 -58
  35. lionagi/protocols/generic/event.py +6 -6
  36. lionagi/protocols/generic/processor.py +9 -5
  37. lionagi/protocols/graph/graph.py +1 -2
  38. lionagi/protocols/graph/node.py +2 -4
  39. lionagi/protocols/ids.py +82 -0
  40. lionagi/protocols/messages/instruction.py +1 -2
  41. lionagi/protocols/messages/manager.py +1 -2
  42. lionagi/protocols/messages/message.py +1 -4
  43. lionagi/protocols/types.py +10 -12
  44. lionagi/service/connections/providers/claude_code_.py +1 -2
  45. lionagi/service/resilience.py +1 -2
  46. lionagi/tools/memory/tools.py +2 -4
  47. lionagi/utils.py +34 -64
  48. lionagi/version.py +1 -1
  49. {lionagi-0.15.14.dist-info → lionagi-0.16.1.dist-info}/METADATA +4 -2
  50. {lionagi-0.15.14.dist-info → lionagi-0.16.1.dist-info}/RECORD +52 -45
  51. lionagi/ln/_types.py +0 -146
  52. {lionagi-0.15.14.dist-info → lionagi-0.16.1.dist-info}/WHEEL +0 -0
  53. {lionagi-0.15.14.dist-info → lionagi-0.16.1.dist-info}/licenses/LICENSE +0 -0
@@ -18,8 +18,7 @@ class PlannedAction(HashableModel):
18
18
  action_type: str | None = Field(
19
19
  default=None,
20
20
  description=(
21
- "The name or type of tool/action to invoke. "
22
- "(e.g., 'search_exa', 'reader_tool')"
21
+ "The name or type of tool/action to invoke. (e.g., 'search_exa', 'reader_tool')"
23
22
  ),
24
23
  )
25
24
  description: str | None = Field(
@@ -12,7 +12,8 @@ using Events for synchronization and CapacityLimiter for concurrency control.
12
12
  import os
13
13
  from typing import Any
14
14
 
15
- from lionagi.ln import AlcallParams, CapacityLimiter, ConcurrencyEvent
15
+ from lionagi.ln._async_call import AlcallParams
16
+ from lionagi.ln.concurrency import CapacityLimiter, ConcurrencyEvent
16
17
  from lionagi.operations.node import Operation
17
18
  from lionagi.protocols.types import EventStatus, Graph
18
19
  from lionagi.session.branch import Branch
@@ -23,6 +23,29 @@ if TYPE_CHECKING:
23
23
  from lionagi.session.branch import Branch
24
24
 
25
25
 
26
+ def _handle_response_format_kwargs(
27
+ operative_model: type[BaseModel] = None,
28
+ request_model: type[BaseModel] = None,
29
+ response_format: type[BaseModel] = None,
30
+ ):
31
+ if operative_model:
32
+ logging.warning(
33
+ "`operative_model` is deprecated. Use `response_format` instead."
34
+ )
35
+ if (
36
+ (operative_model and response_format)
37
+ or (operative_model and request_model)
38
+ or (response_format and request_model)
39
+ ):
40
+ raise ValueError(
41
+ "Cannot specify both `operative_model` and `response_format` (or `request_model`) "
42
+ "as they are aliases of each other."
43
+ )
44
+
45
+ # Use the final chosen format
46
+ return response_format or operative_model or request_model
47
+
48
+
26
49
  async def operate(
27
50
  branch: "Branch",
28
51
  *,
@@ -66,22 +89,9 @@ async def operate(
66
89
  include_token_usage_to_model: bool = False,
67
90
  **kwargs,
68
91
  ) -> list | BaseModel | None | dict | str:
69
- if operative_model:
70
- logging.warning(
71
- "`operative_model` is deprecated. Use `response_format` instead."
72
- )
73
- if (
74
- (operative_model and response_format)
75
- or (operative_model and request_model)
76
- or (response_format and request_model)
77
- ):
78
- raise ValueError(
79
- "Cannot specify both `operative_model` and `response_format` (or `request_model`) "
80
- "as they are aliases of each other."
81
- )
82
-
83
- # Use the final chosen format
84
- response_format = response_format or operative_model or request_model
92
+ response_format = _handle_response_format_kwargs(
93
+ operative_model, request_model, response_format
94
+ )
85
95
 
86
96
  # Decide which chat model to use
87
97
  chat_model = chat_model or imodel or branch.chat_model
@@ -130,10 +130,7 @@ class FunctionCalling(Event):
130
130
  Returns:
131
131
  A string containing the class name and key attributes.
132
132
  """
133
- return (
134
- f"FunctionCalling(function={self.func_tool.function}, "
135
- f"arguments={self.arguments})"
136
- )
133
+ return f"FunctionCalling(function={self.func_tool.function}, arguments={self.arguments})"
137
134
 
138
135
  def to_dict(self) -> dict[str, Any]:
139
136
  """Convert instance to dictionary.
@@ -0,0 +1,46 @@
1
+ # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """V1 Observable Protocol for gradual evolution.
6
+
7
+ This module provides the runtime-checkable ObservableProto for V1 components
8
+ while maintaining compatibility with V0's nominal Observable ABC.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Protocol, runtime_checkable
14
+
15
+ __all__ = (
16
+ "ObservableProto",
17
+ "Observable",
18
+ "LegacyObservable",
19
+ )
20
+
21
+
22
+ @runtime_checkable
23
+ class ObservableProto(Protocol):
24
+ """Structural Observable Protocol for V1 components.
25
+
26
+ This protocol defines the minimal contract for observable objects:
27
+ they must have an 'id' property. The return type is permissive (Any)
28
+ to maintain compatibility with V0's IDType wrapper while enabling
29
+ V1 evolution.
30
+
31
+ All V0 Element subclasses automatically satisfy this protocol without
32
+ any code changes, enabling zero-risk gradual migration.
33
+ """
34
+
35
+ @property
36
+ def id(self) -> object:
37
+ """Unique identifier. Accepts IDType, UUID, or string."""
38
+ ...
39
+
40
+
41
+ # Convenience alias for V1 consumers (keeps import names short)
42
+ Observable = ObservableProto
43
+
44
+ # Keep legacy nominal ABC for places that need issubclass checks (e.g., Pile)
45
+ # Do NOT remove – Pile and others rely on issubclass(..., Observable) nominal checks.
46
+ from ._concepts import Observable as LegacyObservable
@@ -313,7 +313,6 @@ class Element(BaseModel, Observable):
313
313
  if mode == "db":
314
314
  dict_ = orjson.loads(self.to_json(decode=False))
315
315
  dict_["node_metadata"] = dict_.pop("metadata", {})
316
- dict_["created_at"] = self.created_datetime.isoformat(sep=" ")
317
316
  return dict_
318
317
 
319
318
  def as_jsonable(self) -> dict:
@@ -321,19 +320,13 @@ class Element(BaseModel, Observable):
321
320
  return self.to_dict(mode="json")
322
321
 
323
322
  @classmethod
324
- def from_dict(cls, data: dict, /, mode: str = "python") -> Element:
323
+ def from_dict(cls, data: dict) -> Element:
325
324
  """Deserializes a dictionary into an Element or subclass of Element.
326
325
 
327
326
  If `lion_class` in `metadata` refers to a subclass, this method
328
327
  is polymorphic, it will attempt to create an instance of that subclass.
329
-
330
- Args:
331
- data (dict): A dictionary of field data.
332
- mode (str): Format mode - "python" for normal dicts, "db" for database format.
333
328
  """
334
329
  # Preprocess database format if needed
335
- if mode == "db":
336
- data = cls._preprocess_db_data(data.copy())
337
330
  metadata = {}
338
331
 
339
332
  if "node_metadata" in data:
@@ -367,56 +360,6 @@ class Element(BaseModel, Observable):
367
360
  data["metadata"] = metadata
368
361
  return cls.model_validate(data)
369
362
 
370
- @classmethod
371
- def _preprocess_db_data(cls, data: dict) -> dict:
372
- """Preprocess raw database data for Element compatibility."""
373
- import datetime as dt
374
- import json
375
-
376
- # Handle created_at field - convert datetime string to timestamp
377
- if "created_at" in data and isinstance(data["created_at"], str):
378
- try:
379
- # Parse datetime string and convert to timestamp
380
- dt_obj = dt.datetime.fromisoformat(
381
- data["created_at"].replace(" ", "T")
382
- )
383
- # Treat as UTC if naive
384
- if dt_obj.tzinfo is None:
385
- dt_obj = dt_obj.replace(tzinfo=dt.timezone.utc)
386
- data["created_at"] = dt_obj.timestamp()
387
- except (ValueError, TypeError):
388
- # Keep as string if parsing fails
389
- pass
390
-
391
- # Handle JSON string fields - parse to dict/list
392
- json_fields = ["content", "node_metadata", "embedding"]
393
- for field in json_fields:
394
- if field in data and isinstance(data[field], str):
395
- if data[field] in ("null", ""):
396
- data[field] = None if field == "embedding" else {}
397
- else:
398
- try:
399
- data[field] = json.loads(data[field])
400
- except (json.JSONDecodeError, TypeError):
401
- # Keep as empty dict for metadata fields, None for embedding
402
- data[field] = {} if field != "embedding" else None
403
-
404
- # Handle node_metadata -> metadata mapping
405
- if "node_metadata" in data:
406
- if (
407
- data["node_metadata"] == "null"
408
- or data["node_metadata"] is None
409
- ):
410
- data["metadata"] = {}
411
- else:
412
- data["metadata"] = (
413
- data["node_metadata"] if data["node_metadata"] else {}
414
- )
415
- # Remove node_metadata to avoid Pydantic validation error
416
- data.pop("node_metadata", None)
417
-
418
- return data
419
-
420
363
  def to_json(self, decode: bool = True) -> str:
421
364
  """Converts this Element to a JSON string."""
422
365
  dict_ = self._to_dict()
@@ -5,13 +5,13 @@
5
5
  from __future__ import annotations
6
6
 
7
7
  import contextlib
8
- from enum import Enum
8
+ from enum import Enum as _Enum
9
9
  from typing import Any
10
10
 
11
11
  from pydantic import Field, field_serializer
12
12
 
13
13
  from lionagi import ln
14
- from lionagi.utils import to_dict
14
+ from lionagi.utils import Unset, to_dict
15
15
 
16
16
  from .element import Element
17
17
 
@@ -22,10 +22,10 @@ __all__ = (
22
22
  )
23
23
 
24
24
 
25
- _SIMPLE_TYPE = (str, bytes, bytearray, int, float, type(None), Enum)
25
+ _SIMPLE_TYPE = (str, bytes, bytearray, int, float, type(None), _Enum)
26
26
 
27
27
 
28
- class EventStatus(str, ln.Enum):
28
+ class EventStatus(str, ln.types.Enum):
29
29
  """Status states for tracking action execution progress.
30
30
 
31
31
  Attributes:
@@ -96,7 +96,7 @@ class Execution:
96
96
  Returns:
97
97
  dict: A dictionary representation of the execution state.
98
98
  """
99
- res_ = ln.Unset
99
+ res_ = Unset
100
100
  json_serializable = True
101
101
 
102
102
  if not isinstance(self.response, _SIMPLE_TYPE):
@@ -119,7 +119,7 @@ class Execution:
119
119
  res_ = d_
120
120
  json_serializable = True
121
121
 
122
- if res_ is ln.Unset and not json_serializable:
122
+ if res_ is Unset and not json_serializable:
123
123
  res_ = "<unserializable>"
124
124
 
125
125
  return {
@@ -5,7 +5,11 @@
5
5
  import asyncio
6
6
  from typing import Any, ClassVar
7
7
 
8
- from lionagi.ln import ConcurrencyEvent, Semaphore, create_task_group
8
+ from lionagi.ln.concurrency import (
9
+ ConcurrencyEvent,
10
+ Semaphore,
11
+ create_task_group,
12
+ )
9
13
 
10
14
  from .._concepts import Observer
11
15
  from .element import ID
@@ -166,9 +170,9 @@ class Processor(Observer):
166
170
  async with self._concurrency_sem:
167
171
  await consume_stream(event)
168
172
 
169
- await tg.start_soon(stream_with_sem, next_event)
173
+ tg.start_soon(stream_with_sem, next_event)
170
174
  else:
171
- await tg.start_soon(consume_stream, next_event)
175
+ tg.start_soon(consume_stream, next_event)
172
176
  else:
173
177
  # For non-streaming, just invoke
174
178
  if self._concurrency_sem:
@@ -177,9 +181,9 @@ class Processor(Observer):
177
181
  async with self._concurrency_sem:
178
182
  await event.invoke()
179
183
 
180
- await tg.start_soon(invoke_with_sem, next_event)
184
+ tg.start_soon(invoke_with_sem, next_event)
181
185
  else:
182
- await tg.start_soon(next_event.invoke)
186
+ tg.start_soon(next_event.invoke)
183
187
  events_processed += 1
184
188
 
185
189
  prev_event = next_event
@@ -87,8 +87,7 @@ class Graph(Element, Relational, Generic[T]):
87
87
  or edge.tail not in self.internal_nodes
88
88
  ):
89
89
  raise RelationError(
90
- "Failed to add edge: Either edge head or tail node does"
91
- " not exist in the graph."
90
+ "Failed to add edge: Either edge head or tail node does not exist in the graph."
92
91
  )
93
92
  try:
94
93
  self.internal_edges.insert(len(self.internal_edges), edge)
@@ -63,7 +63,7 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
63
63
  self, obj_key: str, many=False, **kwargs: Any
64
64
  ) -> Any:
65
65
  kwargs["adapt_meth"] = "to_dict"
66
- kwargs["mode"] = "db"
66
+ kwargs["adapt_kw"] = {"mode": "db"}
67
67
  return await super().adapt_to_async(
68
68
  obj_key=obj_key, many=many, **kwargs
69
69
  )
@@ -77,7 +77,6 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
77
77
  **kwargs: Any,
78
78
  ) -> Node:
79
79
  kwargs["adapt_meth"] = "from_dict"
80
- kwargs["mode"] = "db"
81
80
  return await super().adapt_from_async(
82
81
  obj, obj_key=obj_key, many=many, **kwargs
83
82
  )
@@ -87,7 +86,7 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
87
86
  Convert this Node to another format using a registered adapter.
88
87
  """
89
88
  kwargs["adapt_meth"] = "to_dict"
90
- kwargs["mode"] = "db"
89
+ kwargs["adapt_kw"] = {"mode": "db"}
91
90
  return super().adapt_to(obj_key=obj_key, many=many, **kwargs)
92
91
 
93
92
  @classmethod
@@ -104,7 +103,6 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
104
103
  auto-delegate to the correct subclass via from_dict.
105
104
  """
106
105
  kwargs["adapt_meth"] = "from_dict"
107
- kwargs["mode"] = "db"
108
106
  return super().adapt_from(obj, obj_key=obj_key, many=many, **kwargs)
109
107
 
110
108
  @field_serializer("content")
@@ -0,0 +1,82 @@
1
+ # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """ID bridge utilities for V0/V1 compatibility.
6
+
7
+ This module provides utilities to convert between V0's IDType and V1's
8
+ canonical UUID representation, enabling seamless interoperability during
9
+ the gradual evolution process.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+ from uuid import UUID
16
+
17
+ from .generic.element import Element, IDType
18
+
19
+ __all__ = (
20
+ "to_uuid",
21
+ "canonical_id",
22
+ )
23
+
24
+
25
+ def to_uuid(value: Any) -> UUID:
26
+ """Convert ID-like values (IDType | UUID | str | Element) to UUID (v4).
27
+
28
+ Optimized version that avoids string conversion when possible by directly
29
+ accessing IDType's internal UUID. Falls back to V0's IDType.validate()
30
+ for validation semantics only when necessary.
31
+
32
+ Args:
33
+ value: An ID-like value to convert (IDType, UUID, str, or Element)
34
+
35
+ Returns:
36
+ UUID: A validated UUIDv4
37
+
38
+ Raises:
39
+ IDError: If the value cannot be converted to a valid UUIDv4
40
+
41
+ Examples:
42
+ >>> element = Element()
43
+ >>> uuid_val = to_uuid(element)
44
+ >>> isinstance(uuid_val, UUID)
45
+ True
46
+ >>> to_uuid("550e8400-e29b-41d4-a716-446655440000")
47
+ UUID('550e8400-e29b-41d4-a716-446655440000')
48
+ """
49
+ if isinstance(value, Element):
50
+ return value.id._id
51
+ if isinstance(value, UUID):
52
+ return value
53
+ if hasattr(value, "_id") and isinstance(value._id, UUID):
54
+ return value._id
55
+ # Fallback: Validate then access ._id directly (no string conversion)
56
+ validated_id = IDType.validate(value)
57
+ return validated_id._id
58
+
59
+
60
+ def canonical_id(obj: Any) -> UUID:
61
+ """Accept an Observable-like object or raw ID and return canonical UUID.
62
+
63
+ Safe to use across V0/V1 without changing class definitions. Prefers
64
+ attribute access (.id) but falls back to treating the object as a raw ID.
65
+
66
+ Args:
67
+ obj: An Observable object with .id attribute, or a raw ID value
68
+
69
+ Returns:
70
+ UUID: The canonical UUID representation
71
+
72
+ Examples:
73
+ >>> element = Element()
74
+ >>> uuid_val = canonical_id(element)
75
+ >>> isinstance(uuid_val, UUID)
76
+ True
77
+ >>> canonical_id("550e8400-e29b-41d4-a716-446655440000")
78
+ UUID('550e8400-e29b-41d4-a716-446655440000')
79
+ """
80
+ # Prefer attribute access; fall back to treating obj as a raw id
81
+ id_like = getattr(obj, "id", obj)
82
+ return to_uuid(id_like)
@@ -587,8 +587,7 @@ class Instruction(RoledMessage):
587
587
 
588
588
  if request_model and request_fields:
589
589
  raise ValueError(
590
- "You cannot pass both request_model and request_fields "
591
- "to create_instruction"
590
+ "You cannot pass both request_model and request_fields to create_instruction"
592
591
  )
593
592
  if guidance:
594
593
  self.guidance = guidance
@@ -216,8 +216,7 @@ class MessageManager(Manager):
216
216
  """
217
217
  if not isinstance(action_request, ActionRequest):
218
218
  raise ValueError(
219
- "Error: please provide a corresponding action request for an "
220
- "action response."
219
+ "Error: please provide a corresponding action request for an action response."
221
220
  )
222
221
  params = {
223
222
  "action_request": action_request,
@@ -241,10 +241,7 @@ class RoledMessage(Node, Sendable):
241
241
  if len(str(self.content)) > 75
242
242
  else str(self.content)
243
243
  )
244
- return (
245
- f"Message(role={self.role}, sender={self.sender}, "
246
- f"content='{content_preview}')"
247
- )
244
+ return f"Message(role={self.role}, sender={self.sender}, content='{content_preview}')"
248
245
 
249
246
 
250
247
  # File: lionagi/protocols/messages/message.py
@@ -2,18 +2,11 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- from ._concepts import (
6
- Collective,
7
- Communicatable,
8
- Condition,
9
- Manager,
10
- Observable,
11
- Observer,
12
- Ordering,
13
- Relational,
14
- Sendable,
15
- )
5
+ from ._concepts import Collective, Communicatable, Condition, Manager
6
+ from ._concepts import Observable as LegacyObservable
7
+ from ._concepts import Observer, Ordering, Relational, Sendable
16
8
  from .action.manager import ActionManager, FunctionCalling, Tool, ToolRef
9
+ from .contracts import Observable, ObservableProto
17
10
  from .forms.flow import FlowDefinition, FlowStep
18
11
  from .forms.report import BaseForm, Form, Report
19
12
  from .generic.element import ID, Element, IDError, IDType, validate_order
@@ -30,6 +23,7 @@ from .generic.processor import Executor, Processor
30
23
  from .generic.progression import Progression, prog
31
24
  from .graph.edge import EdgeCondition
32
25
  from .graph.graph import Edge, Graph, Node
26
+ from .ids import canonical_id, to_uuid
33
27
  from .mail.exchange import Exchange, Mail, Mailbox, Package, PackageCategory
34
28
  from .mail.manager import MailManager
35
29
  from .messages.base import (
@@ -56,11 +50,15 @@ __all__ = (
56
50
  "Communicatable",
57
51
  "Condition",
58
52
  "Manager",
59
- "Observable",
53
+ "Observable", # V1 Protocol (preferred)
54
+ "ObservableProto", # Explicit V1 Protocol name
55
+ "LegacyObservable", # V0 ABC (deprecated)
60
56
  "Observer",
61
57
  "Ordering",
62
58
  "Relational",
63
59
  "Sendable",
60
+ "canonical_id", # V0/V1 bridge utility
61
+ "to_uuid", # ID conversion utility
64
62
  "ID",
65
63
  "Element",
66
64
  "IDError",
@@ -56,8 +56,7 @@ class ClaudeCodeEndpoint(Endpoint):
56
56
  "Please install it with `uv pip install lionagi[claude_code_sdk]`."
57
57
  )
58
58
  warnings.warn(
59
- "The claude_code `query` endpoint is deprecated. "
60
- "Use `query_cli` endpoint instead.",
59
+ "The claude_code `query` endpoint is deprecated. Use `query_cli` endpoint instead.",
61
60
  DeprecationWarning,
62
61
  )
63
62
 
@@ -216,8 +216,7 @@ class CircuitBreaker:
216
216
  self._metrics["rejected_count"] += 1
217
217
 
218
218
  logger.warning(
219
- f"Circuit '{self.name}' is HALF_OPEN and at capacity. "
220
- f"Try again later."
219
+ f"Circuit '{self.name}' is HALF_OPEN and at capacity. Try again later."
221
220
  )
222
221
 
223
222
  return False
@@ -63,8 +63,7 @@ class MemoryRequest(BaseModel):
63
63
  content: str | None = Field(
64
64
  None,
65
65
  description=(
66
- "Content to store. REQUIRED if action='store'. "
67
- "For other actions, leave it None."
66
+ "Content to store. REQUIRED if action='store'. For other actions, leave it None."
68
67
  ),
69
68
  )
70
69
 
@@ -96,8 +95,7 @@ class MemoryRequest(BaseModel):
96
95
  query: str | None = Field(
97
96
  None,
98
97
  description=(
99
- "Query text for semantic search. "
100
- "REQUIRED for actions: 'recall', 'search', 'explore'."
98
+ "Query text for semantic search. REQUIRED for actions: 'recall', 'search', 'explore'."
101
99
  ),
102
100
  )
103
101