flow-forge-ai-sdk 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.
- flow_forge_ai_sdk-0.1.0/PKG-INFO +307 -0
- flow_forge_ai_sdk-0.1.0/README.md +255 -0
- flow_forge_ai_sdk-0.1.0/pyproject.toml +113 -0
- flow_forge_ai_sdk-0.1.0/setup.cfg +4 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/__init__.py +0 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/config/__init__.py +0 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/config/config_handler.py +97 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/config/models.py +66 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/context.py +121 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/emitter.py +33 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/__init__.py +24 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/base.py +84 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/httpx_instr.py +140 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/models/__init__.py +0 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/models/llm_payloads.py +76 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/models/tool_payloads.py +85 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/ollama_instr.py +258 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/openai_instr.py +271 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/requests_instr.py +139 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/trace_tool.py +82 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/utils.py +26 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/instrumentation/workflow.py +39 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/internal_logging/__init__.py +0 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/internal_logging/logger.py +279 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/internal_logging/logging_handler.py +60 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/py.typed +0 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/replay.py +166 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/runtime.py +424 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/__init__.py +24 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/base.py +13 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/composite_sink.py +34 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/console_sink.py +33 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/database_sink.py +166 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/file_sink.py +29 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/handlers/__init__.py +24 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/handlers/jsonl_handler.py +113 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/handlers/mongodb_handler.py +248 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/handlers/mysql_handler.py +274 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/handlers/postgres_handler.py +284 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/handlers/resource_handler.py +154 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/handlers/sqlite_handler.py +247 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/log_sink.py +17 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/memory_sink.py +26 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/models/__init__.py +0 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/models/event.py +63 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/models/run.py +31 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/models/step.py +31 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/sinks/sink_router.py +34 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/utils/__init__.py +0 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/utils/decorators.py +12 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai/utils/toml.py +9 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai_sdk.egg-info/PKG-INFO +307 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai_sdk.egg-info/SOURCES.txt +63 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai_sdk.egg-info/dependency_links.txt +1 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai_sdk.egg-info/requires.txt +40 -0
- flow_forge_ai_sdk-0.1.0/src/flow_forge_ai_sdk.egg-info/top_level.txt +1 -0
- flow_forge_ai_sdk-0.1.0/tests/test_config.py +101 -0
- flow_forge_ai_sdk-0.1.0/tests/test_context.py +298 -0
- flow_forge_ai_sdk-0.1.0/tests/test_database_sink.py +83 -0
- flow_forge_ai_sdk-0.1.0/tests/test_error_run.py +17 -0
- flow_forge_ai_sdk-0.1.0/tests/test_events.py +15 -0
- flow_forge_ai_sdk-0.1.0/tests/test_logging.py +291 -0
- flow_forge_ai_sdk-0.1.0/tests/test_ollama_instrumentation.py +112 -0
- flow_forge_ai_sdk-0.1.0/tests/test_sinks.py +323 -0
- flow_forge_ai_sdk-0.1.0/tests/test_span.py +17 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flow-forge-ai-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Build, trace, inspect, and replay AI workflows with pluggable instrumentation and storage backends.
|
|
5
|
+
Author-email: Alon Yampolski <yampolski.a@gmail.com>
|
|
6
|
+
Maintainer-email: Alon Yampolski <yampolski.a@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/alonzo86/flow-forge-ai
|
|
9
|
+
Project-URL: Documentation, https://github.com/alonzo86/flow-forge-ai#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/alonzo86/flow-forge-ai
|
|
11
|
+
Project-URL: Issues, https://github.com/alonzo86/flow-forge-ai/issues
|
|
12
|
+
Keywords: ai,llm,agents,observability,instrumentation,tracing,workflow,replay,langchain,openai
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Provides-Extra: httpx-instr
|
|
23
|
+
Requires-Dist: httpx>=0.24.0; extra == "httpx-instr"
|
|
24
|
+
Provides-Extra: openai-instr
|
|
25
|
+
Requires-Dist: openai>=1.0.0; extra == "openai-instr"
|
|
26
|
+
Provides-Extra: ollama-instr
|
|
27
|
+
Requires-Dist: ollama>=0.6.2; extra == "ollama-instr"
|
|
28
|
+
Provides-Extra: langchain-instr
|
|
29
|
+
Requires-Dist: langchain>=1.3.9; extra == "langchain-instr"
|
|
30
|
+
Provides-Extra: postgres-sink
|
|
31
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres-sink"
|
|
32
|
+
Provides-Extra: mongodb-sink
|
|
33
|
+
Requires-Dist: pymongo>=3.12.0; extra == "mongodb-sink"
|
|
34
|
+
Provides-Extra: mysql-sink
|
|
35
|
+
Requires-Dist: mysql-connector-python>=8.0.0; extra == "mysql-sink"
|
|
36
|
+
Provides-Extra: sqlite-sink
|
|
37
|
+
Requires-Dist: pysqlite3>=0.6.0; extra == "sqlite-sink"
|
|
38
|
+
Provides-Extra: ui
|
|
39
|
+
Requires-Dist: flow-forge-ai-sdk-ui>=0.1.0; extra == "ui"
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: respx>=0.23.1; extra == "dev"
|
|
42
|
+
Requires-Dist: responses>=0.26.1; extra == "dev"
|
|
43
|
+
Requires-Dist: mypy>=2.1.0; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest>=9.0.3; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
47
|
+
Requires-Dist: testcontainers>=4.14.2; extra == "dev"
|
|
48
|
+
Requires-Dist: pylint[spelling]>=4.0.6; extra == "dev"
|
|
49
|
+
Requires-Dist: pip-audit>=2.10.1; extra == "dev"
|
|
50
|
+
Requires-Dist: bandit>=1.9.4; extra == "dev"
|
|
51
|
+
Requires-Dist: build; extra == "dev"
|
|
52
|
+
|
|
53
|
+
# flow-forge-ai
|
|
54
|
+
|
|
55
|
+
Core runtime, instrumentation, and storage package for Flow Forge AI.
|
|
56
|
+
|
|
57
|
+
[](#installation)
|
|
58
|
+
[](#development)
|
|
59
|
+
[](#development)
|
|
60
|
+
[](#development)
|
|
61
|
+
|
|
62
|
+
## What It Does
|
|
63
|
+
|
|
64
|
+
- Wraps workflow code with a lightweight run context (context manager or decorator)
|
|
65
|
+
- Auto-instruments LLM and HTTP libraries so every call emits structured trace events
|
|
66
|
+
- Routes events to one or more configurable sinks (file, console, memory, database)
|
|
67
|
+
- Starts an in-process HTTP listener so the UI can query and replay past runs
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cd core
|
|
73
|
+
pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Optional Extras
|
|
77
|
+
|
|
78
|
+
Install only the extras you need:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Instrumentors
|
|
82
|
+
pip install -e ".[openai-instr]" # OpenAI
|
|
83
|
+
pip install -e ".[ollama-instr]" # Ollama
|
|
84
|
+
pip install -e ".[httpx-instr]" # httpx
|
|
85
|
+
pip install -e ".[langchain-instr]" # LangChain
|
|
86
|
+
|
|
87
|
+
# Storage backends
|
|
88
|
+
pip install -e ".[sqlite-sink]"
|
|
89
|
+
pip install -e ".[postgres-sink]"
|
|
90
|
+
pip install -e ".[mysql-sink]"
|
|
91
|
+
pip install -e ".[mongodb-sink]"
|
|
92
|
+
|
|
93
|
+
# Development tooling
|
|
94
|
+
pip install -e ".[dev]"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Quick Start
|
|
98
|
+
|
|
99
|
+
### 1. Create a config file
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cp ../../config.example.toml config.toml
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Configuration is loaded automatically from `config.toml` in the current working directory.
|
|
106
|
+
|
|
107
|
+
### 2. Choose a usage pattern
|
|
108
|
+
|
|
109
|
+
#### Context manager
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from flow_forge_ai.runtime import runtime
|
|
113
|
+
|
|
114
|
+
with runtime.run(workflow="my-workflow") as run_id:
|
|
115
|
+
# instrumented calls inside this block are traced
|
|
116
|
+
print(f"run_id={run_id}")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Workflow decorator
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from flow_forge_ai.instrumentation.workflow import workflow
|
|
123
|
+
|
|
124
|
+
@workflow(workflow_id="my-pipeline")
|
|
125
|
+
def pipeline():
|
|
126
|
+
return "done"
|
|
127
|
+
|
|
128
|
+
pipeline()
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Step decorator
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
@workflow(workflow_id="article-summarizer")
|
|
135
|
+
def summarize(articles: list[str]) -> str:
|
|
136
|
+
return combine([summarize_one(a) for a in articles])
|
|
137
|
+
|
|
138
|
+
@summarize.step(step_id="summarize")
|
|
139
|
+
def summarize_one(text: str) -> str:
|
|
140
|
+
...
|
|
141
|
+
|
|
142
|
+
@summarize.step(step_id="combine")
|
|
143
|
+
def combine(summaries: list[str]) -> str:
|
|
144
|
+
...
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Tool tracing
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from flow_forge_ai.instrumentation.trace_tool import trace_tool
|
|
151
|
+
|
|
152
|
+
@trace_tool(version="v1", tool_id="knowledge_base_search")
|
|
153
|
+
def search_knowledge_base(query: str) -> str:
|
|
154
|
+
...
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 3. Run an example
|
|
158
|
+
|
|
159
|
+
Examples live in [examples/](examples/):
|
|
160
|
+
|
|
161
|
+
| Example | Instrumentation | Sink |
|
|
162
|
+
|---------|----------------|------|
|
|
163
|
+
| [01_openai_context_manager](examples/01_openai_context_manager/example.py) | OpenAI | JSONL file |
|
|
164
|
+
| [02_ollama_workflow_decorator](examples/02_ollama_workflow_decorator/example.py) | Ollama | SQLite |
|
|
165
|
+
| [03_httpx_context_manager](examples/03_httpx_context_manager/example.py) | httpx | JSONL file |
|
|
166
|
+
| [04_requests_workflow_decorator](examples/04_requests_workflow_decorator/example.py) | requests | JSONL file |
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
cd examples/02_ollama_workflow_decorator
|
|
170
|
+
python example.py
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Configuration
|
|
174
|
+
|
|
175
|
+
Configuration is TOML-based with three top-level sections.
|
|
176
|
+
|
|
177
|
+
### `[runtime]`
|
|
178
|
+
|
|
179
|
+
```toml
|
|
180
|
+
[runtime]
|
|
181
|
+
enable = true
|
|
182
|
+
source_sink = "sqlite log" # sink name used by the replay manager
|
|
183
|
+
listener_host = "127.0.0.1"
|
|
184
|
+
listener_port = 7070
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### `[[instrumentors]]`
|
|
188
|
+
|
|
189
|
+
One entry per library to auto-instrument:
|
|
190
|
+
|
|
191
|
+
```toml
|
|
192
|
+
[[instrumentors]]
|
|
193
|
+
class_path = "flow_forge_ai.instrumentation.openai_instr.OpenAIInstrumentor"
|
|
194
|
+
|
|
195
|
+
[[instrumentors]]
|
|
196
|
+
class_path = "flow_forge_ai.instrumentation.httpx_instr.HttpxInstrumentor"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### `[[sinks]]`
|
|
200
|
+
|
|
201
|
+
One entry per output destination. Multiple sinks are supported simultaneously:
|
|
202
|
+
|
|
203
|
+
```toml
|
|
204
|
+
[[sinks]]
|
|
205
|
+
name = "file log"
|
|
206
|
+
class_path = "flow_forge_ai.sinks.file_sink.FileSink"
|
|
207
|
+
|
|
208
|
+
[sinks.options]
|
|
209
|
+
class_path = "flow_forge_ai.sinks.handlers.jsonl_handler.JsonlHandler"
|
|
210
|
+
path = "./traces.jsonl"
|
|
211
|
+
|
|
212
|
+
[[sinks]]
|
|
213
|
+
name = "sqlite log"
|
|
214
|
+
class_path = "flow_forge_ai.sinks.database_sink.DatabaseSink"
|
|
215
|
+
|
|
216
|
+
[sinks.options]
|
|
217
|
+
class_path = "flow_forge_ai.sinks.handlers.sqlite_handler.SQLiteHandler"
|
|
218
|
+
url = "sqlite:///./runs.db"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### `env:` expansion in sink options
|
|
222
|
+
|
|
223
|
+
Prefix any sink option value with `env:` to read it from an environment variable at load time:
|
|
224
|
+
|
|
225
|
+
```toml
|
|
226
|
+
[sinks.options]
|
|
227
|
+
user = "env:FLOW_FORGE_DB_USER"
|
|
228
|
+
password = "env:FLOW_FORGE_DB_PASSWORD"
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Supported Instrumentors
|
|
232
|
+
|
|
233
|
+
| Class path | Library |
|
|
234
|
+
|-----------|---------|
|
|
235
|
+
| `flow_forge_ai.instrumentation.openai_instr.OpenAIInstrumentor` | `openai` |
|
|
236
|
+
| `flow_forge_ai.instrumentation.ollama_instr.OllamaInstrumentor` | `ollama` |
|
|
237
|
+
| `flow_forge_ai.instrumentation.httpx_instr.HttpxInstrumentor` | `httpx` |
|
|
238
|
+
| `flow_forge_ai.instrumentation.requests_instr.RequestsInstrumentor` | `requests` |
|
|
239
|
+
|
|
240
|
+
## Supported Sinks
|
|
241
|
+
|
|
242
|
+
| Class path | Storage |
|
|
243
|
+
|-----------|---------|
|
|
244
|
+
| `flow_forge_ai.sinks.file_sink.FileSink` | JSONL file |
|
|
245
|
+
| `flow_forge_ai.sinks.console_sink.ConsoleSink` | stdout |
|
|
246
|
+
| `flow_forge_ai.sinks.memory_sink.MemorySink` | in-process list |
|
|
247
|
+
| `flow_forge_ai.sinks.database_sink.DatabaseSink` | pluggable handler (see below) |
|
|
248
|
+
|
|
249
|
+
`DatabaseSink` delegates persistence to a handler specified via `sinks.options.class_path`:
|
|
250
|
+
|
|
251
|
+
| Handler | Backend |
|
|
252
|
+
|---------|---------|
|
|
253
|
+
| `flow_forge_ai.sinks.handlers.jsonl_handler.JsonlHandler` | JSONL file |
|
|
254
|
+
| `flow_forge_ai.sinks.handlers.sqlite_handler.SQLiteHandler` | SQLite |
|
|
255
|
+
| `flow_forge_ai.sinks.handlers.postgres_handler.PostgresHandler` | PostgreSQL |
|
|
256
|
+
| `flow_forge_ai.sinks.handlers.mysql_handler.MySQLHandler` | MySQL |
|
|
257
|
+
| `flow_forge_ai.sinks.handlers.mongodb_handler.MongoDBHandler` | MongoDB |
|
|
258
|
+
|
|
259
|
+
## Runtime Replay API
|
|
260
|
+
|
|
261
|
+
When `[runtime].enable = true`, the package starts a local HTTP listener that the UI connects to:
|
|
262
|
+
|
|
263
|
+
| Method | Path | Description |
|
|
264
|
+
|--------|------|-------------|
|
|
265
|
+
| `GET` | `/api/runs` | List all recorded runs |
|
|
266
|
+
| `GET` | `/api/steps?run_id=<id>` | List steps for a run |
|
|
267
|
+
| `POST` | `/api/runs/{run_id}/replay` | Start a replay |
|
|
268
|
+
| `GET` | `/api/runs/{run_id}/replay` | Get replay status |
|
|
269
|
+
| `DELETE` | `/api/runs/{run_id}/replay` | Stop a replay |
|
|
270
|
+
|
|
271
|
+
## Package Layout
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
core/
|
|
275
|
+
├── src/flow_forge_ai/
|
|
276
|
+
│ ├── runtime.py # Runtime entry point (run context manager)
|
|
277
|
+
│ ├── emitter.py # Event emitter
|
|
278
|
+
│ ├── context.py # Run context
|
|
279
|
+
│ ├── replay.py # Replay manager
|
|
280
|
+
│ ├── config/ # Config loading and models
|
|
281
|
+
│ ├── instrumentation/ # Instrumentors + @workflow / @trace_tool decorators
|
|
282
|
+
│ ├── sinks/ # Sink implementations and handlers
|
|
283
|
+
│ ├── internal_logging/ # Internal logger
|
|
284
|
+
│ └── utils/ # Shared utilities
|
|
285
|
+
├── examples/ # Runnable end-to-end scenarios
|
|
286
|
+
└── tests/ # Unit, integration, and e2e tests
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Development
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Install dev dependencies
|
|
293
|
+
pip install -e ".[dev]"
|
|
294
|
+
|
|
295
|
+
# Run tests
|
|
296
|
+
pytest
|
|
297
|
+
|
|
298
|
+
# Run tests with coverage
|
|
299
|
+
pytest --cov=flow_forge_ai --cov-report=term-missing --cov-report=xml
|
|
300
|
+
|
|
301
|
+
# Type checking
|
|
302
|
+
pyright
|
|
303
|
+
|
|
304
|
+
# Lint (requires enchant)
|
|
305
|
+
brew install enchant
|
|
306
|
+
pylint ./src
|
|
307
|
+
```
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# flow-forge-ai
|
|
2
|
+
|
|
3
|
+
Core runtime, instrumentation, and storage package for Flow Forge AI.
|
|
4
|
+
|
|
5
|
+
[](#installation)
|
|
6
|
+
[](#development)
|
|
7
|
+
[](#development)
|
|
8
|
+
[](#development)
|
|
9
|
+
|
|
10
|
+
## What It Does
|
|
11
|
+
|
|
12
|
+
- Wraps workflow code with a lightweight run context (context manager or decorator)
|
|
13
|
+
- Auto-instruments LLM and HTTP libraries so every call emits structured trace events
|
|
14
|
+
- Routes events to one or more configurable sinks (file, console, memory, database)
|
|
15
|
+
- Starts an in-process HTTP listener so the UI can query and replay past runs
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd core
|
|
21
|
+
pip install -e .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Optional Extras
|
|
25
|
+
|
|
26
|
+
Install only the extras you need:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Instrumentors
|
|
30
|
+
pip install -e ".[openai-instr]" # OpenAI
|
|
31
|
+
pip install -e ".[ollama-instr]" # Ollama
|
|
32
|
+
pip install -e ".[httpx-instr]" # httpx
|
|
33
|
+
pip install -e ".[langchain-instr]" # LangChain
|
|
34
|
+
|
|
35
|
+
# Storage backends
|
|
36
|
+
pip install -e ".[sqlite-sink]"
|
|
37
|
+
pip install -e ".[postgres-sink]"
|
|
38
|
+
pip install -e ".[mysql-sink]"
|
|
39
|
+
pip install -e ".[mongodb-sink]"
|
|
40
|
+
|
|
41
|
+
# Development tooling
|
|
42
|
+
pip install -e ".[dev]"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
### 1. Create a config file
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cp ../../config.example.toml config.toml
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Configuration is loaded automatically from `config.toml` in the current working directory.
|
|
54
|
+
|
|
55
|
+
### 2. Choose a usage pattern
|
|
56
|
+
|
|
57
|
+
#### Context manager
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from flow_forge_ai.runtime import runtime
|
|
61
|
+
|
|
62
|
+
with runtime.run(workflow="my-workflow") as run_id:
|
|
63
|
+
# instrumented calls inside this block are traced
|
|
64
|
+
print(f"run_id={run_id}")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Workflow decorator
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from flow_forge_ai.instrumentation.workflow import workflow
|
|
71
|
+
|
|
72
|
+
@workflow(workflow_id="my-pipeline")
|
|
73
|
+
def pipeline():
|
|
74
|
+
return "done"
|
|
75
|
+
|
|
76
|
+
pipeline()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Step decorator
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
@workflow(workflow_id="article-summarizer")
|
|
83
|
+
def summarize(articles: list[str]) -> str:
|
|
84
|
+
return combine([summarize_one(a) for a in articles])
|
|
85
|
+
|
|
86
|
+
@summarize.step(step_id="summarize")
|
|
87
|
+
def summarize_one(text: str) -> str:
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
@summarize.step(step_id="combine")
|
|
91
|
+
def combine(summaries: list[str]) -> str:
|
|
92
|
+
...
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Tool tracing
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from flow_forge_ai.instrumentation.trace_tool import trace_tool
|
|
99
|
+
|
|
100
|
+
@trace_tool(version="v1", tool_id="knowledge_base_search")
|
|
101
|
+
def search_knowledge_base(query: str) -> str:
|
|
102
|
+
...
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 3. Run an example
|
|
106
|
+
|
|
107
|
+
Examples live in [examples/](examples/):
|
|
108
|
+
|
|
109
|
+
| Example | Instrumentation | Sink |
|
|
110
|
+
|---------|----------------|------|
|
|
111
|
+
| [01_openai_context_manager](examples/01_openai_context_manager/example.py) | OpenAI | JSONL file |
|
|
112
|
+
| [02_ollama_workflow_decorator](examples/02_ollama_workflow_decorator/example.py) | Ollama | SQLite |
|
|
113
|
+
| [03_httpx_context_manager](examples/03_httpx_context_manager/example.py) | httpx | JSONL file |
|
|
114
|
+
| [04_requests_workflow_decorator](examples/04_requests_workflow_decorator/example.py) | requests | JSONL file |
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cd examples/02_ollama_workflow_decorator
|
|
118
|
+
python example.py
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
Configuration is TOML-based with three top-level sections.
|
|
124
|
+
|
|
125
|
+
### `[runtime]`
|
|
126
|
+
|
|
127
|
+
```toml
|
|
128
|
+
[runtime]
|
|
129
|
+
enable = true
|
|
130
|
+
source_sink = "sqlite log" # sink name used by the replay manager
|
|
131
|
+
listener_host = "127.0.0.1"
|
|
132
|
+
listener_port = 7070
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `[[instrumentors]]`
|
|
136
|
+
|
|
137
|
+
One entry per library to auto-instrument:
|
|
138
|
+
|
|
139
|
+
```toml
|
|
140
|
+
[[instrumentors]]
|
|
141
|
+
class_path = "flow_forge_ai.instrumentation.openai_instr.OpenAIInstrumentor"
|
|
142
|
+
|
|
143
|
+
[[instrumentors]]
|
|
144
|
+
class_path = "flow_forge_ai.instrumentation.httpx_instr.HttpxInstrumentor"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `[[sinks]]`
|
|
148
|
+
|
|
149
|
+
One entry per output destination. Multiple sinks are supported simultaneously:
|
|
150
|
+
|
|
151
|
+
```toml
|
|
152
|
+
[[sinks]]
|
|
153
|
+
name = "file log"
|
|
154
|
+
class_path = "flow_forge_ai.sinks.file_sink.FileSink"
|
|
155
|
+
|
|
156
|
+
[sinks.options]
|
|
157
|
+
class_path = "flow_forge_ai.sinks.handlers.jsonl_handler.JsonlHandler"
|
|
158
|
+
path = "./traces.jsonl"
|
|
159
|
+
|
|
160
|
+
[[sinks]]
|
|
161
|
+
name = "sqlite log"
|
|
162
|
+
class_path = "flow_forge_ai.sinks.database_sink.DatabaseSink"
|
|
163
|
+
|
|
164
|
+
[sinks.options]
|
|
165
|
+
class_path = "flow_forge_ai.sinks.handlers.sqlite_handler.SQLiteHandler"
|
|
166
|
+
url = "sqlite:///./runs.db"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### `env:` expansion in sink options
|
|
170
|
+
|
|
171
|
+
Prefix any sink option value with `env:` to read it from an environment variable at load time:
|
|
172
|
+
|
|
173
|
+
```toml
|
|
174
|
+
[sinks.options]
|
|
175
|
+
user = "env:FLOW_FORGE_DB_USER"
|
|
176
|
+
password = "env:FLOW_FORGE_DB_PASSWORD"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Supported Instrumentors
|
|
180
|
+
|
|
181
|
+
| Class path | Library |
|
|
182
|
+
|-----------|---------|
|
|
183
|
+
| `flow_forge_ai.instrumentation.openai_instr.OpenAIInstrumentor` | `openai` |
|
|
184
|
+
| `flow_forge_ai.instrumentation.ollama_instr.OllamaInstrumentor` | `ollama` |
|
|
185
|
+
| `flow_forge_ai.instrumentation.httpx_instr.HttpxInstrumentor` | `httpx` |
|
|
186
|
+
| `flow_forge_ai.instrumentation.requests_instr.RequestsInstrumentor` | `requests` |
|
|
187
|
+
|
|
188
|
+
## Supported Sinks
|
|
189
|
+
|
|
190
|
+
| Class path | Storage |
|
|
191
|
+
|-----------|---------|
|
|
192
|
+
| `flow_forge_ai.sinks.file_sink.FileSink` | JSONL file |
|
|
193
|
+
| `flow_forge_ai.sinks.console_sink.ConsoleSink` | stdout |
|
|
194
|
+
| `flow_forge_ai.sinks.memory_sink.MemorySink` | in-process list |
|
|
195
|
+
| `flow_forge_ai.sinks.database_sink.DatabaseSink` | pluggable handler (see below) |
|
|
196
|
+
|
|
197
|
+
`DatabaseSink` delegates persistence to a handler specified via `sinks.options.class_path`:
|
|
198
|
+
|
|
199
|
+
| Handler | Backend |
|
|
200
|
+
|---------|---------|
|
|
201
|
+
| `flow_forge_ai.sinks.handlers.jsonl_handler.JsonlHandler` | JSONL file |
|
|
202
|
+
| `flow_forge_ai.sinks.handlers.sqlite_handler.SQLiteHandler` | SQLite |
|
|
203
|
+
| `flow_forge_ai.sinks.handlers.postgres_handler.PostgresHandler` | PostgreSQL |
|
|
204
|
+
| `flow_forge_ai.sinks.handlers.mysql_handler.MySQLHandler` | MySQL |
|
|
205
|
+
| `flow_forge_ai.sinks.handlers.mongodb_handler.MongoDBHandler` | MongoDB |
|
|
206
|
+
|
|
207
|
+
## Runtime Replay API
|
|
208
|
+
|
|
209
|
+
When `[runtime].enable = true`, the package starts a local HTTP listener that the UI connects to:
|
|
210
|
+
|
|
211
|
+
| Method | Path | Description |
|
|
212
|
+
|--------|------|-------------|
|
|
213
|
+
| `GET` | `/api/runs` | List all recorded runs |
|
|
214
|
+
| `GET` | `/api/steps?run_id=<id>` | List steps for a run |
|
|
215
|
+
| `POST` | `/api/runs/{run_id}/replay` | Start a replay |
|
|
216
|
+
| `GET` | `/api/runs/{run_id}/replay` | Get replay status |
|
|
217
|
+
| `DELETE` | `/api/runs/{run_id}/replay` | Stop a replay |
|
|
218
|
+
|
|
219
|
+
## Package Layout
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
core/
|
|
223
|
+
├── src/flow_forge_ai/
|
|
224
|
+
│ ├── runtime.py # Runtime entry point (run context manager)
|
|
225
|
+
│ ├── emitter.py # Event emitter
|
|
226
|
+
│ ├── context.py # Run context
|
|
227
|
+
│ ├── replay.py # Replay manager
|
|
228
|
+
│ ├── config/ # Config loading and models
|
|
229
|
+
│ ├── instrumentation/ # Instrumentors + @workflow / @trace_tool decorators
|
|
230
|
+
│ ├── sinks/ # Sink implementations and handlers
|
|
231
|
+
│ ├── internal_logging/ # Internal logger
|
|
232
|
+
│ └── utils/ # Shared utilities
|
|
233
|
+
├── examples/ # Runnable end-to-end scenarios
|
|
234
|
+
└── tests/ # Unit, integration, and e2e tests
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Development
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Install dev dependencies
|
|
241
|
+
pip install -e ".[dev]"
|
|
242
|
+
|
|
243
|
+
# Run tests
|
|
244
|
+
pytest
|
|
245
|
+
|
|
246
|
+
# Run tests with coverage
|
|
247
|
+
pytest --cov=flow_forge_ai --cov-report=term-missing --cov-report=xml
|
|
248
|
+
|
|
249
|
+
# Type checking
|
|
250
|
+
pyright
|
|
251
|
+
|
|
252
|
+
# Lint (requires enchant)
|
|
253
|
+
brew install enchant
|
|
254
|
+
pylint ./src
|
|
255
|
+
```
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "flow-forge-ai-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Build, trace, inspect, and replay AI workflows with pluggable instrumentation and storage backends."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = []
|
|
12
|
+
license = "MIT"
|
|
13
|
+
|
|
14
|
+
keywords = [
|
|
15
|
+
"ai",
|
|
16
|
+
"llm",
|
|
17
|
+
"agents",
|
|
18
|
+
"observability",
|
|
19
|
+
"instrumentation",
|
|
20
|
+
"tracing",
|
|
21
|
+
"workflow",
|
|
22
|
+
"replay",
|
|
23
|
+
"langchain",
|
|
24
|
+
"openai"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
classifiers = [
|
|
28
|
+
"Development Status :: 4 - Beta",
|
|
29
|
+
"Intended Audience :: Developers",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Software Development :: Libraries",
|
|
34
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
authors = [
|
|
38
|
+
{ name = "Alon Yampolski", email = "yampolski.a@gmail.com" }
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
maintainers = [
|
|
42
|
+
{ name = "Alon Yampolski", email = "yampolski.a@gmail.com" }
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/alonzo86/flow-forge-ai"
|
|
47
|
+
Documentation = "https://github.com/alonzo86/flow-forge-ai#readme"
|
|
48
|
+
Repository = "https://github.com/alonzo86/flow-forge-ai"
|
|
49
|
+
Issues = "https://github.com/alonzo86/flow-forge-ai/issues"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
[project.optional-dependencies]
|
|
53
|
+
httpx-instr = [
|
|
54
|
+
"httpx>=0.24.0",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
openai-instr = [
|
|
58
|
+
"openai>=1.0.0",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
ollama-instr = [
|
|
62
|
+
"ollama>=0.6.2",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
langchain-instr = [
|
|
66
|
+
"langchain>=1.3.9",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
postgres-sink = [
|
|
70
|
+
"psycopg2-binary>=2.9.0",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
mongodb-sink = [
|
|
74
|
+
"pymongo>=3.12.0",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
mysql-sink = [
|
|
78
|
+
"mysql-connector-python>=8.0.0",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
sqlite-sink = [
|
|
82
|
+
"pysqlite3>=0.6.0",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
ui = [
|
|
86
|
+
"flow-forge-ai-sdk-ui>=0.1.0",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
dev = [
|
|
90
|
+
"respx>=0.23.1",
|
|
91
|
+
"responses>=0.26.1",
|
|
92
|
+
"mypy>=2.1.0",
|
|
93
|
+
"pytest>=9.0.3",
|
|
94
|
+
"pytest-cov>=7.1.0",
|
|
95
|
+
"pytest-asyncio>=0.21.0",
|
|
96
|
+
"testcontainers>=4.14.2",
|
|
97
|
+
"pylint[spelling]>=4.0.6",
|
|
98
|
+
"pip-audit>=2.10.1",
|
|
99
|
+
"bandit>=1.9.4",
|
|
100
|
+
"build",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.setuptools.packages.find]
|
|
104
|
+
where = ["src"]
|
|
105
|
+
|
|
106
|
+
[tool.setuptools.package-data]
|
|
107
|
+
flow_forge_ai = [
|
|
108
|
+
"py.typed"
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
[tool.pytest.ini_options]
|
|
112
|
+
pythonpath = ["src", "tests"]
|
|
113
|
+
testpaths = ["tests"]
|
|
File without changes
|
|
File without changes
|