dsh-gateway-provider 1.1.1 → 1.1.2

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.
package/README.md CHANGED
@@ -92,7 +92,7 @@ the first start where the plugin loads. The frequently used ones:
92
92
 
93
93
  | Key | Default | Meaning |
94
94
  | --- | --- | --- |
95
- | `baseURL` | `https://api.newapi.ai` | Your gateway's base URL. Env fallbacks: `NEWAPI_BASE_URL`, `NEWAPI_API_URL`. |
95
+ | `baseURL` | `https://api.newapi.ai` | Your gateway's base URL. Env fallbacks: `NEWAPI_BASE_URL`, `NEWAPI_API_URL`. A trailing `/v1` or `/v1beta` is tolerated (stripped for discovery); Gemini requests always go to `{base}/v1beta/models/…`. |
96
96
  | `apiKeyEnv` | `NEWAPI_API_KEY` | Which env/credential variable holds the key. |
97
97
  | `label` | `NewAPI` | Route label shown in the picker. |
98
98
  | `flavor` | `newapi` | Template label only (`newapi` / `litellm` / `higress` / `openai-compatible` / `custom`). |
@@ -103,7 +103,7 @@ the first start where the plugin loads. The frequently used ones:
103
103
  | `sortModelsByRelease` | `true` | Newest models first. |
104
104
  | `catalogMode` | `auto` | `v1` (`/v1/models` only) / `management` (newapi user API) / `auto`. |
105
105
  | `endpointPriority` | responses → anthropic → openai → gemini | Which protocol to prefer when a model supports several. |
106
- | `openaiURL` / `responsesURL` / `anthropicURL` | — | Fully-custom gateways only: per-protocol endpoint URLs; unset = that protocol off. |
106
+ | `openaiURL` / `responsesURL` / `anthropicURL` / `geminiURL` | — | Fully-custom gateways only: per-protocol endpoint URLs; unset = that protocol off. `geminiURL` may be a bare host, `…/v1beta`, or `…/v1beta/models` — the `/v1beta` segment is ensured (pi-ai expects it in the base). |
107
107
  | `maxTokens` / `defaultContextWindow` | `32768` / `128000` | Fallbacks when models.dev has no data. |
108
108
  | `streamIdleTimeoutMs` | `600000` | Idle timeout while streaming. |
109
109
  | `headers` | — | Extra HTTP headers sent to the gateway. |
@@ -116,7 +116,8 @@ the first start where the plugin loads. The frequently used ones:
116
116
  | `401` / auth errors on every request | Key missing or wrong: check the badge in Settings → Gateway Models, or `NEWAPI_API_KEY` in `~/.dsh/.credentials.yaml`. |
117
117
  | A model's context window looks wrong | models.dev had no match. Edit the model on the settings page (or a `models:` override). |
118
118
  | Wrong format answers / tool calls flaky for one model | That model is routed over a protocol it handles poorly. Pin `protocol` on the model (`openai`, `openai-response`, `anthropic`, `gemini`). |
