@chainfuse/ai-tools 0.1.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/LICENSE +201 -0
- package/README.md +17 -0
- package/dist/base.d.mts +7 -0
- package/dist/base.mjs +11 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +1 -0
- package/dist/models.d.mts +24 -0
- package/dist/models.mjs +17 -0
- package/dist/providers/customProviders.d.mts +10 -0
- package/dist/providers/customProviders.mjs +167 -0
- package/dist/providers/rawProviders.d.mts +9 -0
- package/dist/providers/rawProviders.mjs +215 -0
- package/dist/providers/types.d.mts +26 -0
- package/dist/providers/types.mjs +1 -0
- package/dist/registry.d.mts +11 -0
- package/dist/registry.mjs +16 -0
- package/dist/serverSelector/azure.d.mts +5 -0
- package/dist/serverSelector/azure.mjs +240 -0
- package/dist/serverSelector/base.d.mts +22 -0
- package/dist/serverSelector/base.mjs +137 -0
- package/dist/serverSelector/types.d.mts +9 -0
- package/dist/serverSelector/types.mjs +1 -0
- package/dist/types.d.mts +101 -0
- package/dist/types.mjs +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Helpers } from '@chainfuse/helpers';
|
|
2
|
+
import haversine from 'haversine-distance';
|
|
3
|
+
import { AiBase } from '../base.mjs';
|
|
4
|
+
export var PrivacyRegion;
|
|
5
|
+
(function (PrivacyRegion) {
|
|
6
|
+
PrivacyRegion["Australian_Privacy_Principles"] = "APPs";
|
|
7
|
+
PrivacyRegion["Brazil_General_Data_protection_Law"] = "LGPD";
|
|
8
|
+
PrivacyRegion["Canada_Personal_Information_Protection_and_Electronic_Documents_Act"] = "PIPEDA";
|
|
9
|
+
PrivacyRegion["General_Data_Protection_Regulation"] = "GDPR";
|
|
10
|
+
PrivacyRegion["Indian_Personal_Protection"] = "PDP";
|
|
11
|
+
PrivacyRegion["Japan_Act_on_the_Protection_of_Personal_Information"] = "APPI";
|
|
12
|
+
PrivacyRegion["Korean_Personal_Information_Protection_Act"] = "PIPA";
|
|
13
|
+
PrivacyRegion["Norwegian_Personal_Data_Act"] = "NPDA";
|
|
14
|
+
PrivacyRegion["SouthAfrica_Protection_Personal_Information_Act"] = "PoPIA";
|
|
15
|
+
PrivacyRegion["Swiss_Federal_Act_on_Data_Protection"] = "revFADP";
|
|
16
|
+
PrivacyRegion["UK_General_Data_Protection_Regulation"] = "UK-GDPR";
|
|
17
|
+
})(PrivacyRegion || (PrivacyRegion = {}));
|
|
18
|
+
export class ServerSelector extends AiBase {
|
|
19
|
+
servers = new Set();
|
|
20
|
+
static determinePrivacyRegion(country, continent) {
|
|
21
|
+
const regions = new Set();
|
|
22
|
+
if (country) {
|
|
23
|
+
switch (country.toUpperCase()) {
|
|
24
|
+
case 'AU':
|
|
25
|
+
regions.add(PrivacyRegion.Australian_Privacy_Principles);
|
|
26
|
+
break;
|
|
27
|
+
case 'BR':
|
|
28
|
+
regions.add(PrivacyRegion.Brazil_General_Data_protection_Law);
|
|
29
|
+
break;
|
|
30
|
+
case 'CA':
|
|
31
|
+
regions.add(PrivacyRegion.Canada_Personal_Information_Protection_and_Electronic_Documents_Act);
|
|
32
|
+
regions.add(PrivacyRegion.General_Data_Protection_Regulation);
|
|
33
|
+
regions.add(PrivacyRegion.Swiss_Federal_Act_on_Data_Protection);
|
|
34
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
35
|
+
break;
|
|
36
|
+
case 'IN':
|
|
37
|
+
regions.add(PrivacyRegion.Indian_Personal_Protection);
|
|
38
|
+
break;
|
|
39
|
+
case 'JP':
|
|
40
|
+
regions.add(PrivacyRegion.Japan_Act_on_the_Protection_of_Personal_Information);
|
|
41
|
+
regions.add(PrivacyRegion.General_Data_Protection_Regulation);
|
|
42
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
43
|
+
break;
|
|
44
|
+
case 'KR':
|
|
45
|
+
regions.add(PrivacyRegion.Korean_Personal_Information_Protection_Act);
|
|
46
|
+
regions.add(PrivacyRegion.General_Data_Protection_Regulation);
|
|
47
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
48
|
+
break;
|
|
49
|
+
case 'NO':
|
|
50
|
+
regions.add(PrivacyRegion.Norwegian_Personal_Data_Act);
|
|
51
|
+
regions.add(PrivacyRegion.Canada_Personal_Information_Protection_and_Electronic_Documents_Act);
|
|
52
|
+
regions.add(PrivacyRegion.General_Data_Protection_Regulation);
|
|
53
|
+
regions.add(PrivacyRegion.Japan_Act_on_the_Protection_of_Personal_Information);
|
|
54
|
+
regions.add(PrivacyRegion.Korean_Personal_Information_Protection_Act);
|
|
55
|
+
regions.add(PrivacyRegion.Swiss_Federal_Act_on_Data_Protection);
|
|
56
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
57
|
+
break;
|
|
58
|
+
case 'ZA':
|
|
59
|
+
regions.add(PrivacyRegion.SouthAfrica_Protection_Personal_Information_Act);
|
|
60
|
+
break;
|
|
61
|
+
case 'CH':
|
|
62
|
+
regions.add(PrivacyRegion.Swiss_Federal_Act_on_Data_Protection);
|
|
63
|
+
regions.add(PrivacyRegion.Canada_Personal_Information_Protection_and_Electronic_Documents_Act);
|
|
64
|
+
regions.add(PrivacyRegion.General_Data_Protection_Regulation);
|
|
65
|
+
regions.add(PrivacyRegion.Japan_Act_on_the_Protection_of_Personal_Information);
|
|
66
|
+
regions.add(PrivacyRegion.Korean_Personal_Information_Protection_Act);
|
|
67
|
+
regions.add(PrivacyRegion.Norwegian_Personal_Data_Act);
|
|
68
|
+
regions.add(PrivacyRegion.Swiss_Federal_Act_on_Data_Protection);
|
|
69
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
70
|
+
break;
|
|
71
|
+
case 'GB':
|
|
72
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
73
|
+
regions.add(PrivacyRegion.Canada_Personal_Information_Protection_and_Electronic_Documents_Act);
|
|
74
|
+
regions.add(PrivacyRegion.General_Data_Protection_Regulation);
|
|
75
|
+
regions.add(PrivacyRegion.Japan_Act_on_the_Protection_of_Personal_Information);
|
|
76
|
+
regions.add(PrivacyRegion.Korean_Personal_Information_Protection_Act);
|
|
77
|
+
regions.add(PrivacyRegion.Norwegian_Personal_Data_Act);
|
|
78
|
+
regions.add(PrivacyRegion.Swiss_Federal_Act_on_Data_Protection);
|
|
79
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (continent) {
|
|
84
|
+
switch (continent.toUpperCase()) {
|
|
85
|
+
case 'EU':
|
|
86
|
+
regions.add(PrivacyRegion.General_Data_Protection_Regulation);
|
|
87
|
+
regions.add(PrivacyRegion.Canada_Personal_Information_Protection_and_Electronic_Documents_Act);
|
|
88
|
+
regions.add(PrivacyRegion.Japan_Act_on_the_Protection_of_Personal_Information);
|
|
89
|
+
regions.add(PrivacyRegion.Korean_Personal_Information_Protection_Act);
|
|
90
|
+
regions.add(PrivacyRegion.Norwegian_Personal_Data_Act);
|
|
91
|
+
regions.add(PrivacyRegion.Swiss_Federal_Act_on_Data_Protection);
|
|
92
|
+
regions.add(PrivacyRegion.UK_General_Data_Protection_Regulation);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return Array.from(regions);
|
|
97
|
+
}
|
|
98
|
+
closestServers(requiredCapability, userCoordinate = {
|
|
99
|
+
lat: this.config.geoRouting?.userCoordinate?.lat ?? '0',
|
|
100
|
+
lon: this.config.geoRouting?.userCoordinate?.lon ?? '0',
|
|
101
|
+
}, privacyRegion = ServerSelector.determinePrivacyRegion(this.config.geoRouting?.country, this.config.geoRouting?.continent)) {
|
|
102
|
+
// Skip over the rest of logic if the server can't handle the incoming request
|
|
103
|
+
// @ts-expect-error it's always strings, just sometimes string literals
|
|
104
|
+
const featureFilteredServers = requiredCapability ? Array.from(this.servers).filter((server) => server.languageModelAvailability.includes(requiredCapability) || server.textEmbeddingModelAvailability.includes(requiredCapability)) : Array.from(this.servers);
|
|
105
|
+
if (featureFilteredServers.length > 0) {
|
|
106
|
+
// Skip over servers not in the save privacy region except if undefined, then you can use any
|
|
107
|
+
const privacyRegionFilteredServers = featureFilteredServers.filter((server) => privacyRegion.length === 0 || (server.region && privacyRegion.includes(server.region)));
|
|
108
|
+
if (privacyRegionFilteredServers.length > 0) {
|
|
109
|
+
// Calculate distance for each server and store it as a tuple [Server, distance]
|
|
110
|
+
const serversWithDistances = privacyRegionFilteredServers.map((server) => {
|
|
111
|
+
// Match decimal point length
|
|
112
|
+
return [
|
|
113
|
+
server,
|
|
114
|
+
haversine({
|
|
115
|
+
lat: Helpers.precisionFloat(userCoordinate.lat),
|
|
116
|
+
lon: Helpers.precisionFloat(userCoordinate.lon),
|
|
117
|
+
}, {
|
|
118
|
+
lat: server.coordinate.lat,
|
|
119
|
+
lon: server.coordinate.lon,
|
|
120
|
+
}),
|
|
121
|
+
];
|
|
122
|
+
});
|
|
123
|
+
// Sort the servers by distance
|
|
124
|
+
serversWithDistances.sort((a, b) => a[1] - b[1]);
|
|
125
|
+
// Extract the ids of the sorted servers
|
|
126
|
+
const sortedServers = serversWithDistances.map(([server]) => server);
|
|
127
|
+
return sortedServers;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
throw new Error(`No server with the capability ${requiredCapability} available in a region covered under ${JSON.stringify(privacyRegion)}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new Error(`No server with the capability ${requiredCapability} available`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AzureChatModels, AzureEmbeddingModels, Coordinate } from '@chainfuse/types';
|
|
2
|
+
import type { PrivacyRegion } from './base.mjs';
|
|
3
|
+
export interface Server {
|
|
4
|
+
id: string;
|
|
5
|
+
coordinate: Coordinate;
|
|
6
|
+
region?: PrivacyRegion;
|
|
7
|
+
languageModelAvailability: AzureChatModels[] | string[];
|
|
8
|
+
textEmbeddingModelAvailability: AzureEmbeddingModels[] | string[];
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { PrefixedUuid, RawCoordinate, UuidExport } from '@chainfuse/types';
|
|
2
|
+
import type { Ai, IncomingRequestCfProperties } from '@cloudflare/workers-types/experimental';
|
|
3
|
+
import type haversine from 'haversine-distance';
|
|
4
|
+
export interface AiConfig {
|
|
5
|
+
gateway: {
|
|
6
|
+
accountId: string;
|
|
7
|
+
apiToken: string;
|
|
8
|
+
};
|
|
9
|
+
geoRouting?: {
|
|
10
|
+
userCoordinate?: RawCoordinate;
|
|
11
|
+
country?: IncomingRequestCfProperties['country'];
|
|
12
|
+
continent?: IncomingRequestCfProperties['continent'];
|
|
13
|
+
};
|
|
14
|
+
environment: 'production' | 'preview';
|
|
15
|
+
providers: AiConfigProviders;
|
|
16
|
+
}
|
|
17
|
+
export interface AiConfigProviders {
|
|
18
|
+
anthropic: AiConfigAnthropic;
|
|
19
|
+
azureOpenAi: AiConfigAzOpenai;
|
|
20
|
+
openAi: AiConfigOaiOpenai;
|
|
21
|
+
workersAi: AiConfigWorkersai;
|
|
22
|
+
}
|
|
23
|
+
export interface AiConfigAnthropic {
|
|
24
|
+
apiToken: `sk-ant-${string}`;
|
|
25
|
+
}
|
|
26
|
+
export interface AiConfigAzOpenai {
|
|
27
|
+
apiTokens: Record<`AZURE_API_KEY_${string}`, string>;
|
|
28
|
+
}
|
|
29
|
+
export interface AiConfigOaiOpenai {
|
|
30
|
+
apiToken: `sk-${string}`;
|
|
31
|
+
organization: `org-${string}`;
|
|
32
|
+
}
|
|
33
|
+
export interface AiConfigWorkersaiRest {
|
|
34
|
+
apiToken: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated Not functional. Use REST instead
|
|
38
|
+
*/
|
|
39
|
+
export type AiConfigWorkersaiBinding = Ai;
|
|
40
|
+
export type AiConfigWorkersai = AiConfigWorkersaiRest | AiConfigWorkersaiBinding;
|
|
41
|
+
/**
|
|
42
|
+
* It's a UUID, but the last block is SHA256 of the request body
|
|
43
|
+
*/
|
|
44
|
+
export type AiRequestIdempotencyId = UuidExport['utf8'];
|
|
45
|
+
export interface AiRequestExecutor {
|
|
46
|
+
type: 'worker' | 'queue' | 'workflow' | 'githubCicd';
|
|
47
|
+
id: string;
|
|
48
|
+
}
|
|
49
|
+
export interface AiRequestConfig {
|
|
50
|
+
/**
|
|
51
|
+
* Sets if a response should be cached (1 month) or for any custom duration
|
|
52
|
+
* @default true
|
|
53
|
+
*/
|
|
54
|
+
cache?: boolean | number;
|
|
55
|
+
dataspaceId: PrefixedUuid | UuidExport['utf8'] | UuidExport['hex'];
|
|
56
|
+
/**
|
|
57
|
+
* Service identification of caller
|
|
58
|
+
*/
|
|
59
|
+
executor: AiRequestExecutor;
|
|
60
|
+
/**
|
|
61
|
+
* Identify the same request across multiple calls. If not provided, a new id will be generated
|
|
62
|
+
* Structure: <UUIDv7>.<last 8 of SHA256 of body>
|
|
63
|
+
*/
|
|
64
|
+
idempotencyId?: AiRequestIdempotencyId;
|
|
65
|
+
/**
|
|
66
|
+
* Logging includes anything up to the ai call. For ai call info, see ai gateway
|
|
67
|
+
* @default false (on production environment), true (on preview environment)
|
|
68
|
+
*/
|
|
69
|
+
logging?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Force a response to be generated even if a matching request is already cached (without changing cache setting)
|
|
72
|
+
* @default false
|
|
73
|
+
*/
|
|
74
|
+
skipCache?: boolean;
|
|
75
|
+
}
|
|
76
|
+
export interface AiRequestMetadataServerInfo {
|
|
77
|
+
name: 'anthropic' | 'cloudflare' | 'openai';
|
|
78
|
+
}
|
|
79
|
+
export interface AiRequestMetadataServerInfoWithLocation {
|
|
80
|
+
name: `${'azure' | 'google'}-${string}`;
|
|
81
|
+
/**
|
|
82
|
+
* @returns distance in meters
|
|
83
|
+
*/
|
|
84
|
+
distance: ReturnType<typeof haversine>;
|
|
85
|
+
}
|
|
86
|
+
export interface AiRequestMetadataTiming {
|
|
87
|
+
modelTime?: number;
|
|
88
|
+
fromCache: boolean;
|
|
89
|
+
totalRoundtripTime: number;
|
|
90
|
+
}
|
|
91
|
+
export interface AiRequestMetadata {
|
|
92
|
+
dataspaceId: AiRequestConfig['dataspaceId'];
|
|
93
|
+
serverInfo: AiRequestMetadataServerInfo | AiRequestMetadataServerInfoWithLocation | string;
|
|
94
|
+
idempotencyId: AiRequestIdempotencyId;
|
|
95
|
+
executor: AiRequestExecutor | string;
|
|
96
|
+
timing: AiRequestMetadataTiming | string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Extracts the chunk type from an asynchronous iterable.
|
|
100
|
+
*/
|
|
101
|
+
export type AiStreamChunkType<T> = T extends AsyncIterable<infer U> ? Awaited<U> : never;
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chainfuse/ai-tools",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"author": "ChainFuse",
|
|
6
|
+
"homepage": "https://github.com/ChainFuse/packages/tree/main/packages/ai-tools#readme",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"main": "./dist/index.mjs",
|
|
9
|
+
"directories": {
|
|
10
|
+
"lib": "dist",
|
|
11
|
+
"test": "__tests__"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public",
|
|
18
|
+
"provenance": true
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/ChainFuse/packages.git"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"fmt": "prettier --check .",
|
|
26
|
+
"fmt:fix": "prettier --write .",
|
|
27
|
+
"lint": "eslint .",
|
|
28
|
+
"lint:fix": "npm run lint -- --fix",
|
|
29
|
+
"clean": "npx -y rimraf@latest ./dist ./.tsbuildinfo",
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"build:clean": "npm run build -- --build --clean && npm run build",
|
|
32
|
+
"pretest": "tsc --project tsconfig.tests.json",
|
|
33
|
+
"test": "node --env-file-if-exists=.dev.vars --enable-source-maps --test --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/ChainFuse/packages/issues"
|
|
38
|
+
},
|
|
39
|
+
"types": "./dist/index.d.mts",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=22.11.0"
|
|
42
|
+
},
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"import": "./dist/index.mjs",
|
|
46
|
+
"types": "./dist/index.d.mts"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"prettier": "@demosjarco/prettier-config",
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@ai-sdk/anthropic": "^1.0.6",
|
|
52
|
+
"@ai-sdk/azure": "^1.0.15",
|
|
53
|
+
"@ai-sdk/openai": "^1.0.5",
|
|
54
|
+
"@chainfuse/helpers": "^0.5.0",
|
|
55
|
+
"@chainfuse/types": "^1.4.0",
|
|
56
|
+
"ai": "^4.0.25",
|
|
57
|
+
"chalk": "^5.4.1",
|
|
58
|
+
"haversine-distance": "^1.2.3",
|
|
59
|
+
"workers-ai-provider": "^0.0.10"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@cloudflare/workers-types": "^4.20241230.0",
|
|
63
|
+
"openai": "^4.77.3"
|
|
64
|
+
},
|
|
65
|
+
"gitHead": "13881adbb875a4b1680448205ff9e0ac55577f72"
|
|
66
|
+
}
|