ragradar-capture 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.
- ragradar_capture-0.1.0/.gitignore +38 -0
- ragradar_capture-0.1.0/PKG-INFO +237 -0
- ragradar_capture-0.1.0/README.md +218 -0
- ragradar_capture-0.1.0/pyproject.toml +39 -0
- ragradar_capture-0.1.0/src/ragradar_capture/__init__.py +44 -0
- ragradar_capture-0.1.0/src/ragradar_capture/api.py +361 -0
- ragradar_capture-0.1.0/src/ragradar_capture/scaffold/__init__.py +0 -0
- ragradar_capture-0.1.0/src/ragradar_capture/scaffold/cli.py +12 -0
- ragradar_capture-0.1.0/src/ragradar_capture/scaffold/template.py +50 -0
- ragradar_capture-0.1.0/src/ragradar_capture/thread_local.py +19 -0
- ragradar_capture-0.1.0/tests/conftest.py +13 -0
- ragradar_capture-0.1.0/tests/test_api.py +456 -0
- ragradar_capture-0.1.0/tests/test_scaffold.py +27 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.so
|
|
9
|
+
|
|
10
|
+
# uv
|
|
11
|
+
.uv/
|
|
12
|
+
uv.lock
|
|
13
|
+
|
|
14
|
+
# ragradar runtime — never commit user run data
|
|
15
|
+
.ragradar/
|
|
16
|
+
|
|
17
|
+
# environment
|
|
18
|
+
.env
|
|
19
|
+
*.env
|
|
20
|
+
.env.*
|
|
21
|
+
|
|
22
|
+
# IDE
|
|
23
|
+
.vscode/
|
|
24
|
+
.idea/
|
|
25
|
+
*.swp
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# test output
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
htmlcov/
|
|
34
|
+
.coverage
|
|
35
|
+
|
|
36
|
+
# example output
|
|
37
|
+
examples/rag_pipeline/output/
|
|
38
|
+
.claude/
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ragradar-capture
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pipeline instrumentation SDK for ragradar observability system
|
|
5
|
+
Project-URL: Homepage, https://github.com/pleokarthik/RAGRadar
|
|
6
|
+
Project-URL: Repository, https://github.com/pleokarthik/RAGRadar
|
|
7
|
+
Project-URL: Issues, https://github.com/pleokarthik/RAGRadar/issues
|
|
8
|
+
Author-email: Leo Karthik Paramasivan <pleokarthik@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: ragradar-core<0.2.0,>=0.1.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# ragradar-capture
|
|
21
|
+
|
|
22
|
+
Developer-side SDK that instruments a RAG or agentic pipeline at key
|
|
23
|
+
stages and writes structured run records to a local SQLite store
|
|
24
|
+
(`~/.ragradar/runs.db`). Capturing is the verb — the stored data are *runs*,
|
|
25
|
+
addressed as `sNrN` ids (e.g. `s2r3`).
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
pip install ragradar-capture
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Stdlib-only at runtime (plus the tiny `ragradar-core` kernel it shares with
|
|
32
|
+
the other ragradar packages).
|
|
33
|
+
|
|
34
|
+
## The one-liner
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import ragradar_capture
|
|
38
|
+
|
|
39
|
+
run_id = ragradar_capture.capture("what is RRF?", "RRF fuses rankings.")
|
|
40
|
+
print(run_id) # "s1r1"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Two fields are enough. Everything else is optional keyword-only:
|
|
44
|
+
`chunks`, `final_prompt`, `token_budget`, `history_pre`, `history_post`,
|
|
45
|
+
`eviction_reason`, `cache_events`, `tool_calls`, `model`, `token_usage`,
|
|
46
|
+
`pipeline`. A misspelled keyword fails immediately with `TypeError` —
|
|
47
|
+
the signature is explicit, not `**kwargs`.
|
|
48
|
+
|
|
49
|
+
## The staged pattern
|
|
50
|
+
|
|
51
|
+
`start()` returns a `Capture` — the action object for one pipeline run.
|
|
52
|
+
Every argument below takes plain Python — dicts, tuples, a bare int —
|
|
53
|
+
never a schema type you have to import:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import ragradar_capture
|
|
57
|
+
|
|
58
|
+
cap = ragradar_capture.start(query="what is RRF?", pipeline="my_project")
|
|
59
|
+
|
|
60
|
+
cap.chunks([ # retrieval stage — only
|
|
61
|
+
{"content": "RRF combines rankings from multiple retrievers.", # "content" is required
|
|
62
|
+
"retrieval_score": 0.9, "rerank_score": 0.95},
|
|
63
|
+
])
|
|
64
|
+
cap.context( # assembly stage — a bare
|
|
65
|
+
"System: answer using context.\nContext: ...\nQuery: what is RRF?",
|
|
66
|
+
4096, # int = total token limit; headroom is derived
|
|
67
|
+
)
|
|
68
|
+
cap.history( # history management stage
|
|
69
|
+
pre=[{"user": "hello"}],
|
|
70
|
+
post=[{"user": "hello"}],
|
|
71
|
+
eviction_reason="token_budget",
|
|
72
|
+
)
|
|
73
|
+
cap.cache({"c1": True}) # any time pre-commit
|
|
74
|
+
cap.tool_call({"tool_name": "rerank", # appends, once per call
|
|
75
|
+
"arguments": {"chunk_ids": ["c1"]}})
|
|
76
|
+
run_id = cap.response( # LLM output stage — auto-commits
|
|
77
|
+
"RRF merges ranked lists into one.",
|
|
78
|
+
token_usage={"input_tokens": 300, "output_tokens": 40}, # total derived
|
|
79
|
+
)
|
|
80
|
+
print(run_id)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The schema dataclasses (`ChunkRecord`, `TokenBudget`, `Turn`, `CacheEvent`,
|
|
84
|
+
`TokenUsage`, `ToolCallRecord` — tables below) still exist for callers who
|
|
85
|
+
want static typing or already have data in that shape; pass one in
|
|
86
|
+
anywhere a dict is shown above and it's used as-is.
|
|
87
|
+
|
|
88
|
+
`cap.response()` auto-commits and returns the run id; `cap.commit()` is
|
|
89
|
+
only needed if you skip `response()`. Commit is idempotent — a second
|
|
90
|
+
call returns the same id without writing again.
|
|
91
|
+
|
|
92
|
+
### The returned run id
|
|
93
|
+
|
|
94
|
+
Every committed capture hands back the run's `sNrN` id — also available
|
|
95
|
+
as `cap.run_id` (which is `None` before commit). Feed it straight to the
|
|
96
|
+
other tools:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
ragradar explain s2r3 # analyze the run
|
|
100
|
+
ragradar-evaluate run s2r3 # score it
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If an internal failure was swallowed (see below), the id is `None`
|
|
104
|
+
instead — check for that before passing it on.
|
|
105
|
+
|
|
106
|
+
### Thread-local proxies
|
|
107
|
+
|
|
108
|
+
After `ragradar_capture.start()`, module-level functions
|
|
109
|
+
(`ragradar_capture.chunks()`, `.context()`, `.history()`, `.cache()`,
|
|
110
|
+
`.tool_call()`, `.response()`, `.commit()`) route to the same capture
|
|
111
|
+
from anywhere on that thread — no need to pass the `Capture` object
|
|
112
|
+
through every function signature. With no active capture they log an
|
|
113
|
+
error and no-op.
|
|
114
|
+
|
|
115
|
+
## Dataclasses
|
|
116
|
+
|
|
117
|
+
These are the advanced/typed path — every capture call above accepts
|
|
118
|
+
plain dicts (with sensible defaults filled in — e.g. `chunks` only
|
|
119
|
+
needs `content`) or shorthand forms (`{"user": "..."}` turns, a bare int
|
|
120
|
+
budget), not these types directly. Reach for them only if you want
|
|
121
|
+
static typing or are round-tripping data already in this shape. All of
|
|
122
|
+
them tolerate unknown keyword arguments (silently dropped), so adding
|
|
123
|
+
fields later never breaks existing instrumentation.
|
|
124
|
+
|
|
125
|
+
### RunRecord
|
|
126
|
+
|
|
127
|
+
| Field | Type | Notes |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `query` | `str` | required |
|
|
130
|
+
| `response` | `str` | required |
|
|
131
|
+
| `chunks` | `list[ChunkRecord]` | optional |
|
|
132
|
+
| `final_prompt` | `str` | optional |
|
|
133
|
+
| `token_budget` | `TokenBudget` | optional |
|
|
134
|
+
| `history_pre` / `history_post` | `list[Turn]` | optional |
|
|
135
|
+
| `eviction_reason` | `str` | optional |
|
|
136
|
+
| `cache_events` | `list[CacheEvent]` | optional |
|
|
137
|
+
| `tool_calls` | `list[ToolCallRecord]` | optional |
|
|
138
|
+
| `model` | `str` | optional |
|
|
139
|
+
| `token_usage` | `TokenUsage` | optional |
|
|
140
|
+
|
|
141
|
+
### ChunkRecord
|
|
142
|
+
|
|
143
|
+
| Field | Type | Notes |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `chunk_id` | `str` | required |
|
|
146
|
+
| `source_doc_id` | `str` | required |
|
|
147
|
+
| `content` | `str` | required |
|
|
148
|
+
| `token_count` | `int` | required |
|
|
149
|
+
| `retrieval_score` | `float` | optional |
|
|
150
|
+
| `rerank_score` | `float` | optional |
|
|
151
|
+
| `retrieval_path` | `str` | optional — e.g. `"bm25"`, `"ann"`, `"hybrid"` |
|
|
152
|
+
| `truncated` | `bool` | default `False` |
|
|
153
|
+
| `cache_hit` | `bool` | optional |
|
|
154
|
+
|
|
155
|
+
### TokenBudget
|
|
156
|
+
|
|
157
|
+
| Field | Type |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `total_limit` | `int` |
|
|
160
|
+
| `chunks_allocated` | `int` |
|
|
161
|
+
| `history_allocated` | `int` |
|
|
162
|
+
| `system_allocated` | `int` |
|
|
163
|
+
| `headroom` | `int` |
|
|
164
|
+
|
|
165
|
+
### TokenUsage
|
|
166
|
+
|
|
167
|
+
| Field | Type |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `input_tokens` | `int` |
|
|
170
|
+
| `output_tokens` | `int` |
|
|
171
|
+
| `total_tokens` | `int` |
|
|
172
|
+
|
|
173
|
+
### Turn
|
|
174
|
+
|
|
175
|
+
| Field | Type | Notes |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| `role` | `str` | e.g. `"user"`, `"assistant"` |
|
|
178
|
+
| `content` | `str` | |
|
|
179
|
+
| `tokens` | `int` | optional |
|
|
180
|
+
|
|
181
|
+
### CacheEvent
|
|
182
|
+
|
|
183
|
+
| Field | Type | Notes |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `chunk_id` | `str` | |
|
|
186
|
+
| `hit` | `bool` | |
|
|
187
|
+
| `cache_source` | `str` | optional — e.g. `"disk"` |
|
|
188
|
+
|
|
189
|
+
### ToolCallRecord
|
|
190
|
+
|
|
191
|
+
| Field | Type | Notes |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `tool_name` | `str` | |
|
|
194
|
+
| `arguments` | `dict` | |
|
|
195
|
+
| `result` | `str` | optional |
|
|
196
|
+
| `error` | `str` | optional |
|
|
197
|
+
| `latency_ms` | `float` | optional |
|
|
198
|
+
|
|
199
|
+
## The never-raise philosophy (and strict mode)
|
|
200
|
+
|
|
201
|
+
In production, instrumentation must never take down the pipeline it
|
|
202
|
+
observes. Every capture call is wrapped internally: conversion errors,
|
|
203
|
+
store failures — all swallowed, logged to `~/.ragradar/errors.log`, and the
|
|
204
|
+
call returns (`None` where a run id was expected). Your pipeline never
|
|
205
|
+
sees an exception from ragradar-capture.
|
|
206
|
+
|
|
207
|
+
During development you usually want the opposite. Strict mode makes
|
|
208
|
+
those same errors raise:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
import ragradar_capture
|
|
212
|
+
|
|
213
|
+
ragradar_capture.set_strict(True) # in code
|
|
214
|
+
# or, without a code change:
|
|
215
|
+
# RAGRADAR_CAPTURE_STRICT=1 python my_pipeline.py
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The only error that raises regardless of mode is a misspelled keyword
|
|
219
|
+
to `capture()` — that's a bug at the call site, caught by Python itself
|
|
220
|
+
as `TypeError`.
|
|
221
|
+
|
|
222
|
+
## Scaffold CLI
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
ragradar-capture init
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Generates a starter `ctx_pipeline.py` in the current directory with
|
|
229
|
+
capture calls pre-positioned at the right pipeline stages (refuses to
|
|
230
|
+
overwrite an existing one).
|
|
231
|
+
|
|
232
|
+
## Store
|
|
233
|
+
|
|
234
|
+
First capture creates `~/.ragradar/runs.db` (schema managed by `ragradar-core`;
|
|
235
|
+
migrations are automatic). Sessions group runs automatically on a
|
|
236
|
+
30-minute idle gap — no developer action needed. Browse with `ragradar`,
|
|
237
|
+
score with `ragradar-evaluate`.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# ragradar-capture
|
|
2
|
+
|
|
3
|
+
Developer-side SDK that instruments a RAG or agentic pipeline at key
|
|
4
|
+
stages and writes structured run records to a local SQLite store
|
|
5
|
+
(`~/.ragradar/runs.db`). Capturing is the verb — the stored data are *runs*,
|
|
6
|
+
addressed as `sNrN` ids (e.g. `s2r3`).
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
pip install ragradar-capture
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Stdlib-only at runtime (plus the tiny `ragradar-core` kernel it shares with
|
|
13
|
+
the other ragradar packages).
|
|
14
|
+
|
|
15
|
+
## The one-liner
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import ragradar_capture
|
|
19
|
+
|
|
20
|
+
run_id = ragradar_capture.capture("what is RRF?", "RRF fuses rankings.")
|
|
21
|
+
print(run_id) # "s1r1"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Two fields are enough. Everything else is optional keyword-only:
|
|
25
|
+
`chunks`, `final_prompt`, `token_budget`, `history_pre`, `history_post`,
|
|
26
|
+
`eviction_reason`, `cache_events`, `tool_calls`, `model`, `token_usage`,
|
|
27
|
+
`pipeline`. A misspelled keyword fails immediately with `TypeError` —
|
|
28
|
+
the signature is explicit, not `**kwargs`.
|
|
29
|
+
|
|
30
|
+
## The staged pattern
|
|
31
|
+
|
|
32
|
+
`start()` returns a `Capture` — the action object for one pipeline run.
|
|
33
|
+
Every argument below takes plain Python — dicts, tuples, a bare int —
|
|
34
|
+
never a schema type you have to import:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import ragradar_capture
|
|
38
|
+
|
|
39
|
+
cap = ragradar_capture.start(query="what is RRF?", pipeline="my_project")
|
|
40
|
+
|
|
41
|
+
cap.chunks([ # retrieval stage — only
|
|
42
|
+
{"content": "RRF combines rankings from multiple retrievers.", # "content" is required
|
|
43
|
+
"retrieval_score": 0.9, "rerank_score": 0.95},
|
|
44
|
+
])
|
|
45
|
+
cap.context( # assembly stage — a bare
|
|
46
|
+
"System: answer using context.\nContext: ...\nQuery: what is RRF?",
|
|
47
|
+
4096, # int = total token limit; headroom is derived
|
|
48
|
+
)
|
|
49
|
+
cap.history( # history management stage
|
|
50
|
+
pre=[{"user": "hello"}],
|
|
51
|
+
post=[{"user": "hello"}],
|
|
52
|
+
eviction_reason="token_budget",
|
|
53
|
+
)
|
|
54
|
+
cap.cache({"c1": True}) # any time pre-commit
|
|
55
|
+
cap.tool_call({"tool_name": "rerank", # appends, once per call
|
|
56
|
+
"arguments": {"chunk_ids": ["c1"]}})
|
|
57
|
+
run_id = cap.response( # LLM output stage — auto-commits
|
|
58
|
+
"RRF merges ranked lists into one.",
|
|
59
|
+
token_usage={"input_tokens": 300, "output_tokens": 40}, # total derived
|
|
60
|
+
)
|
|
61
|
+
print(run_id)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The schema dataclasses (`ChunkRecord`, `TokenBudget`, `Turn`, `CacheEvent`,
|
|
65
|
+
`TokenUsage`, `ToolCallRecord` — tables below) still exist for callers who
|
|
66
|
+
want static typing or already have data in that shape; pass one in
|
|
67
|
+
anywhere a dict is shown above and it's used as-is.
|
|
68
|
+
|
|
69
|
+
`cap.response()` auto-commits and returns the run id; `cap.commit()` is
|
|
70
|
+
only needed if you skip `response()`. Commit is idempotent — a second
|
|
71
|
+
call returns the same id without writing again.
|
|
72
|
+
|
|
73
|
+
### The returned run id
|
|
74
|
+
|
|
75
|
+
Every committed capture hands back the run's `sNrN` id — also available
|
|
76
|
+
as `cap.run_id` (which is `None` before commit). Feed it straight to the
|
|
77
|
+
other tools:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
ragradar explain s2r3 # analyze the run
|
|
81
|
+
ragradar-evaluate run s2r3 # score it
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If an internal failure was swallowed (see below), the id is `None`
|
|
85
|
+
instead — check for that before passing it on.
|
|
86
|
+
|
|
87
|
+
### Thread-local proxies
|
|
88
|
+
|
|
89
|
+
After `ragradar_capture.start()`, module-level functions
|
|
90
|
+
(`ragradar_capture.chunks()`, `.context()`, `.history()`, `.cache()`,
|
|
91
|
+
`.tool_call()`, `.response()`, `.commit()`) route to the same capture
|
|
92
|
+
from anywhere on that thread — no need to pass the `Capture` object
|
|
93
|
+
through every function signature. With no active capture they log an
|
|
94
|
+
error and no-op.
|
|
95
|
+
|
|
96
|
+
## Dataclasses
|
|
97
|
+
|
|
98
|
+
These are the advanced/typed path — every capture call above accepts
|
|
99
|
+
plain dicts (with sensible defaults filled in — e.g. `chunks` only
|
|
100
|
+
needs `content`) or shorthand forms (`{"user": "..."}` turns, a bare int
|
|
101
|
+
budget), not these types directly. Reach for them only if you want
|
|
102
|
+
static typing or are round-tripping data already in this shape. All of
|
|
103
|
+
them tolerate unknown keyword arguments (silently dropped), so adding
|
|
104
|
+
fields later never breaks existing instrumentation.
|
|
105
|
+
|
|
106
|
+
### RunRecord
|
|
107
|
+
|
|
108
|
+
| Field | Type | Notes |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `query` | `str` | required |
|
|
111
|
+
| `response` | `str` | required |
|
|
112
|
+
| `chunks` | `list[ChunkRecord]` | optional |
|
|
113
|
+
| `final_prompt` | `str` | optional |
|
|
114
|
+
| `token_budget` | `TokenBudget` | optional |
|
|
115
|
+
| `history_pre` / `history_post` | `list[Turn]` | optional |
|
|
116
|
+
| `eviction_reason` | `str` | optional |
|
|
117
|
+
| `cache_events` | `list[CacheEvent]` | optional |
|
|
118
|
+
| `tool_calls` | `list[ToolCallRecord]` | optional |
|
|
119
|
+
| `model` | `str` | optional |
|
|
120
|
+
| `token_usage` | `TokenUsage` | optional |
|
|
121
|
+
|
|
122
|
+
### ChunkRecord
|
|
123
|
+
|
|
124
|
+
| Field | Type | Notes |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `chunk_id` | `str` | required |
|
|
127
|
+
| `source_doc_id` | `str` | required |
|
|
128
|
+
| `content` | `str` | required |
|
|
129
|
+
| `token_count` | `int` | required |
|
|
130
|
+
| `retrieval_score` | `float` | optional |
|
|
131
|
+
| `rerank_score` | `float` | optional |
|
|
132
|
+
| `retrieval_path` | `str` | optional — e.g. `"bm25"`, `"ann"`, `"hybrid"` |
|
|
133
|
+
| `truncated` | `bool` | default `False` |
|
|
134
|
+
| `cache_hit` | `bool` | optional |
|
|
135
|
+
|
|
136
|
+
### TokenBudget
|
|
137
|
+
|
|
138
|
+
| Field | Type |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `total_limit` | `int` |
|
|
141
|
+
| `chunks_allocated` | `int` |
|
|
142
|
+
| `history_allocated` | `int` |
|
|
143
|
+
| `system_allocated` | `int` |
|
|
144
|
+
| `headroom` | `int` |
|
|
145
|
+
|
|
146
|
+
### TokenUsage
|
|
147
|
+
|
|
148
|
+
| Field | Type |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `input_tokens` | `int` |
|
|
151
|
+
| `output_tokens` | `int` |
|
|
152
|
+
| `total_tokens` | `int` |
|
|
153
|
+
|
|
154
|
+
### Turn
|
|
155
|
+
|
|
156
|
+
| Field | Type | Notes |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `role` | `str` | e.g. `"user"`, `"assistant"` |
|
|
159
|
+
| `content` | `str` | |
|
|
160
|
+
| `tokens` | `int` | optional |
|
|
161
|
+
|
|
162
|
+
### CacheEvent
|
|
163
|
+
|
|
164
|
+
| Field | Type | Notes |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| `chunk_id` | `str` | |
|
|
167
|
+
| `hit` | `bool` | |
|
|
168
|
+
| `cache_source` | `str` | optional — e.g. `"disk"` |
|
|
169
|
+
|
|
170
|
+
### ToolCallRecord
|
|
171
|
+
|
|
172
|
+
| Field | Type | Notes |
|
|
173
|
+
|---|---|---|
|
|
174
|
+
| `tool_name` | `str` | |
|
|
175
|
+
| `arguments` | `dict` | |
|
|
176
|
+
| `result` | `str` | optional |
|
|
177
|
+
| `error` | `str` | optional |
|
|
178
|
+
| `latency_ms` | `float` | optional |
|
|
179
|
+
|
|
180
|
+
## The never-raise philosophy (and strict mode)
|
|
181
|
+
|
|
182
|
+
In production, instrumentation must never take down the pipeline it
|
|
183
|
+
observes. Every capture call is wrapped internally: conversion errors,
|
|
184
|
+
store failures — all swallowed, logged to `~/.ragradar/errors.log`, and the
|
|
185
|
+
call returns (`None` where a run id was expected). Your pipeline never
|
|
186
|
+
sees an exception from ragradar-capture.
|
|
187
|
+
|
|
188
|
+
During development you usually want the opposite. Strict mode makes
|
|
189
|
+
those same errors raise:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
import ragradar_capture
|
|
193
|
+
|
|
194
|
+
ragradar_capture.set_strict(True) # in code
|
|
195
|
+
# or, without a code change:
|
|
196
|
+
# RAGRADAR_CAPTURE_STRICT=1 python my_pipeline.py
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The only error that raises regardless of mode is a misspelled keyword
|
|
200
|
+
to `capture()` — that's a bug at the call site, caught by Python itself
|
|
201
|
+
as `TypeError`.
|
|
202
|
+
|
|
203
|
+
## Scaffold CLI
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
ragradar-capture init
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Generates a starter `ctx_pipeline.py` in the current directory with
|
|
210
|
+
capture calls pre-positioned at the right pipeline stages (refuses to
|
|
211
|
+
overwrite an existing one).
|
|
212
|
+
|
|
213
|
+
## Store
|
|
214
|
+
|
|
215
|
+
First capture creates `~/.ragradar/runs.db` (schema managed by `ragradar-core`;
|
|
216
|
+
migrations are automatic). Sessions group runs automatically on a
|
|
217
|
+
30-minute idle gap — no developer action needed. Browse with `ragradar`,
|
|
218
|
+
score with `ragradar-evaluate`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ragradar-capture"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Pipeline instrumentation SDK for ragradar observability system"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Leo Karthik Paramasivan", email = "pleokarthik@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"ragradar-core>=0.1.0,<0.2.0",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/pleokarthik/RAGRadar"
|
|
25
|
+
Repository = "https://github.com/pleokarthik/RAGRadar"
|
|
26
|
+
Issues = "https://github.com/pleokarthik/RAGRadar/issues"
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
ragradar-capture = "ragradar_capture.scaffold.cli:main"
|
|
30
|
+
|
|
31
|
+
[tool.uv.sources]
|
|
32
|
+
ragradar-core = { workspace = true }
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/ragradar_capture"]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from ragradar_core.schema import (
|
|
2
|
+
CacheEvent,
|
|
3
|
+
ChunkRecord,
|
|
4
|
+
RunRecord,
|
|
5
|
+
TokenBudget,
|
|
6
|
+
TokenUsage,
|
|
7
|
+
ToolCallRecord,
|
|
8
|
+
Turn,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from ragradar_capture.api import (
|
|
12
|
+
Capture,
|
|
13
|
+
cache,
|
|
14
|
+
capture,
|
|
15
|
+
chunks,
|
|
16
|
+
commit,
|
|
17
|
+
context,
|
|
18
|
+
history,
|
|
19
|
+
response,
|
|
20
|
+
set_strict,
|
|
21
|
+
start,
|
|
22
|
+
tool_call,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"Capture",
|
|
27
|
+
"start",
|
|
28
|
+
"capture",
|
|
29
|
+
"set_strict",
|
|
30
|
+
"chunks",
|
|
31
|
+
"context",
|
|
32
|
+
"history",
|
|
33
|
+
"response",
|
|
34
|
+
"cache",
|
|
35
|
+
"tool_call",
|
|
36
|
+
"commit",
|
|
37
|
+
"ChunkRecord",
|
|
38
|
+
"TokenBudget",
|
|
39
|
+
"TokenUsage",
|
|
40
|
+
"Turn",
|
|
41
|
+
"CacheEvent",
|
|
42
|
+
"ToolCallRecord",
|
|
43
|
+
"RunRecord",
|
|
44
|
+
]
|