ct-toolkit 0.2.1__tar.gz → 0.2.2__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.
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/PKG-INFO +3 -1
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/core/wrapper.py +5 -0
- ct_toolkit-0.2.2/ct_toolkit/divergence/loss.py +59 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit.egg-info/PKG-INFO +3 -1
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit.egg-info/SOURCES.txt +3 -1
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit.egg-info/requires.txt +3 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/pyproject.toml +2 -1
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/tests/test_analysis.py +30 -0
- ct_toolkit-0.2.2/tests/test_loss.py +66 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/LICENSE +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/README.md +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/core/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/core/compatibility.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/core/exceptions.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/core/kernel.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/divergence/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/divergence/analysis.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/divergence/engine.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/divergence/l2_judge.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/divergence/l3_icm.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/divergence/scheduler.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/endorsement/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/endorsement/probes/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/endorsement/reflective.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/identity/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/identity/embedding.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/middleware/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/middleware/autogen.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/middleware/crewai.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/middleware/langchain.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/provenance/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/provenance/log.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/provenance/vault/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/utils/__init__.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit/utils/logger.py +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit.egg-info/dependency_links.txt +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/ct_toolkit.egg-info/top_level.txt +0 -0
- {ct_toolkit-0.2.1 → ct_toolkit-0.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ct-toolkit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
|
|
5
5
|
Author: Hakan Damar
|
|
6
6
|
License: Apache-2.0
|
|
@@ -28,6 +28,8 @@ Provides-Extra: autogen
|
|
|
28
28
|
Requires-Dist: pyautogen>=0.4.0; extra == "autogen"
|
|
29
29
|
Provides-Extra: docs
|
|
30
30
|
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
31
|
+
Provides-Extra: ml
|
|
32
|
+
Requires-Dist: torch>=2.0.0; extra == "ml"
|
|
31
33
|
Provides-Extra: dev
|
|
32
34
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
33
35
|
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
@@ -250,6 +250,7 @@ class TheseusWrapper:
|
|
|
250
250
|
l3_threshold=self._config.divergence_l3_threshold,
|
|
251
251
|
enterprise_mode=self._config.enterprise_mode,
|
|
252
252
|
scheduler=scheduler,
|
|
253
|
+
provenance_log=self._provenance_log,
|
|
253
254
|
)
|
|
254
255
|
|
|
255
256
|
def _log_init(self) -> None:
|
|
@@ -535,6 +536,10 @@ class TheseusWrapper:
|
|
|
535
536
|
def compatibility(self) -> CompatibilityResult:
|
|
536
537
|
return self._compatibility
|
|
537
538
|
|
|
539
|
+
@property
|
|
540
|
+
def divergence_engine(self) -> Any:
|
|
541
|
+
return self._divergence_engine
|
|
542
|
+
|
|
538
543
|
def __repr__(self) -> str:
|
|
539
544
|
return (
|
|
540
545
|
f"TheseusWrapper("
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
import torch.nn as nn
|
|
5
|
+
import torch.nn.functional as F
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
class DivergencePenaltyLoss(nn.Module):
|
|
9
|
+
"""
|
|
10
|
+
Implements the Divergence Penalty as defined in Section 5.1 of the research paper.
|
|
11
|
+
|
|
12
|
+
This loss function penalizes normative drift by calculating the distance between
|
|
13
|
+
the model's generated embeddings and a reference 'Kernel' embedding.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, alpha: float = 1.0, distance_metric: str = "cosine"):
|
|
16
|
+
super().__init__()
|
|
17
|
+
self.alpha = alpha
|
|
18
|
+
self.distance_metric = distance_metric
|
|
19
|
+
|
|
20
|
+
def forward(
|
|
21
|
+
self,
|
|
22
|
+
current_embeddings: torch.Tensor,
|
|
23
|
+
reference_embeddings: torch.Tensor
|
|
24
|
+
) -> torch.Tensor:
|
|
25
|
+
"""
|
|
26
|
+
Calculates the identity-divergence penalty.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
current_embeddings: The hidden states or outputs of the model being trained.
|
|
30
|
+
reference_embeddings: The frozen CIK (Constitutional Identity Kernel) embeddings.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A scalar tensor representing the penalty.
|
|
34
|
+
"""
|
|
35
|
+
if self.distance_metric == "cosine":
|
|
36
|
+
# 1 - cosine_similarity (range: 0 to 2, where 0 is identical)
|
|
37
|
+
similarity = F.cosine_similarity(current_embeddings, reference_embeddings, dim=-1)
|
|
38
|
+
dist = 1.0 - similarity
|
|
39
|
+
else:
|
|
40
|
+
# Fallback to MSE / Euclidean distance
|
|
41
|
+
dist = F.mse_loss(current_embeddings, reference_embeddings, reduction='none')
|
|
42
|
+
if dist.dim() > 1:
|
|
43
|
+
dist = dist.mean(dim=-1)
|
|
44
|
+
|
|
45
|
+
# Penalize drift beyond a certain magnitude
|
|
46
|
+
penalty = self.alpha * torch.mean(dist)
|
|
47
|
+
|
|
48
|
+
return penalty
|
|
49
|
+
|
|
50
|
+
def compute_alignment_loss(
|
|
51
|
+
model_output_embeddings: torch.Tensor,
|
|
52
|
+
kernel_anchor_embeddings: torch.Tensor,
|
|
53
|
+
alpha: float = 0.5
|
|
54
|
+
) -> torch.Tensor:
|
|
55
|
+
"""
|
|
56
|
+
Helper function to compute identity alignment loss during training.
|
|
57
|
+
"""
|
|
58
|
+
criterion = DivergencePenaltyLoss(alpha=alpha)
|
|
59
|
+
return criterion(model_output_embeddings, kernel_anchor_embeddings)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ct-toolkit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
|
|
5
5
|
Author: Hakan Damar
|
|
6
6
|
License: Apache-2.0
|
|
@@ -28,6 +28,8 @@ Provides-Extra: autogen
|
|
|
28
28
|
Requires-Dist: pyautogen>=0.4.0; extra == "autogen"
|
|
29
29
|
Provides-Extra: docs
|
|
30
30
|
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
31
|
+
Provides-Extra: ml
|
|
32
|
+
Requires-Dist: torch>=2.0.0; extra == "ml"
|
|
31
33
|
Provides-Extra: dev
|
|
32
34
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
33
35
|
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
@@ -17,6 +17,7 @@ ct_toolkit/divergence/analysis.py
|
|
|
17
17
|
ct_toolkit/divergence/engine.py
|
|
18
18
|
ct_toolkit/divergence/l2_judge.py
|
|
19
19
|
ct_toolkit/divergence/l3_icm.py
|
|
20
|
+
ct_toolkit/divergence/loss.py
|
|
20
21
|
ct_toolkit/divergence/scheduler.py
|
|
21
22
|
ct_toolkit/endorsement/__init__.py
|
|
22
23
|
ct_toolkit/endorsement/reflective.py
|
|
@@ -32,4 +33,5 @@ ct_toolkit/provenance/log.py
|
|
|
32
33
|
ct_toolkit/provenance/vault/__init__.py
|
|
33
34
|
ct_toolkit/utils/__init__.py
|
|
34
35
|
ct_toolkit/utils/logger.py
|
|
35
|
-
tests/test_analysis.py
|
|
36
|
+
tests/test_analysis.py
|
|
37
|
+
tests/test_loss.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ct-toolkit"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -31,6 +31,7 @@ langchain = ["langchain-core>=1.2.0"]
|
|
|
31
31
|
crewai = ["crewai>=1.10.0"]
|
|
32
32
|
autogen = ["pyautogen>=0.4.0"]
|
|
33
33
|
docs = ["mkdocs-material>=9.5.0"]
|
|
34
|
+
ml = ["torch>=2.0.0"]
|
|
34
35
|
dev = [
|
|
35
36
|
"pytest>=8.0",
|
|
36
37
|
"pytest-asyncio>=0.23",
|
|
@@ -53,6 +53,36 @@ class TestPolicyDrift(unittest.TestCase):
|
|
|
53
53
|
self.assertGreater(score_high, score_low)
|
|
54
54
|
print(f"Risk Normalized Test: LowRiskScore={score_low}, HighRiskScore={score_high}")
|
|
55
55
|
|
|
56
|
+
def test_missing_metadata(self):
|
|
57
|
+
# Entry with missing template/kernel/model should be ignored
|
|
58
|
+
entry_missing = MagicMock(spec=ProvenanceEntry)
|
|
59
|
+
entry_missing.divergence_score = 0.5
|
|
60
|
+
entry_missing.metadata = {} # Missing everything
|
|
61
|
+
|
|
62
|
+
entry_valid = MagicMock(spec=ProvenanceEntry)
|
|
63
|
+
entry_valid.divergence_score = 0.1
|
|
64
|
+
entry_valid.metadata = {"template": "general", "kernel": "standard", "model": "gpt-4"}
|
|
65
|
+
|
|
66
|
+
self.mock_log.get_entries.return_value = [entry_missing] * 10 + [entry_valid] * 5
|
|
67
|
+
|
|
68
|
+
report = self.analyzer.analyze_drift("general", "standard", "gpt-4")
|
|
69
|
+
# Only the 5 valid ones should be counted
|
|
70
|
+
self.assertEqual(report.data_points, 5)
|
|
71
|
+
|
|
72
|
+
def test_inconsistent_scores(self):
|
|
73
|
+
# Even with high variance and negative velocity, shouldn't crash
|
|
74
|
+
entries = []
|
|
75
|
+
for i in range(10):
|
|
76
|
+
entry = MagicMock(spec=ProvenanceEntry)
|
|
77
|
+
entry.divergence_score = 0.5 - (i * 0.05) # Decreasing divergence
|
|
78
|
+
entry.metadata = {"template": "general", "kernel": "standard", "model": "gpt-4"}
|
|
79
|
+
entries.append(entry)
|
|
80
|
+
|
|
81
|
+
self.mock_log.get_entries.return_value = entries
|
|
82
|
+
report = self.analyzer.analyze_drift("general", "standard", "gpt-4")
|
|
83
|
+
self.assertLessEqual(report.drift_velocity, 0)
|
|
84
|
+
self.assertEqual(report.ssc_severity_score, 0.0) # Negative velocity = 0 severity
|
|
85
|
+
|
|
56
86
|
class TestSSCSeverity(unittest.TestCase):
|
|
57
87
|
def test_severity_formula(self):
|
|
58
88
|
calc = SSCSeverityCalculator()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import torch
|
|
3
|
+
import torch.nn as nn
|
|
4
|
+
from ct_toolkit.divergence.loss import DivergencePenaltyLoss
|
|
5
|
+
|
|
6
|
+
class TestDivergenceLoss(unittest.TestCase):
|
|
7
|
+
def test_gradient_flow(self):
|
|
8
|
+
# Setup: Current embedding (trainable) and Reference (frozen)
|
|
9
|
+
current = torch.randn(1, 128, requires_grad=True)
|
|
10
|
+
reference = torch.randn(1, 128)
|
|
11
|
+
|
|
12
|
+
criterion = DivergencePenaltyLoss(alpha=1.0)
|
|
13
|
+
loss = criterion(current, reference)
|
|
14
|
+
|
|
15
|
+
# Verify loss can backpropagate
|
|
16
|
+
loss.backward()
|
|
17
|
+
|
|
18
|
+
self.assertIsNotNone(current.grad)
|
|
19
|
+
self.assertGreater(loss.item(), 0)
|
|
20
|
+
|
|
21
|
+
def test_identical_embeddings(self):
|
|
22
|
+
# If embeddings are identical, loss should be near zero (for cosine distance)
|
|
23
|
+
current = torch.ones(1, 128, requires_grad=True)
|
|
24
|
+
reference = torch.ones(1, 128)
|
|
25
|
+
|
|
26
|
+
criterion = DivergencePenaltyLoss(alpha=1.0, distance_metric="cosine")
|
|
27
|
+
loss = criterion(current, reference)
|
|
28
|
+
|
|
29
|
+
self.assertLess(loss.item(), 1e-6)
|
|
30
|
+
|
|
31
|
+
def test_high_divergence_penalty(self):
|
|
32
|
+
# Orthogonal embeddings should have higher loss than parallel ones
|
|
33
|
+
ref = torch.tensor([[1.0, 0.0]])
|
|
34
|
+
parallel = torch.tensor([[0.9, 0.0]], requires_grad=True)
|
|
35
|
+
orthogonal = torch.tensor([[0.0, 1.0]], requires_grad=True)
|
|
36
|
+
|
|
37
|
+
criterion = DivergencePenaltyLoss(alpha=1.0)
|
|
38
|
+
|
|
39
|
+
loss_low = criterion(parallel, ref)
|
|
40
|
+
loss_high = criterion(orthogonal, ref)
|
|
41
|
+
|
|
42
|
+
self.assertGreater(loss_high.item(), loss_low.item())
|
|
43
|
+
|
|
44
|
+
def test_batch_sequence_dimensions(self):
|
|
45
|
+
# Test with [Batch, Seq, Dim] common in LLM hidden states
|
|
46
|
+
current = torch.randn(8, 32, 128, requires_grad=True)
|
|
47
|
+
# Reference might be a single anchor [1, 128] or matched [8, 32, 128]
|
|
48
|
+
reference = torch.randn(1, 1, 128)
|
|
49
|
+
|
|
50
|
+
criterion = DivergencePenaltyLoss(alpha=1.0)
|
|
51
|
+
loss = criterion(current, reference)
|
|
52
|
+
|
|
53
|
+
loss.backward()
|
|
54
|
+
self.assertEqual(current.grad.shape, current.shape)
|
|
55
|
+
|
|
56
|
+
def test_dimension_mismatch(self):
|
|
57
|
+
# Should raise error if dimensions are completely incompatible
|
|
58
|
+
current = torch.randn(1, 128)
|
|
59
|
+
reference = torch.randn(1, 64)
|
|
60
|
+
|
|
61
|
+
criterion = DivergencePenaltyLoss(alpha=1.0)
|
|
62
|
+
with self.assertRaises(RuntimeError):
|
|
63
|
+
criterion(current, reference)
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|