pump-openai-radar 0.0.1__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 (46) hide show
  1. pump_openai_radar-0.0.1/.github/workflows/format.yml +21 -0
  2. pump_openai_radar-0.0.1/.github/workflows/test.yml +33 -0
  3. pump_openai_radar-0.0.1/.gitignore +9 -0
  4. pump_openai_radar-0.0.1/PKG-INFO +340 -0
  5. pump_openai_radar-0.0.1/README.md +299 -0
  6. pump_openai_radar-0.0.1/openai-radar +14 -0
  7. pump_openai_radar-0.0.1/pyproject.toml +77 -0
  8. pump_openai_radar-0.0.1/src/openai_radar/__init__.py +23 -0
  9. pump_openai_radar-0.0.1/src/openai_radar/agents/__init__.py +78 -0
  10. pump_openai_radar-0.0.1/src/openai_radar/agents/tools.py +251 -0
  11. pump_openai_radar-0.0.1/src/openai_radar/cli.py +431 -0
  12. pump_openai_radar-0.0.1/src/openai_radar/client.py +147 -0
  13. pump_openai_radar-0.0.1/src/openai_radar/exporters/__init__.py +6 -0
  14. pump_openai_radar-0.0.1/src/openai_radar/exporters/csv.py +79 -0
  15. pump_openai_radar-0.0.1/src/openai_radar/exporters/drawio.py +245 -0
  16. pump_openai_radar-0.0.1/src/openai_radar/findings.py +238 -0
  17. pump_openai_radar-0.0.1/src/openai_radar/models/__init__.py +23 -0
  18. pump_openai_radar-0.0.1/src/openai_radar/models/base.py +240 -0
  19. pump_openai_radar-0.0.1/src/openai_radar/pump_login.py +390 -0
  20. pump_openai_radar-0.0.1/src/openai_radar/runner.py +186 -0
  21. pump_openai_radar-0.0.1/src/openai_radar/scanners/__init__.py +28 -0
  22. pump_openai_radar-0.0.1/src/openai_radar/scanners/assistants.py +137 -0
  23. pump_openai_radar-0.0.1/src/openai_radar/scanners/base.py +84 -0
  24. pump_openai_radar-0.0.1/src/openai_radar/scanners/batch_jobs.py +18 -0
  25. pump_openai_radar-0.0.1/src/openai_radar/scanners/fine_tunes.py +18 -0
  26. pump_openai_radar-0.0.1/src/openai_radar/scanners/report.py +174 -0
  27. pump_openai_radar-0.0.1/src/openai_radar/scanners/usage.py +146 -0
  28. pump_openai_radar-0.0.1/src/openai_radar/scanners/vector_stores.py +19 -0
  29. pump_openai_radar-0.0.1/src/openai_radar/upload.py +124 -0
  30. pump_openai_radar-0.0.1/tests/__init__.py +0 -0
  31. pump_openai_radar-0.0.1/tests/conftest.py +18 -0
  32. pump_openai_radar-0.0.1/tests/fakes.py +64 -0
  33. pump_openai_radar-0.0.1/tests/test_agents.py +180 -0
  34. pump_openai_radar-0.0.1/tests/test_cli.py +613 -0
  35. pump_openai_radar-0.0.1/tests/test_client.py +144 -0
  36. pump_openai_radar-0.0.1/tests/test_exporters.py +162 -0
  37. pump_openai_radar-0.0.1/tests/test_findings.py +214 -0
  38. pump_openai_radar-0.0.1/tests/test_models.py +169 -0
  39. pump_openai_radar-0.0.1/tests/test_package.py +28 -0
  40. pump_openai_radar-0.0.1/tests/test_pump_login.py +171 -0
  41. pump_openai_radar-0.0.1/tests/test_relationships.py +54 -0
  42. pump_openai_radar-0.0.1/tests/test_report.py +210 -0
  43. pump_openai_radar-0.0.1/tests/test_runner.py +168 -0
  44. pump_openai_radar-0.0.1/tests/test_scanners.py +283 -0
  45. pump_openai_radar-0.0.1/tests/test_upload.py +105 -0
  46. pump_openai_radar-0.0.1/uv.lock +1870 -0
