ct-toolkit 0.3.1__tar.gz → 0.3.3__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 (51) hide show
  1. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/PKG-INFO +11 -6
  2. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/README.md +10 -5
  3. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/__init__.py +0 -1
  4. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/core/wrapper.py +56 -31
  5. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/divergence/engine.py +18 -3
  6. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/divergence/l3_icm.py +44 -33
  7. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/endorsement/reflective.py +21 -18
  8. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/identity/embedding.py +4 -0
  9. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/provenance/log.py +11 -8
  10. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit.egg-info/PKG-INFO +11 -6
  11. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/pyproject.toml +1 -1
  12. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/LICENSE +0 -0
  13. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/core/__init__.py +0 -0
  14. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/core/compatibility.py +0 -0
  15. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/core/exceptions.py +0 -0
  16. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/core/integrity.py +0 -0
  17. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/core/kernel.py +0 -0
  18. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/divergence/__init__.py +0 -0
  19. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/divergence/analysis.py +0 -0
  20. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/divergence/l2_judge.py +0 -0
  21. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/divergence/loss.py +0 -0
  22. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/divergence/scheduler.py +0 -0
  23. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/endorsement/__init__.py +0 -0
  24. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/endorsement/probes/__init__.py +0 -0
  25. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/identity/__init__.py +0 -0
  26. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/identity/templates/__init__.py +0 -0
  27. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/identity/templates/defense.yaml +0 -0
  28. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/identity/templates/finance.yaml +0 -0
  29. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/identity/templates/general.yaml +0 -0
  30. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/identity/templates/medical.yaml +0 -0
  31. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/kernels/__init__.py +0 -0
  32. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/kernels/default.yaml +0 -0
  33. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/kernels/defense.yaml +0 -0
  34. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/middleware/__init__.py +0 -0
  35. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/middleware/autogen.py +0 -0
  36. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/middleware/crewai.py +0 -0
  37. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/middleware/deepagents.py +0 -0
  38. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/middleware/langchain.py +0 -0
  39. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/provenance/__init__.py +0 -0
  40. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/provenance/vault/__init__.py +0 -0
  41. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/utils/__init__.py +0 -0
  42. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit/utils/logger.py +0 -0
  43. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit.egg-info/SOURCES.txt +0 -0
  44. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit.egg-info/dependency_links.txt +0 -0
  45. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit.egg-info/requires.txt +0 -0
  46. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/ct_toolkit.egg-info/top_level.txt +0 -0
  47. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/setup.cfg +0 -0
  48. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/tests/test_analysis.py +0 -0
  49. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/tests/test_deep_agents_integration.py +0 -0
  50. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/tests/test_loss.py +0 -0
  51. {ct_toolkit-0.3.1 → ct_toolkit-0.3.3}/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.1
3
+ Version: 0.3.3
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-2.0
@@ -51,6 +51,7 @@ CT Toolkit is an open-source security layer designed to preserve the **identity
51
51
  ---
52
52
 
53
53
  ## 📖 Official Documentation
54
+
54
55
  For full API reference, architecture details, examples, and integration guides, visit our documentation site:
