geny-executor 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.
Files changed (98) hide show
  1. geny_executor-0.1.0/.gitignore +32 -0
  2. geny_executor-0.1.0/LICENSE +21 -0
  3. geny_executor-0.1.0/PKG-INFO +593 -0
  4. geny_executor-0.1.0/README.md +555 -0
  5. geny_executor-0.1.0/README_ko.md +555 -0
  6. geny_executor-0.1.0/pyproject.toml +84 -0
  7. geny_executor-0.1.0/src/geny_executor/__init__.py +67 -0
  8. geny_executor-0.1.0/src/geny_executor/core/__init__.py +31 -0
  9. geny_executor-0.1.0/src/geny_executor/core/builder.py +205 -0
  10. geny_executor-0.1.0/src/geny_executor/core/config.py +62 -0
  11. geny_executor-0.1.0/src/geny_executor/core/errors.py +95 -0
  12. geny_executor-0.1.0/src/geny_executor/core/pipeline.py +270 -0
  13. geny_executor-0.1.0/src/geny_executor/core/presets.py +122 -0
  14. geny_executor-0.1.0/src/geny_executor/core/result.py +72 -0
  15. geny_executor-0.1.0/src/geny_executor/core/stage.py +126 -0
  16. geny_executor-0.1.0/src/geny_executor/core/state.py +172 -0
  17. geny_executor-0.1.0/src/geny_executor/events/__init__.py +6 -0
  18. geny_executor-0.1.0/src/geny_executor/events/bus.py +83 -0
  19. geny_executor-0.1.0/src/geny_executor/events/types.py +29 -0
  20. geny_executor-0.1.0/src/geny_executor/py.typed +0 -0
  21. geny_executor-0.1.0/src/geny_executor/session/__init__.py +7 -0
  22. geny_executor-0.1.0/src/geny_executor/session/freshness.py +60 -0
  23. geny_executor-0.1.0/src/geny_executor/session/manager.py +73 -0
  24. geny_executor-0.1.0/src/geny_executor/session/session.py +70 -0
  25. geny_executor-0.1.0/src/geny_executor/stages/__init__.py +1 -0
  26. geny_executor-0.1.0/src/geny_executor/stages/s01_input/__init__.py +29 -0
  27. geny_executor-0.1.0/src/geny_executor/stages/s01_input/normalizers.py +126 -0
  28. geny_executor-0.1.0/src/geny_executor/stages/s01_input/stage.py +83 -0
  29. geny_executor-0.1.0/src/geny_executor/stages/s01_input/types.py +40 -0
  30. geny_executor-0.1.0/src/geny_executor/stages/s01_input/validators.py +121 -0
  31. geny_executor-0.1.0/src/geny_executor/stages/s02_context/__init__.py +35 -0
  32. geny_executor-0.1.0/src/geny_executor/stages/s02_context/compactors.py +103 -0
  33. geny_executor-0.1.0/src/geny_executor/stages/s02_context/retrievers.py +66 -0
  34. geny_executor-0.1.0/src/geny_executor/stages/s02_context/stage.py +141 -0
  35. geny_executor-0.1.0/src/geny_executor/stages/s02_context/strategies.py +94 -0
  36. geny_executor-0.1.0/src/geny_executor/stages/s03_system/__init__.py +27 -0
  37. geny_executor-0.1.0/src/geny_executor/stages/s03_system/builders.py +204 -0
  38. geny_executor-0.1.0/src/geny_executor/stages/s03_system/stage.py +82 -0
  39. geny_executor-0.1.0/src/geny_executor/stages/s04_guard/__init__.py +21 -0
  40. geny_executor-0.1.0/src/geny_executor/stages/s04_guard/guards.py +171 -0
  41. geny_executor-0.1.0/src/geny_executor/stages/s04_guard/stage.py +76 -0
  42. geny_executor-0.1.0/src/geny_executor/stages/s05_cache/__init__.py +17 -0
  43. geny_executor-0.1.0/src/geny_executor/stages/s05_cache/stage.py +61 -0
  44. geny_executor-0.1.0/src/geny_executor/stages/s05_cache/strategies.py +130 -0
  45. geny_executor-0.1.0/src/geny_executor/stages/s06_api/__init__.py +30 -0
  46. geny_executor-0.1.0/src/geny_executor/stages/s06_api/providers.py +319 -0
  47. geny_executor-0.1.0/src/geny_executor/stages/s06_api/retry.py +126 -0
  48. geny_executor-0.1.0/src/geny_executor/stages/s06_api/stage.py +193 -0
  49. geny_executor-0.1.0/src/geny_executor/stages/s06_api/types.py +91 -0
  50. geny_executor-0.1.0/src/geny_executor/stages/s07_token/__init__.py +13 -0
  51. geny_executor-0.1.0/src/geny_executor/stages/s07_token/pricing.py +117 -0
  52. geny_executor-0.1.0/src/geny_executor/stages/s07_token/stage.py +87 -0
  53. geny_executor-0.1.0/src/geny_executor/stages/s07_token/trackers.py +74 -0
  54. geny_executor-0.1.0/src/geny_executor/stages/s08_think/__init__.py +21 -0
  55. geny_executor-0.1.0/src/geny_executor/stages/s08_think/processors.py +108 -0
  56. geny_executor-0.1.0/src/geny_executor/stages/s08_think/stage.py +142 -0
  57. geny_executor-0.1.0/src/geny_executor/stages/s09_parse/__init__.py +29 -0
  58. geny_executor-0.1.0/src/geny_executor/stages/s09_parse/parsers.py +111 -0
  59. geny_executor-0.1.0/src/geny_executor/stages/s09_parse/signals.py +113 -0
  60. geny_executor-0.1.0/src/geny_executor/stages/s09_parse/stage.py +117 -0
  61. geny_executor-0.1.0/src/geny_executor/stages/s09_parse/types.py +58 -0
  62. geny_executor-0.1.0/src/geny_executor/stages/s10_tool/__init__.py +18 -0
  63. geny_executor-0.1.0/src/geny_executor/stages/s10_tool/executors.py +88 -0
  64. geny_executor-0.1.0/src/geny_executor/stages/s10_tool/routers.py +53 -0
  65. geny_executor-0.1.0/src/geny_executor/stages/s10_tool/stage.py +111 -0
  66. geny_executor-0.1.0/src/geny_executor/stages/s11_agent/__init__.py +23 -0
  67. geny_executor-0.1.0/src/geny_executor/stages/s11_agent/orchestrators.py +187 -0
  68. geny_executor-0.1.0/src/geny_executor/stages/s11_agent/stage.py +111 -0
  69. geny_executor-0.1.0/src/geny_executor/stages/s12_evaluate/__init__.py +27 -0
  70. geny_executor-0.1.0/src/geny_executor/stages/s12_evaluate/stage.py +107 -0
  71. geny_executor-0.1.0/src/geny_executor/stages/s12_evaluate/strategies.py +263 -0
  72. geny_executor-0.1.0/src/geny_executor/stages/s13_loop/__init__.py +17 -0
  73. geny_executor-0.1.0/src/geny_executor/stages/s13_loop/controllers.py +137 -0
  74. geny_executor-0.1.0/src/geny_executor/stages/s13_loop/stage.py +75 -0
  75. geny_executor-0.1.0/src/geny_executor/stages/s14_emit/__init__.py +23 -0
  76. geny_executor-0.1.0/src/geny_executor/stages/s14_emit/emitters.py +178 -0
  77. geny_executor-0.1.0/src/geny_executor/stages/s14_emit/stage.py +91 -0
  78. geny_executor-0.1.0/src/geny_executor/stages/s15_memory/__init__.py +25 -0
  79. geny_executor-0.1.0/src/geny_executor/stages/s15_memory/persistence.py +102 -0
  80. geny_executor-0.1.0/src/geny_executor/stages/s15_memory/stage.py +104 -0
  81. geny_executor-0.1.0/src/geny_executor/stages/s15_memory/strategies.py +75 -0
  82. geny_executor-0.1.0/src/geny_executor/stages/s16_yield/__init__.py +17 -0
  83. geny_executor-0.1.0/src/geny_executor/stages/s16_yield/formatters.py +81 -0
  84. geny_executor-0.1.0/src/geny_executor/stages/s16_yield/stage.py +53 -0
  85. geny_executor-0.1.0/src/geny_executor/tools/__init__.py +6 -0
  86. geny_executor-0.1.0/src/geny_executor/tools/base.py +81 -0
  87. geny_executor-0.1.0/src/geny_executor/tools/mcp/__init__.py +10 -0
  88. geny_executor-0.1.0/src/geny_executor/tools/mcp/adapter.py +60 -0
  89. geny_executor-0.1.0/src/geny_executor/tools/mcp/manager.py +152 -0
  90. geny_executor-0.1.0/src/geny_executor/tools/registry.py +69 -0
  91. geny_executor-0.1.0/tests/__init__.py +0 -0
  92. geny_executor-0.1.0/tests/unit/__init__.py +0 -0
  93. geny_executor-0.1.0/tests/unit/test_phase1_pipeline.py +243 -0
  94. geny_executor-0.1.0/tests/unit/test_phase2_agent_loop.py +320 -0
  95. geny_executor-0.1.0/tests/unit/test_phase3_context_memory.py +238 -0
  96. geny_executor-0.1.0/tests/unit/test_phase4_think_agent_evaluate.py +424 -0
  97. geny_executor-0.1.0/tests/unit/test_phase5_emit_presets_mcp.py +285 -0
  98. geny_executor-0.1.0/tests/unit/test_phase6_integration.py +392 -0
