ct-toolkit 0.3.5__tar.gz → 0.3.7__tar.gz

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 (59) hide show
  1. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/PKG-INFO +12 -6
  2. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/README.md +8 -4
  3. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/__init__.py +1 -1
  4. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/cli.py +32 -0
  5. ct_toolkit-0.3.5/ct_toolkit/middleware/deepagents.py → ct_toolkit-0.3.7/ct_toolkit/core/compression_guard.py +23 -60
  6. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/core/wrapper.py +64 -37
  7. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/divergence/l3_icm.py +19 -16
  8. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/identity/embedding.py +16 -14
  9. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/middleware/crewai.py +4 -0
  10. ct_toolkit-0.3.7/ct_toolkit/middleware/deepagents.py +67 -0
  11. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/middleware/langchain.py +5 -0
  12. ct_toolkit-0.3.7/ct_toolkit/middleware/litellm.py +344 -0
  13. ct_toolkit-0.3.7/ct_toolkit/server.py +174 -0
  14. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit.egg-info/PKG-INFO +12 -6
  15. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit.egg-info/SOURCES.txt +4 -0
  16. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit.egg-info/requires.txt +3 -1
  17. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/pyproject.toml +4 -2
  18. ct_toolkit-0.3.7/tests/test_cli_extended.py +53 -0
  19. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/LICENSE +0 -0
  20. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/__main__.py +0 -0
  21. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/core/__init__.py +0 -0
  22. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/core/compatibility.py +0 -0
  23. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/core/exceptions.py +0 -0
  24. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/core/integrity.py +0 -0
  25. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/core/kernel.py +0 -0
  26. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/divergence/__init__.py +0 -0
  27. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/divergence/analysis.py +0 -0
  28. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/divergence/engine.py +0 -0
  29. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/divergence/l2_judge.py +0 -0
  30. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/divergence/loss.py +0 -0
  31. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/divergence/scheduler.py +0 -0
  32. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/endorsement/__init__.py +0 -0
  33. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/endorsement/probes/__init__.py +0 -0
  34. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/endorsement/reflective.py +0 -0
  35. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/identity/__init__.py +0 -0
  36. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/identity/templates/__init__.py +0 -0
  37. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/identity/templates/defense.yaml +0 -0
  38. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/identity/templates/finance.yaml +0 -0
  39. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/identity/templates/general.yaml +0 -0
  40. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/identity/templates/medical.yaml +0 -0
  41. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/kernels/__init__.py +0 -0
  42. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/kernels/default.yaml +0 -0
  43. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/kernels/defense.yaml +0 -0
  44. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/middleware/__init__.py +0 -0
  45. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/middleware/autogen.py +0 -0
  46. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/provenance/__init__.py +0 -0
  47. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/provenance/log.py +0 -0
  48. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/provenance/vault/__init__.py +0 -0
  49. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/utils/__init__.py +0 -0
  50. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit/utils/logger.py +0 -0
  51. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit.egg-info/dependency_links.txt +0 -0
  52. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit.egg-info/entry_points.txt +0 -0
  53. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/ct_toolkit.egg-info/top_level.txt +0 -0
  54. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/setup.cfg +0 -0
  55. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/tests/test_analysis.py +0 -0
  56. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/tests/test_cli.py +0 -0
  57. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/tests/test_deep_agents_integration.py +0 -0
  58. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/tests/test_loss.py +0 -0
  59. {ct_toolkit-0.3.5 → ct_toolkit-0.3.7}/tests/test_summarization_drift.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ct-toolkit
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-2.0
@@ -9,7 +9,7 @@ Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Requires-Dist: instructor>=1.3.0
12
- Requires-Dist: any-llm-sdk[all]>=1.0.0
12
+ Requires-Dist: litellm>=1.40.0
13
13
  Requires-Dist: numpy>=1.26.0
14
14
  Requires-Dist: pyyaml>=6.0
15
15
  Requires-Dist: pydantic>=2.5.0
@@ -20,6 +20,8 @@ Requires-Dist: rich>=13.0.0
20
20
  Requires-Dist: jinja2>=3.1.0
21
21
  Requires-Dist: pyasn1>=0.6.3
22
22
  Requires-Dist: typer[all]>=0.12.0
23
+ Requires-Dist: fastapi>=0.110.0
24
+ Requires-Dist: uvicorn>=0.29.0
23
25
  Provides-Extra: ollama
