workers-ai-provider 0.4.0 → 0.5.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/index.js CHANGED
@@ -9,51 +9,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
9
9
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
10
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
11
 
12
- // src/utils.ts
13
- function createRun(config) {
14
- const { accountId, apiKey } = config;
15
- return async function run(model, inputs, options) {
16
- const { gateway, prefix, extraHeaders, returnRawResponse, ...passthroughOptions } = options || {};
17
- const urlParams = new URLSearchParams();
18
- for (const [key, value] of Object.entries(passthroughOptions)) {
19
- try {
20
- const valueStr = value.toString();
21
- if (!valueStr) {
22
- continue;
23
- }
24
- urlParams.append(key, valueStr);
25
- } catch (error) {
26
- throw new Error(
27
- `Value for option '${key}' is not able to be coerced into a string.`
28
- );
29
- }
30
- }
31
- const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run/${model}${urlParams ? `?${urlParams}` : ""}`;
32
- const headers = {
33
- "Content-Type": "application/json",
34
- Authorization: `Bearer ${apiKey}`
35
- };
36
- const body = JSON.stringify(inputs);
37
- const response = await fetch(url, {
38
- method: "POST",
39
- headers,
40
- body
41
- });
42
- if (returnRawResponse) {
43
- return response;
44
- }
45
- if (inputs.stream === true) {
46
- if (response.body) {
47
- return response.body;
48
- }
49
- throw new Error("No readable body available for streaming.");
50
- }
51
- const data = await response.json();
52
- return data.result;
53
- };
54
- }
55
-
56
- // src/workersai-chat-language-model.ts
12
+ // src/autorag-chat-language-model.ts
57
13
  import {
58
14
  UnsupportedFunctionalityError
59
15
  } from "@ai-sdk/provider";
@@ -117,24 +73,18 @@ function convertToWorkersAIChatMessages(prompt) {
117
73
  }
118
74
  default: {
119
75
  const exhaustiveCheck = part;
120
- throw new Error(
121
- `Unsupported part: ${exhaustiveCheck}`
122
- );
76
+ throw new Error(`Unsupported part: ${exhaustiveCheck}`);
123
77
  }
124
78
  }
125
79
  }
126
80
  messages.push({
127
81
  role: "assistant",
128
82
  content: text,
129
- tool_calls: toolCalls.length > 0 ? toolCalls.map(
130
- ({
131
- function: { name, arguments: args }
132
- }) => ({
133
- id: "null",
134
- type: "function",
135
- function: { name, arguments: args }
136
- })
137
- ) : void 0
83
+ tool_calls: toolCalls.length > 0 ? toolCalls.map(({ function: { name, arguments: args } }) => ({
84
+ id: "null",
85
+ type: "function",
86
+ function: { name, arguments: args }
87
+ })) : void 0
138
88
  });
139
89
  break;
140
90
  }
@@ -157,6 +107,18 @@ function convertToWorkersAIChatMessages(prompt) {
157
107
  return { messages, images };
158
108
  }
159
109
 
110
+ // src/map-workersai-usage.ts
111
+ function mapWorkersAIUsage(output) {
112
+ const usage = output.usage ?? {
113
+ prompt_tokens: 0,
114
+ completion_tokens: 0
115
+ };
116
+ return {
117
+ promptTokens: usage.prompt_tokens,
118
+ completionTokens: usage.completion_tokens
119
+ };
120
+ }
121
+
160
122
  // ../../node_modules/.pnpm/fetch-event-stream@0.1.5/node_modules/fetch-event-stream/esm/deps/jsr.io/@std/streams/0.221.0/text_line_stream.js
161
123
  var _currentLine;
162
124
  var TextLineStream = class extends TransformStream {
@@ -250,19 +212,328 @@ async function* events(res, signal) {
250
212
  }
251
213
  }
252
214
 
253
- // src/map-workersai-usage.ts
254
- function mapWorkersAIUsage(output) {
255
- const usage = output.usage ?? {
256
- prompt_tokens: 0,
257
- completion_tokens: 0
258
- };
259
- return {
260
- promptTokens: usage.prompt_tokens,
261
- completionTokens: usage.completion_tokens
215
+ // src/streaming.ts
216
+ function getMappedStream(response) {
217
+ const chunkEvent = events(response);
218
+ let usage = { promptTokens: 0, completionTokens: 0 };
219
+ return new ReadableStream({
220
+ async start(controller) {
221
+ for await (const event of chunkEvent) {
222
+ if (!event.data) {
223
+ continue;
224
+ }
225
+ if (event.data === "[DONE]") {
226
+ break;
227
+ }
228
+ const chunk = JSON.parse(event.data);
229
+ if (chunk.usage) {
230
+ usage = mapWorkersAIUsage(chunk);
231
+ }
232
+ chunk.response?.length && controller.enqueue({
233
+ type: "text-delta",
234
+ textDelta: chunk.response
235
+ });
236
+ }
237
+ controller.enqueue({
238
+ type: "finish",
239
+ finishReason: "stop",
240
+ usage
241
+ });
242
+ controller.close();
243
+ }
244
+ });
245
+ }
246
+
247
+ // src/utils.ts
248
+ function createRun(config) {
249
+ const { accountId, apiKey } = config;
250
+ return async function run(model, inputs, options) {
251
+ const { gateway, prefix, extraHeaders, returnRawResponse, ...passthroughOptions } = options || {};
252
+ const urlParams = new URLSearchParams();
253
+ for (const [key, value] of Object.entries(passthroughOptions)) {
254
+ try {
255
+ const valueStr = value.toString();
256
+ if (!valueStr) {
257
+ continue;
258
+ }
259
+ urlParams.append(key, valueStr);
260
+ } catch (error) {
261
+ throw new Error(
262
+ `Value for option '${key}' is not able to be coerced into a string.`
263
+ );
264
+ }
265
+ }
266
+ const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run/${model}${urlParams ? `?${urlParams}` : ""}`;
267
+ const headers = {
268
+ "Content-Type": "application/json",
269
+ Authorization: `Bearer ${apiKey}`
270
+ };
271
+ const body = JSON.stringify(inputs);
272
+ const response = await fetch(url, {
273
+ method: "POST",
274
+ headers,
275
+ body
276
+ });
277
+ if (returnRawResponse) {
278
+ return response;
279
+ }
280
+ if (inputs.stream === true) {
281
+ if (response.body) {
282
+ return response.body;
283
+ }
284
+ throw new Error("No readable body available for streaming.");
285
+ }
286
+ const data = await response.json();
287
+ return data.result;
262
288
  };
263
289
  }
