agent-forensics 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ilya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-forensics
3
+ Version: 0.1.0
4
+ Summary: Black box for AI agents — capture decisions, generate forensic reports for EU AI Act compliance
5
+ Author: ilya
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ilya/agent-forensics
8
+ Keywords: ai,agent,forensics,compliance,eu-ai-act,observability
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Libraries
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Provides-Extra: langchain
18
+ Requires-Dist: langchain-core>=0.3; extra == "langchain"
19
+ Provides-Extra: openai-agents
20
+ Requires-Dist: openai-agents>=0.1; extra == "openai-agents"
21
+ Provides-Extra: crewai
22
+ Requires-Dist: crewai>=0.80; extra == "crewai"
23
+ Provides-Extra: pdf
24
+ Requires-Dist: fpdf2>=2.8; extra == "pdf"
25
+ Provides-Extra: all
26
+ Requires-Dist: langchain-core>=0.3; extra == "all"
27
+ Requires-Dist: openai-agents>=0.1; extra == "all"
28
+ Requires-Dist: crewai>=0.80; extra == "all"
29
+ Requires-Dist: fpdf2>=2.8; extra == "all"
30
+ Dynamic: license-file
31
+
32
+ # Agent Forensics
33
+
34
+ **Black box for AI agents.** Capture every decision, generate forensic reports for EU AI Act compliance.
35
+
36
+ When an AI agent makes a wrong purchase, leaks data, or fails silently — you need to know **why**. Agent Forensics records every decision point, tool call, and LLM interaction, then reconstructs the causal chain when things go wrong.
37
+
38
+ ![Agent Forensics Dashboard — Incident Session](content/screenshots/02-incident-overview.png)
39
+
40
+ ## Why
41
+
42
+ - **EU AI Act** (Aug 2026): High-risk AI systems must provide decision traceability. Fines up to €35M or 7% of global revenue.
43
+ - **AI agents are already causing incidents**: Meta Sev-1 data leak (Mar 2026), unauthorized purchases, fabricated customer responses.
44
+ - **No existing tool** reconstructs the *why* behind agent failures. Monitoring tools watch in real-time. Forensics analyzes after the fact.
45
+
46
+ ## Install
47
+
48
+ ### From PyPI
49
+
50
+ ```bash
51
+ pip install agent-forensics # Core only (manual recording)
52
+ pip install agent-forensics[langchain] # + LangChain integration
53
+ pip install agent-forensics[openai-agents] # + OpenAI Agents SDK
54
+ pip install agent-forensics[crewai] # + CrewAI
55
+ pip install agent-forensics[all] # Everything
56
+ ```
57
+
58
+ ### From Source
59
+
60
+ ```bash
61
+ git clone https://github.com/ilflow4592/agent-forensics.git
62
+ cd agent-forensics
63
+ pip install -e ".[all]"
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ ### Manual Recording (Framework-Agnostic)
69
+
70
+ ```python
71
+ from agent_forensics import Forensics
72
+
73
+ f = Forensics(session="order-123", agent="shopping-agent")
74
+
75
+ f.decision("search_products", input={"query": "mouse"}, reasoning="User requested product search")
76
+ f.tool_call("search_api", input={"q": "mouse"}, output={"results": [...]})
77
+ f.error("purchase_failed", output={"reason": "Out of stock"})
78
+ f.finish("Could not complete purchase due to stock unavailability.")
79
+
80
+ print(f.report()) # Markdown forensic report
81
+ f.save_pdf() # PDF report
82
+ f.dashboard(port=8080) # Web dashboard at http://localhost:8080
83
+ ```
84
+
85
+ ### LangChain — One Line
86
+
87
+ ```python
88
+ from agent_forensics import Forensics
89
+
90
+ f = Forensics(session="order-123")
91
+ agent.invoke({"input": "..."}, config={"callbacks": [f.langchain()]})
92
+ ```
93
+
94
+ ### OpenAI Agents SDK — One Line
95
+
96
+ ```python
97
+ from agent_forensics import Forensics
98
+ from agents import Agent, Runner
99
+
100
+ f = Forensics(session="order-123")
101
+ agent = Agent(name="shopper", tools=[...], hooks=f.openai_agents())
102
+ result = await Runner.run(agent, "Buy a wireless mouse")
103
+ ```
104
+
105
+ ### CrewAI — Two Lines
106
+
107
+ ```python
108
+ from agent_forensics import Forensics
109
+
110
+ f = Forensics(session="order-123")
111
+ hooks = f.crewai()
112
+ agent = Agent(role="shopper", step_callback=hooks.step_callback)
113
+ task = Task(description="...", agent=agent, callback=hooks.task_callback)
114
+ ```
115
+
116
+ ## What You Get
117
+
118
+ ### Forensic Report
119
+
120
+ Every report includes:
121
+
122
+ - **Timeline** — Chronological record of all agent actions
123
+ - **Decision Chain** — Each decision with its reasoning
124
+ - **Incident Analysis** — Automatic error detection + root cause chain
125
+ - **Causal Chain** — `Decision → Tool Call → Result → Error` trace
126
+ - **Compliance Notes** — EU AI Act Article 14 (Human Oversight) support
127
+
128
+ ### Web Dashboard
129
+
130
+ ```bash
131
+ python -c "from agent_forensics import Forensics; Forensics(db_path='forensics.db').dashboard()"
132
+ ```
133
+
134
+ Dark-themed dashboard with session selector, color-coded timeline, and causal chain visualization.
135
+
136
+ ![Causal Chain — Root Cause Analysis](content/screenshots/04-causal-chain.png)
137
+
138
+ ![Data Leak Incident](content/screenshots/05-data-leak.png)
139
+
140
+ ### Output Formats
141
+
142
+ - **Markdown** — `f.save_markdown()`
143
+ - **PDF** — `f.save_pdf()` (requires `pip install agent-forensics[pdf]`)
144
+ - **Web Dashboard** — `f.dashboard(port=8080)`
145
+ - **Raw Events** — `f.events()` returns `list[Event]`
146
+
147
+ ## Architecture
148
+
149
+ ```
150
+ Your Agent (any framework)
151
+
152
+ │ Callback / Hook (1 line of code)
153
+
154
+ ┌──────────────────────┐
155
+ │ Forensics Collector │ Captures every LLM call, tool use, decision
156
+ ├──────────────────────┤
157
+ │ Event Store (SQLite) │ Immutable event log
158
+ ├──────────────────────┤
159
+ │ Report Generator │ Markdown / PDF / Dashboard
160
+ └──────────────────────┘
161
+ ```
162
+
163
+ ## Supported Frameworks
164
+
165
+ | Framework | Integration | Method |
166
+ |-----------|------------|--------|
167
+ | **Any** (manual) | `f.decision()`, `f.tool_call()`, `f.error()` | Direct API |
168
+ | **LangChain / LangGraph** | `f.langchain()` | Callback Handler |
169
+ | **OpenAI Agents SDK** | `f.openai_agents()` | AgentHooks |
170
+ | **CrewAI** | `f.crewai()` | step_callback / task_callback |
171
+
172
+ ## Event Types
173
+
174
+ | Type | When | Why It Matters |
175
+ |------|------|---------------|
176
+ | `decision` | Agent decides what to do next | Core of forensics — the *why* |
177
+ | `tool_call_start` | Tool execution begins | What tool, what input |
178
+ | `tool_call_end` | Tool returns result | What came back |
179
+ | `llm_call_start` | LLM request sent | What was asked |
180
+ | `llm_call_end` | LLM response received | What was answered |
181
+ | `error` | Something went wrong | Incident detection |
182
+ | `final_decision` | Agent produces final output | End of decision chain |
183
+
184
+ ## License
185
+
186
+ MIT
@@ -0,0 +1,155 @@
1
+ # Agent Forensics
2
+
3
+ **Black box for AI agents.** Capture every decision, generate forensic reports for EU AI Act compliance.
4
+
5
+ When an AI agent makes a wrong purchase, leaks data, or fails silently — you need to know **why**. Agent Forensics records every decision point, tool call, and LLM interaction, then reconstructs the causal chain when things go wrong.
6
+
7
+ ![Agent Forensics Dashboard — Incident Session](content/screenshots/02-incident-overview.png)
8
+
9
+ ## Why
10
+
11
+ - **EU AI Act** (Aug 2026): High-risk AI systems must provide decision traceability. Fines up to €35M or 7% of global revenue.
12
+ - **AI agents are already causing incidents**: Meta Sev-1 data leak (Mar 2026), unauthorized purchases, fabricated customer responses.
13
+ - **No existing tool** reconstructs the *why* behind agent failures. Monitoring tools watch in real-time. Forensics analyzes after the fact.
14
+
15
+ ## Install
16
+
17
+ ### From PyPI
18
+
19
+ ```bash
20
+ pip install agent-forensics # Core only (manual recording)
21
+ pip install agent-forensics[langchain] # + LangChain integration
22
+ pip install agent-forensics[openai-agents] # + OpenAI Agents SDK
23
+ pip install agent-forensics[crewai] # + CrewAI
24
+ pip install agent-forensics[all] # Everything
25
+ ```
26
+
27
+ ### From Source
28
+
29
+ ```bash
30
+ git clone https://github.com/ilflow4592/agent-forensics.git
31
+ cd agent-forensics
32
+ pip install -e ".[all]"
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ### Manual Recording (Framework-Agnostic)
38
+
39
+ ```python
40
+ from agent_forensics import Forensics
41
+
42
+ f = Forensics(session="order-123", agent="shopping-agent")
43
+
44
+ f.decision("search_products", input={"query": "mouse"}, reasoning="User requested product search")
45
+ f.tool_call("search_api", input={"q": "mouse"}, output={"results": [...]})
46
+ f.error("purchase_failed", output={"reason": "Out of stock"})
47
+ f.finish("Could not complete purchase due to stock unavailability.")
48
+
49
+ print(f.report()) # Markdown forensic report
50
+ f.save_pdf() # PDF report
51
+ f.dashboard(port=8080) # Web dashboard at http://localhost:8080
52
+ ```
53
+
54
+ ### LangChain — One Line
55
+
56
+ ```python
57
+ from agent_forensics import Forensics
58
+
59
+ f = Forensics(session="order-123")
60
+ agent.invoke({"input": "..."}, config={"callbacks": [f.langchain()]})
61
+ ```
62
+
63
+ ### OpenAI Agents SDK — One Line
64
+
65
+ ```python
66
+ from agent_forensics import Forensics
67
+ from agents import Agent, Runner
68
+
69
+ f = Forensics(session="order-123")
70
+ agent = Agent(name="shopper", tools=[...], hooks=f.openai_agents())
71
+ result = await Runner.run(agent, "Buy a wireless mouse")
72
+ ```
73
+
74
+ ### CrewAI — Two Lines
75
+
76
+ ```python
77
+ from agent_forensics import Forensics
78
+
79
+ f = Forensics(session="order-123")
80
+ hooks = f.crewai()
81
+ agent = Agent(role="shopper", step_callback=hooks.step_callback)
82
+ task = Task(description="...", agent=agent, callback=hooks.task_callback)
83
+ ```
84
+
85
+ ## What You Get
86
+
87
+ ### Forensic Report
88
+
89
+ Every report includes:
90
+
91
+ - **Timeline** — Chronological record of all agent actions
92
+ - **Decision Chain** — Each decision with its reasoning
93
+ - **Incident Analysis** — Automatic error detection + root cause chain
94
+ - **Causal Chain** — `Decision → Tool Call → Result → Error` trace
95
+ - **Compliance Notes** — EU AI Act Article 14 (Human Oversight) support
96
+
97
+ ### Web Dashboard
98
+
99
+ ```bash
100
+ python -c "from agent_forensics import Forensics; Forensics(db_path='forensics.db').dashboard()"
101
+ ```
102
+
103
+ Dark-themed dashboard with session selector, color-coded timeline, and causal chain visualization.
104
+
105
+ ![Causal Chain — Root Cause Analysis](content/screenshots/04-causal-chain.png)
106
+
107
+ ![Data Leak Incident](content/screenshots/05-data-leak.png)
108
+
109
+ ### Output Formats
110
+
111
+ - **Markdown** — `f.save_markdown()`
112
+ - **PDF** — `f.save_pdf()` (requires `pip install agent-forensics[pdf]`)
113
+ - **Web Dashboard** — `f.dashboard(port=8080)`
114
+ - **Raw Events** — `f.events()` returns `list[Event]`
115
+
116
+ ## Architecture
117
+
118
+ ```
119
+ Your Agent (any framework)
120
+
121
+ │ Callback / Hook (1 line of code)
122
+
123
+ ┌──────────────────────┐
124
+ │ Forensics Collector │ Captures every LLM call, tool use, decision
125
+ ├──────────────────────┤
126
+ │ Event Store (SQLite) │ Immutable event log
127
+ ├──────────────────────┤
128
+ │ Report Generator │ Markdown / PDF / Dashboard
129
+ └──────────────────────┘
130
+ ```
131
+
132
+ ## Supported Frameworks
133
+
134
+ | Framework | Integration | Method |
135
+ |-----------|------------|--------|
136
+ | **Any** (manual) | `f.decision()`, `f.tool_call()`, `f.error()` | Direct API |
137
+ | **LangChain / LangGraph** | `f.langchain()` | Callback Handler |
138
+ | **OpenAI Agents SDK** | `f.openai_agents()` | AgentHooks |
139
+ | **CrewAI** | `f.crewai()` | step_callback / task_callback |
140
+
141
+ ## Event Types
142
+
143
+ | Type | When | Why It Matters |
144
+ |------|------|---------------|
145
+ | `decision` | Agent decides what to do next | Core of forensics — the *why* |
146
+ | `tool_call_start` | Tool execution begins | What tool, what input |
147
+ | `tool_call_end` | Tool returns result | What came back |
148
+ | `llm_call_start` | LLM request sent | What was asked |
149
+ | `llm_call_end` | LLM response received | What was answered |
150
+ | `error` | Something went wrong | Incident detection |
151
+ | `final_decision` | Agent produces final output | End of decision chain |
152
+
153
+ ## License
154
+
155
+ MIT
@@ -0,0 +1,38 @@
1
+ """
2
+ Agent Forensics — Black box for AI agents.
3
+
4
+ Usage:
5
+ from agent_forensics import Forensics
6
+
7
+ # 1. Initialize
8
+ f = Forensics(session="order-123", agent="shopping-agent")
9
+
10
+ # 2a. Manual recording (framework-agnostic)
11
+ f.decision("search_products", input={"query": "mouse"}, reasoning="User request")
12
+ f.tool_call("search_api", input={"q": "mouse"}, output={"results": [...]})
13
+ f.error("purchase_failed", output={"reason": "Out of stock"})
14
+
15
+ # 2b. LangChain auto-recording
16
+ agent.invoke(..., config={"callbacks": [f.langchain()]})
17
+
18
+ # 2c. OpenAI Agents SDK auto-recording
19
+ agent = Agent(name="...", hooks=f.openai_agents())
20
+
21
+ # 2d. CrewAI auto-recording
22
+ hooks = f.crewai()
23
+ agent = Agent(role="...", step_callback=hooks.step_callback)
24
+
25
+ # 3. Report
26
+ print(f.report()) # Markdown
27
+ f.save_markdown() # forensics-report-order-123.md
28
+ f.save_pdf() # forensics-report-order-123.pdf
29
+
30
+ # 4. Dashboard
31
+ f.dashboard(port=8080) # http://localhost:8080
32
+ """
33
+
34
+ from .core import Forensics
35
+ from .store import Event, EventStore
36
+
37
+ __version__ = "0.1.0"
38
+ __all__ = ["Forensics", "Event", "EventStore"]
@@ -0,0 +1,183 @@
1
+ """
2
+ Forensics — Main interface for AI agent forensics.
3
+
4
+ Provides all functionality through a single class.
5
+ Framework-agnostic, with integrations for LangChain/CrewAI/OpenAI and more.
6
+ """
7
+
8
+ from .store import EventStore, Event, now
9
+ from .report import generate_report, save_report, save_pdf
10
+
11
+
12
+ class Forensics:
13
+ """AI Agent Forensics — Black box + report generator."""
14
+
15
+ def __init__(
16
+ self,
17
+ session: str = "default",
18
+ agent: str = "default-agent",
19
+ db_path: str = "forensics.db",
20
+ ):
21
+ self.session = session
22
+ self.agent = agent
23
+ self.store = EventStore(db_path)
24
+
25
+ # -- Manual Recording API --
26
+
27
+ def decision(self, action: str, *, input: dict = None, reasoning: str = "") -> str:
28
+ """Record when the agent makes a decision."""
29
+ return self.store.save(Event(
30
+ timestamp=now(),
31
+ event_type="decision",
32
+ agent_id=self.agent,
33
+ action=action,
34
+ input_data=input or {},
35
+ output_data={},
36
+ reasoning=reasoning,
37
+ session_id=self.session,
38
+ ))
39
+
40
+ def tool_call(self, action: str, *, input: dict = None, output: dict = None, reasoning: str = "") -> str:
41
+ """Record a tool call."""
42
+ # start
43
+ self.store.save(Event(
44
+ timestamp=now(),
45
+ event_type="tool_call_start",
46
+ agent_id=self.agent,
47
+ action=f"tool:{action}",
48
+ input_data=input or {},
49
+ output_data={},
50
+ reasoning=reasoning or f"Calling tool: {action}",
51
+ session_id=self.session,
52
+ ))
53
+ # end
54
+ return self.store.save(Event(
55
+ timestamp=now(),
56
+ event_type="tool_call_end",
57
+ agent_id=self.agent,
58
+ action="tool_result",
59
+ input_data={},
60
+ output_data=output or {},
61
+ reasoning="Tool execution completed",
62
+ session_id=self.session,
63
+ ))
64
+
65
+ def llm_call(self, *, input: dict = None, output: str = "", reasoning: str = "") -> str:
66
+ """Record an LLM call."""
67
+ self.store.save(Event(
68
+ timestamp=now(),
69
+ event_type="llm_call_start",
70
+ agent_id=self.agent,
71
+ action="llm_call",
72
+ input_data=input or {},
73
+ output_data={},
74
+ reasoning=reasoning or "LLM call",
75
+ session_id=self.session,
76
+ ))
77
+ return self.store.save(Event(
78
+ timestamp=now(),
79
+ event_type="llm_call_end",
80
+ agent_id=self.agent,
81
+ action="llm_response",
82
+ input_data={},
83
+ output_data={"response": output},
84
+ reasoning="LLM response",
85
+ session_id=self.session,
86
+ ))
87
+
88
+ def error(self, action: str, *, output: dict = None, reasoning: str = "") -> str:
89
+ """Record an error/incident."""
90
+ return self.store.save(Event(
91
+ timestamp=now(),
92
+ event_type="error",
93
+ agent_id=self.agent,
94
+ action=action,
95
+ input_data={},
96
+ output_data=output or {},
97
+ reasoning=reasoning or f"Error occurred: {action}",
98
+ session_id=self.session,
99
+ ))
100
+
101
+ def finish(self, output: str = "", *, reasoning: str = "") -> str:
102
+ """Record the agent's final result."""
103
+ return self.store.save(Event(
104
+ timestamp=now(),
105
+ event_type="final_decision",
106
+ agent_id=self.agent,
107
+ action="agent_finish",
108
+ input_data={},
109
+ output_data={"response": output},
110
+ reasoning=reasoning or "Agent determined final answer",
111
+ session_id=self.session,
112
+ ))
113
+
114
+ def record(self, event_type: str, action: str, *, input: dict = None, output: dict = None, reasoning: str = "") -> str:
115
+ """Record a generic event."""
116
+ return self.store.save(Event(
117
+ timestamp=now(),
118
+ event_type=event_type,
119
+ agent_id=self.agent,
120
+ action=action,
121
+ input_data=input or {},
122
+ output_data=output or {},
123
+ reasoning=reasoning,
124
+ session_id=self.session,
125
+ ))
126
+
127
+ # -- Report API --
128
+
129
+ def report(self) -> str:
130
+ """Return the Markdown forensics report as a string."""
131
+ return generate_report(self.store, self.session)
132
+
133
+ def save_markdown(self, path: str = None) -> str:
134
+ """Save the Markdown report to a file."""
135
+ return save_report(self.store, self.session, output_dir=path or ".")
136
+
137
+ def save_pdf(self, path: str = None) -> str:
138
+ """Save the PDF report to a file."""
139
+ return save_pdf(self.store, self.session, output_dir=path or ".")
140
+
141
+ def events(self) -> list[Event]:
142
+ """Return all events for the current session."""
143
+ return self.store.get_session_events(self.session)
144
+
145
+ def sessions(self) -> list[str]:
146
+ """Return a list of all sessions."""
147
+ return self.store.get_all_sessions()
148
+
149
+ # -- Framework Integrations --
150
+
151
+ def langchain(self):
152
+ """Return a LangChain callback handler. agent.invoke(..., config={"callbacks": [f.langchain()]})"""
153
+ from .integrations.langchain import ForensicsCollector
154
+ return ForensicsCollector(
155
+ store=self.store,
156
+ session_id=self.session,
157
+ agent_id=self.agent,
158
+ )
159
+
160
+ def openai_agents(self):
161
+ """Return OpenAI Agents SDK hooks. Agent(hooks=f.openai_agents())"""
162
+ from .integrations.openai_agents import ForensicsAgentHooks
163
+ return ForensicsAgentHooks(
164
+ store=self.store,
165
+ session_id=self.session,
166
+ agent_id=self.agent,
167
+ )
168
+
169
+ def crewai(self):
170
+ """Return CrewAI callback collection. Agent(step_callback=hooks.step_callback)"""
171
+ from .integrations.crewai import ForensicsCrewAIHooks
172
+ return ForensicsCrewAIHooks(
173
+ store=self.store,
174
+ session_id=self.session,
175
+ agent_id=self.agent,
176
+ )
177
+
178
+ # -- Dashboard --
179
+
180
+ def dashboard(self, port: int = 8080):
181
+ """Launch the web dashboard."""
182
+ from .dashboard import run_dashboard
183
+ run_dashboard(self.store, port=port)