@hebo-ai/gateway 0.9.2 → 0.9.3

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 (213) hide show
  1. package/dist/config.d.ts +2 -0
  2. package/dist/config.js +125 -0
  3. package/dist/endpoints/chat-completions/converters.d.ts +26 -0
  4. package/dist/endpoints/chat-completions/converters.js +525 -0
  5. package/dist/endpoints/chat-completions/handler.d.ts +2 -0
  6. package/dist/endpoints/chat-completions/handler.js +152 -0
  7. package/dist/endpoints/chat-completions/index.d.ts +4 -0
  8. package/dist/endpoints/chat-completions/index.js +4 -0
  9. package/dist/endpoints/chat-completions/otel.d.ts +5 -0
  10. package/dist/endpoints/chat-completions/otel.js +178 -0
  11. package/dist/endpoints/chat-completions/schema.d.ts +1170 -0
  12. package/dist/endpoints/chat-completions/schema.js +252 -0
  13. package/dist/endpoints/conversations/converters.d.ts +8 -0
  14. package/dist/endpoints/conversations/converters.js +29 -0
  15. package/dist/endpoints/conversations/handler.d.ts +2 -0
  16. package/dist/endpoints/conversations/handler.js +259 -0
  17. package/dist/endpoints/conversations/index.d.ts +3 -0
  18. package/dist/endpoints/conversations/index.js +3 -0
  19. package/dist/endpoints/conversations/schema.d.ts +1511 -0
  20. package/dist/endpoints/conversations/schema.js +74 -0
  21. package/dist/endpoints/conversations/storage/dialects/greptime.d.ts +10 -0
  22. package/dist/endpoints/conversations/storage/dialects/greptime.js +87 -0
  23. package/dist/endpoints/conversations/storage/dialects/mysql.d.ts +12 -0
  24. package/dist/endpoints/conversations/storage/dialects/mysql.js +118 -0
  25. package/dist/endpoints/conversations/storage/dialects/postgres.d.ts +16 -0
  26. package/dist/endpoints/conversations/storage/dialects/postgres.js +185 -0
  27. package/dist/endpoints/conversations/storage/dialects/sqlite.d.ts +11 -0
  28. package/dist/endpoints/conversations/storage/dialects/sqlite.js +176 -0
  29. package/dist/endpoints/conversations/storage/dialects/types.d.ts +42 -0
  30. package/dist/endpoints/conversations/storage/dialects/types.js +0 -0
  31. package/dist/endpoints/conversations/storage/dialects/utils.d.ts +25 -0
  32. package/dist/endpoints/conversations/storage/dialects/utils.js +80 -0
  33. package/dist/endpoints/conversations/storage/memory.d.ts +25 -0
  34. package/dist/endpoints/conversations/storage/memory.js +200 -0
  35. package/dist/endpoints/conversations/storage/sql.d.ts +33 -0
  36. package/dist/endpoints/conversations/storage/sql.js +276 -0
  37. package/dist/endpoints/conversations/storage/types.d.ts +39 -0
  38. package/dist/endpoints/conversations/storage/types.js +0 -0
  39. package/dist/endpoints/embeddings/converters.d.ts +10 -0
  40. package/dist/endpoints/embeddings/converters.js +31 -0
  41. package/dist/endpoints/embeddings/handler.d.ts +2 -0
  42. package/dist/endpoints/embeddings/handler.js +99 -0
  43. package/dist/endpoints/embeddings/index.d.ts +4 -0
  44. package/dist/endpoints/embeddings/index.js +4 -0
  45. package/dist/endpoints/embeddings/otel.d.ts +5 -0
  46. package/dist/endpoints/embeddings/otel.js +29 -0
  47. package/dist/endpoints/embeddings/schema.d.ts +44 -0
  48. package/dist/endpoints/embeddings/schema.js +29 -0
  49. package/dist/endpoints/models/converters.d.ts +6 -0
  50. package/dist/endpoints/models/converters.js +42 -0
  51. package/dist/endpoints/models/handler.d.ts +2 -0
  52. package/dist/endpoints/models/handler.js +29 -0
  53. package/dist/endpoints/models/index.d.ts +3 -0
  54. package/dist/endpoints/models/index.js +3 -0
  55. package/dist/endpoints/models/schema.d.ts +42 -0
  56. package/dist/endpoints/models/schema.js +31 -0
  57. package/dist/endpoints/responses/converters.d.ts +17 -0
  58. package/dist/endpoints/responses/converters.js +1037 -0
  59. package/dist/endpoints/responses/handler.d.ts +2 -0
  60. package/dist/endpoints/responses/handler.js +141 -0
  61. package/dist/endpoints/responses/index.d.ts +4 -0
  62. package/dist/endpoints/responses/index.js +4 -0
  63. package/dist/endpoints/responses/otel.d.ts +6 -0
  64. package/dist/endpoints/responses/otel.js +226 -0
  65. package/dist/endpoints/responses/schema.d.ts +2109 -0
  66. package/dist/endpoints/responses/schema.js +314 -0
  67. package/dist/endpoints/shared/converters.d.ts +56 -0
  68. package/dist/endpoints/shared/converters.js +180 -0
  69. package/dist/endpoints/shared/schema.d.ts +70 -0
  70. package/dist/endpoints/shared/schema.js +46 -0
  71. package/dist/errors/ai-sdk.d.ts +2 -0
  72. package/dist/errors/ai-sdk.js +52 -0
  73. package/dist/errors/gateway.d.ts +5 -0
  74. package/dist/errors/gateway.js +13 -0
  75. package/dist/errors/openai.d.ts +15 -0
  76. package/dist/errors/openai.js +40 -0
  77. package/dist/errors/utils.d.ts +24 -0
  78. package/dist/errors/utils.js +46 -0
  79. package/dist/gateway.d.ts +11 -0
  80. package/dist/gateway.js +44 -0
  81. package/dist/index.d.ts +11 -0
  82. package/dist/index.js +10 -0
  83. package/dist/lifecycle.d.ts +3 -0
  84. package/dist/lifecycle.js +114 -0
  85. package/dist/logger/default.d.ts +4 -0
  86. package/dist/logger/default.js +81 -0
  87. package/dist/logger/index.d.ts +11 -0
  88. package/dist/logger/index.js +25 -0
  89. package/dist/middleware/common.d.ts +12 -0
  90. package/dist/middleware/common.js +146 -0
  91. package/dist/middleware/debug.d.ts +3 -0
  92. package/dist/middleware/debug.js +27 -0
  93. package/dist/middleware/matcher.d.ts +28 -0
  94. package/dist/middleware/matcher.js +118 -0
  95. package/dist/middleware/utils.d.ts +2 -0
  96. package/dist/middleware/utils.js +24 -0
  97. package/dist/models/amazon/index.d.ts +2 -0
  98. package/dist/models/amazon/index.js +2 -0
  99. package/dist/models/amazon/middleware.d.ts +3 -0
  100. package/dist/models/amazon/middleware.js +69 -0
  101. package/dist/models/amazon/presets.d.ts +345 -0
  102. package/dist/models/amazon/presets.js +80 -0
  103. package/dist/models/anthropic/index.d.ts +2 -0
  104. package/dist/models/anthropic/index.js +2 -0
  105. package/dist/models/anthropic/middleware.d.ts +5 -0
  106. package/dist/models/anthropic/middleware.js +128 -0
  107. package/dist/models/anthropic/presets.d.ts +711 -0
  108. package/dist/models/anthropic/presets.js +140 -0
  109. package/dist/models/catalog.d.ts +4 -0
  110. package/dist/models/catalog.js +8 -0
  111. package/dist/models/cohere/index.d.ts +2 -0
  112. package/dist/models/cohere/index.js +2 -0
  113. package/dist/models/cohere/middleware.d.ts +3 -0
  114. package/dist/models/cohere/middleware.js +62 -0
  115. package/dist/models/cohere/presets.d.ts +411 -0
  116. package/dist/models/cohere/presets.js +134 -0
  117. package/dist/models/google/index.d.ts +2 -0
  118. package/dist/models/google/index.js +2 -0
  119. package/dist/models/google/middleware.d.ts +8 -0
  120. package/dist/models/google/middleware.js +118 -0
  121. package/dist/models/google/presets.d.ts +815 -0
  122. package/dist/models/google/presets.js +184 -0
  123. package/dist/models/meta/index.d.ts +1 -0
  124. package/dist/models/meta/index.js +1 -0
  125. package/dist/models/meta/presets.d.ts +483 -0
  126. package/dist/models/meta/presets.js +105 -0
  127. package/dist/models/openai/index.d.ts +2 -0
  128. package/dist/models/openai/index.js +2 -0
  129. package/dist/models/openai/middleware.d.ts +4 -0
  130. package/dist/models/openai/middleware.js +89 -0
  131. package/dist/models/openai/presets.d.ts +1319 -0
  132. package/dist/models/openai/presets.js +277 -0
  133. package/dist/models/types.d.ts +20 -0
  134. package/dist/models/types.js +100 -0
  135. package/dist/models/voyage/index.d.ts +2 -0
  136. package/dist/models/voyage/index.js +2 -0
  137. package/dist/models/voyage/middleware.d.ts +2 -0
  138. package/dist/models/voyage/middleware.js +19 -0
  139. package/dist/models/voyage/presets.d.ts +436 -0
  140. package/dist/models/voyage/presets.js +85 -0
  141. package/dist/providers/anthropic/canonical.d.ts +3 -0
  142. package/dist/providers/anthropic/canonical.js +9 -0
  143. package/dist/providers/anthropic/index.d.ts +1 -0
  144. package/dist/providers/anthropic/index.js +1 -0
  145. package/dist/providers/bedrock/canonical.d.ts +17 -0
  146. package/dist/providers/bedrock/canonical.js +64 -0
  147. package/dist/providers/bedrock/index.d.ts +2 -0
  148. package/dist/providers/bedrock/index.js +2 -0
  149. package/dist/providers/bedrock/middleware.d.ts +5 -0
  150. package/dist/providers/bedrock/middleware.js +133 -0
  151. package/dist/providers/cohere/canonical.d.ts +3 -0
  152. package/dist/providers/cohere/canonical.js +17 -0
  153. package/dist/providers/cohere/index.d.ts +1 -0
  154. package/dist/providers/cohere/index.js +1 -0
  155. package/dist/providers/groq/canonical.d.ts +3 -0
  156. package/dist/providers/groq/canonical.js +12 -0
  157. package/dist/providers/groq/index.d.ts +2 -0
  158. package/dist/providers/groq/index.js +2 -0
  159. package/dist/providers/groq/middleware.d.ts +2 -0
  160. package/dist/providers/groq/middleware.js +30 -0
  161. package/dist/providers/openai/canonical.d.ts +3 -0
  162. package/dist/providers/openai/canonical.js +8 -0
  163. package/dist/providers/openai/index.d.ts +1 -0
  164. package/dist/providers/openai/index.js +1 -0
  165. package/dist/providers/registry.d.ts +24 -0
  166. package/dist/providers/registry.js +103 -0
  167. package/dist/providers/types.d.ts +7 -0
  168. package/dist/providers/types.js +11 -0
  169. package/dist/providers/vertex/canonical.d.ts +3 -0
  170. package/dist/providers/vertex/canonical.js +8 -0
  171. package/dist/providers/vertex/index.d.ts +2 -0
  172. package/dist/providers/vertex/index.js +2 -0
  173. package/dist/providers/vertex/middleware.d.ts +2 -0
  174. package/dist/providers/vertex/middleware.js +47 -0
  175. package/dist/providers/voyage/canonical.d.ts +3 -0
  176. package/dist/providers/voyage/canonical.js +7 -0
  177. package/dist/providers/voyage/index.d.ts +1 -0
  178. package/dist/providers/voyage/index.js +1 -0
  179. package/dist/telemetry/ai-sdk.d.ts +2 -0
  180. package/dist/telemetry/ai-sdk.js +31 -0
  181. package/dist/telemetry/baggage.d.ts +1 -0
  182. package/dist/telemetry/baggage.js +24 -0
  183. package/dist/telemetry/fetch.d.ts +2 -0
  184. package/dist/telemetry/fetch.js +49 -0
  185. package/dist/telemetry/gen-ai.d.ts +7 -0
  186. package/dist/telemetry/gen-ai.js +108 -0
  187. package/dist/telemetry/http.d.ts +3 -0
  188. package/dist/telemetry/http.js +54 -0
  189. package/dist/telemetry/index.d.ts +1 -0
  190. package/dist/telemetry/index.js +1 -0
  191. package/dist/telemetry/memory.d.ts +2 -0
  192. package/dist/telemetry/memory.js +43 -0
  193. package/dist/telemetry/span.d.ts +13 -0
  194. package/dist/telemetry/span.js +60 -0
  195. package/dist/types.d.ts +231 -0
  196. package/dist/types.js +2 -0
  197. package/dist/utils/body.d.ts +19 -0
  198. package/dist/utils/body.js +99 -0
  199. package/dist/utils/env.d.ts +2 -0
  200. package/dist/utils/env.js +7 -0
  201. package/dist/utils/headers.d.ts +4 -0
  202. package/dist/utils/headers.js +22 -0
  203. package/dist/utils/preset.d.ts +10 -0
  204. package/dist/utils/preset.js +41 -0
  205. package/dist/utils/request.d.ts +2 -0
  206. package/dist/utils/request.js +43 -0
  207. package/dist/utils/response.d.ts +6 -0
  208. package/dist/utils/response.js +55 -0
  209. package/dist/utils/stream.d.ts +9 -0
  210. package/dist/utils/stream.js +100 -0
  211. package/dist/utils/url.d.ts +4 -0
  212. package/dist/utils/url.js +21 -0
  213. package/package.json +1 -1
