@friendliai/ai-provider 1.0.0-beta.1 → 1.0.0-beta.2

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.mjs CHANGED
@@ -17,7 +17,6 @@ import {
17
17
  import {
18
18
  combineHeaders,
19
19
  createEventSourceResponseHandler,
20
- createJsonErrorResponseHandler as createJsonErrorResponseHandler2,
21
20
  createJsonResponseHandler,
22
21
  generateId,
23
22
  isParsableJson,
@@ -27,17 +26,88 @@ import {
27
26
  import { z as z2 } from "zod/v4";
28
27
 
29
28
  // src/friendli-error.ts
30
- import { createJsonErrorResponseHandler } from "@ai-sdk/provider-utils";
29
+ import { APICallError } from "@ai-sdk/provider";
30
+ import { safeParseJSON } from "@ai-sdk/provider-utils";
31
31
  import { z } from "zod/v4";
32
- var friendliaiErrorSchema = z.object({
32
+ var friendliErrorResponseSchema = z.object({
33
33
  message: z.string(),
34
- error: z.record(z.string(), z.any())
34
+ error: z.record(z.string(), z.any()).optional()
35
35
  });
36
+ var openAIStyleErrorResponseSchema = z.object({
37
+ error: z.object({
38
+ message: z.string()
39
+ }).loose()
40
+ }).loose();
41
+ var friendliaiErrorSchema = z.union([
42
+ // OpenAI/OpenRouter style error: { "error": { "message": "..." } }
43
+ openAIStyleErrorResponseSchema,
44
+ // Friendli style error: { "message": "...", "error": { ... } }
45
+ friendliErrorResponseSchema
46
+ ]);
36
47
  var friendliaiErrorStructure = {
37
48
  errorSchema: friendliaiErrorSchema,
38
- errorToMessage: (data) => data.message
49
+ errorToMessage: (data) => {
50
+ if (typeof data === "object" && data != null && "error" in data && typeof data.error === "object" && data.error != null && "message" in data.error && typeof data.error.message === "string") {
51
+ return data.error.message;
52
+ }
53
+ if (typeof data === "object" && data != null && "message" in data && typeof data.message === "string") {
54
+ return data.message;
55
+ }
56
+ return "Unknown error";
57
+ }
58
+ };
59
+ var friendliaiFailedResponseHandler = async ({
60
+ response,
61
+ url,
62
+ requestBodyValues
63
+ }) => {
64
+ const responseBody = await response.text();
65
+ const responseHeaders = {};
66
+ response.headers.forEach((value, key) => {
67
+ responseHeaders[key] = value;
68
+ });
69
+ const baseErrorOptions = {
70
+ url,
71
+ requestBodyValues,
72
+ statusCode: response.status,
73
+ responseHeaders,
74
+ responseBody
75
+ };
76
+ const trimmedBody = responseBody.trim();
77
+ if (trimmedBody === "") {
78
+ const fallback2 = response.statusText || `Request failed with status ${response.status}`;
79
+ return {
80
+ responseHeaders,
81
+ value: new APICallError({
82
+ message: fallback2,
83
+ ...baseErrorOptions
84
+ })
85
+ };
86
+ }
87
+ const parsedError = await safeParseJSON({
88
+ text: responseBody,
89
+ schema: friendliaiErrorSchema
90
+ });
91
+ if (parsedError.success) {
92
+ return {
93
+ responseHeaders,
94
+ value: new APICallError({
95
+ message: friendliaiErrorStructure.errorToMessage(parsedError.value),
96
+ data: parsedError.value,
97
+ ...baseErrorOptions
98
+ })
99
+ };
100
+ }
101
+ const fallback = trimmedBody || response.statusText || `Request failed with status ${response.status}`;
102
+ return {
103
+ responseHeaders,
104
+ value: new APICallError({
105
+ message: fallback,
106
+ cause: parsedError.error,
107
+ ...baseErrorOptions
108
+ })
109
+ };
39
110
  };
40
- var friendliaiFailedResponseHandler = createJsonErrorResponseHandler(friendliaiErrorStructure);
41
111
 
42
112
  // src/friendli-prepare-tools.ts
43
113
  import {
@@ -57,7 +127,9 @@ function prepareTools({
57
127
  for (const tool of tools) {
58
128
  if (tool.type === "provider") {
59
129
  openaiCompatTools.push({
60
- // NOTE: It would be better to use tool.name, but since ":" is replaced with "_", the following code is used instead
130
+ // NOTE: Friendli tool-assisted API expects provider tool types like "web:search".
131
+ // We derive it from the provider tool id (e.g. "friendli.web:search" -> "web:search")
132
+ // instead of tool.name (often "web_search").
61
133
  type: (_a = tool.id.split(".")[1]) != null ? _a : "unknown"
62
134
  });
63
135
  } else {
@@ -99,6 +171,28 @@ function prepareTools({
99
171
  }
100
172
 
101
173
  // src/friendli-chat-language-model.ts
174
+ function isRecord(value) {
175
+ return typeof value === "object" && value != null;
176
+ }
177
+ function isHostedToolExecutionChunk(value) {
178
+ if (!isRecord(value)) return false;
179
+ return typeof value.status === "string" && typeof value.name === "string" && Array.isArray(value.parameters);
180
+ }
181
+ function getChunkErrorMessage(value) {
182
+ if (!isRecord(value)) return void 0;
183
+ if (typeof value.message === "string") {
184
+ return value.message;
185
+ }
186
+ const nestedError = value.error;
187
+ if (isRecord(nestedError) && typeof nestedError.message === "string") {
188
+ return nestedError.message;
189
+ }
190
+ return void 0;
191
+ }
192
+ function isOpenAIChatChunk(value) {
193
+ if (!isRecord(value)) return false;
194
+ return Array.isArray(value.choices);
195
+ }
102
196
  var FriendliAIChatLanguageModel = class {
103
197
  // type inferred via constructor
104
198
  constructor(modelId, config) {
@@ -108,7 +202,7 @@ var FriendliAIChatLanguageModel = class {
108
202
  this.config = config;
109
203
  const errorStructure = friendliaiErrorStructure;
110
204
  this.chunkSchema = createOpenAICompatibleChatChunkSchema(errorStructure.errorSchema);
111
- this.failedResponseHandler = createJsonErrorResponseHandler2(friendliaiErrorStructure);
205
+ this.failedResponseHandler = friendliaiFailedResponseHandler;
112
206
  this.supportsStructuredOutputs = (_a = config.supportsStructuredOutputs) != null ? _a : true;
113
207
  }
114
208
  get provider() {
@@ -136,14 +230,20 @@ var FriendliAIChatLanguageModel = class {
136
230
  }) {
137
231
  var _a;
138
232
  const warnings = [];
139
- if (topK != null) {
140
- warnings.push({ type: "unsupported", feature: "topK" });
141
- }
142
233
  const friendliOptions = await parseProviderOptions({
234
+ provider: "friendliai",
235
+ providerOptions,
236
+ schema: friendliProviderOptionsSchema
237
+ });
238
+ const legacyFriendliOptions = await parseProviderOptions({
143
239
  provider: "friendli",
144
240
  providerOptions,
145
241
  schema: friendliProviderOptionsSchema
146
242
  });
243
+ const options = {
244
+ ...legacyFriendliOptions,
245
+ ...friendliOptions
246
+ };
147
247
  if ((responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null && !this.supportsStructuredOutputs) {
148
248
  warnings.push({
149
249
  type: "unsupported",
@@ -159,6 +259,14 @@ var FriendliAIChatLanguageModel = class {
159
259
  tools,
160
260
  toolChoice
161
261
  });
262
+ const isToolsPresent = openaiTools != null && openaiTools.length > 0;
263
+ if (isToolsPresent && (responseFormat != null || (options == null ? void 0 : options.regex) != null)) {
264
+ warnings.push({
265
+ type: "unsupported",
266
+ feature: "responseFormat",
267
+ details: "response_format is not supported when tools are present."
268
+ });
269
+ }
162
270
  return {
163
271
  args: {
164
272
  // >>> hard-coded default options >>>
@@ -170,30 +278,33 @@ var FriendliAIChatLanguageModel = class {
170
278
  max_tokens: maxOutputTokens,
171
279
  temperature,
172
280
  top_p: topP,
281
+ top_k: topK,
173
282
  frequency_penalty: frequencyPenalty,
174
283
  presence_penalty: presencePenalty,
175
- response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? this.supportsStructuredOutputs === true && responseFormat.schema != null ? {
284
+ response_format: isToolsPresent === false ? (responseFormat == null ? void 0 : responseFormat.type) === "json" ? this.supportsStructuredOutputs === true && responseFormat.schema != null ? {
176
285
  type: "json_schema",
177
286
  json_schema: {
178
287
  schema: responseFormat.schema,
179
288
  name: (_a = responseFormat.name) != null ? _a : "response",
180
289
  description: responseFormat.description
181
290
  }
182
- } : { type: "json_object" } : (friendliOptions == null ? void 0 : friendliOptions.regex) != null ? {
291
+ } : { type: "json_object" } : (options == null ? void 0 : options.regex) != null ? {
183
292
  type: "regex",
184
- schema: friendliOptions.regex
185
- } : void 0,
293
+ schema: options.regex
294
+ } : void 0 : void 0,
186
295
  stop: stopSequences,
187
296
  seed,
188
- ...(friendliOptions == null ? void 0 : friendliOptions.chat_template_kwargs) ? { chat_template_kwargs: friendliOptions.chat_template_kwargs } : {},
189
- // ...providerOptions?.[this.providerOptionsName],
190
- // reasoning_effort: compatibleOptions.reasoningEffort,
297
+ min_p: options == null ? void 0 : options.minP,
298
+ repetition_penalty: options == null ? void 0 : options.repetitionPenalty,
299
+ xtc_threshold: options == null ? void 0 : options.xtcThreshold,
300
+ xtc_probability: options == null ? void 0 : options.xtcProbability,
301
+ ...(options == null ? void 0 : options.chat_template_kwargs) ? { chat_template_kwargs: options.chat_template_kwargs } : {},
191
302
  // messages:
192
303
  messages: convertToOpenAICompatibleChatMessages(prompt),
193
304
  // tools:
194
305
  tools: openaiTools,
195
306
  tool_choice: openaiToolChoice,
196
- parallel_tool_calls: friendliOptions == null ? void 0 : friendliOptions.parallelToolCalls
307
+ parallel_tool_calls: options == null ? void 0 : options.parallelToolCalls
197
308
  },
198
309
  warnings: [...warnings, ...toolWarnings]
199
310
  };
@@ -312,9 +423,10 @@ var FriendliAIChatLanguageModel = class {
312
423
  start(controller) {
313
424
  controller.enqueue({ type: "stream-start", warnings });
314
425
  },
315
- // TODO we lost type safety on Chunk, most likely due to the error schema. MUST FIX
426
+ // NOTE: Chunk values can contain OpenAI-compatible deltas, hosted tool events, and error events.
427
+ // We narrow with type guards for safe handling.
316
428
  transform(chunk, controller) {
317
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
429
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
318
430
  if (!chunk.success) {
319
431
  finishReason = "error";
320
432
  controller.enqueue({ type: "error", error: chunk.error });
@@ -322,7 +434,7 @@ var FriendliAIChatLanguageModel = class {
322
434
  }
323
435
  const value = chunk.value;
324
436
  metadataExtractor == null ? void 0 : metadataExtractor.processChunk(chunk.rawValue);
325
- if ("status" in value) {
437
+ if (isHostedToolExecutionChunk(value)) {
326
438
  const toolCallId = (_a2 = value.tool_call_id) != null ? _a2 : generateId();
327
439
  switch (value.status) {
328
440
  case "STARTED":
@@ -365,26 +477,36 @@ var FriendliAIChatLanguageModel = class {
365
477
  }
366
478
  return;
367
479
  }
368
- if ("error" in value) {
480
+ const chunkErrorMessage = getChunkErrorMessage(value);
481
+ if (chunkErrorMessage != null) {
369
482
  finishReason = "error";
370
- controller.enqueue({ type: "error", error: value.error.message });
483
+ controller.enqueue({ type: "error", error: chunkErrorMessage });
371
484
  return;
372
485
  }
486
+ if (!isOpenAIChatChunk(value)) {
487
+ finishReason = "error";
488
+ controller.enqueue({
489
+ type: "error",
490
+ error: new Error("Unsupported chunk shape")
491
+ });
492
+ return;
493
+ }
494
+ const chunkValue = value;
373
495
  if (isFirstChunk) {
374
496
  isFirstChunk = false;
375
497
  controller.enqueue({
376
498
  type: "response-metadata",
377
- ...getResponseMetadata(value)
499
+ ...getResponseMetadata(chunkValue)
378
500
  });
379
501
  }
380
- if (value.usage != null) {
502
+ if (chunkValue.usage != null) {
381
503
  const {
382
504
  prompt_tokens,
383
505
  completion_tokens,
384
506
  total_tokens,
385
507
  prompt_tokens_details,
386
508
  completion_tokens_details
387
- } = value.usage;
509
+ } = chunkValue.usage;
388
510
  usage.promptTokens = prompt_tokens != null ? prompt_tokens : void 0;
389
511
  usage.completionTokens = completion_tokens != null ? completion_tokens : void 0;
390
512
  usage.totalTokens = total_tokens != null ? total_tokens : void 0;
@@ -401,7 +523,7 @@ var FriendliAIChatLanguageModel = class {
401
523
  usage.promptTokensDetails.cachedTokens = prompt_tokens_details == null ? void 0 : prompt_tokens_details.cached_tokens;
402
524
  }
403
525
  }
404
- const choice = value.choices[0];
526
+ const choice = chunkValue.choices[0];
405
527
  if ((choice == null ? void 0 : choice.finish_reason) != null) {
406
528
  finishReason = mapOpenAICompatibleFinishReason(choice.finish_reason);
407
529
  }
@@ -485,12 +607,12 @@ var FriendliAIChatLanguageModel = class {
485
607
  controller.enqueue({
486
608
  type: "tool-input-delta",
487
609
  id: toolCall.id,
488
- delta: (_m = toolCallDelta.function.arguments) != null ? _m : ""
610
+ delta: (_n = (_m = toolCallDelta.function) == null ? void 0 : _m.arguments) != null ? _n : ""
489
611
  });
490
- if (((_n = toolCall.function) == null ? void 0 : _n.name) != null && ((_o = toolCall.function) == null ? void 0 : _o.arguments) != null && isParsableJson(toolCall.function.arguments)) {
612
+ if (((_o = toolCall.function) == null ? void 0 : _o.name) != null && ((_p = toolCall.function) == null ? void 0 : _p.arguments) != null && isParsableJson(toolCall.function.arguments)) {
491
613
  controller.enqueue({
492
614
  type: "tool-call",
493
- toolCallId: (_p = toolCall.id) != null ? _p : generateId(),
615
+ toolCallId: (_q = toolCall.id) != null ? _q : generateId(),
494
616
  toolName: toolCall.function.name,
495
617
  input: toolCall.function.arguments
496
618
  });
@@ -537,86 +659,6 @@ var FriendliAIChatLanguageModel = class {
537
659
  };
538
660
  }
539
661
  };
540
- var friendliAIChatResponseSchema = z2.object({
541
- id: z2.string().nullish(),
542
- created: z2.number().nullish(),
543
- model: z2.string().nullish(),
544
- choices: z2.array(
545
- z2.object({
546
- message: z2.object({
547
- role: z2.literal("assistant").nullish(),
548
- content: z2.string().nullish(),
549
- tool_calls: z2.array(
550
- z2.object({
551
- id: z2.string().nullish(),
552
- type: z2.literal("function"),
553
- function: z2.object({
554
- name: z2.string(),
555
- arguments: z2.union([z2.string(), z2.any()]).nullish()
556
- })
557
- })
558
- ).nullish()
559
- }),
560
- finish_reason: z2.string().nullish()
561
- })
562
- ),
563
- usage: z2.object({
564
- prompt_tokens: z2.number().nullish(),
565
- completion_tokens: z2.number().nullish()
566
- }).nullish()
567
- });
568
- var friendliaiChatChunkSchema = z2.union([
569
- z2.object({
570
- id: z2.string().nullish(),
571
- created: z2.number().nullish(),
572
- model: z2.string().nullish(),
573
- choices: z2.array(
574
- z2.object({
575
- delta: z2.object({
576
- role: z2.enum(["assistant"]).nullish(),
577
- content: z2.string().nullish(),
578
- tool_calls: z2.array(
579
- z2.object({
580
- index: z2.number(),
581
- id: z2.string().nullish(),
582
- type: z2.literal("function").optional(),
583
- function: z2.object({
584
- name: z2.string().nullish(),
585
- arguments: z2.string().nullish()
586
- })
587
- })
588
- ).nullish()
589
- }).nullish(),
590
- finish_reason: z2.string().nullish()
591
- })
592
- ),
593
- usage: z2.object({
594
- prompt_tokens: z2.number().nullish(),
595
- completion_tokens: z2.number().nullish()
596
- }).nullish()
597
- }),
598
- z2.object({
599
- name: z2.string(),
600
- status: z2.enum(["ENDED", "STARTED", "ERRORED", "UPDATING"]),
601
- message: z2.null(),
602
- parameters: z2.array(
603
- z2.object({
604
- name: z2.string(),
605
- value: z2.string()
606
- })
607
- ),
608
- result: z2.string().nullable(),
609
- error: z2.object({
610
- type: z2.enum(["INVALID_PARAMETER", "UNKNOWN"]),
611
- msg: z2.string()
612
- }).nullable(),
613
- timestamp: z2.number(),
614
- usage: z2.null(),
615
- tool_call_id: z2.string().nullable()
616
- // temporary fix for "file:text" tool calls
617
- }),
618
- friendliaiErrorSchema
619
- ]);
620
662
  var openaiCompatibleTokenUsageSchema = z2.object({
621
663
  prompt_tokens: z2.number().nullish(),
622
664
  completion_tokens: z2.number().nullish(),
@@ -715,7 +757,23 @@ var friendliProviderOptionsSchema = z2.object({
715
757
  */
716
758
  // regex: z.instanceof(RegExp).nullish(),
717
759
  regex: z2.string().nullish(),
718
- chat_template_kwargs: z2.record(z2.string(), z2.any()).nullish()
760
+ chat_template_kwargs: z2.record(z2.string(), z2.any()).nullish(),
761
+ /**
762
+ * A scaling factor used to determine the minimum token probability threshold.
763
+ */
764
+ minP: z2.number().nullish(),
765
+ /**
766
+ * Penalizes tokens that have already appeared in the generated result.
767
+ */
768
+ repetitionPenalty: z2.number().nullish(),
769
+ /**
770
+ * A probability threshold used to identify “top choice” tokens for exclusion in XTC sampling.
771
+ */
772
+ xtcThreshold: z2.number().nullish(),
773
+ /**
774
+ * The probability that XTC (Exclude Top Choices) filtering will be applied for each sampling decision.
775
+ */
776
+ xtcProbability: z2.number().nullish()
719
777
  });
720
778
 
721
779
  // src/friendli-settings.ts
@@ -745,54 +803,65 @@ var FriendliAIServerlessModelIds = [
745
803
  ];
746
804
 
747
805
  // src/friendli-tools.ts
748
- import { jsonSchema } from "@ai-sdk/provider-utils";
806
+ import { createProviderToolFactoryWithOutputSchema } from "@ai-sdk/provider-utils";
807
+ import { z as z3 } from "zod/v4";
808
+ var inputSchema = z3.object({}).loose();
809
+ var outputSchema = z3.unknown();
810
+ var webSearchTool = createProviderToolFactoryWithOutputSchema({
811
+ id: "friendli.web:search",
812
+ inputSchema,
813
+ outputSchema
814
+ });
815
+ var webUrlTool = createProviderToolFactoryWithOutputSchema({
816
+ id: "friendli.web:url",
817
+ inputSchema,
818
+ outputSchema
819
+ });
820
+ var mathCalendarTool = createProviderToolFactoryWithOutputSchema({
821
+ id: "friendli.math:calendar",
822
+ inputSchema,
823
+ outputSchema
824
+ });
825
+ var mathStatisticsTool = createProviderToolFactoryWithOutputSchema({
826
+ id: "friendli.math:statistics",
827
+ inputSchema,
828
+ outputSchema
829
+ });
830
+ var mathCalculatorTool = createProviderToolFactoryWithOutputSchema({
831
+ id: "friendli.math:calculator",
832
+ inputSchema,
833
+ outputSchema
834
+ });
835
+ var codePythonInterpreterTool = createProviderToolFactoryWithOutputSchema({
836
+ id: "friendli.code:python-interpreter",
837
+ inputSchema,
838
+ outputSchema
839
+ });
840
+ var linkupSearchTool = createProviderToolFactoryWithOutputSchema({
841
+ id: "friendli.linkup:search",
842
+ inputSchema,
843
+ outputSchema
844
+ });
749
845
  function webSearch() {
750
- return {
751
- type: "provider",
752
- id: "friendli.web:search",
753
- args: {},
754
- inputSchema: jsonSchema({ type: "object", properties: {} })
755
- };
846
+ return webSearchTool({});
756
847
  }
757
848
  function webUrl() {
758
- return {
759
- type: "provider",
760
- id: "friendli.web:url",
761
- args: {},
762
- inputSchema: jsonSchema({ type: "object", properties: {} })
763
- };
849
+ return webUrlTool({});
764
850
  }
765
851
  function mathCalendar() {
766
- return {
767
- type: "provider",
768
- id: "friendli.math:calendar",
769
- args: {},
770
- inputSchema: jsonSchema({ type: "object", properties: {} })
771
- };
852
+ return mathCalendarTool({});
772
853
  }
773
854
  function mathStatistics() {
774
- return {
775
- type: "provider",
776
- id: "friendli.math:statistics",
777
- args: {},
778
- inputSchema: jsonSchema({ type: "object", properties: {} })
779
- };
855
+ return mathStatisticsTool({});
780
856
  }
781
857
  function mathCalculator() {
782
- return {
783
- type: "provider",
784
- id: "friendli.math:calculator",
785
- args: {},
786
- inputSchema: jsonSchema({ type: "object", properties: {} })
787
- };
858
+ return mathCalculatorTool({});
788
859
  }
789
860
  function codePythonInterpreter() {
790
- return {
791
- type: "provider",
792
- id: "friendli.code:python-interpreter",
793
- args: {},
794
- inputSchema: jsonSchema({ type: "object", properties: {} })
795
- };
861
+ return codePythonInterpreterTool({});
862
+ }
863
+ function linkupSearch() {
864
+ return linkupSearchTool({});
796
865
  }
797
866
  var friendliTools = {
798
867
  webSearch,
@@ -800,7 +869,8 @@ var friendliTools = {
800
869
  mathCalendar,
801
870
  mathStatistics,
802
871
  mathCalculator,
803
- codePythonInterpreter
872
+ codePythonInterpreter,
873
+ linkupSearch
804
874
  };
805
875
 
806
876
  // src/get-available-models.ts
@@ -967,7 +1037,8 @@ function createFriendli(options = {}) {
967
1037
  provider: `friendliai.${type}.chat`,
968
1038
  url: ({ path }) => `${baseURL}${path}`,
969
1039
  headers: getHeaders,
970
- fetch: options.fetch
1040
+ fetch: options.fetch,
1041
+ includeUsage: options.includeUsage
971
1042
  });
972
1043
  };
973
1044
  const createCompletionModel = (modelId) => {