@meetsmore-oss/use-ai-client 1.15.0 → 1.17.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/bundled.js CHANGED
@@ -1333,7 +1333,9 @@ var defaultStrings = {
1333
1333
  /** File size error (use {filename} and {maxSize} placeholders) */
1334
1334
  fileSizeError: 'File "{filename}" exceeds {maxSize}MB limit',
1335
1335
  /** File type error (use {type} placeholder) */
1336
- fileTypeError: 'File type "{type}" is not accepted'
1336
+ fileTypeError: 'File type "{type}" is not accepted',
1337
+ /** Error when too many files are attached (use {max} placeholder) */
1338
+ maxAttachmentsError: "You can attach at most {max} files per message"
1337
1339
  },
1338
1340
  // Floating button
1339
1341
  floatingButton: {
@@ -18289,6 +18291,9 @@ async function getTransformedContent(files, transformer, context, onProgress) {
18289
18291
  transformationCache.set(cacheKey, results);
18290
18292
  return results;
18291
18293
  }
18294
+ function normalizeUploadResult(result) {
18295
+ return typeof result === "string" ? { url: result } : result;
18296
+ }
18292
18297
  async function toContentPart(attachment, backend) {
18293
18298
  if (attachment.transformedContent !== void 0) {
18294
18299
  return {
@@ -18301,16 +18306,12 @@ async function toContentPart(attachment, backend) {
18301
18306
  }
18302
18307
  };
18303
18308
  }
18304
- const url3 = await backend.prepareForSend(attachment.file);
18305
- if (attachment.file.type.startsWith("image/")) {
18306
- return { type: "image", url: url3 };
18309
+ const result = normalizeUploadResult(await backend.prepareForSend(attachment.file));
18310
+ const isImage = attachment.file.type.startsWith("image/");
18311
+ if ("ref" in result) {
18312
+ return isImage ? { type: "image_ref", ref: result.ref } : { type: "file_ref", ref: result.ref, mimeType: attachment.file.type, name: attachment.file.name };
18307
18313
  }
18308
- return {
18309
- type: "file",
18310
- url: url3,
18311
- mimeType: attachment.file.type,
18312
- name: attachment.file.name
18313
- };
18314
+ return isImage ? { type: "image_url", url: result.url } : { type: "file_url", url: result.url, mimeType: attachment.file.type, name: attachment.file.name };
18314
18315
  }
18315
18316
  async function processAttachments(attachments, config2) {
18316
18317
  const { getCurrentChat, backend = new EmbedFileUploadBackend(), transformers = {}, onFileProgress } = config2;
@@ -18464,7 +18465,12 @@ function useFileUpload({
18464
18465
  const enabled = config2 !== void 0;
18465
18466
  const maxFileSize = config2?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
18466
18467
  const acceptedTypes = config2?.acceptedTypes;
18468
+ const maxAttachments = config2?.maxAttachments;
18467
18469
  const transformers = config2?.transformers;
18470
+ const attachmentsRef = useRef3(attachments);
18471
+ useEffect3(() => {
18472
+ attachmentsRef.current = attachments;
18473
+ }, [attachments]);
18468
18474
  useEffect3(() => {
18469
18475
  if (fileError) {
18470
18476
  const timer = setTimeout(() => setFileError(null), 3e3);
@@ -18500,7 +18506,13 @@ function useFileUpload({
18500
18506
  }, [getCurrentChat, transformers]);
18501
18507
  const handleFiles = useCallback2(async (files) => {
18502
18508
  const fileArray = Array.from(files);
18509
+ let currentCount = attachmentsRef.current.length;
18503
18510
  for (const file2 of fileArray) {
18511
+ if (maxAttachments !== void 0 && currentCount >= maxAttachments) {
18512
+ const errorMsg = strings.fileUpload.maxAttachmentsError.replace("{max}", String(maxAttachments));
18513
+ setFileError(errorMsg);
18514
+ break;
18515
+ }
18504
18516
  if (file2.size > maxFileSize) {
18505
18517
  const errorMsg = strings.fileUpload.fileSizeError.replace("{filename}", file2.name).replace("{maxSize}", String(Math.round(maxFileSize / (1024 * 1024))));
18506
18518
  setFileError(errorMsg);
@@ -18519,14 +18531,17 @@ function useFileUpload({
18519
18531
  preview
18520
18532
  };
18521
18533
  setAttachments((prev) => [...prev, attachment]);
18534
+ attachmentsRef.current = [...attachmentsRef.current, attachment];
18535
+ currentCount++;
18522
18536
  const transformerKey = findTransformerPattern(file2.type, transformers);
18523
18537
  if (transformerKey) {
18524
18538
  runTransformer(attachmentId, file2, transformerKey);
18525
18539
  }
18526
18540
  }
18527
- }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);
18541
+ }, [maxFileSize, acceptedTypes, maxAttachments, strings, transformers, runTransformer]);
18528
18542
  const removeAttachment = useCallback2((id) => {
18529
18543
  setAttachments((prev) => prev.filter((a) => a.id !== id));
18544
+ attachmentsRef.current = attachmentsRef.current.filter((a) => a.id !== id);
18530
18545
  setProcessingState((prev) => {
18531
18546
  const next = new Map(prev);
18532
18547
  next.delete(id);
@@ -18535,6 +18550,7 @@ function useFileUpload({
18535
18550
  }, []);
18536
18551
  const clearAttachments = useCallback2(() => {
18537
18552
  setAttachments([]);
18553
+ attachmentsRef.current = [];
18538
18554
  setProcessingState(/* @__PURE__ */ new Map());
18539
18555
  }, []);
18540
18556
  const openFilePicker = useCallback2(() => {
@@ -19105,7 +19121,16 @@ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedCo
19105
19121
  );
19106
19122
  }
19107
19123
  function hasFileContent(content3) {
19108
- return Array.isArray(content3) && content3.some((part) => part.type === "file");
19124
+ return Array.isArray(content3) && content3.some((part) => part.type === "file" || part.type === "attachment_ref");
19125
+ }
19126
+ function fileChipInfo(part) {
19127
+ if (part.type === "file") {
19128
+ return { name: part.file.name, size: part.file.size };
19129
+ }
19130
+ if (part.type === "attachment_ref") {
19131
+ return { name: part.name, size: part.size };
19132
+ }
19133
+ return null;
19109
19134
  }
19110
19135
  function UseAIChatPanel({
19111
19136
  onSendMessage,
@@ -19778,14 +19803,10 @@ function UseAIChatPanel({
19778
19803
  wordWrap: "break-word"
19779
19804
  },
19780
19805
  children: [
19781
- message.role === "user" && hasFileContent(message.content) && /* @__PURE__ */ jsx12("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px", marginBottom: "8px" }, children: message.content.filter((part) => part.type === "file").map((part, idx) => /* @__PURE__ */ jsx12(
19782
- FilePlaceholder,
19783
- {
19784
- name: part.file.name,
19785
- size: part.file.size
19786
- },
19787
- idx
19788
- )) }),
19806
+ message.role === "user" && hasFileContent(message.content) && /* @__PURE__ */ jsx12("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px", marginBottom: "8px" }, children: message.content.flatMap((part, idx) => {
19807
+ const info = fileChipInfo(part);
19808
+ return info ? [/* @__PURE__ */ jsx12(FilePlaceholder, { name: info.name, size: info.size }, idx)] : [];
19809
+ }) }),
19789
19810
  message.role === "assistant" ? /* @__PURE__ */ jsxs9(Fragment, { children: [
19790
19811
  message.reasoningParts && message.reasoningParts.length > 0 && /* @__PURE__ */ jsx12(
19791
19812
  Reasoning,
@@ -23995,31 +24016,13 @@ var UseAIClient = class {
23995
24016
  async sendPrompt(prompt, multimodalContent, forwardedProps) {
23996
24017
  let messageContent = prompt;
23997
24018
  if (multimodalContent && multimodalContent.length > 0) {
23998
- messageContent = multimodalContent.map((part) => {
23999
- if (part.type === "text") {
24000
- return { type: "text", text: part.text };
24001
- } else if (part.type === "image") {
24002
- return { type: "image", url: part.url };
24003
- } else if (part.type === "file") {
24004
- return {
24005
- type: "file",
24006
- url: part.url,
24007
- mimeType: part.mimeType
24008
- };
24009
- } else {
24010
- return {
24011
- type: "transformed_file",
24012
- text: part.text,
24013
- originalFile: part.originalFile
24014
- };
24015
- }
24016
- });
24019
+ messageContent = multimodalContent;
24017
24020
  }
24018
24021
  const userMessage = {
24019
24022
  id: v4_default(),
24020
24023
  role: "user",
24024
+ // @ag-ui/core declares Message.content as `string`; the wire also carries MultimodalContent[].
24021
24025
  content: messageContent
24022
- // Type cast needed for Message type compatibility
24023
24026
  };
24024
24027
  this._messages.push(userMessage);
24025
24028
  const runId = v4_default();
@@ -38484,6 +38487,7 @@ function buildPersistedParts(message, attachments, fileContent) {
38484
38487
  parts2.push({ type: "text", text: message });
38485
38488
  }
38486
38489
  const transformedByKey = /* @__PURE__ */ new Map();
38490
+ const nonTransformedParts = [];
38487
38491
  for (const part of fileContent) {
38488
38492
  if (part.type === "transformed_file") {
38489
38493
  const key = `${part.originalFile.name}:${part.originalFile.size}:${part.originalFile.mimeType}`;
@@ -38493,6 +38497,8 @@ function buildPersistedParts(message, attachments, fileContent) {
38493
38497
  } else {
38494
38498
  transformedByKey.set(key, [part]);
38495
38499
  }
38500
+ } else {
38501
+ nonTransformedParts.push(part);
38496
38502
  }
38497
38503
  }
38498
38504
  for (const attachment of attachments) {
@@ -38504,6 +38510,18 @@ function buildPersistedParts(message, attachments, fileContent) {
38504
38510
  text: transformed.text,
38505
38511
  originalFile: transformed.originalFile
38506
38512
  });
38513
+ continue;
38514
+ }
38515
+ const part = nonTransformedParts.shift();
38516
+ const ref = part && (part.type === "image_ref" || part.type === "file_ref") ? part.ref : void 0;
38517
+ if (ref) {
38518
+ parts2.push({
38519
+ type: "attachment_ref",
38520
+ ref,
38521
+ name: attachment.file.name,
38522
+ mimeType: attachment.file.type,
38523
+ size: attachment.file.size
38524
+ });
38507
38525
  } else {
38508
38526
  parts2.push({
38509
38527
  type: "file",
@@ -38556,9 +38574,18 @@ function transformMessagesToClientFormat(persistedMessages) {
38556
38574
  ${p.text}`
38557
38575
  }];
38558
38576
  }
38577
+ if (p.type === "attachment_ref") {
38578
+ return [
38579
+ p.mimeType.startsWith("image/") ? { type: "image_ref", ref: p.ref } : { type: "file_ref", ref: p.ref, mimeType: p.mimeType, name: p.name }
38580
+ ];
38581
+ }
38559
38582
  return [];
38560
38583
  });
38561
- return { id: msg.id, role: "user", content: parts2 };
38584
+ return {
38585
+ id: msg.id,
38586
+ role: "user",
38587
+ content: parts2
38588
+ };
38562
38589
  }
38563
38590
  }
38564
38591
  });