refactor(client): update file handling in file upload functions

This commit is contained in:
dakai 2024-10-06 15:30:24 +08:00
parent 1f2dea448a
commit e6ef10e96e
5 changed files with 42 additions and 38 deletions

View File

@ -37,14 +37,14 @@ export interface MultimodalContent {
file_url?: { file_url?: {
url: string; url: string;
name: string; name: string;
tokenCount?: string; tokenCount?: number;
}; };
} }
export interface UploadFile { export interface UploadFile {
name: string; name: string;
url: string; url: string;
tokenCount?: string; tokenCount?: number;
} }
export interface RequestMessage { export interface RequestMessage {

View File

@ -71,37 +71,32 @@
border-radius: 5px; border-radius: 5px;
margin-right: 10px; margin-right: 10px;
.attach-file-name-full { %attach-file-name-common {
max-width:calc(62vw); display:flex;
display:flex;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.attach-file-name-full {
@extend %attach-file-name-common;
max-width:calc(62vw);
}
.attach-file-name-half { .attach-file-name-half {
@extend %attach-file-name-common;
max-width:calc(45vw); max-width:calc(45vw);
display:flex;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.attach-file-name-less { .attach-file-name-less {
@extend %attach-file-name-common;
max-width:calc(28vw); max-width:calc(28vw);
display:flex;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.attach-file-name-min { .attach-file-name-min {
@extend %attach-file-name-common;
max-width:calc(12vw); max-width:calc(12vw);
display:flex; }
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.attach-file-icon { .attach-file-icon {
min-width: 16px; min-width: 16px;
max-width: 16px; max-width: 16px;

View File

@ -80,6 +80,7 @@ import {
import type { UploadFile } from "../client/api"; import type { UploadFile } from "../client/api";
import { uploadImage as uploadImageRemote } from "@/app/utils/chat"; import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
import { uploadImage as uploadFileRemote } from "@/app/utils/chat";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
@ -1193,7 +1194,9 @@ function _Chat() {
setIsLoading(true); setIsLoading(true);
const textContent = getMessageTextContent(userMessage); const textContent = getMessageTextContent(userMessage);
const images = getMessageImages(userMessage); const images = getMessageImages(userMessage);
chatStore.onUserInput(textContent, images).then(() => setIsLoading(false)); chatStore
.onUserInput(textContent, images, attachFiles)
.then(() => setIsLoading(false));
inputRef.current?.focus(); inputRef.current?.focus();
}; };
@ -1488,20 +1491,20 @@ function _Chat() {
fileInput.multiple = true; fileInput.multiple = true;
fileInput.onchange = (event: any) => { fileInput.onchange = (event: any) => {
setUploading(true); setUploading(true);
const files = event.target.files; const inputFiles = event.target.files;
const imagesData: UploadFile[] = []; const imagesData: UploadFile[] = [];
(async () => { (async () => {
for (let i = 0; i < files.length; i++) { for (let i = 0; i < inputFiles.length; i++) {
const file = files[i]; const file = inputFiles[i];
try { try {
const dataUrl = await uploadImageRemote(file); const dataUrl = await uploadFileRemote(file);
const fileData: UploadFile = { name: file.name, url: dataUrl }; const fileData: UploadFile = { name: file.name, url: dataUrl };
const tokenCount = await countTokens(fileData); const tokenCount: number = await countTokens(fileData);
fileData.tokenCount = tokenCount; fileData.tokenCount = tokenCount;
imagesData.push(fileData); imagesData.push(fileData);
if ( if (
imagesData.length === 3 || imagesData.length === 3 ||
imagesData.length === files.length imagesData.length === inputFiles.length
) { ) {
setUploading(false); setUploading(false);
res(imagesData); res(imagesData);
@ -1945,7 +1948,7 @@ function _Chat() {
{getMessageFiles(message).length > 0 && ( {getMessageFiles(message).length > 0 && (
<div className={styles["chat-message-item-files"]}> <div className={styles["chat-message-item-files"]}>
{getMessageFiles(message).map((file, index) => { {getMessageFiles(message).map((file, index) => {
const extension: DefaultExtensionType = file.url const extension: DefaultExtensionType = file.name
.split(".") .split(".")
.pop() .pop()
?.toLowerCase() as DefaultExtensionType; ?.toLowerCase() as DefaultExtensionType;
@ -1970,7 +1973,7 @@ function _Chat() {
styles["chat-message-item-file-name"] styles["chat-message-item-file-name"]
} }
> >
{file.name} {file.tokenCount} {file.name} {file.tokenCount}K
</div> </div>
</a> </a>
); );
@ -2072,7 +2075,7 @@ function _Chat() {
{attachFiles.length != 0 && ( {attachFiles.length != 0 && (
<div className={styles["attach-files"]}> <div className={styles["attach-files"]}>
{attachFiles.map((file, index) => { {attachFiles.map((file, index) => {
const extension: DefaultExtensionType = file.url const extension: DefaultExtensionType = file.name
.split(".") .split(".")
.pop() .pop()
?.toLowerCase() as DefaultExtensionType; ?.toLowerCase() as DefaultExtensionType;
@ -2087,22 +2090,22 @@ function _Chat() {
</div> </div>
{attachImages.length == 0 && ( {attachImages.length == 0 && (
<div className={styles["attach-file-name-full"]}> <div className={styles["attach-file-name-full"]}>
{file.name} {file.tokenCount} {file.name} {file.tokenCount}K
</div> </div>
)} )}
{attachImages.length == 1 && ( {attachImages.length == 1 && (
<div className={styles["attach-file-name-half"]}> <div className={styles["attach-file-name-half"]}>
{file.name} {file.tokenCount} {file.name} {file.tokenCount}K
</div> </div>
)} )}
{attachImages.length == 2 && ( {attachImages.length == 2 && (
<div className={styles["attach-file-name-less"]}> <div className={styles["attach-file-name-less"]}>
{file.name} {file.tokenCount} {file.name} {file.tokenCount}K
</div> </div>
)} )}
{attachImages.length == 3 && ( {attachImages.length == 3 && (
<div className={styles["attach-file-name-min"]}> <div className={styles["attach-file-name-min"]}>
{file.name} {file.tokenCount} {file.name} {file.tokenCount}K
</div> </div>
)} )}

View File

@ -351,6 +351,7 @@ export const useChatStore = createPersistStore(
if (attachFiles && attachFiles.length > 0) { if (attachFiles && attachFiles.length > 0) {
let fileContent = userContent + " Here are the files: \n"; let fileContent = userContent + " Here are the files: \n";
for (let i = 0; i < attachFiles.length; i++) { for (let i = 0; i < attachFiles.length; i++) {
fileContent += attachFiles[i].name + "\n";
fileContent += await readFileContent(attachFiles[i]); fileContent += await readFileContent(attachFiles[i]);
} }
mContent = [ mContent = [

View File

@ -18,6 +18,10 @@ export function trimTopic(topic: string) {
} }
export const readFileContent = async (file: UploadFile): Promise<string> => { export const readFileContent = async (file: UploadFile): Promise<string> => {
const host_url = new URL(window.location.href);
if (!file.url.includes(host_url.host)) {
throw new Error(`The URL ${file.url} is not allowed to access.`);
}
try { try {
const response = await fetch(file.url); const response = await fetch(file.url);
if (!response.ok) { if (!response.ok) {
@ -25,9 +29,10 @@ export const readFileContent = async (file: UploadFile): Promise<string> => {
`Failed to fetch content from ${file.url}: ${response.statusText}`, `Failed to fetch content from ${file.url}: ${response.statusText}`,
); );
} }
const content = await response.text(); //const content = await response.text();
const result = file.name + "\n" + content; //const result = file.name + "\n" + content;
return result; //return result;
return await response.text();
} catch (error) { } catch (error) {
console.error("Error reading file content:", error); console.error("Error reading file content:", error);
return ""; return "";
@ -60,7 +65,7 @@ export const countTokens = async (file: UploadFile) => {
totalTokens += 0.98; totalTokens += 0.98;
} }
} }
let totalTokenCount = (totalTokens / 1000).toFixed(2).toString() + "K"; const totalTokenCount: number = +(totalTokens / 1000).toFixed(2);
return totalTokenCount; return totalTokenCount;
}; };
@ -303,8 +308,8 @@ export function getMessageFiles(message: RequestMessage): UploadFile[] {
} }
const files: UploadFile[] = []; const files: UploadFile[] = [];
for (const c of message.content) { for (const c of message.content) {
if (c.type === "file_url") { if (c.type === "file_url" && c.file_url) {
files.push(c.file_url ? c.file_url : { name: "", url: "" }); files.push(c.file_url);
} }
} }
return files; return files;