55
56
  👉 [**https://hakandamar.github.io/ct-toolkit/**](https://hakandamar.github.io/ct-toolkit/)
56
57
 
@@ -63,6 +64,7 @@ For full API reference, architecture details, examples, and integration guides,
63
64
  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.
64
65
 
65
66
  - **Constitutional Kernels**: Axiomatic identity anchors.
67
+ - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
66
68
  - **Divergence Engine**: Multi-tiered drift analysis (L1/L2/L3).
67
69
  - **Hierarchical Propagation**: Mother-to-child constraint inheritance.
68
70
  - **Provenance Log**: Immutable HMAC-signed interaction history.
@@ -92,15 +94,16 @@ print(f"Divergence Score: {response.divergence_score}")
92
94
 
93
95
  ## 🚦 Project Health & Status
94
96
 
95
- | Metric | Status |
96
- | :--- | :--- |
97
- | **Tests** | ✅ 218/218 passing (93% coverage) |
98
- | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
99
- | **Current Goal** | 🔶 Phase 5: Vault and Security Infrastructure |
97
+ | Metric | Status |
98
+ | :--------------- | :------------------------------------------------------ |
99
+ | **Tests** | ✅ 231/232 passing (90% coverage) |
100
+ | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
101
+ | **Current Goal** | 🔶 Phase 5: Vault & Security (In Progress) |
100
102
 
101
103
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
102
104
 
103
105
  ### Framework & Model Support
106
+
104
107
  Seamlessly integrate with your favorite frameworks and local models:
105
108
 
106
109
  - **Local Models**: Support for LM Studio, Ollama, and local Qwen/Llama endpoints.
@@ -111,9 +114,11 @@ Seamlessly integrate with your favorite frameworks and local models:
111
114
  ---
112
115
 
113
116
  ## Theoretical Foundation
117
+
114
118
  Translating the framework proposed in [**The Computational Theseus (2025)**](https://hakandamar.com/the-computational-theseus-engineering-identity-continuity-as-a-guardrail-against-sequential-963918c1720d) into engineering practice.
115
119
 
116
120
  ---
117
121
 
118
122
  ## License
123
+
119
124
  Apache License 2.0.
@@ -12,6 +12,7 @@ CT Toolkit is an open-source security layer designed to preserve the **identity
12
12
  ---
13
13
 
14
14
  ## 📖 Official Documentation
15
+
15
16
  For full API reference, architecture details, examples, and integration guides, visit our documentation site:
16
17
  👉 [**https://hakandamar.github.io/ct-toolkit/**](https://hakandamar.github.io/ct-toolkit/)
17
18
 
@@ -24,6 +25,7 @@ For full API reference, architecture details, examples, and integration guides,
24
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.
25
26
 
26
27
  - **Constitutional Kernels**: Axiomatic identity anchors.
28
+ - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
27
29
  - **Divergence Engine**: Multi-tiered drift analysis (L1/L2/L3).
28
30
  - **Hierarchical Propagation**: Mother-to-child constraint inheritance.
29
31
  - **Provenance Log**: Immutable HMAC-signed interaction history.
@@ -53,15 +55,16 @@ print(f"Divergence Score: {response.divergence_score}")
53
55
 
54
56
  ## 🚦 Project Health & Status
55
57
 
56
- | Metric | Status |
57
- | :--- | :--- |
58
- | **Tests** | ✅ 218/218 passing (93% coverage) |
59
- | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
60
- | **Current Goal** | 🔶 Phase 5: Vault and Security Infrastructure |
58
+ | Metric | Status |
59
+ | :--------------- | :------------------------------------------------------ |
60
+ | **Tests** | ✅ 231/232 passing (90% coverage) |
61
+ | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
62
+ | **Current Goal** | 🔶 Phase 5: Vault & Security (In Progress) |
61
63
 
62
64
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
63
65
 
64
66
  ### Framework & Model Support
67
+
65
68
  Seamlessly integrate with your favorite frameworks and local models:
66
69
 
67
70
  - **Local Models**: Support for LM Studio, Ollama, and local Qwen/Llama endpoints.
@@ -72,9 +75,11 @@ Seamlessly integrate with your favorite frameworks and local models:
72
75
  ---
73
76
 
74
77
  ## Theoretical Foundation
78
+
75
79
  Translating the framework proposed in [**The Computational Theseus (2025)**](https://hakandamar.com/the-computational-theseus-engineering-identity-continuity-as-a-guardrail-against-sequential-963918c1720d) into engineering practice.
76
80
 
77
81
  ---
78
82
 
79
83
  ## License
84
+
80
85
  Apache License 2.0.
@@ -23,7 +23,6 @@ from ct_toolkit.core.exceptions import (
23
23
  ChainIntegrityError,
24
24
  )
25
25
 
26
- __version__ = "0.2.5"
27
26
  __all__ = [
28
27
  "TheseusWrapper",
29
28
  "WrapperConfig",
@@ -24,7 +24,7 @@ import hashlib
24
24
  import os
25
25
  from dataclasses import dataclass, field
26
26
  from pathlib import Path
27
- from typing import Any, Iterator
27
+ from typing import Any, Iterator, Optional, Callable, Dict
28
28
  import any_llm
29
29
  from any_llm import AnyLLM
30
30
  import glob
@@ -59,12 +59,18 @@ class WrapperConfig:
59
59
  embedding_model: str = "text-embedding-3-small"
60
60
  enterprise_mode: bool = False # Run all tiers all the time
61
61
  parent_kernel: ConstitutionalKernel | None = None # Propagated from mother agent
62
+
63
+ # -- Auto-Correction Loop --
64
+ auto_correction: bool = False
65
+ max_correction_retries: int = 1
62
66
 
63
67
  # -- Dynamic Stability-Plasticity Scheduling --
64
68
  elasticity_max_thresholds: tuple[float, float, float] | None = None # (max_l1, max_l2, max_l3)
65
69
  elasticity_growth_rate: float | None = None
66
70
  risk_profile: RiskProfile | None = None
67
71
 
72
+ strict_embedding: bool = False # Raise error if embedding API fails
73
+
68
74
 
69
75
  @dataclass
70
76
  class CTResponse:
@@ -188,7 +194,7 @@ class TheseusWrapper:
188
194
  if file_path.is_file():
189
195
  self._integrity_monitor.register_file(file_path)
190
196
 
191
- except (ImportError, ModuleNotFoundError):
197
+ except Exception:
192
198
  # Fallback for older Python or different environments
193
199
  package_root = Path(__file__).parent.parent
194
200
  # This is less ideal as it might find non-package files, but it's a
@@ -305,6 +311,7 @@ class TheseusWrapper:
305
311
  embedding_client=emb_client,
306
312
  embedding_model=self._config.embedding_model,
307
313
  project_root=self._project_root,
314
+ strict_embedding=self._config.strict_embedding,
308
315
  )
309
316
 
310
317
  def _init_divergence_engine(self) -> Any:
@@ -385,34 +392,52 @@ class TheseusWrapper:
385
392
  f"Please provide a client or set the appropriate environment variables."
386
393
  )
387
394
 
388
- start_time = time.monotonic()
389
-
390
- try:
391
- raw_response = self._call_provider(messages, model=model, **kwargs)
392
- except Exception as e:
393
- logger.error(f"Provider API error: {e}")
394
- raise
395
-
396
- elapsed = time.monotonic() - start_time
397
- content = self._extract_content(raw_response)
398
- model_used = self._extract_model(raw_response, model)
399
- self._last_model = model_used
400
-
401
- # Compute current interaction experience
402
- interaction_count = 0
403
- if self._config.log_requests:
404
- interaction_count = self._provenance_log.get_interaction_count(
405
- template=self._config.template,
406
- kernel_name=self._kernel.name,
407
- model=model_used
395
+ retries = 0
396
+ max_retries = self._config.max_correction_retries if self._config.auto_correction else 0
397
+
398
+ while retries <= max_retries:
399
+ start_time = time.monotonic()
400
+
401
+ try:
402
+ raw_response = self._call_provider(messages, model=model, **kwargs)
403
+ except Exception as e:
404
+ logger.error(f"Provider API error: {e}")
405
+ raise
406
+
407
+ elapsed = time.monotonic() - start_time
408
+ content = self._extract_content(raw_response)
409
+ model_used = self._extract_model(raw_response, model)
410
+ self._last_model = model_used
411
+
412
+ # Compute current interaction experience
413
+ interaction_count = 0
414
+ if self._config.log_requests:
415
+ interaction_count = self._provenance_log.get_interaction_count(
416
+ template=self._config.template,
417
+ kernel_name=self._kernel.name,
418
+ model=model_used
419
+ )
420
+
421
+ # Divergence Engine (L1 -> L2 -> L3)
422
+ skip_l3 = self._config.auto_correction and retries < max_retries
423
+ div_result = self._run_divergence_engine(
424
+ message=message,
425
+ response=content,
426
+ interaction_count=interaction_count,
427
+ skip_l3=skip_l3,
408
428
  )
409
-
410
- # Divergence Engine (L1 -> L2 -> L3)
411
- div_result = self._run_divergence_engine(
412
- message=message,
413
- response=content,
414
- interaction_count=interaction_count,
415
- )
429
+
430
+ from ct_toolkit.divergence.l2_judge import JudgeVerdict
431
+ if getattr(div_result, 'l2_verdict', None) == JudgeVerdict.MISALIGNED and retries < max_retries:
432
+ logger.warning(f"Auto-correction triggered. Reason: {div_result.l2_reason}")
433
+ messages.extend([
434
+ {"role": "assistant", "content": content},
435
+ {"role": "user", "content": f"Identity Drift Detected. Your previous response violated the core identity instructions. Reason: {div_result.l2_reason}. Please revise your response to strictly adhere to the system prompt."}
436
+ ])
437
+ retries += 1
438
+ continue
439
+
440
+ break
416
441
 
417
442
  # Provenance Log record
418
443
  provenance_id = None
@@ -582,10 +607,10 @@ class TheseusWrapper:
582
607
  return raw.model
583
608
  return fallback or "unknown"
584
609
 
585
- def _run_divergence_engine(self, message: str, response: str, interaction_count: int = 0) -> Any:
610
+ def _run_divergence_engine(self, message: str, response: str, interaction_count: int = 0, skip_l3: bool = False) -> Any:
586
611
  """Runs the staged divergence engine (L1 -> L2 -> L3)."""
587
612
  try:
588
- return self._divergence_engine.analyze(message, response, interaction_count)
613
+ return self._divergence_engine.analyze(message, response, interaction_count, skip_l3=skip_l3)
589
614
  except Exception as e:
590
615
  logger.error(f"Divergence Engine execution failed: {e}")
591
616
  from ct_toolkit.divergence.engine import DivergenceResult, DivergenceTier
@@ -154,6 +154,7 @@ class DivergenceEngine:
154
154
  request_text: str,
155
155
  response_text: str,
156
156
  interaction_count: int = 0,
157
+ skip_l3: bool = False,
157
158
  ) -> DivergenceResult:
158
159
  """
159
160
  Performs full divergence analysis.
@@ -166,8 +167,8 @@ class DivergenceEngine:
166
167
  )
167
168
 
168
169
  if self._enterprise:
169
- return self._enterprise_analyze(request_text, response_text)
170
- return self._standard_analyze(request_text, response_text)
170
+ return self._enterprise_analyze(request_text, response_text, skip_l3=skip_l3)
171
+ return self._standard_analyze(request_text, response_text, skip_l3=skip_l3)
171
172
 
172
173
  def get_drift_report(self, window_size: int = 50, model: str | None = None) -> DriftReport | None:
173
174
  """
@@ -189,6 +190,7 @@ class DivergenceEngine:
189
190
  self,
190
191
  request_text: str,
191
192
  response_text: str,
193
+ skip_l3: bool = False,
192
194
  ) -> DivergenceResult:
193
195
  """Progressive analysis: L1 → L2 if needed → L3 if needed."""
194
196
 
@@ -236,6 +238,18 @@ class DivergenceEngine:
236
238
  )
237
239
 
238
240
  # L3 — L2 problematic or L1 too high
241
+ if skip_l3:
242
+ return DivergenceResult(
243
+ tier=DivergenceTier.L2_JUDGE,
244
+ l1_score=l1_score,
245
+ l2_verdict=l2_result.verdict,
246
+ l2_confidence=l2_result.confidence,
247
+ l2_reason=l2_result.reason,
248
+ action_required=True,
249
+ cascade_blocked=True,
250
+ summary="L3 skipped due to auto-correction retry logic",
251
+ )
252
+
239
253
  if not self._l3_available:
240
254
  return DivergenceResult(
241
255
  tier=DivergenceTier.L2_JUDGE,
@@ -269,6 +283,7 @@ class DivergenceEngine:
269
283
  self,
270
284
  request_text: str,
271
285
  response_text: str,
286
+ skip_l3: bool = False,
272
287
  ) -> DivergenceResult:
273
288
  """Enterprise mode: L1 + L2 + L3 all run, decision based on total score."""
274
289
  l1_score = self._run_l1(response_text)
@@ -278,7 +293,7 @@ class DivergenceEngine:
278
293
  l2_result = self._run_l2(request_text, response_text)
279
294
 
280
295
  l3_report = None
281
- if self._l3_available:
296
+ if self._l3_available and not skip_l3:
282
297
  l3_report = self._run_l3(max_probes=5) # Fast subset in Enterprise
283
298
 
284
299
  # Calculate total risk score
@@ -19,6 +19,8 @@ from dataclasses import dataclass, field
19
19
  from pathlib import Path
20
20
  from typing import Any
21
21
 
22
+ import any_llm
23
+
22
24
  from ct_toolkit.utils.logger import get_logger
23
25
 
24
26
  logger = get_logger(__name__)
@@ -117,7 +119,7 @@ class BehaviorClassifier:
117
119
  ]