@@ -0,0 +1,276 @@
1
+ import { v4 as uuidv4, v7 as uuidv7 } from "uuid";
2
+ import { createRowMapper, mergeData, parseJson, toMilliseconds } from "./dialects/utils";
3
+ const rowMapper = createRowMapper([
4
+ parseJson("data"),
5
+ parseJson("metadata"),
6
+ toMilliseconds("created_at"),
7
+ mergeData("data"),
8
+ ]);
9
+ export class SqlStorage {
10
+ dialect;
11
+ constructor(options) {
12
+ if ("executor" in options) {
13
+ this.dialect = options;
14
+ }
15
+ else {
16
+ this.dialect = options.dialect;
17
+ }
18
+ }
19
+ get executor() {
20
+ return this.dialect.executor;
21
+ }
22
+ get config() {
23
+ return this.dialect.config;
24
+ }
25
+ async migrate() {
26
+ const { types, quote: q, supportCreateIndexIfNotExists } = this.config;
27
+ const isTimeIndex = types.index === "TIME";
28
+ const varchar = (len) => types.varchar === "TEXT" ? "TEXT" : `${types.varchar}(${len})`;
29
+ const timeIndex = isTimeIndex ? `, TIME INDEX (${q("created_at")})` : "";
30
+ const withClause = isTimeIndex ? ` WITH ('merge_mode'='last_non_null')` : "";
31
+ const partition = (cols) => this.config.partitionClause
32
+ ? ` ${this.config.partitionClause(cols.map((col) => q(col)))}`
33
+ : "";
34
+ const createIndex = async (table, name, cols, seq = false) => {
35
+ const isBrin = types.index === "BRIN";
36
+ const using = seq && types.index !== "B-TREE" ? `USING ${types.index}` : "";
37
+ const ifNotExists = supportCreateIndexIfNotExists ? "IF NOT EXISTS" : "";
38
+ const formattedCols = cols
39
+ .map((c) => {
40
+ const parts = c.split(" ");
41
+ const col = parts[0];
42
+ const dir = parts[1];
43
+ // BRIN doesn't support ASC/DESC
44
+ const effectiveDir = isBrin ? "" : dir;
45
+ return effectiveDir ? `${q(col)} ${effectiveDir}` : q(col);
46
+ })
47
+ .join(", ");
48
+ try {
49
+ await this.executor.run(`CREATE INDEX ${ifNotExists} ${q(name)} ON ${q(table)} ${using} (${formattedCols})`, []);
50
+ }
51
+ catch (err) {
52
+ if (!supportCreateIndexIfNotExists &&
53
+ err instanceof Error &&
54
+ err.message?.includes("Duplicate key name")) {
55
+ return;
56
+ }
57
+ throw err;
58
+ }
59
+ };
60
+ await this.executor.run(`
61
+ CREATE TABLE IF NOT EXISTS ${q("conversations")} (
62
+ ${q("id")} ${varchar(255)},
63
+ ${q("created_at")} ${types.timestamp},
64
+ ${q("metadata")} ${types.json},
65
+ PRIMARY KEY (${q("id")})
66
+ ${timeIndex}
67
+ )${partition(["id"])}${withClause}
68
+ `, []);
69
+ await this.executor.run(`
70
+ CREATE TABLE IF NOT EXISTS ${q("conversation_items")} (
71
+ ${q("id")} ${varchar(255)}${isTimeIndex ? " SKIPPING INDEX" : ""},
72
+ ${q("conversation_id")} ${varchar(255)},
73
+ ${q("created_at")} ${types.timestamp},
74
+ ${q("type")} ${varchar(64)},
75
+ ${q("data")} ${types.json},
76
+ PRIMARY KEY (${q("conversation_id")}${isTimeIndex ? "" : `, ${q("id")}`})
77
+ ${timeIndex}
78
+ )${partition(["conversation_id"])}${withClause}
79
+ `, []);
80
+ if (!isTimeIndex) {
81
+ await createIndex("conversations", "idx_conversations_created_at", ["created_at DESC", "id DESC"], true);
82
+ await createIndex("conversation_items", "idx_items_conv_id", [
83
+ "conversation_id",
84
+ "created_at DESC",
85
+ "id DESC",
86
+ ]);
87
+ }
88
+ }
89
+ createConversation(params) {
90
+ const { placeholder: p, quote: q } = this.config;
91
+ const isGreptime = this.config.types.index === "TIME";
92
+ const id = isGreptime ? uuidv4() : uuidv7();
93
+ const metadata = params.metadata ?? null;
94
+ const now = new Date();
95
+ return this.executor.transaction(async (tx) => {
96
+ await tx.run(`INSERT INTO ${q("conversations")} (${q("id")}, ${q("metadata")}, ${q("created_at")}) ` +
97
+ `VALUES (${p(0)}, ${p(1)}, ${p(2)})`, [id, metadata, now]);
98
+ const conversation = {
99
+ id,
100
+ created_at: now.getTime(),
101
+ metadata,
102
+ };
103
+ if (params.items?.length) {
104
+ await this.addItemsInternal(id, params.items, true, tx);
105
+ }
106
+ return conversation;
107
+ });
108
+ }
109
+ getConversation(id) {
110
+ return this.getConversationInternal(id, this.executor);
111
+ }
112
+ async getConversationInternal(id, executor) {
113
+ const { placeholder: p, quote: q, selectJson: sj } = this.config;
114
+ const row = await executor.get(`SELECT ${q("id")}, ${q("created_at")}, ${sj(q("metadata"))} as ${q("metadata")} FROM ${q("conversations")} WHERE ${q("id")} = ${p(0)} ORDER BY ${q("created_at")} DESC LIMIT 1`, [id]);
115
+ return row ? rowMapper(row) : undefined;
116
+ }
117
+ async listConversations(params) {
118
+ const { after, order, limit, metadata } = params;
119
+ const { placeholder: p, quote: q, selectJson: sj, limitAsLiteral } = this.config;
120
+ const isAsc = order === "asc";
121
+ const dir = isAsc ? "ASC" : "DESC";
122
+ const sqlParts = [
123
+ `SELECT c.${q("id")}, c.${q("created_at")}, ${sj(`c.${q("metadata")}`)} as ${q("metadata")} FROM ${q("conversations")} c WHERE 1=1`,
124
+ ];
125
+ const args = [];
126
+ let nextIdx = 0;
127
+ // Filter by metadata
128
+ if (metadata && Object.keys(metadata).length > 0) {
129
+ for (const [key, value] of Object.entries(metadata)) {
130
+ const extractExpr = this.config.jsonExtract(`c.${q("metadata")}`, key);
131
+ sqlParts.push(`AND ${extractExpr} = ${p(nextIdx++)}`);
132
+ args.push(value);
133
+ }
134
+ }
135
+ if (after) {
136
+ const op = isAsc ? ">" : "<";
137
+ sqlParts.push(`AND EXISTS (SELECT 1 FROM ${q("conversations")} _cursor WHERE _cursor.${q("id")} = ${p(nextIdx++)} AND (c.${q("created_at")} ${op} _cursor.${q("created_at")} OR (c.${q("created_at")} = _cursor.${q("created_at")} AND c.${q("id")} ${op} _cursor.${q("id")})))`);
138
+ args.push(after);
139
+ }
140
+ sqlParts.push(`ORDER BY c.${q("created_at")} ${dir}, c.${q("id")} ${dir}`);
141
+ if (!Number.isNaN(limit)) {
142
+ if (limitAsLiteral) {
143
+ sqlParts.push(`LIMIT ${limit}`);
144
+ }
145
+ else {
146
+ sqlParts.push(`LIMIT ${p(nextIdx++)}`);
147
+ args.push(limit);
148
+ }
149
+ }
150
+ const query = sqlParts.join(" ");
151
+ const rows = await this.executor.all(query, args);
152
+ for (let i = 0; i < rows.length; i++) {
153
+ rowMapper(rows[i]);
154
+ }
155
+ return rows;
156
+ }
157
+ updateConversation(id, metadata) {
158
+ const { placeholder: p, quote: q, upsertSuffix } = this.config;
159
+ return this.executor.transaction(async (tx) => {
160
+ // Unified approach: Fetch original created_at to verify existence and preserve it.
161
+ // 1. Existence check: Ensure the conversation exists before updating (returning
162
+ // undefined if missing). This prevents clients from accidentally creating
163
+ // "zombie" conversations with custom IDs.
164
+ // 2. Consistency: Standard SQL (Postgres/MySQL/SQLite) preserves the original
165
+ // creation timestamp.
166
+ // 3. Deduplication: GreptimeDB requires the EXACT same Time Index (created_at)
167
+ // to deduplicate the row.
168
+ const conversation = await this.getConversationInternal(id, tx);
169
+ if (!conversation)
170
+ return conversation;
171
+ const createdAt = conversation.created_at;
172
+ const pk = ["id"];
173
+ const updateCols = ["metadata"];
174
+ const suffix = upsertSuffix?.(q, pk, updateCols) ?? "";
175
+ await tx.run(`INSERT INTO ${q("conversations")} (${q("id")}, ${q("metadata")}, ${q("created_at")}) ` +
176
+ `VALUES (${p(0)}, ${p(1)}, ${p(2)}) ${suffix}`, [id, metadata ?? null, new Date(createdAt)]);
177
+ return {
178
+ id,
179
+ created_at: createdAt,
180
+ metadata: metadata ?? null,
181
+ };
182
+ });
183
+ }
184
+ async deleteConversation(id) {
185
+ const { placeholder: p, quote: q } = this.config;
186
+ const { changes } = await this.executor.run(`DELETE FROM ${q("conversations")} WHERE ${q("id")} = ${p(0)}`, [id]);
187
+ return { id, deleted: changes > 0 };
188
+ }
189
+ addItems(conversationId, items) {
190
+ return this.addItemsInternal(conversationId, items, false);
191
+ }
192
+ addItemsInternal(conversationId, items, skipCheck = false, executor = this.executor) {
193
+ return executor.transaction(async (tx) => {
194
+ if (!skipCheck) {
195
+ const conversation = await this.getConversationInternal(conversationId, tx);
196
+ if (!conversation)
197
+ return conversation;
198
+ }
199
+ const { placeholder: p, quote: q } = this.config;
200
+ const columns = ["id", "conversation_id", "type", "data", "created_at"];
201
+ const placeholders = columns.map((_, i) => p(i)).join(", ");
202
+ const sql = `INSERT INTO ${q("conversation_items")} (${columns
203
+ .map((c) => q(c))
204
+ .join(", ")}) VALUES (${placeholders})`;
205
+ const now = Date.now();
206
+ const results = [];
207
+ let i = 0;
208
+ for (const input of items) {
209
+ const { id: inputId, type } = input;
210
+ const id = inputId ?? uuidv7();
211
+ // Add slight offset to ensure unique (PK + TS) even in batch.
212
+ const createdAt = new Date(now + i++);
213
+ // eslint-disable-next-line no-await-in-loop
214
+ await tx.run(sql, [id, conversationId, type, input, createdAt]);
215
+ const item = input;
216
+ item.id = id;
217
+ item.conversation_id = conversationId;
218
+ item.created_at = createdAt.getTime();
219
+ results.push(item);
220
+ }
221
+ return results;
222
+ });
223
+ }
224
+ async getItem(conversationId, itemId) {
225
+ const { placeholder: p, quote: q, selectJson: sj } = this.config;
226
+ const row = await this.executor.get(`SELECT ${q("id")}, ${q("conversation_id")}, ${q("created_at")}, ${q("type")}, ${sj(q("data"))} as ${q("data")} FROM ${q("conversation_items")} WHERE ${q("id")} = ${p(0)} AND ${q("conversation_id")} = ${p(1)}`, [itemId, conversationId]);
227
+ return row ? rowMapper(row) : undefined;
228
+ }
229
+ deleteItem(conversationId, itemId) {
230
+ const { placeholder: p, quote: q } = this.config;
231
+ return this.executor.transaction(async (tx) => {
232
+ await tx.run(`DELETE FROM ${q("conversation_items")} WHERE ${q("id")} = ${p(0)} AND ${q("conversation_id")} = ${p(1)}`, [itemId, conversationId]);
233
+ return this.getConversationInternal(conversationId, tx);
234
+ });
235
+ }
236
+ async listItems(conversationId, params) {
237
+ const conversation = await this.getConversationInternal(conversationId, this.executor);
238
+ if (!conversation)
239
+ return undefined;
240
+ const { after, order, limit } = params;
241
+ const { placeholder: p, quote: q, selectJson: sj, limitAsLiteral } = this.config;
242
+ const isAsc = order === "asc";
243
+ const op = isAsc ? ">" : "<";
244
+ const dir = isAsc ? "ASC" : "DESC";
245
+ const sqlParts = [
246
+ `SELECT c.${q("id")}, c.${q("conversation_id")}, c.${q("created_at")}, c.${q("type")}, ${sj(`c.${q("data")}`)} as ${q("data")} FROM ${q("conversation_items")} c WHERE c.${q("conversation_id")} = ${p(0)}`,
247
+ ];
248
+ const args = [conversationId];
249
+ let nextIdx = 1;
250
+ if (after) {
251
+ sqlParts.push(`AND EXISTS (SELECT 1 FROM ${q("conversation_items")} _cursor WHERE _cursor.${q("id")} = ${p(nextIdx++)} AND _cursor.${q("conversation_id")} = ${p(nextIdx++)} AND (c.${q("created_at")} ${op} _cursor.${q("created_at")} OR (c.${q("created_at")} = _cursor.${q("created_at")} AND c.${q("id")} ${op} _cursor.${q("id")})))`);
252
+ args.push(after, conversationId);
253
+ }
254
+ sqlParts.push(`ORDER BY c.${q("created_at")} ${dir}, c.${q("id")} ${dir}`);
255
+ if (!Number.isNaN(limit)) {
256
+ if (limitAsLiteral) {
257
+ sqlParts.push(`LIMIT ${limit}`);
258
+ }
259
+ else {
260
+ sqlParts.push(`LIMIT ${p(nextIdx++)}`);
261
+ args.push(limit);
262
+ }
263
+ }
264
+ const query = sqlParts.join(" ");
265
+ const rows = await this.executor.all(query, args);
266
+ for (let i = 0; i < rows.length; i++) {
267
+ rowMapper(rows[i]);
268
+ }
269
+ return rows;
270
+ }
271
+ }
272
+ export * from "./dialects/greptime";
273
+ export * from "./dialects/mysql";
274
+ export * from "./dialects/postgres";
275
+ export * from "./dialects/sqlite";
276
+ export * from "./dialects/types";
@@ -0,0 +1,39 @@
1
+ export type ConversationMetadata = Record<string, string> | null;
2
+ export interface ConversationEntity {
3
+ id: string;
4
+ created_at: number;
5
+ metadata: ConversationMetadata;
6
+ }
7
+ export interface ConversationItemInput {
8
+ id?: string;
9
+ type: string;
10
+ [key: string]: unknown;
11
+ }
12
+ export interface ConversationItemEntity extends ConversationItemInput {
13
+ id: string;
14
+ conversation_id: string;
15
+ created_at: number;
16
+ }
17
+ export interface ConversationQueryOptions {
18
+ limit: number;
19
+ after?: string;
20
+ order?: "asc" | "desc";
21
+ metadata?: ConversationMetadata;
22
+ }
23
+ export interface ConversationStorage {
24
+ createConversation(params: {
25
+ metadata?: ConversationMetadata;
26
+ items?: ConversationItemInput[];
27
+ }): Promise<ConversationEntity>;
28
+ getConversation(id: string): Promise<ConversationEntity | undefined>;
29
+ listConversations(params: ConversationQueryOptions): Promise<ConversationEntity[]>;
30
+ updateConversation(id: string, metadata: ConversationMetadata): Promise<ConversationEntity | undefined>;
31
+ deleteConversation(id: string): Promise<{
32
+ id: string;
33
+ deleted: boolean;
34
+ }>;
35
+ addItems(conversationId: string, items: ConversationItemInput[]): Promise<ConversationItemEntity[] | undefined>;
36
+ getItem(conversationId: string, itemId: string): Promise<ConversationItemEntity | undefined>;
37
+ deleteItem(conversationId: string, itemId: string): Promise<ConversationEntity | undefined>;
38
+ listItems(conversationId: string, params: ConversationQueryOptions): Promise<ConversationItemEntity[] | undefined>;
39
+ }
File without changes
@@ -0,0 +1,10 @@
1
+ import type { SharedV3ProviderOptions } from "@ai-sdk/provider";
2
+ import type { EmbedManyResult } from "ai";
3
+ import type { EmbeddingsInputs, Embeddings } from "./schema";
4
+ export type EmbedCallOptions = {
5
+ values: string[];
6
+ providerOptions: SharedV3ProviderOptions;
7
+ };
8
+ export declare function convertToEmbedCallOptions(params: EmbeddingsInputs): EmbedCallOptions;
9
+ export declare function toEmbeddings(embedManyResult: EmbedManyResult, modelId: string): Embeddings;
10
+ export declare function createEmbeddingsResponse(embedManyResult: EmbedManyResult, modelId: string, responseInit?: ResponseInit): Response;
@@ -0,0 +1,31 @@
1
+ import { toResponse } from "../../utils/response";
2
+ export function convertToEmbedCallOptions(params) {
3
+ const { input, ...rest } = params;
4
+ return {
5
+ values: Array.isArray(input) ? input : [input],
6
+ providerOptions: {
7
+ unknown: rest,
8
+ },
9
+ };
10
+ }
11
+ export function toEmbeddings(embedManyResult, modelId) {
12
+ const data = embedManyResult.embeddings.map((embedding, index) => ({
13
+ object: "embedding",
14
+ embedding,
15
+ index,
16
+ }));
17
+ const usage = {
18
+ prompt_tokens: embedManyResult.usage.tokens,
19
+ total_tokens: embedManyResult.usage.tokens,
20
+ };
21
+ return {
22
+ object: "list",
23
+ data,
24
+ model: modelId,
25
+ usage,
26
+ provider_metadata: embedManyResult.providerMetadata,
27
+ };
28
+ }
29
+ export function createEmbeddingsResponse(embedManyResult, modelId, responseInit) {
30
+ return toResponse(toEmbeddings(embedManyResult, modelId), responseInit);
31
+ }
@@ -0,0 +1,2 @@
1
+ import type { GatewayConfig, Endpoint } from "../../types";
2
+ export declare const embeddings: (config: GatewayConfig) => Endpoint;
@@ -0,0 +1,99 @@
1
+ import { embedMany, wrapEmbeddingModel } from "ai";
2
+ import * as z from "zod/mini";
3
+ import { GatewayError } from "../../errors/gateway";
4
+ import { winterCgHandler } from "../../lifecycle";
5
+ import { logger } from "../../logger";
6
+ import { modelMiddlewareMatcher } from "../../middleware/matcher";
7
+ import { resolveProvider } from "../../providers/registry";
8
+ import { getGenAiGeneralAttributes, recordTimePerOutputToken, recordTokenUsage, } from "../../telemetry/gen-ai";
9
+ import { addSpanEvent, setSpanAttributes } from "../../telemetry/span";
10
+ import { parseRequestBody } from "../../utils/body";
11
+ import { prepareForwardHeaders } from "../../utils/request";
12
+ import { convertToEmbedCallOptions, toEmbeddings } from "./converters";
13
+ import { getEmbeddingsRequestAttributes, getEmbeddingsResponseAttributes } from "./otel";
14
+ import { EmbeddingsBodySchema } from "./schema";
15
+ export const embeddings = (config) => {
16
+ const hooks = config.hooks;
17
+ const handler = async (ctx, cfg) => {
18
+ const start = performance.now();
19
+ ctx.operation = "embeddings";
20
+ setSpanAttributes({ "gen_ai.operation.name": ctx.operation });
21
+ addSpanEvent("hebo.handler.started");
22
+ // Guard: enforce HTTP method early.
23
+ if (!ctx.request || ctx.request.method !== "POST") {
24
+ throw new GatewayError("Method Not Allowed", 405);
25
+ }
26
+ // Parse + validate input (handles Content-Encoding decompression + body size limits).
27
+ ctx.body = (await parseRequestBody(ctx.request, cfg.maxBodySize));
28
+ logger.trace({ requestId: ctx.requestId, result: ctx.body }, "[chat] EmbeddingsBody");
29
+ addSpanEvent("hebo.request.deserialized");
30
+ const parsed = EmbeddingsBodySchema.safeParse(ctx.body);
31
+ if (!parsed.success) {
32
+ // FUTURE: consider adding body shape to metadata
33
+ throw new GatewayError(z.prettifyError(parsed.error), 400, undefined, parsed.error);
34
+ }
35
+ ctx.body = parsed.data;
36
+ addSpanEvent("hebo.request.parsed");
37
+ if (hooks?.before) {
38
+ ctx.body = (await hooks.before(ctx)) ?? ctx.body;
39
+ addSpanEvent("hebo.hooks.before.completed");
40
+ }
41
+ // Resolve model + provider (hooks may override defaults).
42
+ ctx.modelId = ctx.body.model;
43
+ ctx.resolvedModelId =
44
+ (await hooks?.resolveModelId?.(ctx)) ?? ctx.modelId;
45
+ logger.debug(`[embeddings] resolved ${ctx.modelId} to ${ctx.resolvedModelId}`);
46
+ addSpanEvent("hebo.model.resolved");
47
+ const override = await hooks?.resolveProvider?.(ctx);
48
+ ctx.provider =
49
+ override ??
50
+ resolveProvider({
51
+ providers: ctx.providers,
52
+ models: ctx.models,
53
+ modelId: ctx.resolvedModelId,
54
+ operation: ctx.operation,
55
+ });
56
+ const embeddingModel = ctx.provider.embeddingModel(ctx.resolvedModelId);
57
+ ctx.resolvedProviderId = embeddingModel.provider;
58
+ logger.debug(`[embeddings] using ${embeddingModel.provider} for ${ctx.resolvedModelId}`);
59
+ addSpanEvent("hebo.provider.resolved");
60
+ const genAiSignalLevel = cfg.telemetry?.signals?.gen_ai;
61
+ const genAiGeneralAttrs = getGenAiGeneralAttributes(ctx, genAiSignalLevel);
62
+ setSpanAttributes(genAiGeneralAttrs);
63
+ // Convert inputs to AI SDK call options.
64
+ const { model: _model, ...inputs } = ctx.body;
65
+ const embedOptions = convertToEmbedCallOptions(inputs);
66
+ logger.trace({ requestId: ctx.requestId, options: embedOptions }, "[embeddings] AI SDK options");
67
+ addSpanEvent("hebo.options.prepared");
68
+ setSpanAttributes(getEmbeddingsRequestAttributes(ctx.body, genAiSignalLevel));
69
+ // Build middleware chain (model -> forward params -> provider).
70
+ const embeddingModelWithMiddleware = wrapEmbeddingModel({
71
+ model: embeddingModel,
72
+ middleware: modelMiddlewareMatcher.forEmbedding(ctx.resolvedModelId, embeddingModel.provider),
73
+ });
74
+ // Execute request.
75
+ addSpanEvent("hebo.ai-sdk.started");
76
+ const result = await embedMany({
77
+ model: embeddingModelWithMiddleware,
78
+ headers: prepareForwardHeaders(ctx.request),
79
+ abortSignal: ctx.request.signal,
80
+ ...embedOptions,
81
+ });
82
+ logger.trace({ requestId: ctx.requestId, result }, "[embeddings] AI SDK result");
83
+ addSpanEvent("hebo.ai-sdk.completed");
84
+ // Transform result.
85
+ ctx.result = toEmbeddings(result, ctx.modelId);
86
+ logger.trace({ requestId: ctx.requestId, result: ctx.result }, "[chat] Embeddings");
87
+ addSpanEvent("hebo.result.transformed");
88
+ const genAiResponseAttrs = getEmbeddingsResponseAttributes(ctx.result, genAiSignalLevel);
89
+ recordTokenUsage(genAiResponseAttrs, genAiGeneralAttrs, genAiSignalLevel);
90
+ setSpanAttributes(genAiResponseAttrs);
91
+ if (hooks?.after) {
92
+ ctx.result = (await hooks.after(ctx)) ?? ctx.result;
93
+ addSpanEvent("hebo.hooks.after.completed");
94
+ }
95
+ recordTimePerOutputToken(start, 0, genAiResponseAttrs, genAiGeneralAttrs, genAiSignalLevel);
96
+ return ctx.result;
97
+ };
98
+ return { handler: winterCgHandler(handler, config) };
99
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./converters";
2
+ export * from "./handler";
3
+ export * from "./schema";
4
+ export * from "./otel";
@@ -0,0 +1,4 @@
1
+ export * from "./converters";
2
+ export * from "./handler";
3
+ export * from "./schema";
4
+ export * from "./otel";
@@ -0,0 +1,5 @@
1
+ import type { Attributes } from "@opentelemetry/api";
2
+ import { type TelemetrySignalLevel } from "../../types";
3
+ import type { Embeddings, EmbeddingsBody } from "./schema";
4
+ export declare const getEmbeddingsRequestAttributes: (body: EmbeddingsBody, signalLevel?: TelemetrySignalLevel) => Attributes;
5
+ export declare const getEmbeddingsResponseAttributes: (embeddings: Embeddings, signalLevel?: TelemetrySignalLevel) => Attributes;
@@ -0,0 +1,29 @@
1
+ import {} from "../../types";
2
+ export const getEmbeddingsRequestAttributes = (body, signalLevel) => {
3
+ if (!signalLevel || signalLevel === "off")
4
+ return {};
5
+ const attrs = {};
6
+ if (signalLevel !== "required") {
7
+ Object.assign(attrs, {
8
+ "gen_ai.embeddings.dimension.count": body.dimensions,
9
+ });
10
+ if (body.metadata) {
11
+ for (const key in body.metadata) {
12
+ attrs[`gen_ai.request.metadata.${key}`] = body.metadata[key];
13
+ }
14
+ }
15
+ }
16
+ return attrs;
17
+ };
18
+ export const getEmbeddingsResponseAttributes = (embeddings, signalLevel) => {
19
+ if (!signalLevel || signalLevel === "off")
20
+ return {};
21
+ const attrs = {};
22
+ if (signalLevel !== "required") {
23
+ Object.assign(attrs, {
24
+ "gen_ai.usage.input_tokens": embeddings.usage?.prompt_tokens,
25
+ "gen_ai.usage.total_tokens": embeddings.usage?.total_tokens,
26
+ });
27
+ }
28
+ return attrs;
29
+ };
@@ -0,0 +1,44 @@
1
+ import * as z from "zod";
2
+ export declare const EmbeddingsDimensionsSchema: z.ZodInt;
3
+ export type EmbeddingsDimensions = z.infer<typeof EmbeddingsDimensionsSchema>;
4
+ export declare const EmbeddingsMetadataSchema: z.ZodRecord<z.ZodString, z.ZodString>;
5
+ export type EmbeddingsMetadata = z.infer<typeof EmbeddingsMetadataSchema>;
6
+ export declare const EmbeddingsInputsSchema: z.ZodObject<{
7
+ input: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
8
+ dimensions: z.ZodOptional<z.ZodInt>;
9
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
10
+ }, z.core.$strip>;
11
+ export type EmbeddingsInputs = z.infer<typeof EmbeddingsInputsSchema>;
12
+ export declare const EmbeddingsBodySchema: z.ZodObject<{
13
+ input: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
14
+ dimensions: z.ZodOptional<z.ZodInt>;
15
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
16
+ model: z.ZodString;
17
+ }, z.core.$loose>;
18
+ export type EmbeddingsBody = z.infer<typeof EmbeddingsBodySchema>;
19
+ export declare const EmbeddingsDataSchema: z.ZodObject<{
20
+ object: z.ZodLiteral<"embedding">;
21
+ embedding: z.ZodArray<z.ZodNumber>;
22
+ index: z.ZodInt;
23
+ }, z.core.$strip>;
24
+ export type EmbeddingsData = z.infer<typeof EmbeddingsDataSchema>;
25
+ export declare const EmbeddingsUsageSchema: z.ZodObject<{
26
+ prompt_tokens: z.ZodOptional<z.ZodInt>;
27
+ total_tokens: z.ZodOptional<z.ZodInt>;
28
+ }, z.core.$strip>;
29
+ export type EmbeddingsUsage = z.infer<typeof EmbeddingsUsageSchema>;
30
+ export declare const EmbeddingsSchema: z.ZodObject<{
31
+ object: z.ZodLiteral<"list">;
32
+ data: z.ZodArray<z.ZodObject<{
33
+ object: z.ZodLiteral<"embedding">;
34
+ embedding: z.ZodArray<z.ZodNumber>;
35
+ index: z.ZodInt;
36
+ }, z.core.$strip>>;
37
+ model: z.ZodString;
38
+ usage: z.ZodNullable<z.ZodObject<{
39
+ prompt_tokens: z.ZodOptional<z.ZodInt>;
40
+ total_tokens: z.ZodOptional<z.ZodInt>;
41
+ }, z.core.$strip>>;
42
+ provider_metadata: z.ZodOptional<z.ZodUnknown>;
43
+ }, z.core.$strip>;
44
+ export type Embeddings = z.infer<typeof EmbeddingsSchema>;
@@ -0,0 +1,29 @@
1
+ import * as z from "zod";
2
+ export const EmbeddingsDimensionsSchema = z.int().nonnegative().max(65536);
3
+ export const EmbeddingsMetadataSchema = z.record(z.string().min(1).max(64), z.string().max(512));
4
+ export const EmbeddingsInputsSchema = z.object({
5
+ input: z.union([z.string(), z.array(z.string())]),
6
+ dimensions: EmbeddingsDimensionsSchema.optional(),
7
+ metadata: EmbeddingsMetadataSchema.optional(),
8
+ });
9
+ export const EmbeddingsBodySchema = z.looseObject({
10
+ model: z.string(),
11
+ ...EmbeddingsInputsSchema.shape,
12
+ });
13
+ export const EmbeddingsDataSchema = z.object({
14
+ object: z.literal("embedding"),
15
+ embedding: z.array(z.number()),
16
+ index: z.int().nonnegative(),
17
+ });
18
+ export const EmbeddingsUsageSchema = z.object({
19
+ prompt_tokens: z.int().nonnegative().optional(),
20
+ total_tokens: z.int().nonnegative().optional(),
21
+ });
22
+ export const EmbeddingsSchema = z.object({
23
+ object: z.literal("list"),
24
+ data: z.array(EmbeddingsDataSchema),
25
+ model: z.string(),
26
+ usage: EmbeddingsUsageSchema.nullable(),
27
+ // Extensions
28
+ provider_metadata: z.unknown().optional().meta({ extension: true }),
29
+ });
@@ -0,0 +1,6 @@
1
+ import type { ModelCatalog, CatalogModel } from "../../models/types";
2
+ import type { ModelList, Model } from "./schema";
3
+ export declare function toModel(id: string, catalogModel: CatalogModel): Model;
4
+ export declare function toModels(models: ModelCatalog): ModelList;
5
+ export declare function createModelsResponse(models: ModelCatalog, responseInit?: ResponseInit): Response;
6
+ export declare function createModelResponse(id: string, catalogModel: CatalogModel, responseInit?: ResponseInit): Response;
@@ -0,0 +1,42 @@
1
+ import { toResponse } from "../../utils/response";
2
+ export function toModel(id, catalogModel) {
3
+ const { created, providers, modalities, additionalProperties, ...rest } = catalogModel;
4
+ let createdTimestamp = Math.floor(Date.now() / 1000);
5
+ if (created) {
6
+ const parsed = Date.parse(created);
7
+ if (!isNaN(parsed)) {
8
+ createdTimestamp = Math.floor(parsed / 1000);
9
+ }
10
+ }
11
+ const model = {
12
+ id,
13
+ object: "model",
14
+ created: createdTimestamp,
15
+ owned_by: id.split("/")[0] ?? "system",
16
+ architecture: {
17
+ input_modalities: modalities?.input ?? [],
18
+ modality: modalities?.input &&
19
+ modalities?.output &&
20
+ `${modalities.input?.[0]}->${modalities.output?.[0]}`,
21
+ output_modalities: modalities?.output ?? [],
22
+ },
23
+ endpoints: providers?.map((provider) => ({
24
+ tag: provider,
25
+ })) || [],
26
+ ...rest,
27
+ ...additionalProperties,
28
+ };
29
+ return model;
30
+ }
31
+ export function toModels(models) {
32
+ return {
33
+ object: "list",
34
+ data: Object.entries(models).map(([id, catalogModel]) => toModel(id, catalogModel)),
35
+ };
36
+ }
37
+ export function createModelsResponse(models, responseInit) {
38
+ return toResponse(toModels(models), responseInit);
39
+ }
40
+ export function createModelResponse(id, catalogModel, responseInit) {
41
+ return toResponse(toModel(id, catalogModel), responseInit);
42
+ }
@@ -0,0 +1,2 @@
1
+ import type { GatewayConfig, Endpoint } from "../../types";
2
+ export declare const models: (config: GatewayConfig) => Endpoint;