@neondatabase/ai-sdk-provider 0.0.0 → 0.1.1

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.
Files changed (53) hide show
  1. package/LICENSE.md +178 -0
  2. package/dist/index.d.ts +7 -0
  3. package/dist/index.js +7 -0
  4. package/dist/lib/neon-anthropic-language-model.d.ts +23 -0
  5. package/dist/lib/neon-anthropic-language-model.d.ts.map +1 -0
  6. package/dist/lib/neon-anthropic-language-model.js +38 -0
  7. package/dist/lib/neon-anthropic-language-model.js.map +1 -0
  8. package/dist/lib/neon-capabilities.d.ts +20 -0
  9. package/dist/lib/neon-capabilities.d.ts.map +1 -0
  10. package/dist/lib/neon-capabilities.js +101 -0
  11. package/dist/lib/neon-capabilities.js.map +1 -0
  12. package/dist/lib/neon-chat-language-model.d.ts +21 -0
  13. package/dist/lib/neon-chat-language-model.d.ts.map +1 -0
  14. package/dist/lib/neon-chat-language-model.js +34 -0
  15. package/dist/lib/neon-chat-language-model.js.map +1 -0
  16. package/dist/lib/neon-chat-options.d.ts +5 -0
  17. package/dist/lib/neon-chat-options.d.ts.map +1 -0
  18. package/dist/lib/neon-chat-options.js +1 -0
  19. package/dist/lib/neon-model-capabilities.d.ts +41 -0
  20. package/dist/lib/neon-model-capabilities.d.ts.map +1 -0
  21. package/dist/lib/neon-model-capabilities.js +74 -0
  22. package/dist/lib/neon-model-capabilities.js.map +1 -0
  23. package/dist/lib/neon-responses-language-model.d.ts +26 -0
  24. package/dist/lib/neon-responses-language-model.d.ts.map +1 -0
  25. package/dist/lib/neon-responses-language-model.js +44 -0
  26. package/dist/lib/neon-responses-language-model.js.map +1 -0
  27. package/dist/lib/provider.d.ts +48 -0
  28. package/dist/lib/provider.d.ts.map +1 -0
  29. package/dist/lib/provider.js +112 -0
  30. package/dist/lib/provider.js.map +1 -0
  31. package/dist/lib/version.d.ts +5 -0
  32. package/dist/lib/version.d.ts.map +1 -0
  33. package/dist/lib/version.js +7 -0
  34. package/dist/lib/version.js.map +1 -0
  35. package/dist/package.js +6 -0
  36. package/dist/package.js.map +1 -0
  37. package/dist/v1.d.ts +7 -0
  38. package/dist/v1.js +6 -0
  39. package/package.json +74 -23
  40. package/src/index.ts +0 -5
  41. package/src/lib/neon-anthropic-language-model.ts +0 -51
  42. package/src/lib/neon-capabilities.ts +0 -133
  43. package/src/lib/neon-chat-language-model.ts +0 -56
  44. package/src/lib/neon-chat-options.ts +0 -48
  45. package/src/lib/neon-model-capabilities.test.ts +0 -66
  46. package/src/lib/neon-model-capabilities.ts +0 -137
  47. package/src/lib/neon-responses-language-model.ts +0 -60
  48. package/src/lib/provider.ts +0 -211
  49. package/src/lib/version.ts +0 -2
  50. package/src/v1.ts +0 -26
  51. package/tsconfig.json +0 -4
  52. package/tsdown.config.ts +0 -12
  53. package/vitest.config.ts +0 -17
