@lota-sdk/core 0.1.17 → 0.1.18

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lota-sdk/core",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -32,7 +32,7 @@
32
32
  "@chat-adapter/slack": "^4.23.0",
33
33
  "@chat-adapter/state-ioredis": "^4.23.0",
34
34
  "@logtape/logtape": "^2.0.5",
35
- "@lota-sdk/shared": "0.1.17",
35
+ "@lota-sdk/shared": "0.1.18",
36
36
  "@mendable/firecrawl-js": "^4.17.0",
37
37
  "@surrealdb/node": "^3.0.3",
38
38
  "ai": "^6.0.137",
@@ -3,26 +3,57 @@ import { createOpenAI } from '@ai-sdk/openai'
3
3
  import { wrapLanguageModel } from 'ai'
4
4
  import type { LanguageModelMiddleware } from 'ai'
5
5
 
6
- import { getRequiredEnv } from '../utils/env'
6
+ import { getRuntimeConfig } from '../runtime/runtime-config'
7
7
  import { isRecord, readString } from '../utils/string'
8
8
 
9
- type BifrostLanguageModel = Parameters<typeof wrapLanguageModel>[0]['model']
10
- type BifrostExtraParams = Record<string, unknown>
11
- type BifrostChatResponse = { body?: unknown }
12
- type BifrostTransformParamsOptions = Parameters<NonNullable<LanguageModelMiddleware['transformParams']>>[0]
9
+ type AiGatewayLanguageModel = Parameters<typeof wrapLanguageModel>[0]['model']
10
+ type AiGatewayExtraParams = Record<string, unknown>
11
+ type AiGatewayChatResponse = { body?: unknown }
12
+ type AiGatewayTransformParamsOptions = Parameters<NonNullable<LanguageModelMiddleware['transformParams']>>[0]
13
13
  type WrapStreamOptions = Parameters<NonNullable<LanguageModelMiddleware['wrapStream']>>[0]
