@fabric-harness/cloudflare 0.10.0 → 0.15.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/workers-ai.d.ts +93 -0
- package/dist/workers-ai.d.ts.map +1 -0
- package/dist/workers-ai.js +92 -0
- package/dist/workers-ai.js.map +1 -0
- package/package.json +13 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type ModelProvider, type ModelRequest, type ModelResponse } from '@fabric-harness/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Reserved model-string prefix that routes through the Workers AI binding
|
|
4
|
+
* provider. `init({ model: 'cloudflare/@cf/meta/llama-3.1-8b-instruct' })`
|
|
5
|
+
* picks this provider when one is registered with the matching name.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CLOUDFLARE_WORKERS_AI_MODEL_PREFIX = "cloudflare/";
|
|
8
|
+
/**
|
|
9
|
+
* Structural shape of Cloudflare Workers' AI binding (`env.AI`).
|
|
10
|
+
*
|
|
11
|
+
* The real `Ai` type lives in `@cloudflare/workers-types`; we accept a
|
|
12
|
+
* narrower structural type so this package doesn't peer-dep `workers-types`.
|
|
13
|
+
* The `run()` method accepts the OpenAI Chat Completions request body and
|
|
14
|
+
* returns an OpenAI-shaped response when `stream: false` is passed.
|
|
15
|
+
*/
|
|
16
|
+
export interface CloudflareWorkersAIBindingLike {
|
|
17
|
+
run(model: string, input: {
|
|
18
|
+
messages: Array<Record<string, unknown>>;
|
|
19
|
+
tools?: Array<Record<string, unknown>>;
|
|
20
|
+
max_tokens?: number;
|
|
21
|
+
temperature?: number;
|
|
22
|
+
stream?: boolean;
|
|
23
|
+
}, options?: {
|
|
24
|
+
gateway?: {
|
|
25
|
+
id: string;
|
|
26
|
+
skipCache?: boolean;
|
|
27
|
+
cacheTtl?: number;
|
|
28
|
+
};
|
|
29
|
+
}): Promise<unknown>;
|
|
30
|
+
}
|
|
31
|
+
export interface CloudflareWorkersAIModelProviderOptions {
|
|
32
|
+
/**
|
|
33
|
+
* The Workers AI binding object — typically `env.AI`. Accept the binding
|
|
34
|
+
* directly; lazy lookup via context can be wired by passing a getter.
|
|
35
|
+
*/
|
|
36
|
+
binding: CloudflareWorkersAIBindingLike;
|
|
37
|
+
/** Default model id used when `request.model` isn't provided. */
|
|
38
|
+
defaultModel?: string;
|
|
39
|
+
/** Optional name override for telemetry. Defaults to `workers-ai`. */
|
|
40
|
+
name?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Optional Cloudflare AI Gateway routing. When set, every call is routed
|
|
43
|
+
* through the named Gateway for caching, logging, and rate-limiting.
|
|
44
|
+
*/
|
|
45
|
+
gateway?: {
|
|
46
|
+
id: string;
|
|
47
|
+
skipCache?: boolean;
|
|
48
|
+
cacheTtl?: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Model provider that dispatches to Cloudflare Workers AI via the platform
|
|
53
|
+
* binding (`env.AI.run()`) instead of HTTP. Use on a Cloudflare Workers
|
|
54
|
+
* deployment to skip API tokens, gain Workers-AI-native rate limiting, and
|
|
55
|
+
* keep inference inside Cloudflare's network.
|
|
56
|
+
*
|
|
57
|
+
* Workers AI accepts the OpenAI Chat Completions request body verbatim, so
|
|
58
|
+
* we serialize through the SDK's `toOpenAIMessage`/`toOpenAITool` helpers and
|
|
59
|
+
* parse the binding's response with `openAIChatCompletionToModelResponse`.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { CloudflareWorkersAIModelProvider } from '@fabric-harness/cloudflare/workers-ai';
|
|
63
|
+
*
|
|
64
|
+
* export default {
|
|
65
|
+
* async fetch(request: Request, env: Env) {
|
|
66
|
+
* const fabric = await init({
|
|
67
|
+
* modelProvider: new CloudflareWorkersAIModelProvider({ binding: env.AI }),
|
|
68
|
+
* model: '@cf/meta/llama-3.1-8b-instruct',
|
|
69
|
+
* });
|
|
70
|
+
* return await fabric.session().prompt('hi');
|
|
71
|
+
* },
|
|
72
|
+
* };
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* Pair with the `cloudflare/` model prefix to make routing explicit when a
|
|
76
|
+
* fallback provider is also configured. Strip the prefix before passing to
|
|
77
|
+
* the binding — the binding expects raw Workers AI model ids
|
|
78
|
+
* (`@cf/meta/...`, `@hf/...`, etc.).
|
|
79
|
+
*/
|
|
80
|
+
export declare class CloudflareWorkersAIModelProvider implements ModelProvider {
|
|
81
|
+
readonly name: string;
|
|
82
|
+
private readonly binding;
|
|
83
|
+
private readonly defaultModel?;
|
|
84
|
+
private readonly gateway?;
|
|
85
|
+
constructor(options: CloudflareWorkersAIModelProviderOptions);
|
|
86
|
+
generate(request: ModelRequest): Promise<ModelResponse>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Strip the `cloudflare/` routing prefix from a model id, leaving the raw
|
|
90
|
+
* Workers AI model id (e.g. `@cf/meta/llama-3.1-8b-instruct`).
|
|
91
|
+
*/
|
|
92
|
+
export declare function stripCloudflarePrefix(model: string): string;
|
|
93
|
+
//# sourceMappingURL=workers-ai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workers-ai.d.ts","sourceRoot":"","sources":["../src/workers-ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAGnB,MAAM,qBAAqB,CAAC;AAE7B;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,gBAAgB,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,WAAW,8BAA8B;IAC7C,GAAG,CACD,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QACL,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACzC,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACvC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,EACD,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,OAAO,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAC7E,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,MAAM,WAAW,uCAAuC;IACtD;;;OAGG;IACH,OAAO,EAAE,8BAA8B,CAAC;IACxC,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,gCAAiC,YAAW,aAAa;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAyD;gBAEtE,OAAO,EAAE,uCAAuC;IAOtD,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;CAyB9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3D"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { openAIChatCompletionToModelResponse, toOpenAIMessage, toOpenAITool, } from '@fabric-harness/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Reserved model-string prefix that routes through the Workers AI binding
|
|
4
|
+
* provider. `init({ model: 'cloudflare/@cf/meta/llama-3.1-8b-instruct' })`
|
|
5
|
+
* picks this provider when one is registered with the matching name.
|
|
6
|
+
*/
|
|
7
|
+
export const CLOUDFLARE_WORKERS_AI_MODEL_PREFIX = 'cloudflare/';
|
|
8
|
+
/**
|
|
9
|
+
* Model provider that dispatches to Cloudflare Workers AI via the platform
|
|
10
|
+
* binding (`env.AI.run()`) instead of HTTP. Use on a Cloudflare Workers
|
|
11
|
+
* deployment to skip API tokens, gain Workers-AI-native rate limiting, and
|
|
12
|
+
* keep inference inside Cloudflare's network.
|
|
13
|
+
*
|
|
14
|
+
* Workers AI accepts the OpenAI Chat Completions request body verbatim, so
|
|
15
|
+
* we serialize through the SDK's `toOpenAIMessage`/`toOpenAITool` helpers and
|
|
16
|
+
* parse the binding's response with `openAIChatCompletionToModelResponse`.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { CloudflareWorkersAIModelProvider } from '@fabric-harness/cloudflare/workers-ai';
|
|
20
|
+
*
|
|
21
|
+
* export default {
|
|
22
|
+
* async fetch(request: Request, env: Env) {
|
|
23
|
+
* const fabric = await init({
|
|
24
|
+
* modelProvider: new CloudflareWorkersAIModelProvider({ binding: env.AI }),
|
|
25
|
+
* model: '@cf/meta/llama-3.1-8b-instruct',
|
|
26
|
+
* });
|
|
27
|
+
* return await fabric.session().prompt('hi');
|
|
28
|
+
* },
|
|
29
|
+
* };
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Pair with the `cloudflare/` model prefix to make routing explicit when a
|
|
33
|
+
* fallback provider is also configured. Strip the prefix before passing to
|
|
34
|
+
* the binding — the binding expects raw Workers AI model ids
|
|
35
|
+
* (`@cf/meta/...`, `@hf/...`, etc.).
|
|
36
|
+
*/
|
|
37
|
+
export class CloudflareWorkersAIModelProvider {
|
|
38
|
+
name;
|
|
39
|
+
binding;
|
|
40
|
+
defaultModel;
|
|
41
|
+
gateway;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.name = options.name ?? 'workers-ai';
|
|
44
|
+
this.binding = options.binding;
|
|
45
|
+
if (options.defaultModel !== undefined)
|
|
46
|
+
this.defaultModel = options.defaultModel;
|
|
47
|
+
if (options.gateway !== undefined)
|
|
48
|
+
this.gateway = options.gateway;
|
|
49
|
+
}
|
|
50
|
+
async generate(request) {
|
|
51
|
+
const requested = request.model ?? this.defaultModel;
|
|
52
|
+
if (!requested)
|
|
53
|
+
throw new Error(`${this.name}: model is required.`);
|
|
54
|
+
const model = stripCloudflarePrefix(requested);
|
|
55
|
+
const body = {
|
|
56
|
+
messages: request.messages.map((m) => toOpenAIMessage(m)),
|
|
57
|
+
stream: false,
|
|
58
|
+
};
|
|
59
|
+
if (request.tools !== undefined) {
|
|
60
|
+
body.tools = request.tools.map((t) => toOpenAITool(t));
|
|
61
|
+
}
|
|
62
|
+
const result = await this.binding.run(model, body, this.gateway ? { gateway: this.gateway } : undefined);
|
|
63
|
+
return openAIChatCompletionToModelResponse(coerceCompletion(result));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Strip the `cloudflare/` routing prefix from a model id, leaving the raw
|
|
68
|
+
* Workers AI model id (e.g. `@cf/meta/llama-3.1-8b-instruct`).
|
|
69
|
+
*/
|
|
70
|
+
export function stripCloudflarePrefix(model) {
|
|
71
|
+
return model.startsWith(CLOUDFLARE_WORKERS_AI_MODEL_PREFIX)
|
|
72
|
+
? model.slice(CLOUDFLARE_WORKERS_AI_MODEL_PREFIX.length)
|
|
73
|
+
: model;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Workers AI returns either an OpenAI Chat Completion or, for older models,
|
|
77
|
+
* a `{ response: string }` shape. Normalize both into the chat completion
|
|
78
|
+
* shape so `openAIChatCompletionToModelResponse` can parse it.
|
|
79
|
+
*/
|
|
80
|
+
function coerceCompletion(value) {
|
|
81
|
+
if (value && typeof value === 'object' && 'choices' in value) {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
if (value && typeof value === 'object' && 'response' in value) {
|
|
85
|
+
const text = String(value.response ?? '');
|
|
86
|
+
return {
|
|
87
|
+
choices: [{ message: { content: text } }],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
throw new Error('workers-ai: unexpected response shape from binding');
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=workers-ai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workers-ai.js","sourceRoot":"","sources":["../src/workers-ai.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mCAAmC,EACnC,eAAe,EACf,YAAY,GAOb,MAAM,qBAAqB,CAAC;AAE7B;;;;GAIG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,aAAa,CAAC;AAyChE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,OAAO,gCAAgC;IAClC,IAAI,CAAS;IACL,OAAO,CAAiC;IACxC,YAAY,CAAU;IACtB,OAAO,CAA0D;IAElF,YAAY,OAAgD;QAC1D,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,YAAY,CAAC;QACzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QACjF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAqB;QAClC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC;QACrD,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,sBAAsB,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;QAE/C,MAAM,IAAI,GAIN;YACF,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YACvE,MAAM,EAAE,KAAK;SACd,CAAC;QACF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAkB,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CACnC,KAAK,EACL,IAAI,EACJ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CACrD,CAAC;QAEF,OAAO,mCAAmC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;IACvE,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,OAAO,KAAK,CAAC,UAAU,CAAC,kCAAkC,CAAC;QACzD,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,kCAAkC,CAAC,MAAM,CAAC;QACxD,CAAC,CAAC,KAAK,CAAC;AACZ,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;QAC7D,OAAO,KAA6B,CAAC;IACvC,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,IAAI,KAAK,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,CAAE,KAA+B,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QACrE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;SAC1C,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;AACxE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabric-harness/cloudflare",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Cloudflare Workers and Sandbox adapter for Fabric Harness.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Fabric",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"type": "module",
|
|
26
26
|
"sideEffects": false,
|
|
27
27
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
28
|
+
"node": ">=20.18.0"
|
|
29
29
|
},
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"registry": "https://registry.npmjs.org",
|
|
@@ -33,13 +33,23 @@
|
|
|
33
33
|
},
|
|
34
34
|
"main": "dist/index.js",
|
|
35
35
|
"types": "dist/index.d.ts",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"import": "./dist/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./workers-ai": {
|
|
42
|
+
"types": "./dist/workers-ai.d.ts",
|
|
43
|
+
"import": "./dist/workers-ai.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
36
46
|
"files": [
|
|
37
47
|
"dist",
|
|
38
48
|
"README.md",
|
|
39
49
|
"LICENSE"
|
|
40
50
|
],
|
|
41
51
|
"dependencies": {
|
|
42
|
-
"@fabric-harness/sdk": "
|
|
52
|
+
"@fabric-harness/sdk": "1.1.0"
|
|
43
53
|
},
|
|
44
54
|
"peerDependencies": {
|
|
45
55
|
"@cloudflare/sandbox": "*"
|