@hebo-ai/gateway 0.6.2-rc0 → 0.6.2

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 (134) hide show
  1. package/README.md +3 -3
  2. package/dist/endpoints/chat-completions/converters.js +26 -21
  3. package/dist/endpoints/chat-completions/handler.js +2 -0
  4. package/dist/endpoints/chat-completions/otel.js +1 -1
  5. package/dist/endpoints/chat-completions/schema.d.ts +4 -18
  6. package/dist/endpoints/chat-completions/schema.js +14 -17
  7. package/dist/endpoints/embeddings/handler.js +2 -0
  8. package/dist/endpoints/embeddings/otel.js +5 -0
  9. package/dist/endpoints/embeddings/schema.d.ts +6 -0
  10. package/dist/endpoints/embeddings/schema.js +4 -1
  11. package/dist/endpoints/models/converters.js +3 -3
  12. package/dist/lifecycle.js +2 -2
  13. package/dist/logger/default.js +3 -3
  14. package/dist/logger/index.d.ts +2 -5
  15. package/dist/middleware/common.js +1 -0
  16. package/dist/middleware/utils.js +0 -3
  17. package/dist/models/amazon/middleware.js +8 -5
  18. package/dist/models/anthropic/middleware.js +13 -13
  19. package/dist/models/catalog.js +5 -1
  20. package/dist/models/cohere/middleware.js +7 -5
  21. package/dist/models/google/middleware.d.ts +1 -1
  22. package/dist/models/google/middleware.js +29 -25
  23. package/dist/models/openai/middleware.js +13 -9
  24. package/dist/models/voyage/middleware.js +2 -1
  25. package/dist/providers/bedrock/middleware.js +21 -23
  26. package/dist/providers/registry.js +3 -0
  27. package/dist/telemetry/fetch.js +7 -2
  28. package/dist/telemetry/gen-ai.js +15 -12
  29. package/dist/telemetry/memory.d.ts +1 -1
  30. package/dist/telemetry/memory.js +30 -14
  31. package/dist/telemetry/span.js +1 -1
  32. package/dist/telemetry/stream.js +30 -23
  33. package/dist/utils/env.js +4 -2
  34. package/dist/utils/preset.js +1 -0
  35. package/dist/utils/response.js +3 -1
  36. package/package.json +36 -50
  37. package/src/config.ts +0 -98
  38. package/src/endpoints/chat-completions/converters.test.ts +0 -631
  39. package/src/endpoints/chat-completions/converters.ts +0 -899
  40. package/src/endpoints/chat-completions/handler.test.ts +0 -391
  41. package/src/endpoints/chat-completions/handler.ts +0 -201
  42. package/src/endpoints/chat-completions/index.ts +0 -4
  43. package/src/endpoints/chat-completions/otel.test.ts +0 -315
  44. package/src/endpoints/chat-completions/otel.ts +0 -214
  45. package/src/endpoints/chat-completions/schema.ts +0 -364
  46. package/src/endpoints/embeddings/converters.ts +0 -51
  47. package/src/endpoints/embeddings/handler.test.ts +0 -133
  48. package/src/endpoints/embeddings/handler.ts +0 -137
  49. package/src/endpoints/embeddings/index.ts +0 -4
  50. package/src/endpoints/embeddings/otel.ts +0 -40
  51. package/src/endpoints/embeddings/schema.ts +0 -36
  52. package/src/endpoints/models/converters.ts +0 -56
  53. package/src/endpoints/models/handler.test.ts +0 -122
  54. package/src/endpoints/models/handler.ts +0 -37
  55. package/src/endpoints/models/index.ts +0 -3
  56. package/src/endpoints/models/schema.ts +0 -37
  57. package/src/errors/ai-sdk.ts +0 -99
  58. package/src/errors/gateway.ts +0 -17
  59. package/src/errors/openai.ts +0 -57
  60. package/src/errors/utils.ts +0 -47
  61. package/src/gateway.ts +0 -50
  62. package/src/index.ts +0 -19
  63. package/src/lifecycle.ts +0 -135
  64. package/src/logger/default.ts +0 -105
  65. package/src/logger/index.ts +0 -42
  66. package/src/middleware/common.test.ts +0 -215
  67. package/src/middleware/common.ts +0 -163
  68. package/src/middleware/debug.ts +0 -37
  69. package/src/middleware/matcher.ts +0 -161
  70. package/src/middleware/utils.ts +0 -34
  71. package/src/models/amazon/index.ts +0 -2
  72. package/src/models/amazon/middleware.test.ts +0 -133
  73. package/src/models/amazon/middleware.ts +0 -79
  74. package/src/models/amazon/presets.ts +0 -104
  75. package/src/models/anthropic/index.ts +0 -2
  76. package/src/models/anthropic/middleware.test.ts +0 -643
  77. package/src/models/anthropic/middleware.ts +0 -148
  78. package/src/models/anthropic/presets.ts +0 -191
  79. package/src/models/catalog.ts +0 -13
  80. package/src/models/cohere/index.ts +0 -2
  81. package/src/models/cohere/middleware.test.ts +0 -138
  82. package/src/models/cohere/middleware.ts +0 -76
  83. package/src/models/cohere/presets.ts +0 -186
  84. package/src/models/google/index.ts +0 -2
  85. package/src/models/google/middleware.test.ts +0 -298
  86. package/src/models/google/middleware.ts +0 -137
  87. package/src/models/google/presets.ts +0 -118
  88. package/src/models/meta/index.ts +0 -1
  89. package/src/models/meta/presets.ts +0 -143
  90. package/src/models/openai/index.ts +0 -2
  91. package/src/models/openai/middleware.test.ts +0 -189
  92. package/src/models/openai/middleware.ts +0 -103
  93. package/src/models/openai/presets.ts +0 -280
  94. package/src/models/types.ts +0 -114
  95. package/src/models/voyage/index.ts +0 -2
  96. package/src/models/voyage/middleware.test.ts +0 -28
  97. package/src/models/voyage/middleware.ts +0 -23
  98. package/src/models/voyage/presets.ts +0 -126
  99. package/src/providers/anthropic/canonical.ts +0 -17
  100. package/src/providers/anthropic/index.ts +0 -1
  101. package/src/providers/bedrock/canonical.ts +0 -87
  102. package/src/providers/bedrock/index.ts +0 -2
  103. package/src/providers/bedrock/middleware.test.ts +0 -303
  104. package/src/providers/bedrock/middleware.ts +0 -128
  105. package/src/providers/cohere/canonical.ts +0 -26
  106. package/src/providers/cohere/index.ts +0 -1
  107. package/src/providers/groq/canonical.ts +0 -21
  108. package/src/providers/groq/index.ts +0 -1
  109. package/src/providers/openai/canonical.ts +0 -16
  110. package/src/providers/openai/index.ts +0 -1
  111. package/src/providers/registry.test.ts +0 -44
  112. package/src/providers/registry.ts +0 -165
  113. package/src/providers/types.ts +0 -20
  114. package/src/providers/vertex/canonical.ts +0 -17
  115. package/src/providers/vertex/index.ts +0 -1
  116. package/src/providers/voyage/canonical.ts +0 -16
  117. package/src/providers/voyage/index.ts +0 -1
  118. package/src/telemetry/ai-sdk.ts +0 -46
  119. package/src/telemetry/baggage.ts +0 -27
  120. package/src/telemetry/fetch.ts +0 -62
  121. package/src/telemetry/gen-ai.ts +0 -113
  122. package/src/telemetry/http.ts +0 -62
  123. package/src/telemetry/index.ts +0 -1
  124. package/src/telemetry/memory.ts +0 -36
  125. package/src/telemetry/span.ts +0 -85
  126. package/src/telemetry/stream.ts +0 -64
  127. package/src/types.ts +0 -223
  128. package/src/utils/env.ts +0 -7
  129. package/src/utils/headers.ts +0 -27
  130. package/src/utils/preset.ts +0 -65
  131. package/src/utils/request.test.ts +0 -75
  132. package/src/utils/request.ts +0 -52
  133. package/src/utils/response.ts +0 -84
  134. package/src/utils/url.ts +0 -26
