oai-workbench-cli 0.1.0__py3-none-any.whl

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,369 @@
1
+ Metadata-Version: 2.4
2
+ Name: oai-workbench-cli
3
+ Version: 0.1.0
4
+ Summary: Developer-first CLI wrapper around the official OpenAI Python SDK.
5
+ Author: oai-cli contributors
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: cli,developer-tools,openai,rich,typer
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development
18
+ Requires-Python: <3.13,>=3.10
19
+ Requires-Dist: jsonschema>=4.22.0
20
+ Requires-Dist: openai>=1.0.0
21
+ Requires-Dist: platformdirs>=4.2.0
22
+ Requires-Dist: pydantic>=2.6.0
23
+ Requires-Dist: rich>=13.7.0
24
+ Requires-Dist: tomli>=2.0.1; python_version < '3.11'
25
+ Requires-Dist: typer>=0.12.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.5.0; extra == 'dev'
30
+ Requires-Dist: types-jsonschema>=4.24.0; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # oai-cli
34
+
35
+ Developer-first CLI for the OpenAI API, built on top of the official `openai-python` SDK.
36
+
37
+ `oai-cli` is designed for day-to-day engineering work: quick prompts, dry-run payload inspection, local request logs, model comparisons, JSON schema validation, file and batch helpers, prompt linting, redaction, and AI-assisted review workflows from pipes.
38
+
39
+ ## Highlights
40
+
41
+ - One-shot prompts with the Responses API: `oai-cli ask`
42
+ - Interactive streaming chat REPL: `oai-cli chat`
43
+ - Safe payload previews with `--dry-run`
44
+ - Local SQLite request history, replay, export, and secret scanning
45
+ - Local redaction for API keys, bearer tokens, JWTs, emails, phone numbers, cookies, private keys, and tokenized URLs
46
+ - Prompt linting without API calls
47
+ - Model comparison and eval runs across multiple models
48
+ - Strict JSON mode with JSON Schema validation
49
+ - Files and Batch API helpers
50
+ - Pipe-friendly code review and traceback explanation commands
51
+ - Profiles that store environment variable names, not plaintext API keys
52
+
53
+ ## Requirements
54
+
55
+ - Python `3.10`, `3.11`, or `3.12`
56
+ - An OpenAI API key exported through an environment variable
57
+
58
+ Python `3.13+` is intentionally not advertised yet because several upstream dependencies may be unstable there.
59
+
60
+ ## Installation
61
+
62
+ From PyPI:
63
+
64
+ ```bash
65
+ pip install oai-workbench-cli
66
+ ```
67
+
68
+ The installed console command is still `oai-cli`.
69
+
70
+ For isolated CLI installation:
71
+
72
+ ```bash
73
+ pipx install oai-workbench-cli
74
+ ```
75
+
76
+ From a local checkout:
77
+
78
+ ```bash
79
+ cd /path/to/oai-cli
80
+ pip install .
81
+ ```
82
+
83
+ For development:
84
+
85
+ ```bash
86
+ python -m venv .venv
87
+ source .venv/bin/activate
88
+ pip install -e ".[dev]"
89
+ ```
90
+
91
+ ## Quickstart
92
+
93
+ ```bash
94
+ export OPENAI_API_KEY=...
95
+
96
+ oai-cli config init
97
+ oai-cli doctor --check-api
98
+ oai-cli ask --no-stream "Reply with OK only."
99
+ oai-cli ask --dry-run "Explain this payload"
100
+ ```
101
+
102
+ Fish shell:
103
+
104
+ ```fish
105
+ set -gx OPENAI_API_KEY 'your_key_here'
106
+ oai-cli doctor --check-api
107
+ ```
108
+
109
+ Do not paste API keys into issues, chats, shell history, or committed files.
110
+
111
+ ## Global Options
112
+
113
+ Global options must appear before the command:
114
+
115
+ ```bash
116
+ oai-cli --timeout 30 ask "Reply briefly"
117
+ oai-cli --plain doctor
118
+ oai-cli --json doctor
119
+ oai-cli --debug ask "Show traceback for expected CLI errors"
120
+ ```
121
+
122
+ `--debug` can show tracebacks. Use it locally and avoid sharing output that may include sensitive data.
123
+
124
+ ## Configuration
125
+
126
+ Initialize config:
127
+
128
+ ```bash
129
+ oai-cli config init
130
+ oai-cli config show
131
+ ```
132
+
133
+ Default config paths:
134
+
135
+ - Linux/macOS: `~/.config/oai-cli/config.toml`
136
+ - Windows: platform app config directory via `platformdirs`
137
+
138
+ The CLI stores the name of the environment variable, not the API key value.
139
+
140
+ Profiles:
141
+
142
+ ```bash
143
+ oai-cli profile list
144
+ oai-cli profile create work --api-key-env OPENAI_WORK_API_KEY --model gpt-4.1
145
+ oai-cli profile use work
146
+ oai-cli profile show
147
+ oai-cli profile delete work
148
+ ```
149
+
150
+ Supported config fields:
151
+
152
+ - `default_model`
153
+ - `api_key_env`
154
+ - `base_url`
155
+ - `organization`
156
+ - `project`
157
+ - `log_enabled`
158
+ - `log_path`
159
+ - `redact_enabled`
160
+ - `active_profile`
161
+ - `profiles`
162
+
163
+ ## Ask
164
+
165
+ ```bash
166
+ oai-cli ask "Explain Python context managers"
167
+ cat prompt.txt | oai-cli ask --model gpt-4.1-mini
168
+ oai-cli ask --system "You are concise." --temperature 0.2 "Summarize this"
169
+ oai-cli ask --dry-run "Show request payload only"
170
+ oai-cli ask --cost-estimate "Estimate input cost"
171
+ ```
172
+
173
+ Key options:
174
+
175
+ - `--model`, `-m`
176
+ - `--system`
177
+ - `--temperature`
178
+ - `--max-output-tokens`
179
+ - `--stream / --no-stream`
180
+ - `--json`
181
+ - `--dry-run`
182
+ - `--cost-estimate`
183
+ - `--save / --no-save`
184
+ - `--redact / --no-redact`
185
+
186
+ ## Chat
187
+
188
+ ```bash
189
+ oai-cli chat --model gpt-4.1-mini
190
+ ```
191
+
192
+ REPL commands:
193
+
194
+ ```text
195
+ /help
196
+ /exit
197
+ /clear
198
+ /model <name>
199
+ /system <text>
200
+ /save <name>
201
+ /load <name>
202
+ /history
203
+ ```
204
+
205
+ For multiline input, enter `"""`, then type lines, then close with `"""`.
206
+
207
+ ## Redaction
208
+
209
+ ```bash
210
+ oai-cli redact "Bearer abcdef1234567890"
211
+ cat file.txt | oai-cli redact
212
+ cat file.txt | oai-cli redact --json
213
+ ```
214
+
215
+ Redaction is local. It detects common secrets including OpenAI keys, JWTs, bearer tokens, GitHub tokens, generic API keys, cookies, private key PEM blocks, emails, phone numbers, and query-string tokens.
216
+
217
+ ## Logs And Replay
218
+
219
+ ```bash
220
+ oai-cli logs list
221
+ oai-cli logs show <id>
222
+ oai-cli logs export --format jsonl
223
+ oai-cli logs scan-secrets
224
+ oai-cli replay <id> --dry-run
225
+ oai-cli replay <id> --model gpt-4.1 --yes
226
+ ```
227
+
228
+ Logs are stored in SQLite under the platform data directory unless `log_path` is configured.
229
+
230
+ ## Compare
231
+
232
+ ```bash
233
+ oai-cli compare "Summarize this release note" -m gpt-4.1-mini -m gpt-4.1
234
+ oai-cli compare --dry-run "Show payload only" -m gpt-4.1-mini -m gpt-4.1
235
+ oai-cli compare "Prompt" -m gpt-4.1-mini -m gpt-4.1 --diff
236
+ oai-cli compare "Prompt" -m gpt-4.1-mini -m gpt-4.1 --json-output result.json
237
+ ```
238
+
239
+ ## Prompt Lint
240
+
241
+ ```bash
242
+ oai-cli prompt-lint prompt.txt
243
+ cat prompt.txt | oai-cli prompt-lint --json
244
+ ```
245
+
246
+ The linter is local-only and reports:
247
+
248
+ - score from `0` to `100`
249
+ - warnings
250
+ - suggestions
251
+ - improved prompt draft
252
+
253
+ It checks for vague wording, missing output format, missing constraints, conflicting instructions, overly long prompts, hidden requirements, possible secrets, and JSON requests without schema.
254
+
255
+ ## JSON Mode
256
+
257
+ ```bash
258
+ oai-cli json "Extract invoice fields" --schema schema.json
259
+ oai-cli json "Extract invoice fields" --schema schema.json --retry 2 --output result.json
260
+ oai-cli json "Show payload" --schema schema.json --dry-run
261
+ ```
262
+
263
+ The command validates the schema locally, asks the model for JSON-only output, parses the response, and validates it against the schema before printing or writing it.
264
+
265
+ ## Files
266
+
267
+ ```bash
268
+ oai-cli files list
269
+ oai-cli files upload data.jsonl --purpose batch
270
+ oai-cli files retrieve file_...
271
+ oai-cli files delete file_...
272
+ oai-cli files download file_... --output out.bin
273
+ ```
274
+
275
+ ## Batch
276
+
277
+ ```bash
278
+ oai-cli batch create requests.jsonl --endpoint /v1/responses --completion-window 24h
279
+ oai-cli batch create requests.jsonl --dry-run
280
+ oai-cli batch status batch_...
281
+ oai-cli batch list
282
+ oai-cli batch cancel batch_...
283
+ oai-cli batch download batch_... --output result.jsonl
284
+ ```
285
+
286
+ `batch create` validates JSONL locally, shows row count, and previews the first row before upload.
287
+
288
+ ## Models
289
+
290
+ ```bash
291
+ oai-cli models list --refresh
292
+ oai-cli models cache
293
+ oai-cli models list
294
+ ```
295
+
296
+ `models list` uses the local cache unless `--refresh` is passed or no cache exists.
297
+
298
+ ## Eval
299
+
300
+ Create a JSONL file:
301
+
302
+ ```jsonl
303
+ {"id":"case-1","prompt":"Summarize this in one sentence: ..."}
304
+ {"id":"case-2","prompt":"Extract the company name: ..."}
305
+ ```
306
+
307
+ Run it:
308
+
309
+ ```bash
310
+ oai-cli eval cases.jsonl -m gpt-4.1-mini -m gpt-4.1 --output eval-results.json
311
+ oai-cli eval cases.jsonl -m gpt-4.1-mini --dry-run
312
+ ```
313
+
314
+ ## Code Review
315
+
316
+ ```bash
317
+ git diff | oai-cli code-review
318
+ git diff | oai-cli code-review --focus security --output json
319
+ git diff | oai-cli code-review --dry-run
320
+ ```
321
+
322
+ Redaction is enabled before sending the diff.
323
+
324
+ ## Explain Error
325
+
326
+ ```bash
327
+ pytest 2>&1 | oai-cli explain-error
328
+ pytest 2>&1 | oai-cli explain-error --dry-run
329
+ ```
330
+
331
+ Redaction is enabled before sending logs.
332
+
333
+ ## Doctor
334
+
335
+ ```bash
336
+ oai-cli doctor
337
+ oai-cli doctor --check-api
338
+ oai-cli doctor --json
339
+ ```
340
+
341
+ Checks Python version, package version, SDK version, active profile, API key environment variable, base URL, config/log directories, and optionally a minimal API request.
342
+
343
+ ## Exit Codes
344
+
345
+ - `0`: success
346
+ - `1`: general error
347
+ - `2`: config error
348
+ - `3`: API error
349
+ - `4`: validation error
350
+
351
+ ## Security Notes
352
+
353
+ - API keys are read from environment variables.
354
+ - API key values are not printed in dry-run output.
355
+ - API keys are not stored in config by default.
356
+ - Redaction is enabled by default for sensitive workflows.
357
+ - If a key is pasted into a chat, issue, PR, terminal transcript, or committed file, revoke it and create a new one.
358
+
359
+ ## Development
360
+
361
+ ```bash
362
+ python -m venv .venv
363
+ source .venv/bin/activate
364
+ pip install -e ".[dev]"
365
+
366
+ ruff check openai_py_cli tests
367
+ pytest
368
+ mypy openai_py_cli
369
+ ```
@@ -0,0 +1,35 @@
1
+ openai_py_cli/__init__.py,sha256=TbSN46sra_u4sSlaiXNqi6Iy-wcy5V0xQtYb8I22m5Y,93
2
+ openai_py_cli/cli.py,sha256=nBY6L_s6gGskcnheTpPb-iYRFOFZtPxc7P82mcH7LcY,2094
3
+ openai_py_cli/client.py,sha256=0vP1AAA4bjXLBBz-gwuMeaM8J-2BWZWC8qLOIvLyIU0,1330
4
+ openai_py_cli/config.py,sha256=HkR7lO-_9C2Lg1AJPXWDrqjetCZIR1KqJeTfkAFTjAA,4591
5
+ openai_py_cli/dry_run.py,sha256=2VUPalDUCh2xLxJHGbBB4zlOcqvqRCvNXieZCCCtCmk,648
6
+ openai_py_cli/errors.py,sha256=YFba3GJtvuCrALktuZWniR1Lx8LdLP0h70YpP3KhS3s,842
7
+ openai_py_cli/logging_store.py,sha256=-rJ83N2XoJmyKlH32t_GZwCMhdEqwGN-RNEUkUR0rQY,5387
8
+ openai_py_cli/output.py,sha256=n252oYDP7J8YjOqPF_x2qZTl1zrXAABaOoJJMcGHlLM,895
9
+ openai_py_cli/pricing.py,sha256=-Jjwk0-uQMb06ZmfXkGGdZ97EIXP5aMxE67meU-qQas,949
10
+ openai_py_cli/redaction.py,sha256=LNbGsPESipD_kuQQ1nBiVn6nZOBgrgyaFoaoWurA_f4,2176
11
+ openai_py_cli/runtime.py,sha256=5gmNHmZOKPGwGkj-iPYArYW-TJ9ejjnz8m2fqcWz_ns,286
12
+ openai_py_cli/schema_validation.py,sha256=5x_2k4zdPRJYJfJjiwc8sBCDq_2zRlMUddGarROy3Bo,2328
13
+ openai_py_cli/commands/__init__.py,sha256=gQ6tnU0Rvm0-ESWFUBU-KDl5dpNOpUTG509hXOQQjwY,27
14
+ openai_py_cli/commands/ask.py,sha256=UAh0VloX5s7vJ4dKg6uzUxb6cvhP41-x7Cefi_BEtRU,6776
15
+ openai_py_cli/commands/batch.py,sha256=zi6RT7rtfIyZFdoq8K61rETVAaoXuarLWT7EzuzNFlo,4034
16
+ openai_py_cli/commands/chat.py,sha256=moIus0rdhEhBO0z84BBVnBgp35lGcZtlh38hMKmOKwg,4099
17
+ openai_py_cli/commands/code_review.py,sha256=WRTMtiVOnQoI0HoLNtzHBvr6_UH1AINxAJ8sM9dnRoU,2609
18
+ openai_py_cli/commands/compare.py,sha256=rFbhzMkKAs8ACwGMxL5pDmJcSH8Dlps0q3FIIwthwX8,6229
19
+ openai_py_cli/commands/config_cmd.py,sha256=EWe0EXgoodIM121j1sSizL3pVMvBvrkYeAI4Ph0ryHA,1672
20
+ openai_py_cli/commands/doctor.py,sha256=sBzqgLmMMZi2hkm5YEs34NLW7b0En7_2wwXRQzm4mP8,3015
21
+ openai_py_cli/commands/eval_cmd.py,sha256=Ss2g42Wk-x0aQNUA6tw9uEkSfKit1y9fdvfb3iAotNk,4800
22
+ openai_py_cli/commands/explain_error.py,sha256=nmk9ldSidfKWOse7nvqy2M2CP2aGq-fa3FbmiLVKeDE,1946
23
+ openai_py_cli/commands/files.py,sha256=ORkHwz4TNxbmyqzeSAbuUFx-bjVz-ln1hOKdu_T89CU,3259
24
+ openai_py_cli/commands/json_mode.py,sha256=e7Kjj7aX9gSi8MPm_BaAnx03nsXqPyXD-XlOvrR1q_Q,3580
25
+ openai_py_cli/commands/logs.py,sha256=dAStj_Lqi2pie6ktDXp5X2i3mqbDiNjEyUFWibXbtdk,3738
26
+ openai_py_cli/commands/models.py,sha256=wBcLZfxB017C-WC3rcJe-TMclRPYRetbqM3ou60VXn4,2279
27
+ openai_py_cli/commands/profile.py,sha256=Xe--7LhSkRfBoGSq30-EPrVwauwqUSwc4WxB38ZlH-A,2789
28
+ openai_py_cli/commands/prompt_lint.py,sha256=L98l34q6W-8TPpHnyX0fcm-w1pRh0gIrpSc7aBFyqec,6445
29
+ openai_py_cli/commands/redact.py,sha256=Wo6ZPJM-hgrcjJfsp64Vgz3caThEqGqB8-BUGr8c37Y,705
30
+ openai_py_cli/commands/replay.py,sha256=iUSDq40VVR-gaQUppTC_pVllYQv0S2gFUehcD2zEvYs,3677
31
+ oai_workbench_cli-0.1.0.dist-info/METADATA,sha256=fprIhSuebjxFW_5i48IgtBYnuRaIQFUf82GGRHrYyfY,9009
32
+ oai_workbench_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
33
+ oai_workbench_cli-0.1.0.dist-info/entry_points.txt,sha256=tYWXFjcXFw6Zi2kBXKKSyC_qm6WMGk_eUvNJznsXU1c,51
34
+ oai_workbench_cli-0.1.0.dist-info/licenses/LICENSE,sha256=icMQscwmqK97s-GnHAPKemnGXteId1Bc4fa31KM6hvA,1079
35
+ oai_workbench_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ oai-cli = openai_py_cli.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 openai-py contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ """Developer-first CLI wrapper for the official OpenAI Python SDK."""
2
+
3
+ __version__ = "0.1.0"
openai_py_cli/cli.py ADDED
@@ -0,0 +1,82 @@
1
+ """CLI entrypoint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ import typer
8
+
9
+ from .commands import (
10
+ ask,
11
+ batch,
12
+ chat,
13
+ code_review,
14
+ compare,
15
+ config_cmd,
16
+ doctor,
17
+ eval_cmd,
18
+ explain_error,
19
+ files,
20
+ json_mode,
21
+ logs,
22
+ models,
23
+ profile,
24
+ prompt_lint,
25
+ redact,
26
+ replay,
27
+ )
28
+ from .errors import OpenAIPyCliError
29
+ from .output import print_error
30
+ from .runtime import options
31
+
32
+ app = typer.Typer(
33
+ name="oai-cli",
34
+ help="Developer-first CLI wrapper around the official OpenAI Python SDK.",
35
+ no_args_is_help=True,
36
+ rich_markup_mode="rich",
37
+ )
38
+
39
+
40
+ @app.callback()
41
+ def global_options(
42
+ debug: bool = typer.Option(False, "--debug", help="Show traceback for expected errors."),
43
+ timeout: float | None = typer.Option(None, "--timeout", help="OpenAI request timeout seconds."),
44
+ plain: bool = typer.Option(False, "--plain", help="Prefer plain text for machine parsing."),
45
+ json_output: bool = typer.Option(False, "--json", help="Prefer JSON for global errors."),
46
+ ) -> None:
47
+ options.debug = debug
48
+ options.timeout = timeout
49
+ options.plain = plain
50
+ options.json_output = json_output
51
+
52
+ app.command("ask")(ask.ask)
53
+ app.command("redact")(redact.redact)
54
+ app.command("doctor")(doctor.doctor)
55
+ app.command("replay")(replay.replay)
56
+ app.command("compare")(compare.compare)
57
+ app.command("prompt-lint")(prompt_lint.prompt_lint)
58
+ app.command("chat")(chat.chat)
59
+ app.command("json")(json_mode.json_command)
60
+ app.command("code-review")(code_review.code_review)
61
+ app.command("explain-error")(explain_error.explain_error)
62
+ app.command("eval")(eval_cmd.eval_command)
63
+ app.add_typer(files.app, name="files")
64
+ app.add_typer(batch.app, name="batch")
65
+ app.add_typer(logs.app, name="logs")
66
+ app.add_typer(profile.app, name="profile")
67
+ app.add_typer(config_cmd.app, name="config")
68
+ app.add_typer(models.app, name="models")
69
+
70
+
71
+ def main() -> None:
72
+ try:
73
+ app()
74
+ except OpenAIPyCliError as exc:
75
+ if options.debug:
76
+ raise
77
+ print_error(str(exc))
78
+ sys.exit(exc.exit_code)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()
@@ -0,0 +1,50 @@
1
+ """OpenAI SDK client creation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+ from .config import AppConfig, Profile, load_config
10
+ from .errors import MissingApiKeyError
11
+ from .runtime import options
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ClientContext:
16
+ config: AppConfig
17
+ profile_name: str
18
+ profile: Profile
19
+ api_key_env: str
20
+ api_key_present: bool
21
+
22
+
23
+ def get_client(config: AppConfig | None = None) -> Any:
24
+ context = get_client_context(config)
25
+ api_key = os.environ.get(context.api_key_env)
26
+ if not api_key:
27
+ raise MissingApiKeyError(
28
+ f"{context.api_key_env} is not set. Run: export {context.api_key_env}=..."
29
+ )
30
+ from openai import OpenAI
31
+
32
+ return OpenAI(
33
+ api_key=api_key,
34
+ base_url=context.profile.base_url,
35
+ organization=context.profile.organization,
36
+ project=context.profile.project,
37
+ timeout=options.timeout,
38
+ )
39
+
40
+
41
+ def get_client_context(config: AppConfig | None = None) -> ClientContext:
42
+ cfg = config or load_config()
43
+ profile = cfg.active()
44
+ return ClientContext(
45
+ config=cfg,
46
+ profile_name=cfg.active_profile,
47
+ profile=profile,
48
+ api_key_env=profile.api_key_env,
49
+ api_key_present=bool(os.environ.get(profile.api_key_env)),
50
+ )
@@ -0,0 +1 @@
1
+ """CLI command modules."""