24
26
  Requires-Dist: ollama>=0.2.0; extra == "ollama"
25
27
  Provides-Extra: langchain
@@ -65,6 +67,7 @@ For full API reference, architecture details, examples, and integration guides,
65
67
 
66
68
  In complex agentic workflows, LLMs tend to "drift" from their original instructions. CT Toolkit provides the mathematical and cryptographic guardrails to ensure your agents remain aligned with their core constitution, even across deep hierarchies.
67
69
 
70
+ - **Passive Context Compression Detection**: Automatically detects silent provider-side history compression (e.g., OpenAI/Anthropic) via history shrinkage heuristics.
68
71
  - **Constitutional Kernels**: Axiomatic identity anchors.
69
72
  - **Standalone Auditor CLI**: Rapidly audit any LLM endpoint for identity drift without writing code.
70
73
  - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
@@ -81,10 +84,13 @@ pip install ct-toolkit
81
84
  ```
82
85
 
83
86
  ```python
84
- from ct_toolkit import TheseusWrapper
87
+ from ct_toolkit import TheseusWrapper, WrapperConfig
88
+
89
+ # Protect against silent provider context compression
90
+ config = WrapperConfig(compression_passive_detection=True)
85
91
 
86
92
  # One-line injection for any LLM provider
87
- client = TheseusWrapper(provider="openai")
93
+ client = TheseusWrapper(provider="openai", config=config)
88
94
 
89
95
  # Guardrails and drift analysis applied automatically
90
96
  response = client.chat("What are your core security axioms?")
@@ -112,9 +118,9 @@ ct-toolkit list-templates
112
118
 
113
119
  | Metric | Status |
114
120
  | :--------------- | :------------------------------------------------------ |
115
- | **Tests** | ✅ 237/240 passing (90% coverage) |
121
+ | **Tests** | ✅ 281/292 passing (95% coverage) |
116
122
  | **Last Phase** | ✅ Phase 6: Auditor Mode (Complete) |
117
- | **Current Goal** | 🔶 Phase 7: Multi-Agent Synchronization (Planning) |
123
+ | **Current Goal** | 🔶 Phase 7: Multi-Agent Synchronization (Integration) |
118
124
 
119
125
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
120
126
 
@@ -24,6 +24,7 @@ For full API reference, architecture details, examples, and integration guides,
24
24
 
25
25
  In complex agentic workflows, LLMs tend to "drift" from their original instructions. CT Toolkit provides the mathematical and cryptographic guardrails to ensure your agents remain aligned with their core constitution, even across deep hierarchies.
26
26
 
27
+ - **Passive Context Compression Detection**: Automatically detects silent provider-side history compression (e.g., OpenAI/Anthropic) via history shrinkage heuristics.
27
28
  - **Constitutional Kernels**: Axiomatic identity anchors.
28
29
  - **Standalone Auditor CLI**: Rapidly audit any LLM endpoint for identity drift without writing code.
29
30
  - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
@@ -40,10 +41,13 @@ pip install ct-toolkit
40
41
  ```
41
42
 
42
43
  ```python
43
- from ct_toolkit import TheseusWrapper
44
+ from ct_toolkit import TheseusWrapper, WrapperConfig
45
+
46
+ # Protect against silent provider context compression
47
+ config = WrapperConfig(compression_passive_detection=True)
44
48
 
45
49
  # One-line injection for any LLM provider
46
- client = TheseusWrapper(provider="openai")
50
+ client = TheseusWrapper(provider="openai", config=config)
47
51
 
48
52
  # Guardrails and drift analysis applied automatically
49
53
  response = client.chat("What are your core security axioms?")
@@ -71,9 +75,9 @@ ct-toolkit list-templates
71
75
 
72
76
  | Metric | Status |
73
77
  | :--------------- | :------------------------------------------------------ |
74
- | **Tests** | ✅ 237/240 passing (90% coverage) |
78
+ | **Tests** | ✅ 281/292 passing (95% coverage) |
75
79
  | **Last Phase** | ✅ Phase 6: Auditor Mode (Complete) |
76
- | **Current Goal** | 🔶 Phase 7: Multi-Agent Synchronization (Planning) |
80
+ | **Current Goal** | 🔶 Phase 7: Multi-Agent Synchronization (Integration) |
77
81
 
78
82
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
79
83
 
