@neon/ai-sdk-provider 0.9.0 → 1.0.0

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
@@ -2,9 +2,9 @@
2
2
 
3
3
  Community [Vercel AI SDK](https://ai-sdk.dev) provider for the [Neon](https://neon.com) AI Gateway. Supports **AI SDK v6 and v7** (`ai@^6` or `ai@^7`).
4
4
 
5
- The Neon AI Gateway is **branch-scoped**: each Neon project branch gets its own gateway host, and a platform token authorizes requests for that branch. Use the same `neon(modelId)` API across the branch's model catalog; the provider selects Anthropic Messages, OpenAI Responses, or Chat Completions for each model.
5
+ The Neon AI Gateway is **branch-scoped**: each Neon project branch gets its own gateway host, and a platform token authorizes requests for that branch. Use the same `neon(modelId)` API across the branch's text models; the provider selects Anthropic Messages, OpenAI Responses, or Chat Completions for each model. Embedding models use `neon.embeddingModel(modelId)`.
6
6
 
7
- Use canonical model ids such as `gpt-5-mini`, `llama-4-maverick`, and `gemini-3-flash`, matching the [`neon` provider on models.dev](https://models.dev). The typed catalog includes the known model ids, and arbitrary strings are accepted so newly available models work before the types update.
7
+ Use canonical model ids such as `gpt-5-mini`, `llama-4-maverick`, and `gemini-3-flash`, matching [neon.com/models](https://neon.com/models). The typed catalog includes the known model ids, and arbitrary strings are accepted so newly available models work before the types update.
8
8
 
9
9
  ## Install
10
10
 
@@ -59,7 +59,7 @@ Routing matches on the model id.
59
59
 
60
60
  ## Capabilities
61
61
 
62
- `generateText` and `streamText` work with any model available to your branch. `generateObject`, `streamObject`, and single- or multi-step tool calls work with OpenAI (including Codex), Meta, Alibaba, Zhipu AI, and Thinking Machines models. Gemini currently supports `generateText` and `streamText`; structured output and multi-step tools are not supported. Vision input works on models that accept images.
62
+ `generateText` and `streamText` work with any text model available to your branch. `generateObject`, `streamObject`, and single- or multi-step tool calls work with OpenAI (including Codex), Meta, Alibaba, Zhipu AI, and Thinking Machines models. Gemini currently supports `generateText` and `streamText`; structured output and multi-step tools are not supported. Vision input works on models that accept images.
63
63
 
64
64
  Claude models use the Messages API. On both that route and Chat Completions the provider removes call options the gateway rejects and reports each one in `result.warnings` instead of failing the request — see [Dropped call options](#dropped-call-options). Unsupported Responses API storage options throw before a request is sent — see [Errors](#errors).
65
65
 
@@ -73,11 +73,11 @@ These rules apply on the Anthropic Messages and Chat Completions routes. The Res
73
73
 
74
74
  | Models | Dropped |
75
75
  | --- | --- |
76
- | Claude 4.7 and newer (`claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-5`, `claude-sonnet-5`, `claude-fable-5`) | `temperature`, `topP` |
76
+ | Claude 4.7 and newer (`claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-5`, `claude-sonnet-5`, `claude-fable-5`, `claude-fable-5-1`) | `temperature`, `topP` |
77
77
  | All Claude | `frequencyPenalty`, `presencePenalty`, `seed`, `reasoningEffort`, and `topP` when you set both `temperature` and `topP` (Anthropic accepts only one) |
78
78
  | `gpt-oss` | `frequencyPenalty`, `presencePenalty`, `seed`, `stopSequences` |
79
79
  | Qwen, Gemma | `frequencyPenalty`, `presencePenalty`, `seed` |
80
- | `glm-5-2`, `inkling`, `kimi-k3` | `frequencyPenalty`, `presencePenalty` |
80
+ | `glm-5-2`, `glm-5-3-flash`, `inkling`, `kimi-k3` | `frequencyPenalty`, `presencePenalty` |
81
81
  | Meta Llama | `frequencyPenalty`, `presencePenalty`, `seed` |
82
82
  | `gemini-3-5-flash-lite` | `frequencyPenalty`, `presencePenalty` |
83
83
  | `gemini-3-6-flash` | `temperature`, `topP`, `frequencyPenalty`, `presencePenalty` |
@@ -100,13 +100,36 @@ Query the rules directly rather than hardcoding them:
100
100
  import { getNeonModelCapabilities } from '@neon/ai-sdk-provider';
101
101
 
102
102
  getNeonModelCapabilities('claude-opus-5').supportsTemperature; // false
103
+ getNeonModelCapabilities('gpt-6-astra').supportsTemperature; // false
103
104
  getNeonModelCapabilities('glm-5-2').supportsPenalties; // false
104
105
  ```
105
106
 
106
- **The rules are per model, not per family.** Two Gemini ids reject penalties while their siblings accept them, and `gemini-3-6-flash` rejects `temperature` and `topP` outright, so reading a row for "Gemini" is not enough — check the id. Every entry above is measured against the gateway rather than inherited from the upstream provider's own documentation, which disagrees in both directions.
107
+ **The rules are per model, not per family.** Two Gemini ids reject penalties while their siblings accept them, and `gemini-3-6-flash` rejects `temperature` and `topP` outright, so reading a row for "Gemini" is not enough — check the id. Every dropped-options table row was measured against the gateway rather than inherited from the upstream provider's own documentation, which disagrees in both directions. `getNeonModelCapabilities('gpt-6-astra').supportsTemperature` is `false` because [neon.com/models.json](https://neon.com/models.json) lists `temperature: false` for that id; it is not a live gateway measurement.
107
108
 
108
109
  A model none of these rules match is left untouched, so a brand-new id gets the gateway's own error rather than a guess. That is also why the table can lag: an id added since the last release inherits the permissive default until someone measures it.
109
110
 
111
+ ## Embeddings
112
+
113
+ `neon.embeddingModel()` calls the gateway's OpenAI-compatible `/v1/embeddings` endpoint with the same base URL and token. It works with `embed()` and `embedMany()`.
114
+
115
+ ```ts
116
+ import { neon } from "@neon/ai-sdk-provider";
117
+ import { embed, embedMany } from "ai";
118
+
119
+ const { embedding } = await embed({
120
+ model: neon.embeddingModel("qwen3-embedding-0-6b"), // or "gte-large-en"
121
+ value: "Neon branches are copy-on-write.",
122
+ });
123
+
124
+ const { embeddings } = await embedMany({
125
+ model: neon.embeddingModel("qwen3-embedding-0-6b"),
126
+ values: ["Scale to zero", "Instant restore", "Read replicas"],
127
+ providerOptions: { neon: { dimensions: 256 } },
128
+ });
129
+ ```
130
+
131
+ Both models return 1024-dimensional vectors. `qwen3-embedding-0-6b` honors `providerOptions.neon.dimensions` for shorter vectors; `gte-large-en` ignores it. The gateway accepts at most 150 inputs per request, so `embedMany()` splits larger lists into batches of 150. `NeonEmbeddingModelId` autocompletes the known ids; any other string is passed through to the gateway. `textEmbeddingModel()` is a deprecated alias.
132
+
110
133
  ## Image generation
111
134
 
112
135
  Available on OpenAI models via the Responses `image_generation` tool (there is no `generateImage()` image-model endpoint). Use `streamText` — streaming returns the image as a `tool-result` part and avoids the gateway's non-streaming response-size cap and read timeout:
@@ -210,17 +233,23 @@ So `store` is only refused on the Responses route; the same option is ignored el
210
233
 
211
234
  ## Limitations
212
235
 
213
- - The gateway does not offer image or embedding model endpoints, so `generateImage()`, `embed()`, and `embedMany()` throw `NoSuchModelError`. Image generation is available through the Responses API's built-in `image_generation` tool with `neon.tools.imageGeneration()`.
236
+ - The gateway does not offer an image model endpoint, so `generateImage()` throws `NoSuchModelError`. Image generation is available through the Responses API's built-in `image_generation` tool with `neon.tools.imageGeneration()`.
214
237
  - Results from provider-executed tools (`neon.tools.imageGeneration`, and the other Responses built-ins) are not replayed to the gateway on a later step — see [Edit a generated image](#edit-a-generated-image).
215
238
  - The Responses route is stateless, so the provider sends `store: false` and refuses `store: true`, `store: null`, `previousResponseId`, or `conversation` — see [Errors](#errors).
216
239
 
217
240
  ## End-to-end tests
218
241
 
219
- Against a live branch with AI Gateway enabled:
242
+ These run against a real gateway, so they need credentials — one of two ways:
220
243
 
221
244
  ```bash
222
- cp .env.example .env # fill NEON_AI_GATEWAY_BASE_URL + NEON_AI_GATEWAY_TOKEN from `neon env pull`
245
+ cp .env.example .env
246
+ # Either NEON_API_KEY for a throwaway org, and the suite creates a project, mints a
247
+ # credential scoped to `ai_gateway:invoke`, and deletes both afterwards.
248
+ # Or NEON_AI_GATEWAY_BASE_URL + NEON_AI_GATEWAY_TOKEN from `neon env pull`, to run
249
+ # against a branch you already have.
223
250
  pnpm test:e2e
224
251
  ```
225
252
 
226
- The matrix covers one models.dev `neon` model per family (Anthropic, OpenAI, Codex, Gemini, Meta, Alibaba, Zhipu, Thinking Machines) across `generateText`, `streamText`, `generateObject`, tool calling, and `neon.tools.imageGeneration`. `generateObject` and tool calling run on the subset of families where they are verified (see [Capabilities](#capabilities)); a family whose representative id the branch does not serve is skipped rather than failed. It also fetches the live `/v1/models` catalog and calls every currently enabled model with both AI SDK 6 and AI SDK 7. Tests are skipped when gateway env vars are absent.
253
+ A run with neither **fails**; it does not skip. The gateway is on every branch and needs no provisioning, so the API-key path is the one CI uses — no gateway token is stored as a secret.
254
+
255
+ The matrix covers one published catalog model per family (Anthropic, OpenAI, Codex, Gemini, Meta, Alibaba, Zhipu, Thinking Machines) across `generateText`, `streamText`, `generateObject`, tool calling, and `neon.tools.imageGeneration`. `generateObject` and tool calling run on the subset of families where they are verified (see [Capabilities](#capabilities)). A family whose representative id the branch does not serve skips its cases, and a single assertion fails the run listing exactly which pinned ids went missing — so a shrinking catalog is reported rather than silently reducing coverage. Model access is granted per account, so the account behind the key needs every id in `MATRIX_MODELS`. The suite also fetches the live `/v1/models` catalog and calls every currently enabled text model with `generateText` and every embedding model with `embed` and `embedMany`, on both AI SDK 6 and AI SDK 7.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { NeonAnthropicLanguageModel } from "./lib/neon-anthropic-language-model.js";
2
2
  import { NeonChatLanguageModel } from "./lib/neon-chat-language-model.js";
3
- import { NEON_MODELS_DEV_IDS, NeonChatModelId, NeonKnownModelId } from "./lib/neon-chat-options.js";
3
+ import { NeonChatModelId, NeonKnownModelId } from "./lib/neon-chat-options.js";
4
+ import { NeonEmbeddingModelId } from "./lib/neon-embedding-options.js";
4
5
  import { NeonModelCapabilities, NeonModelFamily, NeonModelRoute, getNeonModelCapabilities, getNeonModelRoute } from "./lib/neon-model-capabilities.js";
5
6
  import { NeonResponsesLanguageModel } from "./lib/neon-responses-language-model.js";
6
7
  import { NeonErrorData, NeonProvider, NeonProviderSettings, createNeon, neon } from "./lib/provider.js";
7
- export { NEON_MODELS_DEV_IDS, NeonAnthropicLanguageModel, NeonChatLanguageModel, type NeonChatModelId, type NeonErrorData, type NeonKnownModelId, type NeonModelCapabilities, type NeonModelFamily, type NeonModelRoute, type NeonProvider, type NeonProviderSettings, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
8
+ export { NeonAnthropicLanguageModel, NeonChatLanguageModel, type NeonChatModelId, type NeonEmbeddingModelId, type NeonErrorData, type NeonKnownModelId, type NeonModelCapabilities, type NeonModelFamily, type NeonModelRoute, type NeonProvider, type NeonProviderSettings, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { getNeonModelCapabilities, getNeonModelRoute } from "./lib/neon-model-capabilities.js";
2
2
  import { NeonAnthropicLanguageModel } from "./lib/neon-anthropic-language-model.js";
3
3
  import { NeonChatLanguageModel } from "./lib/neon-chat-language-model.js";
4
- import { NEON_MODELS_DEV_IDS } from "./lib/neon-chat-options.js";
5
4
  import { NeonResponsesLanguageModel } from "./lib/neon-responses-language-model.js";
6
5
  import { createNeon, neon } from "./lib/provider.js";
7
- export { NEON_MODELS_DEV_IDS, NeonAnthropicLanguageModel, NeonChatLanguageModel, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
6
+ export { NeonAnthropicLanguageModel, NeonChatLanguageModel, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
@@ -1,6 +1,6 @@
1
1
  //#region src/lib/neon-chat-options.d.ts
2
- /** The models.dev `neon` catalog (canonical, unprefixed ids). */
3
- declare const NEON_MODELS_DEV_IDS: readonly ["claude-fable-5", "claude-haiku-4-5", "claude-opus-4-1", "claude-opus-4-5", "claude-opus-4-6", "claude-opus-4-7", "claude-opus-4-8", "claude-opus-5", "claude-sonnet-4-5", "claude-sonnet-4-6", "claude-sonnet-5", "gpt-5", "gpt-5-1", "gpt-5-2", "gpt-5-3-codex", "gpt-5-4", "gpt-5-4-mini", "gpt-5-4-nano", "gpt-5-5", "gpt-5-5-pro", "gpt-5-6-luna", "gpt-5-6-sol", "gpt-5-6-terra", "gpt-5-mini", "gpt-5-nano", "gpt-oss-120b", "gpt-oss-20b", "gemini-3-1-flash-lite", "gemini-3-1-pro", "gemini-3-5-flash", "gemini-3-5-flash-lite", "gemini-3-6-flash", "gemini-3-flash", "gemma-3-12b", "llama-4-maverick", "meta-llama-3-1-8b-instruct", "meta-llama-3-3-70b-instruct", "qwen3-next-80b-a3b-instruct", "qwen35-122b-a10b", "glm-5-2", "inkling", "kimi-k3"];
2
+ /** Published catalog ids from https://neon.com/models.json (canonical, unprefixed). */
3
+ declare const NEON_MODELS_DEV_IDS: readonly ["claude-fable-5", "claude-fable-5-1", "claude-haiku-4-5", "claude-opus-4-1", "claude-opus-4-5", "claude-opus-4-6", "claude-opus-4-7", "claude-opus-4-8", "claude-opus-5", "claude-sonnet-4-5", "claude-sonnet-4-6", "claude-sonnet-5", "gpt-5", "gpt-5-1", "gpt-5-2", "gpt-5-3-codex", "gpt-5-4", "gpt-5-4-mini", "gpt-5-4-nano", "gpt-5-5", "gpt-5-5-pro", "gpt-5-6-luna", "gpt-5-6-sol", "gpt-5-6-terra", "gpt-5-mini", "gpt-5-nano", "gpt-6-astra", "gpt-oss-120b", "gpt-oss-20b", "gemini-3-1-flash-lite", "gemini-3-1-pro", "gemini-3-5-flash", "gemini-3-5-flash-lite", "gemini-3-6-flash", "gemini-3-flash", "gemma-3-12b", "llama-4-maverick", "meta-llama-3-1-8b-instruct", "meta-llama-3-3-70b-instruct", "qwen3-next-80b-a3b-instruct", "qwen35-122b-a10b", "glm-5-2", "glm-5-3-flash", "grok-4-6", "inkling", "kimi-k3"];
4
4
  /** A known Neon AI Gateway model id. */
5
5
  type NeonKnownModelId = (typeof NEON_MODELS_DEV_IDS)[number];
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"neon-chat-options.d.ts","names":[],"sources":["../../src/lib/neon-chat-options.ts"],"mappings":";;AAsBa,cAAA,mBAoDH,EAAA,SAAA,CAAA,gBAAA,EAAA,kBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,mBAAA,EAAA,mBAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,SAAA,EAAA,SAAA,EAAA,eAAA,EAAA,SAAA,EAAA,cAAA,EAAA,cAAA,EAAA,SAAA,EAAA,aAAA,EAAA,cAAA,EAAA,aAAA,EAAA,eAAA,EAAA,YAAA,EAAA,YAAA,EAAA,cAAA,EAAA,aAAA,EAAA,uBAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,uBAAA,EAAA,kBAAA,EAAA,gBAAA,EAAA,aAAA,EAAA,kBAAA,EAAA,4BAAA,EAAA,6BAAA,EAAA,6BAAA,EAAA,kBAAA,EAAA,SAAA,EAAA,SAAA,EAAA,SAAA,CAAA;AAGV;AAOY,KAPA,gBAAA,GAOe,CAAG,OAPS,mBAOO,CAAA,CAAA,MAAA,CAAA;;;;;;KAAlC,eAAA,GAAkB"}
1
+ {"version":3,"file":"neon-chat-options.d.ts","names":[],"sources":["../../src/lib/neon-chat-options.ts"],"mappings":";;AAgBa,cAAA,mBAyDH,EAAA,SAAA,CAAA,gBAAA,EAAA,kBAAA,EAAA,kBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,mBAAA,EAAA,mBAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,SAAA,EAAA,SAAA,EAAA,eAAA,EAAA,SAAA,EAAA,cAAA,EAAA,cAAA,EAAA,SAAA,EAAA,aAAA,EAAA,cAAA,EAAA,aAAA,EAAA,eAAA,EAAA,YAAA,EAAA,YAAA,EAAA,aAAA,EAAA,cAAA,EAAA,aAAA,EAAA,uBAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,uBAAA,EAAA,kBAAA,EAAA,gBAAA,EAAA,aAAA,EAAA,kBAAA,EAAA,4BAAA,EAAA,6BAAA,EAAA,6BAAA,EAAA,kBAAA,EAAA,SAAA,EAAA,eAAA,EAAA,UAAA,EAAA,SAAA,EAAA,SAAA,CAAA;AAGV;AAOY,KAPA,gBAAA,GAOe,CAAG,OAPS,mBAOO,CAAA,CAAA,MAAA,CAAA;;;;;;KAAlC,eAAA,GAAkB"}
@@ -1,7 +1,8 @@
1
1
  //#region src/lib/neon-chat-options.ts
2
- /** The models.dev `neon` catalog (canonical, unprefixed ids). */
2
+ /** Published catalog ids from https://neon.com/models.json (canonical, unprefixed). */
3
3
  const NEON_MODELS_DEV_IDS = [
4
4
  "claude-fable-5",
5
+ "claude-fable-5-1",
5
6
  "claude-haiku-4-5",
6
7
  "claude-opus-4-1",
7
8
  "claude-opus-4-5",
@@ -26,6 +27,7 @@ const NEON_MODELS_DEV_IDS = [
26
27
  "gpt-5-6-terra",
27
28
  "gpt-5-mini",
28
29
  "gpt-5-nano",
30
+ "gpt-6-astra",
29
31
  "gpt-oss-120b",
30
32
  "gpt-oss-20b",
31
33
  "gemini-3-1-flash-lite",
@@ -41,6 +43,8 @@ const NEON_MODELS_DEV_IDS = [
41
43
  "qwen3-next-80b-a3b-instruct",
42
44
  "qwen35-122b-a10b",
43
45
  "glm-5-2",
46
+ "glm-5-3-flash",
47
+ "grok-4-6",
44
48
  "inkling",
45
49
  "kimi-k3"
46
50
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"neon-chat-options.js","names":[],"sources":["../../src/lib/neon-chat-options.ts"],"sourcesContent":["// Models served by the Neon AI Gateway. Anthropic and OpenAI models are routed\n// to their provider-native endpoints; everything else uses the unified MLflow\n// (OpenAI-compatible) endpoint.\n//\n// Ids use the canonical Neon (unprefixed) form, matching the `neon` provider on\n// models.dev. The gateway also accepts the legacy `databricks-` prefixed form\n// (the `databricks` provider on models.dev), which still resolves via the\n// `(string & {})` fallback on `NeonChatModelId`.\n//\n// `NEON_MODELS_DEV_IDS` mirrors the models.dev `neon` catalog exactly — the\n// `neon-catalog-drift.test.ts` maintainer check fails if the two diverge. The\n// authoritative, always-current catalog is shown in the Neon Console under the\n// branch's \"AI Gateway\" tab.\n//\n// There is deliberately no second list for ids the gateway serves ahead of\n// models.dev. Such an id already works — `NeonChatModelId` accepts any string —\n// so a hand-maintained list would buy autocomplete for a few days at the cost of\n// a surface that silently rots, which is what happened last time. The fix for a\n// gateway model missing here is to add it to models.dev; Orbit's catalog-parity\n// job reports that gap daily.\n\n/** The models.dev `neon` catalog (canonical, unprefixed ids). */\nexport const NEON_MODELS_DEV_IDS = [\n\t// Anthropic (native Messages API)\n\t\"claude-fable-5\",\n\t\"claude-haiku-4-5\",\n\t\"claude-opus-4-1\",\n\t\"claude-opus-4-5\",\n\t\"claude-opus-4-6\",\n\t\"claude-opus-4-7\",\n\t\"claude-opus-4-8\",\n\t\"claude-opus-5\",\n\t\"claude-sonnet-4-5\",\n\t\"claude-sonnet-4-6\",\n\t\"claude-sonnet-5\",\n\t// OpenAI (native Responses API)\n\t\"gpt-5\",\n\t\"gpt-5-1\",\n\t\"gpt-5-2\",\n\t\"gpt-5-3-codex\",\n\t\"gpt-5-4\",\n\t\"gpt-5-4-mini\",\n\t\"gpt-5-4-nano\",\n\t\"gpt-5-5\",\n\t\"gpt-5-5-pro\",\n\t\"gpt-5-6-luna\",\n\t\"gpt-5-6-sol\",\n\t\"gpt-5-6-terra\",\n\t\"gpt-5-mini\",\n\t\"gpt-5-nano\",\n\t// OpenAI open-weight (unified MLflow endpoint)\n\t\"gpt-oss-120b\",\n\t\"gpt-oss-20b\",\n\t// Google (unified MLflow endpoint)\n\t\"gemini-3-1-flash-lite\",\n\t\"gemini-3-1-pro\",\n\t\"gemini-3-5-flash\",\n\t\"gemini-3-5-flash-lite\",\n\t\"gemini-3-6-flash\",\n\t\"gemini-3-flash\",\n\t\"gemma-3-12b\",\n\t// Meta (unified MLflow endpoint)\n\t\"llama-4-maverick\",\n\t\"meta-llama-3-1-8b-instruct\",\n\t\"meta-llama-3-3-70b-instruct\",\n\t// Alibaba (unified MLflow endpoint)\n\t\"qwen3-next-80b-a3b-instruct\",\n\t\"qwen35-122b-a10b\",\n\t// Zhipu (unified MLflow endpoint)\n\t\"glm-5-2\",\n\t// Thinking Machines (unified MLflow endpoint)\n\t\"inkling\",\n\t// Moonshot (unified MLflow endpoint)\n\t\"kimi-k3\",\n] as const;\n\n/** A known Neon AI Gateway model id. */\nexport type NeonKnownModelId = (typeof NEON_MODELS_DEV_IDS)[number];\n\n/**\n * A Neon AI Gateway model id. Known ids are listed for autocomplete; any other\n * id (e.g. a brand-new model, or the legacy `databricks-` prefixed form) is\n * accepted via the `(string & {})` fallback.\n */\nexport type NeonChatModelId = NeonKnownModelId | (string & {});\n"],"mappings":";;AAsBA,MAAa,sBAAsB;CAElC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CAEA;CAEA;CAEA;AACD"}
1
+ {"version":3,"file":"neon-chat-options.js","names":[],"sources":["../../src/lib/neon-chat-options.ts"],"sourcesContent":["// Models served by the Neon AI Gateway. Anthropic and OpenAI models are routed\n// to their provider-native endpoints; everything else uses the unified MLflow\n// (OpenAI-compatible) endpoint.\n//\n// Ids use the canonical Neon (unprefixed) form, matching\n// https://neon.com/models.json. The gateway also accepts the legacy\n// `databricks-` prefixed form, which still resolves via the `(string & {})`\n// fallback on `NeonChatModelId`.\n//\n// `NEON_MODELS_DEV_IDS` mirrors neon.com/models.json `neon.models` exactly —\n// `neon-catalog-drift.test.ts` fails if the two diverge. A branch's live list\n// is `GET $NEON_AI_GATEWAY_BASE_URL/v1/models`. There is deliberately no second\n// list for ids the gateway serves ahead of the published catalog: such an id\n// already works because `NeonChatModelId` accepts any string.\n\n/** Published catalog ids from https://neon.com/models.json (canonical, unprefixed). */\nexport const NEON_MODELS_DEV_IDS = [\n\t// Anthropic (native Messages API)\n\t\"claude-fable-5\",\n\t\"claude-fable-5-1\",\n\t\"claude-haiku-4-5\",\n\t\"claude-opus-4-1\",\n\t\"claude-opus-4-5\",\n\t\"claude-opus-4-6\",\n\t\"claude-opus-4-7\",\n\t\"claude-opus-4-8\",\n\t\"claude-opus-5\",\n\t\"claude-sonnet-4-5\",\n\t\"claude-sonnet-4-6\",\n\t\"claude-sonnet-5\",\n\t// OpenAI (native Responses API)\n\t\"gpt-5\",\n\t\"gpt-5-1\",\n\t\"gpt-5-2\",\n\t\"gpt-5-3-codex\",\n\t\"gpt-5-4\",\n\t\"gpt-5-4-mini\",\n\t\"gpt-5-4-nano\",\n\t\"gpt-5-5\",\n\t\"gpt-5-5-pro\",\n\t\"gpt-5-6-luna\",\n\t\"gpt-5-6-sol\",\n\t\"gpt-5-6-terra\",\n\t\"gpt-5-mini\",\n\t\"gpt-5-nano\",\n\t\"gpt-6-astra\",\n\t// OpenAI open-weight (unified MLflow endpoint)\n\t\"gpt-oss-120b\",\n\t\"gpt-oss-20b\",\n\t// Google (unified MLflow endpoint)\n\t\"gemini-3-1-flash-lite\",\n\t\"gemini-3-1-pro\",\n\t\"gemini-3-5-flash\",\n\t\"gemini-3-5-flash-lite\",\n\t\"gemini-3-6-flash\",\n\t\"gemini-3-flash\",\n\t\"gemma-3-12b\",\n\t// Meta (unified MLflow endpoint)\n\t\"llama-4-maverick\",\n\t\"meta-llama-3-1-8b-instruct\",\n\t\"meta-llama-3-3-70b-instruct\",\n\t// Alibaba (unified MLflow endpoint)\n\t\"qwen3-next-80b-a3b-instruct\",\n\t\"qwen35-122b-a10b\",\n\t// Zhipu (unified MLflow endpoint)\n\t\"glm-5-2\",\n\t\"glm-5-3-flash\",\n\t// xAI (unified MLflow endpoint)\n\t\"grok-4-6\",\n\t// Thinking Machines (unified MLflow endpoint)\n\t\"inkling\",\n\t// Moonshot (unified MLflow endpoint)\n\t\"kimi-k3\",\n] as const;\n\n/** A known Neon AI Gateway model id. */\nexport type NeonKnownModelId = (typeof NEON_MODELS_DEV_IDS)[number];\n\n/**\n * A Neon AI Gateway model id. Known ids are listed for autocomplete; any other\n * id (e.g. a brand-new model, or the legacy `databricks-` prefixed form) is\n * accepted via the `(string & {})` fallback.\n */\nexport type NeonChatModelId = NeonKnownModelId | (string & {});\n"],"mappings":";;AAgBA,MAAa,sBAAsB;CAElC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CAEA;CACA;CAEA;CAEA;CAEA;AACD"}
@@ -0,0 +1,13 @@
1
+ //#region src/lib/neon-embedding-options.d.ts
2
+ /** Published embedding model ids from https://neon.com/models.json. */
3
+ declare const NEON_EMBEDDING_MODEL_IDS: readonly ["gte-large-en", "qwen3-embedding-0-6b"];
4
+ /**
5
+ * A Neon AI Gateway embedding model id. Known ids are listed for autocomplete;
6
+ * any other id is accepted via the `(string & {})` fallback.
7
+ */
8
+ type NeonEmbeddingModelId = (typeof NEON_EMBEDDING_MODEL_IDS)[number] | (string & {});
9
+ /** The gateway rejects an embeddings request with more inputs than this. */
10
+ declare const NEON_MAX_EMBEDDINGS_PER_CALL = 150;
11
+ //#endregion
12
+ export { NEON_EMBEDDING_MODEL_IDS, NEON_MAX_EMBEDDINGS_PER_CALL, NeonEmbeddingModelId };
13
+ //# sourceMappingURL=neon-embedding-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neon-embedding-options.d.ts","names":[],"sources":["../../src/lib/neon-embedding-options.ts"],"mappings":";;AAMa,cAAA,wBAGH,EAAA,SAAA,CAAA,cAAA,EAAA,sBAAA,CAAA;AAMV;AAKA;;;KALY,oBAAA,WACD;;cAIE,4BAAA"}
@@ -0,0 +1,9 @@
1
+ //#region src/lib/neon-embedding-options.ts
2
+ /** Published embedding model ids from https://neon.com/models.json. */
3
+ const NEON_EMBEDDING_MODEL_IDS = ["gte-large-en", "qwen3-embedding-0-6b"];
4
+ /** The gateway rejects an embeddings request with more inputs than this. */
5
+ const NEON_MAX_EMBEDDINGS_PER_CALL = 150;
6
+ //#endregion
7
+ export { NEON_EMBEDDING_MODEL_IDS, NEON_MAX_EMBEDDINGS_PER_CALL };
8
+
9
+ //# sourceMappingURL=neon-embedding-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neon-embedding-options.js","names":[],"sources":["../../src/lib/neon-embedding-options.ts"],"sourcesContent":["// Embedding models served on the Neon AI Gateway's OpenAI-compatible\n// `/v1/embeddings` endpoint. Mirrors the `\"type\": \"embedding\"` entries of\n// https://neon.com/models.json — `neon-catalog-drift.test.ts` fails if the two\n// diverge.\n\n/** Published embedding model ids from https://neon.com/models.json. */\nexport const NEON_EMBEDDING_MODEL_IDS = [\n\t\"gte-large-en\",\n\t\"qwen3-embedding-0-6b\",\n] as const;\n\n/**\n * A Neon AI Gateway embedding model id. Known ids are listed for autocomplete;\n * any other id is accepted via the `(string & {})` fallback.\n */\nexport type NeonEmbeddingModelId =\n\t| (typeof NEON_EMBEDDING_MODEL_IDS)[number]\n\t| (string & {});\n\n/** The gateway rejects an embeddings request with more inputs than this. */\nexport const NEON_MAX_EMBEDDINGS_PER_CALL = 150;\n"],"mappings":";;AAMA,MAAa,2BAA2B,CACvC,gBACA,sBACD;;AAWA,MAAa,+BAA+B"}
@@ -106,6 +106,16 @@ function getNeonModelCapabilities(modelId) {
106
106
  supportsReasoningEffort: true
107
107
  };
108
108
  }
109
+ if (canonicalId(id) === "gpt-6-astra") return {
110
+ family: "openai",
111
+ supportsTemperature: false,
112
+ supportsTopP: true,
113
+ temperatureTopPMutuallyExclusive: false,
114
+ supportsPenalties: true,
115
+ supportsSeed: true,
116
+ supportsStopSequences: true,
117
+ supportsReasoningEffort: true
118
+ };
109
119
  if (id.includes("llama")) return {
110
120
  family: "meta",
111
121
  supportsTemperature: true,
@@ -1 +1 @@
1
- {"version":3,"file":"neon-model-capabilities.js","names":[],"sources":["../../src/lib/neon-model-capabilities.ts"],"sourcesContent":["export type NeonModelFamily =\n\t| \"anthropic\"\n\t| \"google\"\n\t| \"openai\"\n\t| \"meta\"\n\t| \"other\";\n\n/**\n * Which gateway endpoint a model should be routed to.\n *\n * - `anthropic`: native Messages API — streaming structured output + native reasoning.\n * - `openai`: native Responses API — models served only natively (e.g. Codex),\n * native reasoning, and the image-generation tool.\n * - `mlflow`: the unified, OpenAI-compatible endpoint (the fallback). Gemini is\n * routed here because the gateway's native Gemini endpoint does not support\n * streaming (`streamGenerateContent` is rejected).\n */\nexport type NeonModelRoute = \"anthropic\" | \"openai\" | \"mlflow\";\n\nexport function getNeonModelRoute(modelId: string): NeonModelRoute {\n\tconst id = modelId.toLowerCase();\n\tif (id.includes(\"claude\")) {\n\t\treturn \"anthropic\";\n\t}\n\t// OpenAI proprietary models (gpt-4/gpt-5 families, Codex) are served only via\n\t// the native Responses API. `gpt-oss` is open-weight and served on the\n\t// unified chat endpoint, so it is intentionally excluded here.\n\tif (\n\t\t(id.includes(\"gpt-\") && !id.includes(\"gpt-oss\")) ||\n\t\tid.includes(\"codex\")\n\t) {\n\t\treturn \"openai\";\n\t}\n\treturn \"mlflow\";\n}\n\nexport interface NeonModelCapabilities {\n\tfamily: NeonModelFamily;\n\tsupportsTemperature: boolean;\n\tsupportsTopP: boolean;\n\t/**\n\t * Whether `temperature` and `topP` may be sent together. Anthropic models\n\t * accept only one of the two.\n\t */\n\ttemperatureTopPMutuallyExclusive: boolean;\n\tsupportsPenalties: boolean;\n\tsupportsSeed: boolean;\n\tsupportsStopSequences: boolean;\n\tsupportsReasoningEffort: boolean;\n\t/**\n\t * True when a `claude-*` id could not be parsed into a version and was\n\t * assumed to be new. The sampling restriction is then a precaution rather\n\t * than something measured, and the warning says so instead of asserting a\n\t * capability nobody checked.\n\t */\n\tclaudeSamplingUnrecognized?: boolean;\n}\n\n/**\n * Gemini models the gateway rejects penalties on, by measurement. Their older\n * siblings accept them, so this cannot be derived from the id.\n *\n * Matched exactly rather than by substring: `gemini-3-6-flash` is a prefix of\n * any `gemini-3-6-flash-*` id Google may ship next, and inheriting a measured\n * restriction on the strength of a shared prefix is how the Gemini rule was\n * wrong for these two in the first place.\n */\nconst GEMINI_NO_PENALTIES = new Set([\n\t\"gemini-3-5-flash-lite\",\n\t\"gemini-3-6-flash\",\n]);\n\n/** Gemini models that reject `temperature` and `topP` outright. */\nconst GEMINI_NO_SAMPLING = new Set([\"gemini-3-6-flash\"]);\n\n/**\n * GPT-5 ids that reject `temperature` despite carrying a minor version.\n *\n * The version rule below reads `gpt-5-5-pro` as 5.5 and concludes it takes\n * sampling parameters. It does not — the Responses API answers\n * `Unsupported parameter: temperature`.\n */\nconst GPT5_NO_SAMPLING = new Set([\"gpt-5-5-pro\"]);\n\n/**\n * The gateway accepts an optional `databricks-` prefix on any id, so strip it\n * before an exact-match lookup. Prefix rules elsewhere in this file tolerate it\n * incidentally; exact matches do not.\n */\nfunction canonicalId(id: string): string {\n\treturn id.startsWith(\"databricks-\") ? id.slice(\"databricks-\".length) : id;\n}\n\nconst PERMISSIVE: Omit<NeonModelCapabilities, \"family\"> = {\n\tsupportsTemperature: true,\n\tsupportsTopP: true,\n\ttemperatureTopPMutuallyExclusive: false,\n\tsupportsPenalties: true,\n\tsupportsSeed: true,\n\tsupportsStopSequences: true,\n\tsupportsReasoningEffort: true,\n};\n\n/**\n * Whether a Claude id predates the 4.7 cutoff where the gateway stopped\n * accepting `temperature` and `top_p`. Ids look like `claude-<tier>-<major>` or\n * `claude-<tier>-<major>-<minor>`, optionally behind the legacy `databricks-`\n * prefix. An id we cannot parse is treated as new, which is the safe direction.\n */\nfunction claudeVersion(id: string): { major: number; minor: number } | null {\n\t// Anchored: `claude-opus-4-beta` and `claude-opus-4.7` must not match their\n\t// leading digits and be read as old models. A dated id such as\n\t// `claude-3-5-sonnet-20241022` does not parse either, and is reported as\n\t// unrecognised rather than silently classified.\n\tconst match = /^(?:databricks-)?claude-[a-z]+-(\\d+)(?:-(\\d+))?$/.exec(id);\n\tif (!match) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tmajor: Number(match[1]),\n\t\tminor: match[2] === undefined ? 0 : Number(match[2]),\n\t};\n}\n\n/**\n * Heuristic, prefix-based capability detection for MLflow-routed Neon models.\n *\n * The unified endpoint proxies to heterogeneous upstream providers, each of\n * which accepts a different subset of the OpenAI-style parameters the AI SDK\n * emits. Sending a parameter an upstream rejects results in a hard `400`, so we\n * strip the parameters a family is known to reject and surface a warning\n * instead. Unknown/untested models stay permissive (passed through unchanged).\n */\nexport function getNeonModelCapabilities(\n\tmodelId: string,\n): NeonModelCapabilities {\n\tconst id = modelId.toLowerCase();\n\n\t// Anthropic (Claude): rejects penalties and seed, accepts only one of\n\t// temperature/topP, and rejects the OpenAI `reasoning_effort` field.\n\t//\n\t// From Claude 4.7 onward the gateway also rejects non-default sampling\n\t// entirely — `does not support the temperature parameter` /\n\t// `does not support sampling parameters: top_p` — because those models are\n\t// steered with `output_config.effort` instead. Measured: 4.1/4.5/4.6 accept\n\t// both, 4.7/4.8/5 reject both, and every id accepts the default\n\t// `temperature: 1.0`. A version comparison rather than a list of ids, so a\n\t// future Claude inherits the restriction: dropping a parameter the model\n\t// would have accepted costs a warning, claiming one it rejects costs a 400.\n\tif (id.includes(\"claude\")) {\n\t\tconst version = claudeVersion(id);\n\t\tconst sampling =\n\t\t\tversion !== null &&\n\t\t\t(version.major < 4 || (version.major === 4 && version.minor <= 6));\n\t\treturn {\n\t\t\tfamily: \"anthropic\",\n\t\t\tsupportsTemperature: sampling,\n\t\t\tsupportsTopP: sampling,\n\t\t\ttemperatureTopPMutuallyExclusive: true,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t\tsupportsStopSequences: true,\n\t\t\tsupportsReasoningEffort: false,\n\t\t\t...(version === null ? { claudeSamplingUnrecognized: true } : {}),\n\t\t};\n\t}\n\n\t// Google (Gemini): accepts standard sampling params, and does take\n\t// `reasoning_effort` — the gateway maps it onto Gemini's thinking config.\n\t// Measured on gemini-3-6-flash: `minimal` produces 0 reasoning tokens and\n\t// `high` produces 310, against 130 with the field absent.\n\t//\n\t// Newer Gemini models are stricter, and not in a way the id predicts: the\n\t// gateway rejects penalties on `gemini-3-5-flash-lite` while accepting them\n\t// on `gemini-3-1-flash-lite`, so neither the version nor the `-lite` suffix\n\t// is the rule. `gemini-3-6-flash` refuses temperature and topP outright\n\t// (\"does not support the temperature parameter\"). Both are listed by id\n\t// because that is what was measured; a new Gemini model inherits the\n\t// permissive default until someone measures it.\n\tif (id.includes(\"gemini\")) {\n\t\tconst canonical = canonicalId(id);\n\t\tconst acceptsSampling = !GEMINI_NO_SAMPLING.has(canonical);\n\t\treturn {\n\t\t\tfamily: \"google\",\n\t\t\t...PERMISSIVE,\n\t\t\tsupportsTemperature: acceptsSampling,\n\t\t\tsupportsTopP: acceptsSampling,\n\t\t\tsupportsPenalties: !GEMINI_NO_PENALTIES.has(canonical),\n\t\t};\n\t}\n\n\t// OpenAI GPT-5 reasoning family (routed to the native Responses API). The\n\t// Responses model strips parameters the Responses API doesn't accept\n\t// (penalties, seed, stop), but its reasoning-model detection matches the bare\n\t// model id (`gpt-5`), which the gateway's optional `databricks-` prefix can\n\t// defeat. So we only handle the temperature/topP restriction here: the\n\t// original gpt-5 / gpt-5-mini / gpt-5-nano require the default temperature and\n\t// reject topP, while gpt-5.1+ (a minor version digit follows) accept them\n\t// again. The regex matches both prefixed and unprefixed ids.\n\tif (/gpt-5/.test(id)) {\n\t\tconst hasMinorVersion =\n\t\t\t/gpt-5[.-]\\d/.test(id) && !GPT5_NO_SAMPLING.has(canonicalId(id));\n\t\treturn {\n\t\t\tfamily: \"openai\",\n\t\t\tsupportsTemperature: hasMinorVersion,\n\t\t\tsupportsTopP: hasMinorVersion,\n\t\t\ttemperatureTopPMutuallyExclusive: false,\n\t\t\tsupportsPenalties: true,\n\t\t\tsupportsSeed: true,\n\t\t\tsupportsStopSequences: true,\n\t\t\tsupportsReasoningEffort: true,\n\t\t};\n\t}\n\n\t// Meta (Llama): rejects penalties and seed; accepts sampling params and stop.\n\tif (id.includes(\"llama\")) {\n\t\treturn {\n\t\t\tfamily: \"meta\",\n\t\t\tsupportsTemperature: true,\n\t\t\tsupportsTopP: true,\n\t\t\ttemperatureTopPMutuallyExclusive: false,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t\tsupportsStopSequences: true,\n\t\t\tsupportsReasoningEffort: true,\n\t\t};\n\t}\n\n\t// Everything else on the unified endpoint rejects penalties with\n\t// `parameter \"frequency_penalty\" must be equal to 0`. Only the older Gemini\n\t// models, handled above, accept them. Seed and stop vary, so each family\n\t// carries what was measured rather than inheriting a guess.\n\tif (id.includes(\"gpt-oss\")) {\n\t\treturn {\n\t\t\tfamily: \"other\",\n\t\t\t...PERMISSIVE,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t\tsupportsStopSequences: false,\n\t\t};\n\t}\n\n\tif (id.includes(\"qwen\") || id.includes(\"gemma\")) {\n\t\treturn {\n\t\t\tfamily: \"other\",\n\t\t\t...PERMISSIVE,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t};\n\t}\n\n\tif (id.includes(\"glm\") || id.includes(\"inkling\") || id.includes(\"kimi\")) {\n\t\treturn { family: \"other\", ...PERMISSIVE, supportsPenalties: false };\n\t}\n\n\treturn { family: \"other\", ...PERMISSIVE };\n}\n"],"mappings":";AAmBA,SAAgB,kBAAkB,SAAiC;CAClE,MAAM,KAAK,QAAQ,YAAY;CAC/B,IAAI,GAAG,SAAS,QAAQ,GACvB,OAAO;CAKR,IACE,GAAG,SAAS,MAAM,KAAK,CAAC,GAAG,SAAS,SAAS,KAC9C,GAAG,SAAS,OAAO,GAEnB,OAAO;CAER,OAAO;AACR;;;;;;;;;;AAiCA,MAAM,sCAAsB,IAAI,IAAI,CACnC,yBACA,kBACD,CAAC;;AAGD,MAAM,qCAAqB,IAAI,IAAI,CAAC,kBAAkB,CAAC;;;;;;;;AASvD,MAAM,mCAAmB,IAAI,IAAI,CAAC,aAAa,CAAC;;;;;;AAOhD,SAAS,YAAY,IAAoB;CACxC,OAAO,GAAG,WAAW,aAAa,IAAI,GAAG,MAAM,EAAoB,IAAI;AACxE;AAEA,MAAM,aAAoD;CACzD,qBAAqB;CACrB,cAAc;CACd,kCAAkC;CAClC,mBAAmB;CACnB,cAAc;CACd,uBAAuB;CACvB,yBAAyB;AAC1B;;;;;;;AAQA,SAAS,cAAc,IAAqD;CAK3E,MAAM,QAAQ,mDAAmD,KAAK,EAAE;CACxE,IAAI,CAAC,OACJ,OAAO;CAER,OAAO;EACN,OAAO,OAAO,MAAM,EAAE;EACtB,OAAO,MAAM,OAAO,KAAA,IAAY,IAAI,OAAO,MAAM,EAAE;CACpD;AACD;;;;;;;;;;AAWA,SAAgB,yBACf,SACwB;CACxB,MAAM,KAAK,QAAQ,YAAY;CAa/B,IAAI,GAAG,SAAS,QAAQ,GAAG;EAC1B,MAAM,UAAU,cAAc,EAAE;EAChC,MAAM,WACL,YAAY,SACX,QAAQ,QAAQ,KAAM,QAAQ,UAAU,KAAK,QAAQ,SAAS;EAChE,OAAO;GACN,QAAQ;GACR,qBAAqB;GACrB,cAAc;GACd,kCAAkC;GAClC,mBAAmB;GACnB,cAAc;GACd,uBAAuB;GACvB,yBAAyB;GACzB,GAAI,YAAY,OAAO,EAAE,4BAA4B,KAAK,IAAI,CAAC;EAChE;CACD;CAcA,IAAI,GAAG,SAAS,QAAQ,GAAG;EAC1B,MAAM,YAAY,YAAY,EAAE;EAChC,MAAM,kBAAkB,CAAC,mBAAmB,IAAI,SAAS;EACzD,OAAO;GACN,QAAQ;GACR,GAAG;GACH,qBAAqB;GACrB,cAAc;GACd,mBAAmB,CAAC,oBAAoB,IAAI,SAAS;EACtD;CACD;CAUA,IAAI,QAAQ,KAAK,EAAE,GAAG;EACrB,MAAM,kBACL,cAAc,KAAK,EAAE,KAAK,CAAC,iBAAiB,IAAI,YAAY,EAAE,CAAC;EAChE,OAAO;GACN,QAAQ;GACR,qBAAqB;GACrB,cAAc;GACd,kCAAkC;GAClC,mBAAmB;GACnB,cAAc;GACd,uBAAuB;GACvB,yBAAyB;EAC1B;CACD;CAGA,IAAI,GAAG,SAAS,OAAO,GACtB,OAAO;EACN,QAAQ;EACR,qBAAqB;EACrB,cAAc;EACd,kCAAkC;EAClC,mBAAmB;EACnB,cAAc;EACd,uBAAuB;EACvB,yBAAyB;CAC1B;CAOD,IAAI,GAAG,SAAS,SAAS,GACxB,OAAO;EACN,QAAQ;EACR,GAAG;EACH,mBAAmB;EACnB,cAAc;EACd,uBAAuB;CACxB;CAGD,IAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,OAAO,GAC7C,OAAO;EACN,QAAQ;EACR,GAAG;EACH,mBAAmB;EACnB,cAAc;CACf;CAGD,IAAI,GAAG,SAAS,KAAK,KAAK,GAAG,SAAS,SAAS,KAAK,GAAG,SAAS,MAAM,GACrE,OAAO;EAAE,QAAQ;EAAS,GAAG;EAAY,mBAAmB;CAAM;CAGnE,OAAO;EAAE,QAAQ;EAAS,GAAG;CAAW;AACzC"}
1
+ {"version":3,"file":"neon-model-capabilities.js","names":[],"sources":["../../src/lib/neon-model-capabilities.ts"],"sourcesContent":["export type NeonModelFamily =\n\t| \"anthropic\"\n\t| \"google\"\n\t| \"openai\"\n\t| \"meta\"\n\t| \"other\";\n\n/**\n * Which gateway endpoint a model should be routed to.\n *\n * - `anthropic`: native Messages API — streaming structured output + native reasoning.\n * - `openai`: native Responses API — models served only natively (e.g. Codex),\n * native reasoning, and the image-generation tool.\n * - `mlflow`: the unified, OpenAI-compatible endpoint (the fallback). Gemini is\n * routed here because the gateway's native Gemini endpoint does not support\n * streaming (`streamGenerateContent` is rejected).\n */\nexport type NeonModelRoute = \"anthropic\" | \"openai\" | \"mlflow\";\n\nexport function getNeonModelRoute(modelId: string): NeonModelRoute {\n\tconst id = modelId.toLowerCase();\n\tif (id.includes(\"claude\")) {\n\t\treturn \"anthropic\";\n\t}\n\t// OpenAI proprietary models (gpt-4/gpt-5 families, Codex) are served only via\n\t// the native Responses API. `gpt-oss` is open-weight and served on the\n\t// unified chat endpoint, so it is intentionally excluded here.\n\tif (\n\t\t(id.includes(\"gpt-\") && !id.includes(\"gpt-oss\")) ||\n\t\tid.includes(\"codex\")\n\t) {\n\t\treturn \"openai\";\n\t}\n\treturn \"mlflow\";\n}\n\nexport interface NeonModelCapabilities {\n\tfamily: NeonModelFamily;\n\tsupportsTemperature: boolean;\n\tsupportsTopP: boolean;\n\t/**\n\t * Whether `temperature` and `topP` may be sent together. Anthropic models\n\t * accept only one of the two.\n\t */\n\ttemperatureTopPMutuallyExclusive: boolean;\n\tsupportsPenalties: boolean;\n\tsupportsSeed: boolean;\n\tsupportsStopSequences: boolean;\n\tsupportsReasoningEffort: boolean;\n\t/**\n\t * True when a `claude-*` id could not be parsed into a version and was\n\t * assumed to be new. The sampling restriction is then a precaution rather\n\t * than something measured, and the warning says so instead of asserting a\n\t * capability nobody checked.\n\t */\n\tclaudeSamplingUnrecognized?: boolean;\n}\n\n/**\n * Gemini models the gateway rejects penalties on, by measurement. Their older\n * siblings accept them, so this cannot be derived from the id.\n *\n * Matched exactly rather than by substring: `gemini-3-6-flash` is a prefix of\n * any `gemini-3-6-flash-*` id Google may ship next, and inheriting a measured\n * restriction on the strength of a shared prefix is how the Gemini rule was\n * wrong for these two in the first place.\n */\nconst GEMINI_NO_PENALTIES = new Set([\n\t\"gemini-3-5-flash-lite\",\n\t\"gemini-3-6-flash\",\n]);\n\n/** Gemini models that reject `temperature` and `topP` outright. */\nconst GEMINI_NO_SAMPLING = new Set([\"gemini-3-6-flash\"]);\n\n/**\n * GPT-5 ids that reject `temperature` despite carrying a minor version.\n *\n * The version rule below reads `gpt-5-5-pro` as 5.5 and concludes it takes\n * sampling parameters. It does not — the Responses API answers\n * `Unsupported parameter: temperature`.\n */\nconst GPT5_NO_SAMPLING = new Set([\"gpt-5-5-pro\"]);\n\n/**\n * The gateway accepts an optional `databricks-` prefix on any id, so strip it\n * before an exact-match lookup. Prefix rules elsewhere in this file tolerate it\n * incidentally; exact matches do not.\n */\nfunction canonicalId(id: string): string {\n\treturn id.startsWith(\"databricks-\") ? id.slice(\"databricks-\".length) : id;\n}\n\nconst PERMISSIVE: Omit<NeonModelCapabilities, \"family\"> = {\n\tsupportsTemperature: true,\n\tsupportsTopP: true,\n\ttemperatureTopPMutuallyExclusive: false,\n\tsupportsPenalties: true,\n\tsupportsSeed: true,\n\tsupportsStopSequences: true,\n\tsupportsReasoningEffort: true,\n};\n\n/**\n * Whether a Claude id predates the 4.7 cutoff where the gateway stopped\n * accepting `temperature` and `top_p`. Ids look like `claude-<tier>-<major>` or\n * `claude-<tier>-<major>-<minor>`, optionally behind the legacy `databricks-`\n * prefix. An id we cannot parse is treated as new, which is the safe direction.\n */\nfunction claudeVersion(id: string): { major: number; minor: number } | null {\n\t// Anchored: `claude-opus-4-beta` and `claude-opus-4.7` must not match their\n\t// leading digits and be read as old models. A dated id such as\n\t// `claude-3-5-sonnet-20241022` does not parse either, and is reported as\n\t// unrecognised rather than silently classified.\n\tconst match = /^(?:databricks-)?claude-[a-z]+-(\\d+)(?:-(\\d+))?$/.exec(id);\n\tif (!match) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tmajor: Number(match[1]),\n\t\tminor: match[2] === undefined ? 0 : Number(match[2]),\n\t};\n}\n\n/**\n * Heuristic, prefix-based capability detection for MLflow-routed Neon models.\n *\n * The unified endpoint proxies to heterogeneous upstream providers, each of\n * which accepts a different subset of the OpenAI-style parameters the AI SDK\n * emits. Sending a parameter an upstream rejects results in a hard `400`, so we\n * strip the parameters a family is known to reject and surface a warning\n * instead. Unknown/untested models stay permissive (passed through unchanged).\n */\nexport function getNeonModelCapabilities(\n\tmodelId: string,\n): NeonModelCapabilities {\n\tconst id = modelId.toLowerCase();\n\n\t// Anthropic (Claude): rejects penalties and seed, accepts only one of\n\t// temperature/topP, and rejects the OpenAI `reasoning_effort` field.\n\t//\n\t// From Claude 4.7 onward the gateway also rejects non-default sampling\n\t// entirely — `does not support the temperature parameter` /\n\t// `does not support sampling parameters: top_p` — because those models are\n\t// steered with `output_config.effort` instead. Measured: 4.1/4.5/4.6 accept\n\t// both, 4.7/4.8/5 reject both, and every id accepts the default\n\t// `temperature: 1.0`. A version comparison rather than a list of ids, so a\n\t// future Claude inherits the restriction: dropping a parameter the model\n\t// would have accepted costs a warning, claiming one it rejects costs a 400.\n\tif (id.includes(\"claude\")) {\n\t\tconst version = claudeVersion(id);\n\t\tconst sampling =\n\t\t\tversion !== null &&\n\t\t\t(version.major < 4 || (version.major === 4 && version.minor <= 6));\n\t\treturn {\n\t\t\tfamily: \"anthropic\",\n\t\t\tsupportsTemperature: sampling,\n\t\t\tsupportsTopP: sampling,\n\t\t\ttemperatureTopPMutuallyExclusive: true,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t\tsupportsStopSequences: true,\n\t\t\tsupportsReasoningEffort: false,\n\t\t\t...(version === null ? { claudeSamplingUnrecognized: true } : {}),\n\t\t};\n\t}\n\n\t// Google (Gemini): accepts standard sampling params, and does take\n\t// `reasoning_effort` — the gateway maps it onto Gemini's thinking config.\n\t// Measured on gemini-3-6-flash: `minimal` produces 0 reasoning tokens and\n\t// `high` produces 310, against 130 with the field absent.\n\t//\n\t// Newer Gemini models are stricter, and not in a way the id predicts: the\n\t// gateway rejects penalties on `gemini-3-5-flash-lite` while accepting them\n\t// on `gemini-3-1-flash-lite`, so neither the version nor the `-lite` suffix\n\t// is the rule. `gemini-3-6-flash` refuses temperature and topP outright\n\t// (\"does not support the temperature parameter\"). Both are listed by id\n\t// because that is what was measured; a new Gemini model inherits the\n\t// permissive default until someone measures it.\n\tif (id.includes(\"gemini\")) {\n\t\tconst canonical = canonicalId(id);\n\t\tconst acceptsSampling = !GEMINI_NO_SAMPLING.has(canonical);\n\t\treturn {\n\t\t\tfamily: \"google\",\n\t\t\t...PERMISSIVE,\n\t\t\tsupportsTemperature: acceptsSampling,\n\t\t\tsupportsTopP: acceptsSampling,\n\t\t\tsupportsPenalties: !GEMINI_NO_PENALTIES.has(canonical),\n\t\t};\n\t}\n\n\t// OpenAI GPT-5 reasoning family (routed to the native Responses API). The\n\t// Responses model strips parameters the Responses API doesn't accept\n\t// (penalties, seed, stop), but its reasoning-model detection matches the bare\n\t// model id (`gpt-5`), which the gateway's optional `databricks-` prefix can\n\t// defeat. So we only handle the temperature/topP restriction here: the\n\t// original gpt-5 / gpt-5-mini / gpt-5-nano require the default temperature and\n\t// reject topP, while gpt-5.1+ (a minor version digit follows) accept them\n\t// again. The regex matches both prefixed and unprefixed ids.\n\tif (/gpt-5/.test(id)) {\n\t\tconst hasMinorVersion =\n\t\t\t/gpt-5[.-]\\d/.test(id) && !GPT5_NO_SAMPLING.has(canonicalId(id));\n\t\treturn {\n\t\t\tfamily: \"openai\",\n\t\t\tsupportsTemperature: hasMinorVersion,\n\t\t\tsupportsTopP: hasMinorVersion,\n\t\t\ttemperatureTopPMutuallyExclusive: false,\n\t\t\tsupportsPenalties: true,\n\t\t\tsupportsSeed: true,\n\t\t\tsupportsStopSequences: true,\n\t\t\tsupportsReasoningEffort: true,\n\t\t};\n\t}\n\n\t// Catalog `temperature: false` on gpt-6-astra; the gpt-5 minor-version rule\n\t// does not match gpt-6. Exact id: a future gpt-6-* is unmeasured.\n\tif (canonicalId(id) === \"gpt-6-astra\") {\n\t\treturn {\n\t\t\tfamily: \"openai\",\n\t\t\tsupportsTemperature: false,\n\t\t\tsupportsTopP: true,\n\t\t\ttemperatureTopPMutuallyExclusive: false,\n\t\t\tsupportsPenalties: true,\n\t\t\tsupportsSeed: true,\n\t\t\tsupportsStopSequences: true,\n\t\t\tsupportsReasoningEffort: true,\n\t\t};\n\t}\n\n\t// Meta (Llama): rejects penalties and seed; accepts sampling params and stop.\n\tif (id.includes(\"llama\")) {\n\t\treturn {\n\t\t\tfamily: \"meta\",\n\t\t\tsupportsTemperature: true,\n\t\t\tsupportsTopP: true,\n\t\t\ttemperatureTopPMutuallyExclusive: false,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t\tsupportsStopSequences: true,\n\t\t\tsupportsReasoningEffort: true,\n\t\t};\n\t}\n\n\t// Everything else on the unified endpoint rejects penalties with\n\t// `parameter \"frequency_penalty\" must be equal to 0`. Only the older Gemini\n\t// models, handled above, accept them. Seed and stop vary, so each family\n\t// carries what was measured rather than inheriting a guess.\n\tif (id.includes(\"gpt-oss\")) {\n\t\treturn {\n\t\t\tfamily: \"other\",\n\t\t\t...PERMISSIVE,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t\tsupportsStopSequences: false,\n\t\t};\n\t}\n\n\tif (id.includes(\"qwen\") || id.includes(\"gemma\")) {\n\t\treturn {\n\t\t\tfamily: \"other\",\n\t\t\t...PERMISSIVE,\n\t\t\tsupportsPenalties: false,\n\t\t\tsupportsSeed: false,\n\t\t};\n\t}\n\n\tif (id.includes(\"glm\") || id.includes(\"inkling\") || id.includes(\"kimi\")) {\n\t\treturn { family: \"other\", ...PERMISSIVE, supportsPenalties: false };\n\t}\n\n\treturn { family: \"other\", ...PERMISSIVE };\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { NeonChatModelId } from "./neon-chat-options.js";
2
+ import { NeonEmbeddingModelId } from "./neon-embedding-options.js";
2
3
  import { neonOpenAITools } from "./neon-openai-tools.js";
3
- import { LanguageModelV3, ProviderV3 } from "@ai-sdk/provider";
4
+ import { EmbeddingModelV3, LanguageModelV3, ProviderV3 } from "@ai-sdk/provider";
4
5
  import { FetchFunction } from "@ai-sdk/provider-utils";
5
6
  import { z } from "zod/v4";
6
7
 
@@ -40,8 +41,13 @@ interface NeonProvider extends ProviderV3 {
40
41
  chat(modelId: NeonChatModelId): LanguageModelV3;
41
42
  /** OpenAI Responses tools (e.g. `imageGeneration`) for OpenAI-routed models. */
42
43
  tools: typeof neonOpenAITools;
44
+ /**
45
+ * Creates a Neon AI Gateway embedding model for `embed()` and `embedMany()`.
46
+ * Provider options go under `providerOptions.neon` (`dimensions`, `user`).
47
+ */
48
+ embeddingModel(modelId: NeonEmbeddingModelId): EmbeddingModelV3;
43
49
  /** @deprecated Use `embeddingModel` instead. */
44
- textEmbeddingModel(modelId: string): never;
50
+ textEmbeddingModel(modelId: NeonEmbeddingModelId): EmbeddingModelV3;
45
51
  }
46
52
  declare function createNeon(options?: NeonProviderSettings): NeonProvider;
47
53
  /** Default Neon provider instance (reads `NEON_AI_GATEWAY_*` env vars). */
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","names":[],"sources":["../../src/lib/provider.ts"],"mappings":";;;;;;;cAqCM,iBAAe,CAAA,CAAA;;IAAf,OAAA,aAOJ;IAAA,IAAA,eAAA,cAAA,YAAA,CAAA,CAAA;;;;GAAA,CAAA,CAAA,IAAA,CAAA;KAEU,aAAA,GAAgB,CAAA,CAAE,aAAa;UA4C1B,oBAAA;;;;;;;EA9Cf;AAPmB;AAAA;AASrB;EAAyB,MAAA,CAAA,EAAA,MAAA;EAAkB;EAAf,OAAE,CAAA,EA2DnB,MA3DmB,CAAA,MAAA,EAAA,MAAA,CAAA;EAAK;EA4ClB,KAAA,CAAA,EAkBR,aAlBQ;AAAoB;AAe1B,UAMM,YAAA,SAAqB,UAN3B,CAAA;EAGF;EAAa,CAAA,OAAA,EAKX,eALW,CAAA,EAKO,eALP;EAGL;EAAa,aAAA,CAAA,OAAA,EAKN,eALM,CAAA,EAKY,eALZ;EAEnB;EAAkB,IAAA,CAAA,OAAA,EAMd,eANc,CAAA,EAMI,eANJ;EAGL;EAAkB,KAAA,EAAA,OAM3B,eAN2B;EAG3B;EAAkB,kBAAA,CAAA,OAAA,EAAA,MAAA,CAAA,EAAA,KAAA;AAGlB;AAXuB,iBAiBtB,UAAA,CAjBsB,OAAA,CAAA,EAiBF,oBAjBE,CAAA,EAiB0B,YAjB1B;AAAU;AAiBhC,cAiHH,IAjHa,EAiHT,YAjHS"}
1
+ {"version":3,"file":"provider.d.ts","names":[],"sources":["../../src/lib/provider.ts"],"mappings":";;;;;;;;cA6CM,iBAAe,CAAA,CAAA;;IAAf,OAAA,aAOJ;IAAA,IAAA,eAAA,cAAA,YAAA,CAAA,CAAA;;;;GAAA,CAAA,CAAA,IAAA,CAAA;KAEU,aAAA,GAAgB,CAAA,CAAE,aAAa;UA4C1B,oBAAA;;;;;;;EA9Cf;AAPmB;AAAA;AASrB;EAAyB,MAAA,CAAA,EAAA,MAAA;EAAkB;EAAf,OAAE,CAAA,EA2DnB,MA3DmB,CAAA,MAAA,EAAA,MAAA,CAAA;EAAK;EA4ClB,KAAA,CAAA,EAkBR,aAlBQ;AAAoB;AAe1B,UAMM,YAAA,SAAqB,UAN3B,CAAA;EAGF;EAAa,CAAA,OAAA,EAKX,eALW,CAAA,EAKO,eALP;EAGL;EAAa,aAAA,CAAA,OAAA,EAKN,eALM,CAAA,EAKY,eALZ;EAEnB;EAAkB,IAAA,CAAA,OAAA,EAMd,eANc,CAAA,EAMI,eANJ;EAGL;EAAkB,KAAA,EAAA,OAM3B,eAN2B;EAG3B;AAAkB;AAGlB;AAMU;EAAuB,cAAA,CAAA,OAAA,EAAvB,oBAAuB,CAAA,EAAA,gBAAA;EAGnB;EAAuB,kBAAA,CAAA,OAAA,EAAvB,oBAAuB,CAAA,EAAA,gBAAA;AApBd;AAAU,iBAuBhC,UAAA,CAvBgC,OAAA,CAAA,EAuBZ,oBAvBY,CAAA,EAuBgB,YAvBhB;AAuBhD;AAA0B,cA4Hb,IA5Ha,EA4HT,YA5HS"}
@@ -2,14 +2,28 @@ import { getNeonModelRoute } from "./neon-model-capabilities.js";
2
2
  import { NeonAnthropicLanguageModel } from "./neon-anthropic-language-model.js";
3
3
  import { NeonChatLanguageModel } from "./neon-chat-language-model.js";
4
4
  import { NeonResponsesLanguageModel } from "./neon-responses-language-model.js";
5
+ import "./neon-embedding-options.js";
5
6
  import { wrapFetchWithGatewayErrorNormalization } from "./neon-gateway-error.js";
6
7
  import { wrapFetchWithHarmonyNormalization } from "./neon-harmony-normalize.js";
7
8
  import { neonOpenAITools } from "./neon-openai-tools.js";
8
9
  import { VERSION } from "./version.js";
10
+ import { OpenAICompatibleEmbeddingModel } from "@ai-sdk/openai-compatible";
9
11
  import { NoSuchModelError } from "@ai-sdk/provider";
10
12
  import { generateId, loadApiKey, loadSetting, withUserAgentSuffix, withoutTrailingSlash } from "@ai-sdk/provider-utils";
11
13
  import { z } from "zod/v4";
12
14
  //#region src/lib/provider.ts
15
+ /**
16
+ * Community Vercel AI SDK provider for the Neon AI Gateway.
17
+ *
18
+ * `createNeon()` returns a provider that routes each model to the best gateway
19
+ * endpoint based on its id (Anthropic → native Messages, OpenAI → native
20
+ * Responses incl. Codex, everything else → unified Chat Completions), so a single
21
+ * `neon('claude-...')` call (same base URL + token) reaches the whole catalog.
22
+ * Ids use the canonical Neon (unprefixed) form; the legacy `databricks-` prefix
23
+ * is also accepted. Configure with the branch-scoped `NEON_AI_GATEWAY_BASE_URL` +
24
+ * `NEON_AI_GATEWAY_TOKEN` emitted by `neonctl env pull` / `neon dev`, or pass
25
+ * `baseURL` / `apiKey` explicitly.
26
+ */
13
27
  const neonErrorStructure = {
14
28
  errorSchema: z.object({ error: z.object({
15
29
  message: z.string(),
@@ -81,6 +95,14 @@ function createNeon(options = {}) {
81
95
  transformRequestBody: transformNeonRequestBody,
82
96
  supportsStructuredOutputs: true
83
97
  });
98
+ const createEmbeddingModel = (modelId) => new OpenAICompatibleEmbeddingModel(modelId, {
99
+ provider: "neon.embedding",
100
+ url: ({ path }) => `${getHost()}/v1${path}`,
101
+ headers: getHeaders,
102
+ fetch: wrapFetchWithGatewayErrorNormalization(options.fetch, "openai"),
103
+ errorStructure: neonErrorStructure,
104
+ maxEmbeddingsPerCall: 150
105
+ });
84
106
  const createLanguageModel = (modelId) => {
85
107
  switch (getNeonModelRoute(modelId)) {
86
108
  case "anthropic": return createAnthropicModel(modelId);
@@ -93,13 +115,8 @@ function createNeon(options = {}) {
93
115
  provider.languageModel = createLanguageModel;
94
116
  provider.chat = createLanguageModel;
95
117
  provider.tools = neonOpenAITools;
96
- provider.embeddingModel = (modelId) => {
97
- throw new NoSuchModelError({
98
- modelId,
99
- modelType: "embeddingModel"
100
- });
101
- };
102
- provider.textEmbeddingModel = provider.embeddingModel;
118
+ provider.embeddingModel = createEmbeddingModel;
119
+ provider.textEmbeddingModel = createEmbeddingModel;
103
120
  provider.imageModel = (modelId) => {
104
121
  throw new NoSuchModelError({
105
122
  modelId,
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","names":[],"sources":["../../src/lib/provider.ts"],"sourcesContent":["/**\n * Community Vercel AI SDK provider for the Neon AI Gateway.\n *\n * `createNeon()` returns a provider that routes each model to the best gateway\n * endpoint based on its id (Anthropic → native Messages, OpenAI → native\n * Responses incl. Codex, everything else → unified Chat Completions), so a single\n * `neon('claude-...')` call (same base URL + token) reaches the whole catalog.\n * Ids use the canonical Neon (unprefixed) form; the legacy `databricks-` prefix\n * is also accepted. Configure with the branch-scoped `NEON_AI_GATEWAY_BASE_URL` +\n * `NEON_AI_GATEWAY_TOKEN` emitted by `neonctl env pull` / `neon dev`, or pass\n * `baseURL` / `apiKey` explicitly.\n */\nimport type { ProviderErrorStructure } from \"@ai-sdk/openai-compatible\";\nimport {\n\ttype LanguageModelV3,\n\tNoSuchModelError,\n\ttype ProviderV3,\n} from \"@ai-sdk/provider\";\nimport {\n\ttype FetchFunction,\n\tgenerateId,\n\tloadApiKey,\n\tloadSetting,\n\twithoutTrailingSlash,\n\twithUserAgentSuffix,\n} from \"@ai-sdk/provider-utils\";\nimport { z } from \"zod/v4\";\nimport { NeonAnthropicLanguageModel } from \"./neon-anthropic-language-model.js\";\nimport { NeonChatLanguageModel } from \"./neon-chat-language-model.js\";\nimport type { NeonChatModelId } from \"./neon-chat-options.js\";\nimport { wrapFetchWithGatewayErrorNormalization } from \"./neon-gateway-error.js\";\nimport { wrapFetchWithHarmonyNormalization } from \"./neon-harmony-normalize.js\";\nimport { getNeonModelRoute } from \"./neon-model-capabilities.js\";\nimport { neonOpenAITools } from \"./neon-openai-tools.js\";\nimport { NeonResponsesLanguageModel } from \"./neon-responses-language-model.js\";\nimport { VERSION } from \"./version.js\";\n\nconst neonErrorSchema = z.object({\n\terror: z.object({\n\t\tmessage: z.string(),\n\t\ttype: z.string().nullish(),\n\t\tparam: z.unknown().nullish(),\n\t\tcode: z.unknown().nullish(),\n\t}),\n});\n\nexport type NeonErrorData = z.infer<typeof neonErrorSchema>;\n\nconst neonErrorStructure: ProviderErrorStructure<NeonErrorData> = {\n\terrorSchema: neonErrorSchema,\n\terrorToMessage: (data) => data.error.message,\n};\n\n/**\n * Recursively remove the JSON Schema `$schema` marker, which some gateway\n * backends (notably Gemini) reject in tool/structured-output schemas. Other\n * backends ignore its absence, so stripping it everywhere is safe.\n */\nfunction stripJsonSchemaMarker(value: unknown): unknown {\n\tif (Array.isArray(value)) {\n\t\treturn value.map(stripJsonSchemaMarker);\n\t}\n\tif (value !== null && typeof value === \"object\") {\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [key, entry] of Object.entries(value)) {\n\t\t\tif (key === \"$schema\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tresult[key] = stripJsonSchemaMarker(entry);\n\t\t}\n\t\treturn result;\n\t}\n\treturn value;\n}\n\nfunction transformNeonRequestBody(\n\targs: Record<string, unknown>,\n): Record<string, unknown> {\n\tconst transformed = { ...args };\n\tif (transformed.tools != null) {\n\t\ttransformed.tools = stripJsonSchemaMarker(transformed.tools);\n\t}\n\tif (transformed.response_format != null) {\n\t\ttransformed.response_format = stripJsonSchemaMarker(\n\t\t\ttransformed.response_format,\n\t\t);\n\t}\n\treturn transformed;\n}\n\nexport interface NeonProviderSettings {\n\t/**\n\t * Neon AI Gateway base URL — the branch-scoped host root, e.g.\n\t * `https://<branch-id>-api.ai.<region>.aws.neon.tech`. The gateway paths are\n\t * appended internally. Falls back to the `NEON_AI_GATEWAY_BASE_URL` env var.\n\t */\n\tbaseURL?: string;\n\n\t/**\n\t * Neon AI Gateway platform token (the `nt_live_...` value). Falls back to the\n\t * `NEON_AI_GATEWAY_TOKEN` env var.\n\t */\n\tapiKey?: string;\n\n\t/** Custom headers to include in the requests. */\n\theaders?: Record<string, string>;\n\n\t/** Custom fetch implementation (e.g. for testing or middleware). */\n\tfetch?: FetchFunction;\n}\n\nexport interface NeonProvider extends ProviderV3 {\n\t/** Creates a Neon AI Gateway model for text generation. */\n\t(modelId: NeonChatModelId): LanguageModelV3;\n\n\t/** Creates a Neon AI Gateway model for text generation. */\n\tlanguageModel(modelId: NeonChatModelId): LanguageModelV3;\n\n\t/** Creates a Neon AI Gateway chat model for text generation. */\n\tchat(modelId: NeonChatModelId): LanguageModelV3;\n\n\t/** OpenAI Responses tools (e.g. `imageGeneration`) for OpenAI-routed models. */\n\ttools: typeof neonOpenAITools;\n\n\t/** @deprecated Use `embeddingModel` instead. */\n\ttextEmbeddingModel(modelId: string): never;\n}\n\nexport function createNeon(options: NeonProviderSettings = {}): NeonProvider {\n\t// Resolved lazily so that `createNeon()` and the default `neon` instance do\n\t// not throw at import time when configuration comes from the environment.\n\tconst getHost = () =>\n\t\twithoutTrailingSlash(\n\t\t\tloadSetting({\n\t\t\t\tsettingValue: options.baseURL,\n\t\t\t\tenvironmentVariableName: \"NEON_AI_GATEWAY_BASE_URL\",\n\t\t\t\tsettingName: \"baseURL\",\n\t\t\t\tdescription: \"Neon AI Gateway base URL\",\n\t\t\t}),\n\t\t);\n\n\tconst getHeaders = (extra?: Record<string, string>) =>\n\t\twithUserAgentSuffix(\n\t\t\t{\n\t\t\t\tAuthorization: `Bearer ${loadApiKey({\n\t\t\t\t\tapiKey: options.apiKey,\n\t\t\t\t\tenvironmentVariableName: \"NEON_AI_GATEWAY_TOKEN\",\n\t\t\t\t\tdescription: \"Neon AI Gateway token\",\n\t\t\t\t})}`,\n\t\t\t\t...extra,\n\t\t\t\t...options.headers,\n\t\t\t},\n\t\t\t`neondatabase/ai-sdk-provider/${VERSION}`,\n\t\t);\n\n\t// Anthropic models -> native Messages API.\n\tconst createAnthropicModel = (modelId: NeonChatModelId) =>\n\t\tnew NeonAnthropicLanguageModel(modelId, {\n\t\t\tprovider: \"neon.anthropic\",\n\t\t\tbaseURL: `${getHost()}/anthropic/v1`,\n\t\t\theaders: () => getHeaders({ \"anthropic-version\": \"2023-06-01\" }),\n\t\t\tfetch: wrapFetchWithGatewayErrorNormalization(\n\t\t\t\toptions.fetch,\n\t\t\t\t\"anthropic\",\n\t\t\t),\n\t\t\tgenerateId,\n\t\t});\n\n\t// OpenAI models (incl. Codex, only served natively) -> Responses API.\n\tconst createOpenAIModel = (modelId: NeonChatModelId) =>\n\t\tnew NeonResponsesLanguageModel(modelId, {\n\t\t\tprovider: \"neon.openai.responses\",\n\t\t\turl: ({ path }) => `${getHost()}/openai/v1${path}`,\n\t\t\theaders: getHeaders,\n\t\t\t// This route returns Databricks error envelopes the OpenAI schema\n\t\t\t// cannot read, which reach the caller as a bare \"Bad Request\".\n\t\t\t// `OpenAIConfig` has no error hook, so the rewrite rides on fetch.\n\t\t\tfetch: wrapFetchWithGatewayErrorNormalization(\n\t\t\t\toptions.fetch,\n\t\t\t\t\"openai\",\n\t\t\t),\n\t\t\tfileIdPrefixes: [\"file-\"],\n\t\t});\n\n\t// Everything else (Gemini, Llama, Qwen, gpt-oss, ...) -> unified Chat\n\t// Completions endpoint. Gemini is here because its native endpoint can't stream.\n\tconst createChatModel = (modelId: NeonChatModelId) =>\n\t\tnew NeonChatLanguageModel(modelId, {\n\t\t\tprovider: \"neon.chat\",\n\t\t\turl: ({ path }) => `${getHost()}/v1${path}`,\n\t\t\theaders: getHeaders,\n\t\t\t// Two wrappers, on disjoint halves of the response: the harmony one\n\t\t\t// rewrites successful gpt-oss bodies and returns early on any error,\n\t\t\t// the error one only touches non-2xx bodies. This route emits the\n\t\t\t// same flat `{ error_code, message }` Databricks envelope as the\n\t\t\t// Responses route (an out-of-range `temperature`, say), which\n\t\t\t// `errorStructure` below cannot read on its own.\n\t\t\t//\n\t\t\t// TODO(#308): Temporary workaround for gpt-oss. The gateway returns a\n\t\t\t// non-compliant \"harmony\" content-array shape; this wrapper normalizes it\n\t\t\t// to the Chat Completions contract and is a transparent pass-through for\n\t\t\t// every compliant response. Remove it (and neon-harmony-normalize.ts) once\n\t\t\t// the gateway returns spec-compliant gpt-oss responses.\n\t\t\tfetch: wrapFetchWithHarmonyNormalization(\n\t\t\t\twrapFetchWithGatewayErrorNormalization(options.fetch, \"openai\"),\n\t\t\t),\n\t\t\terrorStructure: neonErrorStructure,\n\t\t\ttransformRequestBody: transformNeonRequestBody,\n\t\t\tsupportsStructuredOutputs: true,\n\t\t});\n\n\tconst createLanguageModel = (modelId: NeonChatModelId): LanguageModelV3 => {\n\t\tswitch (getNeonModelRoute(modelId)) {\n\t\t\tcase \"anthropic\":\n\t\t\t\treturn createAnthropicModel(modelId);\n\t\t\tcase \"openai\":\n\t\t\t\treturn createOpenAIModel(modelId);\n\t\t\tdefault:\n\t\t\t\treturn createChatModel(modelId);\n\t\t}\n\t};\n\n\tconst provider = (modelId: NeonChatModelId) => createLanguageModel(modelId);\n\n\tprovider.specificationVersion = \"v3\" as const;\n\tprovider.languageModel = createLanguageModel;\n\tprovider.chat = createLanguageModel;\n\tprovider.tools = neonOpenAITools;\n\n\tprovider.embeddingModel = (modelId: string) => {\n\t\tthrow new NoSuchModelError({ modelId, modelType: \"embeddingModel\" });\n\t};\n\tprovider.textEmbeddingModel = provider.embeddingModel;\n\tprovider.imageModel = (modelId: string) => {\n\t\tthrow new NoSuchModelError({ modelId, modelType: \"imageModel\" });\n\t};\n\n\treturn provider;\n}\n\n/** Default Neon provider instance (reads `NEON_AI_GATEWAY_*` env vars). */\nexport const neon = createNeon();\n"],"mappings":";;;;;;;;;;;;AAgDA,MAAM,qBAA4D;CACjE,aAZuB,EAAE,OAAO,EAChC,OAAO,EAAE,OAAO;EACf,SAAS,EAAE,OAAO;EAClB,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ;EACzB,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ;EAC3B,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ;CAC3B,CAAC,EACF,CAK4B;CAC3B,iBAAiB,SAAS,KAAK,MAAM;AACtC;;;;;;AAOA,SAAS,sBAAsB,OAAyB;CACvD,IAAI,MAAM,QAAQ,KAAK,GACtB,OAAO,MAAM,IAAI,qBAAqB;CAEvC,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;EAChD,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAAG;GACjD,IAAI,QAAQ,WACX;GAED,OAAO,OAAO,sBAAsB,KAAK;EAC1C;EACA,OAAO;CACR;CACA,OAAO;AACR;AAEA,SAAS,yBACR,MAC0B;CAC1B,MAAM,cAAc,EAAE,GAAG,KAAK;CAC9B,IAAI,YAAY,SAAS,MACxB,YAAY,QAAQ,sBAAsB,YAAY,KAAK;CAE5D,IAAI,YAAY,mBAAmB,MAClC,YAAY,kBAAkB,sBAC7B,YAAY,eACb;CAED,OAAO;AACR;AAwCA,SAAgB,WAAW,UAAgC,CAAC,GAAiB;CAG5E,MAAM,gBACL,qBACC,YAAY;EACX,cAAc,QAAQ;EACtB,yBAAyB;EACzB,aAAa;EACb,aAAa;CACd,CAAC,CACF;CAED,MAAM,cAAc,UACnB,oBACC;EACC,eAAe,UAAU,WAAW;GACnC,QAAQ,QAAQ;GAChB,yBAAyB;GACzB,aAAa;EACd,CAAC;EACD,GAAG;EACH,GAAG,QAAQ;CACZ,GACA,gCAAgC,SACjC;CAGD,MAAM,wBAAwB,YAC7B,IAAI,2BAA2B,SAAS;EACvC,UAAU;EACV,SAAS,GAAG,QAAQ,EAAE;EACtB,eAAe,WAAW,EAAE,qBAAqB,aAAa,CAAC;EAC/D,OAAO,uCACN,QAAQ,OACR,WACD;EACA;CACD,CAAC;CAGF,MAAM,qBAAqB,YAC1B,IAAI,2BAA2B,SAAS;EACvC,UAAU;EACV,MAAM,EAAE,WAAW,GAAG,QAAQ,EAAE,YAAY;EAC5C,SAAS;EAIT,OAAO,uCACN,QAAQ,OACR,QACD;EACA,gBAAgB,CAAC,OAAO;CACzB,CAAC;CAIF,MAAM,mBAAmB,YACxB,IAAI,sBAAsB,SAAS;EAClC,UAAU;EACV,MAAM,EAAE,WAAW,GAAG,QAAQ,EAAE,KAAK;EACrC,SAAS;EAaT,OAAO,kCACN,uCAAuC,QAAQ,OAAO,QAAQ,CAC/D;EACA,gBAAgB;EAChB,sBAAsB;EACtB,2BAA2B;CAC5B,CAAC;CAEF,MAAM,uBAAuB,YAA8C;EAC1E,QAAQ,kBAAkB,OAAO,GAAjC;GACC,KAAK,aACJ,OAAO,qBAAqB,OAAO;GACpC,KAAK,UACJ,OAAO,kBAAkB,OAAO;GACjC,SACC,OAAO,gBAAgB,OAAO;EAChC;CACD;CAEA,MAAM,YAAY,YAA6B,oBAAoB,OAAO;CAE1E,SAAS,uBAAuB;CAChC,SAAS,gBAAgB;CACzB,SAAS,OAAO;CAChB,SAAS,QAAQ;CAEjB,SAAS,kBAAkB,YAAoB;EAC9C,MAAM,IAAI,iBAAiB;GAAE;GAAS,WAAW;EAAiB,CAAC;CACpE;CACA,SAAS,qBAAqB,SAAS;CACvC,SAAS,cAAc,YAAoB;EAC1C,MAAM,IAAI,iBAAiB;GAAE;GAAS,WAAW;EAAa,CAAC;CAChE;CAEA,OAAO;AACR;;AAGA,MAAa,OAAO,WAAW"}
1
+ {"version":3,"file":"provider.js","names":[],"sources":["../../src/lib/provider.ts"],"sourcesContent":["/**\n * Community Vercel AI SDK provider for the Neon AI Gateway.\n *\n * `createNeon()` returns a provider that routes each model to the best gateway\n * endpoint based on its id (Anthropic → native Messages, OpenAI → native\n * Responses incl. Codex, everything else → unified Chat Completions), so a single\n * `neon('claude-...')` call (same base URL + token) reaches the whole catalog.\n * Ids use the canonical Neon (unprefixed) form; the legacy `databricks-` prefix\n * is also accepted. Configure with the branch-scoped `NEON_AI_GATEWAY_BASE_URL` +\n * `NEON_AI_GATEWAY_TOKEN` emitted by `neonctl env pull` / `neon dev`, or pass\n * `baseURL` / `apiKey` explicitly.\n */\nimport {\n\tOpenAICompatibleEmbeddingModel,\n\ttype ProviderErrorStructure,\n} from \"@ai-sdk/openai-compatible\";\nimport {\n\ttype EmbeddingModelV3,\n\ttype LanguageModelV3,\n\tNoSuchModelError,\n\ttype ProviderV3,\n} from \"@ai-sdk/provider\";\nimport {\n\ttype FetchFunction,\n\tgenerateId,\n\tloadApiKey,\n\tloadSetting,\n\twithoutTrailingSlash,\n\twithUserAgentSuffix,\n} from \"@ai-sdk/provider-utils\";\nimport { z } from \"zod/v4\";\nimport { NeonAnthropicLanguageModel } from \"./neon-anthropic-language-model.js\";\nimport { NeonChatLanguageModel } from \"./neon-chat-language-model.js\";\nimport type { NeonChatModelId } from \"./neon-chat-options.js\";\nimport {\n\tNEON_MAX_EMBEDDINGS_PER_CALL,\n\ttype NeonEmbeddingModelId,\n} from \"./neon-embedding-options.js\";\nimport { wrapFetchWithGatewayErrorNormalization } from \"./neon-gateway-error.js\";\nimport { wrapFetchWithHarmonyNormalization } from \"./neon-harmony-normalize.js\";\nimport { getNeonModelRoute } from \"./neon-model-capabilities.js\";\nimport { neonOpenAITools } from \"./neon-openai-tools.js\";\nimport { NeonResponsesLanguageModel } from \"./neon-responses-language-model.js\";\nimport { VERSION } from \"./version.js\";\n\nconst neonErrorSchema = z.object({\n\terror: z.object({\n\t\tmessage: z.string(),\n\t\ttype: z.string().nullish(),\n\t\tparam: z.unknown().nullish(),\n\t\tcode: z.unknown().nullish(),\n\t}),\n});\n\nexport type NeonErrorData = z.infer<typeof neonErrorSchema>;\n\nconst neonErrorStructure: ProviderErrorStructure<NeonErrorData> = {\n\terrorSchema: neonErrorSchema,\n\terrorToMessage: (data) => data.error.message,\n};\n\n/**\n * Recursively remove the JSON Schema `$schema` marker, which some gateway\n * backends (notably Gemini) reject in tool/structured-output schemas. Other\n * backends ignore its absence, so stripping it everywhere is safe.\n */\nfunction stripJsonSchemaMarker(value: unknown): unknown {\n\tif (Array.isArray(value)) {\n\t\treturn value.map(stripJsonSchemaMarker);\n\t}\n\tif (value !== null && typeof value === \"object\") {\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [key, entry] of Object.entries(value)) {\n\t\t\tif (key === \"$schema\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tresult[key] = stripJsonSchemaMarker(entry);\n\t\t}\n\t\treturn result;\n\t}\n\treturn value;\n}\n\nfunction transformNeonRequestBody(\n\targs: Record<string, unknown>,\n): Record<string, unknown> {\n\tconst transformed = { ...args };\n\tif (transformed.tools != null) {\n\t\ttransformed.tools = stripJsonSchemaMarker(transformed.tools);\n\t}\n\tif (transformed.response_format != null) {\n\t\ttransformed.response_format = stripJsonSchemaMarker(\n\t\t\ttransformed.response_format,\n\t\t);\n\t}\n\treturn transformed;\n}\n\nexport interface NeonProviderSettings {\n\t/**\n\t * Neon AI Gateway base URL — the branch-scoped host root, e.g.\n\t * `https://<branch-id>-api.ai.<region>.aws.neon.tech`. The gateway paths are\n\t * appended internally. Falls back to the `NEON_AI_GATEWAY_BASE_URL` env var.\n\t */\n\tbaseURL?: string;\n\n\t/**\n\t * Neon AI Gateway platform token (the `nt_live_...` value). Falls back to the\n\t * `NEON_AI_GATEWAY_TOKEN` env var.\n\t */\n\tapiKey?: string;\n\n\t/** Custom headers to include in the requests. */\n\theaders?: Record<string, string>;\n\n\t/** Custom fetch implementation (e.g. for testing or middleware). */\n\tfetch?: FetchFunction;\n}\n\nexport interface NeonProvider extends ProviderV3 {\n\t/** Creates a Neon AI Gateway model for text generation. */\n\t(modelId: NeonChatModelId): LanguageModelV3;\n\n\t/** Creates a Neon AI Gateway model for text generation. */\n\tlanguageModel(modelId: NeonChatModelId): LanguageModelV3;\n\n\t/** Creates a Neon AI Gateway chat model for text generation. */\n\tchat(modelId: NeonChatModelId): LanguageModelV3;\n\n\t/** OpenAI Responses tools (e.g. `imageGeneration`) for OpenAI-routed models. */\n\ttools: typeof neonOpenAITools;\n\n\t/**\n\t * Creates a Neon AI Gateway embedding model for `embed()` and `embedMany()`.\n\t * Provider options go under `providerOptions.neon` (`dimensions`, `user`).\n\t */\n\tembeddingModel(modelId: NeonEmbeddingModelId): EmbeddingModelV3;\n\n\t/** @deprecated Use `embeddingModel` instead. */\n\ttextEmbeddingModel(modelId: NeonEmbeddingModelId): EmbeddingModelV3;\n}\n\nexport function createNeon(options: NeonProviderSettings = {}): NeonProvider {\n\t// Resolved lazily so that `createNeon()` and the default `neon` instance do\n\t// not throw at import time when configuration comes from the environment.\n\tconst getHost = () =>\n\t\twithoutTrailingSlash(\n\t\t\tloadSetting({\n\t\t\t\tsettingValue: options.baseURL,\n\t\t\t\tenvironmentVariableName: \"NEON_AI_GATEWAY_BASE_URL\",\n\t\t\t\tsettingName: \"baseURL\",\n\t\t\t\tdescription: \"Neon AI Gateway base URL\",\n\t\t\t}),\n\t\t);\n\n\tconst getHeaders = (extra?: Record<string, string>) =>\n\t\twithUserAgentSuffix(\n\t\t\t{\n\t\t\t\tAuthorization: `Bearer ${loadApiKey({\n\t\t\t\t\tapiKey: options.apiKey,\n\t\t\t\t\tenvironmentVariableName: \"NEON_AI_GATEWAY_TOKEN\",\n\t\t\t\t\tdescription: \"Neon AI Gateway token\",\n\t\t\t\t})}`,\n\t\t\t\t...extra,\n\t\t\t\t...options.headers,\n\t\t\t},\n\t\t\t`neondatabase/ai-sdk-provider/${VERSION}`,\n\t\t);\n\n\t// Anthropic models -> native Messages API.\n\tconst createAnthropicModel = (modelId: NeonChatModelId) =>\n\t\tnew NeonAnthropicLanguageModel(modelId, {\n\t\t\tprovider: \"neon.anthropic\",\n\t\t\tbaseURL: `${getHost()}/anthropic/v1`,\n\t\t\theaders: () => getHeaders({ \"anthropic-version\": \"2023-06-01\" }),\n\t\t\tfetch: wrapFetchWithGatewayErrorNormalization(\n\t\t\t\toptions.fetch,\n\t\t\t\t\"anthropic\",\n\t\t\t),\n\t\t\tgenerateId,\n\t\t});\n\n\t// OpenAI models (incl. Codex, only served natively) -> Responses API.\n\tconst createOpenAIModel = (modelId: NeonChatModelId) =>\n\t\tnew NeonResponsesLanguageModel(modelId, {\n\t\t\tprovider: \"neon.openai.responses\",\n\t\t\turl: ({ path }) => `${getHost()}/openai/v1${path}`,\n\t\t\theaders: getHeaders,\n\t\t\t// This route returns Databricks error envelopes the OpenAI schema\n\t\t\t// cannot read, which reach the caller as a bare \"Bad Request\".\n\t\t\t// `OpenAIConfig` has no error hook, so the rewrite rides on fetch.\n\t\t\tfetch: wrapFetchWithGatewayErrorNormalization(\n\t\t\t\toptions.fetch,\n\t\t\t\t\"openai\",\n\t\t\t),\n\t\t\tfileIdPrefixes: [\"file-\"],\n\t\t});\n\n\t// Everything else (Gemini, Llama, Qwen, gpt-oss, ...) -> unified Chat\n\t// Completions endpoint. Gemini is here because its native endpoint can't stream.\n\tconst createChatModel = (modelId: NeonChatModelId) =>\n\t\tnew NeonChatLanguageModel(modelId, {\n\t\t\tprovider: \"neon.chat\",\n\t\t\turl: ({ path }) => `${getHost()}/v1${path}`,\n\t\t\theaders: getHeaders,\n\t\t\t// Two wrappers, on disjoint halves of the response: the harmony one\n\t\t\t// rewrites successful gpt-oss bodies and returns early on any error,\n\t\t\t// the error one only touches non-2xx bodies. This route emits the\n\t\t\t// same flat `{ error_code, message }` Databricks envelope as the\n\t\t\t// Responses route (an out-of-range `temperature`, say), which\n\t\t\t// `errorStructure` below cannot read on its own.\n\t\t\t//\n\t\t\t// TODO(#308): Temporary workaround for gpt-oss. The gateway returns a\n\t\t\t// non-compliant \"harmony\" content-array shape; this wrapper normalizes it\n\t\t\t// to the Chat Completions contract and is a transparent pass-through for\n\t\t\t// every compliant response. Remove it (and neon-harmony-normalize.ts) once\n\t\t\t// the gateway returns spec-compliant gpt-oss responses.\n\t\t\tfetch: wrapFetchWithHarmonyNormalization(\n\t\t\t\twrapFetchWithGatewayErrorNormalization(options.fetch, \"openai\"),\n\t\t\t),\n\t\t\terrorStructure: neonErrorStructure,\n\t\t\ttransformRequestBody: transformNeonRequestBody,\n\t\t\tsupportsStructuredOutputs: true,\n\t\t});\n\n\tconst createEmbeddingModel = (modelId: NeonEmbeddingModelId) =>\n\t\tnew OpenAICompatibleEmbeddingModel(modelId, {\n\t\t\tprovider: \"neon.embedding\",\n\t\t\turl: ({ path }) => `${getHost()}/v1${path}`,\n\t\t\theaders: getHeaders,\n\t\t\tfetch: wrapFetchWithGatewayErrorNormalization(\n\t\t\t\toptions.fetch,\n\t\t\t\t\"openai\",\n\t\t\t),\n\t\t\terrorStructure: neonErrorStructure,\n\t\t\tmaxEmbeddingsPerCall: NEON_MAX_EMBEDDINGS_PER_CALL,\n\t\t});\n\n\tconst createLanguageModel = (modelId: NeonChatModelId): LanguageModelV3 => {\n\t\tswitch (getNeonModelRoute(modelId)) {\n\t\t\tcase \"anthropic\":\n\t\t\t\treturn createAnthropicModel(modelId);\n\t\t\tcase \"openai\":\n\t\t\t\treturn createOpenAIModel(modelId);\n\t\t\tdefault:\n\t\t\t\treturn createChatModel(modelId);\n\t\t}\n\t};\n\n\tconst provider = (modelId: NeonChatModelId) => createLanguageModel(modelId);\n\n\tprovider.specificationVersion = \"v3\" as const;\n\tprovider.languageModel = createLanguageModel;\n\tprovider.chat = createLanguageModel;\n\tprovider.tools = neonOpenAITools;\n\n\tprovider.embeddingModel = createEmbeddingModel;\n\tprovider.textEmbeddingModel = createEmbeddingModel;\n\tprovider.imageModel = (modelId: string) => {\n\t\tthrow new NoSuchModelError({ modelId, modelType: \"imageModel\" });\n\t};\n\n\treturn provider;\n}\n\n/** Default Neon provider instance (reads `NEON_AI_GATEWAY_*` env vars). */\nexport const neon = createNeon();\n"],"mappings":"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"}
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.9.0";
2
+ var version = "1.0.0";
3
3
  //#endregion
4
4
  export { version };
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neon/ai-sdk-provider",
3
- "version": "0.9.0",
3
+ "version": "1.0.0",
4
4
  "description": "Community Vercel AI SDK provider for the Neon AI Gateway.",
5
5
  "keywords": [
6
6
  "neon",
@@ -45,7 +45,8 @@
45
45
  "tsdown": "^0.14.1",
46
46
  "typescript": "^5.9.0",
47
47
  "vitest": "^3.0.9",
48
- "zod": "^4.4.3"
48
+ "zod": "^4.4.3",
49
+ "@neon/e2e-harness": "0.0.0"
49
50
  },
50
51
  "engines": {
51
52
  "node": ">=20.19.0"