Files
zjdataai-app/frontend/app/components/ui/chat/chat-input.tsx
T

128 lines
3.4 KiB
TypeScript

import { JSONValue } from "ai";
import { useState } from "react";
import { Button } from "../button";
import { DocumentPreview } from "../document-preview";
import FileUploader from "../file-uploader";
import { Input } from "../input";
import UploadImagePreview from "../upload-image-preview";
import { ChatHandler } from "./chat.interface";
import { useFile } from "./hooks/use-file";
import { LlamaCloudSelector } from "./widgets/LlamaCloudSelector";
const ALLOWED_EXTENSIONS = ["png", "jpg", "jpeg", "csv", "pdf", "txt", "docx"];
export default function ChatInput(
props: Pick<
ChatHandler,
| "isLoading"
| "input"
| "onFileUpload"
| "onFileError"
| "handleSubmit"
| "handleInputChange"
| "messages"
| "setInput"
| "append"
> & {
requestParams?: any;
},
) {
const {
imageUrl,
setImageUrl,
uploadFile,
files,
removeDoc,
reset,
getAnnotations,
} = useFile();
const [requestData, setRequestData] = useState<any>();
// default submit function does not handle including annotations in the message
// so we need to use append function to submit new message with annotations
const handleSubmitWithAnnotations = (
e: React.FormEvent<HTMLFormElement>,
annotations: JSONValue[] | undefined,
) => {
e.preventDefault();
props.append!(
{
content: props.input,
role: "user",
createdAt: new Date(),
annotations,
},
{ data: requestData },
);
props.setInput!("");
};
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
const annotations = getAnnotations();
if (annotations.length) {
handleSubmitWithAnnotations(e, annotations);
return reset();
}
props.handleSubmit(e, { data: requestData });
};
const handleUploadFile = async (file: File) => {
if (imageUrl || files.length > 0) {
alert("同一时刻只能上传一个文件。");
return;
}
try {
await uploadFile(file, props.requestParams);
props.onFileUpload?.(file);
} catch (error: any) {
props.onFileError?.(error.message);
}
};
return (
<form
onSubmit={onSubmit}
className="rounded-xl bg-white p-4 shadow-xl space-y-4 shrink-0"
>
{imageUrl && (
<UploadImagePreview url={imageUrl} onRemove={() => setImageUrl(null)} />
)}
{files.length > 0 && (
<div className="flex gap-4 w-full overflow-auto py-2">
{files.map((file) => (
<DocumentPreview
key={file.id}
file={file}
onRemove={() => removeDoc(file)}
/>
))}
</div>
)}
<div className="flex w-full items-start justify-between gap-4 ">
<Input
autoFocus
name="message"
placeholder="请输入消息"
className="flex-1"
value={props.input}
onChange={props.handleInputChange}
/>
<FileUploader
onFileUpload={handleUploadFile}
onFileError={props.onFileError}
config={{
allowedExtensions: ALLOWED_EXTENSIONS,
disabled: props.isLoading,
}}
/>
{process.env.NEXT_PUBLIC_USE_LLAMACLOUD === "true" && (
<LlamaCloudSelector setRequestData={setRequestData} />
)}
<Button type="submit" disabled={props.isLoading || !props.input.trim()}>
发送
</Button>
</div>
</form>
);
}