@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.
Files changed (55) hide show
  1. package/LICENSE.md +178 -0
  2. package/README.md +24 -9
  3. package/dist/index.d.ts +7 -0
  4. package/dist/index.js +8 -0
  5. package/dist/lib/neon-anthropic-language-model.d.ts +23 -0
  6. package/dist/lib/neon-anthropic-language-model.d.ts.map +1 -0
  7. package/dist/lib/neon-anthropic-language-model.js +38 -0
  8. package/dist/lib/neon-anthropic-language-model.js.map +1 -0
  9. package/dist/lib/neon-capabilities.d.ts +20 -0
  10. package/dist/lib/neon-capabilities.d.ts.map +1 -0
  11. package/dist/lib/neon-capabilities.js +97 -0
  12. package/dist/lib/neon-capabilities.js.map +1 -0
  13. package/dist/lib/neon-chat-language-model.d.ts +21 -0
  14. package/dist/lib/neon-chat-language-model.d.ts.map +1 -0
  15. package/dist/lib/neon-chat-language-model.js +34 -0
  16. package/dist/lib/neon-chat-language-model.js.map +1 -0
  17. package/dist/lib/neon-chat-options.d.ts +21 -0
  18. package/dist/lib/neon-chat-options.d.ts.map +1 -0
  19. package/dist/lib/neon-chat-options.js +52 -0
  20. package/dist/lib/neon-chat-options.js.map +1 -0
  21. package/dist/lib/neon-model-capabilities.d.ts +41 -0
  22. package/dist/lib/neon-model-capabilities.d.ts.map +1 -0
  23. package/dist/lib/neon-model-capabilities.js +74 -0
  24. package/dist/lib/neon-model-capabilities.js.map +1 -0
  25. package/dist/lib/neon-responses-language-model.d.ts +27 -0
  26. package/dist/lib/neon-responses-language-model.d.ts.map +1 -0
  27. package/dist/lib/neon-responses-language-model.js +45 -0
  28. package/dist/lib/neon-responses-language-model.js.map +1 -0
  29. package/dist/lib/provider.d.ts +52 -0
  30. package/dist/lib/provider.d.ts.map +1 -0
  31. package/dist/lib/provider.js +126 -0
  32. package/dist/lib/provider.js.map +1 -0
  33. package/dist/lib/version.d.ts +5 -0
  34. package/dist/lib/version.d.ts.map +1 -0
  35. package/dist/lib/version.js +7 -0
  36. package/dist/lib/version.js.map +1 -0
  37. package/dist/package.js +6 -0
  38. package/dist/package.js.map +1 -0
  39. package/dist/v1.d.ts +7 -0
  40. package/dist/v1.js +7 -0
  41. package/package.json +78 -23
  42. package/src/index.ts +0 -5
  43. package/src/lib/neon-anthropic-language-model.ts +0 -51
  44. package/src/lib/neon-capabilities.ts +0 -133
  45. package/src/lib/neon-chat-language-model.ts +0 -56
  46. package/src/lib/neon-chat-options.ts +0 -48
  47. package/src/lib/neon-model-capabilities.test.ts +0 -66
  48. package/src/lib/neon-model-capabilities.ts +0 -137
  49. package/src/lib/neon-responses-language-model.ts +0 -60
  50. package/src/lib/provider.ts +0 -211
  51. package/src/lib/version.ts +0 -2
  52. package/src/v1.ts +0 -26
  53. package/tsconfig.json +0 -4
  54. package/tsdown.config.ts +0 -12
  55. package/vitest.config.ts +0 -17