290
+ function prepareToolsAndToolChoice(mode) {
291
+ const tools = mode.tools?.length ? mode.tools : void 0;
292
+ if (tools == null) {
293
+ return { tools: void 0, tool_choice: void 0 };
294
+ }
295
+ const mappedTools = tools.map((tool) => ({
296
+ type: "function",
297
+ function: {
298
+ name: tool.name,
299
+ // @ts-expect-error - description is not a property of tool
300
+ description: tool.description,
301
+ // @ts-expect-error - parameters is not a property of tool
302
+ parameters: tool.parameters
303
+ }
304
+ }));
305
+ const toolChoice = mode.toolChoice;
306
+ if (toolChoice == null) {
307
+ return { tools: mappedTools, tool_choice: void 0 };
308
+ }
309
+ const type = toolChoice.type;
310
+ switch (type) {
311
+ case "auto":
312
+ return { tools: mappedTools, tool_choice: type };
313
+ case "none":
314
+ return { tools: mappedTools, tool_choice: type };
315
+ case "required":
316
+ return { tools: mappedTools, tool_choice: "any" };
317
+ // workersAI does not support tool mode directly,
318
+ // so we filter the tools and force the tool choice through 'any'
319
+ case "tool":
320
+ return {
321
+ tools: mappedTools.filter((tool) => tool.function.name === toolChoice.toolName),
322
+ tool_choice: "any"
323
+ };
324
+ default: {
325
+ const exhaustiveCheck = type;
326
+ throw new Error(`Unsupported tool choice type: ${exhaustiveCheck}`);
327
+ }
328
+ }
329
+ }
330
+ function lastMessageWasUser(messages) {
331
+ return messages.length > 0 && messages[messages.length - 1].role === "user";
332
+ }
333
+ function processToolCalls(output) {
334
+ if (output.tool_calls && Array.isArray(output.tool_calls)) {
335
+ return output.tool_calls.map((toolCall) => {
336
+ if (toolCall.function && toolCall.id) {
337
+ return {
338
+ toolCallType: "function",
339
+ toolCallId: toolCall.id,
340
+ toolName: toolCall.function.name,
341
+ args: typeof toolCall.function.arguments === "string" ? toolCall.function.arguments : JSON.stringify(toolCall.function.arguments || {})
342
+ };
343
+ }
344
+ return {
345
+ toolCallType: "function",
346
+ toolCallId: toolCall.name,
347
+ toolName: toolCall.name,
348
+ args: typeof toolCall.arguments === "string" ? toolCall.arguments : JSON.stringify(toolCall.arguments || {})
349
+ };
350
+ });
351
+ }
352
+ return [];
353
+ }
354
+
355
+ // src/autorag-chat-language-model.ts
356
+ var AutoRAGChatLanguageModel = class {
357
+ constructor(modelId, settings, config) {
358
+ __publicField(this, "specificationVersion", "v1");
359
+ __publicField(this, "defaultObjectGenerationMode", "json");
360
+ __publicField(this, "modelId");
361
+ __publicField(this, "settings");
362
+ __publicField(this, "config");
363
+ this.modelId = modelId;
364
+ this.settings = settings;
365
+ this.config = config;
366
+ }
367
+ get provider() {
368
+ return this.config.provider;
369
+ }
370
+ getArgs({
371
+ mode,
372
+ prompt,
373
+ frequencyPenalty,
374
+ presencePenalty
375
+ }) {
376
+ const type = mode.type;
377
+ const warnings = [];
378
+ if (frequencyPenalty != null) {
379
+ warnings.push({
380
+ type: "unsupported-setting",
381
+ setting: "frequencyPenalty"
382
+ });
383
+ }
384
+ if (presencePenalty != null) {
385
+ warnings.push({
386
+ type: "unsupported-setting",
387
+ setting: "presencePenalty"
388
+ });
389
+ }
390
+ const baseArgs = {
391
+ // model id:
392
+ model: this.modelId,
393
+ // messages:
394
+ messages: convertToWorkersAIChatMessages(prompt)
395
+ };
396
+ switch (type) {
397
+ case "regular": {
398
+ return {
399
+ args: { ...baseArgs, ...prepareToolsAndToolChoice(mode) },
400
+ warnings
401
+ };
402
+ }
403
+ case "object-json": {
404
+ return {
405
+ args: {
406
+ ...baseArgs,
407
+ response_format: {
408
+ type: "json_schema",
409
+ json_schema: mode.schema
410
+ },
411
+ tools: void 0
412
+ },
413
+ warnings
414
+ };
415
+ }
416
+ case "object-tool": {
417
+ return {
418
+ args: {
419
+ ...baseArgs,
420
+ tool_choice: "any",
421
+ tools: [{ type: "function", function: mode.tool }]
422
+ },
423
+ warnings
424
+ };
425
+ }
426
+ // @ts-expect-error - this is unreachable code
427
+ // TODO: fixme
428
+ case "object-grammar": {
429
+ throw new UnsupportedFunctionalityError({
430
+ functionality: "object-grammar mode"
431
+ });
432
+ }
433
+ default: {
434
+ const exhaustiveCheck = type;
435
+ throw new Error(`Unsupported type: ${exhaustiveCheck}`);
436
+ }
437
+ }
438
+ }
439
+ async doGenerate(options) {
440
+ const { args, warnings } = this.getArgs(options);
441
+ const { messages } = convertToWorkersAIChatMessages(options.prompt);
442
+ const output = await this.config.binding.aiSearch({
443
+ query: messages.map(({ content, role }) => `${role}: ${content}`).join("\n\n")
444
+ });
445
+ return {
446
+ text: output.response,
447
+ toolCalls: processToolCalls(output),
448
+ finishReason: "stop",
449
+ // TODO: mapWorkersAIFinishReason(response.finish_reason),
450
+ rawCall: { rawPrompt: args.messages, rawSettings: args },
451
+ usage: mapWorkersAIUsage(output),
452
+ warnings,
453
+ sources: output.data.map(({ file_id, filename, score }) => ({
454
+ id: file_id,
455
+ sourceType: "url",
456
+ url: filename,
457
+ providerMetadata: {
458
+ attributes: { score }
459
+ }
460
+ }))
461
+ };
462
+ }
463
+ async doStream(options) {
464
+ const { args, warnings } = this.getArgs(options);
465
+ const { messages } = convertToWorkersAIChatMessages(options.prompt);
466
+ const query = messages.map(({ content, role }) => `${role}: ${content}`).join("\n\n");
467
+ const response = await this.config.binding.aiSearch({
468
+ query,
469
+ stream: true
470
+ });
471
+ return {
472
+ stream: getMappedStream(response),
473
+ rawCall: { rawPrompt: args.messages, rawSettings: args },
474
+ warnings
475
+ };
476
+ }
477
+ };
478
+
479
+ // src/workers-ai-embedding-model.ts
480
+ import { TooManyEmbeddingValuesForCallError } from "@ai-sdk/provider";
481
+ var WorkersAIEmbeddingModel = class {
482
+ constructor(modelId, settings, config) {
483
+ /**
484
+ * Semantic version of the {@link EmbeddingModelV1} specification implemented
485
+ * by this class. It never changes.
486
+ */
487
+ __publicField(this, "specificationVersion", "v1");
488
+ __publicField(this, "modelId");
489
+ __publicField(this, "config");
490
+ __publicField(this, "settings");
491
+ this.modelId = modelId;
492
+ this.settings = settings;
493
+ this.config = config;
494
+ }
495
+ /**
496
+ * Provider name exposed for diagnostics and error reporting.
497
+ */
498
+ get provider() {
499
+ return this.config.provider;
500
+ }
501
+ get maxEmbeddingsPerCall() {
502
+ const maxEmbeddingsPerCall = this.modelId === "@cf/baai/bge-large-en-v1.5" ? 1500 : 3e3;
503
+ return this.settings.maxEmbeddingsPerCall ?? maxEmbeddingsPerCall;
504
+ }
505
+ get supportsParallelCalls() {
506
+ return this.settings.supportsParallelCalls ?? true;
507
+ }
508
+ async doEmbed({
509
+ values
510
+ }) {
511
+ if (values.length > this.maxEmbeddingsPerCall) {
512
+ throw new TooManyEmbeddingValuesForCallError({
513
+ provider: this.provider,
514
+ modelId: this.modelId,
515
+ maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
516
+ values
517
+ });
518
+ }
519
+ const { gateway, ...passthroughOptions } = this.settings;
520
+ const response = await this.config.binding.run(
521
+ this.modelId,
522
+ {
523
+ text: values
524
+ },
525
+ { gateway: this.config.gateway ?? gateway, ...passthroughOptions }
526
+ );
527
+ return {
528
+ embeddings: response.data
529
+ };
530
+ }
531
+ };
264
532
 
