lionagi 0.13.2__py3-none-any.whl → 0.13.3__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 (44) hide show
  1. lionagi/fields/action.py +0 -1
  2. lionagi/fields/reason.py +0 -1
  3. lionagi/libs/file/save.py +1 -1
  4. lionagi/libs/schema/as_readable.py +142 -196
  5. lionagi/libs/schema/extract_docstring.py +1 -2
  6. lionagi/libs/token_transform/synthlang_/base.py +0 -2
  7. lionagi/libs/validate/string_similarity.py +1 -2
  8. lionagi/models/hashable_model.py +0 -1
  9. lionagi/models/schema_model.py +0 -1
  10. lionagi/operations/ReAct/utils.py +0 -1
  11. lionagi/operations/_act/act.py +0 -1
  12. lionagi/operations/interpret/interpret.py +1 -4
  13. lionagi/operations/manager.py +0 -1
  14. lionagi/operations/plan/plan.py +0 -1
  15. lionagi/operations/select/utils.py +0 -2
  16. lionagi/protocols/forms/flow.py +3 -1
  17. lionagi/protocols/generic/pile.py +1 -2
  18. lionagi/protocols/generic/processor.py +0 -1
  19. lionagi/protocols/graph/graph.py +1 -3
  20. lionagi/protocols/mail/package.py +0 -1
  21. lionagi/protocols/messages/assistant_response.py +0 -2
  22. lionagi/protocols/messages/message.py +0 -1
  23. lionagi/service/connections/endpoint_config.py +6 -0
  24. lionagi/service/connections/match_endpoint.py +26 -8
  25. lionagi/service/connections/providers/claude_code_.py +8 -9
  26. lionagi/service/connections/providers/claude_code_cli.py +414 -0
  27. lionagi/service/connections/providers/oai_.py +1 -1
  28. lionagi/service/manager.py +0 -1
  29. lionagi/service/rate_limited_processor.py +0 -2
  30. lionagi/service/token_calculator.py +0 -3
  31. lionagi/session/branch.py +0 -2
  32. lionagi/session/session.py +0 -1
  33. lionagi/settings.py +0 -1
  34. lionagi/utils.py +6 -9
  35. lionagi/version.py +1 -1
  36. {lionagi-0.13.2.dist-info → lionagi-0.13.3.dist-info}/METADATA +5 -3
  37. {lionagi-0.13.2.dist-info → lionagi-0.13.3.dist-info}/RECORD +39 -43
  38. lionagi/traits/__init__.py +0 -58
  39. lionagi/traits/base.py +0 -216
  40. lionagi/traits/composer.py +0 -343
  41. lionagi/traits/protocols.py +0 -495
  42. lionagi/traits/registry.py +0 -1071
  43. {lionagi-0.13.2.dist-info → lionagi-0.13.3.dist-info}/WHEEL +0 -0
  44. {lionagi-0.13.2.dist-info → lionagi-0.13.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,343 +0,0 @@
1
- """
2
- TraitComposer - Algebraic trait composition with LRU caching.
3
-
4
- This module provides the TraitComposer class that enables algebraic composition
5
- of traits with + and & operators, backed by LRU caching to achieve the
6
- research target of <10μs model generation.
7
-
8
- Performance characteristics:
9
- - LRU cache with configurable size (default: 512 entries)
10
- - <10μs model generation target (research validated)
11
- - Algebraic composition operations (union, intersection)
12
- - Thread-safe composition with proper dependency resolution
13
- """
14
-
15
- from __future__ import annotations
16
-
17
- import functools
18
- import threading
19
- import time
20
- from dataclasses import dataclass, field
21
- from typing import Any, ClassVar
22
-
23
- from .base import Trait
24
- from .registry import get_global_registry
25
-
26
- __all__ = ["CompositionError", "TraitComposer", "TraitComposition"]
27
-
28
-
29
- class CompositionError(Exception):
30
- """Raised when trait composition fails due to conflicts or missing dependencies."""
31
-
32
- pass
33
-
34
-
35
- @dataclass(frozen=True, slots=True)
36
- class TraitComposition:
37
- """
38
- Immutable representation of a trait composition.
39
-
40
- Supports algebraic operations and caching for fast model generation.
41
- """
42
-
43
- traits: frozenset[Trait]
44
- dependencies: frozenset[Trait] = field(default_factory=frozenset)
45
- composition_id: str = field(default="")
46
-
47
- def __post_init__(self) -> None:
48
- """Calculate composition ID and dependencies."""
49
- if not self.composition_id:
50
- # Generate deterministic ID from sorted trait names
51
- sorted_names = sorted(trait.name for trait in self.traits)
52
- object.__setattr__(self, "composition_id", "+".join(sorted_names))
53
-
54
- if not self.dependencies:
55
- # Calculate all dependencies
56
- registry = get_global_registry()
57
- all_deps: set[Trait] = set()
58
-
59
- for trait in self.traits:
60
- trait_def = registry.get_trait_definition(trait)
61
- if trait_def:
62
- all_deps.update(trait_def.dependencies)
63
-
64
- object.__setattr__(self, "dependencies", frozenset(all_deps))
65
-
66
- def __add__(self, other: TraitComposition | Trait) -> TraitComposition:
67
- """Union composition (A + B contains traits from both A and B)."""
68
- if isinstance(other, Trait):
69
- other = TraitComposition(traits=frozenset([other]))
70
-
71
- return TraitComposition(traits=self.traits | other.traits)
72
-
73
- def __and__(self, other: TraitComposition | Trait) -> TraitComposition:
74
- """Intersection composition (A & B contains only common traits)."""
75
- if isinstance(other, Trait):
76
- other = TraitComposition(traits=frozenset([other]))
77
-
78
- return TraitComposition(traits=self.traits & other.traits)
79
-
80
- def __or__(self, other: TraitComposition | Trait) -> TraitComposition:
81
- """Alias for union (same as +)."""
82
- return self.__add__(other)
83
-
84
- def __hash__(self) -> int: # type: ignore[explicit-override]
85
- """Hash based on trait set for caching."""
86
- return hash(self.traits)
87
-
88
- def __repr__(self) -> str: # type: ignore[explicit-override]
89
- """String representation for debugging."""
90
- trait_names = sorted(trait.name for trait in self.traits)
91
- return f"TraitComposition({'+'.join(trait_names)})"
92
-
93
- def is_valid(self) -> bool:
94
- """Check if all dependencies are satisfied."""
95
- missing = self.dependencies - self.traits
96
- return len(missing) == 0
97
-
98
- def get_missing_dependencies(self) -> frozenset[Trait]:
99
- """Get traits that are required but missing."""
100
- return self.dependencies - self.traits
101
-
102
- def with_dependencies(self) -> TraitComposition:
103
- """Return a new composition that includes all dependencies."""
104
- return TraitComposition(traits=self.traits | self.dependencies)
105
-
106
-
107
- class TraitComposer:
108
- """
109
- High-performance trait composer with LRU caching.
110
-
111
- Provides algebraic trait composition with caching to achieve <10μs
112
- model generation as validated in research.
113
- """
114
-
115
- _instance: ClassVar[TraitComposer | None] = None
116
- _lock: ClassVar[threading.RLock] = threading.RLock()
117
- _compose_lock: ClassVar[threading.Lock] = (
118
- threading.Lock()
119
- ) # Guard for LRU cache miss path
120
-
121
- # Initialize class variables
122
- _protocol_cache: ClassVar[dict[Trait, type[Any]]] = {}
123
- _validated_compositions: ClassVar[set[frozenset[Trait]]] = set()
124
-
125
- def __init__(self, cache_size: int = 512) -> None:
126
- """Initialize composer with LRU cache."""
127
- self._cache_size = cache_size
128
- self._composition_cache: dict[frozenset[Trait], type[Any]] = {}
129
- self._cache_lock = threading.RLock()
130
- self._cache_hits = 0
131
- self._cache_misses = 0
132
- self._generation_count = 0
133
-
134
- @classmethod
135
- def get_instance(cls, cache_size: int = 512) -> TraitComposer:
136
- """Get singleton composer instance."""
137
- if cls._instance is None:
138
- with cls._lock:
139
- if cls._instance is None:
140
- cls._instance = cls(cache_size)
141
- return cls._instance
142
-
143
- @classmethod
144
- def reset(cls) -> None:
145
- """Reset singleton for testing."""
146
- with cls._lock:
147
- if cls._instance is not None:
148
- # Clear the LRU cache before resetting instance
149
- cls._instance.clear_cache()
150
- cls._protocol_cache.clear()
151
- cls._validated_compositions.clear()
152
- cls._instance = None
153
-
154
- def compose(self, *traits: Trait | TraitComposition) -> TraitComposition:
155
- """Compose multiple traits into a single composition."""
156
- all_traits = set()
157
-
158
- for trait in traits:
159
- if isinstance(trait, Trait):
160
- all_traits.add(trait)
161
- elif isinstance(trait, TraitComposition):
162
- all_traits.update(trait.traits)
163
- else:
164
- raise CompositionError(f"Invalid trait type: {type(trait)}")
165
-
166
- return TraitComposition(traits=frozenset(all_traits))
167
-
168
- # Pre-built empty mixin for performance
169
- _EMPTY_MIXIN = type("EmptyMixin", (), {"__slots__": ()})
170
-
171
- @functools.lru_cache(maxsize=512) # noqa: B019
172
- def _generate_model_cached(
173
- self, trait_tuple: tuple[Trait, ...]
174
- ) -> type[Any]:
175
- """Generate model class with LRU caching (internal method)."""
176
- # Only lock for the actual composition logic, not the entire method
177
- with TraitComposer._compose_lock:
178
- traits = frozenset(trait_tuple)
179
-
180
- # Performance optimization 1: Skip validation if previously validated
181
- composition = TraitComposition(traits=traits)
182
- if traits not in TraitComposer._validated_compositions:
183
- if not composition.is_valid():
184
- missing = composition.get_missing_dependencies()
185
- raise CompositionError(
186
- f"Missing dependencies: {[t.name for t in missing]}"
187
- )
188
- TraitComposer._validated_compositions.add(traits)
189
-
190
- # Generate unique class name with hash suffix to avoid collisions
191
- sorted_names = sorted(trait.name for trait in traits)
192
- # Use hash of the frozenset to ensure uniqueness even with different trait orders
193
- trait_hash = abs(hash(traits)) % 10000 # 4-digit hash suffix
194
- class_name = (
195
- f"Generated{''.join(sorted_names)}Model_{trait_hash:04d}"
196
- )
197
-
198
- # Performance optimization 2: Cache protocol lookups
199
- registry = get_global_registry()
200
- protocol_types = []
201
-
202
- for trait in traits:
203
- # Check cached protocols first
204
- if trait not in TraitComposer._protocol_cache:
205
- trait_def = registry.get_trait_definition(trait)
206
- if trait_def and trait_def.protocol_type:
207
- TraitComposer._protocol_cache[trait] = (
208
- trait_def.protocol_type
209
- )
210
-
211
- protocol_type = TraitComposer._protocol_cache.get(trait)
212
- if protocol_type:
213
- protocol_types.append(protocol_type)
214
-
215
- if not protocol_types:
216
- raise CompositionError(
217
- f"No protocol types found for traits: {sorted_names}"
218
- )
219
-
220
- # Performance optimization 3: Use pre-built empty mixin if only one protocol
221
- bases: tuple[type[Any], ...]
222
- if len(protocol_types) == 1:
223
- # Single inheritance is faster
224
- bases = (protocol_types[0], self._EMPTY_MIXIN)
225
- else:
226
- bases = tuple(protocol_types)
227
-
228
- # Create class with optimized attributes
229
- model_class = type(
230
- class_name,
231
- bases,
232
- {
233
- "__traits__": traits,
234
- "__composition__": composition,
235
- "__module__": "lionagi.traits.generated",
236
- "__slots__": (), # Prevent __dict__ creation for memory efficiency
237
- },
238
- )
239
-
240
- # Performance optimization 4: Batch register traits
241
- for trait in traits:
242
- # Skip validation since we already validated composition
243
- registry._trait_implementations.setdefault(
244
- model_class, set()
245
- ).add(trait)
246
-
247
- return model_class
248
-
249
- def generate_model(self, composition: TraitComposition) -> type[Any]:
250
- """
251
- Generate a model class from trait composition.
252
-
253
- Performance characteristics:
254
- - First generation (cold): ~25μs (relaxed from 10μs research target)
255
- - Cached generation (warm): <1μs (meets research target)
256
-
257
- The 25μs cold generation is acceptable for production use as:
258
- 1. It only occurs once per unique trait combination
259
- 2. Subsequent calls use LRU cache (<1μs)
260
- 3. Most applications have limited trait combinations
261
- """
262
- start_time = time.perf_counter()
263
-
264
- # Convert to sorted tuple for caching
265
- trait_tuple = tuple(sorted(composition.traits, key=lambda t: t.name))
266
-
267
- try:
268
- # Use LRU cache for fast generation
269
- model_class = self._generate_model_cached(trait_tuple)
270
-
271
- # Track performance
272
- generation_time = (
273
- time.perf_counter() - start_time
274
- ) * 1_000_000 # μs
275
- self._generation_count += 1
276
-
277
- # Updated performance target to realistic value
278
- PERFORMANCE_TARGET_US = (
279
- 25.0 # Relaxed from 10μs for cold generation
280
- )
281
- if generation_time > PERFORMANCE_TARGET_US and __debug__:
282
- import warnings
283
-
284
- warnings.warn(
285
- f"Model generation took {generation_time:.1f}μs, "
286
- f"exceeding {PERFORMANCE_TARGET_US}μs target",
287
- PerformanceWarning,
288
- stacklevel=2,
289
- )
290
-
291
- return model_class
292
-
293
- except Exception as e:
294
- raise CompositionError(f"Failed to generate model: {e}") from e
295
-
296
- def get_cache_stats(self) -> dict[str, Any]:
297
- """Get performance statistics."""
298
- info = self._generate_model_cached.cache_info()
299
- return {
300
- "cache_hits": info.hits,
301
- "cache_misses": info.misses,
302
- "cache_size": info.currsize,
303
- "max_cache_size": info.maxsize,
304
- "hit_ratio": (
305
- info.hits / (info.hits + info.misses)
306
- if (info.hits + info.misses) > 0
307
- else 0.0
308
- ),
309
- "generations": self._generation_count,
310
- }
311
-
312
- def clear_cache(self) -> None:
313
- """Clear the composition cache."""
314
- self._generate_model_cached.cache_clear()
315
- self._generation_count = 0
316
-
317
-
318
- # Initialize class-level caches
319
- TraitComposer._protocol_cache = {}
320
- TraitComposer._validated_compositions = set()
321
-
322
-
323
- # Convenience functions
324
- def compose(*traits: Trait | TraitComposition) -> TraitComposition:
325
- """Compose traits using the global composer."""
326
- return TraitComposer.get_instance().compose(*traits)
327
-
328
-
329
- def generate_model(composition: TraitComposition) -> type[Any]:
330
- """Generate a model class from composition using the global composer."""
331
- return TraitComposer.get_instance().generate_model(composition)
332
-
333
-
334
- def create_trait_composition(*traits: Trait) -> TraitComposition:
335
- """Create a trait composition from individual traits."""
336
- return TraitComposition(traits=frozenset(traits))
337
-
338
-
339
- # Performance warning class
340
- class PerformanceWarning(UserWarning):
341
- """Warning for performance issues in trait composition."""
342
-
343
- pass