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.
@@ -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
+ ]