@neondatabase/ai-sdk-provider 0.0.0 → 0.4.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/LICENSE.md +178 -0
- package/README.md +24 -9
- package/dist/index.d.ts +7 -0
- package/dist/index.js +8 -0
- package/dist/lib/neon-anthropic-language-model.d.ts +23 -0
- package/dist/lib/neon-anthropic-language-model.d.ts.map +1 -0
- package/dist/lib/neon-anthropic-language-model.js +38 -0
- package/dist/lib/neon-anthropic-language-model.js.map +1 -0
- package/dist/lib/neon-capabilities.d.ts +20 -0
- package/dist/lib/neon-capabilities.d.ts.map +1 -0
- package/dist/lib/neon-capabilities.js +97 -0
- package/dist/lib/neon-capabilities.js.map +1 -0
- package/dist/lib/neon-chat-language-model.d.ts +21 -0
- package/dist/lib/neon-chat-language-model.d.ts.map +1 -0
- package/dist/lib/neon-chat-language-model.js +34 -0
- package/dist/lib/neon-chat-language-model.js.map +1 -0
- package/dist/lib/neon-chat-options.d.ts +21 -0
- package/dist/lib/neon-chat-options.d.ts.map +1 -0
- package/dist/lib/neon-chat-options.js +52 -0
- package/dist/lib/neon-chat-options.js.map +1 -0
- package/dist/lib/neon-model-capabilities.d.ts +41 -0
- package/dist/lib/neon-model-capabilities.d.ts.map +1 -0
- package/dist/lib/neon-model-capabilities.js +74 -0
- package/dist/lib/neon-model-capabilities.js.map +1 -0
- package/dist/lib/neon-responses-language-model.d.ts +27 -0
- package/dist/lib/neon-responses-language-model.d.ts.map +1 -0
- package/dist/lib/neon-responses-language-model.js +45 -0
- package/dist/lib/neon-responses-language-model.js.map +1 -0
- package/dist/lib/provider.d.ts +52 -0
- package/dist/lib/provider.d.ts.map +1 -0
- package/dist/lib/provider.js +126 -0
- package/dist/lib/provider.js.map +1 -0
- package/dist/lib/version.d.ts +5 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +7 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/v1.d.ts +7 -0
- package/dist/v1.js +7 -0
- package/package.json +78 -23
- package/src/index.ts +0 -5
- package/src/lib/neon-anthropic-language-model.ts +0 -51
- package/src/lib/neon-capabilities.ts +0 -133
- package/src/lib/neon-chat-language-model.ts +0 -56
- package/src/lib/neon-chat-options.ts +0 -48
- package/src/lib/neon-model-capabilities.test.ts +0 -66
- package/src/lib/neon-model-capabilities.ts +0 -137
- package/src/lib/neon-responses-language-model.ts +0 -60
- package/src/lib/provider.ts +0 -211
- package/src/lib/version.ts +0 -2
- package/src/v1.ts +0 -26
- package/tsconfig.json +0 -4
- package/tsdown.config.ts +0 -12
- package/vitest.config.ts +0 -17
|
@@ -0,0 +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// `NEON_EXTRA_MODEL_IDS` are additional ids the gateway serves that models.dev\n// does not (yet) list; they are kept for autocomplete and verified to resolve\n// against the gateway. The authoritative, always-current catalog is shown in\n// the Neon Console under the branch's \"AI Gateway\" tab.\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-opus-4-7\",\n\t\"claude-opus-4-6\",\n\t\"claude-opus-4-5\",\n\t\"claude-opus-4-1\",\n\t\"claude-sonnet-4-6\",\n\t\"claude-sonnet-4-5\",\n\t\"claude-sonnet-4\",\n\t\"claude-haiku-4-5\",\n\t// OpenAI (native Responses API)\n\t\"gpt-5\",\n\t\"gpt-5-mini\",\n\t\"gpt-5-nano\",\n\t\"gpt-5-1\",\n\t\"gpt-5-2\",\n\t\"gpt-5-4\",\n\t\"gpt-5-4-mini\",\n\t\"gpt-5-4-nano\",\n\t\"gpt-5-5\",\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-pro\",\n\t\"gemini-3-flash\",\n\t\"gemini-3-1-pro\",\n\t\"gemini-3-1-flash-lite\",\n\t\"gemini-2-5-pro\",\n\t\"gemini-2-5-flash\",\n] as const;\n\n/**\n * Ids the gateway serves that the models.dev `neon` provider does not list.\n * Verified to resolve against the gateway; retained for autocomplete. If\n * models.dev later lists one of these, the drift check flags it for promotion\n * into `NEON_MODELS_DEV_IDS`.\n */\nexport const NEON_EXTRA_MODEL_IDS = [\n\t// Anthropic (native Messages API)\n\t\"claude-opus-4-8\",\n\t// OpenAI (native Responses API) — Codex is served only natively\n\t\"gpt-5-2-codex\",\n\t\"gpt-5-3-codex\",\n\t\"gpt-5-5-pro\",\n\t// Google (unified MLflow endpoint)\n\t\"gemini-3-5-flash\",\n\t\"gemma-3-12b\",\n\t// Meta (unified MLflow endpoint)\n\t\"llama-4-maverick\",\n\t\"meta-llama-3-3-70b-instruct\",\n\t\"meta-llama-3-1-8b-instruct\",\n\t// Alibaba (unified MLflow endpoint)\n\t\"qwen3-next-80b-a3b-instruct\",\n\t\"qwen35-122b-a10b\",\n] as const;\n\n/** A known Neon AI Gateway model id (models.dev catalog + gateway extras). */\nexport type NeonKnownModelId =\n\t| (typeof NEON_MODELS_DEV_IDS)[number]\n\t| (typeof NEON_EXTRA_MODEL_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":";;AAiBA,MAAa,sBAAsB;CAElC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;;;AAQA,MAAa,uBAAuB;CAEnC;CAEA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CAEA;CACA;AACD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/lib/neon-model-capabilities.d.ts
|
|
2
|
+
type NeonModelFamily = "anthropic" | "google" | "openai" | "meta" | "other";
|
|
3
|
+
/**
|
|
4
|
+
* Which gateway endpoint a model should be routed to.
|
|
5
|
+
*
|
|
6
|
+
* - `anthropic`: native Messages API — streaming structured output + native reasoning.
|
|
7
|
+
* - `openai`: native Responses API — models served only natively (e.g. Codex),
|
|
8
|
+
* native reasoning, and the image-generation tool.
|
|
9
|
+
* - `mlflow`: the unified, OpenAI-compatible endpoint (the fallback). Gemini is
|
|
10
|
+
* routed here because the gateway's native Gemini endpoint does not support
|
|
11
|
+
* streaming (`streamGenerateContent` is rejected).
|
|
12
|
+
*/
|
|
13
|
+
type NeonModelRoute = "anthropic" | "openai" | "mlflow";
|
|
14
|
+
declare function getNeonModelRoute(modelId: string): NeonModelRoute;
|
|
15
|
+
interface NeonModelCapabilities {
|
|
16
|
+
family: NeonModelFamily;
|
|
17
|
+
supportsTemperature: boolean;
|
|
18
|
+
supportsTopP: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether `temperature` and `topP` may be sent together. Anthropic models
|
|
21
|
+
* accept only one of the two.
|
|
22
|
+
*/
|
|
23
|
+
temperatureTopPMutuallyExclusive: boolean;
|
|
24
|
+
supportsPenalties: boolean;
|
|
25
|
+
supportsSeed: boolean;
|
|
26
|
+
supportsStopSequences: boolean;
|
|
27
|
+
supportsReasoningEffort: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Heuristic, prefix-based capability detection for MLflow-routed Neon models.
|
|
31
|
+
*
|
|
32
|
+
* The unified endpoint proxies to heterogeneous upstream providers, each of
|
|
33
|
+
* which accepts a different subset of the OpenAI-style parameters the AI SDK
|
|
34
|
+
* emits. Sending a parameter an upstream rejects results in a hard `400`, so we
|
|
35
|
+
* strip the parameters a family is known to reject and surface a warning
|
|
36
|
+
* instead. Unknown/untested models stay permissive (passed through unchanged).
|
|
37
|
+
*/
|
|
38
|
+
declare function getNeonModelCapabilities(modelId: string): NeonModelCapabilities;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { NeonModelCapabilities, NeonModelFamily, NeonModelRoute, getNeonModelCapabilities, getNeonModelRoute };
|
|
41
|
+
//# sourceMappingURL=neon-model-capabilities.d.ts.map
|
|
@@ -0,0 +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;AAkCA;;;;;;KArDY,cAAA;iBAEI,iBAAA,mBAAoC;UAiBnC,qBAAA;UACR;;;;;;;;;;;;;;;;;;;;;;iBAiCO,wBAAA,mBAEb"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/lib/neon-model-capabilities.ts
|
|
2
|
+
function getNeonModelRoute(modelId) {
|
|
3
|
+
const id = modelId.toLowerCase();
|
|
4
|
+
if (id.includes("claude")) return "anthropic";
|
|
5
|
+
if (id.includes("gpt-") && !id.includes("gpt-oss") || id.includes("codex")) return "openai";
|
|
6
|
+
return "mlflow";
|
|
7
|
+
}
|
|
8
|
+
const PERMISSIVE = {
|
|
9
|
+
supportsTemperature: true,
|
|
10
|
+
supportsTopP: true,
|
|
11
|
+
temperatureTopPMutuallyExclusive: false,
|
|
12
|
+
supportsPenalties: true,
|
|
13
|
+
supportsSeed: true,
|
|
14
|
+
supportsStopSequences: true,
|
|
15
|
+
supportsReasoningEffort: true
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Heuristic, prefix-based capability detection for MLflow-routed Neon models.
|
|
19
|
+
*
|
|
20
|
+
* The unified endpoint proxies to heterogeneous upstream providers, each of
|
|
21
|
+
* which accepts a different subset of the OpenAI-style parameters the AI SDK
|
|
22
|
+
* emits. Sending a parameter an upstream rejects results in a hard `400`, so we
|
|
23
|
+
* strip the parameters a family is known to reject and surface a warning
|
|
24
|
+
* instead. Unknown/untested models stay permissive (passed through unchanged).
|
|
25
|
+
*/
|
|
26
|
+
function getNeonModelCapabilities(modelId) {
|
|
27
|
+
const id = modelId.toLowerCase();
|
|
28
|
+
if (id.includes("claude")) return {
|
|
29
|
+
family: "anthropic",
|
|
30
|
+
supportsTemperature: true,
|
|
31
|
+
supportsTopP: true,
|
|
32
|
+
temperatureTopPMutuallyExclusive: true,
|
|
33
|
+
supportsPenalties: false,
|
|
34
|
+
supportsSeed: false,
|
|
35
|
+
supportsStopSequences: true,
|
|
36
|
+
supportsReasoningEffort: false
|
|
37
|
+
};
|
|
38
|
+
if (id.includes("gemini")) return {
|
|
39
|
+
family: "google",
|
|
40
|
+
...PERMISSIVE,
|
|
41
|
+
supportsReasoningEffort: false
|
|
42
|
+
};
|
|
43
|
+
if (/gpt-5/.test(id)) {
|
|
44
|
+
const hasMinorVersion = /gpt-5[.-]\d/.test(id);
|
|
45
|
+
return {
|
|
46
|
+
family: "openai",
|
|
47
|
+
supportsTemperature: hasMinorVersion,
|
|
48
|
+
supportsTopP: hasMinorVersion,
|
|
49
|
+
temperatureTopPMutuallyExclusive: false,
|
|
50
|
+
supportsPenalties: true,
|
|
51
|
+
supportsSeed: true,
|
|
52
|
+
supportsStopSequences: true,
|
|
53
|
+
supportsReasoningEffort: true
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (id.includes("llama")) return {
|
|
57
|
+
family: "meta",
|
|
58
|
+
supportsTemperature: true,
|
|
59
|
+
supportsTopP: true,
|
|
60
|
+
temperatureTopPMutuallyExclusive: false,
|
|
61
|
+
supportsPenalties: false,
|
|
62
|
+
supportsSeed: false,
|
|
63
|
+
supportsStopSequences: true,
|
|
64
|
+
supportsReasoningEffort: true
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
family: "other",
|
|
68
|
+
...PERMISSIVE
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { getNeonModelCapabilities, getNeonModelRoute };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=neon-model-capabilities.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { OpenAIResponsesLanguageModel } from "@ai-sdk/openai/internal";
|
|
2
|
+
import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3GenerateResult, LanguageModelV3StreamResult } from "@ai-sdk/provider";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/neon-responses-language-model.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Language model for OpenAI models served via the Neon AI Gateway's native
|
|
8
|
+
* Responses API (`/ai-gateway/openai/v1/responses`). This route is required for
|
|
9
|
+
* models that are only served natively (e.g. Codex) and unlocks native
|
|
10
|
+
* reasoning and the image-generation tool.
|
|
11
|
+
*
|
|
12
|
+
* The shared OpenAI Responses model shapes a request based on whether the model
|
|
13
|
+
* is a reasoning model, but its detection is brittle here: the gateway also
|
|
14
|
+
* accepts the legacy `databricks-` prefix, which defeats the upstream bare-id
|
|
15
|
+
* (`gpt-5`) match. For the GPT-5 reasoning family we set the model's own
|
|
16
|
+
* `forceReasoning` provider option so it applies the correct reasoning behavior
|
|
17
|
+
* for both id forms. Users can still override it.
|
|
18
|
+
*/
|
|
19
|
+
declare class NeonResponsesLanguageModel extends OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
20
|
+
private get isReasoningFamily();
|
|
21
|
+
private withForcedReasoning;
|
|
22
|
+
doGenerate(options: LanguageModelV3CallOptions): Promise<LanguageModelV3GenerateResult>;
|
|
23
|
+
doStream(options: LanguageModelV3CallOptions): Promise<LanguageModelV3StreamResult>;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { NeonResponsesLanguageModel };
|
|
27
|
+
//# sourceMappingURL=neon-responses-language-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neon-responses-language-model.d.ts","names":[],"sources":["../../src/lib/neon-responses-language-model.ts"],"mappings":";;;;;;;AAqBA;;;;;;;;;;AAE2B;cAFd,0BAAA,SACJ,4BAAA,YACG;;;sBA2BD,6BACP,QAAQ;oBAKD,6BACP,QAAQ"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { OpenAIResponsesLanguageModel } from "@ai-sdk/openai/internal";
|
|
2
|
+
//#region src/lib/neon-responses-language-model.ts
|
|
3
|
+
/**
|
|
4
|
+
* Language model for OpenAI models served via the Neon AI Gateway's native
|
|
5
|
+
* Responses API (`/ai-gateway/openai/v1/responses`). This route is required for
|
|
6
|
+
* models that are only served natively (e.g. Codex) and unlocks native
|
|
7
|
+
* reasoning and the image-generation tool.
|
|
8
|
+
*
|
|
9
|
+
* The shared OpenAI Responses model shapes a request based on whether the model
|
|
10
|
+
* is a reasoning model, but its detection is brittle here: the gateway also
|
|
11
|
+
* accepts the legacy `databricks-` prefix, which defeats the upstream bare-id
|
|
12
|
+
* (`gpt-5`) match. For the GPT-5 reasoning family we set the model's own
|
|
13
|
+
* `forceReasoning` provider option so it applies the correct reasoning behavior
|
|
14
|
+
* for both id forms. Users can still override it.
|
|
15
|
+
*/
|
|
16
|
+
var NeonResponsesLanguageModel = class extends OpenAIResponsesLanguageModel {
|
|
17
|
+
get isReasoningFamily() {
|
|
18
|
+
return /gpt-5/.test(this.modelId.toLowerCase());
|
|
19
|
+
}
|
|
20
|
+
withForcedReasoning(options) {
|
|
21
|
+
if (!this.isReasoningFamily) return options;
|
|
22
|
+
const openai = options.providerOptions?.openai;
|
|
23
|
+
if (openai != null && "forceReasoning" in openai) return options;
|
|
24
|
+
return {
|
|
25
|
+
...options,
|
|
26
|
+
providerOptions: {
|
|
27
|
+
...options.providerOptions,
|
|
28
|
+
openai: {
|
|
29
|
+
...openai,
|
|
30
|
+
forceReasoning: true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
doGenerate(options) {
|
|
36
|
+
return super.doGenerate(this.withForcedReasoning(options));
|
|
37
|
+
}
|
|
38
|
+
doStream(options) {
|
|
39
|
+
return super.doStream(this.withForcedReasoning(options));
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { NeonResponsesLanguageModel };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=neon-responses-language-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neon-responses-language-model.js","names":[],"sources":["../../src/lib/neon-responses-language-model.ts"],"sourcesContent":["import { OpenAIResponsesLanguageModel } from \"@ai-sdk/openai/internal\";\nimport type {\n\tLanguageModelV3,\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3GenerateResult,\n\tLanguageModelV3StreamResult,\n} from \"@ai-sdk/provider\";\n\n/**\n * Language model for OpenAI models served via the Neon AI Gateway's native\n * Responses API (`/ai-gateway/openai/v1/responses`). This route is required for\n * models that are only served natively (e.g. Codex) and unlocks native\n * reasoning and the image-generation tool.\n *\n * The shared OpenAI Responses model shapes a request based on whether the model\n * is a reasoning model, but its detection is brittle here: the gateway also\n * accepts the legacy `databricks-` prefix, which defeats the upstream bare-id\n * (`gpt-5`) match. For the GPT-5 reasoning family we set the model's own\n * `forceReasoning` provider option so it applies the correct reasoning behavior\n * for both id forms. Users can still override it.\n */\nexport class NeonResponsesLanguageModel\n\textends OpenAIResponsesLanguageModel\n\timplements LanguageModelV3\n{\n\tprivate get isReasoningFamily(): boolean {\n\t\treturn /gpt-5/.test(this.modelId.toLowerCase());\n\t}\n\n\tprivate withForcedReasoning(\n\t\toptions: LanguageModelV3CallOptions,\n\t): LanguageModelV3CallOptions {\n\t\tif (!this.isReasoningFamily) {\n\t\t\treturn options;\n\t\t}\n\t\tconst openai = options.providerOptions?.openai;\n\t\t// Respect an explicit user setting.\n\t\tif (openai != null && \"forceReasoning\" in openai) {\n\t\t\treturn options;\n\t\t}\n\t\treturn {\n\t\t\t...options,\n\t\t\tproviderOptions: {\n\t\t\t\t...options.providerOptions,\n\t\t\t\topenai: { ...openai, forceReasoning: true },\n\t\t\t},\n\t\t};\n\t}\n\n\toverride doGenerate(\n\t\toptions: LanguageModelV3CallOptions,\n\t): Promise<LanguageModelV3GenerateResult> {\n\t\treturn super.doGenerate(this.withForcedReasoning(options));\n\t}\n\n\toverride doStream(\n\t\toptions: LanguageModelV3CallOptions,\n\t): Promise<LanguageModelV3StreamResult> {\n\t\treturn super.doStream(this.withForcedReasoning(options));\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,IAAa,6BAAb,cACS,6BAET;CACC,IAAY,oBAA6B;EACxC,OAAO,QAAQ,KAAK,KAAK,QAAQ,YAAY,CAAC;CAC/C;CAEA,oBACC,SAC6B;EAC7B,IAAI,CAAC,KAAK,mBACT,OAAO;EAER,MAAM,SAAS,QAAQ,iBAAiB;EAExC,IAAI,UAAU,QAAQ,oBAAoB,QACzC,OAAO;EAER,OAAO;GACN,GAAG;GACH,iBAAiB;IAChB,GAAG,QAAQ;IACX,QAAQ;KAAE,GAAG;KAAQ,gBAAgB;IAAK;GAC3C;EACD;CACD;CAEA,WACC,SACyC;EACzC,OAAO,MAAM,WAAW,KAAK,oBAAoB,OAAO,CAAC;CAC1D;CAEA,SACC,SACuC;EACvC,OAAO,MAAM,SAAS,KAAK,oBAAoB,OAAO,CAAC;CACxD;AACD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { NeonChatModelId } from "./neon-chat-options.js";
|
|
2
|
+
import { openai } from "@ai-sdk/openai";
|
|
3
|
+
import { LanguageModelV3, ProviderV3 } from "@ai-sdk/provider";
|
|
4
|
+
import { FetchFunction } from "@ai-sdk/provider-utils";
|
|
5
|
+
import { z } from "zod/v4";
|
|
6
|
+
|
|
7
|
+
//#region src/lib/provider.d.ts
|
|
8
|
+
|
|
9
|
+
declare const neonErrorSchema: z.ZodObject<{
|
|
10
|
+
error: z.ZodObject<{
|
|
11
|
+
message: z.ZodString;
|
|
12
|
+
type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
13
|
+
param: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
|
|
14
|
+
code: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
type NeonErrorData = z.infer<typeof neonErrorSchema>;
|
|
18
|
+
interface NeonProviderSettings {
|
|
19
|
+
/**
|
|
20
|
+
* Neon AI Gateway base URL — the branch-scoped host root, e.g.
|
|
21
|
+
* `https://<branch-id>-api.ai.<region>.aws.neon.tech`. The gateway paths are
|
|
22
|
+
* appended internally. Falls back to the `NEON_AI_GATEWAY_BASE_URL` env var.
|
|
23
|
+
*/
|
|
24
|
+
baseURL?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Neon AI Gateway platform token (the `nt_live_...` value). Falls back to the
|
|
27
|
+
* `NEON_AI_GATEWAY_TOKEN` env var.
|
|
28
|
+
*/
|
|
29
|
+
apiKey?: string;
|
|
30
|
+
/** Custom headers to include in the requests. */
|
|
31
|
+
headers?: Record<string, string>;
|
|
32
|
+
/** Custom fetch implementation (e.g. for testing or middleware). */
|
|
33
|
+
fetch?: FetchFunction;
|
|
34
|
+
}
|
|
35
|
+
interface NeonProvider extends ProviderV3 {
|
|
36
|
+
/** Creates a Neon AI Gateway model for text generation. */
|
|
37
|
+
(modelId: NeonChatModelId): LanguageModelV3;
|
|
38
|
+
/** Creates a Neon AI Gateway model for text generation. */
|
|
39
|
+
languageModel(modelId: NeonChatModelId): LanguageModelV3;
|
|
40
|
+
/** Creates a Neon AI Gateway chat model for text generation. */
|
|
41
|
+
chat(modelId: NeonChatModelId): LanguageModelV3;
|
|
42
|
+
/** OpenAI Responses tools (e.g. `imageGeneration`) for OpenAI-routed models. */
|
|
43
|
+
tools: typeof openai.tools;
|
|
44
|
+
/** @deprecated Use `embeddingModel` instead. */
|
|
45
|
+
textEmbeddingModel(modelId: string): never;
|
|
46
|
+
}
|
|
47
|
+
declare function createNeon(options?: NeonProviderSettings): NeonProvider;
|
|
48
|
+
/** Default Neon provider instance (reads `NEON_AI_GATEWAY_*` env vars). */
|
|
49
|
+
declare const neon: NeonProvider;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { NeonErrorData, NeonProvider, NeonProviderSettings, createNeon, neon };
|
|
52
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","names":[],"sources":["../../src/lib/provider.ts"],"mappings":";;;;;;;;cAmCM,iBAAe,CAAA,CAAA;;;;;IAAA,IAAA,eAAA,cAAA,aAAA,CAAA,CAAA;EAAA,CAAA,eAAA,CAAA;AASrB,CAAA,eAAY,CAAA;AAAa,KAAb,aAAA,GAAgB,CAAA,CAAE,KAAL,CAAA,OAAkB,eAAlB,CAAA;AAAkB,UA4C1B,oBAAA,CA5C0B;;AAAR;AA4CnC;;;SAkBS,CAAA,EAAA,MAAA;EAAa;AAGtB;;;QAE6B,CAAA,EAAA,MAAA;;SAGa,CAAA,EAX/B,MAW+B,CAAA,MAAA,EAAA,MAAA,CAAA;;OAGT,CAAA,EAXxB,aAWwB;;AARK,UAArB,YAAA,SAAqB,UAAA,CAAA;EAAU;EAiBhC,CAAA,OAAA,EAfL,eAee,CAAA,EAfG,eAeH;EAAA;eAAU,CAAA,OAAA,EAZZ,eAYY,CAAA,EAZM,eAYN;;EAAwC,IAAA,CAAA,OAAA,EAT7D,eAS6D,CAAA,EAT3C,eAS2C;EA0F/D;gBAhGE,MAAA,CAAe;;;;iBAMd,UAAA,WAAoB,uBAA4B;;cA0FnD,MAAI"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { NeonAnthropicLanguageModel } from "./neon-anthropic-language-model.js";
|
|
2
|
+
import { getNeonModelRoute } from "./neon-model-capabilities.js";
|
|
3
|
+
import { NeonChatLanguageModel } from "./neon-chat-language-model.js";
|
|
4
|
+
import { NeonResponsesLanguageModel } from "./neon-responses-language-model.js";
|
|
5
|
+
import { VERSION } from "./version.js";
|
|
6
|
+
import { openai } from "@ai-sdk/openai";
|
|
7
|
+
import { NoSuchModelError } from "@ai-sdk/provider";
|
|
8
|
+
import { generateId, loadApiKey, loadSetting, withUserAgentSuffix, withoutTrailingSlash } from "@ai-sdk/provider-utils";
|
|
9
|
+
import { z } from "zod/v4";
|
|
10
|
+
//#region src/lib/provider.ts
|
|
11
|
+
/**
|
|
12
|
+
* Community Vercel AI SDK provider for the Neon AI Gateway.
|
|
13
|
+
*
|
|
14
|
+
* `createNeon()` returns a provider that routes each model to the best gateway
|
|
15
|
+
* endpoint based on its id (Anthropic → native Messages, OpenAI → native
|
|
16
|
+
* Responses incl. Codex, everything else → unified MLflow), so a single
|
|
17
|
+
* `neon('claude-...')` call (same base URL + token) reaches the whole catalog.
|
|
18
|
+
* Ids use the canonical Neon (unprefixed) form; the legacy `databricks-` prefix
|
|
19
|
+
* is also accepted. Configure with the branch-scoped `NEON_AI_GATEWAY_BASE_URL` +
|
|
20
|
+
* `NEON_AI_GATEWAY_TOKEN` emitted by `neonctl env pull` / `neon dev`, or pass
|
|
21
|
+
* `baseURL` / `apiKey` explicitly.
|
|
22
|
+
*/
|
|
23
|
+
const neonErrorStructure = {
|
|
24
|
+
errorSchema: z.object({ error: z.object({
|
|
25
|
+
message: z.string(),
|
|
26
|
+
type: z.string().nullish(),
|
|
27
|
+
param: z.unknown().nullish(),
|
|
28
|
+
code: z.unknown().nullish()
|
|
29
|
+
}) }),
|
|
30
|
+
errorToMessage: (data) => data.error.message
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Recursively remove the JSON Schema `$schema` marker, which some gateway
|
|
34
|
+
* backends (notably Gemini) reject in tool/structured-output schemas. Other
|
|
35
|
+
* backends ignore its absence, so stripping it everywhere is safe.
|
|
36
|
+
*/
|
|
37
|
+
function stripJsonSchemaMarker(value) {
|
|
38
|
+
if (Array.isArray(value)) return value.map(stripJsonSchemaMarker);
|
|
39
|
+
if (value !== null && typeof value === "object") {
|
|
40
|
+
const result = {};
|
|
41
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
42
|
+
if (key === "$schema") continue;
|
|
43
|
+
result[key] = stripJsonSchemaMarker(entry);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function transformNeonRequestBody(args) {
|
|
50
|
+
const transformed = { ...args };
|
|
51
|
+
if (transformed.tools != null) transformed.tools = stripJsonSchemaMarker(transformed.tools);
|
|
52
|
+
if (transformed.response_format != null) transformed.response_format = stripJsonSchemaMarker(transformed.response_format);
|
|
53
|
+
return transformed;
|
|
54
|
+
}
|
|
55
|
+
function createNeon(options = {}) {
|
|
56
|
+
const getHost = () => withoutTrailingSlash(loadSetting({
|
|
57
|
+
settingValue: options.baseURL,
|
|
58
|
+
environmentVariableName: "NEON_AI_GATEWAY_BASE_URL",
|
|
59
|
+
settingName: "baseURL",
|
|
60
|
+
description: "Neon AI Gateway base URL"
|
|
61
|
+
}));
|
|
62
|
+
const getHeaders = (extra) => withUserAgentSuffix({
|
|
63
|
+
Authorization: `Bearer ${loadApiKey({
|
|
64
|
+
apiKey: options.apiKey,
|
|
65
|
+
environmentVariableName: "NEON_AI_GATEWAY_TOKEN",
|
|
66
|
+
description: "Neon AI Gateway token"
|
|
67
|
+
})}`,
|
|
68
|
+
...extra,
|
|
69
|
+
...options.headers
|
|
70
|
+
}, `neondatabase/ai-sdk-provider/${VERSION}`);
|
|
71
|
+
const createAnthropicModel = (modelId) => new NeonAnthropicLanguageModel(modelId, {
|
|
72
|
+
provider: "neon.anthropic",
|
|
73
|
+
baseURL: `${getHost()}/ai-gateway/anthropic/v1`,
|
|
74
|
+
headers: () => getHeaders({ "anthropic-version": "2023-06-01" }),
|
|
75
|
+
fetch: options.fetch,
|
|
76
|
+
generateId
|
|
77
|
+
});
|
|
78
|
+
const createOpenAIModel = (modelId) => new NeonResponsesLanguageModel(modelId, {
|
|
79
|
+
provider: "neon.openai.responses",
|
|
80
|
+
url: ({ path }) => `${getHost()}/ai-gateway/openai/v1${path}`,
|
|
81
|
+
headers: getHeaders,
|
|
82
|
+
fetch: options.fetch,
|
|
83
|
+
fileIdPrefixes: ["file-"]
|
|
84
|
+
});
|
|
85
|
+
const createChatModel = (modelId) => new NeonChatLanguageModel(modelId, {
|
|
86
|
+
provider: "neon.chat",
|
|
87
|
+
url: ({ path }) => `${getHost()}/ai-gateway/mlflow/v1${path}`,
|
|
88
|
+
headers: getHeaders,
|
|
89
|
+
fetch: options.fetch,
|
|
90
|
+
errorStructure: neonErrorStructure,
|
|
91
|
+
transformRequestBody: transformNeonRequestBody,
|
|
92
|
+
supportsStructuredOutputs: true
|
|
93
|
+
});
|
|
94
|
+
const createLanguageModel = (modelId) => {
|
|
95
|
+
switch (getNeonModelRoute(modelId)) {
|
|
96
|
+
case "anthropic": return createAnthropicModel(modelId);
|
|
97
|
+
case "openai": return createOpenAIModel(modelId);
|
|
98
|
+
default: return createChatModel(modelId);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const provider = (modelId) => createLanguageModel(modelId);
|
|
102
|
+
provider.specificationVersion = "v3";
|
|
103
|
+
provider.languageModel = createLanguageModel;
|
|
104
|
+
provider.chat = createLanguageModel;
|
|
105
|
+
provider.tools = openai.tools;
|
|
106
|
+
provider.embeddingModel = (modelId) => {
|
|
107
|
+
throw new NoSuchModelError({
|
|
108
|
+
modelId,
|
|
109
|
+
modelType: "embeddingModel"
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
provider.textEmbeddingModel = provider.embeddingModel;
|
|
113
|
+
provider.imageModel = (modelId) => {
|
|
114
|
+
throw new NoSuchModelError({
|
|
115
|
+
modelId,
|
|
116
|
+
modelType: "imageModel"
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
return provider;
|
|
120
|
+
}
|
|
121
|
+
/** Default Neon provider instance (reads `NEON_AI_GATEWAY_*` env vars). */
|
|
122
|
+
const neon = createNeon();
|
|
123
|
+
//#endregion
|
|
124
|
+
export { createNeon, neon };
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","names":["openaiProvider"],"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 MLflow), 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 { openai as openaiProvider } from \"@ai-sdk/openai\";\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 { getNeonModelRoute } from \"./neon-model-capabilities.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 openaiProvider.tools;\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()}/ai-gateway/anthropic/v1`,\n\t\t\theaders: () => getHeaders({ \"anthropic-version\": \"2023-06-01\" }),\n\t\t\tfetch: options.fetch,\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()}/ai-gateway/openai/v1${path}`,\n\t\t\theaders: getHeaders,\n\t\t\tfetch: options.fetch,\n\t\t\tfileIdPrefixes: [\"file-\"],\n\t\t});\n\n\t// Everything else (Gemini, Llama, Qwen, gpt-oss, ...) -> unified MLflow\n\t// 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()}/ai-gateway/mlflow/v1${path}`,\n\t\t\theaders: getHeaders,\n\t\t\tfetch: options.fetch,\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 = openaiProvider.tools;\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":";;;;;;;;;;;;;;;;;;;;;;AA8CA,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,QAAQ;EACf;CACD,CAAC;CAGF,MAAM,qBAAqB,YAC1B,IAAI,2BAA2B,SAAS;EACvC,UAAU;EACV,MAAM,EAAE,WAAW,GAAG,QAAQ,EAAE,uBAAuB;EACvD,SAAS;EACT,OAAO,QAAQ;EACf,gBAAgB,CAAC,OAAO;CACzB,CAAC;CAIF,MAAM,mBAAmB,YACxB,IAAI,sBAAsB,SAAS;EAClC,UAAU;EACV,MAAM,EAAE,WAAW,GAAG,QAAQ,EAAE,uBAAuB;EACvD,SAAS;EACT,OAAO,QAAQ;EACf,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,QAAQA,OAAe;CAEhC,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","names":[],"sources":["../../src/lib/version.ts"],"mappings":";cAIa"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","names":["pkg.version"],"sources":["../../src/lib/version.ts"],"sourcesContent":["import pkg from \"../../package.json\" with { type: \"json\" };\n\n// Package version, used in the User-Agent suffix sent to the gateway. Derived\n// from package.json so it stays in sync with the changeset version bump flow.\nexport const VERSION: string = pkg.version;\n"],"mappings":";;AAIA,MAAa,UAAkBA"}
|
package/dist/package.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|
package/dist/v1.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NeonAnthropicLanguageModel } from "./lib/neon-anthropic-language-model.js";
|
|
2
|
+
import { NeonChatLanguageModel } from "./lib/neon-chat-language-model.js";
|
|
3
|
+
import { NEON_EXTRA_MODEL_IDS, NEON_MODELS_DEV_IDS, NeonChatModelId, NeonKnownModelId } from "./lib/neon-chat-options.js";
|
|
4
|
+
import { NeonModelCapabilities, NeonModelFamily, NeonModelRoute, getNeonModelCapabilities, getNeonModelRoute } from "./lib/neon-model-capabilities.js";
|
|
5
|
+
import { NeonResponsesLanguageModel } from "./lib/neon-responses-language-model.js";
|
|
6
|
+
import { NeonErrorData, NeonProvider, NeonProviderSettings, createNeon, neon } from "./lib/provider.js";
|
|
7
|
+
export { NEON_EXTRA_MODEL_IDS, 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/v1.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NeonAnthropicLanguageModel } from "./lib/neon-anthropic-language-model.js";
|
|
2
|
+
import { getNeonModelCapabilities, getNeonModelRoute } from "./lib/neon-model-capabilities.js";
|
|
3
|
+
import { NeonChatLanguageModel } from "./lib/neon-chat-language-model.js";
|
|
4
|
+
import { NEON_EXTRA_MODEL_IDS, NEON_MODELS_DEV_IDS } from "./lib/neon-chat-options.js";
|
|
5
|
+
import { NeonResponsesLanguageModel } from "./lib/neon-responses-language-model.js";
|
|
6
|
+
import { createNeon, neon } from "./lib/provider.js";
|
|
7
|
+
export { NEON_EXTRA_MODEL_IDS, NEON_MODELS_DEV_IDS, NeonAnthropicLanguageModel, NeonChatLanguageModel, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
|
package/package.json
CHANGED
|
@@ -1,24 +1,79 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
2
|
+
"name": "@neondatabase/ai-sdk-provider",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Community Vercel AI SDK provider for the Neon AI Gateway.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"neon",
|
|
7
|
+
"ai",
|
|
8
|
+
"ai-sdk",
|
|
9
|
+
"vercel-ai-sdk",
|
|
10
|
+
"provider",
|
|
11
|
+
"ai-gateway",
|
|
12
|
+
"llm",
|
|
13
|
+
"platform"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/neondatabase/neon-pkgs.git"
|
|
18
|
+
},
|
|
19
|
+
"license": "Apache-2.0",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Neon",
|
|
22
|
+
"url": "https://neon.com"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"types": "dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./v1": {
|
|
34
|
+
"types": "./dist/v1.d.ts",
|
|
35
|
+
"import": "./dist/v1.js",
|
|
36
|
+
"default": "./dist/v1.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"README.md",
|
|
41
|
+
"dist/",
|
|
42
|
+
"package.json"
|
|
43
|
+
],
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^20.19.0",
|
|
46
|
+
"@vitest/coverage-v8": "3.0.9",
|
|
47
|
+
"ai": "6.0.132",
|
|
48
|
+
"console-fail-test": "0.5.0",
|
|
49
|
+
"tsdown": "^0.14.1",
|
|
50
|
+
"typescript": "^5.8.2",
|
|
51
|
+
"vitest": "^3.0.9",
|
|
52
|
+
"zod": "^4.4.3"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=22"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"provenance": false
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@ai-sdk/anthropic": "3.0.82",
|
|
62
|
+
"@ai-sdk/openai": "3.0.69",
|
|
63
|
+
"@ai-sdk/openai-compatible": "2.0.48",
|
|
64
|
+
"@ai-sdk/provider": "3.0.10",
|
|
65
|
+
"@ai-sdk/provider-utils": "4.0.27"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"ai": "^6.0.0",
|
|
69
|
+
"zod": "^3.25.76 || ^4.1.8"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "tsc --noEmit && tsdown",
|
|
73
|
+
"test": "vitest --passWithNoTests",
|
|
74
|
+
"test:ci": "vitest run --passWithNoTests",
|
|
75
|
+
"test:drift": "NEON_DRIFT_CHECK=1 vitest run neon-catalog-drift",
|
|
76
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
77
|
+
"tsc": "tsc"
|
|
78
|
+
}
|
|
79
|
+
}
|