@chainfuse/ai-tools 1.0.15 → 1.0.16

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/dist/base.d.mts CHANGED
@@ -3,7 +3,7 @@ export declare class AiBase {
3
3
  protected readonly chalk: import("chalk").ChalkInstance;
4
4
  protected _config: AiConfig;
5
5
  constructor(config: AiConfig);
6
- get config(): Readonly<AiConfig>;
6
+ get config(): Readonly<Omit<AiConfig, 'backgroundContext'>> & Pick<AiConfig, 'backgroundContext'>;
7
7
  protected get gatewayName(): "nocost" | "production-passive-bot" | "production-passive-human" | "production-active-bot" | "production-active-human" | "preview-passive-bot" | "preview-passive-human" | "preview-active-bot" | "preview-active-human";
8
8
  protected get gatewayLog(): boolean;
9
9
  }
package/dist/base.mjs CHANGED
@@ -6,7 +6,10 @@ export class AiBase {
6
6
  this._config = config;
7
7
  }
8
8
  get config() {
9
- return Object.freeze(this._config);
9
+ return {
10
+ ...Object.freeze(this._config),
11
+ backgroundContext: this._config.backgroundContext,
12
+ };
10
13
  }
11
14
  get gatewayName() {
12
15
  if (this.config.billing.noCost) {
@@ -23,22 +23,39 @@ export class AiRawProviders extends AiBase {
23
23
  },
24
24
  };
25
25
  if (logId) {
26
- const updateMetadata = import('@chainfuse/helpers')
27
- .then(({ NetHelpers }) => NetHelpers.cfApi(this.config.gateway.apiToken, { logging: { level: Number(logging) } }))
28
- .then((cf) => cf.aiGateway.logs.edit(this.gatewayName, logId, {
29
- account_id: this.config.gateway.accountId,
30
- metadata: {
31
- ...Object.entries(rawMetadata).reduce((acc, [key, value]) => {
32
- acc[key] = typeof value === 'string' ? value : JSON.stringify(value);
33
- return acc;
34
- }, {}),
35
- },
36
- }));
26
+ const updateMetadata = (() => {
27
+ if ('gateway' in this.config.providers.workersAi && typeof this.config.providers.workersAi.gateway === 'function') {
28
+ return this.config.providers.workersAi
29
+ .gateway(this.gatewayName)
30
+ .patchLog(logId, {
31
+ metadata: {
32
+ ...Object.entries(rawMetadata).reduce((acc, [key, value]) => {
33
+ acc[key] = typeof value === 'string' ? value : JSON.stringify(value);
34
+ return acc;
35
+ }, {}),
36
+ },
37
+ })
38
+ .catch((error) => console.warn('Not updating gateway log', error));
39
+ }
40
+ else {
41
+ return import('@chainfuse/helpers')
42
+ .then(({ NetHelpers }) => NetHelpers.cfApi(this.config.gateway.apiToken, { logging: { level: Number(logging) } }))
43
+ .then((cf) => cf.aiGateway.logs.edit(this.gatewayName, logId, {
44
+ account_id: this.config.gateway.accountId,
45
+ metadata: {
46
+ ...Object.entries(rawMetadata).reduce((acc, [key, value]) => {
47
+ acc[key] = typeof value === 'string' ? value : JSON.stringify(value);
48
+ return acc;
49
+ }, {}),
50
+ },
51
+ }).catch((error) => console.warn('Not updating gateway log', error)));
52
+ }
53
+ })();
37
54
  if (this.config.backgroundContext) {
38
55
  this.config.backgroundContext.waitUntil(updateMetadata);
39
56
  }
40
57
  else {
41
- await updateMetadata.catch((error) => console.warn('Not updating gateway log', error));
58
+ await updateMetadata;
42
59
  }
43
60
  }
44
61
  else {
@@ -99,31 +116,36 @@ export class AiRawProviders extends AiBase {
99
116
  },
100
117
  }));
101
118
  }
102
- azOpenai(args) {
103
- return import('@ai-sdk/azure').then(async ({ createAzure }) => createAzure({
119
+ async azOpenai(args) {
120
+ const metadataHeader = {
121
+ dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
122
+ ...(args.groupBillingId && { groupBillingId: (await BufferHelpers.uuidConvert(args.groupBillingId)).utf8 }),
123
+ // Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
124
+ idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid7()).utf8.slice(0, 23),
125
+ executor: JSON.stringify(args.executor),
126
+ // @ts-expect-error server info gets added in afterwards
127
+ };
128
+ return import('@ai-sdk/azure').then(({ createAzure }) => createAzure({
104
129
  apiKey: 'apikey-placeholder',
130
+ /**
131
+ * @link https://learn.microsoft.com/en-us/azure/ai-foundry/openai/api-version-lifecycle?view=foundry-classic
132
+ * @link https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#api-specs
133
+ * From the table, pick the `Latest GA release` for `Data plane - inference`
134
+ */
135
+ apiVersion: '2025-04-01-preview',
105
136
  useDeploymentBasedUrls: true,
106
137
  baseURL: new URL(['v1', this.config.gateway.accountId, this.gatewayName, 'azure-openai', 'server-placeholder'].join('/'), 'https://gateway.ai.cloudflare.com').toString(),
107
138
  headers: {
108
139
  'cf-aig-authorization': `Bearer ${this.config.gateway.apiToken}`,
109
- // ...(cost && { 'cf-aig-custom-cost': JSON.stringify({ per_token_in: cost.inputTokenCost ?? undefined, per_token_out: cost.outputTokenCost ?? undefined }) }),
110
- 'cf-aig-metadata': JSON.stringify({
111
- dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
112
- ...(args.groupBillingId && { groupBillingId: (await BufferHelpers.uuidConvert(args.groupBillingId)).utf8 }),
113
- // Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
114
- idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid7()).utf8.slice(0, 23),
115
- executor: JSON.stringify(args.executor),
116
- // @ts-expect-error server info gets added in afterwards
117
- }),
140
+ 'cf-aig-metadata': JSON.stringify(metadataHeader),
118
141
  ...(args.cache && { 'cf-aig-cache-ttl': (typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache).toString() }),
119
142
  ...(args.skipCache && { 'cf-aig-skip-cache': 'true' }),
120
143
  },
121
144
  fetch: (input, rawInit) => Promise.all([import('../serverSelector.mjs'), import('@chainfuse/types/ai-tools/azure/catalog')])
122
145
  .then(([{ ServerSelector }, { azureCatalog }]) => new ServerSelector(this.config).closestServers(azureCatalog))
123
- .then(async (filteredServers) => {
146
+ .then(async (closestServers) => {
124
147
  const startRoundTrip = performance.now();
125
148
  const headers = new Headers(rawInit?.headers);
126
- const metadataHeader = JSON.parse(headers.get('cf-aig-metadata'));
127
149
  // Calculate the idempotencyId if it doesn't exist yet
128
150
  if (metadataHeader.idempotencyId.split('-').length === 4) {
129
151
  metadataHeader.idempotencyId = `${metadataHeader.idempotencyId}-${(await CryptoHelpers.getHash('SHA-256', await new Request(input, rawInit).arrayBuffer())).slice(0, 12)}`;
@@ -148,6 +170,14 @@ export class AiRawProviders extends AiBase {
148
170
  catch (error) {
149
171
  fallbackedQuery = rawInit?.body;
150
172
  }
173
+ const modelName = fallbackedEndpointParts[1];
174
+ const filteredServers =
175
+ // Check language models
176
+ closestServers.filter((server) => server.languageModelAvailability.some((model) => model.name === modelName) ??
177
+ // Check image models
178
+ server.imageModelAvailability.some((model) => model.name === modelName) ??
179
+ // Check embedding models
180
+ server.textEmbeddingModelAvailability.some((model) => model.name === modelName));
151
181
  // Build universal gateway request
152
182
  const fallbackedBody = await Promise.all([import('haversine-distance'), import("../serverSelector.mjs")]).then(([{ default: haversine }, { ServerSelector }]) => Promise.all(filteredServers.map(async (server) => {
153
183
  const fallbackedHeaders = {
@@ -167,7 +197,6 @@ export class AiRawProviders extends AiBase {
167
197
  }),
168
198
  }),
169
199
  };
170
- const modelName = fallbackedEndpointParts[0];
171
200
  const languageModel = server.languageModelAvailability.find((model) => model.name === modelName);
172
201
  if (languageModel && ('inputTokenCost' in languageModel || 'outputTokenCost' in languageModel)) {
173
202
  fallbackedHeaders['cf-aig-custom-cost'] = {
@@ -190,7 +219,31 @@ export class AiRawProviders extends AiBase {
190
219
  })));
191
220
  if (args.logging ?? this.gatewayLog)
192
221
  console.info(new Date().toISOString(), this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), this.chalk.magenta(rawInit?.method), this.chalk.magenta(new URL(new Request(input).url).pathname));
193
- return fetch(new URL(['v1', this.config.gateway.accountId, this.gatewayName].join('/'), 'https://gateway.ai.cloudflare.com'), { ...rawInit, headers, body: JSON.stringify(fallbackedBody) }).then(async (response) => {
222
+ return (() => {
223
+ if ('gateway' in this.config.providers.workersAi && typeof this.config.providers.workersAi.gateway === 'function') {
224
+ return this.config.providers.workersAi.gateway(this.gatewayName).run(fallbackedBody, {
225
+ extraHeaders: (() => {
226
+ // Prevent duplicates
227
+ headers.delete('cf-aig-authorization');
228
+ headers.delete('cf-aig-metadata');
229
+ headers.delete('cf-aig-cache-ttl');
230
+ headers.delete('cf-aig-skip-cache');
231
+ return headers;
232
+ })(),
233
+ gateway: {
234
+ id: this.gatewayName,
235
+ eventId: metadataHeader.idempotencyId,
236
+ metadata: metadataHeader,
237
+ ...(args.cache && { cacheTtl: typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache }),
238
+ ...(args.skipCache && { skipCache: true }),
239
+ },
240
+ });
241
+ }
242
+ else {
243
+ return fetch(new URL(['v1', this.config.gateway.accountId, this.gatewayName].join('/'), 'https://gateway.ai.cloudflare.com'), { ...rawInit, headers, body: JSON.stringify(fallbackedBody) });
244
+ }
245
+ })().then(async (_response) => {
246
+ const response = _response;
194
247
  // Inject it to have it available for retries
195
248
  const mutableHeaders = new Headers(response.headers);
196
249
  // Carry down
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chainfuse/ai-tools",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "",
5
5
  "author": "ChainFuse",
6
6
  "homepage": "https://github.com/ChainFuse/packages/tree/main/packages/ai-tools#readme",
@@ -49,13 +49,13 @@
49
49
  "prettier": "@demosjarco/prettier-config",
50
50
  "dependencies": {
51
51
  "@ai-sdk/anthropic": "^2.0.49",
52
- "@ai-sdk/azure": "^2.0.74",
52
+ "@ai-sdk/azure": "^2.0.75",
53
53
  "@ai-sdk/google": "^2.0.43",
54
54
  "@ai-sdk/openai": "^2.0.71",
55
55
  "@ai-sdk/openai-compatible": "^1.0.27",
56
56
  "@ai-sdk/provider": "^2.0.0",
57
- "@chainfuse/helpers": "^4.2.5",
58
- "@chainfuse/types": "^4.1.5",
57
+ "@chainfuse/helpers": "^4.2.6",
58
+ "@chainfuse/types": "^4.1.6",
59
59
  "ai": "^5.0.102",
60
60
  "chalk": "^5.6.2",
61
61
  "haversine-distance": "^1.2.4",
@@ -63,8 +63,8 @@
63
63
  "zod": "^4.1.13"
64
64
  },
65
65
  "devDependencies": {
66
- "@cloudflare/workers-types": "^4.20251125.0",
66
+ "@cloudflare/workers-types": "^4.20251127.0",
67
67
  "openai": "^6.9.1"
68
68
  },
69
- "gitHead": "ad1e1b8662a1792b4e39dc02fba265d07bdaaf8b"
69
+ "gitHead": "a7ba85aa01dd1c54356ef54b1af743cd9f6a767e"
70
70
  }