@@ -0,0 +1,26 @@
1
+ import { OpenAIResponsesLanguageModel } from "@ai-sdk/openai/internal";
2
+ import { LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4GenerateResult, LanguageModelV4StreamResult } 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 it detects that from the bare model id (`gpt-5`),
14
+ * which the gateway's required `databricks-` prefix defeats. For the GPT-5
15
+ * reasoning family we set the model's own `forceReasoning` provider option so it
16
+ * applies the correct reasoning behavior. Users can still override it.
17
+ */
18
+ declare class NeonResponsesLanguageModel extends OpenAIResponsesLanguageModel implements LanguageModelV4 {
19
+ private get isReasoningFamily();
20
+ private withForcedReasoning;
21
+ doGenerate(options: LanguageModelV4CallOptions): Promise<LanguageModelV4GenerateResult>;
22
+ doStream(options: LanguageModelV4CallOptions): Promise<LanguageModelV4StreamResult>;
23
+ }
24
+ //#endregion
25
+ export { NeonResponsesLanguageModel };
26
+ //# 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":";;;;;;;AAoBA;;;;;;;;;;AAE2B,cAFd,0BAAA,SACJ,4BAAA,YACG,eAAe,CAAA;;;sBA2BhB,6BACP,QAAQ;oBAKD,6BACP,QAAQ"}
@@ -0,0 +1,44 @@
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 it detects that from the bare model id (`gpt-5`),
11
+ * which the gateway's required `databricks-` prefix defeats. For the GPT-5
12
+ * reasoning family we set the model's own `forceReasoning` provider option so it
13
+ * applies the correct reasoning behavior. Users can still override it.
14
+ */
15
+ var NeonResponsesLanguageModel = class extends OpenAIResponsesLanguageModel {
16
+ get isReasoningFamily() {
17
+ return /gpt-5/.test(this.modelId.toLowerCase());
18
+ }
19
+ withForcedReasoning(options) {
20
+ if (!this.isReasoningFamily) return options;
21
+ const openai = options.providerOptions?.openai;
22
+ if (openai != null && "forceReasoning" in openai) return options;
23
+ return {
24
+ ...options,
25
+ providerOptions: {
26
+ ...options.providerOptions,
27
+ openai: {
28
+ ...openai,
29
+ forceReasoning: true
30
+ }
31
+ }
32
+ };
33
+ }
34
+ doGenerate(options) {
35
+ return super.doGenerate(this.withForcedReasoning(options));
36
+ }
37
+ doStream(options) {
38
+ return super.doStream(this.withForcedReasoning(options));
39
+ }
40
+ };
41
+ //#endregion
42
+ export { NeonResponsesLanguageModel };
43
+
44
+ //# 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\tLanguageModelV4,\n\tLanguageModelV4CallOptions,\n\tLanguageModelV4GenerateResult,\n\tLanguageModelV4StreamResult,\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 it detects that from the bare model id (`gpt-5`),\n * which the gateway's required `databricks-` prefix defeats. For the GPT-5\n * reasoning family we set the model's own `forceReasoning` provider option so it\n * applies the correct reasoning behavior. Users can still override it.\n */\nexport class NeonResponsesLanguageModel\n\textends OpenAIResponsesLanguageModel\n\timplements LanguageModelV4\n{\n\tprivate get isReasoningFamily(): boolean {\n\t\treturn /gpt-5/.test(this.modelId.toLowerCase());\n\t}\n\n\tprivate withForcedReasoning(\n\t\toptions: LanguageModelV4CallOptions,\n\t): LanguageModelV4CallOptions {\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: LanguageModelV4CallOptions,\n\t): Promise<LanguageModelV4GenerateResult> {\n\t\treturn super.doGenerate(this.withForcedReasoning(options));\n\t}\n\n\toverride doStream(\n\t\toptions: LanguageModelV4CallOptions,\n\t): Promise<LanguageModelV4StreamResult> {\n\t\treturn super.doStream(this.withForcedReasoning(options));\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AAoBA,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,48 @@
1
+ import { NeonChatModelId } from "./neon-chat-options.js";
2
+ import { FetchFunction } from "@ai-sdk/provider-utils";
3
+ import { LanguageModelV4, ProviderV4 } from "@ai-sdk/provider";
4
+ import { z } from "zod/v4";
5
+
6
+ //#region src/lib/provider.d.ts
7
+ declare const neonErrorSchema: z.ZodObject<{
8
+ error: z.ZodObject<{
9
+ message: z.ZodString;
10
+ type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
11
+ param: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
12
+ code: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
13
+ }, z.core.$strip>;
14
+ }, z.core.$strip>;
15
+ type NeonErrorData = z.infer<typeof neonErrorSchema>;
16
+ interface NeonProviderSettings {
17
+ /**
18
+ * Neon AI Gateway base URL — the branch-scoped host root, e.g.
19
+ * `https://<branch-id>-api.ai.<region>.aws.neon.tech`. The gateway paths are
20
+ * appended internally. Falls back to the `NEON_AI_GATEWAY_BASE_URL` env var.
21
+ */
22
+ baseURL?: string;
23
+ /**
24
+ * Neon AI Gateway platform token (the `nt_live_...` value). Falls back to the
25
+ * `NEON_AI_GATEWAY_TOKEN` env var.
26
+ */
27
+ apiKey?: string;
28
+ /** Custom headers to include in the requests. */
29
+ headers?: Record<string, string>;
30
+ /** Custom fetch implementation (e.g. for testing or middleware). */
31
+ fetch?: FetchFunction;
32
+ }
33
+ interface NeonProvider extends ProviderV4 {
34
+ /** Creates a Neon AI Gateway model for text generation. */
35
+ (modelId: NeonChatModelId): LanguageModelV4;
36
+ /** Creates a Neon AI Gateway model for text generation. */
37
+ languageModel(modelId: NeonChatModelId): LanguageModelV4;
38
+ /** Creates a Neon AI Gateway chat model for text generation. */
39
+ chat(modelId: NeonChatModelId): LanguageModelV4;
40
+ /** @deprecated Use `embeddingModel` instead. */
41
+ textEmbeddingModel(modelId: string): never;
42
+ }
43
+ declare function createNeon(options?: NeonProviderSettings): NeonProvider;
44
+ /** Default Neon provider instance (reads `NEON_AI_GATEWAY_*` env vars). */
45
+ declare const neon: NeonProvider;
46
+ //#endregion
47
+ export { NeonErrorData, NeonProvider, NeonProviderSettings, createNeon, neon };
48
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","names":[],"sources":["../../src/lib/provider.ts"],"mappings":";;;;;;cAiCM,iBAAe,CAAA,CAAA;;IAAf,OAAA,aAOJ;IAAA,IAAA,eAAA,cAAA,YAAA,CAAA,CAAA;;;;;KAEU,aAAA,GAAgB,CAAA,CAAE,aAAa;UA4C1B,oBAAA;;;;;;;;;AArDI;AASrB;EAAyB,MAAA,CAAA,EAAA,MAAA;;SAAK,CAAA,EA2DnB,MA3DmB,CAAA,MAAA,EAAA,MAAA,CAAA;EAAK;EA4ClB,KAAA,CAAA,EAkBR,aAlBQ;;AAeN,UAMM,YAAA,SAAqB,UAN3B,CAAA;;EAGW,CAAA,OAAA,EAKX,eALW,CAAA,EAKO,eALP;EAGL;EAAa,aAAA,CAAA,OAAA,EAKN,eALM,CAAA,EAKY,eALZ;;MAED,CAAA,OAAA,EAMd,eANc,CAAA,EAMI,eANJ;;oBAGa,CAAA,OAAA,EAAA,MAAA,CAAA,EAAA,KAAA;;AAGT,iBAMjB,UAAA,CANiB,OAAA,CAAA,EAMG,oBANH,CAAA,EAM+B,YAN/B;;AARe,cAuGnC,IAvGmC,EAuG/B,YAvG+B"}
@@ -0,0 +1,112 @@
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 { generateId, loadApiKey, loadSetting, withUserAgentSuffix, withoutTrailingSlash } from "@ai-sdk/provider-utils";
7
+ import { NoSuchModelError } from "@ai-sdk/provider";
8
+ import { z } from "zod/v4";
9
+ //#region src/lib/provider.ts
10
+ const neonErrorStructure = {
11
+ errorSchema: z.object({ error: z.object({
12
+ message: z.string(),
13
+ type: z.string().nullish(),
14
+ param: z.unknown().nullish(),
15
+ code: z.unknown().nullish()
16
+ }) }),
17
+ errorToMessage: (data) => data.error.message
18
+ };
19
+ /**
20
+ * Recursively remove the JSON Schema `$schema` marker, which some gateway
21
+ * backends (notably Gemini) reject in tool/structured-output schemas. Other
22
+ * backends ignore its absence, so stripping it everywhere is safe.
23
+ */
24
+ function stripJsonSchemaMarker(value) {
25
+ if (Array.isArray(value)) return value.map(stripJsonSchemaMarker);
26
+ if (value !== null && typeof value === "object") {
27
+ const result = {};
28
+ for (const [key, entry] of Object.entries(value)) {
29
+ if (key === "$schema") continue;
30
+ result[key] = stripJsonSchemaMarker(entry);
31
+ }
32
+ return result;
33
+ }
34
+ return value;
35
+ }
36
+ function transformNeonRequestBody(args) {
37
+ const transformed = { ...args };
38
+ if (transformed.tools != null) transformed.tools = stripJsonSchemaMarker(transformed.tools);
39
+ if (transformed.response_format != null) transformed.response_format = stripJsonSchemaMarker(transformed.response_format);
40
+ return transformed;
41
+ }
42
+ function createNeon(options = {}) {
43
+ const getHost = () => withoutTrailingSlash(loadSetting({
44
+ settingValue: options.baseURL,
45
+ environmentVariableName: "NEON_AI_GATEWAY_BASE_URL",
46
+ settingName: "baseURL",
47
+ description: "Neon AI Gateway base URL"
48
+ }));
49
+ const getHeaders = (extra) => withUserAgentSuffix({
50
+ Authorization: `Bearer ${loadApiKey({
51
+ apiKey: options.apiKey,
52
+ environmentVariableName: "NEON_AI_GATEWAY_TOKEN",
53
+ description: "Neon AI Gateway token"
54
+ })}`,
55
+ ...extra,
56
+ ...options.headers
57
+ }, `neondatabase/ai-sdk-provider/${VERSION}`);
58
+ const createAnthropicModel = (modelId) => new NeonAnthropicLanguageModel(modelId, {
59
+ provider: "neon.anthropic",
60
+ baseURL: `${getHost()}/ai-gateway/anthropic/v1`,
61
+ headers: () => getHeaders({ "anthropic-version": "2023-06-01" }),
62
+ fetch: options.fetch,
63
+ generateId
64
+ });
65
+ const createOpenAIModel = (modelId) => new NeonResponsesLanguageModel(modelId, {
66
+ provider: "neon.openai.responses",
67
+ url: ({ path }) => `${getHost()}/ai-gateway/openai/v1${path}`,
68
+ headers: getHeaders,
69
+ fetch: options.fetch,
70
+ fileIdPrefixes: ["file-"]
71
+ });
72
+ const createChatModel = (modelId) => new NeonChatLanguageModel(modelId, {
73
+ provider: "neon.chat",
74
+ url: ({ path }) => `${getHost()}/ai-gateway/mlflow/v1${path}`,
75
+ headers: getHeaders,
76
+ fetch: options.fetch,
77
+ errorStructure: neonErrorStructure,
78
+ transformRequestBody: transformNeonRequestBody,
79
+ supportsStructuredOutputs: true
80
+ });
81
+ const createLanguageModel = (modelId) => {
82
+ switch (getNeonModelRoute(modelId)) {
83
+ case "anthropic": return createAnthropicModel(modelId);
84
+ case "openai": return createOpenAIModel(modelId);
85
+ default: return createChatModel(modelId);
86
+ }
87
+ };
88
+ const provider = (modelId) => createLanguageModel(modelId);
89
+ provider.specificationVersion = "v4";
90
+ provider.languageModel = createLanguageModel;
91
+ provider.chat = createLanguageModel;
92
+ provider.embeddingModel = (modelId) => {
93
+ throw new NoSuchModelError({
94
+ modelId,
95
+ modelType: "embeddingModel"
96
+ });
97
+ };
98
+ provider.textEmbeddingModel = provider.embeddingModel;
99
+ provider.imageModel = (modelId) => {
100
+ throw new NoSuchModelError({
101
+ modelId,
102
+ modelType: "imageModel"
103
+ });
104
+ };
105
+ return provider;
106
+ }
107
+ /** Default Neon provider instance (reads `NEON_AI_GATEWAY_*` env vars). */
108
+ const neon = createNeon();
109
+ //#endregion
110
+ export { createNeon, neon };
111
+
112
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","names":[],"sources":["../../src/lib/provider.ts"],"sourcesContent":["/**\n * Community Vercel AI SDK provider for the Neon AI Gateway.\n *\n * `createNeon()` returns a provider that routes each model to the best gateway\n * endpoint based on its id (Anthropic → native Messages, OpenAI → native\n * Responses incl. Codex, everything else → unified MLflow), so a single\n * `neon('databricks-...')` call (same base URL + token) reaches the whole\n * catalog. Configure with the branch-scoped `NEON_AI_GATEWAY_BASE_URL` +\n * `NEON_AI_GATEWAY_TOKEN` emitted by `neonctl env pull` / `neon dev`, or pass\n * `baseURL` / `apiKey` explicitly.\n */\nimport type { ProviderErrorStructure } from \"@ai-sdk/openai-compatible\";\nimport {\n\ttype LanguageModelV4,\n\tNoSuchModelError,\n\ttype ProviderV4,\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 ProviderV4 {\n\t/** Creates a Neon AI Gateway model for text generation. */\n\t(modelId: NeonChatModelId): LanguageModelV4;\n\n\t/** Creates a Neon AI Gateway model for text generation. */\n\tlanguageModel(modelId: NeonChatModelId): LanguageModelV4;\n\n\t/** Creates a Neon AI Gateway chat model for text generation. */\n\tchat(modelId: NeonChatModelId): LanguageModelV4;\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): LanguageModelV4 => {\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 = \"v4\" as const;\n\tprovider.languageModel = createLanguageModel;\n\tprovider.chat = createLanguageModel;\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":";;;;;;;;;AA4CA,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;AAqCA,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;CAEhB,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,5 @@
1
+ //#region src/lib/version.d.ts
2
+ declare const VERSION: string;
3
+ //#endregion
4
+ export { VERSION };
5
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","names":[],"sources":["../../src/lib/version.ts"],"mappings":";cAIa"}
@@ -0,0 +1,7 @@
1
+ import { version } from "../package.js";
2
+ //#region src/lib/version.ts
3
+ const VERSION = version;
4
+ //#endregion
5
+ export { VERSION };
6
+
7
+ //# sourceMappingURL=version.js.map
@@ -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"}
@@ -0,0 +1,6 @@
1
+ //#region package.json
2
+ var version = "0.1.1";
3
+ //#endregion
4
+ export { version };
5
+
6
+ //# sourceMappingURL=package.js.map
@@ -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 { NeonChatModelId } 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 { NeonAnthropicLanguageModel, NeonChatLanguageModel, type NeonChatModelId, type NeonErrorData, type NeonModelCapabilities, type NeonModelFamily, type NeonModelRoute, type NeonProvider, type NeonProviderSettings, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
package/dist/v1.js ADDED
@@ -0,0 +1,6 @@
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 { NeonResponsesLanguageModel } from "./lib/neon-responses-language-model.js";
5
+ import { createNeon, neon } from "./lib/provider.js";
6
+ export { NeonAnthropicLanguageModel, NeonChatLanguageModel, NeonResponsesLanguageModel, createNeon, getNeonModelCapabilities, getNeonModelRoute, neon };
package/package.json CHANGED
@@ -1,24 +1,75 @@
1
1
  {
2
- "name": "@neondatabase/ai-sdk-provider",
3
- "version": "0.0.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
- }
2
+ "name": "@neondatabase/ai-sdk-provider",
3
+ "version": "0.1.1",
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
+ "console-fail-test": "0.5.0",
48
+ "tsdown": "^0.14.1",
49
+ "typescript": "^5.8.2",
50
+ "vitest": "^3.0.9",
51
+ "zod": "^4.4.3"
52
+ },
53
+ "engines": {
54
+ "node": ">=22"
55
+ },
56
+ "publishConfig": {
57
+ "provenance": false
58
+ },
59
+ "dependencies": {
60
+ "@ai-sdk/anthropic": "4.0.0-beta.42",
61
+ "@ai-sdk/openai": "4.0.0-beta.44",
62
+ "@ai-sdk/openai-compatible": "3.0.0-beta.36",
63
+ "@ai-sdk/provider": "4.0.0-beta.14",
64
+ "@ai-sdk/provider-utils": "5.0.0-beta.30"
65
+ },
66
+ "peerDependencies": {
67
+ "zod": "^3.25.76 || ^4.1.8"
68
+ },
69
+ "scripts": {
70
+ "build": "tsc --noEmit && tsdown",
71
+ "test": "vitest --passWithNoTests",
72
+ "test:ci": "vitest run --passWithNoTests",
73
+ "tsc": "tsc"
74
+ }
75
+ }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- /**
2
- * The default entry point re-exports the latest stable version. New consumers should import
3
- * directly from `@neondatabase/ai-sdk-provider/v1` to opt in to a specific major.
4
- */
5
- export * from "./v1.js";
@@ -1,51 +0,0 @@
1
- import { AnthropicLanguageModel } from "@ai-sdk/anthropic/internal";
2
- import type {
3
- LanguageModelV4,
4
- LanguageModelV4CallOptions,
5
- LanguageModelV4GenerateResult,
6
- LanguageModelV4StreamResult,
7
- } from "@ai-sdk/provider";
8
-
9
- /**
10
- * Language model for Anthropic models served via the Neon AI Gateway's native
11
- * Messages API (`/ai-gateway/anthropic/v1/messages`). Unlocks streaming
12
- * structured output and native reasoning for Claude.
13
- *
14
- * The shared Anthropic model defaults to fine-grained tool-input streaming
15
- * (`eager_input_streaming: true`) on streaming tool calls, which the gateway
16
- * rejects (`Extra inputs are not permitted`). We disable it via the model's own
17
- * `toolStreaming` option so streaming tool calls work. Users can still override.
18
- */
19
- export class NeonAnthropicLanguageModel
20
- extends AnthropicLanguageModel
21
- implements LanguageModelV4
22
- {
23
- private withGatewayCompat(
24
- options: LanguageModelV4CallOptions,
25
- ): LanguageModelV4CallOptions {
26
- const anthropic = options.providerOptions?.anthropic;
27
- // Respect an explicit user setting.
28
- if (anthropic != null && "toolStreaming" in anthropic) {
29
- return options;
30
- }
31
- return {
32
- ...options,
33
- providerOptions: {
34
- ...options.providerOptions,
35
- anthropic: { ...anthropic, toolStreaming: false },
36
- },
37
- };
38
- }
39
-
40
- override doGenerate(
41
- options: LanguageModelV4CallOptions,
42
- ): Promise<LanguageModelV4GenerateResult> {
43
- return super.doGenerate(this.withGatewayCompat(options));
44
- }
45
-
46
- override doStream(
47
- options: LanguageModelV4CallOptions,
48
- ): Promise<LanguageModelV4StreamResult> {
49
- return super.doStream(this.withGatewayCompat(options));
50
- }
51
- }
@@ -1,133 +0,0 @@
1
- import type {
2
- LanguageModelV4CallOptions,
3
- LanguageModelV4StreamPart,
4
- SharedV4ProviderOptions,
5
- SharedV4Warning,
6
- } from "@ai-sdk/provider";
7
- import { isCustomReasoning } from "@ai-sdk/provider-utils";
8
- import { getNeonModelCapabilities } from "./neon-model-capabilities.js";
9
-
10
- /**
11
- * Drop call options the resolved model's upstream backend does not accept and
12
- * collect a warning for each, so callers get a clear signal instead of a hard
13
- * `400` from the gateway.
14
- */
15
- export function applyNeonCapabilities(
16
- modelId: string,
17
- options: LanguageModelV4CallOptions,
18
- ): { options: LanguageModelV4CallOptions; warnings: SharedV4Warning[] } {
19
- const caps = getNeonModelCapabilities(modelId);
20
- const warnings: SharedV4Warning[] = [];
21
- const patch: Partial<LanguageModelV4CallOptions> = {};
22
-
23
- const dropUnsupported = (feature: string) => {
24
- warnings.push({
25
- type: "unsupported",
26
- feature,
27
- details: `${feature} is not supported by the Neon AI Gateway for ${caps.family} model "${modelId}" and was dropped.`,
28
- });
29
- };
30
-
31
- if (options.temperature != null && !caps.supportsTemperature) {
32
- patch.temperature = undefined;
33
- dropUnsupported("temperature");
34
- }
35
- if (options.topP != null && !caps.supportsTopP) {
36
- patch.topP = undefined;
37
- dropUnsupported("topP");
38
- }
39
-
40
- // Anthropic-style models accept only one of temperature / topP.
41
- const effectiveTemperature =
42
- "temperature" in patch ? patch.temperature : options.temperature;
43
- const effectiveTopP = "topP" in patch ? patch.topP : options.topP;
44
- if (
45
- caps.temperatureTopPMutuallyExclusive &&
46
- effectiveTemperature != null &&
47
- effectiveTopP != null
48
- ) {
49
- patch.topP = undefined;
50
- warnings.push({
51
- type: "compatibility",
52
- feature: "topP",
53
- details: `${caps.family} models accept only one of temperature or topP; topP was dropped.`,
54
- });
55
- }
56
-
57
- if (options.frequencyPenalty != null && !caps.supportsPenalties) {
58
- patch.frequencyPenalty = undefined;
59
- dropUnsupported("frequencyPenalty");
60
- }
61
- if (options.presencePenalty != null && !caps.supportsPenalties) {
62
- patch.presencePenalty = undefined;
63
- dropUnsupported("presencePenalty");
64
- }
65
- if (options.seed != null && !caps.supportsSeed) {
66
- patch.seed = undefined;
67
- dropUnsupported("seed");
68
- }
69
- if (options.stopSequences != null && !caps.supportsStopSequences) {
70
- patch.stopSequences = undefined;
71
- dropUnsupported("stopSequences");
72
- }
73
-
74
- if (!caps.supportsReasoningEffort) {
75
- const { providerOptions } = options;
76
- const hasProviderEffort =
77
- providerOptions != null &&
78
- Object.values(providerOptions).some(
79
- (group) =>
80
- "reasoningEffort" in group && group.reasoningEffort != null,
81
- );
82
- const hasReasoningOption =
83
- isCustomReasoning(options.reasoning) &&
84
- options.reasoning !== "none";
85
-
86
- if (hasProviderEffort || hasReasoningOption) {
87
- dropUnsupported("reasoningEffort");
88
- if (hasProviderEffort && providerOptions != null) {
89
- const cleaned: SharedV4ProviderOptions = {};
90
- for (const [key, group] of Object.entries(providerOptions)) {
91
- if ("reasoningEffort" in group) {
92
- const { reasoningEffort: _removed, ...rest } = group;
93
- cleaned[key] = rest;
94
- } else {
95
- cleaned[key] = group;
96
- }
97
- }
98
- patch.providerOptions = cleaned;
99
- }
100
- if (hasReasoningOption) {
101
- patch.reasoning = undefined;
102
- }
103
- }
104
- }
105
-
106
- if (Object.keys(patch).length === 0) {
107
- return { options, warnings };
108
- }
109
- return { options: { ...options, ...patch }, warnings };
110
- }
111
-
112
- /**
113
- * Merge additional warnings into the `stream-start` part of a model stream.
114
- */
115
- export function mergeStreamStartWarnings(extra: SharedV4Warning[]) {
116
- let merged = false;
117
- return new TransformStream<
118
- LanguageModelV4StreamPart,
119
- LanguageModelV4StreamPart
120
- >({
121
- transform(part, controller) {
122
- if (!merged && part.type === "stream-start") {
123
- merged = true;
124
- controller.enqueue({
125
- ...part,
126
- warnings: [...extra, ...part.warnings],
127
- });
128
- } else {
129
- controller.enqueue(part);
130
- }
131
- },
132
- });
133
- }