@@ -1,148 +0,0 @@
1
- import type { LanguageModelMiddleware } from "ai";
2
-
3
- import type {
4
- ChatCompletionsCacheControl,
5
- ChatCompletionsReasoningConfig,
6
- ChatCompletionsReasoningEffort,
7
- } from "../../endpoints/chat-completions/schema";
8
-
9
- import { modelMiddlewareMatcher } from "../../middleware/matcher";
10
- import { calculateReasoningBudgetFromEffort } from "../../middleware/utils";
11
-
12
- const isClaude = (family: "opus" | "sonnet" | "haiku", version: string) => {
13
- const dashed = version.replace(".", "-");
14
-
15
- return (modelId: string) =>
16
- modelId.includes(`claude-${family}-${version}`) ||
17
- modelId.includes(`claude-${family}-${dashed}`);
18
- };
19
-
20
- const isClaude4 = (modelId: string) => modelId.includes("claude-") && modelId.includes("-4");
21
-
22
- const isOpus46 = isClaude("opus", "4.6");
23
- const isOpus45 = isClaude("opus", "4.5");
24
- const isOpus4 = isClaude("opus", "4");
25
- const isSonnet46 = isClaude("sonnet", "4.6");
26
-
27
- export function mapClaudeReasoningEffort(effort: ChatCompletionsReasoningEffort, modelId: string) {
28
- if (isOpus46(modelId)) {
29
- switch (effort) {
30
- case "none":
31
- case "minimal":
32
- case "low":
33
- return "low";
34
- case "medium":
35
- return "medium";
36
- case "high":
37
- return "high";
38
- case "xhigh":
39
- case "max":
40
- return "max";
41
- }
42
- }
43
-
44
- switch (effort) {
45
- case "none":
46
- case "minimal":
47
- case "low":
48
- return "low";
49
- case "medium":
50
- return "medium";
51
- case "high":
52
- case "xhigh":
53
- case "max":
54
- return "high";
55
- }
56
- }
57
-
58
- function getMaxOutputTokens(modelId: string): number {
59
- if (isOpus46(modelId)) return 128_000;
60
- if (isOpus45(modelId)) return 64_000;
61
- if (isOpus4(modelId)) return 32_000;
62
- return 64_000;
63
- }
64
-
65
- // Documentation:
66
- // https://platform.claude.com/docs/en/build-with-claude/effort
67
- // https://platform.claude.com/docs/en/build-with-claude/extended-thinking
68
- // https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking
69
- export const claudeReasoningMiddleware: LanguageModelMiddleware = {
70
- specificationVersion: "v3",
71
- // oxlint-disable-next-line require-await
72
- transformParams: async ({ params, model }) => {
73
- const unknown = params.providerOptions?.["unknown"];
74
- if (!unknown) return params;
75
-
76
- const reasoning = unknown["reasoning"] as ChatCompletionsReasoningConfig;
77
- if (!reasoning) return params;
78
-
79
- const target = (params.providerOptions!["anthropic"] ??= {});
80
- const modelId = model.modelId;
81
- const clampedMaxTokens =
82
- reasoning.max_tokens && Math.min(reasoning.max_tokens, getMaxOutputTokens(modelId));
83
-
84
- if (!reasoning.enabled) {
85
- target["thinking"] = { type: "disabled" };
86
- } else if (reasoning.effort) {
87
- if (isClaude4(modelId)) {
88
- target["effort"] = mapClaudeReasoningEffort(reasoning.effort, modelId);
89
- }
90
-
91
- if (isOpus46(modelId)) {
92
- target["thinking"] = clampedMaxTokens
93
- ? { type: "adaptive", budgetTokens: clampedMaxTokens }
94
- : { type: "adaptive" };
95
- } else if (isSonnet46(modelId)) {
96
- target["thinking"] = clampedMaxTokens
97
- ? { type: "enabled", budgetTokens: clampedMaxTokens }
98
- : { type: "adaptive" };
99
- } else {
100
- target["thinking"] = { type: "enabled" };
101
- if (clampedMaxTokens) {
102
- target["thinking"]["budgetTokens"] = clampedMaxTokens;
103
- } else {
104
- // FUTURE: warn that reasoning.max_tokens was computed
105
- target["thinking"]["budgetTokens"] = calculateReasoningBudgetFromEffort(
106
- reasoning.effort,
107
- params.maxOutputTokens ?? getMaxOutputTokens(modelId),
108
- 1024,
109
- );
110
- }
111
- }
112
- } else if (clampedMaxTokens) {
113
- target["thinking"] = {
114
- type: "enabled",
115
- budgetTokens: clampedMaxTokens,
116
- };
117
- } else {
118
- target["thinking"] = { type: "enabled" };
119
- }
120
-
121
- delete unknown["reasoning"];
122
-
123
- return params;
124
- },
125
- };
126
-
127
- // https://platform.claude.com/docs/en/build-with-claude/prompt-caching
128
- export const claudePromptCachingMiddleware: LanguageModelMiddleware = {
129
- specificationVersion: "v3",
130
- // oxlint-disable-next-line require-await
131
- transformParams: async ({ params }) => {
132
- const unknown = params.providerOptions?.["unknown"];
133
- if (!unknown) return params;
134
-
135
- const cacheControl = unknown["cache_control"] as ChatCompletionsCacheControl;
136
- if (cacheControl) {
137
- (params.providerOptions!["anthropic"] ??= {})["cacheControl"] = cacheControl;
138
- }
139
-
140
- delete unknown["cache_control"];
141
-
142
- return params;
143
- },
144
- };
145
-
146
- modelMiddlewareMatcher.useForModel(["anthropic/claude-*3*7*", "anthropic/claude-*4*"], {
147
- language: [claudeReasoningMiddleware, claudePromptCachingMiddleware],
148
- });
@@ -1,191 +0,0 @@
1
- import type { CanonicalProviderId } from "../../providers/types";
2
- import type { CanonicalModelId, CatalogModel } from "../types";
3
-
4
- import { presetFor, type DeepPartial } from "../../utils/preset";
5
-
6
- const CLAUDE_BASE = {
7
- modalities: {
8
- input: ["text", "image", "file"] as const,
9
- output: ["text"] as const,
10
- },
11
- capabilities: ["attachments", "tool_call", "structured_output", "temperature"],
12
- context: 200000,
13
- providers: ["anthropic", "bedrock", "vertex"] as const satisfies readonly CanonicalProviderId[],
14
- } satisfies DeepPartial<CatalogModel>;
15
-
16
- const CLAUDE_PDF_MODALITIES = {
17
- modalities: {
18
- input: ["text", "image", "pdf", "file"] as const,
19
- output: ["text"] as const,
20
- },
21
- } satisfies DeepPartial<CatalogModel>;
22
-
23
- export const claudeHaiku45 = presetFor<CanonicalModelId, CatalogModel>()(
24
- "anthropic/claude-haiku-4.5" as const,
25
- {
26
- ...CLAUDE_BASE,
27
- ...CLAUDE_PDF_MODALITIES,
28
- name: "Claude Haiku 4.5",
29
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
30
- created: "2025-10-01",
31
- knowledge: "2025-07",
32
- } satisfies DeepPartial<CatalogModel>,
33
- );
34
-
35
- export const claudeHaiku35 = presetFor<CanonicalModelId, CatalogModel>()(
36
- "anthropic/claude-haiku-3.5" as const,
37
- {
38
- ...CLAUDE_BASE,
39
- ...CLAUDE_PDF_MODALITIES,
40
- name: "Claude Haiku 3.5",
41
- created: "2024-10-22",
42
- knowledge: "2024-07",
43
- } satisfies DeepPartial<CatalogModel>,
44
- );
45
-
46
- export const claudeHaiku3 = presetFor<CanonicalModelId, CatalogModel>()(
47
- "anthropic/claude-haiku-3" as const,
48
- {
49
- ...CLAUDE_BASE,
50
- name: "Claude Haiku 3",
51
- created: "2024-03-07",
52
- knowledge: "2023-08",
53
- } satisfies DeepPartial<CatalogModel>,
54
- );
55
-
56
- export const claudeSonnet45 = presetFor<CanonicalModelId, CatalogModel>()(
57
- "anthropic/claude-sonnet-4.5" as const,
58
- {
59
- ...CLAUDE_BASE,
60
- ...CLAUDE_PDF_MODALITIES,
61
- name: "Claude Sonnet 4.5",
62
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
63
- created: "2025-09-29",
64
- knowledge: "2025-07",
65
- } satisfies DeepPartial<CatalogModel>,
66
- );
67
-
68
- export const claudeSonnet46 = presetFor<CanonicalModelId, CatalogModel>()(
69
- "anthropic/claude-sonnet-4.6" as const,
70
- {
71
- ...CLAUDE_BASE,
72
- ...CLAUDE_PDF_MODALITIES,
73
- name: "Claude Sonnet 4.6",
74
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
75
- created: "2026-02-17",
76
- knowledge: "2025-08",
77
- } satisfies DeepPartial<CatalogModel>,
78
- );
79
-
80
- export const claudeSonnet4 = presetFor<CanonicalModelId, CatalogModel>()(
81
- "anthropic/claude-sonnet-4" as const,
82
- {
83
- ...CLAUDE_BASE,
84
- ...CLAUDE_PDF_MODALITIES,
85
- name: "Claude Sonnet 4",
86
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
87
- created: "2025-05-14",
88
- knowledge: "2025-03",
89
- } satisfies DeepPartial<CatalogModel>,
90
- );
91
-
92
- export const claudeSonnet37 = presetFor<CanonicalModelId, CatalogModel>()(
93
- "anthropic/claude-sonnet-3.7" as const,
94
- {
95
- ...CLAUDE_BASE,
96
- ...CLAUDE_PDF_MODALITIES,
97
- name: "Claude Sonnet 3.7",
98
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
99
- created: "2025-02-19",
100
- knowledge: "2024-10",
101
- } satisfies DeepPartial<CatalogModel>,
102
- );
103
-
104
- export const claudeSonnet35 = presetFor<CanonicalModelId, CatalogModel>()(
105
- "anthropic/claude-sonnet-3.5" as const,
106
- {
107
- ...CLAUDE_BASE,
108
- ...CLAUDE_PDF_MODALITIES,
109
- name: "Claude Sonnet 3.5",
110
- created: "2024-10-22",
111
- knowledge: "2024-04",
112
- } satisfies DeepPartial<CatalogModel>,
113
- );
114
-
115
- export const claudeOpus45 = presetFor<CanonicalModelId, CatalogModel>()(
116
- "anthropic/claude-opus-4.5" as const,
117
- {
118
- ...CLAUDE_BASE,
119
- ...CLAUDE_PDF_MODALITIES,
120
- name: "Claude Opus 4.5",
121
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
122
- created: "2025-11-01",
123
- knowledge: "2025-05",
124
- } satisfies DeepPartial<CatalogModel>,
125
- );
126
-
127
- export const claudeOpus46 = presetFor<CanonicalModelId, CatalogModel>()(
128
- "anthropic/claude-opus-4.6" as const,
129
- {
130
- ...CLAUDE_BASE,
131
- ...CLAUDE_PDF_MODALITIES,
132
- name: "Claude Opus 4.6",
133
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
134
- created: "2026-02-05",
135
- knowledge: "2025-05",
136
- } satisfies DeepPartial<CatalogModel>,
137
- );
138
-
139
- export const claudeOpus41 = presetFor<CanonicalModelId, CatalogModel>()(
140
- "anthropic/claude-opus-4.1" as const,
141
- {
142
- ...CLAUDE_BASE,
143
- ...CLAUDE_PDF_MODALITIES,
144
- name: "Claude Opus 4.1",
145
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
146
- created: "2025-08-05",
147
- knowledge: "2025-03",
148
- } satisfies DeepPartial<CatalogModel>,
149
- );
150
-
151
- export const claudeOpus4 = presetFor<CanonicalModelId, CatalogModel>()(
152
- "anthropic/claude-opus-4" as const,
153
- {
154
- ...CLAUDE_BASE,
155
- ...CLAUDE_PDF_MODALITIES,
156
- name: "Claude Opus 4",
157
- capabilities: [...CLAUDE_BASE.capabilities, "reasoning"],
158
- created: "2025-05-14",
159
- knowledge: "2025-03",
160
- } satisfies DeepPartial<CatalogModel>,
161
- );
162
-
163
- const claudeAtomic = {
164
- "v4.6": [claudeSonnet46, claudeOpus46],
165
- "v4.5": [claudeHaiku45, claudeSonnet45, claudeOpus45],
166
- "v4.1": [claudeOpus41],
167
- v4: [claudeSonnet4, claudeOpus4],
168
- "v3.7": [claudeSonnet37],
169
- "v3.5": [claudeSonnet35, claudeHaiku35],
170
- v3: [claudeHaiku3],
171
- haiku: [claudeHaiku45, claudeHaiku35, claudeHaiku3],
172
- sonnet: [claudeSonnet46, claudeSonnet45, claudeSonnet4, claudeSonnet37, claudeSonnet35],
173
- opus: [claudeOpus46, claudeOpus45, claudeOpus41, claudeOpus4],
174
- } as const;
175
-
176
- const claudeGroups = {
177
- "v4.x": [
178
- ...claudeAtomic["v4.6"],
179
- ...claudeAtomic["v4.5"],
180
- ...claudeAtomic["v4.1"],
181
- ...claudeAtomic["v4"],
182
- ],
183
- "v3.x": [...claudeAtomic["v3.7"], ...claudeAtomic["v3.5"], ...claudeAtomic["v3"]],
184
- } as const;
185
-
186
- export const claude = {
187
- ...claudeAtomic,
188
- ...claudeGroups,
189
- latest: [...claudeAtomic["v4.6"]],
190
- all: Object.values(claudeAtomic).flat(),
191
- } as const;
@@ -1,13 +0,0 @@
1
- import type { ModelCatalog } from "./types";
2
-
3
- type ModelCatalogInput =
4
- | ModelCatalog
5
- | (() => ModelCatalog)
6
- | ModelCatalog[]
7
- | (() => ModelCatalog)[];
8
-
9
- export function defineModelCatalog(...inputs: ModelCatalogInput[]): ModelCatalog {
10
- const catalogs = inputs.flat().map((input) => (typeof input === "function" ? input() : input));
11
-
12
- return Object.assign({}, ...catalogs);
13
- }
@@ -1,2 +0,0 @@
1
- export * from "./middleware";
2
- export * from "./presets";
@@ -1,138 +0,0 @@
1
- import { MockLanguageModelV3 } from "ai/test";
2
- import { expect, test } from "bun:test";
3
-
4
- import { modelMiddlewareMatcher } from "../../middleware/matcher";
5
- import { CANONICAL_MODEL_IDS } from "../../models/types";
6
- import { cohereDimensionsMiddleware, cohereReasoningMiddleware } from "./middleware";
7
-
8
- test("cohere middleware > matching patterns", () => {
9
- const languageMatching = [
10
- "cohere/command-a-reasoning",
11
- ] satisfies (typeof CANONICAL_MODEL_IDS)[number][];
12
-
13
- const languageNonMatching = [
14
- "cohere/command-a",
15
- "cohere/command-r-plus",
16
- ] satisfies (typeof CANONICAL_MODEL_IDS)[number][];
17
-
18
- for (const id of languageMatching) {
19
- const middleware = modelMiddlewareMatcher.resolve({ kind: "text", modelId: id });
20
- expect(middleware).toContain(cohereReasoningMiddleware);
21
- }
22
-
23
- for (const id of languageNonMatching) {
24
- const middleware = modelMiddlewareMatcher.resolve({ kind: "text", modelId: id });
25
- expect(middleware).not.toContain(cohereReasoningMiddleware);
26
- }
27
-
28
- const embeddingMatching = [
29
- "cohere/embed-v4.0",
30
- "cohere/embed-english-v3.0",
31
- ] satisfies (typeof CANONICAL_MODEL_IDS)[number][];
32
-
33
- const embeddingNonMatching = [
34
- "cohere/command-a",
35
- ] satisfies (typeof CANONICAL_MODEL_IDS)[number][];
36
-
37
- for (const id of embeddingMatching) {
38
- const middleware = modelMiddlewareMatcher.resolve({ kind: "embedding", modelId: id });
39
- expect(middleware).toContain(cohereDimensionsMiddleware);
40
- }
41
-
42
- for (const id of embeddingNonMatching) {
43
- const middleware = modelMiddlewareMatcher.resolve({ kind: "embedding", modelId: id });
44
- expect(middleware).not.toContain(cohereDimensionsMiddleware);
45
- }
46
- });
47
-
48
- test("cohereReasoningMiddleware > should map effort to thinking budget", async () => {
49
- const params = {
50
- prompt: [],
51
- maxOutputTokens: 2000,
52
- providerOptions: {
53
- unknown: {
54
- reasoning: { enabled: true, effort: "medium" },
55
- },
56
- },
57
- };
58
-
59
- const result = await cohereReasoningMiddleware.transformParams!({
60
- type: "generate",
61
- params,
62
- model: new MockLanguageModelV3(),
63
- });
64
-
65
- expect(result).toEqual({
66
- prompt: [],
67
- maxOutputTokens: 2000,
68
- providerOptions: {
69
- cohere: {
70
- thinking: {
71
- type: "enabled",
72
- tokenBudget: 1024,
73
- },
74
- },
75
- unknown: {},
76
- },
77
- });
78
- });
79
-
80
- test("cohereReasoningMiddleware > should disable reasoning when requested", async () => {
81
- const params = {
82
- prompt: [],
83
- providerOptions: {
84
- unknown: {
85
- reasoning: { enabled: false },
86
- },
87
- },
88
- };
89
-
90
- const result = await cohereReasoningMiddleware.transformParams!({
91
- type: "generate",
92
- params,
93
- model: new MockLanguageModelV3(),
94
- });
95
-
96
- expect(result).toEqual({
97
- prompt: [],
98
- providerOptions: {
99
- cohere: {
100
- thinking: {
101
- type: "disabled",
102
- },
103
- },
104
- unknown: {},
105
- },
106
- });
107
- });
108
-
109
- test("cohereReasoningMiddleware > should default reasoning budget when enabled without effort", async () => {
110
- const params = {
111
- prompt: [],
112
- maxOutputTokens: 4000,
113
- providerOptions: {
114
- unknown: {
115
- reasoning: { enabled: true },
116
- },
117
- },
118
- };
119
-
120
- const result = await cohereReasoningMiddleware.transformParams!({
121
- type: "generate",
122
- params,
123
- model: new MockLanguageModelV3(),
124
- });
125
-
126
- expect(result).toEqual({
127
- prompt: [],
128
- maxOutputTokens: 4000,
129
- providerOptions: {
130
- cohere: {
131
- thinking: {
132
- type: "enabled",
133
- },
134
- },
135
- unknown: {},
136
- },
137
- });
138
- });
@@ -1,76 +0,0 @@
1
- import type { EmbeddingModelMiddleware, LanguageModelMiddleware } from "ai";
2
-
3
- import type { ChatCompletionsReasoningConfig } from "../../endpoints/chat-completions/schema";
4
-
5
- import { modelMiddlewareMatcher } from "../../middleware/matcher";
6
- import { calculateReasoningBudgetFromEffort } from "../../middleware/utils";
7
-
8
- // Convert `dimensions` (OpenAI) to `outputDimension` (Cohere)
9
- export const cohereDimensionsMiddleware: EmbeddingModelMiddleware = {
10
- specificationVersion: "v3",
11
- // oxlint-disable-next-line require-await
12
- transformParams: async ({ params, model }) => {
13
- const unknown = params.providerOptions?.["unknown"];
14
- if (!unknown) return params;
15
-
16
- const modelId = model.modelId;
17
- if (
18
- modelId.includes("cohere/embed-english-light") ||
19
- modelId.includes("cohere/embed-multilingual-light")
20
- ) {
21
- delete unknown["dimensions"];
22
- return params;
23
- }
24
-
25
- const dimensions = unknown["dimensions"] as number;
26
- if (!dimensions) return params;
27
-
28
- (params.providerOptions!["cohere"] ??= {})["outputDimension"] = dimensions;
29
- delete unknown["dimensions"];
30
-
31
- return params;
32
- },
33
- };
34
-
35
- const COHERE_MAX_OUTPUT_TOKENS = 32000;
36
- export const cohereReasoningMiddleware: LanguageModelMiddleware = {
37
- specificationVersion: "v3",
38
- // oxlint-disable-next-line require-await
39
- transformParams: async ({ params }) => {
40
- const unknown = params.providerOptions?.["unknown"];
41
- if (!unknown) return params;
42
-
43
- const reasoning = unknown["reasoning"] as ChatCompletionsReasoningConfig;
44
- if (!reasoning) return params;
45
-
46
- const target = (params.providerOptions!["cohere"] ??= {});
47
-
48
- if (!reasoning.enabled) {
49
- target["thinking"] = { type: "disabled" };
50
- } else if (reasoning.max_tokens) {
51
- target["thinking"] = { type: "enabled", tokenBudget: reasoning.max_tokens };
52
- } else if (reasoning.effort) {
53
- // FUTURE: warn that reasoning.max_tokens was computed
54
- target["thinking"] = {
55
- type: "enabled",
56
- tokenBudget: calculateReasoningBudgetFromEffort(
57
- reasoning.effort,
58
- params.maxOutputTokens ?? COHERE_MAX_OUTPUT_TOKENS,
59
- 1024,
60
- ),
61
- };
62
- } else {
63
- target["thinking"] = { type: "enabled" };
64
- }
65
-
66
- delete unknown["reasoning"];
67
-
68
- return params;
69
- },
70
- };
71
-
72
- modelMiddlewareMatcher.useForModel("cohere/embed-*", { embedding: [cohereDimensionsMiddleware] });
73
-
74
- modelMiddlewareMatcher.useForModel("cohere/command-a-reasoning", {
75
- language: [cohereReasoningMiddleware],
76
- });