14
- type BifrostCallOptions = WrapStreamOptions['params']
15
- type BifrostGenerateResult = Awaited<ReturnType<WrapStreamOptions['doGenerate']>>
16
- type BifrostStreamResult = Awaited<ReturnType<WrapStreamOptions['doStream']>>
17
- type BifrostGeneratedContent = BifrostGenerateResult['content'][number]
18
- type BifrostStreamPart = BifrostStreamResult['stream'] extends ReadableStream<infer T> ? T : never
19
-
14
+ type AiGatewayCallOptions = WrapStreamOptions['params']
15
+ type AiGatewayGenerateResult = Awaited<ReturnType<WrapStreamOptions['doGenerate']>>
16
+ type AiGatewayStreamResult = Awaited<ReturnType<WrapStreamOptions['doStream']>>
17
+ type AiGatewayGeneratedContent = AiGatewayGenerateResult['content'][number]
18
+ type AiGatewayStreamPart = AiGatewayStreamResult['stream'] extends ReadableStream<infer T> ? T : never
19
+ type AiGatewayConfig = { apiKey: string; baseURL: string }
20
+
21
+ const EXPECTED_GATEWAY_KEY_PREFIX = 'sk-bf-'
22
+ const AI_GATEWAY_VIRTUAL_KEY_HEADER = 'x-bf-vk'
23
+ const AI_GATEWAY_EXTRA_PARAMS_HEADER = 'x-bf-passthrough-extra-params'
24
+ const DEFAULT_AI_GATEWAY_URL = 'https://ai-gateway.gobrainy.ai' as const
20
25
  const OPENROUTER_RESPONSE_HEALING_EXTRA_PARAMS = {
21
26
  plugins: [{ id: 'response-healing' }],
22
- } as const satisfies BifrostExtraParams
27
+ } as const satisfies AiGatewayExtraParams
28
+
29
+ function normalizeAiGatewayUrl(value: string): string {
30
+ const trimmed = value.trim()
31
+ if (!trimmed) {
32
+ throw new Error('[ai-gateway] AI gateway URL is required.')
33
+ }
34
+
35
+ const normalized = trimmed.replace(/\/+$/, '')
36
+ return normalized.endsWith('/v1') ? normalized : `${normalized}/v1`
37
+ }
38
+
39
+ function readDirectEnvAiGatewayConfig(): AiGatewayConfig {
40
+ const apiKey = (process.env.LOTA_KEY ?? process.env.VENTUROS_KEY ?? '').trim()
41
+ if (!apiKey) {
42
+ throw new Error(
43
+ '[ai-gateway] Missing AI gateway key. Set LOTA_KEY or VENTUROS_KEY, or configure createLotaRuntime({ aiGateway: { key } }).',
44
+ )
45
+ }
23
46
 
24
- function readRequiredGatewayEnv(name: 'AI_GATEWAY_KEY' | 'AI_GATEWAY_URL'): string {
25
- return getRequiredEnv(name).trim()
47
+ return { apiKey, baseURL: normalizeAiGatewayUrl(process.env.AI_GATEWAY_URL?.trim() || DEFAULT_AI_GATEWAY_URL) }
48
+ }
49
+
50
+ function readAiGatewayConfig(): AiGatewayConfig {
51
+ try {
52
+ const { aiGateway } = getRuntimeConfig()
53
+ return { apiKey: aiGateway.key.trim(), baseURL: normalizeAiGatewayUrl(aiGateway.url) }
54
+ } catch {
55
+ return readDirectEnvAiGatewayConfig()
56
+ }
26
57
  }
27
58
 
28
59
  function readReasoningDetailsText(value: unknown): string | null {
@@ -41,7 +72,7 @@ function readReasoningDeltaText(value: unknown): string | null {
41
72
  return typeof value === 'string' && value.length > 0 ? value : null
42
73
  }
43
74
 
44
- function readBifrostChatReasoningText(message: Record<string, unknown>): string | null {
75
+ function readAiGatewayChatReasoningText(message: Record<string, unknown>): string | null {
45
76
  return (
46
77
  readString(message.reasoning) ??
47
78
  readString(message.reasoning_content) ??
@@ -49,20 +80,20 @@ function readBifrostChatReasoningText(message: Record<string, unknown>): string
49
80
  )
50
81
  }
51
82
 
52
- export function extractBifrostChatReasoningText(responseBody: unknown): string | null {
83
+ export function extractAiGatewayChatReasoningText(responseBody: unknown): string | null {
53
84
  if (!isRecord(responseBody) || !Array.isArray(responseBody.choices)) return null
54
85
 
55
86
  for (const choice of responseBody.choices) {
56
87
  if (!isRecord(choice) || !isRecord(choice.message)) continue
57
88
 
58
- const reasoningText = readBifrostChatReasoningText(choice.message)
89
+ const reasoningText = readAiGatewayChatReasoningText(choice.message)
59
90
  if (reasoningText) return reasoningText
60
91
  }
61
92
 
62
93
  return null
63
94
  }
64
95
 
65
- export function extractBifrostChatReasoningDeltaText(rawChunk: unknown): string | null {
96
+ export function extractAiGatewayChatReasoningDeltaText(rawChunk: unknown): string | null {
66
97
  if (!isRecord(rawChunk) || !Array.isArray(rawChunk.choices)) return null
67
98
 
68
99
  for (const choice of rawChunk.choices) {
@@ -78,9 +109,9 @@ export function extractBifrostChatReasoningDeltaText(rawChunk: unknown): string
78
109
  return null
79
110
  }
80
111
 
81
- type BifrostResponsesReasoningDelta = { id: string; delta: string; itemId: string }
112
+ type AiGatewayResponsesReasoningDelta = { id: string; delta: string; itemId: string }
82
113
 
83
- export function extractBifrostResponsesReasoningDelta(rawChunk: unknown): BifrostResponsesReasoningDelta | null {
114
+ export function extractAiGatewayResponsesReasoningDelta(rawChunk: unknown): AiGatewayResponsesReasoningDelta | null {
84
115
  if (!isRecord(rawChunk) || rawChunk.type !== 'response.reasoning_summary_text.delta') return null
85
116
  if ('summary_index' in rawChunk) return null
86
117
 
@@ -91,21 +122,21 @@ export function extractBifrostResponsesReasoningDelta(rawChunk: unknown): Bifros
91
122
  return { id: `${itemId}:0`, delta, itemId }
92
123
  }
93
124
 
94
- export function injectBifrostChatReasoningContent(
95
- content: readonly BifrostGeneratedContent[],
96
- response?: BifrostChatResponse,
97
- ): BifrostGeneratedContent[] {
125
+ export function injectAiGatewayChatReasoningContent(
126
+ content: readonly AiGatewayGeneratedContent[],
127
+ response?: AiGatewayChatResponse,
128
+ ): AiGatewayGeneratedContent[] {
98
129
  if (content.some((part) => part.type === 'reasoning')) {
99
130
  return [...content]
100
131
  }
101
132
 
102
- const reasoningText = extractBifrostChatReasoningText(response?.body)
133
+ const reasoningText = extractAiGatewayChatReasoningText(response?.body)
103
134
  if (!reasoningText) return [...content]
104
135
 
105
136
  return [{ type: 'reasoning', text: reasoningText }, ...content]
106
137
  }
107
138
 
108
- function isReasoningEnabled(params: BifrostCallOptions): boolean {
139
+ function isReasoningEnabled(params: AiGatewayCallOptions): boolean {
109
140
  if (!isRecord(params.providerOptions) || !isRecord(params.providerOptions.openai)) return false
110
141
 
111
142
  const openaiOptions = params.providerOptions.openai
@@ -114,34 +145,34 @@ function isReasoningEnabled(params: BifrostCallOptions): boolean {
114
145
  return typeof openaiOptions.reasoningEffort === 'string' && openaiOptions.reasoningEffort !== 'none'
115
146
  }
116
147
 
117
- function shouldCloseInjectedReasoning(chunk: BifrostStreamPart): boolean {
148
+ function shouldCloseInjectedReasoning(chunk: AiGatewayStreamPart): boolean {
118
149
  return chunk.type !== 'stream-start' && chunk.type !== 'response-metadata' && chunk.type !== 'raw'
119
150
  }
120
151
 
121
- export function injectBifrostChatReasoningStream(
122
- stream: ReadableStream<BifrostStreamPart>,
123
- ): ReadableStream<BifrostStreamPart> {
124
- const reasoningId = 'bifrost-reasoning-0'
152
+ export function injectAiGatewayChatReasoningStream(
153
+ stream: ReadableStream<AiGatewayStreamPart>,
154
+ ): ReadableStream<AiGatewayStreamPart> {
155
+ const reasoningId = 'ai-gateway-reasoning-0'
125
156
  let reasoningOpen = false
126
157
  let reasoningClosed = false
127
158
 
128
159
  return stream.pipeThrough(
129
- new TransformStream<BifrostStreamPart, BifrostStreamPart>({
160
+ new TransformStream<AiGatewayStreamPart, AiGatewayStreamPart>({
130
161
  transform(chunk, controller) {
131
162
  const closeReasoning = () => {
132
163
  if (!reasoningOpen || reasoningClosed) return
133
164
 
134
- controller.enqueue({ type: 'reasoning-end', id: reasoningId } satisfies BifrostStreamPart)
165
+ controller.enqueue({ type: 'reasoning-end', id: reasoningId } satisfies AiGatewayStreamPart)
135
166
  reasoningClosed = true
136
167
  }
137
168
 
138
169
  if (chunk.type === 'raw') {
139
- const reasoningDelta = reasoningClosed ? null : extractBifrostChatReasoningDeltaText(chunk.rawValue)
170
+ const reasoningDelta = reasoningClosed ? null : extractAiGatewayChatReasoningDeltaText(chunk.rawValue)
140
171
  controller.enqueue(chunk)
141
172
 
142
173
  if (reasoningDelta) {
143
174
  if (!reasoningOpen) {
144
- controller.enqueue({ type: 'reasoning-start', id: reasoningId } satisfies BifrostStreamPart)
175
+ controller.enqueue({ type: 'reasoning-start', id: reasoningId } satisfies AiGatewayStreamPart)
145
176
  reasoningOpen = true
146
177
  }
147
178
 
@@ -149,7 +180,7 @@ export function injectBifrostChatReasoningStream(
149
180
  type: 'reasoning-delta',
150
181
  id: reasoningId,
151
182
  delta: reasoningDelta,
152
- } satisfies BifrostStreamPart)
183
+ } satisfies AiGatewayStreamPart)
153
184
  }
154
185
  return
155
186
  }
@@ -162,23 +193,23 @@ export function injectBifrostChatReasoningStream(
162
193
  },
163
194
  flush(controller) {
164
195
  if (!reasoningOpen || reasoningClosed) return
165
- controller.enqueue({ type: 'reasoning-end', id: reasoningId } satisfies BifrostStreamPart)
196
+ controller.enqueue({ type: 'reasoning-end', id: reasoningId } satisfies AiGatewayStreamPart)
166
197
  },
167
198
  }),
168
199
  )
169
200
  }
170
201
 
171
- export function injectBifrostResponsesReasoningStream(
172
- stream: ReadableStream<BifrostStreamPart>,
173
- ): ReadableStream<BifrostStreamPart> {
202
+ export function injectAiGatewayResponsesReasoningStream(
203
+ stream: ReadableStream<AiGatewayStreamPart>,
204
+ ): ReadableStream<AiGatewayStreamPart> {
174
205
  return stream.pipeThrough(
175
- new TransformStream<BifrostStreamPart, BifrostStreamPart>({
206
+ new TransformStream<AiGatewayStreamPart, AiGatewayStreamPart>({
176
207
  transform(chunk, controller) {
177
208
  controller.enqueue(chunk)
178
209
 
179
210
  if (chunk.type !== 'raw') return
180
211
 
181
- const reasoningDelta = extractBifrostResponsesReasoningDelta(chunk.rawValue)
212
+ const reasoningDelta = extractAiGatewayResponsesReasoningDelta(chunk.rawValue)
182
213
  if (!reasoningDelta) return
183
214
 
184
215
  controller.enqueue({
@@ -186,16 +217,16 @@ export function injectBifrostResponsesReasoningStream(
186
217
  id: reasoningDelta.id,
187
218
  delta: reasoningDelta.delta,
188
219
  providerMetadata: { openai: { itemId: reasoningDelta.itemId } },
189
- } satisfies BifrostStreamPart)
220
+ } satisfies AiGatewayStreamPart)
190
221
  },
191
222
  }),
192
223
  )
193
224
  }
194
225
 
195
- function addBifrostReasoningRawChunks(
196
- params: BifrostCallOptions,
197
- type: BifrostTransformParamsOptions['type'],
198
- ): BifrostCallOptions {
226
+ function addAiGatewayReasoningRawChunks(
227
+ params: AiGatewayCallOptions,
228
+ type: AiGatewayTransformParamsOptions['type'],
229
+ ): AiGatewayCallOptions {
199
230
  if (type !== 'stream' || !isReasoningEnabled(params) || params.includeRawChunks === true) {
200
231
  return params
201
232
  }
@@ -203,9 +234,9 @@ function addBifrostReasoningRawChunks(
203
234
  return { ...params, includeRawChunks: true }
204
235
  }
205
236
 
206
- export function injectBifrostExtraParamsRequestBody(
237
+ export function injectAiGatewayExtraParamsRequestBody(
207
238
  body: BodyInit | null | undefined,
208
- extraParams: BifrostExtraParams,
239
+ extraParams: AiGatewayExtraParams,
209
240
  ): BodyInit | null | undefined {
210
241
  if (typeof body !== 'string') return body
211
242
 
@@ -225,87 +256,90 @@ export function injectBifrostExtraParamsRequestBody(
225
256
  return JSON.stringify({ ...parsed, extra_params: mergedExtraParams })
226
257
  }
227
258
 
228
- function createBifrostFetchWithExtraParams(extraParams: BifrostExtraParams): typeof fetch {
259
+ function createAiGatewayFetchWithExtraParams(extraParams: AiGatewayExtraParams): typeof fetch {
229
260
  const fetchWithExtraParams = (input: RequestInfo | URL, init?: RequestInit | BunFetchRequestInit) =>
230
- globalThis.fetch(input, { ...init, body: injectBifrostExtraParamsRequestBody(init?.body, extraParams) })
261
+ globalThis.fetch(input, { ...init, body: injectAiGatewayExtraParamsRequestBody(init?.body, extraParams) })
231
262
 
232
263
  return Object.assign(fetchWithExtraParams, { preconnect: globalThis.fetch.preconnect.bind(globalThis.fetch) })
233
264
  }
234
265
 
235
- function createBifrostProvider(extraParams?: BifrostExtraParams) {
236
- const apiKey = readRequiredGatewayEnv('AI_GATEWAY_KEY')
237
- if (!apiKey.startsWith('sk-bf-')) {
238
- throw new Error('[bifrost] AI_GATEWAY_KEY must use the Bifrost virtual-key format (sk-bf-*).')
266
+ function createAiGatewayProvider(extraParams?: AiGatewayExtraParams) {
267
+ const { apiKey, baseURL } = readAiGatewayConfig()
268
+ if (!apiKey.startsWith(EXPECTED_GATEWAY_KEY_PREFIX)) {
269
+ throw new Error(`[ai-gateway] Gateway keys must use the ${EXPECTED_GATEWAY_KEY_PREFIX}* format.`)
239
270
  }
240
271
 
241
272
  return createOpenAI({
242
- baseURL: readRequiredGatewayEnv('AI_GATEWAY_URL'),
273
+ baseURL,
243
274
  apiKey,
244
- headers: { 'x-bf-vk': apiKey, ...(extraParams ? { 'x-bf-passthrough-extra-params': 'true' } : {}) },
245
- ...(extraParams ? { fetch: createBifrostFetchWithExtraParams(extraParams) } : {}),
275
+ headers: {
276
+ [AI_GATEWAY_VIRTUAL_KEY_HEADER]: apiKey,
277
+ ...(extraParams ? { [AI_GATEWAY_EXTRA_PARAMS_HEADER]: 'true' } : {}),
278
+ },
279
+ ...(extraParams ? { fetch: createAiGatewayFetchWithExtraParams(extraParams) } : {}),
246
280
  })
247
281
  }
248
282
 
249
- function withBifrostDevTools<TModel extends BifrostLanguageModel>(model: TModel): TModel {
283
+ function withAiGatewayDevTools<TModel extends AiGatewayLanguageModel>(model: TModel): TModel {
250
284
  return wrapLanguageModel({ model, middleware: devToolsMiddleware() }) as TModel
251
285
  }
252
286
 
253
287
  let provider: ReturnType<typeof createOpenAI> | null = null
254
288
  let openRouterResponseHealingProvider: ReturnType<typeof createOpenAI> | null = null
255
289
 
256
- export function getBifrostProvider() {
290
+ export function getAiGatewayProvider() {
257
291
  if (provider) return provider
258
292
 
259
- provider = createBifrostProvider()
293
+ provider = createAiGatewayProvider()
260
294
 
261
295
  return provider
262
296
  }
263
297
 
264
- export function getBifrostOpenRouterResponseHealingProvider() {
298
+ export function getAiGatewayOpenRouterResponseHealingProvider() {
265
299
  if (openRouterResponseHealingProvider) return openRouterResponseHealingProvider
266
300
 
267
- openRouterResponseHealingProvider = createBifrostProvider(OPENROUTER_RESPONSE_HEALING_EXTRA_PARAMS)
301
+ openRouterResponseHealingProvider = createAiGatewayProvider(OPENROUTER_RESPONSE_HEALING_EXTRA_PARAMS)
268
302
 
269
303
  return openRouterResponseHealingProvider
270
304
  }
271
305
 
272
- export function bifrostModel(modelId: string) {
273
- return withBifrostDevTools(
306
+ export function aiGatewayModel(modelId: string) {
307
+ return withAiGatewayDevTools(
274
308
  wrapLanguageModel({
275
- model: getBifrostProvider()(modelId),
309
+ model: getAiGatewayProvider()(modelId),
276
310
  middleware: {
277
311
  specificationVersion: 'v3',
278
- transformParams: async ({ params, type }) => addBifrostReasoningRawChunks(params, type),
312
+ transformParams: async ({ params, type }) => addAiGatewayReasoningRawChunks(params, type),
279
313
  wrapStream: async ({ doStream, params }) => {
280
314
  const result = await doStream()
281
315
  if (!isReasoningEnabled(params)) return result
282
316
 
283
- return { ...result, stream: injectBifrostResponsesReasoningStream(result.stream) }
317
+ return { ...result, stream: injectAiGatewayResponsesReasoningStream(result.stream) }
284
318
  },
285
319
  },
286
320
  }),
287
321
  )
288
322
  }
289
323
 
290
- export function bifrostOpenRouterResponseHealingModel(modelId: string) {
291
- return withBifrostDevTools(getBifrostOpenRouterResponseHealingProvider()(modelId))
324
+ export function aiGatewayOpenRouterResponseHealingModel(modelId: string) {
325
+ return withAiGatewayDevTools(getAiGatewayOpenRouterResponseHealingProvider()(modelId))
292
326
  }
293
327
 
294
- export function bifrostChatModel(modelId: string) {
295
- return withBifrostDevTools(
328
+ export function aiGatewayChatModel(modelId: string) {
329
+ return withAiGatewayDevTools(
296
330
  wrapLanguageModel({
297
- model: getBifrostProvider().chat(modelId),
331
+ model: getAiGatewayProvider().chat(modelId),
298
332
  middleware: {
299
333
  specificationVersion: 'v3',
300
- transformParams: async ({ params, type }) => addBifrostReasoningRawChunks(params, type),
334
+ transformParams: async ({ params, type }) => addAiGatewayReasoningRawChunks(params, type),
301
335
  wrapGenerate: async ({ doGenerate }) => {
302
336
  const result = await doGenerate()
303
337
 
304
338
  return {
305
339
  ...result,
306
- content: injectBifrostChatReasoningContent(
340
+ content: injectAiGatewayChatReasoningContent(
307
341
  result.content,
308
- result.response as BifrostChatResponse | undefined,
342
+ result.response as AiGatewayChatResponse | undefined,
309
343
  ),
310
344
  }
311
345
  },
@@ -313,13 +347,15 @@ export function bifrostChatModel(modelId: string) {
313
347
  const result = await doStream()
314
348
  if (!isReasoningEnabled(params)) return result
315
349
 
316
- return { ...result, stream: injectBifrostChatReasoningStream(result.stream) }
350
+ return { ...result, stream: injectAiGatewayChatReasoningStream(result.stream) }
317
351
  },
318
352
  },
319
353
  }),
320
354
  )
321
355
  }
322
356
 
323
- export function bifrostEmbeddingModel(modelId: string) {
324
- return getBifrostProvider().embeddingModel(modelId)
357
+ export function aiGatewayEmbeddingModel(modelId: string) {
358
+ return getAiGatewayProvider().embeddingModel(modelId)
325
359
  }
360
+
361
+ export { DEFAULT_AI_GATEWAY_URL, normalizeAiGatewayUrl }
@@ -0,0 +1,8 @@
1
+ const AI_GATEWAY_CACHE_KEY_HEADER = 'x-bf-cache-key'
2
+ const AI_GATEWAY_CACHE_TTL_HEADER = 'x-bf-cache-ttl'
3
+
4
+ export function buildAiGatewayCacheHeaders(cacheKey: string, ttl?: string): Record<string, string> {
5
+ const headers: Record<string, string> = { [AI_GATEWAY_CACHE_KEY_HEADER]: cacheKey }
6
+ if (ttl) headers[AI_GATEWAY_CACHE_TTL_HEADER] = ttl
7
+ return headers
8
+ }
@@ -1,2 +1,2 @@
1
- export * from './bifrost'
1
+ export * from './ai-gateway'
2
2
  export * from './cache-headers'
@@ -1,5 +1,5 @@
1
1
  export {
2
- BIFROST_REASONING_SUMMARY_LEVEL,
2
+ AI_GATEWAY_REASONING_SUMMARY_LEVEL,
3
3
  OPENAI_HIGH_REASONING_PROVIDER_OPTIONS,
4
4
  OPENAI_REASONING_MODEL_ID,
5
5
  OPENROUTER_DELEGATED_REASONING_MODEL_ID,
@@ -1,7 +1,7 @@
1
1
  import { embed, embedMany } from 'ai'
2
2
 
3
+ import { aiGatewayEmbeddingModel } from '../ai-gateway/ai-gateway'
3
4
  import { getEmbeddingCache } from '../ai/embedding-cache'
4
- import { bifrostEmbeddingModel } from '../bifrost/bifrost'
5
5
  import { getRuntimeConfig } from '../runtime/runtime-config'
6
6
 
7
7
  const SUPPORTED_EMBEDDING_PREFIXES = ['openai/', 'openrouter/'] as const
@@ -30,7 +30,7 @@ function resolveEmbeddingModel(modelId: string) {
30
30
  )
31
31
  }
32
32
 
33
- return bifrostEmbeddingModel(normalized)
33
+ return aiGatewayEmbeddingModel(normalized)
34
34
  }
35
35
 
36
36
  function normalizeEmbedding(embedding: readonly number[]): number[] {
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from './create-runtime'
2
2
  export * from './ai'
3
- export * from './bifrost'
3
+ export * from './ai-gateway'
4
4
  export * from './config'
5
5
  export * from './db'
6
6
  export * from './document'
@@ -220,8 +220,6 @@ export const LotaRuntimeConfigSchema = z.object({
220
220
  aiGateway: z.object({
221
221
  url: z.string().trim().min(1),
222
222
  key: z.string().trim().min(1),
223
- admin: z.string().trim().min(1).optional(),
224
- pass: z.string().trim().min(1).optional(),
225
223
  embeddingModel: z.string().trim().min(1).default('openai/text-embedding-3-small'),
226
224
  }),
227
225
  s3: z.object({
@@ -294,9 +292,7 @@ export const LOTA_RUNTIME_ENV_KEYS = Object.freeze([
294
292
  'SURREALDB_PASSWORD',
295
293
  'REDIS_URL',
296
294
  'AI_GATEWAY_URL',
297
- 'AI_GATEWAY_KEY',
298
- 'AI_GATEWAY_ADMIN',
299
- 'AI_GATEWAY_PASS',
295
+ 'LOTA_KEY',
300
296
  'AI_EMBEDDING_MODEL',
301
297
  'S3_ENDPOINT',
302
298
  'S3_BUCKET',
@@ -1,7 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
4
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
5
  import {
6
6
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
7
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -40,8 +40,8 @@ Return valid data for:
40
40
  export function createContextCompactionAgent(options: CreateHelperToolLoopAgentOptions) {
41
41
  return new ToolLoopAgent({
42
42
  id: 'context-compaction',
43
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
44
- headers: buildBifrostCacheHeaders('context-compaction'),
43
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
44
+ headers: buildAiGatewayCacheHeaders('context-compaction'),
45
45
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
46
46
  ...resolveHelperAgentOptions(options, { instructions: CONTEXT_COMPACTION_PROMPT }),
47
47
  })
@@ -1,7 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
4
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
5
  import {
6
6
  OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
7
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -32,8 +32,8 @@ Set every item.relevance as a string; use empty string when no reason is needed.
32
32
  export function createMemoryRerankerAgent(options: CreateHelperToolLoopAgentOptions) {
33
33
  return new ToolLoopAgent({
34
34
  id: 'memory-reranker',
35
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
36
- headers: buildBifrostCacheHeaders('memory-reranker'),
35
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
36
+ headers: buildAiGatewayCacheHeaders('memory-reranker'),
37
37
  providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
38
38
  ...resolveHelperAgentOptions(options),
39
39
  })
@@ -1,7 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
4
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
5
  import {
6
6
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
7
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -52,8 +52,8 @@ Return only the schema fields with no extra formatting.
52
52
  export function createOrgMemoryAgent(options: CreateHelperToolLoopAgentOptions) {
53
53
  return new ToolLoopAgent({
54
54
  id: 'org-memory',
55
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
56
- headers: buildBifrostCacheHeaders('org-memory'),
55
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
56
+ headers: buildAiGatewayCacheHeaders('org-memory'),
57
57
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
58
58
  ...resolveHelperAgentOptions(options),
59
59
  })
@@ -1,7 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostModel } from '../bifrost/bifrost'
4
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
3
+ import { aiGatewayModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
5
  import { getLeadAgentDisplayName } from '../config/agent-defaults'
6
6
  import {
7
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
@@ -79,8 +79,8 @@ Return only the title text. No quotes, labels, JSON, markdown, or explanation.
79
79
  export function createRecentActivityTitleRefinerAgent(options: CreateHelperToolLoopAgentOptions) {
80
80
  return new ToolLoopAgent({
81
81
  id: 'recent-activity-title-refiner',
82
- model: bifrostModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
83
- headers: buildBifrostCacheHeaders('recent-activity-title-refiner'),
82
+ model: aiGatewayModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
83
+ headers: buildAiGatewayCacheHeaders('recent-activity-title-refiner'),
84
84
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
85
85
  ...resolveHelperAgentOptions(options, {
86
86
  instructions: buildRecentActivityTitleRefinerPrompt(),
@@ -1,7 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
4
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
3
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
5
  import {
6
6
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
7
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -66,8 +66,8 @@ Return only schema fields.
66
66
  export function createRegularChatMemoryDigestAgent(options: CreateHelperToolLoopAgentOptions) {
67
67
  return new ToolLoopAgent({
68
68
  id: 'regular-chat-memory-digest',
69
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
70
- headers: buildBifrostCacheHeaders('regular-chat-memory-digest'),
69
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
70
+ headers: buildAiGatewayCacheHeaders('regular-chat-memory-digest'),
71
71
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
72
72
  ...resolveHelperAgentOptions(options, {
73
73
  instructions: regularChatMemoryDigestPrompt,
@@ -1,8 +1,8 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
  import { z } from 'zod'
3
3
 
4
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
5
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
4
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
5
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
6
6
  import {
7
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
8
8
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -79,8 +79,8 @@ export type SkillCandidate = z.infer<typeof SkillCandidateSchema>
79
79
  export function createSkillExtractorAgent(options: CreateHelperToolLoopAgentOptions) {
80
80
  return new ToolLoopAgent({
81
81
  id: 'skill-extractor',
82
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
83
- headers: buildBifrostCacheHeaders('skill-extractor'),
82
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
83
+ headers: buildAiGatewayCacheHeaders('skill-extractor'),
84
84
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
85
85
  ...resolveHelperAgentOptions(options, {
86
86
  instructions: skillExtractorPrompt,
@@ -1,8 +1,8 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
  import { z } from 'zod'
3
3
 
4
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
5
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
4
+ import { aiGatewayOpenRouterResponseHealingModel } from '../ai-gateway/ai-gateway'
5
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
6
6
  import {
7
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
8
8
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
@@ -69,8 +69,8 @@ export const SkillManagerOutputSchema = z.object({
69
69
  export function createSkillManagerAgent(options: CreateHelperToolLoopAgentOptions) {
70
70
  return new ToolLoopAgent({
71
71
  id: 'skill-manager',
72
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
73
- headers: buildBifrostCacheHeaders('skill-manager'),
72
+ model: aiGatewayOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
73
+ headers: buildAiGatewayCacheHeaders('skill-manager'),
74
74
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
75
75
  ...resolveHelperAgentOptions(options, {
76
76
  instructions: skillManagerPrompt,
@@ -1,7 +1,7 @@
1
1
  import { ToolLoopAgent } from 'ai'
2
2
 
3
- import { bifrostModel } from '../bifrost/bifrost'
4
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
3
+ import { aiGatewayModel } from '../ai-gateway/ai-gateway'
4
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
5
5
  import {
6
6
  OPENROUTER_FAST_REASONING_MODEL_ID,
7
7
  OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
@@ -33,8 +33,8 @@ Return only the title text. No quotes, no labels, no explanation.
33
33
  export function createWorkstreamTitleGeneratorAgent(options: CreateHelperToolLoopAgentOptions) {
34
34
  return new ToolLoopAgent({
35
35
  id: 'workstream-title-generator',
36
- model: bifrostModel(OPENROUTER_FAST_REASONING_MODEL_ID),
37
- headers: buildBifrostCacheHeaders('workstream-title-generator'),
36
+ model: aiGatewayModel(OPENROUTER_FAST_REASONING_MODEL_ID),
37
+ headers: buildAiGatewayCacheHeaders('workstream-title-generator'),
38
38
  providerOptions: OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
39
39
  ...resolveHelperAgentOptions(options, {
40
40
  instructions: WORKSTREAM_TITLE_GENERATOR_PROMPT,
@@ -1,5 +1,5 @@
1
- import { bifrostChatModel } from '../bifrost/bifrost'
2
- import { buildBifrostCacheHeaders } from '../bifrost/cache-headers'
1
+ import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
2
+ import { buildAiGatewayCacheHeaders } from '../ai-gateway/cache-headers'
3
3
  import {
4
4
  OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
5
5
  OPENROUTER_WEB_RESEARCH_MODEL_ID,
@@ -13,9 +13,9 @@ export const researchTopicTool = createDelegatedAgentTool({
13
13
  id: 'researchTopic',
14
14
  description:
15
15
  'Delegate a research task to a dedicated research agent that searches the web, fetches pages, and returns a synthesized markdown report. Call multiple instances in parallel for broad research across different topics.',
16
- model: () => bifrostChatModel(OPENROUTER_WEB_RESEARCH_MODEL_ID),
16
+ model: () => aiGatewayChatModel(OPENROUTER_WEB_RESEARCH_MODEL_ID),
17
17
  providerOptions: OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS,
18
- headers: buildBifrostCacheHeaders('researchTopic'),
18
+ headers: buildAiGatewayCacheHeaders('researchTopic'),
19
19
  instructions: RESEARCHER_PROMPT,
20
20
  tools: { searchWeb: searchWebTool.create(), fetchWebpage: fetchWebpageTool.create() },
21
21
  })
@@ -1,8 +0,0 @@
1
- const BIFROST_CACHE_KEY_HEADER = 'x-bf-cache-key'
2
- const BIFROST_CACHE_TTL_HEADER = 'x-bf-cache-ttl'
3
-
4
- export function buildBifrostCacheHeaders(cacheKey: string, ttl?: string): Record<string, string> {
5
- const headers: Record<string, string> = { [BIFROST_CACHE_KEY_HEADER]: cacheKey }
6
- if (ttl) headers[BIFROST_CACHE_TTL_HEADER] = ttl
7
- return headers
8
- }