ct-toolkit 0.3.2__tar.gz → 0.3.4__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.2 → ct_toolkit-0.3.4}/PKG-INFO +1 -1
  2. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/__init__.py +0 -1
  3. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/core/wrapper.py +5 -2
  4. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/divergence/l3_icm.py +44 -33
  5. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/endorsement/reflective.py +21 -18
  6. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/identity/embedding.py +4 -0
  7. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/provenance/log.py +11 -8
  8. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit.egg-info/PKG-INFO +1 -1
  9. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/pyproject.toml +1 -1
  10. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/LICENSE +0 -0
  11. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/README.md +0 -0
  12. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/core/__init__.py +0 -0
  13. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/core/compatibility.py +0 -0
  14. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/core/exceptions.py +0 -0
  15. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/core/integrity.py +0 -0
  16. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/core/kernel.py +0 -0
  17. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/divergence/__init__.py +0 -0
  18. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/divergence/analysis.py +0 -0
  19. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/divergence/engine.py +0 -0
  20. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/divergence/l2_judge.py +0 -0
  21. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/divergence/loss.py +0 -0
  22. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/divergence/scheduler.py +0 -0
  23. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/endorsement/__init__.py +0 -0
  24. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/endorsement/probes/__init__.py +0 -0
  25. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/identity/__init__.py +0 -0
  26. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/identity/templates/__init__.py +0 -0
  27. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/identity/templates/defense.yaml +0 -0
  28. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/identity/templates/finance.yaml +0 -0
  29. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/identity/templates/general.yaml +0 -0
  30. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/identity/templates/medical.yaml +0 -0
  31. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/kernels/__init__.py +0 -0
  32. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/kernels/default.yaml +0 -0
  33. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/kernels/defense.yaml +0 -0
  34. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/middleware/__init__.py +0 -0
  35. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/middleware/autogen.py +0 -0
  36. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/middleware/crewai.py +0 -0
  37. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/middleware/deepagents.py +0 -0
  38. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/middleware/langchain.py +0 -0
  39. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/provenance/__init__.py +0 -0
  40. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/provenance/vault/__init__.py +0 -0
  41. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/utils/__init__.py +0 -0
  42. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit/utils/logger.py +0 -0
  43. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit.egg-info/SOURCES.txt +0 -0
  44. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit.egg-info/dependency_links.txt +0 -0
  45. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit.egg-info/requires.txt +0 -0
  46. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/ct_toolkit.egg-info/top_level.txt +0 -0
  47. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/setup.cfg +0 -0
  48. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/tests/test_analysis.py +0 -0
  49. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/tests/test_deep_agents_integration.py +0 -0
  50. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/tests/test_loss.py +0 -0
  51. {ct_toolkit-0.3.2 → ct_toolkit-0.3.4}/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.2
3
+ Version: 0.3.4
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-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
@@ -69,6 +69,8 @@ class WrapperConfig:
69
69
  elasticity_growth_rate: float | None = None
70
70
  risk_profile: RiskProfile | None = None
71
71
 
72
+ strict_embedding: bool = False # Raise error if embedding API fails
73
+
72
74
 
73
75
  @dataclass
74
76
  class CTResponse:
@@ -192,7 +194,7 @@ class TheseusWrapper:
192
194
  if file_path.is_file():
193
195
  self._integrity_monitor.register_file(file_path)
194
196
 
195
- except (ImportError, ModuleNotFoundError):
197
+ except Exception:
196
198
  # Fallback for older Python or different environments
197
199
  package_root = Path(__file__).parent.parent
198
200
  # This is less ideal as it might find non-package files, but it's a
@@ -309,6 +311,7 @@ class TheseusWrapper:
309
311
  embedding_client=emb_client,
310
312
  embedding_model=self._config.embedding_model,
311
313
  project_root=self._project_root,
314
+ strict_embedding=self._config.strict_embedding,
312
315
  )
313
316
 
314
317
  def _init_divergence_engine(self) -> Any:
@@ -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.2
3
+ Version: 0.3.4
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-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.2"
7
+ version = "0.3.4"
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
File without changes