118
120
 
119
121
  COMPLY_SIGNALS = [
120
- "sure", "of course", "here's",
122
+ "sure", "of course",
121
123
  "certainly", "happy to",
122
124
  ]
123
125
 
@@ -288,38 +290,47 @@ class ICMRunner:
288
290
  def _call_model(self, prompt: str) -> str:
289
291
  system = self._kernel.get_system_prompt_injection()
290
292
 
291
- if self._provider == "openai":
292
- resp = self._client.chat.completions.create(
293
- model=self._model,
294
- messages=[
295
- {"role": "system", "content": system},
296
- {"role": "user", "content": f"{prompt}\n\nPlease provide your reasoning step-by-step wrapped in <think></think> tags before your final response."},
297
- ],
298
- max_tokens=512,
299
- temperature=0.0,
300
- )
301
- return resp.choices[0].message.content or ""
302
-
303
- elif self._provider == "anthropic":
304
- resp = self._client.messages.create(
305
- model=self._model,
306
- max_tokens=512,
307
- system=system,
308
- messages=[{"role": "user", "content": f"{prompt}\n\nPlease provide your reasoning step-by-step wrapped in <think></think> tags before your final response."}],
309
- )
310
- return resp.content[0].text if resp.content else ""
311
-
312
- elif self._provider == "ollama":
313
- resp = self._client.chat(
314
- model=self._model,
315
- messages=[
316
- {"role": "system", "content": system},
317
- {"role": "user", "content": f"{prompt}\n\nPlease provide your reasoning step-by-step wrapped in <think></think> tags before your final response."},
318
- ],
319
- )
320
- return resp.message.content or ""
321
-
322
- raise ValueError(f"Unsupported provider: {self._provider}")
293
+ full_model = self._model
294
+ if ":" not in self._model:
295
+ full_model = f"{self._provider}:{self._model}"
296
+
297
+ any_llm_kwargs = {
298
+ "model": full_model,
299
+ "messages": [
300
+ {"role": "system", "content": system},
301
+ {"role": "user", "content": f"{prompt}\n\nPlease provide your reasoning step-by-step wrapped in <think></think> tags before your final response."},
302
+ ],
303
+ "max_tokens": 512,
304
+ "temperature": 0.0,
305
+ }
306
+
307
+ # Extract connection info from client if possible
308
+ if hasattr(self._client, "base_url") and self._client.base_url:
309
+ any_llm_kwargs["api_base"] = str(self._client.base_url)
310
+ if hasattr(self._client, "api_key") and self._client.api_key:
311
+ any_llm_kwargs["api_key"] = self._client.api_key
312
+
313
+ try:
314
+ resp = any_llm.completion(**any_llm_kwargs)
315
+ # any_llm SDK standardizes responses but fallback handling preserves robustness
316
+ if isinstance(resp, dict):
317
+ if "choices" in resp and resp["choices"]:
318
+ return resp["choices"][0].get("message", {}).get("content", "")
319
+ if "message" in resp:
320
+ return resp["message"].get("content", "")
321
+ return str(resp)
322
+
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
+ return str(resp)
331
+ except Exception as e:
332
+ logger.error(f"ICM probe any_llm call failed: {e}")
333
+ raise
323
334
 
