@neondatabase/ai-sdk-provider 0.7.3 → 0.9.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 +46 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/lib/neon-anthropic-language-model.d.ts +8 -0
- package/dist/lib/neon-anthropic-language-model.d.ts.map +1 -1
- package/dist/lib/neon-anthropic-language-model.js +39 -13
- package/dist/lib/neon-anthropic-language-model.js.map +1 -1
- package/dist/lib/neon-capabilities.d.ts.map +1 -1
- package/dist/lib/neon-capabilities.js +17 -11
- package/dist/lib/neon-capabilities.js.map +1 -1
- package/dist/lib/neon-chat-options.d.ts +4 -11
- package/dist/lib/neon-chat-options.d.ts.map +1 -1
- package/dist/lib/neon-chat-options.js +26 -28
- package/dist/lib/neon-chat-options.js.map +1 -1
- package/dist/lib/neon-model-capabilities.d.ts +7 -0
- package/dist/lib/neon-model-capabilities.d.ts.map +1 -1
- package/dist/lib/neon-model-capabilities.js +87 -16
- package/dist/lib/neon-model-capabilities.js.map +1 -1
- package/dist/lib/provider.js +1 -1
- package/dist/package.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,11 +59,53 @@ 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, and
|
|
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.
|
|
63
63
|
|
|
64
|
-
Claude models use the Messages API
|
|
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
|
|
|
66
|
-
Which ids your branch serves is account-specific during the beta; `GET $NEON_AI_GATEWAY_BASE_URL/v1/models` is the authoritative list.
|
|
66
|
+
Which ids your branch serves is account-specific during the beta; `GET $NEON_AI_GATEWAY_BASE_URL/v1/models` is the authoritative list. `NeonChatModelId` accepts any string, so an id this package has not caught up with still works — and, equally, an id the gateway has retired still type-checks and fails at the gateway instead.
|
|
67
|
+
|
|
68
|
+
### Dropped call options
|
|
69
|
+
|
|
70
|
+
Upstream backends behind the gateway accept different subsets of the OpenAI-style parameters the AI SDK emits, and sending one an upstream rejects is a hard `400`. The provider drops those before the request and records a warning in `result.warnings`, so a call succeeds instead of failing on a parameter you passed in good faith.
|
|
71
|
+
|
|
72
|
+
These rules apply on the Anthropic Messages and Chat Completions routes. The Responses route (every `gpt-*` id) relies on the upstream OpenAI model's own stripping instead, so its behaviour and its warning wording are the AI SDK's rather than described here — but `getNeonModelCapabilities` still answers for those ids, and the `gpt-5` row below is what it reports rather than something this provider drops.
|
|
73
|
+
|
|
74
|
+
| Models | Dropped |
|
|
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` |
|
|
77
|
+
| All Claude | `frequencyPenalty`, `presencePenalty`, `seed`, `reasoningEffort`, and `topP` when you set both `temperature` and `topP` (Anthropic accepts only one) |
|
|
78
|
+
| `gpt-oss` | `frequencyPenalty`, `presencePenalty`, `seed`, `stopSequences` |
|
|
79
|
+
| Qwen, Gemma | `frequencyPenalty`, `presencePenalty`, `seed` |
|
|
80
|
+
| `glm-5-2`, `inkling`, `kimi-k3` | `frequencyPenalty`, `presencePenalty` |
|
|
81
|
+
| Meta Llama | `frequencyPenalty`, `presencePenalty`, `seed` |
|
|
82
|
+
| `gemini-3-5-flash-lite` | `frequencyPenalty`, `presencePenalty` |
|
|
83
|
+
| `gemini-3-6-flash` | `temperature`, `topP`, `frequencyPenalty`, `presencePenalty` |
|
|
84
|
+
| Every other Gemini | nothing |
|
|
85
|
+
| `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-5-5-pro` (reported only — Responses strips these upstream) | `temperature`, `topP` |
|
|
86
|
+
|
|
87
|
+
**`temperature` on a Claude 5 model therefore has no effect.** That is the gateway's behaviour, not a provider choice; sending it returns `does not support the temperature parameter`. Steer those models with Anthropic's own effort control instead — note that this is `effort`, not the OpenAI-style `reasoningEffort`, which every Claude id drops:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
await generateText({
|
|
91
|
+
model: neon('claude-opus-5'),
|
|
92
|
+
prompt: 'Plan a schema migration.',
|
|
93
|
+
providerOptions: { anthropic: { effort: 'high' } }, // low | medium | high | xhigh | max
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Query the rules directly rather than hardcoding them:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { getNeonModelCapabilities } from '@neon/ai-sdk-provider';
|
|
101
|
+
|
|
102
|
+
getNeonModelCapabilities('claude-opus-5').supportsTemperature; // false
|
|
103
|
+
getNeonModelCapabilities('glm-5-2').supportsPenalties; // false
|
|
104
|
+
```
|
|
105
|
+
|
|
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
|
+
|
|
108
|
+
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.
|
|
67
109
|
|
|
68
110
|
## Image generation
|
|
69
111
|
|
|
@@ -181,4 +223,4 @@ cp .env.example .env # fill NEON_AI_GATEWAY_BASE_URL + NEON_AI_GATEWAY_TOKEN f
|
|
|
181
223
|
pnpm test:e2e
|
|
182
224
|
```
|
|
183
225
|
|
|
184
|
-
The matrix covers one models.dev `neon` model per family (Anthropic, OpenAI, Codex, Gemini, Meta, Alibaba) 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.
|
|
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.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NeonAnthropicLanguageModel } from "./lib/neon-anthropic-language-model.js";
|
|
2
2
|
import { NeonChatLanguageModel } from "./lib/neon-chat-language-model.js";
|
|
3
|
-
import {
|
|
3
|
+
import { NEON_MODELS_DEV_IDS, NeonChatModelId, NeonKnownModelId } from "./lib/neon-chat-options.js";
|
|
4
4
|
import { NeonModelCapabilities, NeonModelFamily, NeonModelRoute, getNeonModelCapabilities, getNeonModelRoute } from "./lib/neon-model-capabilities.js";
|
|
5
5
|
import { NeonResponsesLanguageModel } from "./lib/neon-responses-language-model.js";
|
|
6
6
|
import { NeonErrorData, NeonProvider, NeonProviderSettings, createNeon, neon } from "./lib/provider.js";
|
|
7
|
-
export {
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { NeonAnthropicLanguageModel } from "./lib/neon-anthropic-language-model.js";
|
|
2
1
|
import { getNeonModelCapabilities, getNeonModelRoute } from "./lib/neon-model-capabilities.js";
|
|
2
|
+
import { NeonAnthropicLanguageModel } from "./lib/neon-anthropic-language-model.js";
|
|
3
3
|
import { NeonChatLanguageModel } from "./lib/neon-chat-language-model.js";
|
|
4
|
-
import {
|
|
4
|
+
import { NEON_MODELS_DEV_IDS } from "./lib/neon-chat-options.js";
|
|
5
5
|
import { NeonResponsesLanguageModel } from "./lib/neon-responses-language-model.js";
|
|
6
6
|
import { createNeon, neon } from "./lib/provider.js";
|
|
7
|
-
export {
|
|
7
|
+
export { NEON_MODELS_DEV_IDS, NeonAnthropicLanguageModel, NeonChatLanguageModel, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
|
|
@@ -12,6 +12,14 @@ import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3GenerateRes
|
|
|
12
12
|
* (`eager_input_streaming: true`) on streaming tool calls, which the gateway
|
|
13
13
|
* rejects (`Extra inputs are not permitted`). We disable it via the model's own
|
|
14
14
|
* `toolStreaming` option so streaming tool calls work. Users can still override.
|
|
15
|
+
*
|
|
16
|
+
* Capability filtering runs here as well as on the unified endpoint. The
|
|
17
|
+
* upstream Anthropic model drops sampling parameters for the Claude versions it
|
|
18
|
+
* knows about, but not for ones released after it, so `claude-opus-5` and
|
|
19
|
+
* `claude-sonnet-5` reached the API and came back
|
|
20
|
+
* `` `temperature` is deprecated for this model ``. Applying our own rules keeps
|
|
21
|
+
* the behaviour tied to what the gateway was measured to accept rather than to
|
|
22
|
+
* the SDK's release date.
|
|
15
23
|
*/
|
|
16
24
|
declare class NeonAnthropicLanguageModel extends AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
17
25
|
private withGatewayCompat;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-anthropic-language-model.d.ts","names":[],"sources":["../../src/lib/neon-anthropic-language-model.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"neon-anthropic-language-model.d.ts","names":[],"sources":["../../src/lib/neon-anthropic-language-model.ts"],"mappings":";;;;;;;AA+BA;AACC;AA6BU;AACC;AAAR;AASO;AACC;AAAR;AAxCK;AACG;AAAe;;;;;;cAFd,0BAAA,SACJ,8BAAA,YACG;;sBA4BD,6BACP,QAAQ;oBASD,6BACP,QAAQ"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyNeonCapabilities, mergeStreamStartWarnings } from "./neon-capabilities.js";
|
|
1
2
|
import { AnthropicMessagesLanguageModel } from "@ai-sdk/anthropic/internal";
|
|
2
3
|
//#region src/lib/neon-anthropic-language-model.ts
|
|
3
4
|
/**
|
|
@@ -9,27 +10,52 @@ import { AnthropicMessagesLanguageModel } from "@ai-sdk/anthropic/internal";
|
|
|
9
10
|
* (`eager_input_streaming: true`) on streaming tool calls, which the gateway
|
|
10
11
|
* rejects (`Extra inputs are not permitted`). We disable it via the model's own
|
|
11
12
|
* `toolStreaming` option so streaming tool calls work. Users can still override.
|
|
13
|
+
*
|
|
14
|
+
* Capability filtering runs here as well as on the unified endpoint. The
|
|
15
|
+
* upstream Anthropic model drops sampling parameters for the Claude versions it
|
|
16
|
+
* knows about, but not for ones released after it, so `claude-opus-5` and
|
|
17
|
+
* `claude-sonnet-5` reached the API and came back
|
|
18
|
+
* `` `temperature` is deprecated for this model ``. Applying our own rules keeps
|
|
19
|
+
* the behaviour tied to what the gateway was measured to accept rather than to
|
|
20
|
+
* the SDK's release date.
|
|
12
21
|
*/
|
|
13
22
|
var NeonAnthropicLanguageModel = class extends AnthropicMessagesLanguageModel {
|
|
14
23
|
withGatewayCompat(options) {
|
|
15
|
-
const
|
|
16
|
-
|
|
24
|
+
const { options: adjusted, warnings } = applyNeonCapabilities(this.modelId, options);
|
|
25
|
+
const anthropic = adjusted.providerOptions?.anthropic;
|
|
26
|
+
if (anthropic != null && "toolStreaming" in anthropic) return {
|
|
27
|
+
options: adjusted,
|
|
28
|
+
warnings
|
|
29
|
+
};
|
|
17
30
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
options: {
|
|
32
|
+
...adjusted,
|
|
33
|
+
providerOptions: {
|
|
34
|
+
...adjusted.providerOptions,
|
|
35
|
+
anthropic: {
|
|
36
|
+
...anthropic,
|
|
37
|
+
toolStreaming: false
|
|
38
|
+
}
|
|
24
39
|
}
|
|
25
|
-
}
|
|
40
|
+
},
|
|
41
|
+
warnings
|
|
26
42
|
};
|
|
27
43
|
}
|
|
28
|
-
doGenerate(options) {
|
|
29
|
-
|
|
44
|
+
async doGenerate(options) {
|
|
45
|
+
const { options: adjusted, warnings } = this.withGatewayCompat(options);
|
|
46
|
+
const result = await super.doGenerate(adjusted);
|
|
47
|
+
return warnings.length > 0 ? {
|
|
48
|
+
...result,
|
|
49
|
+
warnings: [...warnings, ...result.warnings]
|
|
50
|
+
} : result;
|
|
30
51
|
}
|
|
31
|
-
doStream(options) {
|
|
32
|
-
|
|
52
|
+
async doStream(options) {
|
|
53
|
+
const { options: adjusted, warnings } = this.withGatewayCompat(options);
|
|
54
|
+
const result = await super.doStream(adjusted);
|
|
55
|
+
return warnings.length > 0 ? {
|
|
56
|
+
...result,
|
|
57
|
+
stream: result.stream.pipeThrough(mergeStreamStartWarnings(warnings))
|
|
58
|
+
} : result;
|
|
33
59
|
}
|
|
34
60
|
};
|
|
35
61
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-anthropic-language-model.js","names":[],"sources":["../../src/lib/neon-anthropic-language-model.ts"],"sourcesContent":["import { AnthropicMessagesLanguageModel } from \"@ai-sdk/anthropic/internal\";\nimport type {\n\tLanguageModelV3,\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3GenerateResult,\n\tLanguageModelV3StreamResult,\n} from \"@ai-sdk/provider\";\n\n/**\n * Language model for Anthropic models served via the Neon AI Gateway's native\n * Messages API (`/anthropic/v1/messages`). Unlocks streaming\n * structured output and native reasoning for Claude.\n *\n * The shared Anthropic model defaults to fine-grained tool-input streaming\n * (`eager_input_streaming: true`) on streaming tool calls, which the gateway\n * rejects (`Extra inputs are not permitted`). We disable it via the model's own\n * `toolStreaming` option so streaming tool calls work. Users can still override.\n */\nexport class NeonAnthropicLanguageModel\n\textends AnthropicMessagesLanguageModel\n\timplements LanguageModelV3\n{\n\tprivate withGatewayCompat(\n\t\toptions: LanguageModelV3CallOptions
|
|
1
|
+
{"version":3,"file":"neon-anthropic-language-model.js","names":[],"sources":["../../src/lib/neon-anthropic-language-model.ts"],"sourcesContent":["import { AnthropicMessagesLanguageModel } from \"@ai-sdk/anthropic/internal\";\nimport type {\n\tLanguageModelV3,\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3GenerateResult,\n\tLanguageModelV3StreamResult,\n\tSharedV3Warning,\n} from \"@ai-sdk/provider\";\nimport {\n\tapplyNeonCapabilities,\n\tmergeStreamStartWarnings,\n} from \"./neon-capabilities.js\";\n\n/**\n * Language model for Anthropic models served via the Neon AI Gateway's native\n * Messages API (`/anthropic/v1/messages`). Unlocks streaming\n * structured output and native reasoning for Claude.\n *\n * The shared Anthropic model defaults to fine-grained tool-input streaming\n * (`eager_input_streaming: true`) on streaming tool calls, which the gateway\n * rejects (`Extra inputs are not permitted`). We disable it via the model's own\n * `toolStreaming` option so streaming tool calls work. Users can still override.\n *\n * Capability filtering runs here as well as on the unified endpoint. The\n * upstream Anthropic model drops sampling parameters for the Claude versions it\n * knows about, but not for ones released after it, so `claude-opus-5` and\n * `claude-sonnet-5` reached the API and came back\n * `` `temperature` is deprecated for this model ``. Applying our own rules keeps\n * the behaviour tied to what the gateway was measured to accept rather than to\n * the SDK's release date.\n */\nexport class NeonAnthropicLanguageModel\n\textends AnthropicMessagesLanguageModel\n\timplements LanguageModelV3\n{\n\tprivate withGatewayCompat(options: LanguageModelV3CallOptions): {\n\t\toptions: LanguageModelV3CallOptions;\n\t\twarnings: SharedV3Warning[];\n\t} {\n\t\tconst { options: adjusted, warnings } = applyNeonCapabilities(\n\t\t\tthis.modelId,\n\t\t\toptions,\n\t\t);\n\t\tconst anthropic = adjusted.providerOptions?.anthropic;\n\t\t// Respect an explicit user setting.\n\t\tif (anthropic != null && \"toolStreaming\" in anthropic) {\n\t\t\treturn { options: adjusted, warnings };\n\t\t}\n\t\treturn {\n\t\t\toptions: {\n\t\t\t\t...adjusted,\n\t\t\t\tproviderOptions: {\n\t\t\t\t\t...adjusted.providerOptions,\n\t\t\t\t\tanthropic: { ...anthropic, toolStreaming: false },\n\t\t\t\t},\n\t\t\t},\n\t\t\twarnings,\n\t\t};\n\t}\n\n\toverride async doGenerate(\n\t\toptions: LanguageModelV3CallOptions,\n\t): Promise<LanguageModelV3GenerateResult> {\n\t\tconst { options: adjusted, warnings } = this.withGatewayCompat(options);\n\t\tconst result = await super.doGenerate(adjusted);\n\t\treturn warnings.length > 0\n\t\t\t? { ...result, warnings: [...warnings, ...result.warnings] }\n\t\t\t: result;\n\t}\n\n\toverride async doStream(\n\t\toptions: LanguageModelV3CallOptions,\n\t): Promise<LanguageModelV3StreamResult> {\n\t\tconst { options: adjusted, warnings } = this.withGatewayCompat(options);\n\t\tconst result = await super.doStream(adjusted);\n\t\treturn warnings.length > 0\n\t\t\t? {\n\t\t\t\t\t...result,\n\t\t\t\t\tstream: result.stream.pipeThrough(\n\t\t\t\t\t\tmergeStreamStartWarnings(warnings),\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: result;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA+BA,IAAa,6BAAb,cACS,+BAET;CACC,kBAA0B,SAGxB;EACD,MAAM,EAAE,SAAS,UAAU,aAAa,sBACvC,KAAK,SACL,OACD;EACA,MAAM,YAAY,SAAS,iBAAiB;EAE5C,IAAI,aAAa,QAAQ,mBAAmB,WAC3C,OAAO;GAAE,SAAS;GAAU;EAAS;EAEtC,OAAO;GACN,SAAS;IACR,GAAG;IACH,iBAAiB;KAChB,GAAG,SAAS;KACZ,WAAW;MAAE,GAAG;MAAW,eAAe;KAAM;IACjD;GACD;GACA;EACD;CACD;CAEA,MAAe,WACd,SACyC;EACzC,MAAM,EAAE,SAAS,UAAU,aAAa,KAAK,kBAAkB,OAAO;EACtE,MAAM,SAAS,MAAM,MAAM,WAAW,QAAQ;EAC9C,OAAO,SAAS,SAAS,IACtB;GAAE,GAAG;GAAQ,UAAU,CAAC,GAAG,UAAU,GAAG,OAAO,QAAQ;EAAE,IACzD;CACJ;CAEA,MAAe,SACd,SACuC;EACvC,MAAM,EAAE,SAAS,UAAU,aAAa,KAAK,kBAAkB,OAAO;EACtE,MAAM,SAAS,MAAM,MAAM,SAAS,QAAQ;EAC5C,OAAO,SAAS,SAAS,IACtB;GACA,GAAG;GACH,QAAQ,OAAO,OAAO,YACrB,yBAAyB,QAAQ,CAClC;EACD,IACC;CACJ;AACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-capabilities.d.ts","names":[],"sources":["../../src/lib/neon-capabilities.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"neon-capabilities.d.ts","names":[],"sources":["../../src/lib/neon-capabilities.ts"],"mappings":";;;;;;AAoBA;AAAqC;AAE3B;AACI,iBAHE,qBAAA,CAGF,OAAA,EAAA,MAAA,EAAA,OAAA,EADJ,0BACI,CAAA,EAAA;EAAsC,OAAA,EAAtC,0BAAsC;EAAe,QAAA,EAAf,eAAe,EAAA;AA6GnE,CAAA;AAAwC;AAAQ;AAAiB;AAAA,iBAAjD,wBAAA,CAAiD,KAAA,EAAjB,eAAiB,EAAA,CAAA,EAAA,eAAA,CAAA,yBAAA,EAAA,yBAAA,CAAA"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getNeonModelCapabilities } from "./neon-model-capabilities.js";
|
|
2
2
|
//#region src/lib/neon-capabilities.ts
|
|
3
|
+
const PENALTY_DETAILS = "The request was sent without it. Check getNeonModelCapabilities('<model-id>').supportsPenalties before setting penalties.";
|
|
4
|
+
const GENERIC_DETAILS = "The request was sent without it. Call getNeonModelCapabilities('<model-id>') to see which sampling parameters this model takes, or steer it through the prompt.";
|
|
5
|
+
const REASONING_EFFORT_DETAILS = "The request was sent without it. Claude takes providerOptions.anthropic.effort instead.";
|
|
3
6
|
/**
|
|
4
7
|
* Drop call options the resolved model's upstream backend does not accept and
|
|
5
8
|
* collect a warning for each, so callers get a clear signal instead of a hard
|
|
@@ -9,20 +12,23 @@ function applyNeonCapabilities(modelId, options) {
|
|
|
9
12
|
const caps = getNeonModelCapabilities(modelId);
|
|
10
13
|
const warnings = [];
|
|
11
14
|
const patch = {};
|
|
12
|
-
const
|
|
15
|
+
const drop = (feature, details, type = "unsupported") => {
|
|
13
16
|
warnings.push({
|
|
14
|
-
type
|
|
17
|
+
type,
|
|
15
18
|
feature,
|
|
16
|
-
details
|
|
19
|
+
details
|
|
17
20
|
});
|
|
18
21
|
};
|
|
22
|
+
const unrecognizedClaude = caps.claudeSamplingUnrecognized === true;
|
|
23
|
+
const samplingDetails = unrecognizedClaude ? "This Claude version was not recognised. Claude 4.7 and newer reject sampling parameters, so the request was sent without it as a precaution; set `providerOptions.anthropic.effort` instead." : caps.family === "anthropic" ? "The request was sent without it. Claude 4.7 and newer reject sampling parameters; use `providerOptions.anthropic.effort` (low | medium | high | xhigh | max) to steer these models." : GENERIC_DETAILS;
|
|
24
|
+
const samplingType = unrecognizedClaude ? "compatibility" : "unsupported";
|
|
19
25
|
if (options.temperature != null && !caps.supportsTemperature) {
|
|
20
26
|
patch.temperature = void 0;
|
|
21
|
-
|
|
27
|
+
drop("temperature", samplingDetails, samplingType);
|
|
22
28
|
}
|
|
23
29
|
if (options.topP != null && !caps.supportsTopP) {
|
|
24
30
|
patch.topP = void 0;
|
|
25
|
-
|
|
31
|
+
drop("topP", samplingDetails, samplingType);
|
|
26
32
|
}
|
|
27
33
|
const effectiveTemperature = "temperature" in patch ? patch.temperature : options.temperature;
|
|
28
34
|
const effectiveTopP = "topP" in patch ? patch.topP : options.topP;
|
|
@@ -31,29 +37,29 @@ function applyNeonCapabilities(modelId, options) {
|
|
|
31
37
|
warnings.push({
|
|
32
38
|
type: "compatibility",
|
|
33
39
|
feature: "topP",
|
|
34
|
-
details:
|
|
40
|
+
details: "Anthropic models accept only one of temperature or topP; topP was dropped and temperature kept."
|
|
35
41
|
});
|
|
36
42
|
}
|
|
37
43
|
if (options.frequencyPenalty != null && !caps.supportsPenalties) {
|
|
38
44
|
patch.frequencyPenalty = void 0;
|
|
39
|
-
|
|
45
|
+
drop("frequencyPenalty", PENALTY_DETAILS);
|
|
40
46
|
}
|
|
41
47
|
if (options.presencePenalty != null && !caps.supportsPenalties) {
|
|
42
48
|
patch.presencePenalty = void 0;
|
|
43
|
-
|
|
49
|
+
drop("presencePenalty", PENALTY_DETAILS);
|
|
44
50
|
}
|
|
45
51
|
if (options.seed != null && !caps.supportsSeed) {
|
|
46
52
|
patch.seed = void 0;
|
|
47
|
-
|
|
53
|
+
drop("seed", GENERIC_DETAILS);
|
|
48
54
|
}
|
|
49
55
|
if (options.stopSequences != null && !caps.supportsStopSequences) {
|
|
50
56
|
patch.stopSequences = void 0;
|
|
51
|
-
|
|
57
|
+
drop("stopSequences", GENERIC_DETAILS);
|
|
52
58
|
}
|
|
53
59
|
if (!caps.supportsReasoningEffort) {
|
|
54
60
|
const { providerOptions } = options;
|
|
55
61
|
if (providerOptions != null && Object.values(providerOptions).some((group) => "reasoningEffort" in group && group.reasoningEffort != null)) {
|
|
56
|
-
|
|
62
|
+
drop("reasoningEffort", REASONING_EFFORT_DETAILS);
|
|
57
63
|
if (providerOptions != null) {
|
|
58
64
|
const cleaned = {};
|
|
59
65
|
for (const [key, group] of Object.entries(providerOptions)) if ("reasoningEffort" in group) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-capabilities.js","names":[],"sources":["../../src/lib/neon-capabilities.ts"],"sourcesContent":["import type {\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3StreamPart,\n\tSharedV3ProviderOptions,\n\tSharedV3Warning,\n} from \"@ai-sdk/provider\";\nimport { getNeonModelCapabilities } from \"./neon-model-capabilities.js\";\n\n/**\n * Drop call options the resolved model's upstream backend does not accept and\n * collect a warning for each, so callers get a clear signal instead of a hard\n * `400` from the gateway.\n */\nexport function applyNeonCapabilities(\n\tmodelId: string,\n\toptions: LanguageModelV3CallOptions,\n): { options: LanguageModelV3CallOptions; warnings: SharedV3Warning[] } {\n\tconst caps = getNeonModelCapabilities(modelId);\n\tconst warnings: SharedV3Warning[] = [];\n\tconst patch: Partial<LanguageModelV3CallOptions> = {};\n\n\tconst
|
|
1
|
+
{"version":3,"file":"neon-capabilities.js","names":[],"sources":["../../src/lib/neon-capabilities.ts"],"sourcesContent":["import type {\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3StreamPart,\n\tSharedV3ProviderOptions,\n\tSharedV3Warning,\n} from \"@ai-sdk/provider\";\nimport { getNeonModelCapabilities } from \"./neon-model-capabilities.js\";\n\nconst PENALTY_DETAILS =\n\t\"The request was sent without it. Check getNeonModelCapabilities('<model-id>').supportsPenalties before setting penalties.\";\nconst GENERIC_DETAILS =\n\t\"The request was sent without it. Call getNeonModelCapabilities('<model-id>') to see which sampling parameters this model takes, or steer it through the prompt.\";\nconst REASONING_EFFORT_DETAILS =\n\t\"The request was sent without it. Claude takes providerOptions.anthropic.effort instead.\";\n\n/**\n * Drop call options the resolved model's upstream backend does not accept and\n * collect a warning for each, so callers get a clear signal instead of a hard\n * `400` from the gateway.\n */\nexport function applyNeonCapabilities(\n\tmodelId: string,\n\toptions: LanguageModelV3CallOptions,\n): { options: LanguageModelV3CallOptions; warnings: SharedV3Warning[] } {\n\tconst caps = getNeonModelCapabilities(modelId);\n\tconst warnings: SharedV3Warning[] = [];\n\tconst patch: Partial<LanguageModelV3CallOptions> = {};\n\n\t// The AI SDK prints the provider, the model id and the feature name ahead of\n\t// `details`, so each reason says only what those cannot: that the call still\n\t// went out, and what to reach for instead. Restating the feature name or\n\t// asserting a 400 for the unrecognised case is the overstatement this\n\t// wording exists to avoid.\n\tconst drop = (\n\t\tfeature: string,\n\t\tdetails: string,\n\t\ttype: \"unsupported\" | \"compatibility\" = \"unsupported\",\n\t) => {\n\t\twarnings.push({ type, feature, details });\n\t};\n\n\t// Precautionary rather than measured, so it is flagged as a compatibility\n\t// choice: the SDK renders `unsupported` as a flat \"is not supported\", which\n\t// would contradict the hedge in the sentence that follows it.\n\tconst unrecognizedClaude = caps.claudeSamplingUnrecognized === true;\n\tconst samplingDetails = unrecognizedClaude\n\t\t? \"This Claude version was not recognised. Claude 4.7 and newer reject sampling parameters, so the request was sent without it as a precaution; set `providerOptions.anthropic.effort` instead.\"\n\t\t: caps.family === \"anthropic\"\n\t\t\t? \"The request was sent without it. Claude 4.7 and newer reject sampling parameters; use `providerOptions.anthropic.effort` (low | medium | high | xhigh | max) to steer these models.\"\n\t\t\t: GENERIC_DETAILS;\n\tconst samplingType = unrecognizedClaude ? \"compatibility\" : \"unsupported\";\n\n\tif (options.temperature != null && !caps.supportsTemperature) {\n\t\tpatch.temperature = undefined;\n\t\tdrop(\"temperature\", samplingDetails, samplingType);\n\t}\n\tif (options.topP != null && !caps.supportsTopP) {\n\t\tpatch.topP = undefined;\n\t\tdrop(\"topP\", samplingDetails, samplingType);\n\t}\n\n\t// Anthropic-style models accept only one of temperature / topP.\n\tconst effectiveTemperature =\n\t\t\"temperature\" in patch ? patch.temperature : options.temperature;\n\tconst effectiveTopP = \"topP\" in patch ? patch.topP : options.topP;\n\tif (\n\t\tcaps.temperatureTopPMutuallyExclusive &&\n\t\teffectiveTemperature != null &&\n\t\teffectiveTopP != null\n\t) {\n\t\tpatch.topP = undefined;\n\t\twarnings.push({\n\t\t\ttype: \"compatibility\",\n\t\t\tfeature: \"topP\",\n\t\t\tdetails:\n\t\t\t\t\"Anthropic models accept only one of temperature or topP; topP was dropped and temperature kept.\",\n\t\t});\n\t}\n\n\tif (options.frequencyPenalty != null && !caps.supportsPenalties) {\n\t\tpatch.frequencyPenalty = undefined;\n\t\tdrop(\"frequencyPenalty\", PENALTY_DETAILS);\n\t}\n\tif (options.presencePenalty != null && !caps.supportsPenalties) {\n\t\tpatch.presencePenalty = undefined;\n\t\tdrop(\"presencePenalty\", PENALTY_DETAILS);\n\t}\n\tif (options.seed != null && !caps.supportsSeed) {\n\t\tpatch.seed = undefined;\n\t\tdrop(\"seed\", GENERIC_DETAILS);\n\t}\n\n\tif (options.stopSequences != null && !caps.supportsStopSequences) {\n\t\tpatch.stopSequences = undefined;\n\t\tdrop(\"stopSequences\", GENERIC_DETAILS);\n\t}\n\n\tif (!caps.supportsReasoningEffort) {\n\t\tconst { providerOptions } = options;\n\t\tconst hasProviderEffort =\n\t\t\tproviderOptions != null &&\n\t\t\tObject.values(providerOptions).some(\n\t\t\t\t(group) =>\n\t\t\t\t\t\"reasoningEffort\" in group && group.reasoningEffort != null,\n\t\t\t);\n\n\t\tif (hasProviderEffort) {\n\t\t\tdrop(\"reasoningEffort\", REASONING_EFFORT_DETAILS);\n\t\t\tif (providerOptions != null) {\n\t\t\t\tconst cleaned: SharedV3ProviderOptions = {};\n\t\t\t\tfor (const [key, group] of Object.entries(providerOptions)) {\n\t\t\t\t\tif (\"reasoningEffort\" in group) {\n\t\t\t\t\t\tconst { reasoningEffort: _removed, ...rest } = group;\n\t\t\t\t\t\tcleaned[key] = rest;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcleaned[key] = group;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpatch.providerOptions = cleaned;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (Object.keys(patch).length === 0) {\n\t\treturn { options, warnings };\n\t}\n\treturn { options: { ...options, ...patch }, warnings };\n}\n\n/**\n * Merge additional warnings into the `stream-start` part of a model stream.\n */\nexport function mergeStreamStartWarnings(extra: SharedV3Warning[]) {\n\tlet merged = false;\n\treturn new TransformStream<\n\t\tLanguageModelV3StreamPart,\n\t\tLanguageModelV3StreamPart\n\t>({\n\t\ttransform(part, controller) {\n\t\t\tif (!merged && part.type === \"stream-start\") {\n\t\t\t\tmerged = true;\n\t\t\t\tcontroller.enqueue({\n\t\t\t\t\t...part,\n\t\t\t\t\twarnings: [...extra, ...part.warnings],\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tcontroller.enqueue(part);\n\t\t\t}\n\t\t},\n\t});\n}\n"],"mappings":";;AAQA,MAAM,kBACL;AACD,MAAM,kBACL;AACD,MAAM,2BACL;;;;;;AAOD,SAAgB,sBACf,SACA,SACuE;CACvE,MAAM,OAAO,yBAAyB,OAAO;CAC7C,MAAM,WAA8B,CAAC;CACrC,MAAM,QAA6C,CAAC;CAOpD,MAAM,QACL,SACA,SACA,OAAwC,kBACpC;EACJ,SAAS,KAAK;GAAE;GAAM;GAAS;EAAQ,CAAC;CACzC;CAKA,MAAM,qBAAqB,KAAK,+BAA+B;CAC/D,MAAM,kBAAkB,qBACrB,iMACA,KAAK,WAAW,cACf,wLACA;CACJ,MAAM,eAAe,qBAAqB,kBAAkB;CAE5D,IAAI,QAAQ,eAAe,QAAQ,CAAC,KAAK,qBAAqB;EAC7D,MAAM,cAAc,KAAA;EACpB,KAAK,eAAe,iBAAiB,YAAY;CAClD;CACA,IAAI,QAAQ,QAAQ,QAAQ,CAAC,KAAK,cAAc;EAC/C,MAAM,OAAO,KAAA;EACb,KAAK,QAAQ,iBAAiB,YAAY;CAC3C;CAGA,MAAM,uBACL,iBAAiB,QAAQ,MAAM,cAAc,QAAQ;CACtD,MAAM,gBAAgB,UAAU,QAAQ,MAAM,OAAO,QAAQ;CAC7D,IACC,KAAK,oCACL,wBAAwB,QACxB,iBAAiB,MAChB;EACD,MAAM,OAAO,KAAA;EACb,SAAS,KAAK;GACb,MAAM;GACN,SAAS;GACT,SACC;EACF,CAAC;CACF;CAEA,IAAI,QAAQ,oBAAoB,QAAQ,CAAC,KAAK,mBAAmB;EAChE,MAAM,mBAAmB,KAAA;EACzB,KAAK,oBAAoB,eAAe;CACzC;CACA,IAAI,QAAQ,mBAAmB,QAAQ,CAAC,KAAK,mBAAmB;EAC/D,MAAM,kBAAkB,KAAA;EACxB,KAAK,mBAAmB,eAAe;CACxC;CACA,IAAI,QAAQ,QAAQ,QAAQ,CAAC,KAAK,cAAc;EAC/C,MAAM,OAAO,KAAA;EACb,KAAK,QAAQ,eAAe;CAC7B;CAEA,IAAI,QAAQ,iBAAiB,QAAQ,CAAC,KAAK,uBAAuB;EACjE,MAAM,gBAAgB,KAAA;EACtB,KAAK,iBAAiB,eAAe;CACtC;CAEA,IAAI,CAAC,KAAK,yBAAyB;EAClC,MAAM,EAAE,oBAAoB;EAQ5B,IANC,mBAAmB,QACnB,OAAO,OAAO,eAAe,CAAC,CAAC,MAC7B,UACA,qBAAqB,SAAS,MAAM,mBAAmB,IACzD,GAEsB;GACtB,KAAK,mBAAmB,wBAAwB;GAChD,IAAI,mBAAmB,MAAM;IAC5B,MAAM,UAAmC,CAAC;IAC1C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,eAAe,GACxD,IAAI,qBAAqB,OAAO;KAC/B,MAAM,EAAE,iBAAiB,UAAU,GAAG,SAAS;KAC/C,QAAQ,OAAO;IAChB,OACC,QAAQ,OAAO;IAGjB,MAAM,kBAAkB;GACzB;EACD;CACD;CAEA,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,GACjC,OAAO;EAAE;EAAS;CAAS;CAE5B,OAAO;EAAE,SAAS;GAAE,GAAG;GAAS,GAAG;EAAM;EAAG;CAAS;AACtD;;;;AAKA,SAAgB,yBAAyB,OAA0B;CAClE,IAAI,SAAS;CACb,OAAO,IAAI,gBAGT,EACD,UAAU,MAAM,YAAY;EAC3B,IAAI,CAAC,UAAU,KAAK,SAAS,gBAAgB;GAC5C,SAAS;GACT,WAAW,QAAQ;IAClB,GAAG;IACH,UAAU,CAAC,GAAG,OAAO,GAAG,KAAK,QAAQ;GACtC,CAAC;EACF,OACC,WAAW,QAAQ,IAAI;CAEzB,EACD,CAAC;AACF"}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
//#region src/lib/neon-chat-options.d.ts
|
|
2
2
|
/** The models.dev `neon` catalog (canonical, unprefixed ids). */
|
|
3
|
-
declare const NEON_MODELS_DEV_IDS: readonly ["claude-
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
* Verified to resolve against the gateway; retained for autocomplete. If
|
|
7
|
-
* models.dev later lists one of these, the drift check flags it for promotion
|
|
8
|
-
* into `NEON_MODELS_DEV_IDS`.
|
|
9
|
-
*/
|
|
10
|
-
declare const NEON_EXTRA_MODEL_IDS: readonly ["claude-opus-4-8", "gpt-5-2-codex", "gpt-5-3-codex", "gpt-5-5-pro", "gemini-3-5-flash", "gemma-3-12b", "llama-4-maverick", "meta-llama-3-3-70b-instruct", "meta-llama-3-1-8b-instruct", "qwen3-next-80b-a3b-instruct", "qwen35-122b-a10b"];
|
|
11
|
-
/** A known Neon AI Gateway model id (models.dev catalog + gateway extras). */
|
|
12
|
-
type NeonKnownModelId = (typeof NEON_MODELS_DEV_IDS)[number] | (typeof NEON_EXTRA_MODEL_IDS)[number];
|
|
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"];
|
|
4
|
+
/** A known Neon AI Gateway model id. */
|
|
5
|
+
type NeonKnownModelId = (typeof NEON_MODELS_DEV_IDS)[number];
|
|
13
6
|
/**
|
|
14
7
|
* A Neon AI Gateway model id. Known ids are listed for autocomplete; any other
|
|
15
8
|
* id (e.g. a brand-new model, or the legacy `databricks-` prefixed form) is
|
|
@@ -17,5 +10,5 @@ type NeonKnownModelId = (typeof NEON_MODELS_DEV_IDS)[number] | (typeof NEON_EXTR
|
|
|
17
10
|
*/
|
|
18
11
|
type NeonChatModelId = NeonKnownModelId | (string & {});
|
|
19
12
|
//#endregion
|
|
20
|
-
export {
|
|
13
|
+
export { NEON_MODELS_DEV_IDS, NeonChatModelId, NeonKnownModelId };
|
|
21
14
|
//# sourceMappingURL=neon-chat-options.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-chat-options.d.ts","names":[],"sources":["../../src/lib/neon-chat-options.ts"],"mappings":";;
|
|
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,52 +1,50 @@
|
|
|
1
1
|
//#region src/lib/neon-chat-options.ts
|
|
2
2
|
/** The models.dev `neon` catalog (canonical, unprefixed ids). */
|
|
3
3
|
const NEON_MODELS_DEV_IDS = [
|
|
4
|
-
"claude-
|
|
5
|
-
"claude-
|
|
6
|
-
"claude-opus-4-5",
|
|
4
|
+
"claude-fable-5",
|
|
5
|
+
"claude-haiku-4-5",
|
|
7
6
|
"claude-opus-4-1",
|
|
8
|
-
"claude-
|
|
7
|
+
"claude-opus-4-5",
|
|
8
|
+
"claude-opus-4-6",
|
|
9
|
+
"claude-opus-4-7",
|
|
10
|
+
"claude-opus-4-8",
|
|
11
|
+
"claude-opus-5",
|
|
9
12
|
"claude-sonnet-4-5",
|
|
10
|
-
"claude-sonnet-4",
|
|
11
|
-
"claude-
|
|
13
|
+
"claude-sonnet-4-6",
|
|
14
|
+
"claude-sonnet-5",
|
|
12
15
|
"gpt-5",
|
|
13
|
-
"gpt-5-mini",
|
|
14
|
-
"gpt-5-nano",
|
|
15
16
|
"gpt-5-1",
|
|
16
17
|
"gpt-5-2",
|
|
18
|
+
"gpt-5-3-codex",
|
|
17
19
|
"gpt-5-4",
|
|
18
20
|
"gpt-5-4-mini",
|
|
19
21
|
"gpt-5-4-nano",
|
|
20
22
|
"gpt-5-5",
|
|
23
|
+
"gpt-5-5-pro",
|
|
24
|
+
"gpt-5-6-luna",
|
|
25
|
+
"gpt-5-6-sol",
|
|
26
|
+
"gpt-5-6-terra",
|
|
27
|
+
"gpt-5-mini",
|
|
28
|
+
"gpt-5-nano",
|
|
21
29
|
"gpt-oss-120b",
|
|
22
30
|
"gpt-oss-20b",
|
|
23
|
-
"gemini-3-pro",
|
|
24
|
-
"gemini-3-flash",
|
|
25
|
-
"gemini-3-1-pro",
|
|
26
31
|
"gemini-3-1-flash-lite",
|
|
27
|
-
"gemini-
|
|
28
|
-
"gemini-2-5-flash"
|
|
29
|
-
];
|
|
30
|
-
/**
|
|
31
|
-
* Ids the gateway serves that the models.dev `neon` provider does not list.
|
|
32
|
-
* Verified to resolve against the gateway; retained for autocomplete. If
|
|
33
|
-
* models.dev later lists one of these, the drift check flags it for promotion
|
|
34
|
-
* into `NEON_MODELS_DEV_IDS`.
|
|
35
|
-
*/
|
|
36
|
-
const NEON_EXTRA_MODEL_IDS = [
|
|
37
|
-
"claude-opus-4-8",
|
|
38
|
-
"gpt-5-2-codex",
|
|
39
|
-
"gpt-5-3-codex",
|
|
40
|
-
"gpt-5-5-pro",
|
|
32
|
+
"gemini-3-1-pro",
|
|
41
33
|
"gemini-3-5-flash",
|
|
34
|
+
"gemini-3-5-flash-lite",
|
|
35
|
+
"gemini-3-6-flash",
|
|
36
|
+
"gemini-3-flash",
|
|
42
37
|
"gemma-3-12b",
|
|
43
38
|
"llama-4-maverick",
|
|
44
|
-
"meta-llama-3-3-70b-instruct",
|
|
45
39
|
"meta-llama-3-1-8b-instruct",
|
|
40
|
+
"meta-llama-3-3-70b-instruct",
|
|
46
41
|
"qwen3-next-80b-a3b-instruct",
|
|
47
|
-
"qwen35-122b-a10b"
|
|
42
|
+
"qwen35-122b-a10b",
|
|
43
|
+
"glm-5-2",
|
|
44
|
+
"inkling",
|
|
45
|
+
"kimi-k3"
|
|
48
46
|
];
|
|
49
47
|
//#endregion
|
|
50
|
-
export {
|
|
48
|
+
export { NEON_MODELS_DEV_IDS };
|
|
51
49
|
|
|
52
50
|
//# sourceMappingURL=neon-chat-options.js.map
|
|
@@ -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.\n//
|
|
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"}
|
|
@@ -25,6 +25,13 @@ interface NeonModelCapabilities {
|
|
|
25
25
|
supportsSeed: boolean;
|
|
26
26
|
supportsStopSequences: boolean;
|
|
27
27
|
supportsReasoningEffort: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* True when a `claude-*` id could not be parsed into a version and was
|
|
30
|
+
* assumed to be new. The sampling restriction is then a precaution rather
|
|
31
|
+
* than something measured, and the warning says so instead of asserting a
|
|
32
|
+
* capability nobody checked.
|
|
33
|
+
*/
|
|
34
|
+
claudeSamplingUnrecognized?: boolean;
|
|
28
35
|
}
|
|
29
36
|
/**
|
|
30
37
|
* Heuristic, prefix-based capability detection for MLflow-routed Neon models.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-model-capabilities.d.ts","names":[],"sources":["../../src/lib/neon-model-capabilities.ts"],"mappings":";KAAY,eAAA;AAAZ;AAiBA;AAEA;AAiBA;
|
|
1
|
+
{"version":3,"file":"neon-model-capabilities.d.ts","names":[],"sources":["../../src/lib/neon-model-capabilities.ts"],"mappings":";KAAY,eAAA;AAAZ;AAiBA;AAEA;AAiBA;AAiGA;;;;;;KApHY,cAAA;iBAEI,iBAAA,mBAAoC;UAiBnC,qBAAA;UACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgGO,wBAAA,mBAEb"}
|
|
@@ -5,6 +5,34 @@ function getNeonModelRoute(modelId) {
|
|
|
5
5
|
if (id.includes("gpt-") && !id.includes("gpt-oss") || id.includes("codex")) return "openai";
|
|
6
6
|
return "mlflow";
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Gemini models the gateway rejects penalties on, by measurement. Their older
|
|
10
|
+
* siblings accept them, so this cannot be derived from the id.
|
|
11
|
+
*
|
|
12
|
+
* Matched exactly rather than by substring: `gemini-3-6-flash` is a prefix of
|
|
13
|
+
* any `gemini-3-6-flash-*` id Google may ship next, and inheriting a measured
|
|
14
|
+
* restriction on the strength of a shared prefix is how the Gemini rule was
|
|
15
|
+
* wrong for these two in the first place.
|
|
16
|
+
*/
|
|
17
|
+
const GEMINI_NO_PENALTIES = /* @__PURE__ */ new Set(["gemini-3-5-flash-lite", "gemini-3-6-flash"]);
|
|
18
|
+
/** Gemini models that reject `temperature` and `topP` outright. */
|
|
19
|
+
const GEMINI_NO_SAMPLING = /* @__PURE__ */ new Set(["gemini-3-6-flash"]);
|
|
20
|
+
/**
|
|
21
|
+
* GPT-5 ids that reject `temperature` despite carrying a minor version.
|
|
22
|
+
*
|
|
23
|
+
* The version rule below reads `gpt-5-5-pro` as 5.5 and concludes it takes
|
|
24
|
+
* sampling parameters. It does not — the Responses API answers
|
|
25
|
+
* `Unsupported parameter: temperature`.
|
|
26
|
+
*/
|
|
27
|
+
const GPT5_NO_SAMPLING = /* @__PURE__ */ new Set(["gpt-5-5-pro"]);
|
|
28
|
+
/**
|
|
29
|
+
* The gateway accepts an optional `databricks-` prefix on any id, so strip it
|
|
30
|
+
* before an exact-match lookup. Prefix rules elsewhere in this file tolerate it
|
|
31
|
+
* incidentally; exact matches do not.
|
|
32
|
+
*/
|
|
33
|
+
function canonicalId(id) {
|
|
34
|
+
return id.startsWith("databricks-") ? id.slice(11) : id;
|
|
35
|
+
}
|
|
8
36
|
const PERMISSIVE = {
|
|
9
37
|
supportsTemperature: true,
|
|
10
38
|
supportsTopP: true,
|
|
@@ -15,6 +43,20 @@ const PERMISSIVE = {
|
|
|
15
43
|
supportsReasoningEffort: true
|
|
16
44
|
};
|
|
17
45
|
/**
|
|
46
|
+
* Whether a Claude id predates the 4.7 cutoff where the gateway stopped
|
|
47
|
+
* accepting `temperature` and `top_p`. Ids look like `claude-<tier>-<major>` or
|
|
48
|
+
* `claude-<tier>-<major>-<minor>`, optionally behind the legacy `databricks-`
|
|
49
|
+
* prefix. An id we cannot parse is treated as new, which is the safe direction.
|
|
50
|
+
*/
|
|
51
|
+
function claudeVersion(id) {
|
|
52
|
+
const match = /^(?:databricks-)?claude-[a-z]+-(\d+)(?:-(\d+))?$/.exec(id);
|
|
53
|
+
if (!match) return null;
|
|
54
|
+
return {
|
|
55
|
+
major: Number(match[1]),
|
|
56
|
+
minor: match[2] === void 0 ? 0 : Number(match[2])
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
18
60
|
* Heuristic, prefix-based capability detection for MLflow-routed Neon models.
|
|
19
61
|
*
|
|
20
62
|
* The unified endpoint proxies to heterogeneous upstream providers, each of
|
|
@@ -25,23 +67,34 @@ const PERMISSIVE = {
|
|
|
25
67
|
*/
|
|
26
68
|
function getNeonModelCapabilities(modelId) {
|
|
27
69
|
const id = modelId.toLowerCase();
|
|
28
|
-
if (id.includes("claude"))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
70
|
+
if (id.includes("claude")) {
|
|
71
|
+
const version = claudeVersion(id);
|
|
72
|
+
const sampling = version !== null && (version.major < 4 || version.major === 4 && version.minor <= 6);
|
|
73
|
+
return {
|
|
74
|
+
family: "anthropic",
|
|
75
|
+
supportsTemperature: sampling,
|
|
76
|
+
supportsTopP: sampling,
|
|
77
|
+
temperatureTopPMutuallyExclusive: true,
|
|
78
|
+
supportsPenalties: false,
|
|
79
|
+
supportsSeed: false,
|
|
80
|
+
supportsStopSequences: true,
|
|
81
|
+
supportsReasoningEffort: false,
|
|
82
|
+
...version === null ? { claudeSamplingUnrecognized: true } : {}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (id.includes("gemini")) {
|
|
86
|
+
const canonical = canonicalId(id);
|
|
87
|
+
const acceptsSampling = !GEMINI_NO_SAMPLING.has(canonical);
|
|
88
|
+
return {
|
|
89
|
+
family: "google",
|
|
90
|
+
...PERMISSIVE,
|
|
91
|
+
supportsTemperature: acceptsSampling,
|
|
92
|
+
supportsTopP: acceptsSampling,
|
|
93
|
+
supportsPenalties: !GEMINI_NO_PENALTIES.has(canonical)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
43
96
|
if (/gpt-5/.test(id)) {
|
|
44
|
-
const hasMinorVersion = /gpt-5[.-]\d/.test(id);
|
|
97
|
+
const hasMinorVersion = /gpt-5[.-]\d/.test(id) && !GPT5_NO_SAMPLING.has(canonicalId(id));
|
|
45
98
|
return {
|
|
46
99
|
family: "openai",
|
|
47
100
|
supportsTemperature: hasMinorVersion,
|
|
@@ -63,6 +116,24 @@ function getNeonModelCapabilities(modelId) {
|
|
|
63
116
|
supportsStopSequences: true,
|
|
64
117
|
supportsReasoningEffort: true
|
|
65
118
|
};
|
|
119
|
+
if (id.includes("gpt-oss")) return {
|
|
120
|
+
family: "other",
|
|
121
|
+
...PERMISSIVE,
|
|
122
|
+
supportsPenalties: false,
|
|
123
|
+
supportsSeed: false,
|
|
124
|
+
supportsStopSequences: false
|
|
125
|
+
};
|
|
126
|
+
if (id.includes("qwen") || id.includes("gemma")) return {
|
|
127
|
+
family: "other",
|
|
128
|
+
...PERMISSIVE,
|
|
129
|
+
supportsPenalties: false,
|
|
130
|
+
supportsSeed: false
|
|
131
|
+
};
|
|
132
|
+
if (id.includes("glm") || id.includes("inkling") || id.includes("kimi")) return {
|
|
133
|
+
family: "other",
|
|
134
|
+
...PERMISSIVE,
|
|
135
|
+
supportsPenalties: false
|
|
136
|
+
};
|
|
66
137
|
return {
|
|
67
138
|
family: "other",
|
|
68
139
|
...PERMISSIVE
|
|
@@ -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}\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 * 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\tif (id.includes(\"claude\")) {\n\t\treturn {\n\t\t\tfamily: \"anthropic\",\n\t\t\tsupportsTemperature: true,\n\t\t\tsupportsTopP: true,\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};\n\t}\n\n\t// Google (Gemini): accepts standard sampling params but rejects the OpenAI\n\t// `reasoning_effort` field (not part of Gemini's generation config).\n\tif (id.includes(\"gemini\")) {\n\t\treturn {\n\t\t\tfamily: \"google\",\n\t\t\t...PERMISSIVE,\n\t\t\tsupportsReasoningEffort: false,\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 = /gpt-5[.-]\\d/.test(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\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;AAiBA,MAAM,aAAoD;CACzD,qBAAqB;CACrB,cAAc;CACd,kCAAkC;CAClC,mBAAmB;CACnB,cAAc;CACd,uBAAuB;CACvB,yBAAyB;AAC1B;;;;;;;;;;AAWA,SAAgB,yBACf,SACwB;CACxB,MAAM,KAAK,QAAQ,YAAY;CAI/B,IAAI,GAAG,SAAS,QAAQ,GACvB,OAAO;EACN,QAAQ;EACR,qBAAqB;EACrB,cAAc;EACd,kCAAkC;EAClC,mBAAmB;EACnB,cAAc;EACd,uBAAuB;EACvB,yBAAyB;CAC1B;CAKD,IAAI,GAAG,SAAS,QAAQ,GACvB,OAAO;EACN,QAAQ;EACR,GAAG;EACH,yBAAyB;CAC1B;CAWD,IAAI,QAAQ,KAAK,EAAE,GAAG;EACrB,MAAM,kBAAkB,cAAc,KAAK,EAAE;EAC7C,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;CAGD,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// 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"}
|
package/dist/lib/provider.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { NeonAnthropicLanguageModel } from "./neon-anthropic-language-model.js";
|
|
2
1
|
import { getNeonModelRoute } from "./neon-model-capabilities.js";
|
|
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
5
|
import { wrapFetchWithGatewayErrorNormalization } from "./neon-gateway-error.js";
|
package/dist/package.js
CHANGED