@@ -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
- }
@@ -1,56 +0,0 @@
1
- import { OpenAICompatibleChatLanguageModel } from "@ai-sdk/openai-compatible";
2
- import type {
3
- LanguageModelV4,
4
- LanguageModelV4CallOptions,
5
- LanguageModelV4GenerateResult,
6
- LanguageModelV4StreamResult,
7
- } from "@ai-sdk/provider";
8
- import {
9
- applyNeonCapabilities,
10
- mergeStreamStartWarnings,
11
- } from "./neon-capabilities.js";
12
-
13
- /**
14
- * Language model for the Neon AI Gateway unified (MLflow) endpoint.
15
- *
16
- * Used for every model that is not routed to a provider-native endpoint
17
- * (Gemini, Llama, Qwen, gpt-oss, ...). It is a thin specialization of the shared
18
- * OpenAI-compatible chat model that adds per-model capability handling:
19
- * parameters an upstream backend is known to reject are dropped and reported as
20
- * warnings instead of failing the request.
21
- */
22
- export class NeonChatLanguageModel
23
- extends OpenAICompatibleChatLanguageModel
24
- implements LanguageModelV4
25
- {
26
- override async doGenerate(
27
- options: LanguageModelV4CallOptions,
28
- ): Promise<LanguageModelV4GenerateResult> {
29
- const { options: adjusted, warnings } = applyNeonCapabilities(
30
- this.modelId,
31
- options,
32
- );
33
- const result = await super.doGenerate(adjusted);
34
- return warnings.length > 0
35
- ? { ...result, warnings: [...warnings, ...result.warnings] }
36
- : result;
37
- }
38
-
39
- override async doStream(
40
- options: LanguageModelV4CallOptions,
41
- ): Promise<LanguageModelV4StreamResult> {
42
- const { options: adjusted, warnings } = applyNeonCapabilities(
43
- this.modelId,
44
- options,
45
- );
46
- const result = await super.doStream(adjusted);
47
- return warnings.length > 0
48
- ? {
49
- ...result,
50
- stream: result.stream.pipeThrough(
51
- mergeStreamStartWarnings(warnings),
52
- ),
53
- }
54
- : result;
55
- }
56
- }
@@ -1,48 +0,0 @@
1
- // Models served by the Neon AI Gateway. Anthropic and OpenAI models are routed
2
- // to their provider-native endpoints; everything else uses the unified MLflow
3
- // (OpenAI-compatible) endpoint.
4
- //
5
- // The authoritative, always-current catalog is shown in the Neon Console under
6
- // the branch's "AI Gateway" tab. Any other id can be passed as a plain string
7
- // via the `(string & {})` fallback.
8
- export type NeonChatModelId =
9
- // Anthropic (native Messages API)
10
- | "databricks-claude-opus-4-8"
11
- | "databricks-claude-opus-4-7"
12
- | "databricks-claude-opus-4-6"
13
- | "databricks-claude-opus-4-5"
14
- | "databricks-claude-opus-4-1"
15
- | "databricks-claude-sonnet-4-6"
16
- | "databricks-claude-sonnet-4-5"
17
- | "databricks-claude-sonnet-4"
18
- | "databricks-claude-haiku-4-5"
19
- // OpenAI (native Responses API, incl. Codex)
20
- | "databricks-gpt-5"
21
- | "databricks-gpt-5-mini"
22
- | "databricks-gpt-5-nano"
23
- | "databricks-gpt-5-1"
24
- | "databricks-gpt-5-2"
25
- | "databricks-gpt-5-2-codex"
26
- | "databricks-gpt-5-3-codex"
27
- | "databricks-gpt-5-4"
28
- | "databricks-gpt-5-4-mini"
29
- | "databricks-gpt-5-4-nano"
30
- | "databricks-gpt-5-5"
31
- | "databricks-gpt-5-5-pro"
32
- // OpenAI open-weight (unified MLflow endpoint)
33
- | "databricks-gpt-oss-120b"
34
- | "databricks-gpt-oss-20b"
35
- // Google (unified MLflow endpoint)
36
- | "databricks-gemini-3-5-flash"
37
- | "databricks-gemini-3-1-flash-lite"
38
- | "databricks-gemini-2-5-pro"
39
- | "databricks-gemini-2-5-flash"
40
- | "databricks-gemma-3-12b"
41
- // Meta (unified MLflow endpoint)
42
- | "databricks-llama-4-maverick"
43
- | "databricks-meta-llama-3-3-70b-instruct"
44
- | "databricks-meta-llama-3-1-8b-instruct"
45
- // Alibaba (unified MLflow endpoint)
46
- | "databricks-qwen3-next-80b-a3b-instruct"
47
- | "databricks-qwen35-122b-a10b"
48
- | (string & {});
@@ -1,66 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- getNeonModelCapabilities,
4
- getNeonModelRoute,
5
- } from "./neon-model-capabilities.js";
6
-
7
- describe("getNeonModelRoute", () => {
8
- it("routes Anthropic and OpenAI models to their native endpoints", () => {
9
- expect(getNeonModelRoute("databricks-claude-opus-4-8")).toBe(
10
- "anthropic",
11
- );
12
- expect(getNeonModelRoute("databricks-claude-haiku-4-5")).toBe(
13
- "anthropic",
14
- );
15
- expect(getNeonModelRoute("databricks-gpt-5")).toBe("openai");
16
- expect(getNeonModelRoute("databricks-gpt-5-mini")).toBe("openai");
17
- expect(getNeonModelRoute("databricks-gpt-5-3-codex")).toBe("openai");
18
- });
19
-
20
- it("falls back to the unified MLflow endpoint for everything else", () => {
21
- // Gemini is routed to MLflow because its native endpoint cannot stream.
22
- expect(getNeonModelRoute("databricks-gemini-2-5-flash")).toBe("mlflow");
23
- expect(getNeonModelRoute("databricks-llama-4-maverick")).toBe("mlflow");
24
- expect(getNeonModelRoute("databricks-qwen35-122b-a10b")).toBe("mlflow");
25
- // gpt-oss is open-weight and served on the unified endpoint, not Responses.
26
- expect(getNeonModelRoute("databricks-gpt-oss-120b")).toBe("mlflow");
27
- });
28
- });
29
-
30
- describe("getNeonModelCapabilities", () => {
31
- it("marks Anthropic models correctly", () => {
32
- const caps = getNeonModelCapabilities("databricks-claude-haiku-4-5");
33
- expect(caps.family).toBe("anthropic");
34
- expect(caps.supportsPenalties).toBe(false);
35
- expect(caps.supportsSeed).toBe(false);
36
- expect(caps.temperatureTopPMutuallyExclusive).toBe(true);
37
- expect(caps.supportsReasoningEffort).toBe(false);
38
- });
39
-
40
- it("marks plain GPT-5 reasoning models without temperature/topP support", () => {
41
- const caps = getNeonModelCapabilities("databricks-gpt-5-mini");
42
- expect(caps.family).toBe("openai");
43
- expect(caps.supportsTemperature).toBe(false);
44
- expect(caps.supportsTopP).toBe(false);
45
- });
46
-
47
- it("keeps temperature for gpt-5.1+ models", () => {
48
- const caps = getNeonModelCapabilities("databricks-gpt-5-1");
49
- expect(caps.supportsTemperature).toBe(true);
50
- expect(caps.supportsTopP).toBe(true);
51
- });
52
-
53
- it("marks Meta models without penalties/seed support", () => {
54
- const caps = getNeonModelCapabilities("databricks-llama-4-maverick");
55
- expect(caps.family).toBe("meta");
56
- expect(caps.supportsPenalties).toBe(false);
57
- expect(caps.supportsSeed).toBe(false);
58
- });
59
-
60
- it("is permissive for unknown models", () => {
61
- const caps = getNeonModelCapabilities("databricks-qwen35-122b-a10b");
62
- expect(caps.family).toBe("other");
63
- expect(caps.supportsPenalties).toBe(true);
64
- expect(caps.supportsReasoningEffort).toBe(true);
65
- });
66
- });
@@ -1,137 +0,0 @@
1
- export type NeonModelFamily =
2
- | "anthropic"
3
- | "google"
4
- | "openai"
5
- | "meta"
6
- | "other";
7
-
8
- /**
9
- * Which gateway endpoint a model should be routed to.
10
- *
11
- * - `anthropic`: native Messages API — streaming structured output + native reasoning.
12
- * - `openai`: native Responses API — models served only natively (e.g. Codex),
13
- * native reasoning, and the image-generation tool.
14
- * - `mlflow`: the unified, OpenAI-compatible endpoint (the fallback). Gemini is
15
- * routed here because the gateway's native Gemini endpoint does not support
16
- * streaming (`streamGenerateContent` is rejected).
17
- */
18
- export type NeonModelRoute = "anthropic" | "openai" | "mlflow";
19
-
20
- export function getNeonModelRoute(modelId: string): NeonModelRoute {
21
- const id = modelId.toLowerCase();
22
- if (id.includes("claude")) {
23
- return "anthropic";
24
- }
25
- // OpenAI proprietary models (gpt-4/gpt-5 families, Codex) are served only via
26
- // the native Responses API. `gpt-oss` is open-weight and served on the
27
- // unified chat endpoint, so it is intentionally excluded here.
28
- if (
29
- (id.includes("gpt-") && !id.includes("gpt-oss")) ||
30
- id.includes("codex")
31
- ) {
32
- return "openai";
33
- }
34
- return "mlflow";
35
- }
36
-
37
- export interface NeonModelCapabilities {
38
- family: NeonModelFamily;
39
- supportsTemperature: boolean;
40
- supportsTopP: boolean;
41
- /**
42
- * Whether `temperature` and `topP` may be sent together. Anthropic models
43
- * accept only one of the two.
44
- */
45
- temperatureTopPMutuallyExclusive: boolean;
46
- supportsPenalties: boolean;
47
- supportsSeed: boolean;
48
- supportsStopSequences: boolean;
49
- supportsReasoningEffort: boolean;
50
- }
51
-
52
- const PERMISSIVE: Omit<NeonModelCapabilities, "family"> = {
53
- supportsTemperature: true,
54
- supportsTopP: true,
55
- temperatureTopPMutuallyExclusive: false,
56
- supportsPenalties: true,
57
- supportsSeed: true,
58
- supportsStopSequences: true,
59
- supportsReasoningEffort: true,
60
- };
61
-
62
- /**
63
- * Heuristic, prefix-based capability detection for MLflow-routed Neon models.
64
- *
65
- * The unified endpoint proxies to heterogeneous upstream providers, each of
66
- * which accepts a different subset of the OpenAI-style parameters the AI SDK
67
- * emits. Sending a parameter an upstream rejects results in a hard `400`, so we
68
- * strip the parameters a family is known to reject and surface a warning
69
- * instead. Unknown/untested models stay permissive (passed through unchanged).
70
- */
71
- export function getNeonModelCapabilities(
72
- modelId: string,
73
- ): NeonModelCapabilities {
74
- const id = modelId.toLowerCase();
75
-
76
- // Anthropic (Claude): rejects penalties and seed, accepts only one of
77
- // temperature/topP, and rejects the OpenAI `reasoning_effort` field.
78
- if (id.includes("claude")) {
79
- return {
80
- family: "anthropic",
81
- supportsTemperature: true,
82
- supportsTopP: true,
83
- temperatureTopPMutuallyExclusive: true,
84
- supportsPenalties: false,
85
- supportsSeed: false,
86
- supportsStopSequences: true,
87
- supportsReasoningEffort: false,
88
- };
89
- }
90
-
91
- // Google (Gemini): accepts standard sampling params but rejects the OpenAI
92
- // `reasoning_effort` field (not part of Gemini's generation config).
93
- if (id.includes("gemini")) {
94
- return {
95
- family: "google",
96
- ...PERMISSIVE,
97
- supportsReasoningEffort: false,
98
- };
99
- }
100
-
101
- // OpenAI GPT-5 reasoning family (routed to the native Responses API). The
102
- // Responses model strips parameters the Responses API doesn't accept
103
- // (penalties, seed, stop), but its reasoning-model detection matches the bare
104
- // model id (`gpt-5`), which the gateway's `databricks-` prefix defeats. So we
105
- // only handle the temperature/topP restriction here: the original gpt-5 /
106
- // gpt-5-mini / gpt-5-nano require the default temperature and reject topP,
107
- // while gpt-5.1+ (a minor version digit follows) accept them again.
108
- if (/gpt-5/.test(id)) {
109
- const hasMinorVersion = /gpt-5[.-]\d/.test(id);
110
- return {
111
- family: "openai",
112
- supportsTemperature: hasMinorVersion,
113
- supportsTopP: hasMinorVersion,
114
- temperatureTopPMutuallyExclusive: false,
115
- supportsPenalties: true,
116
- supportsSeed: true,
117
- supportsStopSequences: true,
118
- supportsReasoningEffort: true,
119
- };
120
- }
121
-
122
- // Meta (Llama): rejects penalties and seed; accepts sampling params and stop.
123
- if (id.includes("llama")) {
124
- return {
125
- family: "meta",
126
- supportsTemperature: true,
127
- supportsTopP: true,
128
- temperatureTopPMutuallyExclusive: false,
129
- supportsPenalties: false,
130
- supportsSeed: false,
131
- supportsStopSequences: true,
132
- supportsReasoningEffort: true,
133
- };
134
- }
135
-
136
- return { family: "other", ...PERMISSIVE };
137
- }
@@ -1,60 +0,0 @@
1
- import { OpenAIResponsesLanguageModel } from "@ai-sdk/openai/internal";
2
- import type {
3
- LanguageModelV4,
4
- LanguageModelV4CallOptions,
5
- LanguageModelV4GenerateResult,
6
- LanguageModelV4StreamResult,
7
- } from "@ai-sdk/provider";
8
-
9
- /**
10
- * Language model for OpenAI models served via the Neon AI Gateway's native
11
- * Responses API (`/ai-gateway/openai/v1/responses`). This route is required for
12
- * models that are only served natively (e.g. Codex) and unlocks native
13
- * reasoning and the image-generation tool.
14
- *
15
- * The shared OpenAI Responses model shapes a request based on whether the model
16
- * is a reasoning model, but it detects that from the bare model id (`gpt-5`),
17
- * which the gateway's required `databricks-` prefix defeats. For the GPT-5
18
- * reasoning family we set the model's own `forceReasoning` provider option so it
19
- * applies the correct reasoning behavior. Users can still override it.
20
- */
21
- export class NeonResponsesLanguageModel
22
- extends OpenAIResponsesLanguageModel
23
- implements LanguageModelV4
24
- {
25
- private get isReasoningFamily(): boolean {
26
- return /gpt-5/.test(this.modelId.toLowerCase());
27
- }
28
-
29
- private withForcedReasoning(
30
- options: LanguageModelV4CallOptions,
31
- ): LanguageModelV4CallOptions {
32
- if (!this.isReasoningFamily) {
33
- return options;
34
- }
35
- const openai = options.providerOptions?.openai;
36
- // Respect an explicit user setting.
37
- if (openai != null && "forceReasoning" in openai) {
38
- return options;
39
- }
40
- return {
41
- ...options,
42
- providerOptions: {
43
- ...options.providerOptions,
44
- openai: { ...openai, forceReasoning: true },
45
- },
46
- };
47
- }
48
-
49
- override doGenerate(
50
- options: LanguageModelV4CallOptions,
51
- ): Promise<LanguageModelV4GenerateResult> {
52
- return super.doGenerate(this.withForcedReasoning(options));
53
- }
54
-
55
- override doStream(
56
- options: LanguageModelV4CallOptions,
57
- ): Promise<LanguageModelV4StreamResult> {
58
- return super.doStream(this.withForcedReasoning(options));
59
- }
60
- }