@hebo-ai/gateway 0.11.3 → 0.11.5

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.
@@ -5,26 +5,26 @@ export const ChatCompletionsContentPartTextSchema = z.object({
5
5
  type: z.literal("text"),
6
6
  text: z.string(),
7
7
  // Extension origin: Anthropic/OpenRouter/Vercel
8
- cache_control: ChatCompletionsCacheControlSchema.optional().meta({ extension: true }),
8
+ cache_control: ChatCompletionsCacheControlSchema.nullish().meta({ extension: true }),
9
9
  });
10
10
  export const ChatCompletionsContentPartImageSchema = z.object({
11
11
  type: z.literal("image_url"),
12
12
  image_url: z.object({
13
13
  url: z.string(),
14
- detail: z.enum(["low", "high", "auto"]).optional(),
14
+ detail: z.enum(["low", "high", "auto"]).nullish(),
15
15
  }),
16
16
  // Extension origin: OpenRouter/Vercel/Anthropic
17
- cache_control: ChatCompletionsCacheControlSchema.optional().meta({ extension: true }),
17
+ cache_control: ChatCompletionsCacheControlSchema.nullish().meta({ extension: true }),
18
18
  });
19
19
  export const ChatCompletionsContentPartFileSchema = z.object({
20
20
  type: z.literal("file"),
21
21
  file: z.object({
22
22
  data: z.string(),
23
23
  media_type: z.string(),
24
- filename: z.string().optional(),
24
+ filename: z.string().nullish(),
25
25
  }),
26
26
  // Extension origin: OpenRouter/Vercel/Anthropic
27
- cache_control: ChatCompletionsCacheControlSchema.optional().meta({ extension: true }),
27
+ cache_control: ChatCompletionsCacheControlSchema.nullish().meta({ extension: true }),
28
28
  });
29
29
  export const ChatCompletionsContentPartSchema = z.discriminatedUnion("type", [
30
30
  ChatCompletionsContentPartTextSchema,
@@ -40,51 +40,51 @@ export const ChatCompletionsToolCallSchema = z.object({
40
40
  name: z.string(),
41
41
  }),
42
42
  // Extension origin: Gemini
43
- extra_content: ChatCompletionsProviderMetadataSchema.optional().meta({ extension: true }),
43
+ extra_content: ChatCompletionsProviderMetadataSchema.nullish().meta({ extension: true }),
44
44
  });
45
45
  export const ChatCompletionsSystemMessageSchema = z.object({
46
46
  role: z.literal("system"),
47
47
  content: z.union([z.string(), z.array(ChatCompletionsContentPartTextSchema)]),
48
- name: z.string().optional(),
48
+ name: z.string().nullish(),
49
49
  // Extension origin: OpenRouter/Vercel/Anthropic
50
- cache_control: ChatCompletionsCacheControlSchema.optional().meta({ extension: true }),
50
+ cache_control: ChatCompletionsCacheControlSchema.nullish().meta({ extension: true }),
51
51
  });
52
52
  export const ChatCompletionsUserMessageSchema = z.object({
53
53
  role: z.literal("user"),
54
54
  content: z.union([z.string(), z.array(ChatCompletionsContentPartSchema)]),
55
- name: z.string().optional(),
55
+ name: z.string().nullish(),
56
56
  // Extension origin: OpenRouter/Vercel/Anthropic
57
- cache_control: ChatCompletionsCacheControlSchema.optional().meta({ extension: true }),
57
+ cache_control: ChatCompletionsCacheControlSchema.nullish().meta({ extension: true }),
58
58
  });
59
59
  export const ChatCompletionsReasoningDetailSchema = z.object({
60
- id: z.string().optional(),
60
+ id: z.string().nullish(),
61
61
  index: z.int().nonnegative(),
62
62
  type: z.string(),
63
- text: z.string().optional(),
64
- signature: z.string().optional(),
65
- data: z.string().optional(),
66
- summary: z.string().optional(),
67
- format: z.string().optional(),
63
+ text: z.string().nullish(),
64
+ signature: z.string().nullish(),
65
+ data: z.string().nullish(),
66
+ summary: z.string().nullish(),
67
+ format: z.string().nullish(),
68
68
  });
69
69
  export const ChatCompletionsAssistantMessageSchema = z.object({
70
70
  role: z.literal("assistant"),
71
71
  content: z
72
72
  .union([z.string(), z.null(), z.array(ChatCompletionsContentPartTextSchema)])
73
73
  .optional(),
74
- name: z.string().optional(),
74
+ name: z.string().nullish(),
75
75
  // FUTURE: This should also support Custom Tool Calls
76
- tool_calls: z.array(ChatCompletionsToolCallSchema).optional(),
76
+ tool_calls: z.array(ChatCompletionsToolCallSchema).nullish(),
77
77
  // Extension origin: OpenRouter/Vercel
78
- reasoning: z.string().optional().meta({ extension: true }),
78
+ reasoning: z.string().nullish().meta({ extension: true }),
79
79
  // Extension origin: OpenRouter/Vercel
80
80
  reasoning_details: z
81
81
  .array(ChatCompletionsReasoningDetailSchema)
82
- .optional()
82
+ .nullish()
83
83
  .meta({ extension: true }),
84
84
  // Extension origin: Gemini
85
- extra_content: ChatCompletionsProviderMetadataSchema.optional().meta({ extension: true }),
85
+ extra_content: ChatCompletionsProviderMetadataSchema.nullish().meta({ extension: true }),
86
86
  // Extension origin: OpenRouter/Vercel/Anthropic
87
- cache_control: ChatCompletionsCacheControlSchema.optional().meta({ extension: true }),
87
+ cache_control: ChatCompletionsCacheControlSchema.nullish().meta({ extension: true }),
88
88
  });