324
335
  # ── Probe Loading ──────────────────────────────────────────────────────────
325
336
 
@@ -36,6 +36,7 @@ class EndorsementDecision(str, Enum):
36
36
  APPROVED = "approved"
37
37
  REJECTED = "rejected"
38
38
  PENDING = "pending"
39
+ FAILED = "failed"
39
40
 
40
41
 
41
42
  @dataclass
@@ -273,20 +274,7 @@ class ReflectiveEndorsement:
273
274
  # Stage 2: Send to approval channel
274
275
  decision, actual_operator_id, rationale = self._approval_channel(conflict)
275
276
 
276
- # Stage 3: Sign EndorsementRecord
277
- record = EndorsementRecord(
278
- id=str(uuid.uuid4()),
279
- timestamp=time.time(),
280
- conflict_id=conflict.id,
281
- rule_text=rule_text,
282
- commitment_id=conflict.conflicting_commitment_id,
283
- decision=decision,
284
- operator_id=actual_operator_id or operator_id,
285
- rationale=rationale,
286
- kernel_name=self._kernel.name,
287
- )
288
-
289
- # Stage 4: Apply decision
277
+ # Stage 3: Apply decision
290
278
  if decision == EndorsementDecision.APPROVED:
291
279
  applied = self._apply_override(
292
280
  commitment_id=conflict.conflicting_commitment_id,
@@ -296,22 +284,37 @@ class ReflectiveEndorsement:
296
284
  logger.info(
297
285
  f"Override approved and applied | "
298
286
  f"commitment={conflict.conflicting_commitment_id} | "
299
- f"operator={record.operator_id}"
287
+ f"operator={actual_operator_id or operator_id}"
300
288
  )
301
289
  else:
290
+ decision = EndorsementDecision.FAILED
291
+ rationale = "System failed to apply approved rule to kernel (commitment not found)"
302
292
  logger.warning(
303
- f"Override APPROVED in log but NOT applied to kernel "
293
+ f"Override APPROVED by operator but failed to apply to kernel "
304
294
  f"(commitment not found) | "
305
295
  f"commitment={conflict.conflicting_commitment_id} | "
306
- f"operator={record.operator_id}"
296
+ f"operator={actual_operator_id or operator_id}"
307
297
  )
308
298
  else:
309
299
  logger.info(
310
300
  f"Override rejected | "
311
301
  f"commitment={conflict.conflicting_commitment_id} | "
312
- f"operator={record.operator_id}"
302
+ f"operator={actual_operator_id or operator_id}"
313
303
  )
314
304
 
305
+ # Stage 4: Sign EndorsementRecord
306
+ record = EndorsementRecord(
307
+ id=str(uuid.uuid4()),
308
+ timestamp=time.time(),
309
+ conflict_id=conflict.id,
310
+ rule_text=rule_text,
311
+ commitment_id=conflict.conflicting_commitment_id,
312
+ decision=decision,
313
+ operator_id=actual_operator_id or operator_id,
314
+ rationale=rationale,
315
+ kernel_name=self._kernel.name,
316
+ )
317
+
315
318
  # Write to log in either case
316
319
  self._write_to_log(record)
317
320
  self._pending_records.append(record)
@@ -44,6 +44,7 @@ class IdentityEmbeddingLayer:
44
44
  embedding_client: Any = None, # Provider client for real embeddings
45
45
  embedding_model: str = "text-embedding-3-small",
46
46
  project_root: Path | None = None,
47
+ strict_embedding: bool = False,
47
48
  ) -> None:
