rdpge 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.
rdpge-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jahanzeb Ahmed
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.
rdpge-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,357 @@
1
+ Metadata-Version: 2.4
2
+ Name: rdpge
3
+ Version: 0.1.0
4
+ Summary: RDPGE: Runtime Dynamic & Probabilistic Graph Execution — A novel paradigm for agentic AI
5
+ Author: Jahanzeb Ahmed
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Jahanzeb-git/rdpge
8
+ Project-URL: Documentation, https://github.com/Jahanzeb-git/rdpge#readme
9
+ Project-URL: Issues, https://github.com/Jahanzeb-git/rdpge/issues
10
+ Keywords: ai,agents,llm,agentic,graph,execution,framework
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: httpx>=0.27.0
23
+ Requires-Dist: pydantic>=2.0
24
+ Requires-Dist: jinja2>=3.1
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
28
+ Requires-Dist: ruff>=0.4; extra == "dev"
29
+ Requires-Dist: mypy>=1.10; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # RDPGE
33
+
34
+ **Runtime Dynamic & Probabilistic Graph Execution**
35
+
36
+ A novel agentic AI framework that treats LLM outputs as probabilistic events, not deterministic programs.
37
+
38
+ RDPGE replaces the flat ReAct loop with a dynamic execution graph — giving agents structured memory, selective context, and real-time situational awareness.
39
+
40
+ ---
41
+
42
+ ## Why RDPGE?
43
+
44
+ Standard agentic frameworks (ReAct, function-calling loops) suffer from three core problems:
45
+
46
+ | Problem | What Happens | RDPGE's Solution |
47
+ |---------|-------------|-----------------|
48
+ | **Myopia** | The agent only sees recent context. Work done 15 steps ago fades from attention. | **Graph Manifest** — a live dashboard showing all tasks, distances, references, and step budget. Updated every turn. |
49
+ | **Greedy execution** | The agent does the first thing that seems useful without considering the bigger picture. | **Task-based organization** — work is structured into named tasks. The agent sees the full map before acting. |
50
+ | **Context waste** | All tool outputs stay in context forever, consuming tokens even when irrelevant. | **Context blurring** — inactive tasks' tool outputs are replaced with `[BLURRED]`. Restored on demand via edges. |
51
+
52
+ ## Key Ideas
53
+
54
+ - **Code-as-interface** — The LLM outputs Python code, not JSON tool calls. Comments serve as chain-of-thought. An `action` dictionary is the structured interface.
55
+ - **Probabilistic graph** — Execution is tracked as a dynamic graph of nodes organized by tasks. The LLM creates tasks and nodes as needed.
56
+ - **Context blurring** — Only the active task's tool outputs are visible. Everything else is `[BLURRED]`. The LLM can restore any task's context by creating an edge.
57
+ - **Graph manifest** — Every turn, the LLM sees a live dashboard: active node, current task, step budget, task distances, and inter-task references.
58
+ - **Signal tools** — Built-in tools (`complete`, `ask_user`, `surrender`) that control execution flow explicitly.
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install rdpge
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ ```python
69
+ import asyncio
70
+ from rdpge import Agent, tool
71
+ from rdpge.llm import OpenAIAdapter, LLMConfig
72
+
73
+ # 1. Define tools
74
+ @tool()
75
+ def read_file(path: str) -> str:
76
+ """Read file contents from disk."""
77
+ with open(path) as f:
78
+ return f.read()
79
+
80
+ @tool()
81
+ def write_file(path: str, content: str) -> str:
82
+ """Write content to a file on disk."""
83
+ with open(path, "w") as f:
84
+ f.write(content)
85
+ return f"Written {len(content)} bytes to {path}"
86
+
87
+ # 2. Create agent
88
+ agent = Agent(
89
+ llm=OpenAIAdapter(LLMConfig(
90
+ model="gpt-4o",
91
+ api_key="sk-...",
92
+ )),
93
+ tools=[read_file, write_file],
94
+ instructions="You are a code assistant.",
95
+ max_steps=25,
96
+ )
97
+
98
+ # 3. Run
99
+ async def main():
100
+ result = await agent.run("Read auth.py and fix the login bug")
101
+ print(f"Status: {result.status}")
102
+ print(f"Steps used: {result.steps}")
103
+ print(f"Reason: {result.reason}")
104
+
105
+ asyncio.run(main())
106
+ ```
107
+
108
+ ## Using with Together AI
109
+
110
+ ```python
111
+ from rdpge.llm import OpenAIAdapter, LLMConfig
112
+
113
+ llm = OpenAIAdapter(LLMConfig(
114
+ model="Qwen/Qwen3-Coder-480B-A35B-Instruct",
115
+ api_key="your-together-key",
116
+ base_url="https://api.together.xyz/v1",
117
+ ))
118
+ agent = Agent(llm=llm, tools=[...])
119
+ ```
120
+
121
+ ## Using with Anthropic
122
+
123
+ ```python
124
+ from rdpge.llm import AnthropicAdapter, LLMConfig
125
+
126
+ llm = AnthropicAdapter(LLMConfig(
127
+ model="claude-sonnet-4-20250514",
128
+ api_key="sk-ant-...",
129
+ ))
130
+ agent = Agent(llm=llm, tools=[...])
131
+ ```
132
+
133
+ ## How It Works
134
+
135
+ ```
136
+ User Request
137
+
138
+
139
+ ┌─────────────────────────────────────────────┐
140
+ │ EXECUTION LOOP │
141
+ │ │
142
+ │ 1. Build Context │
143
+ │ ├── System Prompt │
144
+ │ ├── Graph Manifest (live dashboard) │
145
+ │ └── Node History (with blurring) │
146
+ │ │
147
+ │ 2. LLM Generates Python Code │
148
+ │ └── action = {node, reason, tool_call} │
149
+ │ │
150
+ │ 3. Execute Code in Sandbox │
151
+ │ └── Extract action dict │
152
+ │ │
153
+ │ 4. Route Tool Call │
154
+ │ ├── Signal tool? → Handle internally │
155
+ │ └── Regular tool? → Execute via registry│
156
+ │ │
157
+ │ 5. Update Graph │
158
+ │ ├── Record node in task │
159
+ │ ├── Apply blurring to inactive tasks │
160
+ │ └── Save state (multi-turn) │
161
+ │ │
162
+ │ 6. Loop until: complete / surrender / │
163
+ │ ask_user / max_steps │
164
+ └─────────────────────────────────────────────┘
165
+
166
+
167
+ AgentResult(status, reason, steps, trace, graph)
168
+ ```
169
+
170
+ ### The Graph Manifest
171
+
172
+ Every turn, the LLM sees this dashboard in its context:
173
+
174
+ ```
175
+ Active Node: node-b2
176
+ Current Task: b
177
+ Step: 6 of 25
178
+ Active Edge: (none)
179
+
180
+ Task Map:
181
+ Task A: 2 steps ago | 0 references | 3 steps
182
+ Task B: active | 0 references | 2 steps
183
+ ```
184
+
185
+ - **Distance** tells the LLM how far back a task is (recency).
186
+ - **References** show inter-task dependencies (importance).
187
+ - **Step counter** shows remaining budget (the hard execution limit).
188
+
189
+ ### Context Blurring
190
+
191
+ When the LLM switches from Task A to Task B:
192
+
193
+ ```
194
+ # Task A's tool outputs become:
195
+ Tool: [BLURRED]
196
+
197
+ # Task B's tool outputs remain fully visible:
198
+ Tool: def login(user, pwd): ...
199
+ ```
200
+
201
+ The LLM can restore Task A's context by setting `edge: "node-a"` in its action.
202
+
203
+ ## Signal Tools
204
+
205
+ Built-in tools that control execution flow:
206
+
207
+ | Signal | Args | Effect |
208
+ |--------|------|--------|
209
+ | `complete` | `{}` | Task is done. Loop ends. |
210
+ | `ask_user` | `{"question": str}` | Pauses execution. Returns the question to the caller. |
211
+ | `surrender` | `{"reason": str}` | Cannot accomplish the task. Loop ends. |
212
+
213
+ Developer-side abort:
214
+ ```python
215
+ # From a UI button or hook callback:
216
+ agent.abort("User pressed cancel")
217
+ ```
218
+
219
+ ## Multi-Turn Sessions
220
+
221
+ RDPGE preserves full graph state between conversations:
222
+
223
+ ```python
224
+ # Turn 1
225
+ result = await agent.run("Read the codebase and find bugs")
226
+ # result.status == "awaiting_input" (agent used ask_user)
227
+
228
+ # Turn 2 — agent remembers everything from Turn 1
229
+ result = await agent.run("Focus on auth.py")
230
+ ```
231
+
232
+ ### Persistence
233
+
234
+ ```python
235
+ from rdpge import Agent, InMemoryStore
236
+
237
+ # In-memory (default)
238
+ agent = Agent(llm=llm, tools=[...], store=InMemoryStore())
239
+
240
+ # Custom store (Redis, database, etc.)
241
+ # Implement the SessionStore protocol:
242
+ class RedisStore:
243
+ async def save(self, session_id: str, data: dict) -> None: ...
244
+ async def load(self, session_id: str) -> dict | None: ...
245
+ async def delete(self, session_id: str) -> None: ...
246
+ async def list_sessions(self) -> list[str]: ...
247
+ ```
248
+
249
+ ## Event Hooks
250
+
251
+ Monitor and react to agent behavior:
252
+
253
+ ```python
254
+ @agent.on("step_end")
255
+ async def log_step(data):
256
+ print(f"Step {data['step']}: {data['node_id']} → {data['tool']}")
257
+
258
+ @agent.on("complete")
259
+ async def on_done(data):
260
+ print(f"Finished in {data['steps']} steps")
261
+
262
+ @agent.on("surrender")
263
+ async def on_surrender(data):
264
+ print(f"Gave up: {data['reason']}")
265
+ ```
266
+
267
+ Available events: `step_start`, `step_end`, `tool_called`, `complete`, `ask_user`, `surrender`, `abort`, `error`
268
+
269
+ ## Execution Traces
270
+
271
+ Every run produces a detailed trace:
272
+
273
+ ```python
274
+ result = await agent.run("Fix the bug")
275
+ summary = result.trace.summary()
276
+
277
+ print(summary)
278
+ # {
279
+ # "session_id": "a1b2c3d4",
280
+ # "total_steps": 5,
281
+ # "total_duration_ms": 12340,
282
+ # "tools_used": {"read_file": 2, "write_file": 1, "complete": 1, ...},
283
+ # "nodes": ["node-a1", "node-a2", "node-a3", "node-b1", "node-b2"],
284
+ # }
285
+ ```
286
+
287
+ ## API Reference
288
+
289
+ ### `Agent`
290
+
291
+ ```python
292
+ Agent(
293
+ llm, # LLMProvider instance
294
+ tools: list = None, # List of @tool() or BaseTool instances
295
+ instructions: str = "", # Domain-specific instructions for the LLM
296
+ instructions_file: str = None,# Or load instructions from a file
297
+ max_steps: int = 25, # Hard execution limit
298
+ store = None, # SessionStore for persistence
299
+ signal_handlers: dict = None, # Override signal tool behavior
300
+ )
301
+ ```
302
+
303
+ **Methods:**
304
+ - `await agent.run(request)` → `AgentResult`
305
+ - `agent.abort(reason)` — stop execution from outside
306
+ - `agent.new_session()` — start fresh
307
+ - `await agent.load_session(session_id)` → `bool`
308
+ - `agent.on(event)` — decorator for event hooks
309
+
310
+ ### `AgentResult`
311
+
312
+ ```python
313
+ @dataclass
314
+ class AgentResult:
315
+ success: bool # True if task completed successfully
316
+ status: str # "completed" | "awaiting_input" | "surrendered" | "aborted" | "max_steps" | "error"
317
+ reason: str # Human-readable summary
318
+ steps: int # Number of steps executed
319
+ session_id: str # Session identifier
320
+ trace: SessionTrace # Execution trace
321
+ graph: GraphState # Final graph state
322
+ error: str = "" # Error details (if status == "error")
323
+ ```
324
+
325
+ ### `@tool()`
326
+
327
+ ```python
328
+ @tool()
329
+ def my_tool(param: str) -> str:
330
+ """Tool description (used in the LLM prompt)."""
331
+ return result
332
+
333
+ # Or with explicit description:
334
+ @tool("Override the docstring description")
335
+ def my_tool(param: str) -> str:
336
+ ...
337
+ ```
338
+
339
+ ## Architecture
340
+
341
+ RDPGE is built on four principles:
342
+
343
+ 1. **Respect probabilistic nature** — LLM outputs are unpredictable. The framework handles malformed outputs, retries, and edge cases structurally.
344
+
345
+ 2. **Overcome myopia** — The graph manifest gives the LLM a bird's-eye view of its entire session, preventing short-sighted decisions.
346
+
347
+ 3. **Ensure task accuracy** — Signal tools (`surrender`, `ask_user`) let the agent be honest about its limitations instead of producing garbage output.
348
+
349
+ 4. **Complete observability** — Traces, hooks, and graph export provide full transparency into agent behavior at every level.
350
+
351
+ ## License
352
+
353
+ MIT
354
+
355
+ ## Author
356
+
357
+ **Jahanzeb Ahmed** — [GitHub](https://github.com/Jahanzeb-git)
rdpge-0.1.0/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # RDPGE
2
+
3
+ **Runtime Dynamic & Probabilistic Graph Execution**
4
+
5
+ A novel agentic AI framework that treats LLM outputs as probabilistic events, not deterministic programs.
6
+
7
+ RDPGE replaces the flat ReAct loop with a dynamic execution graph — giving agents structured memory, selective context, and real-time situational awareness.
8
+
9
+ ---
10
+
11
+ ## Why RDPGE?
12
+
13
+ Standard agentic frameworks (ReAct, function-calling loops) suffer from three core problems:
14
+
15
+ | Problem | What Happens | RDPGE's Solution |
16
+ |---------|-------------|-----------------|
17
+ | **Myopia** | The agent only sees recent context. Work done 15 steps ago fades from attention. | **Graph Manifest** — a live dashboard showing all tasks, distances, references, and step budget. Updated every turn. |
18
+ | **Greedy execution** | The agent does the first thing that seems useful without considering the bigger picture. | **Task-based organization** — work is structured into named tasks. The agent sees the full map before acting. |
19
+ | **Context waste** | All tool outputs stay in context forever, consuming tokens even when irrelevant. | **Context blurring** — inactive tasks' tool outputs are replaced with `[BLURRED]`. Restored on demand via edges. |
20
+
21
+ ## Key Ideas
22
+
23
+ - **Code-as-interface** — The LLM outputs Python code, not JSON tool calls. Comments serve as chain-of-thought. An `action` dictionary is the structured interface.
24
+ - **Probabilistic graph** — Execution is tracked as a dynamic graph of nodes organized by tasks. The LLM creates tasks and nodes as needed.
25
+ - **Context blurring** — Only the active task's tool outputs are visible. Everything else is `[BLURRED]`. The LLM can restore any task's context by creating an edge.
26
+ - **Graph manifest** — Every turn, the LLM sees a live dashboard: active node, current task, step budget, task distances, and inter-task references.
27
+ - **Signal tools** — Built-in tools (`complete`, `ask_user`, `surrender`) that control execution flow explicitly.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install rdpge
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```python
38
+ import asyncio
39
+ from rdpge import Agent, tool
40
+ from rdpge.llm import OpenAIAdapter, LLMConfig
41
+
42
+ # 1. Define tools
43
+ @tool()
44
+ def read_file(path: str) -> str:
45
+ """Read file contents from disk."""
46
+ with open(path) as f:
47
+ return f.read()
48
+
49
+ @tool()
50
+ def write_file(path: str, content: str) -> str:
51
+ """Write content to a file on disk."""
52
+ with open(path, "w") as f:
53
+ f.write(content)
54
+ return f"Written {len(content)} bytes to {path}"
55
+
56
+ # 2. Create agent
57
+ agent = Agent(
58
+ llm=OpenAIAdapter(LLMConfig(
59
+ model="gpt-4o",
60
+ api_key="sk-...",
61
+ )),
62
+ tools=[read_file, write_file],
63
+ instructions="You are a code assistant.",
64
+ max_steps=25,
65
+ )
66
+
67
+ # 3. Run
68
+ async def main():
69
+ result = await agent.run("Read auth.py and fix the login bug")
70
+ print(f"Status: {result.status}")
71
+ print(f"Steps used: {result.steps}")
72
+ print(f"Reason: {result.reason}")
73
+
74
+ asyncio.run(main())
75
+ ```
76
+
77
+ ## Using with Together AI
78
+
79
+ ```python
80
+ from rdpge.llm import OpenAIAdapter, LLMConfig
81
+
82
+ llm = OpenAIAdapter(LLMConfig(
83
+ model="Qwen/Qwen3-Coder-480B-A35B-Instruct",
84
+ api_key="your-together-key",
85
+ base_url="https://api.together.xyz/v1",
86
+ ))
87
+ agent = Agent(llm=llm, tools=[...])
88
+ ```
89
+
90
+ ## Using with Anthropic
91
+
92
+ ```python
93
+ from rdpge.llm import AnthropicAdapter, LLMConfig
94
+
95
+ llm = AnthropicAdapter(LLMConfig(
96
+ model="claude-sonnet-4-20250514",
97
+ api_key="sk-ant-...",
98
+ ))
99
+ agent = Agent(llm=llm, tools=[...])
100
+ ```
101
+
102
+ ## How It Works
103
+
104
+ ```
105
+ User Request
106
+
107
+
108
+ ┌─────────────────────────────────────────────┐
109
+ │ EXECUTION LOOP │
110
+ │ │
111
+ │ 1. Build Context │
112
+ │ ├── System Prompt │
113
+ │ ├── Graph Manifest (live dashboard) │
114
+ │ └── Node History (with blurring) │
115
+ │ │
116
+ │ 2. LLM Generates Python Code │
117
+ │ └── action = {node, reason, tool_call} │
118
+ │ │
119
+ │ 3. Execute Code in Sandbox │
120
+ │ └── Extract action dict │
121
+ │ │
122
+ │ 4. Route Tool Call │
123
+ │ ├── Signal tool? → Handle internally │
124
+ │ └── Regular tool? → Execute via registry│
125
+ │ │
126
+ │ 5. Update Graph │
127
+ │ ├── Record node in task │
128
+ │ ├── Apply blurring to inactive tasks │
129
+ │ └── Save state (multi-turn) │
130
+ │ │
131
+ │ 6. Loop until: complete / surrender / │
132
+ │ ask_user / max_steps │
133
+ └─────────────────────────────────────────────┘
134
+
135
+
136
+ AgentResult(status, reason, steps, trace, graph)
137
+ ```
138
+
139
+ ### The Graph Manifest
140
+
141
+ Every turn, the LLM sees this dashboard in its context:
142
+
143
+ ```
144
+ Active Node: node-b2
145
+ Current Task: b
146
+ Step: 6 of 25
147
+ Active Edge: (none)
148
+
149
+ Task Map:
150
+ Task A: 2 steps ago | 0 references | 3 steps
151
+ Task B: active | 0 references | 2 steps
152
+ ```
153
+
154
+ - **Distance** tells the LLM how far back a task is (recency).
155
+ - **References** show inter-task dependencies (importance).
156
+ - **Step counter** shows remaining budget (the hard execution limit).
157
+
158
+ ### Context Blurring
159
+
160
+ When the LLM switches from Task A to Task B:
161
+
162
+ ```
163
+ # Task A's tool outputs become:
164
+ Tool: [BLURRED]
165
+
166
+ # Task B's tool outputs remain fully visible:
167
+ Tool: def login(user, pwd): ...
168
+ ```
169
+
170
+ The LLM can restore Task A's context by setting `edge: "node-a"` in its action.
171
+
172
+ ## Signal Tools
173
+
174
+ Built-in tools that control execution flow:
175
+
176
+ | Signal | Args | Effect |
177
+ |--------|------|--------|
178
+ | `complete` | `{}` | Task is done. Loop ends. |
179
+ | `ask_user` | `{"question": str}` | Pauses execution. Returns the question to the caller. |
180
+ | `surrender` | `{"reason": str}` | Cannot accomplish the task. Loop ends. |
181
+
182
+ Developer-side abort:
183
+ ```python
184
+ # From a UI button or hook callback:
185
+ agent.abort("User pressed cancel")
186
+ ```
187
+
188
+ ## Multi-Turn Sessions
189
+
190
+ RDPGE preserves full graph state between conversations:
191
+
192
+ ```python
193
+ # Turn 1
194
+ result = await agent.run("Read the codebase and find bugs")
195
+ # result.status == "awaiting_input" (agent used ask_user)
196
+
197
+ # Turn 2 — agent remembers everything from Turn 1
198
+ result = await agent.run("Focus on auth.py")
199
+ ```
200
+
201
+ ### Persistence
202
+
203
+ ```python
204
+ from rdpge import Agent, InMemoryStore
205
+
206
+ # In-memory (default)
207
+ agent = Agent(llm=llm, tools=[...], store=InMemoryStore())
208
+
209
+ # Custom store (Redis, database, etc.)
210
+ # Implement the SessionStore protocol:
211
+ class RedisStore:
212
+ async def save(self, session_id: str, data: dict) -> None: ...
213
+ async def load(self, session_id: str) -> dict | None: ...
214
+ async def delete(self, session_id: str) -> None: ...
215
+ async def list_sessions(self) -> list[str]: ...
216
+ ```
217
+
218
+ ## Event Hooks
219
+
220
+ Monitor and react to agent behavior:
221
+
222
+ ```python
223
+ @agent.on("step_end")
224
+ async def log_step(data):
225
+ print(f"Step {data['step']}: {data['node_id']} → {data['tool']}")
226
+
227
+ @agent.on("complete")
228
+ async def on_done(data):
229
+ print(f"Finished in {data['steps']} steps")
230
+
231
+ @agent.on("surrender")
232
+ async def on_surrender(data):
233
+ print(f"Gave up: {data['reason']}")
234
+ ```
235
+
236
+ Available events: `step_start`, `step_end`, `tool_called`, `complete`, `ask_user`, `surrender`, `abort`, `error`
237
+
238
+ ## Execution Traces
239
+
240
+ Every run produces a detailed trace:
241
+
242
+ ```python
243
+ result = await agent.run("Fix the bug")
244
+ summary = result.trace.summary()
245
+
246
+ print(summary)
247
+ # {
248
+ # "session_id": "a1b2c3d4",
249
+ # "total_steps": 5,
250
+ # "total_duration_ms": 12340,
251
+ # "tools_used": {"read_file": 2, "write_file": 1, "complete": 1, ...},
252
+ # "nodes": ["node-a1", "node-a2", "node-a3", "node-b1", "node-b2"],
253
+ # }
254
+ ```
255
+
256
+ ## API Reference
257
+
258
+ ### `Agent`
259
+
260
+ ```python
261
+ Agent(
262
+ llm, # LLMProvider instance
263
+ tools: list = None, # List of @tool() or BaseTool instances
264
+ instructions: str = "", # Domain-specific instructions for the LLM
265
+ instructions_file: str = None,# Or load instructions from a file
266
+ max_steps: int = 25, # Hard execution limit
267
+ store = None, # SessionStore for persistence
268
+ signal_handlers: dict = None, # Override signal tool behavior
269
+ )
270
+ ```
271
+
272
+ **Methods:**
273
+ - `await agent.run(request)` → `AgentResult`
274
+ - `agent.abort(reason)` — stop execution from outside
275
+ - `agent.new_session()` — start fresh
276
+ - `await agent.load_session(session_id)` → `bool`
277
+ - `agent.on(event)` — decorator for event hooks
278
+
279
+ ### `AgentResult`
280
+
281
+ ```python
282
+ @dataclass
283
+ class AgentResult:
284
+ success: bool # True if task completed successfully
285
+ status: str # "completed" | "awaiting_input" | "surrendered" | "aborted" | "max_steps" | "error"
286
+ reason: str # Human-readable summary
287
+ steps: int # Number of steps executed
288
+ session_id: str # Session identifier
289
+ trace: SessionTrace # Execution trace
290
+ graph: GraphState # Final graph state
291
+ error: str = "" # Error details (if status == "error")
292
+ ```
293
+
294
+ ### `@tool()`
295
+
296
+ ```python
297
+ @tool()
298
+ def my_tool(param: str) -> str:
299
+ """Tool description (used in the LLM prompt)."""
300
+ return result
301
+
302
+ # Or with explicit description:
303
+ @tool("Override the docstring description")
304
+ def my_tool(param: str) -> str:
305
+ ...
306
+ ```
307
+
308
+ ## Architecture
309
+
310
+ RDPGE is built on four principles:
311
+
312
+ 1. **Respect probabilistic nature** — LLM outputs are unpredictable. The framework handles malformed outputs, retries, and edge cases structurally.
313
+
314
+ 2. **Overcome myopia** — The graph manifest gives the LLM a bird's-eye view of its entire session, preventing short-sighted decisions.
315
+
316
+ 3. **Ensure task accuracy** — Signal tools (`surrender`, `ask_user`) let the agent be honest about its limitations instead of producing garbage output.
317
+
318
+ 4. **Complete observability** — Traces, hooks, and graph export provide full transparency into agent behavior at every level.
319
+
320
+ ## License
321
+
322
+ MIT
323
+
324
+ ## Author
325
+
326
+ **Jahanzeb Ahmed** — [GitHub](https://github.com/Jahanzeb-git)