dsh-openai-shim 0.2.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,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ dist/
7
+ build/
8
+ .git/
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ dist/
7
+ build/
8
+ .git/
@@ -0,0 +1,66 @@
1
+ # dsh-openai-shim Changelog
2
+
3
+ All notable changes to the `dsh-openai-shim` package are documented here.
4
+ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [0.2.0] — 2026-09-21
7
+
8
+ ### Added
9
+ - **Context-window-aware `max_tokens` (fixes the sglang/vLLM "context length" 400).**
10
+ Providers such as sglang/vLLM report `max_model_len` as the *total* context
11
+ window (input + output), not the output cap. A non-trivial prompt plus a large
12
+ `max_tokens` overflowed the window and 400'd with
13
+ *"exceeds the model's maximum context length of N tokens. You requested a
14
+ total of X tokens: I tokens from the input messages and N tokens for
15
+ completion."* This version handles that in two layers:
16
+ - **Proactive clamp** — `proxy.conf` now stores both `token_cap` (output) and
17
+ `context_window` (total); the shim clamps
18
+ `max_tokens = min(token_cap, context_window − est_input − 128)` so a long
19
+ prompt never overflows. `dsh-proxy use --base` auto-discovers **both**
20
+ limits from `/v1/models`.
21
+ - **Self-heal backstop** — if a context-window 400 still occurs, the shim
22
+ parses the server's *exact* input-token count from the error, retries once
23
+ with `context_window − input − 128`, and persists the discovered
24
+ `context_window` so the next request clamps proactively (no further 400).
25
+
26
+ ### Changed
27
+ - `resolve_upstream` / `ShimConfig` now carry the context window alongside the
28
+ output cap (4-tuple resolution). The build-time deployment-agnostic gate is
29
+ updated for the 4-tuple.
30
+ - Provider discovery reports both `token_cap` and `context_window`
31
+ (`discover_model_limits`); `discover_completion_cap` remains as a
32
+ back-compatible wrapper.
33
+
34
+ ### Fixed
35
+ - Requests with a large prompt + a large `max_tokens` no longer fail against
36
+ sglang/vLLM endpoints (the second, context-window 400 that the 0.1.x
37
+ output-cap-only self-heal did not catch).
38
+
39
+ ## [0.1.1] — 2026-09
40
+
41
+ ### Added
42
+ - **Per-provider completion-token cap, auto-discovered.** `dsh-proxy use --base`
43
+ probes `/v1/models` to learn the upstream's output limit and stores it as the
44
+ provider's `token_cap`; the shim clamps `max_tokens` to it and self-heals on a
45
+ *completion-cap* 400 (parses the server-reported cap, retries, persists).
46
+ - **`dsh-proxy use` restarts the shim** (was stop-only, leaving a dead endpoint).
47
+
48
+ ### Fixed
49
+ - `dsh-proxy status` exit code and the `serve` pidfile handling (two
50
+ boot-breaking bugs).
51
+ - Default provider is now `litellm` (DiveAI 404s on `/chat/completions` and is
52
+ not a valid default).
53
+
54
+ ## [0.1.0] — 2026-09
55
+
56
+ ### Added
57
+ - Initial release: a tiny, **zero-dependency (stdlib-only)** OpenAI-compatible
58
+ proxy that adapts DeepSeek Harness (dsh) to any OpenAI-compatible endpoint
59
+ (Socrates / LiteLLM / any sglang/vLLM endpoint).
60
+ - Safe request rewrites: `reasoning_effort` (remap to a supported value or drop)
61
+ and `max_tokens` (clamp).
62
+ - `dsh-openai-shim` (serve) and `dsh-proxy` (use/list/show/status/restart/sync)
63
+ command-line entry points.
64
+
65
+ [0.2.0]: https://github.com/dive4dec/dsh-openai-shim/compare/v0.1.1...v0.2.0
66
+ [0.1.1]: https://github.com/dive4dec/dsh-openai-shim/compare/v0.1.0...v0.1.1
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dive4dec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.5
2
+ Name: dsh-openai-shim
3
+ Version: 0.2.0
4
+ Summary: Zero-dependency OpenAI-compatible proxy that adapts DeepSeek Harness (dsh) to any OpenAI-compatible endpoint (Socrates/LiteLLM/sglang/vLLM)
5
+ Project-URL: Homepage, https://github.com/dive4dec/dsh-openai-shim
6
+ Project-URL: Source Code, https://github.com/dive4dec/dsh-openai-shim
7
+ Author: dive4dec
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: deepseek,dsh,harness,litellm,openai,proxy,reasoning,sglang,vllm
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.10
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # dsh-openai-shim
26
+
27
+ A **zero-dependency, stdlib-only** OpenAI-compatible proxy that adapts
28
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) to
29
+ **any** OpenAI-compatible endpoint — Socrates, LiteLLM, sglang, vLLM, etc.
30
+
31
+ ## Why
32
+
33
+ `dsh`'s bundled `deepseek-official` provider is written for the DeepSeek API and
34
+ does two things that break against general OpenAI endpoints like Socrates:
35
+
36
+ 1. It **hard-injects `reasoning_effort: "high"`** into every request. Socrates
37
+ (sglang) rejects `"high"` — it only accepts `xhigh` / `medium` / `low`.
38
+ → `HTTP 400: Unexpected reasoning effort high.`
39
+ 2. It requests a **default `max_tokens` of 256000**, which — combined with a
40
+ large system prompt — can exceed the model's context window.
41
+ → `HTTP 400: Requested token count exceeds the model's maximum context length.`
42
+
43
+ Both are *request-parameter* mismatches, not protocol mismatches. `dsh` is not
44
+ DeepSeek-locked; it connects, authenticates, routes, and runs its full agent
45
+ tool loop against any OpenAI endpoint. The only friction is these two params.
46
+
47
+ Rather than fight dsh's `cordis.patch.yml` / model-catalog (dev-preview,
48
+ compiled into a native binary, version-churny), this shim is the **single
49
+ compatibility chokepoint**: a tiny proxy dsh points at, which forwards to your
50
+ endpoint and rewrites the two params. It is:
51
+
52
+ - **agent-agnostic** — works for every dsh profile (`sdk`, `sdk-minimal`,
53
+ `acp`, `web`) and any OpenAI client.
54
+ - **version-immune** — if dsh changes its defaults, the shim still fixes them.
55
+ - **transparent** — passes through auth headers, streaming, `/models`,
56
+ tool-calling, everything else unchanged.
57
+
58
+ ## What it rewrites
59
+
60
+ | Param | Default behavior | Options |
61
+ |-------|------------------|---------|
62
+ | `reasoning_effort` | `high → medium` (map to a supported value) | `--effort-mode map` (default) / `drop` (remove it) / `off` |
63
+ | `max_tokens` | clamp to the provider's `token_cap` (env default `DSH_SHIM_TOKEN_CAP`, `100000`) | per-provider cap (auto-discovered); `0` disables clamping |
64
+
65
+ The `max_tokens` cap is **per-provider**, not global: different models have
66
+ different completion ceilings (a local sglang/vLLM server may allow 64000, a
67
+ big hosted endpoint 100000+). The cap is discovered automatically when a
68
+ provider is added, corrected automatically if the upstream rejects it, and can
69
+ always be overridden with `--token-cap` — see [Per-user providers](#per-user-providers-dsh-proxy).
70
+
71
+ ## Install
72
+
73
+ ```sh
74
+ pip install dsh-openai-shim
75
+ # or from source
76
+ pip install -e .
77
+ ```
78
+
79
+ No third-party runtime dependencies — pure Python stdlib.
80
+
81
+ ## Use
82
+
83
+ Run the shim, then point dsh at it:
84
+
85
+ ```sh
86
+ # 1. start the shim (maps high→medium, caps max_tokens at 100000)
87
+ dsh-openai-shim serve \
88
+ --upstream https://socratic.cs.cityu.edu.hk/ai-test \
89
+ --port 8090
90
+
91
+ # 2. point dsh at the shim (not the upstream directly)
92
+ DSH_HOME=/tmp/dsh python - <<'PY'
93
+ from deepseek_harness import DeepSeekHarness
94
+ with DeepSeekHarness(
95
+ dsh_home="/tmp/dsh",
96
+ cwd="/workspace",
97
+ provider="deepseek-official",
98
+ model="Socrates", # your endpoint's model id
99
+ base_url="http://127.0.0.1:8090/v1", # NOTE: points at the shim
100
+ api_key="...",
101
+ max_tokens=100000,
102
+ ) as h:
103
+ print(h.run("Say hi.", session_id="s").final_response)
104
+ PY
105
+ ```
106
+
107
+ > **Path note:** if the client appends `/v1/...` to `base_url`, set
108
+ > `--upstream` to the path **without** a trailing `/v1`.
109
+
110
+ ### Useful flags
111
+
112
+ ```
113
+ --effort-mode {map,drop,off} how to handle reasoning_effort (default: map)
114
+ --token-cap N clamp completion max_tokens to N (default: 100000, 0 = off)
115
+ --upstream-key Bearer force a specific Authorization key upstream
116
+ --host / --port listen address (default 127.0.0.1:8090)
117
+ ```
118
+
119
+ ### One-shot rewrite (no server) — for tests / debugging
120
+
121
+ ```sh
122
+ echo '{"reasoning_effort":"high","max_tokens":256000}' | \
123
+ dsh-openai-shim rewrite --effort-mode map
124
+ # {"reasoning_effort": "medium", "max_tokens": 100000}
125
+ ```
126
+
127
+ ## Per-user providers (`dsh-proxy`)
128
+
129
+ Beyond the one-shot `dsh-openai-shim serve --upstream …`, the package ships a
130
+ **per-user provider manager** (`dsh-proxy`) for deployments that run one pod
131
+ per student (e.g. JupyterHub). The student's choice of upstream is persisted
132
+ in `~/.dsh/proxy.conf` (NFS home) so it survives pod restarts — the same
133
+ file-based model as Hermes' `~/.hermes/config.yaml`:
134
+
135
+ ```json
136
+ {
137
+ "provider": "litellm",
138
+ "providers": {
139
+ "diveai": {"base_url": "https://…/ai/v1", "api_key": "…"},
140
+ "litellm": {"base_url": "https://…/litellm/v1", "api_key": "…"},
141
+ "myprov": {"base_url": "http://10.0.0.5:30000/v1", "api_key": "…",
142
+ "token_cap": 64000}
143
+ }
144
+ }
145
+ ```
146
+
147
+ - **The deployment's providers** (e.g. `diveai`, `litellm`) are upserted by
148
+ `dsh-proxy sync` on every notebook start — it reads the endpoint + key from
149
+ the pod environment (`DIVEAI_API_BASE/_KEY`, `LITELLM_API_BASE/_KEY`) and
150
+ refreshes those entries (key rotation, endpoint moves), without touching
151
+ student-added providers. The first-run default provider is set once and
152
+ never clobbered afterwards. A student-set `token_cap` survives a re-sync.
153
+ - **Custom providers** are the student's own: `dsh-proxy use <name> --base
154
+ <url> [--key <key>]` adds ANY name (as many as you like) and switches to
155
+ it. Omit `--key` to forward the caller's `Authorization` header as-is.
156
+ - **Completion-token cap is auto-discovered** (the student never has to know
157
+ the model's limit). On `use <name> --base <url>`, the shim probes
158
+ `GET /v1/models` on the new endpoint and reads the model's completion ceiling
159
+ (`max_completion_tokens` / `max_model_len`) into `token_cap`. This is the same
160
+ technique Hermes uses when you add a custom endpoint and skip the context
161
+ length. If the probe can't reach the endpoint (or it doesn't advertise a
162
+ limit), the cap is left at the env default — and the shim **self-heals at
163
+ request time**: if the upstream rejects a request with an "N too large … at
164
+ most M tokens" 400, the shim parses `M`, lowers the cap, retries the request
165
+ once, and persists `M` to proxy.conf. So a wrong/missing cap corrects itself
166
+ on the first failed turn. Override either step with `--token-cap N`
167
+ (or `--no-discover` to skip the probe entirely).
168
+ - **Resolution is file-based and env-inert** — the daemon reads only
169
+ proxy.conf. A trailing `/v1` on a base is stripped automatically (the shim
170
+ re-appends it when forwarding).
171
+
172
+ ```sh
173
+ dsh-proxy list # all providers, current flagged (*), caps shown
174
+ dsh-proxy use litellm # switch to an existing provider
175
+ dsh-proxy use myprov --base https://host/v1 --key sk-... # add + switch (cap auto-detected)
176
+ dsh-proxy use spark --base http://10.0.0.5:30000/v1 --key sk-... # cap auto-detected
177
+ dsh-proxy use myprov --base https://host/v1 --key sk-... --token-cap 40000 # force a cap
178
+ dsh-proxy show # current provider + upstream + effective cap (key masked)
179
+ dsh-proxy sync # (in-pod) refresh deployment providers
180
+ dsh-proxy status # is the shim daemon running?
181
+ dsh-proxy restart # restart the shim daemon from proxy.conf
182
+ ```
183
+
184
+ `use` writes the file and then **restarts** the running shim so the new
185
+ provider takes effect immediately (there is no lazy auto-start in the request
186
+ path — dsh makes plain HTTP calls to the loopback port). `sync` never
187
+ restarts anything; the caller (the pod startup hook) restarts the daemon
188
+ only when `sync` prints `changed=yes`.
189
+
190
+ The package itself is **deployment-agnostic**: no provider catalog, no
191
+ endpoints, no env-var names beyond the two raw `DIVEAI_*` / `LITELLM_*` pair
192
+ the sync reads. A build-time gate (`dsh_shim_deployment_agnostic_gate.py` in
193
+ the Jupyter image) fails the build if a deployment host ever leaks into the
194
+ installed package or if resolution starts reading the environment.
195
+
196
+ ## Library use
197
+
198
+ ```python
199
+ from dsh_openai_shim import ShimConfig, apply_rewrites, make_handler, serve
200
+
201
+ cfg = ShimConfig(upstream="http://127.0.0.1:9999/v1",
202
+ effort_mode="map", token_cap=100000)
203
+ new_body, changes = apply_rewrites(body, cfg) # pure, testable
204
+ serve(cfg) # run the server
205
+ ```
206
+
207
+ ## Security
208
+
209
+ - By default the shim **forwards the client's `Authorization` header** to the
210
+ upstream. Use `--upstream-key` to replace it with a server-side key so the
211
+ client never holds the real endpoint credential.
212
+ - Bind to `127.0.0.1` (default). Do not expose the shim to untrusted networks
213
+ without auth.
214
+
215
+ ## Tests
216
+
217
+ ```sh
218
+ python -m pytest tests/ -q
219
+ ```
220
+
221
+ The end-to-end tests run a local fake OpenAI upstream (no external network, no
222
+ dsh required) and assert the shim rewrites `reasoning_effort` and `max_tokens`
223
+ while passing through auth and the response.
224
+
225
+ ## Status
226
+
227
+ v0.1.0 — spike-proven against Socrates (dsh → shim → Socrates → `pong`).
228
+ Zero deps, MIT.
@@ -0,0 +1,204 @@
1
+ # dsh-openai-shim
2
+
3
+ A **zero-dependency, stdlib-only** OpenAI-compatible proxy that adapts
4
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) to
5
+ **any** OpenAI-compatible endpoint — Socrates, LiteLLM, sglang, vLLM, etc.
6
+
7
+ ## Why
8
+
9
+ `dsh`'s bundled `deepseek-official` provider is written for the DeepSeek API and
10
+ does two things that break against general OpenAI endpoints like Socrates:
11
+
12
+ 1. It **hard-injects `reasoning_effort: "high"`** into every request. Socrates
13
+ (sglang) rejects `"high"` — it only accepts `xhigh` / `medium` / `low`.
14
+ → `HTTP 400: Unexpected reasoning effort high.`
15
+ 2. It requests a **default `max_tokens` of 256000**, which — combined with a
16
+ large system prompt — can exceed the model's context window.
17
+ → `HTTP 400: Requested token count exceeds the model's maximum context length.`
18
+
19
+ Both are *request-parameter* mismatches, not protocol mismatches. `dsh` is not
20
+ DeepSeek-locked; it connects, authenticates, routes, and runs its full agent
21
+ tool loop against any OpenAI endpoint. The only friction is these two params.
22
+
23
+ Rather than fight dsh's `cordis.patch.yml` / model-catalog (dev-preview,
24
+ compiled into a native binary, version-churny), this shim is the **single
25
+ compatibility chokepoint**: a tiny proxy dsh points at, which forwards to your
26
+ endpoint and rewrites the two params. It is:
27
+
28
+ - **agent-agnostic** — works for every dsh profile (`sdk`, `sdk-minimal`,
29
+ `acp`, `web`) and any OpenAI client.
30
+ - **version-immune** — if dsh changes its defaults, the shim still fixes them.
31
+ - **transparent** — passes through auth headers, streaming, `/models`,
32
+ tool-calling, everything else unchanged.
33
+
34
+ ## What it rewrites
35
+
36
+ | Param | Default behavior | Options |
37
+ |-------|------------------|---------|
38
+ | `reasoning_effort` | `high → medium` (map to a supported value) | `--effort-mode map` (default) / `drop` (remove it) / `off` |
39
+ | `max_tokens` | clamp to the provider's `token_cap` (env default `DSH_SHIM_TOKEN_CAP`, `100000`) | per-provider cap (auto-discovered); `0` disables clamping |
40
+
41
+ The `max_tokens` cap is **per-provider**, not global: different models have
42
+ different completion ceilings (a local sglang/vLLM server may allow 64000, a
43
+ big hosted endpoint 100000+). The cap is discovered automatically when a
44
+ provider is added, corrected automatically if the upstream rejects it, and can
45
+ always be overridden with `--token-cap` — see [Per-user providers](#per-user-providers-dsh-proxy).
46
+
47
+ ## Install
48
+
49
+ ```sh
50
+ pip install dsh-openai-shim
51
+ # or from source
52
+ pip install -e .
53
+ ```
54
+
55
+ No third-party runtime dependencies — pure Python stdlib.
56
+
57
+ ## Use
58
+
59
+ Run the shim, then point dsh at it:
60
+
61
+ ```sh
62
+ # 1. start the shim (maps high→medium, caps max_tokens at 100000)
63
+ dsh-openai-shim serve \
64
+ --upstream https://socratic.cs.cityu.edu.hk/ai-test \
65
+ --port 8090
66
+
67
+ # 2. point dsh at the shim (not the upstream directly)
68
+ DSH_HOME=/tmp/dsh python - <<'PY'
69
+ from deepseek_harness import DeepSeekHarness
70
+ with DeepSeekHarness(
71
+ dsh_home="/tmp/dsh",
72
+ cwd="/workspace",
73
+ provider="deepseek-official",
74
+ model="Socrates", # your endpoint's model id
75
+ base_url="http://127.0.0.1:8090/v1", # NOTE: points at the shim
76
+ api_key="...",
77
+ max_tokens=100000,
78
+ ) as h:
79
+ print(h.run("Say hi.", session_id="s").final_response)
80
+ PY
81
+ ```
82
+
83
+ > **Path note:** if the client appends `/v1/...` to `base_url`, set
84
+ > `--upstream` to the path **without** a trailing `/v1`.
85
+
86
+ ### Useful flags
87
+
88
+ ```
89
+ --effort-mode {map,drop,off} how to handle reasoning_effort (default: map)
90
+ --token-cap N clamp completion max_tokens to N (default: 100000, 0 = off)
91
+ --upstream-key Bearer force a specific Authorization key upstream
92
+ --host / --port listen address (default 127.0.0.1:8090)
93
+ ```
94
+
95
+ ### One-shot rewrite (no server) — for tests / debugging
96
+
97
+ ```sh
98
+ echo '{"reasoning_effort":"high","max_tokens":256000}' | \
99
+ dsh-openai-shim rewrite --effort-mode map
100
+ # {"reasoning_effort": "medium", "max_tokens": 100000}
101
+ ```
102
+
103
+ ## Per-user providers (`dsh-proxy`)
104
+
105
+ Beyond the one-shot `dsh-openai-shim serve --upstream …`, the package ships a
106
+ **per-user provider manager** (`dsh-proxy`) for deployments that run one pod
107
+ per student (e.g. JupyterHub). The student's choice of upstream is persisted
108
+ in `~/.dsh/proxy.conf` (NFS home) so it survives pod restarts — the same
109
+ file-based model as Hermes' `~/.hermes/config.yaml`:
110
+
111
+ ```json
112
+ {
113
+ "provider": "litellm",
114
+ "providers": {
115
+ "diveai": {"base_url": "https://…/ai/v1", "api_key": "…"},
116
+ "litellm": {"base_url": "https://…/litellm/v1", "api_key": "…"},
117
+ "myprov": {"base_url": "http://10.0.0.5:30000/v1", "api_key": "…",
118
+ "token_cap": 64000}
119
+ }
120
+ }
121
+ ```
122
+
123
+ - **The deployment's providers** (e.g. `diveai`, `litellm`) are upserted by
124
+ `dsh-proxy sync` on every notebook start — it reads the endpoint + key from
125
+ the pod environment (`DIVEAI_API_BASE/_KEY`, `LITELLM_API_BASE/_KEY`) and
126
+ refreshes those entries (key rotation, endpoint moves), without touching
127
+ student-added providers. The first-run default provider is set once and
128
+ never clobbered afterwards. A student-set `token_cap` survives a re-sync.
129
+ - **Custom providers** are the student's own: `dsh-proxy use <name> --base
130
+ <url> [--key <key>]` adds ANY name (as many as you like) and switches to
131
+ it. Omit `--key` to forward the caller's `Authorization` header as-is.
132
+ - **Completion-token cap is auto-discovered** (the student never has to know
133
+ the model's limit). On `use <name> --base <url>`, the shim probes
134
+ `GET /v1/models` on the new endpoint and reads the model's completion ceiling
135
+ (`max_completion_tokens` / `max_model_len`) into `token_cap`. This is the same
136
+ technique Hermes uses when you add a custom endpoint and skip the context
137
+ length. If the probe can't reach the endpoint (or it doesn't advertise a
138
+ limit), the cap is left at the env default — and the shim **self-heals at
139
+ request time**: if the upstream rejects a request with an "N too large … at
140
+ most M tokens" 400, the shim parses `M`, lowers the cap, retries the request
141
+ once, and persists `M` to proxy.conf. So a wrong/missing cap corrects itself
142
+ on the first failed turn. Override either step with `--token-cap N`
143
+ (or `--no-discover` to skip the probe entirely).
144
+ - **Resolution is file-based and env-inert** — the daemon reads only
145
+ proxy.conf. A trailing `/v1` on a base is stripped automatically (the shim
146
+ re-appends it when forwarding).
147
+
148
+ ```sh
149
+ dsh-proxy list # all providers, current flagged (*), caps shown
150
+ dsh-proxy use litellm # switch to an existing provider
151
+ dsh-proxy use myprov --base https://host/v1 --key sk-... # add + switch (cap auto-detected)
152
+ dsh-proxy use spark --base http://10.0.0.5:30000/v1 --key sk-... # cap auto-detected
153
+ dsh-proxy use myprov --base https://host/v1 --key sk-... --token-cap 40000 # force a cap
154
+ dsh-proxy show # current provider + upstream + effective cap (key masked)
155
+ dsh-proxy sync # (in-pod) refresh deployment providers
156
+ dsh-proxy status # is the shim daemon running?
157
+ dsh-proxy restart # restart the shim daemon from proxy.conf
158
+ ```
159
+
160
+ `use` writes the file and then **restarts** the running shim so the new
161
+ provider takes effect immediately (there is no lazy auto-start in the request
162
+ path — dsh makes plain HTTP calls to the loopback port). `sync` never
163
+ restarts anything; the caller (the pod startup hook) restarts the daemon
164
+ only when `sync` prints `changed=yes`.
165
+
166
+ The package itself is **deployment-agnostic**: no provider catalog, no
167
+ endpoints, no env-var names beyond the two raw `DIVEAI_*` / `LITELLM_*` pair
168
+ the sync reads. A build-time gate (`dsh_shim_deployment_agnostic_gate.py` in
169
+ the Jupyter image) fails the build if a deployment host ever leaks into the
170
+ installed package or if resolution starts reading the environment.
171
+
172
+ ## Library use
173
+
174
+ ```python
175
+ from dsh_openai_shim import ShimConfig, apply_rewrites, make_handler, serve
176
+
177
+ cfg = ShimConfig(upstream="http://127.0.0.1:9999/v1",
178
+ effort_mode="map", token_cap=100000)
179
+ new_body, changes = apply_rewrites(body, cfg) # pure, testable
180
+ serve(cfg) # run the server
181
+ ```
182
+
183
+ ## Security
184
+
185
+ - By default the shim **forwards the client's `Authorization` header** to the
186
+ upstream. Use `--upstream-key` to replace it with a server-side key so the
187
+ client never holds the real endpoint credential.
188
+ - Bind to `127.0.0.1` (default). Do not expose the shim to untrusted networks
189
+ without auth.
190
+
191
+ ## Tests
192
+
193
+ ```sh
194
+ python -m pytest tests/ -q
195
+ ```
196
+
197
+ The end-to-end tests run a local fake OpenAI upstream (no external network, no
198
+ dsh required) and assert the shim rewrites `reasoning_effort` and `max_tokens`
199
+ while passing through auth and the response.
200
+
201
+ ## Status
202
+
203
+ v0.1.0 — spike-proven against Socrates (dsh → shim → Socrates → `pong`).
204
+ Zero deps, MIT.