48
49
  self._template = template
49
50
  self._embedding_client = embedding_client
@@ -52,6 +53,7 @@ class IdentityEmbeddingLayer:
52
53
  self._template_keywords: list[str] = []
53
54
  self._reference_text: str = "" # stored for lazy computation
54
55
  self._project_root = project_root
56
+ self._strict_embedding = strict_embedding
55
57
  self._embedding_method: str = "uninitialized"
56
58
  self._load_template()
57
59
 
@@ -165,6 +167,8 @@ class IdentityEmbeddingLayer:
165
167
  else:
166
168
  logger.debug("Embedding client does not support .embeddings API. Falling back.")
167
169
  except Exception as e:
170
+ if self._strict_embedding:
171
+ raise RuntimeError(f"Embedding API failed: {e}") from e
168
172
  logger.error(f"Embedding API failed: {e}. Falling back to local method.")
169
173
  if not self._template_keywords:
170
174
  # Fallback: character n-gram hash vector
@@ -85,7 +85,7 @@ class ProvenanceLog:
85
85
  self._vault_type = vault_type
86
86
  self._vault_path = Path(vault_path)
87
87
  self._hmac_key = hmac_key or self._load_or_generate_key()
88
- self._lock = threading.Lock()
88
+ self._lock = threading.RLock()
89
89
  self._conn = self._init_db()
