traceiq 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
traceiq/__init__.py ADDED
@@ -0,0 +1,48 @@
1
+ """TraceIQ: Measure AI-to-AI influence in multi-agent systems."""
2
+
3
+ from importlib.metadata import version as _get_version
4
+
5
+ from traceiq.capabilities import CapabilityRegistry
6
+ from traceiq.metrics import (
7
+ compute_accumulated_influence,
8
+ compute_attack_surface,
9
+ compute_drift_l2,
10
+ compute_IQx,
11
+ compute_propagation_risk,
12
+ compute_RWI,
13
+ compute_z_score,
14
+ )
15
+ from traceiq.models import (
16
+ AgentCapabilities,
17
+ InteractionEvent,
18
+ PropagationRiskResult,
19
+ ScoreResult,
20
+ SummaryReport,
21
+ TrackerConfig,
22
+ )
23
+ from traceiq.tracker import InfluenceTracker
24
+
25
+ __version__ = _get_version("traceiq")
26
+
27
+ __all__ = [
28
+ # Core classes
29
+ "InfluenceTracker",
30
+ "CapabilityRegistry",
31
+ # Models
32
+ "InteractionEvent",
33
+ "ScoreResult",
34
+ "SummaryReport",
35
+ "TrackerConfig",
36
+ "AgentCapabilities",
37
+ "PropagationRiskResult",
38
+ # Metrics functions
39
+ "compute_drift_l2",
40
+ "compute_IQx",
41
+ "compute_accumulated_influence",
42
+ "compute_propagation_risk",
43
+ "compute_attack_surface",
44
+ "compute_RWI",
45
+ "compute_z_score",
46
+ # Version
47
+ "__version__",
48
+ ]
@@ -0,0 +1,215 @@
1
+ """Agent capability registry for attack surface computation.
2
+
3
+ This module provides a registry for tracking agent capabilities and computing
4
+ attack surface scores. The attack surface represents the potential risk
5
+ associated with an agent based on its permissions/capabilities.
6
+
7
+ Default capability weights are provided based on common security risk assessments:
8
+ - execute_code: 1.0 (high risk - can run arbitrary code)
9
+ - admin: 1.5 (highest risk - full system access)
10
+ - network_access: 0.8 (can exfiltrate data or communicate externally)
11
+ - file_write: 0.7 (can modify system state)
12
+ - memory_access: 0.5 (can read/write memory)
13
+ - file_read: 0.3 (limited risk - read-only)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ from pathlib import Path
20
+ from typing import Any
21
+
22
+ from traceiq.metrics import compute_attack_surface
23
+ from traceiq.models import AgentCapabilities
24
+
25
+ # Default weights based on security risk assessment
26
+ DEFAULT_CAPABILITY_WEIGHTS: dict[str, float] = {
27
+ "execute_code": 1.0,
28
+ "network_access": 0.8,
29
+ "file_write": 0.7,
30
+ "file_read": 0.3,
31
+ "admin": 1.5,
32
+ "memory_access": 0.5,
33
+ "database_write": 0.6,
34
+ "database_read": 0.3,
35
+ "api_access": 0.4,
36
+ "subprocess": 0.9,
37
+ }
38
+
39
+
40
+ class CapabilityRegistry:
41
+ """Registry for agent capabilities and attack surface computation."""
42
+
43
+ def __init__(self, weights: dict[str, float] | None = None) -> None:
44
+ """Initialize the capability registry.
45
+
46
+ Args:
47
+ weights: Custom capability weights. If None, uses DEFAULT_CAPABILITY_WEIGHTS.
48
+ """
49
+ self._weights = dict(DEFAULT_CAPABILITY_WEIGHTS)
50
+ if weights:
51
+ self._weights.update(weights)
52
+
53
+ # Maps agent_id -> list of capabilities
54
+ self._agents: dict[str, list[str]] = {}
55
+
56
+ @property
57
+ def weights(self) -> dict[str, float]:
58
+ """Get current capability weights."""
59
+ return dict(self._weights)
60
+
61
+ def load_from_file(self, path: str | Path) -> None:
62
+ """Load registry state from a JSON file.
63
+
64
+ Expected format:
65
+ {
66
+ "weights": {"capability": weight, ...},
67
+ "agents": {"agent_id": ["cap1", "cap2"], ...}
68
+ }
69
+
70
+ Args:
71
+ path: Path to JSON file
72
+ """
73
+ path = Path(path)
74
+ with open(path) as f:
75
+ data = json.load(f)
76
+
77
+ if "weights" in data:
78
+ self._weights.update(data["weights"])
79
+
80
+ if "agents" in data:
81
+ for agent_id, caps in data["agents"].items():
82
+ self._agents[agent_id] = list(caps)
83
+
84
+ def save_to_file(self, path: str | Path) -> None:
85
+ """Save registry state to a JSON file.
86
+
87
+ Args:
88
+ path: Path to JSON file
89
+ """
90
+ path = Path(path)
91
+ data = {
92
+ "weights": self._weights,
93
+ "agents": self._agents,
94
+ }
95
+ with open(path, "w") as f:
96
+ json.dump(data, f, indent=2)
97
+
98
+ def register_agent(self, agent_id: str, capabilities: list[str]) -> None:
99
+ """Register an agent with its capabilities.
100
+
101
+ Args:
102
+ agent_id: Unique agent identifier
103
+ capabilities: List of capability names
104
+ """
105
+ self._agents[agent_id] = list(capabilities)
106
+
107
+ def unregister_agent(self, agent_id: str) -> None:
108
+ """Remove an agent from the registry.
109
+
110
+ Args:
111
+ agent_id: Agent to remove
112
+ """
113
+ self._agents.pop(agent_id, None)
114
+
115
+ def get_capabilities(self, agent_id: str) -> list[str]:
116
+ """Get capabilities for an agent.
117
+
118
+ Args:
119
+ agent_id: Agent identifier
120
+
121
+ Returns:
122
+ List of capability names, empty if agent not registered
123
+ """
124
+ return list(self._agents.get(agent_id, []))
125
+
126
+ def compute_attack_surface(self, agent_id: str) -> float:
127
+ """Compute attack surface score for an agent.
128
+
129
+ Args:
130
+ agent_id: Agent identifier
131
+
132
+ Returns:
133
+ Sum of capability weights for this agent
134
+ """
135
+ caps = self.get_capabilities(agent_id)
136
+ return compute_attack_surface(caps, self._weights)
137
+
138
+ def set_capability_weight(self, capability: str, weight: float) -> None:
139
+ """Set or update weight for a capability.
140
+
141
+ Args:
142
+ capability: Capability name
143
+ weight: Weight value (should be >= 0)
144
+ """
145
+ if weight < 0:
146
+ raise ValueError("Capability weight must be non-negative")
147
+ self._weights[capability] = weight
148
+
149
+ def get_agent_capabilities_model(self, agent_id: str) -> AgentCapabilities:
150
+ """Get AgentCapabilities model for an agent.
151
+
152
+ Args:
153
+ agent_id: Agent identifier
154
+
155
+ Returns:
156
+ AgentCapabilities model with computed attack surface
157
+ """
158
+ caps = self.get_capabilities(agent_id)
159
+ attack_surface = self.compute_attack_surface(agent_id)
160
+ return AgentCapabilities(
161
+ agent_id=agent_id,
162
+ capabilities=caps,
163
+ attack_surface=attack_surface,
164
+ )
165
+
166
+ def get_all_agents(self) -> list[str]:
167
+ """Get list of all registered agent IDs.
168
+
169
+ Returns:
170
+ List of agent identifiers
171
+ """
172
+ return list(self._agents.keys())
173
+
174
+ def get_all_capabilities_models(self) -> list[AgentCapabilities]:
175
+ """Get AgentCapabilities models for all registered agents.
176
+
177
+ Returns:
178
+ List of AgentCapabilities models
179
+ """
180
+ return [self.get_agent_capabilities_model(aid) for aid in self._agents]
181
+
182
+ def to_dict(self) -> dict[str, Any]:
183
+ """Serialize registry to dictionary.
184
+
185
+ Returns:
186
+ Dict with weights and agents
187
+ """
188
+ return {
189
+ "weights": self._weights,
190
+ "agents": self._agents,
191
+ }
192
+
193
+ @classmethod
194
+ def from_dict(cls, data: dict[str, Any]) -> CapabilityRegistry:
195
+ """Create registry from dictionary.
196
+
197
+ Args:
198
+ data: Dict with weights and agents
199
+
200
+ Returns:
201
+ New CapabilityRegistry instance
202
+ """
203
+ registry = cls(weights=data.get("weights"))
204
+ if "agents" in data:
205
+ for agent_id, caps in data["agents"].items():
206
+ registry.register_agent(agent_id, caps)
207
+ return registry
208
+
209
+ def __len__(self) -> int:
210
+ """Return number of registered agents."""
211
+ return len(self._agents)
212
+
213
+ def __contains__(self, agent_id: str) -> bool:
214
+ """Check if agent is registered."""
215
+ return agent_id in self._agents