265
533
  // src/workersai-chat-language-model.ts
534
+ import {
535
+ UnsupportedFunctionalityError as UnsupportedFunctionalityError2
536
+ } from "@ai-sdk/provider";
266
537
  var WorkersAIChatLanguageModel = class {
267
538
  constructor(modelId, settings, config) {
268
539
  __publicField(this, "specificationVersion", "v1");
@@ -344,7 +615,7 @@ var WorkersAIChatLanguageModel = class {
344
615
  // @ts-expect-error - this is unreachable code
345
616
  // TODO: fixme
346
617
  case "object-grammar": {
347
- throw new UnsupportedFunctionalityError({
618
+ throw new UnsupportedFunctionalityError2({
348
619
  functionality: "object-grammar mode"
349
620
  });
350
621
  }
@@ -357,9 +628,7 @@ var WorkersAIChatLanguageModel = class {
357
628
  async doGenerate(options) {
358
629
  const { args, warnings } = this.getArgs(options);
359
630
  const { gateway, safePrompt, ...passthroughOptions } = this.settings;
360
- const { messages, images } = convertToWorkersAIChatMessages(
361
- options.prompt
362
- );
631
+ const { messages, images } = convertToWorkersAIChatMessages(options.prompt);
363
632
  if (images.length !== 0 && images.length !== 1) {
364
633
  throw new Error("Multiple images are not yet supported as input");
365
634
  }
@@ -395,9 +664,7 @@ var WorkersAIChatLanguageModel = class {
395
664
  }
396
665
  async doStream(options) {
397
666
  const { args, warnings } = this.getArgs(options);
398
- const { messages, images } = convertToWorkersAIChatMessages(
399
- options.prompt
400
- );
667
+ const { messages, images } = convertToWorkersAIChatMessages(options.prompt);
401
668
  if (args.tools?.length && lastMessageWasUser(messages)) {
402
669
  const response2 = await this.doGenerate(options);
403
670
  if (response2 instanceof ReadableStream) {
@@ -457,106 +724,13 @@ var WorkersAIChatLanguageModel = class {
457
724
  if (!(response instanceof ReadableStream)) {
458
725
  throw new Error("This shouldn't happen");
459
726
  }
460
- const chunkEvent = events(new Response(response));
461
- let usage = { promptTokens: 0, completionTokens: 0 };
462
727
  return {
463
- stream: new ReadableStream({
464
- async start(controller) {
465
- for await (const event of chunkEvent) {
466
- if (!event.data) {
467
- continue;
468
- }
469
- if (event.data === "[DONE]") {
470
- break;
471
- }
472
- const chunk = JSON.parse(event.data);
473
- if (chunk.usage) {
474
- usage = mapWorkersAIUsage(chunk);
475
- }
476
- chunk.response?.length && controller.enqueue({
477
- type: "text-delta",
478
- textDelta: chunk.response
479
- });
480
- }
481
- controller.enqueue({
482
- type: "finish",
483
- finishReason: "stop",
484
- usage
485
- });
486
- controller.close();
487
- }
488
- }),
728
+ stream: getMappedStream(new Response(response)),
489
729
  rawCall: { rawPrompt: messages, rawSettings: args },
490
730
  warnings
491
731
  };
492
732
  }
493
733
  };
494
- function processToolCalls(output) {
495
- if (output.tool_calls && Array.isArray(output.tool_calls)) {
496
- return output.tool_calls.map((toolCall) => {
497
- if (toolCall.function && toolCall.id) {
498
- return {
499
- toolCallType: "function",
500
- toolCallId: toolCall.id,
501
- toolName: toolCall.function.name,
502
- args: typeof toolCall.function.arguments === "string" ? toolCall.function.arguments : JSON.stringify(toolCall.function.arguments || {})
503
- };
504
- }
505
- return {
506
- toolCallType: "function",
507
- toolCallId: toolCall.name,
508
- toolName: toolCall.name,
509
- args: typeof toolCall.arguments === "string" ? toolCall.arguments : JSON.stringify(toolCall.arguments || {})
510
- };
511
- });
512
- }
513
- return [];
514
- }
515
- function prepareToolsAndToolChoice(mode) {
516
- const tools = mode.tools?.length ? mode.tools : void 0;
517
- if (tools == null) {
518
- return { tools: void 0, tool_choice: void 0 };
519
- }
520
- const mappedTools = tools.map((tool) => ({
521
- type: "function",
522
- function: {
523
- name: tool.name,
524
- // @ts-expect-error - description is not a property of tool
525
- description: tool.description,
526
- // @ts-expect-error - parameters is not a property of tool
527
- parameters: tool.parameters
528
- }
529
- }));
530
- const toolChoice = mode.toolChoice;
531
- if (toolChoice == null) {
532
- return { tools: mappedTools, tool_choice: void 0 };
533
- }
534
- const type = toolChoice.type;
535
- switch (type) {
536
- case "auto":
537
- return { tools: mappedTools, tool_choice: type };
538
- case "none":
539
- return { tools: mappedTools, tool_choice: type };
540
- case "required":
541
- return { tools: mappedTools, tool_choice: "any" };
542
- // workersAI does not support tool mode directly,
543
- // so we filter the tools and force the tool choice through 'any'
544
- case "tool":
545
- return {
546
- tools: mappedTools.filter(
547
- (tool) => tool.function.name === toolChoice.toolName
548
- ),
549
- tool_choice: "any"
550
- };
551
- default: {
552
- const exhaustiveCheck = type;
553
- throw new Error(`Unsupported tool choice type: ${exhaustiveCheck}`);
554
- }
555
- }
556
- }
557
- function lastMessageWasUser(messages) {
558
- return messages.length > 0 && messages[messages.length - 1].role === "user";
559
- }
560
734
 
561
735
  // src/workersai-image-model.ts
562
736
  var WorkersAIImageModel = class {
@@ -671,6 +845,11 @@ function createWorkersAI(options) {
671
845
  binding,
672
846
  gateway: options.gateway
673
847
  });
848
+ const createEmbeddingModel = (modelId, settings = {}) => new WorkersAIEmbeddingModel(modelId, settings, {
849
+ provider: "workersai.embedding",
850
+ binding,
851
+ gateway: options.gateway
852
+ });
674
853
  const provider = (modelId, settings) => {
675
854
  if (new.target) {
676
855
  throw new Error("The WorkersAI model function cannot be called with the new keyword.");
@@ -678,11 +857,30 @@ function createWorkersAI(options) {
678
857
  return createChatModel(modelId, settings);
679
858
  };
680
859
  provider.chat = createChatModel;
860
+ provider.embedding = createEmbeddingModel;
861
+ provider.textEmbedding = createEmbeddingModel;
862
+ provider.textEmbeddingModel = createEmbeddingModel;
681
863
  provider.image = createImageModel;
682
864
  provider.imageModel = createImageModel;
683
865
  return provider;
684
866
  }
867
+ function createAutoRAG(options) {
868
+ const binding = options.binding;
869
+ const createChatModel = (settings = {}) => new AutoRAGChatLanguageModel("@cf/meta/llama-3.3-70b-instruct-fp8-fast", settings, {
870
+ provider: "autorag.chat",
871
+ binding
872
+ });
873
+ const provider = (settings) => {
874
+ if (new.target) {
875
+ throw new Error("The WorkersAI model function cannot be called with the new keyword.");
876
+ }
877
+ return createChatModel(settings);
878
+ };
879
+ provider.chat = createChatModel;
880
+ return provider;
881
+ }
685
882
  export {
883
+ createAutoRAG,
686
884
  createWorkersAI
687
885
  };
688
886
  //# sourceMappingURL=index.js.map