89
89
  export const ChatCompletionsToolMessageSchema = z.object({
90
90
  role: z.literal("tool"),
@@ -97,7 +97,7 @@ export const ChatCompletionsMessageSchema = z.discriminatedUnion("role", [
97
97
  ChatCompletionsAssistantMessageSchema,
98
98
  ChatCompletionsToolMessageSchema,
99
99
  ]);
100
- export const ChatCompletionsToolSchema = z.object({
100
+ export const ChatCompletionsFunctionToolSchema = z.object({
101
101
  type: z.literal("function"),
102
102
  function: z.object({
103
103
  name: z.string(),
@@ -107,6 +107,21 @@ export const ChatCompletionsToolSchema = z.object({
107
107
  }),
108
108
  // FUTURE: cache_control support on tools
109
109
  });
110
+ // Hosted/built-in tools (e.g. web_search, file_search, code_interpreter).
111
+ // Accepted at the edge so clients like Codex can send their default payload,
112
+ // but not executed by the gateway — they are dropped before the AI SDK call
113
+ // unless the downstream handler explicitly opts them in.
114
+ export const ChatCompletionsHostedToolSchema = z
115
+ .looseObject({
116
+ type: z.string(),
117
+ })
118
+ .refine((value) => value.type !== "function", {
119
+ message: 'Function tools must use the function tool schema (type: "function")',
120
+ });
121
+ export const ChatCompletionsToolSchema = z.union([
122
+ ChatCompletionsFunctionToolSchema,
123
+ ChatCompletionsHostedToolSchema,
124
+ ]);
110
125
  const ChatCompletionsNamedFunctionToolChoiceSchema = z.object({
111
126
  type: z.literal("function"),
112
127
  function: z.object({
@@ -161,7 +176,7 @@ const ChatCompletionsInputsSchema = z.object({
161
176
  top_p: z.number().min(0).max(1.0).optional(),
162
177
  metadata: ChatCompletionsMetadataSchema.optional(),
163
178
  response_format: ChatCompletionsResponseFormatSchema.optional(),
164
- reasoning_effort: ChatCompletionsReasoningEffortSchema.optional(),
179
+ reasoning_effort: ChatCompletionsReasoningEffortSchema.nullish(),
165
180
  service_tier: ChatCompletionsServiceTierSchema.optional(),
166
181
  prompt_cache_key: z.string().optional(),
167
182
  prompt_cache_retention: z.enum(["in-memory", "24h"]).optional(),
@@ -173,7 +188,7 @@ const ChatCompletionsInputsSchema = z.object({
173
188
  // Extension origin: OpenRouter/Vercel/Anthropic
174
189
  cache_control: ChatCompletionsCacheControlSchema.optional().meta({ extension: true }),
175
190
  // Extension origin: OpenRouter
176
- reasoning: ChatCompletionsReasoningConfigSchema.optional().meta({ extension: true }),
191
+ reasoning: ChatCompletionsReasoningConfigSchema.nullish().meta({ extension: true }),
177
192
  // Extension origin: Gemini extra_body
178
193
  // https://docs.cloud.google.com/vertex-ai/generative-ai/docs/migrate/openai/overview#extra_body
179
194
  extra_body: ChatCompletionsProviderMetadataSchema.optional().meta({ extension: true }),
@@ -7,6 +7,13 @@ import { addSpanEvent } from "../../telemetry/span";
7
7
  import { parseRequestBody } from "../../utils/body";
8
8
  import { toConversation, toConversationItem, toConversationDeleted } from "./converters";
9
9
  import { ConversationCreateParamsSchema, ConversationItemsAddBodySchema, ConversationUpdateBodySchema, ConversationItemListParamsSchema, ConversationListParamsSchema, } from "./schema";
10
+ // Codex-shaped clients echo absent optional fields back as literal null; the
11
+ // request schema accepts that, but the storage layer uses `id?: string`, so
12
+ // normalize null → undefined at the boundary.
13
+ const toStorageItem = (item) => ({
14
+ ...item,
15
+ id: item.id ?? undefined,
16
+ });
10
17
  export const conversations = (config) => {
11
18
  const parsedConfig = parseConfig(config);
12
19
  const storage = parsedConfig.storage;
@@ -52,7 +59,7 @@ export const conversations = (config) => {
52
59
  addSpanEvent("hebo.request.parsed");
53
60
  const entity = await storage.createConversation({
54
61
  metadata: parsed.data.metadata,
55
- items: parsed.data.items,
62
+ items: parsed.data.items?.map(toStorageItem),
56
63
  });
57
64
  logger.debug(`[conversations] created conversation: ${entity.id}`);
58
65
  logger.trace({ requestId: ctx.requestId, entity }, "[storage] createConversation result");
@@ -146,7 +153,7 @@ export const conversations = (config) => {
146
153
  throw new GatewayError(z.prettifyError(parsed.error), 400, undefined, parsed.error);
147
154
  }
148
155
  addSpanEvent("hebo.request.parsed");
149
- const entities = await storage.addItems(conversationId, parsed.data.items);
156
+ const entities = await storage.addItems(conversationId, parsed.data.items.map(toStorageItem));
150
157
  if (!entities) {
151
158
  throw new GatewayError("Conversation not found", 404);
152
159
  }