@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.
@@ -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.10.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": ">=22.0.0"
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": "0.10.0"
52
+ "@fabric-harness/sdk": "1.1.0"
43
53
  },
44
54
  "peerDependencies": {
45
55
  "@cloudflare/sandbox": "*"