@@ -0,0 +1,21 @@
1
+ name: Format
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ ruff-format:
12
+ name: ruff format
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Check formatting
19
+ uses: astral-sh/ruff-action@v4.1.0
20
+ with:
21
+ args: format --check --diff
@@ -0,0 +1,33 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ pytest:
12
+ name: pytest (${{ matrix.python-version }})
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - name: Install
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ python -m pip install -e ".[dev]"
31
+
32
+ - name: Run tests
33
+ run: python -m pytest
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ *.egg-info/
5
+ .venv/
6
+ dist/
7
+ build/
8
+ report.csv
9
+ usage.csv
@@ -0,0 +1,340 @@
1
+ Metadata-Version: 2.5
2
+ Name: pump-openai-radar
3
+ Version: 0.0.1
4
+ Summary: OpenAI infrastructure FinOps SDK — scan, analyze, and export your OpenAI org resources
5
+ Project-URL: Homepage, https://github.com/gomorsmi/openai-radar
6
+ Project-URL: Repository, https://github.com/gomorsmi/openai-radar
7
+ Project-URL: Bug Tracker, https://github.com/gomorsmi/openai-radar/issues
8
+ Project-URL: PyPI, https://pypi.org/project/openai-radar/
9
+ Author: pump.co, Mor Michaeli
10
+ License: MIT
11
+ Keywords: cloud-cost,finops,hyperscaler,llm,openai,radar
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: System :: Monitoring
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: anyio>=4.0.0
26
+ Requires-Dist: httpx>=0.27.0
27
+ Requires-Dist: lxml>=5.0.0
28
+ Requires-Dist: openai>=1.60.0
29
+ Requires-Dist: pydantic>=2.0.0
30
+ Requires-Dist: rich>=13.0.0
31
+ Requires-Dist: typer>=0.12.0
32
+ Provides-Extra: agents
33
+ Requires-Dist: openai-agents>=0.22.0; extra == 'agents'
34
+ Provides-Extra: all
35
+ Requires-Dist: openai-radar[agents]; extra == 'all'
36
+ Provides-Extra: csv
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=8.0; extra == 'dev'
39
+ Provides-Extra: drawio
40
+ Description-Content-Type: text/markdown
41
+
42
+ # openai-radar
43
+
44
+ **OpenAI infrastructure FinOps SDK** — part of the [Hyperscaler Radar](https://github.com/gomorsmi) suite.
45
+
46
+ [![PyPI](https://img.shields.io/pypi/v/openai-radar)](https://pypi.org/project/openai-radar/)
47
+ [![Python](https://img.shields.io/pypi/pyversions/openai-radar)](https://pypi.org/project/openai-radar/)
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
49
+
50
+ Scan your OpenAI org for assistants, vector stores, fine-tunes, batch jobs, and token usage.
51
+ Detect external service relationships in assistant instructions, flag cost anomalies via a findings engine,
52
+ and export inventory to CSV or a draw.io architecture diagram — all through a clean Python SDK
53
+ that mirrors the `openai-agents` Runner API.
54
+
55
+ ---
56
+
57
+ ## Install
58
+
59
+ ```bash
60
+ pip install openai-radar # SDK + CLI + CSV and draw.io export
61
+ pip install openai-radar[agents] # + openai-agents integration
62
+ pip install openai-radar[all] # everything
63
+ ```
64
+
65
+ Requires Python 3.10+.
66
+
67
+ CSV and draw.io export are part of the base install — no extras needed, same as
68
+ the rest of the Radar suite. The `[csv]` and `[drawio]` extras still resolve (as
69
+ no-ops) so older pins keep working.
70
+
71
+ ---
72
+
73
+ ## Quick-start
74
+
75
+ ```python
76
+ from openai_radar import RadarClient, Runner
77
+
78
+ # Reads OPENAI_API_KEY from environment
79
+ client = RadarClient()
80
+
81
+ result = Runner.run_sync(client)
82
+
83
+ print(result.summary())
84
+ result.export_csv("./out/")
85
+ result.export_drawio("./out/openai_arch.drawio")
86
+ ```
87
+
88
+ ### With admin key (org-wide visibility)
89
+
90
+ ```python
91
+ client = RadarClient(
92
+ api_key="sk-proj-...",
93
+ admin_key="sk-admin-...", # unlocks cross-project usage data
94
+ )
95
+ result = Runner.run_sync(client, config)
96
+ ```
97
+
98
+ ### Scoped to a project
99
+
100
+ ```python
101
+ from openai_radar import RadarClient, Runner, RunConfig
102
+
103
+ client = RadarClient()
104
+ config = RunConfig(project_id="proj_xxx", usage_lookback_days=7)
105
+ result = Runner.run_sync(client, config)
106
+ ```
107
+
108
+ ---
109
+
110
+ ## CLI
111
+
112
+ ```bash
113
+ # Full scan — findings table to stdout
114
+ openai-radar run
115
+
116
+ # CSVs + draw.io diagram
117
+ openai-radar run --csv-dir ./out --drawio-file arch.drawio
118
+
119
+ # JSON instead of a table
120
+ openai-radar run --output json --out-file scan.json
121
+
122
+ # Admin key for org-wide usage data
123
+ openai-radar run --admin-key sk-admin-... --csv-dir ./out
124
+
125
+ # Findings only
126
+ openai-radar findings
127
+
128
+ # Scope to a project, 7-day lookback
129
+ openai-radar run --project proj_xxx --lookback 7
130
+
131
+ # Log in to Pump, then push the org cost report (admin key required)
132
+ openai-radar login
133
+ openai-radar run --admin-key sk-admin-... --upload
134
+
135
+ # Check or forget the stored Pump token
136
+ openai-radar status
137
+ openai-radar logout
138
+
139
+ # Print the version
140
+ openai-radar version
141
+ ```
142
+
143
+ Flags follow the Radar suite convention: `--output/-o` selects `table` or `json`,
144
+ `--out-file` writes the JSON payload, `--csv-dir` writes per-resource CSVs.
145
+ `--upload` writes `report.csv` (costs) and `usage.csv` (token usage) and pushes
146
+ them with the token from `openai-radar login`. Costs upload as role `billing`;
147
+ usage uploads as role `inventory`. `--upload-token` does the same with a one-shot
148
+ token and overrides the stored login. `--report-file` chooses the cost CSV path;
149
+ with `--csv-dir` and no `--report-file` it is `{csv-dir}/report.csv`. `usage.csv`
150
+ is written next to it.
151
+
152
+ ---
153
+
154
+ ## Pump onboarding
155
+
156
+ The token is exchanged for a presigned S3 URL, and only the cost and usage CSVs leave the machine.
157
+
158
+ 1. Log in. This runs the browser OAuth flow and stores an upload token locally:
159
+
160
+ ```bash
161
+ openai-radar login
162
+ ```
163
+
164
+ 2. Scan and upload with an OpenAI admin key:
165
+
166
+ ```bash
167
+ openai-radar run --admin-key sk-admin-... --upload
168
+ ```
169
+
170
+ This scans the org, pulls daily costs from `/organization/costs`, writes
171
+ `report.csv` and `usage.csv`, and uploads them with the stored token. Costs
172
+ are role `billing`; usage is role `inventory`. `--csv-dir` and `--drawio-file`
173
+ still work on the same command. Pass `--report-file` to choose where the cost
174
+ CSV is written.
175
+ 3. Pump detects the upload and runs its analysis.
176
+
177
+ `openai-radar status` shows whether a token is stored (not the token itself).
178
+ `openai-radar logout` deletes it. A one-shot token still works without logging in:
179
+
180
+ ```bash
181
+ openai-radar run --admin-key sk-admin-... --upload-token <TOKEN>
182
+ ```
183
+
184
+ `report.csv` columns:
185
+
186
+ | Column | Meaning |
187
+ | --------- | ----------------------------------------- |
188
+ | Date | UTC day (`YYYY-MM-DD`) |
189
+ | ProjectID | OpenAI project, or `-` |
190
+ | LineItem | Cost line item (model and token category) |
191
+ | Amount | Non-zero cost, 6 decimal places |
192
+ | Currency | e.g. `USD` |
193
+
194
+ Zero-cost buckets are omitted. The token carries no company id — Pump binds the
195
+ company and the S3 key server-side. Login stores the API origin it used, and
196
+ `run --upload` sends the report there. Override it with `--api-base` or
197
+ `PUMP_API_BASE` (default `https://api.pump.co`):
198
+
199
+ ```bash
200
+ openai-radar run --admin-key sk-admin-... --upload-token <TOKEN> --api-base http://localhost:8001
201
+ # or
202
+ PUMP_API_BASE=http://localhost:8001 openai-radar run --admin-key sk-admin-... --upload-token <TOKEN>
203
+ ```
204
+
205
+ `openai_radar/upload.py` posts `{api_base}/api/v1/estimate/radar/urls` once per
206
+ file, with `{"token", "role", "provider": "openai"}`. Costs use role `billing`
207
+ and usage uses role `inventory`. Each CSV is then `PUT` as `Content-Type: text/csv`
208
+ (the presigned URL signs that content type).
209
+
210
+ ---
211
+
212
+ ## openai-agents integration
213
+
214
+ ```bash
215
+ pip install openai-radar[agents]
216
+ ```
217
+
218
+ ```python
219
+ from openai_radar.agents import build_radar_agent
220
+ from agents import Runner
221
+
222
+ agent = build_radar_agent()
223
+ result = Runner.run_sync(agent, "Scan my org and flag any cost anomalies")
224
+ print(result.final_output)
225
+ ```
226
+
227
+ Or compose individual Radar tools into your own agent:
228
+
229
+ ```python
230
+ from agents import Agent
231
+ from openai_radar.agents.tools import scan_assistants, run_findings, export_drawio
232
+
233
+ agent = Agent(
234
+ name="My FinOps Agent",
235
+ instructions="...",
236
+ tools=[scan_assistants, run_findings, export_drawio],
237
+ )
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Findings engine
243
+
244
+ | Rule ID | Severity | Condition |
245
+ | --------- | -------- | ---------------------------------------------- |
246
+ | ASST_001 | LOW | Assistant has zero tools |
247
+ | ASST_002 | INFO | Code Interpreter enabled (file storage costs) |
248
+ | VS_001 | MEDIUM | Vector store > 5 GB |
249
+ | VS_002 | HIGH | Vector store expires within 7 days |
250
+ | FT_001 | MEDIUM | Fine-tune job in `failed` state |
251
+ | BATCH_001 | HIGH | Batch job failure rate > 10% |
252
+ | USAGE_001 | MEDIUM | Model consumes > 10M tokens in lookback window |
253
+
254
+ ---
255
+
256
+ ## Service relationship detection
257
+
258
+ `AssistantScanner` inspects each assistant's `instructions` field for external service signals
259
+ and emits `ServiceRelationship` objects (visualized as edges in the draw.io diagram):
260
+
261
+ | Kind | Signals |
262
+ | -------- | ----------------------------------------------- |
263
+ | AWS | `aws`, `s3`, `ec2`, `lambda`, `dynamodb`, `sqs` |
264
+ | GCP | `gcp`, `bigquery`, `gcs`, `google cloud` |
265
+ | AZURE | `azure`, `blob.core.windows`, `cosmosdb` |
266
+ | DATABASE | `postgres`, `mysql`, `mongo`, `redis`, `neon` |
267
+ | SLACK | `slack` |
268
+ | EMAIL | `sendgrid`, `mailgun`, `smtp` |
269
+ | WEBHOOK | `webhook`, `http://` |
270
+
271
+ ---
272
+
273
+ ## SDK structure
274
+
275
+ ```
276
+ src/openai_radar/
277
+ ├── client.py # RadarClient (auth, project vs admin key)
278
+ ├── runner.py # Runner, RunConfig, RunResult
279
+ ├── findings.py # FindingEngine, Finding, Severity
280
+ ├── models/base.py # Pydantic v2 models for all resource types
281
+ ├── scanners/ # One scanner per resource type, plus the cost report
282
+ ├── exporters/ # CSV + draw.io exporters
283
+ ├── pump_login.py # `login` / `logout` / `status` (OAuth + PKCE)
284
+ ├── upload.py # Pump presigned-URL upload (billing + inventory)
285
+ ├── agents/ # openai-agents tools + build_radar_agent()
286
+ └── cli.py # openai-radar CLI
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Development
292
+
293
+ Python 3.10 or newer. From a checkout:
294
+
295
+ ```bash
296
+ python3 -m venv .venv
297
+ source .venv/bin/activate
298
+ pip install -e ".[all]"
299
+ ```
300
+
301
+ With [uv](https://docs.astral.sh/uv/):
302
+
303
+ ```bash
304
+ uv venv
305
+ source .venv/bin/activate
306
+ uv pip install -e ".[all]"
307
+ ```
308
+
309
+ The package lives in `src/openai_radar`, including `cli.py` and `pump_login.py`.
310
+
311
+ From this checkout:
312
+
313
+ ```bash
314
+ ./openai-radar --help
315
+ ./openai-radar login
316
+ ./openai-radar status
317
+ ./openai-radar logout
318
+ ./openai-radar run --upload
319
+ ./openai-radar version
320
+ ```
321
+
322
+ `login` opens a browser for the Pump OAuth flow and writes the token to
323
+ `$XDG_CONFIG_HOME/openai-radar/credentials.json`, or
324
+ `~/.config/openai-radar/credentials.json` when `XDG_CONFIG_HOME` is unset.
325
+ `OPENAI_RADAR_CONFIG_DIR` overrides that directory. `PUMP_API_BASE` and
326
+ `PUMP_APP_BASE` override the Pump origins, as do `--api-base` and `--app-base`.
327
+ `run --upload` sends `report.csv` with the stored token. Scans still read
328
+ `OPENAI_API_KEY` and, for org-wide usage, `OPENAI_ADMIN_KEY`.
329
+
330
+ Login tests talk to a local fake Pump:
331
+
332
+ ```bash
333
+ python -m unittest tests.test_pump_login
334
+ ```
335
+
336
+ ---
337
+
338
+ ## Part of the Hyperscaler Radar suite
339
+
340
+ `aws-radar` · `gcp-radar` · `azure-radar` · `oci-radar` · `openai-radar` ·`claude-radar` · `gemini-radar` · `datadog-radar`
@@ -0,0 +1,299 @@
1
+ # openai-radar
2
+
3
+ **OpenAI infrastructure FinOps SDK** — part of the [Hyperscaler Radar](https://github.com/gomorsmi) suite.
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/openai-radar)](https://pypi.org/project/openai-radar/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/openai-radar)](https://pypi.org/project/openai-radar/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+
9
+ Scan your OpenAI org for assistants, vector stores, fine-tunes, batch jobs, and token usage.
10
+ Detect external service relationships in assistant instructions, flag cost anomalies via a findings engine,
11
+ and export inventory to CSV or a draw.io architecture diagram — all through a clean Python SDK
12
+ that mirrors the `openai-agents` Runner API.
13
+
14
+ ---
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install openai-radar # SDK + CLI + CSV and draw.io export
20
+ pip install openai-radar[agents] # + openai-agents integration
21
+ pip install openai-radar[all] # everything
22
+ ```
23
+
24
+ Requires Python 3.10+.
25
+
26
+ CSV and draw.io export are part of the base install — no extras needed, same as
27
+ the rest of the Radar suite. The `[csv]` and `[drawio]` extras still resolve (as
28
+ no-ops) so older pins keep working.
29
+
30
+ ---
31
+
32
+ ## Quick-start
33
+
34
+ ```python
35
+ from openai_radar import RadarClient, Runner
36
+
37
+ # Reads OPENAI_API_KEY from environment
38
+ client = RadarClient()
39
+
40
+ result = Runner.run_sync(client)
41
+
42
+ print(result.summary())
43
+ result.export_csv("./out/")
44
+ result.export_drawio("./out/openai_arch.drawio")
45
+ ```
46
+
47
+ ### With admin key (org-wide visibility)
48
+
49
+ ```python
50
+ client = RadarClient(
51
+ api_key="sk-proj-...",
52
+ admin_key="sk-admin-...", # unlocks cross-project usage data
53
+ )
54
+ result = Runner.run_sync(client, config)
55
+ ```
56
+
57
+ ### Scoped to a project
58
+
59
+ ```python
60
+ from openai_radar import RadarClient, Runner, RunConfig
61
+
62
+ client = RadarClient()
63
+ config = RunConfig(project_id="proj_xxx", usage_lookback_days=7)
64
+ result = Runner.run_sync(client, config)
65
+ ```
66
+
67
+ ---
68
+
69
+ ## CLI
70
+
71
+ ```bash
72
+ # Full scan — findings table to stdout
73
+ openai-radar run
74
+
75
+ # CSVs + draw.io diagram
76
+ openai-radar run --csv-dir ./out --drawio-file arch.drawio
77
+
78
+ # JSON instead of a table
79
+ openai-radar run --output json --out-file scan.json
80
+
81
+ # Admin key for org-wide usage data
82
+ openai-radar run --admin-key sk-admin-... --csv-dir ./out
83
+
84
+ # Findings only
85
+ openai-radar findings
86
+
87
+ # Scope to a project, 7-day lookback
88
+ openai-radar run --project proj_xxx --lookback 7
89
+
90
+ # Log in to Pump, then push the org cost report (admin key required)
91
+ openai-radar login
92
+ openai-radar run --admin-key sk-admin-... --upload
93
+
94
+ # Check or forget the stored Pump token
95
+ openai-radar status
96
+ openai-radar logout
97
+
98
+ # Print the version
99
+ openai-radar version
100
+ ```
101
+
102
+ Flags follow the Radar suite convention: `--output/-o` selects `table` or `json`,
103
+ `--out-file` writes the JSON payload, `--csv-dir` writes per-resource CSVs.
104
+ `--upload` writes `report.csv` (costs) and `usage.csv` (token usage) and pushes
105
+ them with the token from `openai-radar login`. Costs upload as role `billing`;
106
+ usage uploads as role `inventory`. `--upload-token` does the same with a one-shot
107
+ token and overrides the stored login. `--report-file` chooses the cost CSV path;
108
+ with `--csv-dir` and no `--report-file` it is `{csv-dir}/report.csv`. `usage.csv`
109
+ is written next to it.
110
+
111
+ ---
112
+
113
+ ## Pump onboarding
114
+
115
+ The token is exchanged for a presigned S3 URL, and only the cost and usage CSVs leave the machine.
116
+
117
+ 1. Log in. This runs the browser OAuth flow and stores an upload token locally:
118
+
119
+ ```bash
120
+ openai-radar login
121
+ ```
122
+
123
+ 2. Scan and upload with an OpenAI admin key:
124
+
125
+ ```bash
126
+ openai-radar run --admin-key sk-admin-... --upload
127
+ ```
128
+
129
+ This scans the org, pulls daily costs from `/organization/costs`, writes
130
+ `report.csv` and `usage.csv`, and uploads them with the stored token. Costs
131
+ are role `billing`; usage is role `inventory`. `--csv-dir` and `--drawio-file`
132
+ still work on the same command. Pass `--report-file` to choose where the cost
133
+ CSV is written.
134
+ 3. Pump detects the upload and runs its analysis.
135
+
136
+ `openai-radar status` shows whether a token is stored (not the token itself).
137
+ `openai-radar logout` deletes it. A one-shot token still works without logging in:
138
+
139
+ ```bash
140
+ openai-radar run --admin-key sk-admin-... --upload-token <TOKEN>
141
+ ```
142
+
143
+ `report.csv` columns:
144
+
145
+ | Column | Meaning |
146
+ | --------- | ----------------------------------------- |
147
+ | Date | UTC day (`YYYY-MM-DD`) |
148
+ | ProjectID | OpenAI project, or `-` |
149
+ | LineItem | Cost line item (model and token category) |
150
+ | Amount | Non-zero cost, 6 decimal places |
151
+ | Currency | e.g. `USD` |
152
+
153
+ Zero-cost buckets are omitted. The token carries no company id — Pump binds the
154
+ company and the S3 key server-side. Login stores the API origin it used, and
155
+ `run --upload` sends the report there. Override it with `--api-base` or
156
+ `PUMP_API_BASE` (default `https://api.pump.co`):
157
+
158
+ ```bash
159
+ openai-radar run --admin-key sk-admin-... --upload-token <TOKEN> --api-base http://localhost:8001
160
+ # or
161
+ PUMP_API_BASE=http://localhost:8001 openai-radar run --admin-key sk-admin-... --upload-token <TOKEN>
162
+ ```
163
+
164
+ `openai_radar/upload.py` posts `{api_base}/api/v1/estimate/radar/urls` once per
165
+ file, with `{"token", "role", "provider": "openai"}`. Costs use role `billing`
166
+ and usage uses role `inventory`. Each CSV is then `PUT` as `Content-Type: text/csv`
167
+ (the presigned URL signs that content type).
168
+
169
+ ---
170
+
171
+ ## openai-agents integration
172
+
173
+ ```bash
174
+ pip install openai-radar[agents]
175
+ ```
176
+
177
+ ```python
178
+ from openai_radar.agents import build_radar_agent
179
+ from agents import Runner
180
+
181
+ agent = build_radar_agent()
182
+ result = Runner.run_sync(agent, "Scan my org and flag any cost anomalies")
183
+ print(result.final_output)
184
+ ```
185
+
186
+ Or compose individual Radar tools into your own agent:
187
+
188
+ ```python
189
+ from agents import Agent
190
+ from openai_radar.agents.tools import scan_assistants, run_findings, export_drawio
191
+
192
+ agent = Agent(
193
+ name="My FinOps Agent",
194
+ instructions="...",
195
+ tools=[scan_assistants, run_findings, export_drawio],
196
+ )
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Findings engine
202
+
203
+ | Rule ID | Severity | Condition |
204
+ | --------- | -------- | ---------------------------------------------- |
205
+ | ASST_001 | LOW | Assistant has zero tools |
206
+ | ASST_002 | INFO | Code Interpreter enabled (file storage costs) |
207
+ | VS_001 | MEDIUM | Vector store > 5 GB |
208
+ | VS_002 | HIGH | Vector store expires within 7 days |
209
+ | FT_001 | MEDIUM | Fine-tune job in `failed` state |
210
+ | BATCH_001 | HIGH | Batch job failure rate > 10% |
211
+ | USAGE_001 | MEDIUM | Model consumes > 10M tokens in lookback window |
212
+
213
+ ---
214
+
215
+ ## Service relationship detection
216
+
217
+ `AssistantScanner` inspects each assistant's `instructions` field for external service signals
218
+ and emits `ServiceRelationship` objects (visualized as edges in the draw.io diagram):
219
+
220
+ | Kind | Signals |
221
+ | -------- | ----------------------------------------------- |
222
+ | AWS | `aws`, `s3`, `ec2`, `lambda`, `dynamodb`, `sqs` |
223
+ | GCP | `gcp`, `bigquery`, `gcs`, `google cloud` |
224
+ | AZURE | `azure`, `blob.core.windows`, `cosmosdb` |
225
+ | DATABASE | `postgres`, `mysql`, `mongo`, `redis`, `neon` |
226
+ | SLACK | `slack` |
227
+ | EMAIL | `sendgrid`, `mailgun`, `smtp` |
228
+ | WEBHOOK | `webhook`, `http://` |
229
+
230
+ ---
231
+
232
+ ## SDK structure
233
+
234
+ ```
235
+ src/openai_radar/
236
+ ├── client.py # RadarClient (auth, project vs admin key)
237
+ ├── runner.py # Runner, RunConfig, RunResult
238
+ ├── findings.py # FindingEngine, Finding, Severity
239
+ ├── models/base.py # Pydantic v2 models for all resource types
240
+ ├── scanners/ # One scanner per resource type, plus the cost report
241
+ ├── exporters/ # CSV + draw.io exporters
242
+ ├── pump_login.py # `login` / `logout` / `status` (OAuth + PKCE)
243
+ ├── upload.py # Pump presigned-URL upload (billing + inventory)
244
+ ├── agents/ # openai-agents tools + build_radar_agent()
245
+ └── cli.py # openai-radar CLI
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Development
251
+
252
+ Python 3.10 or newer. From a checkout:
253
+
254
+ ```bash
255
+ python3 -m venv .venv
256
+ source .venv/bin/activate
257
+ pip install -e ".[all]"
258
+ ```
259
+
260
+ With [uv](https://docs.astral.sh/uv/):
261
+
262
+ ```bash
263
+ uv venv
264
+ source .venv/bin/activate
265
+ uv pip install -e ".[all]"
266
+ ```
267
+
268
+ The package lives in `src/openai_radar`, including `cli.py` and `pump_login.py`.
269
+
270
+ From this checkout:
271
+
272
+ ```bash
273
+ ./openai-radar --help
274
+ ./openai-radar login
275
+ ./openai-radar status
276
+ ./openai-radar logout
277
+ ./openai-radar run --upload
278
+ ./openai-radar version
279
+ ```
280
+
281
+ `login` opens a browser for the Pump OAuth flow and writes the token to
282
+ `$XDG_CONFIG_HOME/openai-radar/credentials.json`, or
283
+ `~/.config/openai-radar/credentials.json` when `XDG_CONFIG_HOME` is unset.
284
+ `OPENAI_RADAR_CONFIG_DIR` overrides that directory. `PUMP_API_BASE` and
285
+ `PUMP_APP_BASE` override the Pump origins, as do `--api-base` and `--app-base`.
286
+ `run --upload` sends `report.csv` with the stored token. Scans still read
287
+ `OPENAI_API_KEY` and, for org-wide usage, `OPENAI_ADMIN_KEY`.
288
+
289
+ Login tests talk to a local fake Pump:
290
+
291
+ ```bash
292
+ python -m unittest tests.test_pump_login
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Part of the Hyperscaler Radar suite
298
+
299
+ `aws-radar` · `gcp-radar` · `azure-radar` · `oci-radar` · `openai-radar` ·`claude-radar` · `gemini-radar` · `datadog-radar`