@@ -23,7 +23,7 @@ from ct_toolkit.core.exceptions import (
23
23
  ChainIntegrityError,
24
24
  )
25
25
 
26
- __version__ = "0.3.5"
26
+ __version__ = "0.3.7"
27
27
 
28
28
  __all__ = [
29
29
  "TheseusWrapper",
@@ -17,6 +17,7 @@ from rich.table import Table
17
17
  from ct_toolkit import TheseusWrapper, WrapperConfig, __version__
18
18
  from ct_toolkit.divergence.l3_icm import ICMRunner
19
19
  from ct_toolkit.core.kernel import ConstitutionalKernel
20
+ from ct_toolkit.server import start_server
20
21
 
21
22
  app = typer.Typer(
22
23
  help="Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems.",
@@ -135,6 +136,37 @@ def audit(
135
136
  if not report.is_healthy:
136
137
  raise typer.Exit(code=1)
137
138
 
139
+ @app.command()
140
+ def serve(
141
+ host: str = typer.Option("127.0.0.1", "--host", help="Host to bind the server to."),
142
+ port: int = typer.Option(8001, "--port", help="Port to bind the server to."),
143
+ kernel: str = typer.Option("general", "--kernel", help="Name of the Constitutional Kernel to use."),
144
+ template: str = typer.Option("general", "--template", help="Name of the Identity Template to use."),
145
+ vault_path: str = typer.Option("./ct_provenance.db", "--vault", help="Path to the provenance log database."),
146
+ judge_provider: str = typer.Option("openai", "--judge-provider", help="LLM provider for the L2 judge."),
147
+ judge_model: Optional[str] = typer.Option(None, "--judge-model", help="Model ID for the L2 judge."),
148
+ ):
149
+ """Start the CT-Toolkit Guardrail Server for LiteLLM integration."""
150
+ console.print(f"Starting Guardrail Server...")
151
+ console.print(f" - Kernel: [bold yellow]{kernel}[/bold yellow]")
152
+ console.print(f" - Template: [bold green]{template}[/bold green]")
153
+ console.print(f" - Bind: [bold cyan]{host}:{port}[/bold cyan]")
154
+
155
+ try:
156
+ config = WrapperConfig(
157
+ kernel_name=kernel,
158
+ template=template,
159
+ vault_path=vault_path,
160
+ project_root=Path.cwd()
161
+ )
162
+ wrapper = TheseusWrapper(provider=judge_provider, config=config)
163
+
164
+ # Start the uvicorn server
165
+ start_server(wrapper=wrapper, host=host, port=port)
166
+ except Exception as e:
167
+ console.print(f"[bold red]Failed to start server:[/bold red] {e}")
168
+ raise typer.Exit(code=1)
169
+
138
170
  @app.command()
139
171
  def list_kernels():
140
172
  """List available Constitutional Kernels."""
@@ -1,20 +1,18 @@
1
1
  """
2
- ct_toolkit.middleware.deepagents
2
+ ct_toolkit.core.compression_guard
3
3
  ---------------------------------
4
- Specialized integration for LangChain Deep Agents (deepagents).
5
-
6
- This module provides helpers to wrap Deep Agents with CT Toolkit's
7
- identity-continuity guardrails, specifically ensuring that when a
8
- Deep Agent spawns sub-agents, they inherit the mother agent's
9
- Constitutional Kernel.
4
+ Provider-agnostic compression drift detector.
5
+ Works with OpenAI compaction, Anthropic compaction, LangChain memory,
6
+ or any framework that silently shortens context.
10
7
  """
11
8
  from __future__ import annotations
12
9
 
13
- from typing import Any, Dict, List, Optional, Union, Callable
14
- from ct_toolkit.core.wrapper import TheseusWrapper, WrapperConfig
15
- from ct_toolkit.middleware.langchain import TheseusChatModel
10
+ from typing import Any, Dict, List, TYPE_CHECKING
16
11
  from ct_toolkit.utils.logger import get_logger
17
12
 
13
+ if TYPE_CHECKING:
14
+ from ct_toolkit.core.wrapper import TheseusWrapper
15
+
18
16
  logger = get_logger(__name__)
19
17
 
20
18
  class ContextCompressionGuard:
@@ -27,7 +25,7 @@ class ContextCompressionGuard:
27
25
 
28
26
  def analyze_summary_drift(
29
27
  self,
30
- original_messages: List[Dict[str, str]],
28
+ original_messages: List[Dict[str, str]] | str,
31
29
  summary_text: str
32
30
  ) -> Dict[str, Any]:
33
31
  """
@@ -37,9 +35,12 @@ class ContextCompressionGuard:
37
35
  Returns:
38
36
  Dict containing similarity score and drift status.
39
37
  """
40
- # 1. Flatten original messages into a single identity string
41
- original_text = " ".join([m.get("content", "") for m in original_messages])
42
-
38
+ # 1. Flatten original messages into a single identity string if it's a list
39
+ if isinstance(original_messages, list):
40
+ original_text = " ".join([m.get("content", "") for m in original_messages])
41
+ else:
42
+ original_text = original_messages
43
+
43
44
  # 2. Compute embeddings via identity layer
44
45
  original_emb = self.wrapper._identity_layer._compute_vector(original_text)
45
46
  summary_emb = self.wrapper._identity_layer._compute_vector(summary_text)
@@ -49,7 +50,6 @@ class ContextCompressionGuard:
49
50
  similarity = IdentityEmbeddingLayer.calculate_similarity(original_emb, summary_emb)
50
51
 
51
52
  # 4. Identity Safety Floor (Internal protection against manipulated thresholds)
52
- # Even if the user sets threshold to 0.4, 0.6 is still risky for LLM identity.
53
53
  drift_detected = similarity < self.threshold
54
54
  is_critical = similarity < 0.70 # A hard floor for identity integrity
55
55
 
@@ -88,48 +88,11 @@ class ContextCompressionGuard:
88
88
 
89
89
  return result
90
90
 
91
- def wrap_deep_agent_factory(
92
- create_deep_agent_fn: Callable,
93
- wrapper: Optional[TheseusWrapper] = None,
94
- wrapper_config: Optional[WrapperConfig] = None,
95
- compression_threshold: float = 0.85,
96
- ) -> Callable:
97
- """
98
- Wraps the LangChain `create_deep_agent` function to automatically
99
- inject Theseus guardrails, enable hierarchical kernel propagation,
100
- and monitor context compression drift.
101
- """
102
- _wrapper = wrapper or TheseusWrapper(config=wrapper_config)
103
- guard = ContextCompressionGuard(_wrapper, threshold=compression_threshold)
104
-
105
- def wrapped_create_deep_agent(*args: Any, **kwargs: Any) -> Any:
106
- # 1. Inject TheseusChatModel
107
- model = kwargs.get("model")
108
- if isinstance(model, str) or model is None:
109
- kwargs["model"] = TheseusChatModel(
110
- wrapper=_wrapper,
111
- model=model if isinstance(model, str) else "gpt-4o-mini"
112
- )
113
-
114
- # 2. Inject ContextCompressionGuard into the middleware stack if possible
115
- # Since deepagents middleware is internal, we use the helper to signal it
116
- # In a real integration, we'd wrap the SummarizationMiddleware.
117
-
118
- return create_deep_agent_fn(*args, **kwargs)
119
-
120
- return wrapped_create_deep_agent
121
-
122
- class DeepAgentTheseusHelper:
123
- """
124
- Helper for programmatic integration with Deep Agents.
125
- """
126
- @staticmethod
127
- def prepare_config(wrapper: TheseusWrapper) -> Dict[str, Any]:
128
- """Returns configuration for Deep Agents that includes identity constraints."""
129
- return {
130
- "callbacks": [], # We could add TheseusLangChainCallback here
131
- "metadata": {
132
- "ct_kernel": wrapper.kernel.name,
133
- "ct_identity_protection": True
134
- }
135
- }
91
+ def on_passive_detection(self, original: List[Dict[str, str]], compressed: List[Dict[str, str]]) -> Dict[str, Any]:
92
+ """
93
+ Triggered when TheseusWrapper detects a significant history shrinkage.
94
+ """
95
+ logger.info("CT Toolkit | Passive compression detection triggered.")
96
+ original_text = " ".join(m.get("content", "") for m in original)
97
+ compressed_text = " ".join(m.get("content", "") for m in compressed)
98
+ return self.analyze_summary_drift(original_text, compressed_text)
@@ -25,15 +25,14 @@ import os
25
25
  from dataclasses import dataclass, field
26
26
  from pathlib import Path
27
27
  from typing import Any, Iterator, Optional, Callable, Dict
28
- import any_llm
29
- from any_llm import AnyLLM
30
- import glob
28
+ import litellm
31
29
 
32
30
  from ct_toolkit.core.kernel import ConstitutionalKernel
33
31
  from ct_toolkit.core.compatibility import CompatibilityLayer, CompatibilityResult
34
32
  from ct_toolkit.core.exceptions import CTToolkitError, MissingClientError
35
33
  from ct_toolkit.core.integrity import IntegrityMonitor
36
34
  from ct_toolkit.divergence.scheduler import RiskProfile
35
+ from ct_toolkit.core.compression_guard import ContextCompressionGuard
37
36
  from ct_toolkit.utils.logger import get_logger
38
37
 
39
38
  logger = get_logger(__name__)
@@ -71,6 +70,10 @@ class WrapperConfig:
71
70
 
72
71
  strict_embedding: bool = False # Raise error if embedding API fails
73
72
 
73
+ # -- Context Compression Guard --
74
+ compression_threshold: float = 0.85
75
+ compression_passive_detection: bool = True
76
+
74
77
 
75
78
  @dataclass
76
79
  class CTResponse:
@@ -92,7 +95,7 @@ class TheseusWrapper:
92
95
  Identity-continuity proxy wrapping the LLM API client.
93
96
 
94
97
  Supported providers: openai (default), anthropic, ollama, google, etc.
95
- (Any provider supported by any-llm-sdk)
98
+ (Any provider supported by LiteLLM)
96
99
 
97
100
  Usage:
98
101
  from ct_toolkit import TheseusWrapper
@@ -128,7 +131,7 @@ class TheseusWrapper:
128
131
  Optional if 'provider' is specified.
129
132
  config: Optional WrapperConfig instance.
130
133
  provider: Provider name (e.g. "openai", "anthropic", "ollama", "google", "cohere").
131
- If provided, 'any-llm' will be used to initialize the client internally.
134
+ If provided, LiteLLM will be used to initialize the client internally.
132
135
  project_root: Path to the user's project root directory. Used to find a custom 'config/' folder.
133
136
  Defaults to the current working directory.
134
137
  """
@@ -165,6 +168,13 @@ class TheseusWrapper:
165
168
  self._divergence_engine = self._init_divergence_engine()
166
169
  self._last_model: str = "unknown"
167
170
 
171
+ # ── Context Compression Guard ──
172
+ self._compression_guard = ContextCompressionGuard(
173
+ self,
174
+ threshold=self._config.compression_threshold
175
+ )
176
+ self._shadow_history: Optional[List[Dict[str, str]]] = None
177
+
168
178
  # ── Integrity Monitoring ──
169
179
  self._integrity_monitor = IntegrityMonitor()
170
180
  self._register_monitored_files()
@@ -231,7 +241,7 @@ class TheseusWrapper:
231
241
  return client.lower()
232
242
 
233
243
  try:
234
- # Check for any_llm wrapper
244
+ # Check for LiteLLM specific attributes if any
235
245
  if hasattr(client, "provider") and isinstance(client.provider, str):
236
246
  return client.provider
237
247
 
@@ -489,9 +499,21 @@ class TheseusWrapper:
489
499
  model: str | None,
490
500
  **kwargs: Any,
491
501
  ) -> Any:
492
- """Uses any_llm for unified provider calling."""
493
- # Convert messages to any_llm format if necessary
494
- # any_llm.completion handles different formats internally
502
+ """Uses LiteLLM for unified provider calling."""
503
+ # --- Passive Context Compression Detection ---
504
+ if self._config.compression_passive_detection and self._shadow_history:
505
+ # Simple heuristic: message count dropped significantly
506
+ # We use 0.7 as a threshold (30% drop)
507
+ if len(messages) < len(self._shadow_history) * 0.7:
508
+ self._compression_guard.on_passive_detection(
509
+ original=self._shadow_history,
510
+ compressed=messages
511
+ )
512
+
513
+ # Update shadow history for next call
514
+ self._shadow_history = list(messages)
515
+
516
+ # LiteLLM handles different message formats internally
495
517
 
496
518
  # Set default models per provider if not specified
497
519
  if not model:
@@ -500,32 +522,29 @@ class TheseusWrapper:
500
522
  elif self._provider == "ollama": model = "llama3"
501
523
  else: model = "gpt-4o-mini"
502
524
 
503
- # any_llm expects "provider:model" format
525
+ # LiteLLM expects "provider/model" format for non-default providers
526
+ # Usually OpenAI models don't need a prefix.
504
527
  full_model = model
505
- if ":" not in model:
506
- full_model = f"{self._provider}:{model}"
507
-
508
- # Prepare any_llm call
509
- any_llm_kwargs = {
510
- "model": full_model,
511
- "messages": messages,
512
- **kwargs
513
- }
528
+ if ":" in model:
529
+ # If it has the legacy format "provider:model", convert it
530
+ full_model = model.replace(":", "/", 1)
531
+ elif self._provider not in ("openai", "unknown"):
532
+ full_model = f"{self._provider}/{model}"
514
533
 
515
534
  # Extract connection info from client if possible
516
535
  if hasattr(self._client, "base_url") and self._client.base_url:
517
- any_llm_kwargs["api_base"] = str(self._client.base_url)
536
+ kwargs["api_base"] = str(self._client.base_url)
518
537
  if hasattr(self._client, "api_key") and self._client.api_key:
519
- any_llm_kwargs["api_key"] = self._client.api_key
538
+ kwargs["api_key"] = self._client.api_key
520
539
 
521
540
  # Transfer common client configurations if available
522
- if hasattr(self._client, "timeout") and "timeout" not in any_llm_kwargs:
523
- any_llm_kwargs["timeout"] = self._client.timeout
541
+ if hasattr(self._client, "timeout") and "timeout" not in kwargs:
542
+ kwargs["timeout"] = self._client.timeout
524
543
 
525
544
  try:
526
- return any_llm.completion(**any_llm_kwargs)
545
+ return litellm.completion(model=full_model, messages=messages, **kwargs)
527
546
  except Exception as e:
528
- logger.error(f"any_llm call failed: {e}")
547
+ logger.error(f"litellm call failed: {e}")
529
548
  raise
530
549
 
531
550
  # ── Helper Methods ──────────────────────────────────────────────────────
@@ -579,25 +598,33 @@ class TheseusWrapper:
579
598
  return messages
580
599
 
581
600
  def _extract_content(self, raw: Any) -> str:
582
- # Handle dict responses (e.g. from some mockers or raw APIs)
601
+ # Handle dict responses
583
602
  if isinstance(raw, dict):
584
603
  if "choices" in raw and isinstance(raw["choices"], list) and raw["choices"]:
585
- # OpenAI dict format
586
604
  return raw["choices"][0].get("message", {}).get("content", "") or ""
587
605
  if "message" in raw:
588
- # Ollama dict format
606
+ # Ollama dictate format
589
607
  return raw["message"].get("content", "") or ""
590
608
  return str(raw)
591
609
 
592
- # OpenAI
593
- if hasattr(raw, "choices"):
594
- return raw.choices[0].message.content or ""
595
- # Anthropic
596
- if hasattr(raw, "content") and isinstance(raw.content, list):
597
- return raw.content[0].text if raw.content else ""
598
- # Ollama
599
- if hasattr(raw, "message"):
600
- return raw.message.content or ""
610
+ # LiteLLM ModelResponse (Standardized for all providers)
611
+ try:
612
+ if hasattr(raw, "choices") and raw.choices:
613
+ choice = raw.choices[0]
614
+ if hasattr(choice, "message") and choice.message:
615
+ return choice.message.content or ""
616
+ # Some older/alternative formats
617
+ if hasattr(choice, "text"):
618
+ return choice.text or ""
619
+ except Exception:
620
+ pass
621
+
622
+ # Fallback raw extraction for edge cases
623
+ if hasattr(raw, "content") and isinstance(raw.content, list): # Anthropic style
624
+ return raw.content[0].text if raw.content else ""
625
+ if hasattr(raw, "message"): # Ollama generic style
626
+ return getattr(raw.message, "content", "") or ""
627
+
601
628
  return str(raw)
602
629
 
603
630
  def _extract_model(self, raw: Any, fallback: str | None) -> str:
@@ -19,7 +19,7 @@ from dataclasses import dataclass, field
19
19
  from pathlib import Path
20
20
  from typing import Any
21
21
 
22
- import any_llm
22
+ import litellm
23
23
 
24
24
  from ct_toolkit.utils.logger import get_logger
25
25
 
@@ -290,11 +290,14 @@ class ICMRunner:
290
290
  def _call_model(self, prompt: str) -> str:
291
291
  system = self._kernel.get_system_prompt_injection()
292
292
 
293
+ # LiteLLM format
293
294
  full_model = self._model
294
- if ":" not in self._model:
295
- full_model = f"{self._provider}:{self._model}"
295
+ if ":" in self._model:
296
+ full_model = self._model.replace(":", "/", 1)
297
+ elif self._provider not in ("openai", "unknown"):
298
+ full_model = f"{self._provider}/{self._model}"
296
299
 
297
- any_llm_kwargs = {
300
+ kwargs: dict[str, Any] = {
298
301
  "model": full_model,
299
302
  "messages": [
300
303
  {"role": "system", "content": system},
@@ -306,13 +309,20 @@ class ICMRunner:
306
309
 
307
310
  # Extract connection info from client if possible
308
311
  if hasattr(self._client, "base_url") and self._client.base_url:
309
- any_llm_kwargs["api_base"] = str(self._client.base_url)
312
+ kwargs["api_base"] = str(self._client.base_url)
310
313
  if hasattr(self._client, "api_key") and self._client.api_key:
311
- any_llm_kwargs["api_key"] = self._client.api_key
314
+ kwargs["api_key"] = self._client.api_key
312
315
 
313
316
  try:
314
- resp = any_llm.completion(**any_llm_kwargs)
315
- # any_llm SDK standardizes responses but fallback handling preserves robustness
317
+ resp = litellm.completion(**kwargs)
318
+
319
+ # Extract content from LiteLLM response
320
+ if hasattr(resp, "choices") and resp.choices:
321
+ choice = resp.choices[0]
322
+ if hasattr(choice, "message") and choice.message:
323
+ return choice.message.content or ""
324
+
325
+ # Fallback
316
326
  if isinstance(resp, dict):
317
327
  if "choices" in resp and resp["choices"]:
318
328
  return resp["choices"][0].get("message", {}).get("content", "")
@@ -320,16 +330,9 @@ class ICMRunner:
320
330
  return resp["message"].get("content", "")
321
331
  return str(resp)
322
332
 
323
- if hasattr(resp, "choices"):
324
- return resp.choices[0].message.content or ""
325
- if hasattr(resp, "content") and isinstance(resp.content, list):
326
- return resp.content[0].text if resp.content else ""
327
- if hasattr(resp, "message"):
328
- return resp.message.content or ""
329
-
330
333
  return str(resp)
331
334
  except Exception as e:
332
- logger.error(f"ICM probe any_llm call failed: {e}")
335
+ logger.error(f"ICM probe litellm call failed: {e}")
333
336
  raise
334
337
 
335
338
  # ── Probe Loading ──────────────────────────────────────────────────────────
@@ -9,9 +9,7 @@ Step 3 MVP implementation:
9
9
  - Each response embedding is calculated
10
10
  - Cosine distance = 1 - cosine_similarity → divergence score
11
11
  """
12
- from __future__ import annotations
13
-
14
- import json
12
+ import litellm
15
13
  import hashlib
16
14
  from pathlib import Path
17
15
  from typing import Any
@@ -155,17 +153,21 @@ class IdentityEmbeddingLayer:
155
153
  """
156
154
  if self._embedding_client is not None:
157
155
  try:
158
- # Robust attribute check for OpenAI-compatible clients
159
- embeddings_api = getattr(self._embedding_client, "embeddings", None)
160
- if embeddings_api:
161
- response = embeddings_api.create(
162
- input=[text],
163
- model=self._embedding_model
164
- )
165
- embedding = response.data[0].embedding
166
- return np.array(embedding, dtype=np.float32)
167
- else:
168
- logger.debug("Embedding client does not support .embeddings API. Falling back.")
156
+ # Prepare arguments for litellm.embedding
157
+ kwargs: dict[str, Any] = {
158
+ "model": self._embedding_model,
159
+ "input": [text]
160
+ }
161
+
162
+ # Transfer client configuration if available
163
+ if hasattr(self._embedding_client, "base_url") and self._embedding_client.base_url:
164
+ kwargs["api_base"] = str(self._embedding_client.base_url)
165
+ if hasattr(self._embedding_client, "api_key") and self._embedding_client.api_key:
166
+ kwargs["api_key"] = self._embedding_client.api_key
167
+
168
+ response = litellm.embedding(**kwargs)
169
+ embedding = response.data[0]["embedding"]
170
+ return np.array(embedding, dtype=np.float32)
169
171
  except Exception as e:
170
172
  if self._strict_embedding:
171
173
  raise RuntimeError(f"Embedding API failed: {e}") from e
@@ -92,6 +92,8 @@ class TheseusCrewMiddleware:
92
92
  parent_kernel=manager_wrapper.kernel,
93
93
  vault_path=manager_wrapper._config.vault_path,
94
94
  template=manager_wrapper._config.template,
95
+ compression_threshold=manager_wrapper._config.compression_threshold,
96
+ compression_passive_detection=manager_wrapper._config.compression_passive_detection,
95
97
  )
96
98
  agent.llm = TheseusChatModel(
97
99
  provider=manager_wrapper._provider,
@@ -134,6 +136,8 @@ class TheseusCrewMiddleware:
134
136
  parent_kernel=manager_wrapper.kernel,
135
137
  vault_path=manager_wrapper._config.vault_path,
136
138
  template=manager_wrapper._config.template,
139
+ compression_threshold=manager_wrapper._config.compression_threshold,
140
+ compression_passive_detection=manager_wrapper._config.compression_passive_detection,
137
141
  )
138
142
  agent.llm = TheseusChatModel(
139
143
  provider=manager_wrapper._provider,
@@ -0,0 +1,67 @@
1
+ """
2
+ ct_toolkit.middleware.deepagents
3
+ ---------------------------------
4
+ Specialized integration for LangChain Deep Agents (deepagents).
5
+
6
+ This module provides helpers to wrap Deep Agents with CT Toolkit's
7
+ identity-continuity guardrails, specifically ensuring that when a
8
+ Deep Agent spawns sub-agents, they inherit the mother agent's
9
+ Constitutional Kernel.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, Dict, List, Optional, Union, Callable
14
+ from ct_toolkit.core.wrapper import TheseusWrapper, WrapperConfig
15
+ from ct_toolkit.core.compression_guard import ContextCompressionGuard
16
+ from ct_toolkit.middleware.langchain import TheseusChatModel
17
+ from ct_toolkit.utils.logger import get_logger
18
+
19
+ logger = get_logger(__name__)
20
+
21
+
22
+
23
+ def wrap_deep_agent_factory(
24
+ create_deep_agent_fn: Callable,
25
+ wrapper: Optional[TheseusWrapper] = None,
26
+ wrapper_config: Optional[WrapperConfig] = None,
27
+ compression_threshold: float = 0.85,
28
+ ) -> Callable:
29
+ """
30
+ Wraps the LangChain `create_deep_agent` function to automatically
31
+ inject Theseus guardrails, enable hierarchical kernel propagation,
32
+ and monitor context compression drift.
33
+ """
34
+ _wrapper = wrapper or TheseusWrapper(config=wrapper_config)
35
+ guard = ContextCompressionGuard(_wrapper, threshold=compression_threshold)
36
+
37
+ def wrapped_create_deep_agent(*args: Any, **kwargs: Any) -> Any:
38
+ # 1. Inject TheseusChatModel
39
+ model = kwargs.get("model")
40
+ if isinstance(model, str) or model is None:
41
+ kwargs["model"] = TheseusChatModel(
42
+ wrapper=_wrapper,
43
+ model=model if isinstance(model, str) else "gpt-4o-mini"
44
+ )
45
+
46
+ # 2. Inject ContextCompressionGuard into the middleware stack if possible
47
+ # Since deepagents middleware is internal, we use the helper to signal it
48
+ # In a real integration, we'd wrap the SummarizationMiddleware.
49
+
50
+ return create_deep_agent_fn(*args, **kwargs)
51
+
52
+ return wrapped_create_deep_agent
53
+
54
+ class DeepAgentTheseusHelper:
55
+ """
56
+ Helper for programmatic integration with Deep Agents.
57
+ """
58
+ @staticmethod
59
+ def prepare_config(wrapper: TheseusWrapper) -> Dict[str, Any]:
60
+ """Returns configuration for Deep Agents that includes identity constraints."""
61
+ return {
62
+ "callbacks": [], # We could add TheseusLangChainCallback here
63
+ "metadata": {
64
+ "ct_kernel": wrapper.kernel.name,
65
+ "ct_identity_protection": True
66
+ }
67
+ }