90
90
  logger.info(f"ProvenanceLog initialized | vault={vault_path}")
91
91
 
@@ -134,7 +134,8 @@ class ProvenanceLog:
134
134
 
135
135
  Returns: True (valid) | raises ChainIntegrityError
136
136
  """
137
- entries = self._load_all_entries(include_rolled_back=False)
137
+ with self._lock:
138
+ entries = self._load_all_entries(include_rolled_back=False)
138
139
  if not entries:
139
140
  return True
140
141
 
@@ -157,14 +158,16 @@ class ProvenanceLog:
157
158
 
158
159
  def get_entries(self, limit: int = 100, include_rolled_back: bool = False) -> list[ProvenanceEntry]:
159
160
  """Returns the last N entries."""
160
- return self._load_all_entries(limit=limit, include_rolled_back=include_rolled_back)
161
+ with self._lock:
162
+ return self._load_all_entries(limit=limit, include_rolled_back=include_rolled_back)
161
163
 
162
164
  def get_entry(self, entry_id: str) -> ProvenanceEntry | None:
163
- cursor = self._conn.execute(
164
- "SELECT * FROM provenance WHERE id = ?", (entry_id,)
165
- )
166
- row = cursor.fetchone()
167
- return self._row_to_entry(row) if row else None
165
+ with self._lock:
166
+ cursor = self._conn.execute(
167
+ "SELECT * FROM provenance WHERE id = ?", (entry_id,)
168
+ )
169
+ row = cursor.fetchone()
170
+ return self._row_to_entry(row) if row else None
168
171
 
169
172
  def get_interaction_count(
170
173
  self, template: str, kernel_name: str, model: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ct-toolkit
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-2.0
@@ -51,6 +51,7 @@ CT Toolkit is an open-source security layer designed to preserve the **identity
51
51
  ---
52
52
 
53
53
  ## 📖 Official Documentation
54
+
54
55
  For full API reference, architecture details, examples, and integration guides, visit our documentation site:
55
56
  👉 [**https://hakandamar.github.io/ct-toolkit/**](https://hakandamar.github.io/ct-toolkit/)
56
57
 
@@ -63,6 +64,7 @@ For full API reference, architecture details, examples, and integration guides,
63
64
  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.
64
65
 
65
66
  - **Constitutional Kernels**: Axiomatic identity anchors.
67
+ - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
66
68
  - **Divergence Engine**: Multi-tiered drift analysis (L1/L2/L3).
67
69
  - **Hierarchical Propagation**: Mother-to-child constraint inheritance.
68
70
  - **Provenance Log**: Immutable HMAC-signed interaction history.
@@ -92,15 +94,16 @@ print(f"Divergence Score: {response.divergence_score}")
92
94
 
93
95
  ## 🚦 Project Health & Status
94
96
 
95
- | Metric | Status |
96
- | :--- | :--- |
97
- | **Tests** | ✅ 218/218 passing (93% coverage) |
98
- | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
99
- | **Current Goal** | 🔶 Phase 5: Vault and Security Infrastructure |
97
+ | Metric | Status |
98
+ | :--------------- | :------------------------------------------------------ |
99
+ | **Tests** | ✅ 231/232 passing (90% coverage) |
100
+ | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
101
+ | **Current Goal** | 🔶 Phase 5: Vault & Security (In Progress) |
100
102
 
101
103
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
102
104
 
103
105
  ### Framework & Model Support
106
+
104
107
  Seamlessly integrate with your favorite frameworks and local models:
105
108
 
106
109
  - **Local Models**: Support for LM Studio, Ollama, and local Qwen/Llama endpoints.
@@ -111,9 +114,11 @@ Seamlessly integrate with your favorite frameworks and local models:
111
114
  ---
112
115
 
113
116
  ## Theoretical Foundation
117
+
114
118
  Translating the framework proposed in [**The Computational Theseus (2025)**](https://hakandamar.com/the-computational-theseus-engineering-identity-continuity-as-a-guardrail-against-sequential-963918c1720d) into engineering practice.
115
119
 
116
120
  ---
117
121
 
118
122
  ## License
123
+
119
124
  Apache License 2.0.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ct-toolkit"
7
- version = "0.3.1"
7
+ version = "0.3.3"
8
8
  description = "Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes