@chainfuse/ai-tools 0.7.1 → 0.9.0
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/models.d.mts +2 -2
- package/dist/models.mjs +5 -5
- package/dist/providers/customProviders.d.mts +2 -2
- package/dist/providers/customProviders.mjs +23 -9
- package/dist/providers/rawProviders.d.mts +5 -3
- package/dist/providers/rawProviders.mjs +73 -103
- package/dist/registry.mjs +5 -7
- package/dist/serverSelector.d.mts +17 -0
- package/dist/serverSelector.mjs +151 -0
- package/dist/types.d.mts +18 -14
- package/package.json +11 -11
- package/dist/serverSelector/azure.d.mts +0 -5
- package/dist/serverSelector/azure.mjs +0 -284
- package/dist/serverSelector/base.d.mts +0 -22
- package/dist/serverSelector/base.mjs +0 -137
- package/dist/serverSelector/types.d.mts +0 -10
- package/dist/serverSelector/types.mjs +0 -1
package/dist/models.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LanguageModelValues, TextEmbeddingModelValues } from '@chainfuse/types';
|
|
2
|
-
import {
|
|
2
|
+
import type { embed, embedMany, wrapLanguageModel } from 'ai';
|
|
3
3
|
import { AiBase } from './base.mjs';
|
|
4
|
-
import { AiRegistry } from './registry.mjs';
|
|
4
|
+
import type { AiRegistry } from './registry.mjs';
|
|
5
5
|
import type { AiRequestConfig } from './types.mjs';
|
|
6
6
|
type ProvidersReturnType = Awaited<ReturnType<AiRegistry['providers']>>;
|
|
7
7
|
type ValidProviders = keyof ProvidersReturnType;
|
package/dist/models.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { extractReasoningMiddleware, wrapLanguageModel } from 'ai';
|
|
2
1
|
import { AiBase } from './base.mjs';
|
|
3
|
-
import { AiRegistry } from './registry.mjs';
|
|
4
2
|
export class AiModel extends AiBase {
|
|
5
3
|
wrappedLanguageModel(args, modelOrProvider, model) {
|
|
6
|
-
return
|
|
4
|
+
return import('./registry.mjs')
|
|
5
|
+
.then(({ AiRegistry }) => new AiRegistry(this.config).registry(args))
|
|
6
|
+
.then((registry) => import('ai').then(({ extractReasoningMiddleware, wrapLanguageModel }) => wrapLanguageModel({
|
|
7
7
|
model: registry.languageModel(model ? `${modelOrProvider}:${model}` : modelOrProvider),
|
|
8
8
|
middleware: [extractReasoningMiddleware({ tagName: 'think' }), this.middleware],
|
|
9
|
-
}));
|
|
9
|
+
})));
|
|
10
10
|
}
|
|
11
11
|
wrappedTextEmbeddingModel(args, modelOrProvider, model) {
|
|
12
|
-
return new AiRegistry(this.config).registry(args).then((registry) => registry.textEmbeddingModel(model ? `${modelOrProvider}:${model}` : modelOrProvider));
|
|
12
|
+
return import('./registry.mjs').then(({ AiRegistry }) => new AiRegistry(this.config).registry(args)).then((registry) => registry.textEmbeddingModel(model ? `${modelOrProvider}:${model}` : modelOrProvider));
|
|
13
13
|
}
|
|
14
14
|
get middleware() {
|
|
15
15
|
return {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { GoogleGenerativeAIProvider } from '@ai-sdk/google';
|
|
2
2
|
import type { OpenAICompatibleProvider } from '@ai-sdk/openai-compatible';
|
|
3
3
|
import { AiBase } from '../base.mjs';
|
|
4
|
-
import type { AiRequestConfig } from '../types.mjs';
|
|
4
|
+
import type { AiRequestConfig, AzureServers } from '../types.mjs';
|
|
5
5
|
import type { AzureOpenAIProvider, WorkersAIProvider } from './types.mjs';
|
|
6
6
|
export declare class AiCustomProviders extends AiBase {
|
|
7
7
|
oaiOpenai(args: AiRequestConfig): Promise<import("@ai-sdk/openai").OpenAIProvider>;
|
|
8
|
-
azOpenai(args: AiRequestConfig,
|
|
8
|
+
azOpenai(args: AiRequestConfig, filteredServers?: AzureServers): Promise<AzureOpenAIProvider>;
|
|
9
9
|
anthropic(args: AiRequestConfig): Promise<import("@ai-sdk/anthropic").AnthropicProvider>;
|
|
10
10
|
private static workersAiIsRest;
|
|
11
11
|
cfWorkersAi(args: AiRequestConfig): Promise<WorkersAIProvider | OpenAICompatibleProvider<"@cf/qwen/qwen1.5-0.5b-chat" | "@cf/google/gemma-2b-it-lora" | "@hf/nexusflow/starling-lm-7b-beta" | "@cf/meta/llama-3-8b-instruct" | "@cf/meta/llama-3.2-3b-instruct" | "@hf/thebloke/llamaguard-7b-awq" | "@hf/thebloke/neural-chat-7b-v3-1-awq" | "@cf/meta/llama-guard-3-8b" | "@cf/meta/llama-2-7b-chat-fp16" | "@cf/mistral/mistral-7b-instruct-v0.1" | "@cf/mistral/mistral-7b-instruct-v0.2-lora" | "@cf/tinyllama/tinyllama-1.1b-chat-v1.0" | "@hf/mistral/mistral-7b-instruct-v0.2" | "@cf/fblgit/una-cybertron-7b-v2-bf16" | "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b" | "@cf/thebloke/discolm-german-7b-v1-awq" | "@cf/meta/llama-2-7b-chat-int8" | "@cf/meta/llama-3.1-8b-instruct-fp8" | "@hf/thebloke/mistral-7b-instruct-v0.1-awq" | "@cf/qwen/qwen1.5-7b-chat-awq" | "@cf/meta/llama-3.2-1b-instruct" | "@hf/thebloke/llama-2-13b-chat-awq" | "@hf/thebloke/deepseek-coder-6.7b-base-awq" | "@cf/meta-llama/llama-2-7b-chat-hf-lora" | "@cf/meta/llama-3.3-70b-instruct-fp8-fast" | "@hf/thebloke/openhermes-2.5-mistral-7b-awq" | "@hf/thebloke/deepseek-coder-6.7b-instruct-awq" | "@cf/deepseek-ai/deepseek-math-7b-instruct" | "@cf/tiiuae/falcon-7b-instruct" | "@hf/nousresearch/hermes-2-pro-mistral-7b" | "@cf/meta/llama-3.1-8b-instruct" | "@cf/meta/llama-3.1-8b-instruct-awq" | "@hf/thebloke/zephyr-7b-beta-awq" | "@cf/google/gemma-7b-it-lora" | "@cf/qwen/qwen1.5-1.8b-chat" | "@cf/meta/llama-3-8b-instruct-awq" | "@cf/meta/llama-3.2-11b-vision-instruct" | "@cf/defog/sqlcoder-7b-2" | "@cf/microsoft/phi-2" | "@hf/meta-llama/meta-llama-3-8b-instruct" | "@hf/google/gemma-7b-it" | "@cf/qwen/qwen1.5-14b-chat-awq" | "@cf/openchat/openchat-3.5-0106", "@cf/qwen/qwen1.5-0.5b-chat" | "@cf/google/gemma-2b-it-lora" | "@hf/nexusflow/starling-lm-7b-beta" | "@cf/meta/llama-3-8b-instruct" | "@cf/meta/llama-3.2-3b-instruct" | "@hf/thebloke/llamaguard-7b-awq" | "@hf/thebloke/neural-chat-7b-v3-1-awq" | "@cf/meta/llama-guard-3-8b" | "@cf/meta/llama-2-7b-chat-fp16" | "@cf/mistral/mistral-7b-instruct-v0.1" | "@cf/mistral/mistral-7b-instruct-v0.2-lora" | "@cf/tinyllama/tinyllama-1.1b-chat-v1.0" | "@hf/mistral/mistral-7b-instruct-v0.2" | "@cf/fblgit/una-cybertron-7b-v2-bf16" | "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b" | "@cf/thebloke/discolm-german-7b-v1-awq" | "@cf/meta/llama-2-7b-chat-int8" | "@cf/meta/llama-3.1-8b-instruct-fp8" | "@hf/thebloke/mistral-7b-instruct-v0.1-awq" | "@cf/qwen/qwen1.5-7b-chat-awq" | "@cf/meta/llama-3.2-1b-instruct" | "@hf/thebloke/llama-2-13b-chat-awq" | "@hf/thebloke/deepseek-coder-6.7b-base-awq" | "@cf/meta-llama/llama-2-7b-chat-hf-lora" | "@cf/meta/llama-3.3-70b-instruct-fp8-fast" | "@hf/thebloke/openhermes-2.5-mistral-7b-awq" | "@hf/thebloke/deepseek-coder-6.7b-instruct-awq" | "@cf/deepseek-ai/deepseek-math-7b-instruct" | "@cf/tiiuae/falcon-7b-instruct" | "@hf/nousresearch/hermes-2-pro-mistral-7b" | "@cf/meta/llama-3.1-8b-instruct" | "@cf/meta/llama-3.1-8b-instruct-awq" | "@hf/thebloke/zephyr-7b-beta-awq" | "@cf/google/gemma-7b-it-lora" | "@cf/qwen/qwen1.5-1.8b-chat" | "@cf/meta/llama-3-8b-instruct-awq" | "@cf/meta/llama-3.2-11b-vision-instruct" | "@cf/defog/sqlcoder-7b-2" | "@cf/microsoft/phi-2" | "@hf/meta-llama/meta-llama-3-8b-instruct" | "@hf/google/gemma-7b-it" | "@cf/qwen/qwen1.5-14b-chat-awq" | "@cf/openchat/openchat-3.5-0106", "@cf/baai/bge-m3" | "@cf/baai/bge-small-en-v1.5" | "@cf/baai/bge-base-en-v1.5" | "@cf/baai/bge-large-en-v1.5">>;
|
|
@@ -3,21 +3,24 @@ import { AiModels, enabledCloudflareLlmProviders } from '@chainfuse/types';
|
|
|
3
3
|
import { APICallError, customProvider, TypeValidationError, wrapLanguageModel } from 'ai';
|
|
4
4
|
import { ZodError } from 'zod';
|
|
5
5
|
import { AiBase } from '../base.mjs';
|
|
6
|
-
import {
|
|
6
|
+
import { ServerSelector } from "../serverSelector.mjs";
|
|
7
7
|
import { AiRawProviders } from './rawProviders.mjs';
|
|
8
8
|
export class AiCustomProviders extends AiBase {
|
|
9
9
|
oaiOpenai(args) {
|
|
10
10
|
return new AiRawProviders(this.config).oaiOpenai(args);
|
|
11
11
|
}
|
|
12
|
-
async azOpenai(args,
|
|
12
|
+
async azOpenai(args, filteredServers) {
|
|
13
|
+
if (!filteredServers)
|
|
14
|
+
filteredServers = await new ServerSelector(this.config).closestServers(await import('@chainfuse/types/ai-tools/catalog/azure').then(({ azureCatalog }) => azureCatalog));
|
|
15
|
+
const [server, ...servers] = filteredServers;
|
|
13
16
|
const raw = new AiRawProviders(this.config);
|
|
14
17
|
return customProvider({
|
|
15
18
|
// @ts-expect-error override for types
|
|
16
19
|
languageModels: await server.languageModelAvailability.reduce(async (accPromise, model) => {
|
|
17
20
|
const acc = await accPromise;
|
|
18
21
|
// @ts-expect-error override for types
|
|
19
|
-
acc[model] = wrapLanguageModel({
|
|
20
|
-
model: (await raw.azOpenai(args, server))(model),
|
|
22
|
+
acc[model.name] = wrapLanguageModel({
|
|
23
|
+
model: (await raw.azOpenai(args, server, 'inputTokenCost' in model || 'outputTokenCost' in model ? { inputTokenCost: model.inputTokenCost, outputTokenCost: model.outputTokenCost } : undefined))(model.name),
|
|
21
24
|
middleware: {
|
|
22
25
|
wrapGenerate: async ({ doGenerate, model, params }) => {
|
|
23
26
|
try {
|
|
@@ -28,23 +31,34 @@ export class AiCustomProviders extends AiBase {
|
|
|
28
31
|
if (APICallError.isInstance(error)) {
|
|
29
32
|
const idempotencyId = new Headers(error.responseHeaders).get('X-Idempotency-Id');
|
|
30
33
|
const lastServer = new URL(error.url).pathname.split('/')[5];
|
|
31
|
-
const compatibleServers = new
|
|
34
|
+
const compatibleServers = await new ServerSelector(this.config).closestServers(await import('@chainfuse/types/ai-tools/catalog/azure').then(({ azureCatalog }) => azureCatalog), model.modelId);
|
|
32
35
|
const lastServerIndex = compatibleServers.findIndex((s) => s.id.toLowerCase() === lastServer.toLowerCase());
|
|
33
|
-
if (args.logging ?? this.config.environment
|
|
36
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
34
37
|
console.error('ai', 'custom provider', this.chalk.rgb(...Helpers.uniqueIdColor(idempotencyId))(`[${idempotencyId}]`), this.chalk.red('FAIL'), compatibleServers[lastServerIndex].id, 'REMAINING', JSON.stringify(compatibleServers.slice(lastServerIndex + 1).map((s) => s.id)));
|
|
35
38
|
// Should retry with the next server
|
|
36
39
|
const leftOverServers = compatibleServers.slice(lastServerIndex + 1);
|
|
37
40
|
const errors = [error];
|
|
38
41
|
for (const nextServer of leftOverServers) {
|
|
39
42
|
try {
|
|
40
|
-
if (args.logging ?? this.config.environment
|
|
43
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
41
44
|
console.error('ai', 'custom provider', this.chalk.rgb(...Helpers.uniqueIdColor(idempotencyId))(`[${idempotencyId}]`), this.chalk.blue('FALLBACK'), nextServer.id, 'REMAINING', JSON.stringify(leftOverServers.slice(leftOverServers.indexOf(nextServer) + 1).map((s) => s.id)));
|
|
42
45
|
// Must be double awaited to prevent a promise from being returned
|
|
43
|
-
return await (await raw.azOpenai({ ...args, idempotencyId }, nextServer
|
|
46
|
+
return await (await raw.azOpenai({ ...args, idempotencyId }, nextServer, (() => {
|
|
47
|
+
const foundModel = server.languageModelAvailability.find((languageModel) => languageModel.name === model.modelId);
|
|
48
|
+
if (foundModel && ('inputTokenCost' in foundModel || 'outputTokenCost' in foundModel)) {
|
|
49
|
+
return {
|
|
50
|
+
inputTokenCost: foundModel.inputTokenCost,
|
|
51
|
+
outputTokenCost: foundModel.outputTokenCost,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
})()))(model.modelId).doGenerate(params);
|
|
44
58
|
}
|
|
45
59
|
catch (nextServerError) {
|
|
46
60
|
if (APICallError.isInstance(nextServerError)) {
|
|
47
|
-
if (args.logging ?? this.config.environment
|
|
61
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
48
62
|
console.error('ai', 'custom provider', this.chalk.rgb(...Helpers.uniqueIdColor(idempotencyId))(`[${idempotencyId}]`), this.chalk.red('FAIL'), nextServer.id, 'REMAINING', JSON.stringify(leftOverServers.slice(leftOverServers.indexOf(nextServer) + 1).map((s) => s.id)));
|
|
49
63
|
errors.push(nextServerError);
|
|
50
64
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { OpenAICompatibleProvider } from '@ai-sdk/openai-compatible';
|
|
2
2
|
import type { cloudflareModelPossibilities } from '@chainfuse/types';
|
|
3
3
|
import { AiBase } from '../base.mjs';
|
|
4
|
-
import type {
|
|
5
|
-
import type { AiRequestConfig } from '../types.mjs';
|
|
4
|
+
import type { AiRequestConfig, Servers } from '../types.mjs';
|
|
6
5
|
import type { WorkersAIProvider } from './types.mts';
|
|
7
6
|
export declare class AiRawProviders extends AiBase {
|
|
8
7
|
private readonly cacheTtl;
|
|
9
8
|
private updateGatewayLog;
|
|
10
9
|
oaiOpenai(args: AiRequestConfig): Promise<import("@ai-sdk/openai").OpenAIProvider>;
|
|
11
|
-
azOpenai(args: AiRequestConfig, server:
|
|
10
|
+
azOpenai(args: AiRequestConfig, server: Servers[number], cost?: {
|
|
11
|
+
inputTokenCost?: number;
|
|
12
|
+
outputTokenCost?: number;
|
|
13
|
+
}): Promise<import("@ai-sdk/azure").AzureOpenAIProvider>;
|
|
12
14
|
anthropic(args: AiRequestConfig): Promise<import("@ai-sdk/anthropic").AnthropicProvider>;
|
|
13
15
|
custom(args: AiRequestConfig): Promise<OpenAICompatibleProvider<string, string, string>>;
|
|
14
16
|
googleAi(args: AiRequestConfig): Promise<import("@ai-sdk/google").GoogleGenerativeAIProvider>;
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import { BufferHelpers, CryptoHelpers,
|
|
2
|
-
import haversine from 'haversine-distance';
|
|
3
|
-
import { z } from 'zod';
|
|
1
|
+
import { BufferHelpers, CryptoHelpers, Helpers } from '@chainfuse/helpers';
|
|
4
2
|
import { AiBase } from '../base.mjs';
|
|
5
3
|
export class AiRawProviders extends AiBase {
|
|
6
4
|
// 2628288 seconds is what cf defines as 1 month in their cache rules
|
|
7
5
|
cacheTtl = 2628288;
|
|
8
6
|
async updateGatewayLog(response, metadataHeader, startRoundTrip, modelTime) {
|
|
9
|
-
const updateMetadata =
|
|
7
|
+
const updateMetadata = import('@chainfuse/helpers')
|
|
8
|
+
.then(({ NetHelpers }) => NetHelpers.cfApi(this.config.gateway.apiToken))
|
|
9
|
+
.then((cf) => cf.aiGateway.logs.edit(this.config.environment, response.headers.get('cf-aig-log-id'), {
|
|
10
10
|
account_id: this.config.gateway.accountId,
|
|
11
11
|
metadata: {
|
|
12
|
-
...Object.entries(
|
|
12
|
+
...Object.entries({
|
|
13
|
+
...metadataHeader,
|
|
14
|
+
serverInfo: {
|
|
15
|
+
...JSON.parse(metadataHeader.serverInfo),
|
|
16
|
+
timing: {
|
|
17
|
+
fromCache: response.headers.get('cf-aig-cache-status')?.toLowerCase() === 'hit',
|
|
18
|
+
totalRoundtripTime: performance.now() - startRoundTrip,
|
|
19
|
+
modelTime,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
}).reduce((acc, [key, value]) => {
|
|
13
23
|
acc[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
14
24
|
return acc;
|
|
15
25
|
}, {}),
|
|
16
|
-
timing: JSON.stringify({
|
|
17
|
-
fromCache: response.headers.get('cf-aig-cache-status')?.toLowerCase() === 'hit',
|
|
18
|
-
totalRoundtripTime: performance.now() - startRoundTrip,
|
|
19
|
-
modelTime,
|
|
20
|
-
}),
|
|
21
26
|
},
|
|
22
27
|
}));
|
|
23
28
|
if (this.config.backgroundContext) {
|
|
@@ -35,21 +40,14 @@ export class AiRawProviders extends AiBase {
|
|
|
35
40
|
headers: {
|
|
36
41
|
'cf-aig-authorization': `Bearer ${this.config.gateway.apiToken}`,
|
|
37
42
|
'cf-aig-metadata': JSON.stringify({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
41
|
-
}),
|
|
42
|
-
executor: JSON.stringify(args.executor),
|
|
43
|
-
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
44
|
-
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
43
|
+
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
44
|
+
messageId: (await BufferHelpers.uuidConvert(args.messageId)).utf8,
|
|
45
45
|
serverInfo: JSON.stringify({
|
|
46
46
|
name: 'openai',
|
|
47
47
|
}),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*/
|
|
52
|
-
timing: JSON.stringify({}),
|
|
48
|
+
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
49
|
+
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
50
|
+
executor: JSON.stringify(args.executor),
|
|
53
51
|
}),
|
|
54
52
|
...(args.cache && { 'cf-aig-cache-ttl': (typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache).toString() }),
|
|
55
53
|
...(args.skipCache && { 'cf-aig-skip-cache': 'true' }),
|
|
@@ -63,10 +61,10 @@ export class AiRawProviders extends AiBase {
|
|
|
63
61
|
metadataHeader.idempotencyId = `${metadataHeader.idempotencyId}-${(await CryptoHelpers.getHash('SHA-256', await new Request(input, rawInit).arrayBuffer())).slice(0, 12)}`;
|
|
64
62
|
headers.set('cf-aig-metadata', JSON.stringify(metadataHeader));
|
|
65
63
|
}
|
|
66
|
-
if (args.logging ?? this.config.environment
|
|
64
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
67
65
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), this.chalk.magenta(rawInit?.method), this.chalk.magenta(new URL(new Request(input).url).pathname));
|
|
68
66
|
return fetch(input, { ...rawInit, headers }).then(async (response) => {
|
|
69
|
-
if (args.logging ?? this.config.environment
|
|
67
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
70
68
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), response.ok ? this.chalk.green(response.status) : this.chalk.red(response.status), response.ok ? this.chalk.green(new URL(response.url).pathname) : this.chalk.red(new URL(response.url).pathname));
|
|
71
69
|
await this.updateGatewayLog(response, metadataHeader, startRoundTrip, response.headers.has('openai-processing-ms') ? parseInt(response.headers.get('openai-processing-ms')) : undefined);
|
|
72
70
|
// Inject it to have it available for retries
|
|
@@ -84,7 +82,7 @@ export class AiRawProviders extends AiBase {
|
|
|
84
82
|
},
|
|
85
83
|
}));
|
|
86
84
|
}
|
|
87
|
-
azOpenai(args, server) {
|
|
85
|
+
azOpenai(args, server, cost) {
|
|
88
86
|
return import('@ai-sdk/azure').then(async ({ createAzure }) => createAzure({
|
|
89
87
|
apiKey: this.config.providers.azureOpenAi.apiTokens[`AZURE_API_KEY_${server.id.toUpperCase().replaceAll('-', '_')}`],
|
|
90
88
|
/**
|
|
@@ -95,26 +93,23 @@ export class AiRawProviders extends AiBase {
|
|
|
95
93
|
baseURL: new URL(['v1', this.config.gateway.accountId, this.config.environment, 'azure-openai', server.id.toLowerCase()].join('/'), 'https://gateway.ai.cloudflare.com').toString(),
|
|
96
94
|
headers: {
|
|
97
95
|
'cf-aig-authorization': `Bearer ${this.config.gateway.apiToken}`,
|
|
96
|
+
...(cost && { 'cf-aig-custom-cost': JSON.stringify({ per_token_in: cost.inputTokenCost ?? undefined, per_token_out: cost.outputTokenCost ?? undefined }) }),
|
|
98
97
|
'cf-aig-metadata': JSON.stringify({
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
102
|
-
}),
|
|
103
|
-
executor: JSON.stringify(args.executor),
|
|
104
|
-
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
105
|
-
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
98
|
+
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
99
|
+
messageId: (await BufferHelpers.uuidConvert(args.messageId)).utf8,
|
|
106
100
|
serverInfo: JSON.stringify({
|
|
107
101
|
name: `azure-${server.id}`,
|
|
108
|
-
distance: haversine({
|
|
109
|
-
lat: Helpers.precisionFloat(
|
|
110
|
-
lon: Helpers.precisionFloat(
|
|
111
|
-
},
|
|
102
|
+
distance: await import('haversine-distance').then(async ({ default: haversine }) => haversine(await import("../serverSelector.mjs").then(({ ServerSelector }) => new ServerSelector(this.config).determineLocation().then(({ coordinate }) => ({
|
|
103
|
+
lat: Helpers.precisionFloat(coordinate.lat),
|
|
104
|
+
lon: Helpers.precisionFloat(coordinate.lon),
|
|
105
|
+
}))), {
|
|
106
|
+
lat: Helpers.precisionFloat(server.coordinate.lat),
|
|
107
|
+
lon: Helpers.precisionFloat(server.coordinate.lon),
|
|
108
|
+
})),
|
|
112
109
|
}),
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
*/
|
|
117
|
-
timing: JSON.stringify({}),
|
|
110
|
+
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
111
|
+
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
112
|
+
executor: JSON.stringify(args.executor),
|
|
118
113
|
}),
|
|
119
114
|
...(args.cache && { 'cf-aig-cache-ttl': (typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache).toString() }),
|
|
120
115
|
...(args.skipCache && { 'cf-aig-skip-cache': 'true' }),
|
|
@@ -127,10 +122,10 @@ export class AiRawProviders extends AiBase {
|
|
|
127
122
|
metadataHeader.idempotencyId = `${metadataHeader.idempotencyId}-${(await CryptoHelpers.getHash('SHA-256', await new Request(input, rawInit).arrayBuffer())).slice(0, 12)}`;
|
|
128
123
|
headers.set('cf-aig-metadata', JSON.stringify(metadataHeader));
|
|
129
124
|
}
|
|
130
|
-
if (args.logging ?? this.config.environment
|
|
125
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
131
126
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), this.chalk.magenta(rawInit?.method), this.chalk.magenta(new URL(new Request(input).url).pathname));
|
|
132
127
|
return fetch(input, { ...rawInit, headers }).then(async (response) => {
|
|
133
|
-
if (args.logging ?? this.config.environment
|
|
128
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
134
129
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), response.ok ? this.chalk.green(response.status) : this.chalk.red(response.status), response.ok ? this.chalk.green(new URL(response.url).pathname) : this.chalk.red(new URL(response.url).pathname));
|
|
135
130
|
await this.updateGatewayLog(response, metadataHeader, startRoundTrip, response.headers.has('x-envoy-upstream-service-time') ? parseInt(response.headers.get('x-envoy-upstream-service-time')) : undefined);
|
|
136
131
|
// Inject it to have it available for retries
|
|
@@ -155,21 +150,14 @@ export class AiRawProviders extends AiBase {
|
|
|
155
150
|
headers: {
|
|
156
151
|
'cf-aig-authorization': `Bearer ${this.config.gateway.apiToken}`,
|
|
157
152
|
'cf-aig-metadata': JSON.stringify({
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
161
|
-
}),
|
|
162
|
-
executor: JSON.stringify(args.executor),
|
|
163
|
-
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
164
|
-
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
153
|
+
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
154
|
+
messageId: (await BufferHelpers.uuidConvert(args.messageId)).utf8,
|
|
165
155
|
serverInfo: JSON.stringify({
|
|
166
156
|
name: 'anthropic',
|
|
167
157
|
}),
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
*/
|
|
172
|
-
timing: JSON.stringify({}),
|
|
158
|
+
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
159
|
+
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
160
|
+
executor: JSON.stringify(args.executor),
|
|
173
161
|
}),
|
|
174
162
|
...(args.cache && { 'cf-aig-cache-ttl': (typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache).toString() }),
|
|
175
163
|
...(args.skipCache && { 'cf-aig-skip-cache': 'true' }),
|
|
@@ -182,10 +170,10 @@ export class AiRawProviders extends AiBase {
|
|
|
182
170
|
metadataHeader.idempotencyId = `${metadataHeader.idempotencyId}-${(await CryptoHelpers.getHash('SHA-256', await new Request(input, rawInit).arrayBuffer())).slice(0, 12)}`;
|
|
183
171
|
headers.set('cf-aig-metadata', JSON.stringify(metadataHeader));
|
|
184
172
|
}
|
|
185
|
-
if (args.logging ?? this.config.environment
|
|
173
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
186
174
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), this.chalk.magenta(rawInit?.method), this.chalk.magenta(new URL(new Request(input).url).pathname));
|
|
187
175
|
return fetch(input, { ...rawInit, headers }).then(async (response) => {
|
|
188
|
-
if (args.logging ?? this.config.environment
|
|
176
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
189
177
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), response.ok ? this.chalk.green(response.status) : this.chalk.red(response.status), response.ok ? this.chalk.green(new URL(response.url).pathname) : this.chalk.red(new URL(response.url).pathname));
|
|
190
178
|
await this.updateGatewayLog(response, metadataHeader, startRoundTrip);
|
|
191
179
|
// Inject it to have it available for retries
|
|
@@ -205,8 +193,10 @@ export class AiRawProviders extends AiBase {
|
|
|
205
193
|
}
|
|
206
194
|
custom(args) {
|
|
207
195
|
if (this.config.providers.custom?.url) {
|
|
196
|
+
return import('zod')
|
|
197
|
+
.then(({ z }) =>
|
|
208
198
|
// Verify that the custom provider url is a valid URL
|
|
209
|
-
|
|
199
|
+
z
|
|
210
200
|
.string()
|
|
211
201
|
.trim()
|
|
212
202
|
.url()
|
|
@@ -219,13 +209,14 @@ export class AiRawProviders extends AiBase {
|
|
|
219
209
|
.trim()
|
|
220
210
|
.ip()
|
|
221
211
|
.safeParseAsync(customProviderUrl.hostname)
|
|
222
|
-
.then(
|
|
212
|
+
.then(({ success }) => ({ customProviderUrl, success }))))
|
|
213
|
+
.then(async ({ customProviderUrl, success }) => {
|
|
223
214
|
if (success) {
|
|
224
215
|
throw new Error('IP custom providers not allowed');
|
|
225
216
|
}
|
|
226
217
|
else {
|
|
227
218
|
// Run domain through ZT policies
|
|
228
|
-
const doh = new DnsHelpers(new URL('dns-query', `https://${this.config.providers.custom?.dohId}.cloudflare-gateway.com`));
|
|
219
|
+
const doh = await import('@chainfuse/helpers').then(({ DnsHelpers }) => new DnsHelpers(new URL('dns-query', `https://${this.config.providers.custom?.dohId}.cloudflare-gateway.com`)));
|
|
229
220
|
const aCheck = doh.query(customProviderUrl.hostname, 'A', undefined, undefined, 2 * 1000);
|
|
230
221
|
const aaaaCheck = doh.query(customProviderUrl.hostname, 'AAAA', undefined, undefined, 2 * 1000);
|
|
231
222
|
return Promise.allSettled([aCheck, aaaaCheck]).then((checks) => {
|
|
@@ -265,10 +256,10 @@ export class AiRawProviders extends AiBase {
|
|
|
265
256
|
idempotencyId = `${idempotencyId}-${(await CryptoHelpers.getHash('SHA-256', await new Request(input, rawInit).arrayBuffer())).slice(0, 12)}`;
|
|
266
257
|
headers.set('X-Idempotency-Id', idempotencyId);
|
|
267
258
|
}
|
|
268
|
-
if (args.logging ?? this.config.environment
|
|
259
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
269
260
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(idempotencyId))(`[${idempotencyId}]`), this.chalk.magenta(rawInit?.method), this.chalk.magenta(new URL(new Request(input).url).pathname));
|
|
270
261
|
return fetch(input, { ...rawInit, headers }).then(async (response) => {
|
|
271
|
-
if (args.logging ?? this.config.environment
|
|
262
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
272
263
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(idempotencyId))(`[${idempotencyId}]`), response.ok ? this.chalk.green(response.status) : this.chalk.red(response.status), response.ok ? this.chalk.green(new URL(response.url).pathname) : this.chalk.red(new URL(response.url).pathname));
|
|
273
264
|
// Inject it to have it available for retries
|
|
274
265
|
const mutableHeaders = new Headers(response.headers);
|
|
@@ -290,7 +281,7 @@ export class AiRawProviders extends AiBase {
|
|
|
290
281
|
}
|
|
291
282
|
});
|
|
292
283
|
}
|
|
293
|
-
})
|
|
284
|
+
})
|
|
294
285
|
.catch(() => {
|
|
295
286
|
throw new Error('Invalid custom provider url');
|
|
296
287
|
});
|
|
@@ -319,21 +310,14 @@ export class AiRawProviders extends AiBase {
|
|
|
319
310
|
headers: {
|
|
320
311
|
'cf-aig-authorization': `Bearer ${this.config.gateway.apiToken}`,
|
|
321
312
|
'cf-aig-metadata': JSON.stringify({
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
325
|
-
}),
|
|
326
|
-
executor: JSON.stringify(args.executor),
|
|
327
|
-
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
328
|
-
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
313
|
+
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
314
|
+
messageId: (await BufferHelpers.uuidConvert(args.messageId)).utf8,
|
|
329
315
|
serverInfo: JSON.stringify({
|
|
330
316
|
name: 'googleai',
|
|
331
317
|
}),
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
*/
|
|
336
|
-
timing: JSON.stringify({}),
|
|
318
|
+
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
319
|
+
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
320
|
+
executor: JSON.stringify(args.executor),
|
|
337
321
|
}),
|
|
338
322
|
...(args.cache && { 'cf-aig-cache-ttl': (typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache).toString() }),
|
|
339
323
|
...(args.skipCache && { 'cf-aig-skip-cache': 'true' }),
|
|
@@ -346,10 +330,10 @@ export class AiRawProviders extends AiBase {
|
|
|
346
330
|
metadataHeader.idempotencyId = `${metadataHeader.idempotencyId}-${(await CryptoHelpers.getHash('SHA-256', await new Request(input, rawInit).arrayBuffer())).slice(0, 12)}`;
|
|
347
331
|
headers.set('cf-aig-metadata', JSON.stringify(metadataHeader));
|
|
348
332
|
}
|
|
349
|
-
if (args.logging ?? this.config.environment
|
|
333
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
350
334
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), this.chalk.magenta(rawInit?.method), this.chalk.magenta(new URL(new Request(input).url).pathname));
|
|
351
335
|
return fetch(input, { ...rawInit, headers }).then(async (response) => {
|
|
352
|
-
if (args.logging ?? this.config.environment
|
|
336
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
353
337
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), response.ok ? this.chalk.green(response.status) : this.chalk.red(response.status), response.ok ? this.chalk.green(new URL(response.url).pathname) : this.chalk.red(new URL(response.url).pathname));
|
|
354
338
|
await this.updateGatewayLog(response, metadataHeader, startRoundTrip);
|
|
355
339
|
// Inject it to have it available for retries
|
|
@@ -374,21 +358,14 @@ export class AiRawProviders extends AiBase {
|
|
|
374
358
|
headers: {
|
|
375
359
|
'cf-aig-authorization': `Bearer ${this.config.gateway.apiToken}`,
|
|
376
360
|
'cf-aig-metadata': JSON.stringify({
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
380
|
-
}),
|
|
381
|
-
executor: JSON.stringify(args.executor),
|
|
382
|
-
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
383
|
-
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
361
|
+
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
362
|
+
messageId: (await BufferHelpers.uuidConvert(args.messageId)).utf8,
|
|
384
363
|
serverInfo: JSON.stringify({
|
|
385
364
|
name: 'cloudflare',
|
|
386
365
|
}),
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
*/
|
|
391
|
-
timing: JSON.stringify({}),
|
|
366
|
+
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
367
|
+
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
368
|
+
executor: JSON.stringify(args.executor),
|
|
392
369
|
}),
|
|
393
370
|
...(args.cache && { 'cf-aig-cache-ttl': (typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache).toString() }),
|
|
394
371
|
...(args.skipCache && { 'cf-aig-skip-cache': 'true' }),
|
|
@@ -402,10 +379,10 @@ export class AiRawProviders extends AiBase {
|
|
|
402
379
|
metadataHeader.idempotencyId = `${metadataHeader.idempotencyId}-${(await CryptoHelpers.getHash('SHA-256', await new Request(input, rawInit).arrayBuffer())).slice(0, 12)}`;
|
|
403
380
|
headers.set('cf-aig-metadata', JSON.stringify(metadataHeader));
|
|
404
381
|
}
|
|
405
|
-
if (args.logging ?? this.config.environment
|
|
382
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
406
383
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), this.chalk.magenta(rawInit?.method), this.chalk.magenta(new URL(new Request(input).url).pathname));
|
|
407
384
|
return fetch(input, { ...rawInit, headers }).then(async (response) => {
|
|
408
|
-
if (args.logging ?? this.config.environment
|
|
385
|
+
if (args.logging ?? !this.config.environment.startsWith('production'))
|
|
409
386
|
console.info('ai', 'raw provider', this.chalk.rgb(...Helpers.uniqueIdColor(metadataHeader.idempotencyId))(`[${metadataHeader.idempotencyId}]`), response.ok ? this.chalk.green(response.status) : this.chalk.red(response.status), response.ok ? this.chalk.green(new URL(response.url).pathname) : this.chalk.red(new URL(response.url).pathname));
|
|
410
387
|
await this.updateGatewayLog(response, metadataHeader, startRoundTrip);
|
|
411
388
|
// Inject it to have it available for retries
|
|
@@ -431,21 +408,14 @@ export class AiRawProviders extends AiBase {
|
|
|
431
408
|
...(args.cache && { cacheTtl: typeof args.cache === 'boolean' ? (args.cache ? this.cacheTtl : 0) : args.cache }),
|
|
432
409
|
...(args.skipCache && { skipCache: true }),
|
|
433
410
|
metadata: {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
437
|
-
}),
|
|
438
|
-
executor: JSON.stringify(args.executor),
|
|
439
|
-
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
440
|
-
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
411
|
+
dataspaceId: (await BufferHelpers.uuidConvert(args.dataspaceId)).utf8,
|
|
412
|
+
messageId: (await BufferHelpers.uuidConvert(args.messageId)).utf8,
|
|
441
413
|
serverInfo: JSON.stringify({
|
|
442
414
|
name: 'cloudflare',
|
|
443
415
|
}),
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
*/
|
|
448
|
-
timing: JSON.stringify({}),
|
|
416
|
+
// Generate incomplete id because we don't have the body to hash yet. Fill it in in the `fetch()`
|
|
417
|
+
idempotencyId: args.idempotencyId ?? (await BufferHelpers.generateUuid).utf8.slice(0, 23),
|
|
418
|
+
executor: JSON.stringify(args.executor),
|
|
449
419
|
},
|
|
450
420
|
},
|
|
451
421
|
}));
|
package/dist/registry.mjs
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { experimental_createProviderRegistry as createProviderRegistry } from 'ai';
|
|
2
1
|
import { AiBase } from './base.mjs';
|
|
3
|
-
import { AiCustomProviders } from './providers/customProviders.mjs';
|
|
4
2
|
export class AiRegistry extends AiBase {
|
|
5
|
-
|
|
6
|
-
return Object.freeze({
|
|
3
|
+
providers(args) {
|
|
4
|
+
return import('./providers/customProviders.mjs').then(async ({ AiCustomProviders }) => Object.freeze({
|
|
7
5
|
openai: await new AiCustomProviders(this.config).oaiOpenai(args),
|
|
8
6
|
azure: await new AiCustomProviders(this.config).azOpenai(args),
|
|
9
7
|
anthropic: await new AiCustomProviders(this.config).anthropic(args),
|
|
10
8
|
custom: await new AiCustomProviders(this.config).custom(args),
|
|
11
9
|
'google.generative-ai': await new AiCustomProviders(this.config).googleAi(args),
|
|
12
10
|
workersai: await new AiCustomProviders(this.config).cfWorkersAi(args),
|
|
13
|
-
});
|
|
11
|
+
}));
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
return createProviderRegistry(await this.providers(args));
|
|
13
|
+
registry(args) {
|
|
14
|
+
return import('ai').then(async ({ experimental_createProviderRegistry: createProviderRegistry }) => createProviderRegistry(await this.providers(args)));
|
|
17
15
|
}
|
|
18
16
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Coordinate } from '@chainfuse/types';
|
|
2
|
+
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types/experimental';
|
|
3
|
+
import { AiBase } from './base.mts';
|
|
4
|
+
import type { PrivacyRegion, Servers } from './types.mjs';
|
|
5
|
+
export declare class ServerSelector extends AiBase {
|
|
6
|
+
static determinePrivacyRegion(country?: IncomingRequestCfProperties['country'], continent?: IncomingRequestCfProperties['continent']): ("APPs" | "LGPD" | "GDPR" | "PIPEDA" | "APPI" | "UK-GDPR" | "PIPA" | "PoPIA" | "revFADP" | "NPDA" | "PDP")[];
|
|
7
|
+
determineLocation(geoRouting?: {
|
|
8
|
+
userCoordinate?: Coordinate;
|
|
9
|
+
country?: IncomingRequestCfProperties["country"];
|
|
10
|
+
continent?: IncomingRequestCfProperties["continent"];
|
|
11
|
+
} | undefined): Promise<{
|
|
12
|
+
coordinate: Coordinate;
|
|
13
|
+
country: IncomingRequestCfProperties['country'];
|
|
14
|
+
continent: IncomingRequestCfProperties['continent'];
|
|
15
|
+
}>;
|
|
16
|
+
closestServers(servers: Servers, requiredCapability?: string, userCoordinate?: Coordinate, privacyRegion?: PrivacyRegion[]): Promise<Servers>;
|
|
17
|
+
}
|