@@ -0,0 +1,32 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ *.egg
7
+ dist/
8
+ build/
9
+ *.whl
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+ env/
15
+
16
+ # Testing
17
+ .pytest_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # IDE
22
+ .idea/
23
+ .vscode/
24
+ *.swp
25
+ *.swo
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+
31
+ # Ruff
32
+ .ruff_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CocoRoF
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,593 @@
1
+ Metadata-Version: 2.4
2
+ Name: geny-executor
3
+ Version: 0.1.0
4
+ Summary: Harness-engineered agent pipeline library with 16-stage dual-abstraction architecture, built on the Anthropic API
5
+ Project-URL: Homepage, https://github.com/CocoRoF/geny-executor
6
+ Project-URL: Repository, https://github.com/CocoRoF/geny-executor
7
+ Project-URL: Issues, https://github.com/CocoRoF/geny-executor/issues
8
+ Project-URL: Documentation, https://github.com/CocoRoF/geny-executor#readme
9
+ Author-email: CocoRoF <cocorof@users.noreply.github.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent,ai,anthropic,claude,harness,llm,mcp,orchestration,pipeline
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: anthropic>=0.52.0
26
+ Requires-Dist: mcp>=1.0.0
27
+ Requires-Dist: pydantic>=2.0
28
+ Provides-Extra: all
29
+ Requires-Dist: numpy>=1.24; extra == 'all'
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
32
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0; extra == 'dev'
34
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
35
+ Provides-Extra: memory
36
+ Requires-Dist: numpy>=1.24; extra == 'memory'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # geny-executor
40
+
41
+ [![PyPI version](https://img.shields.io/pypi/v/geny-executor.svg)](https://pypi.org/project/geny-executor/)
42
+ [![Python 3.11+](https://img.shields.io/pypi/pyversions/geny-executor.svg)](https://pypi.org/project/geny-executor/)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44
+ [![CI](https://github.com/CocoRoF/geny-executor/actions/workflows/ci.yml/badge.svg)](https://github.com/CocoRoF/geny-executor/actions/workflows/ci.yml)
45
+
46
+ **Harness-engineered agent pipeline library built on the Anthropic API.**
47
+
48
+ geny-executor implements a **16-stage pipeline** with **dual-abstraction architecture** — inspired by Claude Code's agent loop and Anthropic's harness design principles. No LangChain. No LangGraph. Just a clean, modular pipeline that gives you full control over every step of agent execution.
49
+
50
+ [한국어 README](README_ko.md)
51
+
52
+ ---
53
+
54
+ ## Why geny-executor?
55
+
56
+ | Problem | geny-executor's Answer |
57
+ |---------|----------------------|
58
+ | Frameworks hide too much behind abstractions | Every stage is explicit and inspectable |
59
+ | Hard to customize one part without rewriting everything | **Dual Abstraction** — swap stages *or* strategies within stages |
60
+ | Agent loops are opaque black boxes | 16 clearly defined stages with event-driven observability |
61
+ | Vendor lock-in across multiple LLM providers | Single dependency: Anthropic SDK. One provider, done right |
62
+ | Cost tracking is an afterthought | Built-in token tracking, cost calculation, and budget guards |
63
+
64
+ ---
65
+
66
+ ## Architecture
67
+
68
+ ### The 16-Stage Pipeline
69
+
70
+ ```
71
+ Phase A (once): [1: Input]
72
+ Phase B (loop): [2: Context] → [3: System] → [4: Guard] → [5: Cache]
73
+ → [6: API] → [7: Token] → [8: Think] → [9: Parse]
74
+ → [10: Tool] → [11: Agent] → [12: Evaluate] → [13: Loop]
75
+ Phase C (once): [14: Emit] → [15: Memory] → [16: Yield]
76
+ ```
77
+
78
+ | # | Stage | Purpose | Example Strategies |
79
+ |---|-------|---------|--------------------|
80
+ | 1 | **Input** | Validate & normalize user input | Default, Strict, Schema, Multimodal |
81
+ | 2 | **Context** | Load conversation history & memory | SimpleLoad, ProgressiveDisclosure, VectorSearch |
82
+ | 3 | **System** | Build system prompt | Static, Composable, Adaptive |
83
+ | 4 | **Guard** | Safety checks & budget enforcement | TokenBudget, Cost, RateLimit, Permission |
84
+ | 5 | **Cache** | Optimize prompt caching | NoCache, System, Aggressive, Adaptive |
85
+ | 6 | **API** | Call Anthropic Messages API | Anthropic, Mock, Recording, Replay |
86
+ | 7 | **Token** | Track usage & calculate costs | Default, Detailed + AnthropicPricing |
87
+ | 8 | **Think** | Process extended thinking blocks | Passthrough, ExtractAndStore, Filter |
88
+ | 9 | **Parse** | Parse response & detect completion | Default, StructuredOutput + SignalDetector |
89
+ | 10 | **Tool** | Execute tool calls | Sequential, Parallel + RegistryRouter |
90
+ | 11 | **Agent** | Multi-agent orchestration | SingleAgent, Delegate, Evaluator |
91
+ | 12 | **Evaluate** | Judge quality & completion | SignalBased, CriteriaBased, AgentEval |
92
+ | 13 | **Loop** | Decide: continue or finish? | Standard, SingleTurn, BudgetAware |
93
+ | 14 | **Emit** | Output results | Text, Callback, VTuber, TTS, Streaming |
94
+ | 15 | **Memory** | Persist conversation memory | AppendOnly, Reflective + File/SQLite |
95
+ | 16 | **Yield** | Format final result | Default, Structured, Streaming |
96
+
97
+ ### Dual Abstraction
98
+
99
+ ```
100
+ ┌─ Level 1: Stage Abstraction ─────────────────────────┐
101
+ │ Swap entire stage modules in/out of the pipeline │
102
+ │ │
103
+ │ ┌─ Level 2: Strategy Abstraction ─────────────────┐ │
104
+ │ │ Swap internal logic within a stage │ │
105
+ │ │ │ │
106
+ │ │ ContextStage can use: │ │
107
+ │ │ → SimpleLoadStrategy (default) │ │
108
+ │ │ → ProgressiveDisclosureStrategy │ │
109
+ │ │ → VectorSearchStrategy │ │
110
+ │ │ → YourCustomStrategy │ │
111
+ │ └──────────────────────────────────────────────────┘ │
112
+ └────────────────────────────────────────────────────────┘
113
+ ```
114
+
115
+ **Level 1:** Replace an entire stage (e.g., swap `APIStage` for a custom provider).
116
+ **Level 2:** Change behavior *within* a stage (e.g., switch context loading strategy from simple to vector search).
117
+
118
+ ---
119
+
120
+ ## Installation
121
+
122
+ ```bash
123
+ pip install geny-executor
124
+ ```
125
+
126
+ With optional dependencies:
127
+
128
+ ```bash
129
+ # Memory features (numpy for vector operations)
130
+ pip install geny-executor[memory]
131
+
132
+ # All optional dependencies
133
+ pip install geny-executor[all]
134
+
135
+ # Development
136
+ pip install geny-executor[dev]
137
+ ```
138
+
139
+ ### Requirements
140
+
141
+ - Python 3.11+
142
+ - Anthropic API key
143
+
144
+ ---
145
+
146
+ ## Quick Start
147
+
148
+ ### Minimal Pipeline
149
+
150
+ The simplest possible agent — input, API call, parse, output:
151
+
152
+ ```python
153
+ import asyncio
154
+ from geny_executor import PipelinePresets
155
+
156
+ async def main():
157
+ pipeline = PipelinePresets.minimal(api_key="sk-ant-...")
158
+ result = await pipeline.run("What is the capital of France?")
159
+ print(result.text)
160
+
161
+ asyncio.run(main())
162
+ ```
163
+
164
+ ### Chat Pipeline
165
+
166
+ Full conversational agent with history, system prompt, and tool support:
167
+
168
+ ```python
169
+ from geny_executor import PipelinePresets
170
+
171
+ pipeline = PipelinePresets.chat(
172
+ api_key="sk-ant-...",
173
+ system_prompt="You are a helpful coding assistant.",
174
+ )
175
+
176
+ result = await pipeline.run("Explain Python decorators")
177
+ print(result.text)
178
+ print(f"Cost: ${result.total_cost_usd:.4f}")
179
+ ```
180
+
181
+ ### Agent Pipeline (All 16 Stages)
182
+
183
+ Autonomous agent with tools, evaluation, memory, and loop control:
184
+
185
+ ```python
186
+ from geny_executor import PipelinePresets
187
+ from geny_executor.tools import ToolRegistry, Tool, ToolResult, ToolContext
188
+
189
+ class SearchTool(Tool):
190
+ @property
191
+ def name(self) -> str:
192
+ return "search"
193
+
194
+ @property
195
+ def description(self) -> str:
196
+ return "Search the web for information"
197
+
198
+ @property
199
+ def input_schema(self) -> dict:
200
+ return {
201
+ "type": "object",
202
+ "properties": {
203
+ "query": {"type": "string", "description": "Search query"}
204
+ },
205
+ "required": ["query"],
206
+ }
207
+
208
+ async def execute(self, input: dict, context: ToolContext) -> ToolResult:
209
+ # Your search implementation
210
+ return ToolResult(content=f"Results for: {input['query']}")
211
+
212
+ registry = ToolRegistry()
213
+ registry.register(SearchTool())
214
+
215
+ pipeline = PipelinePresets.agent(
216
+ api_key="sk-ant-...",
217
+ system_prompt="You are a research assistant. Use tools to find answers.",
218
+ tools=registry,
219
+ max_turns=20,
220
+ )
221
+
222
+ result = await pipeline.run("Find the latest Python release version")
223
+ ```
224
+
225
+ ### Custom Pipeline with Builder
226
+
227
+ Fine-grained control over every stage:
228
+
229
+ ```python
230
+ from geny_executor import PipelineBuilder
231
+
232
+ pipeline = (
233
+ PipelineBuilder("my-agent", api_key="sk-ant-...")
234
+ .with_model(model="claude-sonnet-4-20250514", max_tokens=4096)
235
+ .with_system(prompt="You are a concise assistant.")
236
+ .with_context()
237
+ .with_guard(cost_budget_usd=1.0, max_iterations=30)
238
+ .with_cache(strategy="aggressive")
239
+ .with_tools(registry=my_registry)
240
+ .with_think(enabled=True, budget_tokens=10000)
241
+ .with_evaluate()
242
+ .with_loop(max_turns=30)
243
+ .with_memory()
244
+ .build()
245
+ )
246
+
247
+ result = await pipeline.run("Complex multi-step task here")
248
+ ```
249
+
250
+ ### Manual Pipeline Construction
251
+
252
+ For maximum control, assemble stages directly:
253
+
254
+ ```python
255
+ from geny_executor import Pipeline, PipelineConfig
256
+ from geny_executor.stages.s01_input import InputStage
257
+ from geny_executor.stages.s06_api import APIStage, MockProvider
258
+ from geny_executor.stages.s09_parse import ParseStage
259
+ from geny_executor.stages.s16_yield import YieldStage
260
+
261
+ config = PipelineConfig(name="custom", api_key="sk-ant-...")
262
+ pipeline = Pipeline(config)
263
+
264
+ pipeline.register_stage(InputStage())
265
+ pipeline.register_stage(APIStage(provider=MockProvider(responses=["Hello!"])))
266
+ pipeline.register_stage(ParseStage())
267
+ pipeline.register_stage(YieldStage())
268
+
269
+ result = await pipeline.run("Test input")
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Sessions
275
+
276
+ Persistent state management across multiple interactions:
277
+
278
+ ```python
279
+ from geny_executor import PipelinePresets
280
+ from geny_executor.session import SessionManager
281
+
282
+ manager = SessionManager()
283
+ pipeline = PipelinePresets.chat(api_key="sk-ant-...")
284
+
285
+ # Create a session — state persists across runs
286
+ session = manager.create(pipeline)
287
+ result1 = await session.run("My name is Alice")
288
+ result2 = await session.run("What's my name?") # Remembers context
289
+
290
+ # List active sessions
291
+ for info in manager.list_sessions():
292
+ print(f"Session {info.session_id}: {info.message_count} messages, ${info.total_cost_usd:.4f}")
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Event System
298
+
299
+ Real-time observability with pub/sub events:
300
+
301
+ ```python
302
+ from geny_executor import PipelinePresets
303
+
304
+ pipeline = PipelinePresets.agent(api_key="sk-ant-...")
305
+
306
+ # Subscribe to specific events
307
+ @pipeline.on("stage.enter")
308
+ async def on_stage_enter(event):
309
+ print(f" → Entering: {event.stage}")
310
+
311
+ @pipeline.on("stage.exit")
312
+ async def on_stage_exit(event):
313
+ print(f" ← Exiting: {event.stage}")
314
+
315
+ @pipeline.on("pipeline.complete")
316
+ async def on_complete(event):
317
+ print(f"Done! Iterations: {event.data.get('iterations')}")
318
+
319
+ # Wildcard: listen to all events
320
+ @pipeline.on("*")
321
+ async def on_any(event):
322
+ pass # Log everything
323
+
324
+ result = await pipeline.run("Hello")
325
+ ```
326
+
327
+ ### Streaming
328
+
329
+ ```python
330
+ async for event in pipeline.run_stream("Solve this step by step"):
331
+ if event.type == "stage.enter":
332
+ print(f"Stage: {event.stage}")
333
+ elif event.type == "api.response":
334
+ print(f"Response received")
335
+ elif event.type == "pipeline.complete":
336
+ print(f"Final: {event.data['result'].text}")
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Tool System
342
+
343
+ ### Creating Tools
344
+
345
+ ```python
346
+ from geny_executor.tools import Tool, ToolResult, ToolContext
347
+
348
+ class CalculatorTool(Tool):
349
+ @property
350
+ def name(self) -> str:
351
+ return "calculator"
352
+
353
+ @property
354
+ def description(self) -> str:
355
+ return "Perform arithmetic calculations"
356
+
357
+ @property
358
+ def input_schema(self) -> dict:
359
+ return {
360
+ "type": "object",
361
+ "properties": {
362
+ "expression": {"type": "string", "description": "Math expression to evaluate"}
363
+ },
364
+ "required": ["expression"],
365
+ }
366
+
367
+ async def execute(self, input: dict, context: ToolContext) -> ToolResult:
368
+ try:
369
+ result = eval(input["expression"]) # Use a safe evaluator in production
370
+ return ToolResult(content=str(result))
371
+ except Exception as e:
372
+ return ToolResult(content=str(e), is_error=True)
373
+ ```
374
+
375
+ ### Tool Registry
376
+
377
+ ```python
378
+ from geny_executor.tools import ToolRegistry
379
+
380
+ registry = ToolRegistry()
381
+ registry.register(CalculatorTool())
382
+ registry.register(SearchTool())
383
+
384
+ # Filter tools per request
385
+ math_tools = registry.filter(include=["calculator"])
386
+ api_format = registry.to_api_format() # Anthropic API format
387
+ ```
388
+
389
+ ### MCP Integration
390
+
391
+ Connect to Model Context Protocol servers:
392
+
393
+ ```python
394
+ from geny_executor.tools.mcp import MCPManager
395
+
396
+ mcp = MCPManager()
397
+ await mcp.connect("filesystem", command="npx", args=["-y", "@anthropic/mcp-filesystem"])
398
+
399
+ # MCP tools are automatically adapted to the Tool interface
400
+ for tool in mcp.list_tools():
401
+ registry.register(tool)
402
+ ```
403
+
404
+ ---
405
+
406
+ ## Error Handling
407
+
408
+ Structured error hierarchy with automatic classification:
409
+
410
+ ```python
411
+ from geny_executor import (
412
+ GenyExecutorError, # Base exception
413
+ PipelineError, # Pipeline-level errors
414
+ StageError, # Stage execution errors
415
+ GuardRejectError, # Guard rejection (budget, permissions)
416
+ APIError, # Anthropic API errors (with category)
417
+ ToolExecutionError, # Tool failures
418
+ ErrorCategory, # rate_limited, timeout, token_limit, etc.
419
+ )
420
+
421
+ try:
422
+ result = await pipeline.run("input")
423
+ except GuardRejectError as e:
424
+ print(f"Blocked by guard: {e}")
425
+ except APIError as e:
426
+ print(f"API error ({e.category}): {e}")
427
+ if e.category == ErrorCategory.rate_limited:
428
+ # Handle rate limiting
429
+ pass
430
+ except GenyExecutorError as e:
431
+ print(f"Pipeline error: {e}")
432
+ ```
433
+
434
+ ---
435
+
436
+ ## Pipeline Presets
437
+
438
+ Five ready-to-use configurations:
439
+
440
+ | Preset | Stages | Use Case |
441
+ |--------|--------|----------|
442
+ | `PipelinePresets.minimal()` | 1→6→9→16 | Simple Q&A, testing |
443
+ | `PipelinePresets.chat()` | 1→2→3→4→5→6→7→9→13→16 | Conversational chatbot |
444
+ | `PipelinePresets.agent()` | All 16 | Autonomous agent with tools |
445
+ | `PipelinePresets.evaluator()` | 1→3→6→9→12→16 | Quality evaluation |
446
+ | `PipelinePresets.geny_vtuber()` | All 16 + VTuber emit | Geny VTuber system |
447
+
448
+ ---
449
+
450
+ ## Custom Stages & Strategies
451
+
452
+ ### Creating a Custom Strategy
453
+
454
+ ```python
455
+ from geny_executor.core.stage import Strategy
456
+
457
+ class MyContextStrategy(Strategy):
458
+ @property
459
+ def name(self) -> str:
460
+ return "my_context"
461
+
462
+ @property
463
+ def description(self) -> str:
464
+ return "Custom context loading with RAG"
465
+
466
+ def configure(self, config: dict) -> None:
467
+ self.top_k = config.get("top_k", 5)
468
+
469
+ async def load(self, state):
470
+ # Your custom context loading logic
471
+ ...
472
+ ```
473
+
474
+ ### Creating a Custom Stage
475
+
476
+ ```python
477
+ from geny_executor.core.stage import Stage
478
+ from geny_executor.core.state import PipelineState
479
+
480
+ class LoggingStage(Stage[dict, dict]):
481
+ @property
482
+ def name(self) -> str:
483
+ return "logging"
484
+
485
+ @property
486
+ def order(self) -> int:
487
+ return 7 # After API, before Think
488
+
489
+ @property
490
+ def category(self) -> str:
491
+ return "execution"
492
+
493
+ async def execute(self, input: dict, state: PipelineState) -> dict:
494
+ print(f"[{state.iteration}] API response received")
495
+ return input # Pass through
496
+
497
+ # Register into pipeline
498
+ pipeline.register_stage(LoggingStage())
499
+ ```
500
+
501
+ ---
502
+
503
+ ## Configuration Reference
504
+
505
+ ### ModelConfig
506
+
507
+ | Parameter | Type | Default | Description |
508
+ |-----------|------|---------|-------------|
509
+ | `model` | `str` | `"claude-sonnet-4-20250514"` | Anthropic model ID |
510
+ | `max_tokens` | `int` | `8192` | Max output tokens |
511
+ | `temperature` | `float` | `0.0` | Sampling temperature |
512
+ | `thinking_enabled` | `bool` | `False` | Enable extended thinking |
513
+ | `thinking_budget_tokens` | `int` | `10000` | Thinking token budget |
514
+
515
+ ### PipelineConfig
516
+
517
+ | Parameter | Type | Default | Description |
518
+ |-----------|------|---------|-------------|
519
+ | `name` | `str` | required | Pipeline name |
520
+ | `api_key` | `str` | required | Anthropic API key |
521
+ | `model` | `ModelConfig` | default | Model configuration |
522
+ | `max_iterations` | `int` | `50` | Max loop iterations |
523
+ | `cost_budget_usd` | `float?` | `None` | Cost budget limit |
524
+ | `context_window_budget` | `int` | `200_000` | Context window token limit |
525
+ | `stream` | `bool` | `False` | Enable streaming mode |
526
+ | `single_turn` | `bool` | `False` | Single turn (no loop) |
527
+
528
+ ---
529
+
530
+ ## Project Structure
531
+
532
+ ```
533
+ geny-executor/
534
+ ├── src/geny_executor/
535
+ │ ├── __init__.py # Public API
536
+ │ ├── py.typed # PEP 561 type marker
537
+ │ ├── core/ # Pipeline engine, config, state, errors
538
+ │ ├── events/ # EventBus pub/sub system
539
+ │ ├── stages/ # 16 pipeline stages (s01-s16)
540
+ │ ├── tools/ # Tool system + MCP integration
541
+ │ └── session/ # Session management + freshness
542
+ ├── tests/ # 81 unit & integration tests
543
+ ├── pyproject.toml # Package configuration (Hatch)
544
+ └── LICENSE # MIT License
545
+ ```
546
+
547
+ ---
548
+
549
+ ## Development
550
+
551
+ ```bash
552
+ # Clone
553
+ git clone https://github.com/CocoRoF/geny-executor.git
554
+ cd geny-executor
555
+
556
+ # Install with dev dependencies
557
+ pip install -e ".[dev]"
558
+
559
+ # Run tests
560
+ pytest
561
+
562
+ # Run tests with coverage
563
+ pytest --cov=geny_executor --cov-report=term-missing
564
+
565
+ # Lint
566
+ ruff check src/ tests/
567
+ ruff format src/ tests/
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Roadmap
573
+
574
+ - [ ] Streaming response support (full implementation)
575
+ - [ ] OpenTelemetry integration for tracing
576
+ - [ ] Additional memory backends (Redis, PostgreSQL)
577
+ - [ ] WebUI pipeline configurator
578
+ - [ ] Plugin system for community stages
579
+ - [ ] Batch execution mode
580
+
581
+ ---
582
+
583
+ ## License
584
+
585
+ This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
586
+
587
+ ---
588
+
589
+ ## Related Projects
590
+
591
+ - [Anthropic SDK](https://github.com/anthropics/anthropic-sdk-python) — The foundation geny-executor is built on
592
+ - [MCP](https://modelcontextprotocol.io/) — Model Context Protocol for tool integration
593
+ - [Claude Code](https://claude.ai/code) — The agent loop that inspired this architecture