119
- | Custom gateway with separate endpoints | Use `flavor: "custom"` and set `openaiURL` / `responsesURL` / `anthropicURL` explicitly. |
119
+ | Custom gateway with separate endpoints | Use `flavor: "custom"` and set `openaiURL` / `responsesURL` / `anthropicURL` / `geminiURL` explicitly. |
120
+ | Gemini model returns HTML / `Unexpected token '<'` (issue #4) | The gateway serves Gemini under `/v1beta`; fixed in 1.1.2 — a plain `baseURL` now sends Gemini to `{base}/v1beta/models/…` (a `baseURL` ending in `/v1beta` also works). No older-version workaround exists; update the plugin. |
120
121
 
121
122
  ## How it works (one minute version)
122
123
 
package/docs/README.zh.md CHANGED
@@ -56,7 +56,7 @@ dsh --profile web
56
56
 
57
57
  | 键 | 默认值 | 含义 |
58
58
  | --- | --- | --- |
59
- | `baseURL` | `https://api.newapi.ai` | 网关 base URL。环境变量兜底:`NEWAPI_BASE_URL`、`NEWAPI_API_URL`。 |
59
+ | `baseURL` | `https://api.newapi.ai` | 网关 base URL。环境变量兜底:`NEWAPI_BASE_URL`、`NEWAPI_API_URL`。结尾带 `/v1` 或 `/v1beta` 均可(发现时会剥掉);Gemini 请求固定发往 `{base}/v1beta/models/…`。 |
60
60
  | `apiKeyEnv` | `NEWAPI_API_KEY` | key 存在哪个环境/凭据变量里。 |
61
61
  | `label` | `NewAPI` | 选择器里显示的路由名。 |
62
62
  | `flavor` | `newapi` | 仅作模板标签(`newapi` / `litellm` / `higress` / `openai-compatible` / `custom`)。 |
@@ -67,7 +67,7 @@ dsh --profile web
67
67
  | `sortModelsByRelease` | `true` | 最新模型排前面。 |
68
68
  | `catalogMode` | `auto` | `v1`(只用 `/v1/models`)/ `management`(newapi 用户 API)/ `auto`。 |
69
69
  | `endpointPriority` | responses → anthropic → openai → gemini | 模型支持多种协议时的优先序。 |
70
- | `openaiURL` / `responsesURL` / `anthropicURL` | — | 仅完全自定义网关:按协议分别填端点 URL;不填 = 该协议关闭。 |
70
+ | `openaiURL` / `responsesURL` / `anthropicURL` / `geminiURL` | — | 仅完全自定义网关:按协议分别填端点 URL;不填 = 该协议关闭。`geminiURL` 可填裸主机名、`…/v1beta` 或 `…/v1beta/models` —— `/v1beta` 段会自动确保(pi-ai 要求它含在 base 里)。 |
71
71
  | `maxTokens` / `defaultContextWindow` | `32768` / `128000` | models.dev 无数据时的兜底。 |
72
72
  | `streamIdleTimeoutMs` | `600000` | 流式空闲超时。 |
73
73
  | `headers` | — | 发往网关的额外 HTTP 头。 |
@@ -80,7 +80,8 @@ dsh --profile web
80
80
  | 每次请求都 `401` / 鉴权错误 | key 缺失或不对:看 Settings → Gateway Models 的徽标,或 `~/.dsh/.credentials.yaml` 里的 `NEWAPI_API_KEY`。 |
81
81
  | 某模型的上下文窗口看着不对 | models.dev 没匹配上。在设置页编辑该模型(或写 `models:` 覆盖)。 |
82
82
  | 单个模型工具调用不稳、格式怪 | 该模型被路由到了它处理不好的协议。在模型上钉死 `protocol`(`openai`、`openai-response`、`anthropic`、`gemini`)。 |
83
- | 自定义网关各协议端点分开 | 用 `flavor: "custom"`,显式填 `openaiURL` / `responsesURL` / `anthropicURL`。 |
83
+ | 自定义网关各协议端点分开 | 用 `flavor: "custom"`,显式填 `openaiURL` / `responsesURL` / `anthropicURL` / `geminiURL`。 |
84
+ | Gemini 模型返回 HTML / `Unexpected token '<'`(issue #4) | 网关把 Gemini 挂在 `/v1beta` 下;1.1.2 已修复 —— 普通 `baseURL` 也会把 Gemini 请求发到 `{base}/v1beta/models/…`(`baseURL` 以 `/v1beta` 结尾同样可用)。旧版本没有任何绕过手段,请升级插件。 |
84
85
 
85
86
  ## 工作原理(一分钟版)
86
87
 
package/index.js CHANGED
@@ -4,7 +4,8 @@
4
4
  * Registers one or more `gateway:*` provider routes on `ctx.llm`, each backed
5
5
  * by an OpenAI-compatible `/v1/models` gateway (newapi, LiteLLM, Higress, or
6
6
  * any generic gateway), or by a fully-custom gateway whose per-protocol
7
- * endpoint URLs (`openaiURL` / `responsesURL` / `anthropicURL`) are written
7
+ * endpoint URLs (`openaiURL` / `responsesURL` / `anthropicURL` / `geminiURL`)
8
+ * are written
8
9
  * out in full. Backwards-compatible with the original single-connection
9
10
  * `newapi` route: the legacy flat fields (`baseURL`, `apiKeyEnv`, …) build a
10
11
  * default gateway, while the `gateways` array adds more.
@@ -121,6 +122,12 @@ const GatewaySchema = z.object({
121
122
  responsesURL: coercedString(),
122
123
  /** Full Anthropic messages endpoint URL (custom template). */
123
124
  anthropicURL: coercedString(),
125
+ /**
126
+ * Gemini native endpoint base (custom template): a bare host, a base
127
+ * ending in `/v1beta`, or one with a trailing `/models`. The `/v1beta`
128
+ * segment is ensured — pi-ai's Gemini client expects it in the base.
129
+ */
130
+ geminiURL: coercedString(),
124
131
  /** Environment-variable name (credential ref) holding the API key. */
125
132
  apiKeyEnv: coercedString().role("credential-ref"),
126
133
  /** Gateway type label (newapi/litellm/higress/openai-compatible/custom). */
@@ -149,6 +156,8 @@ export const Config = z.object({
149
156
  responsesURL: coercedString(),
150
157
  /** Full Anthropic messages endpoint URL (custom template for the default gateway). */
151
158
  anthropicURL: coercedString(),
159
+ /** Gemini native endpoint base (custom template for the default gateway). */
160
+ geminiURL: coercedString(),
152
161
  /** Gateway type label shown in the UI (newapi/litellm/higress/openai-compatible/custom). */
153
162
  flavor: z.union(FLAVORS).default("newapi"),
154
163
  /** models.dev catalog URL (any fetch-able URL; file: works for offline mirrors). */
@@ -193,7 +202,8 @@ export const Config = z.object({
193
202
  /**
194
203
  * Normalize one gateway entry into the connection facts the adapter needs.
195
204
  * Per-gateway fields fall back to the shared defaults. Protocol URL fields
196
- * (`openaiURL` / `responsesURL` / `anthropicURL`, the "custom" template)
205
+ * (`openaiURL` / `responsesURL` / `anthropicURL` / `geminiURL`, the "custom"
206
+ * template)
197
207
  * redefine the gateway's protocol surface: each configured URL becomes the
198
208
  * exact SDK base for its wire protocol, unconfigured protocols are
199
209
  * unavailable, and the plain `baseURL` is no longer inherited from the
@@ -290,6 +300,7 @@ export function resolveGateways(config, environment) {
290
300
  openaiURL: config.openaiURL,
291
301
  responsesURL: config.responsesURL,
292
302
  anthropicURL: config.anthropicURL,
303
+ geminiURL: config.geminiURL,
293
304
  }, defaults, PROVIDER, defaultLabel) },
294
305
  ];
295
306
  // Additional gateways from the array.
package/lib/catalog.js CHANGED
@@ -122,15 +122,16 @@ async function fetchManagementModels(baseURL, apiKey, userId, signal) {
122
122
  }
123
123
 
124
124
  /**
125
- * Strip trailing slashes and a trailing version segment (`/v1`, `/v2`, …)
126
- * from a gateway base URL. The catalog always appends its own full paths
127
- * (`/v1/models`, `/api/user/models`), so a user-supplied base that already
128
- * carries `/v1` (the OpenAI-SDK convention, which the chat path also accepts)
129
- * must not leak through as `/v1/v1/models`. Mirrors the derivation the custom
125
+ * Strip trailing slashes and a trailing version segment (`/v1`, `/v2`, and
126
+ * the Gemini-style `/v1beta`) from a gateway base URL. The catalog always
127
+ * appends its own full paths (`/v1/models`, `/api/user/models`), so a
128
+ * user-supplied base that already carries a version (the SDK conventions,
129
+ * which the chat path also accepts) must not leak through as
130
+ * `/v1/v1/models` or `/v1beta/v1/models`. Mirrors the derivation the custom
130
131
  * (protocol-URL) template performs in `protocols.js`.
131
132
  */
132
133
  function stripVersion(baseURL) {
133
- return String(baseURL ?? "").replace(/\/+$/, "").replace(/\/v\d+$/, "");
134
+ return String(baseURL ?? "").replace(/\/+$/, "").replace(/\/v\d+(?:beta)?$/i, "");
134
135
  }
135
136
 
136
137
  /**
package/lib/client.js CHANGED
@@ -26,7 +26,7 @@ window.__ModuleLoader__.load({
26
26
  var CATALOG_MODES = ["auto", "v1", "management"];
27
27
  var PROTOCOLS = ["openai", "openai-response", "anthropic", "gemini"];
28
28
  /** Gateway template fields (suffix "URL") → card protocol tag. */
29
- var PROTOCOL_URL_KEYS = ["openaiURL", "responsesURL", "anthropicURL"];
29
+ var PROTOCOL_URL_KEYS = ["openaiURL", "responsesURL", "anthropicURL", "geminiURL"];
30
30
 
31
31
  // ---- i18n (manual zh/en switch; no locale registration needed) ----
32
32
  var ZH = (typeof navigator !== "undefined" && navigator.language && navigator.language.startsWith("zh"));
@@ -65,19 +65,21 @@ window.__ModuleLoader__.load({
65
65
  tplHintLitellm: "LiteLLM Proxy:/v1/chat/completions 与 /v1/responses,经 /v1/models 自动发现",
66
66
  tplHintHigress: "Higress AI 网关:OpenAI 格式为主,新版本同端口支持 Anthropic /v1/messages",
67
67
  tplHintOpenaiCompatible: "任意 OpenAI 兼容网关:填一个基础地址,各协议路径自动派生",
68
- tplHintCustom: "三种协议地址分别填写完整 URL;留空的协议不启用,模型仅以已启用协议请求",
68
+ tplHintCustom: "各协议地址分别填写(Gemini 可只填主机名,自动补 /v1beta);留空的协议不启用,模型仅以已启用协议请求",
69
69
  tplNewapi: "NewAPI", tplLitellm: "LiteLLM", tplHigress: "Higress",
70
70
  tplOpenaiCompatible: "OpenAI 兼容", tplCustom: "完全自定义",
71
- openaiURL: "OpenAI 兼容地址", responsesURL: "Responses 地址", anthropicURL: "Anthropic 地址",
71
+ openaiURL: "OpenAI 兼容地址", responsesURL: "Responses 地址", anthropicURL: "Anthropic 地址", geminiURL: "Gemini 地址",
72
72
  openaiURLHint: "完整地址,以 /chat/completions 结尾(也可只填到 /v1)",
73
73
  responsesURLHint: "完整地址,以 /responses 结尾(也可只填到 /v1)",
74
74
  anthropicURLHint: "完整地址,以 /v1/messages 结尾",
75
+ geminiURLHint: "以 /v1beta 结尾(也可只填主机名,自动补 /v1beta)",
75
76
  customNeedOne: "至少填写一个协议地址",
76
77
  customURLPlaceholder: "https://gw.example.com/v1/chat/completions",
77
78
  responsesURLPlaceholder: "https://gw.example.com/v1/responses",
78
79
  anthropicURLPlaceholder: "https://gw.example.com/v1/messages",
80
+ geminiURLPlaceholder: "https://gw.example.com/v1beta",
79
81
  catalogHint: "auto:优先 /v1/models,鉴权失败时回退管理 API",
80
- baseURLClearHint: "留空则回退到环境变量 NEWAPI_BASE_URL 或官方云端地址;结尾带不带 /v1 均可",
82
+ baseURLClearHint: "留空则回退到环境变量 NEWAPI_BASE_URL 或官方云端地址;结尾带不带 /v1 或 /v1beta 均可",
81
83
  labelClearHint: "留空则回退为 gateway:<id>",
82
84
  keyEnvHint: "留空则继承默认网关的 Key(NEWAPI_API_KEY)",
83
85
  keyKeepHint: "留空保持不变;填写则覆盖存储的 Key",
@@ -125,19 +127,21 @@ window.__ModuleLoader__.load({
125
127
  tplHintLitellm: "LiteLLM proxy: /v1/chat/completions and /v1/responses; discovery via /v1/models",
126
128
  tplHintHigress: "Higress AI gateway: OpenAI format; newer versions also serve Anthropic /v1/messages",
127
129
  tplHintOpenaiCompatible: "Any OpenAI-compatible gateway: one base address, protocol paths derived automatically",
128
- tplHintCustom: "Enter each protocol's full URL; empty protocols stay disabled and models use enabled protocols only",
130
+ tplHintCustom: "Enter each protocol's URL (Gemini accepts a bare host; /v1beta is added automatically); empty protocols stay disabled and models use enabled protocols only",
129
131
  tplNewapi: "NewAPI", tplLitellm: "LiteLLM", tplHigress: "Higress",
130
132
  tplOpenaiCompatible: "OpenAI-compatible", tplCustom: "Fully custom",
131
- openaiURL: "OpenAI-compatible URL", responsesURL: "Responses URL", anthropicURL: "Anthropic URL",
133
+ openaiURL: "OpenAI-compatible URL", responsesURL: "Responses URL", anthropicURL: "Anthropic URL", geminiURL: "Gemini URL",
132
134
  openaiURLHint: "Full address ending in /chat/completions (a /v1 base also works)",
133
135
  responsesURLHint: "Full address ending in /responses (a /v1 base also works)",
134
136
  anthropicURLHint: "Full address ending in /v1/messages",
137
+ geminiURLHint: "Ending in /v1beta (a bare host also works; /v1beta is added)",
135
138
  customNeedOne: "Fill at least one protocol URL",
136
139
  customURLPlaceholder: "https://gw.example.com/v1/chat/completions",
137
140
  responsesURLPlaceholder: "https://gw.example.com/v1/responses",
138
141
  anthropicURLPlaceholder: "https://gw.example.com/v1/messages",
142
+ geminiURLPlaceholder: "https://gw.example.com/v1beta",
139
143
  catalogHint: "auto: prefer /v1/models, fall back to the management API on auth failure",
140
- baseURLClearHint: "Empty falls back to NEWAPI_BASE_URL or the public cloud default; a trailing /v1 is optional",
144
+ baseURLClearHint: "Empty falls back to NEWAPI_BASE_URL or the public cloud default; a trailing /v1 or /v1beta is optional",
141
145
  labelClearHint: "Empty falls back to gateway:<id>",
142
146
  keyEnvHint: "Empty inherits the default gateway's key (NEWAPI_API_KEY)",
143
147
  keyKeepHint: "Empty keeps the stored key; type to overwrite",
@@ -166,6 +170,7 @@ window.__ModuleLoader__.load({
166
170
  openaiURL: { label: T.openaiURL, hint: T.openaiURLHint, ph: T.customURLPlaceholder, tag: "openai" },
167
171
  responsesURL: { label: T.responsesURL, hint: T.responsesURLHint, ph: T.responsesURLPlaceholder, tag: "responses" },
168
172
  anthropicURL: { label: T.anthropicURL, hint: T.anthropicURLHint, ph: T.anthropicURLPlaceholder, tag: "anthropic" },
173
+ geminiURL: { label: T.geminiURL, hint: T.geminiURLHint, ph: T.geminiURLPlaceholder, tag: "gemini" },
169
174
  };
170
175
 
171
176
  /** Protocol tags a gateway card shows (configured URL fields only). */
@@ -346,6 +351,7 @@ window.__ModuleLoader__.load({
346
351
  openaiURL: protoOf(isDefault ? props.snapshot : gw, "openaiURL"),
347
352
  responsesURL: protoOf(isDefault ? props.snapshot : gw, "responsesURL"),
348
353
  anthropicURL: protoOf(isDefault ? props.snapshot : gw, "anthropicURL"),
354
+ geminiURL: protoOf(isDefault ? props.snapshot : gw, "geminiURL"),
349
355
  apiKey: "",
350
356
  };
351
357
  var _v = React.useState(initial); var values = _v[0]; var setValues = _v[1];
@@ -52,17 +52,30 @@ const PROTOCOLS = {
52
52
  * `/responses`. A gateway baseURL without `/v1` makes the SDK request
53
53
  * `{base}/responses`, which the gateway answers with its HTML UI (HTTP 200,
54
54
  * content-type text/html) instead of an SSE stream, so the SDK receives zero
55
- * events. Anthropic and Google protocols build their own full paths, so they
56
- * need no `/v1` appended.
55
+ * events.
56
+ *
57
+ * pi-ai's Gemini client follows the same convention with `/v1beta`: whenever
58
+ * a model carries a baseUrl it sets `httpOptions.apiVersion = ""` (its own
59
+ * Google provider ships `…googleapis.com/v1beta` as the base), so the version
60
+ * segment must come from the base itself. Without it the SDK requests
61
+ * `{base}/models/{model}:generateContent`, which gateways serving Gemini
62
+ * under `/v1beta` (e.g. new-api) answer with their HTML UI fallback.
63
+ * The Anthropic SDK builds its own full path, so it needs no version appended.
57
64
  * @param baseURL - the gateway base URL as configured.
58
65
  * @param api - the pi-ai API identifier the model will use.
59
- * @returns the baseURL the OpenAI SDK should point at.
66
+ * @returns the baseURL the SDK should point at.
60
67
  */
61
- function sdkBaseURL(baseURL, api) {
68
+ export function sdkBaseURL(baseURL, api) {
62
69
  const trimmed = String(baseURL ?? "").replace(/\/+$/, "");
63
70
  if (api === "openai-completions" || api === "openai-responses") {
64
71
  if (!/\/v\d+$/.test(trimmed)) return `${trimmed}/v1`;
65
72
  }
73
+ // pi-ai pins apiVersion = "" when a baseUrl is set, so the version segment
74
+ // must already live in the base (issue #4). Case-insensitive, matching the
75
+ // anthropic branch's tolerance in protocols.js.
76
+ if (api === "google-generative-ai" && !/\/v\d+beta$/i.test(trimmed)) {
77
+ return `${trimmed}/v1beta`;
78
+ }
66
79
  return trimmed;
67
80
  }
68
81
 
package/lib/protocols.js CHANGED
@@ -24,6 +24,10 @@ const ENDPOINT_SUFFIXES = {
24
24
  "openai-completions": "/chat/completions",
25
25
  "openai-responses": "/responses",
26
26
  "anthropic-messages": "/v1/messages",
27
+ // Gemini has no fixed endpoint suffix (the path embeds the model id:
28
+ // `{base}/v1beta/models/{model}:generateContent`), so only a trailing
29
+ // `/models` — copied from the docs' list URL — is tolerated.
30
+ "google-generative-ai": "/models",
27
31
  };
28
32
 
29
33
  /** Trim a configured URL and strip one known endpoint suffix if present. */
@@ -36,14 +40,16 @@ function stripSuffix(url, suffix) {
36
40
 
37
41
  /**
38
42
  * Derive the per-protocol facts from fully-qualified endpoint URLs
39
- * (`openaiURL` / `responsesURL` / `anthropicURL`). Unset URLs mean the
40
- * protocol is unavailable on this gateway ("没填的就是没有"): the returned
41
- * `availableTypes` is exactly the configured set.
43
+ * (`openaiURL` / `responsesURL` / `anthropicURL` / `geminiURL`). Unset URLs
44
+ * mean the protocol is unavailable on this gateway ("没填的就是没有"): the
45
+ * returned `availableTypes` is exactly the configured set.
42
46
  *
43
47
  * Each URL may be the complete endpoint address (`…/v1/chat/completions`),
44
48
  * an SDK base (`…/v1`), or — for anthropic — a bare host; the derivation
45
49
  * strips the known suffix and re-adds the version segment where the SDK
46
- * expects it.
50
+ * expects it. Gemini URLs accept a bare host, a base ending in `/v1beta`,
51
+ * or one with a trailing `/models`; the `/v1beta` segment is always ensured
52
+ * (pi-ai's Gemini client disables its own apiVersion when baseUrl is set).
47
53
  * @param urls - the gateway's protocol URL fields (any may be absent).
48
54
  * @returns `{ apiBases, availableTypes, catalogBase }` where apiBases maps
49
55
  * pi-ai API id → SDK base URL, availableTypes lists gateway endpoint
@@ -80,7 +86,19 @@ export function deriveProtocolURLs(urls) {
80
86
  availableTypes.push("anthropic");
81
87
  }
82
88
  }
89
+ // Gemini: pi-ai's client pins `apiVersion = ""` whenever the model carries
90
+ // a baseUrl (mirroring its builtin Google provider, whose baseUrl is
91
+ // `https://generativelanguage.googleapis.com/v1beta`), so the version
92
+ // segment must live in the base. Accept a bare host (append `/v1beta`), a
93
+ // base already ending in `/v1beta`, or one with a trailing `/models`.
94
+ const gm = stripSuffix(urls.geminiURL, ENDPOINT_SUFFIXES["google-generative-ai"]);
95
+ if (gm !== undefined) {
96
+ apiBases["google-generative-ai"] = /\/v\d+beta$/i.test(gm) ? gm : `${gm}/v1beta`;
97
+ availableTypes.push("gemini");
98
+ }
83
99
  if (availableTypes.length === 0) return undefined;
100
+ // Gemini has no OpenAI-shaped `/v1/models` list endpoint, so only the
101
+ // OpenAI-style URLs contribute the discovery base.
84
102
  for (const api of ["openai-completions", "openai-responses"]) {
85
103
  if (apiBases[api] !== undefined) { catalogBase = apiBases[api].replace(/\/v\d+$/, ""); break; }
86
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-gateway-provider",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Generic LLM gateway model provider plugin for DeepSeek Harness: mounts newapi / LiteLLM / Higress / any OpenAI-compatible gateway as provider routes, auto-discovers the model list, enriches every model with models.dev parameters, and dispatches each model over its native wire protocol (OpenAI / Anthropic / Gemini) via the pi-ai SDK",
5
5
  "repository": {
6
6
  "type": "git",