初始化提交

This commit is contained in:
2024-08-13 09:37:23 +08:00
parent 4923337038
commit e112fa4e44
50 changed files with 1649 additions and 259 deletions
@@ -14,13 +14,13 @@ export default function ChatActions(
{props.showStop && (
<Button variant="outline" size="sm" onClick={props.stop}>
<PauseCircle className="mr-2 h-4 w-4" />
Stop generating
</Button>
)}
{props.showReload && (
<Button variant="outline" size="sm" onClick={props.reload}>
<RefreshCw className="mr-2 h-4 w-4" />
Regenerate
</Button>
)}
</div>
@@ -68,7 +68,7 @@ export default function ChatInput(
const handleUploadFile = async (file: File) => {
if (imageUrl || files.length > 0) {
alert("You can only upload one file at a time.");
alert("同一时刻只能上传一个文件。");
return;
}
try {
@@ -99,10 +99,11 @@ export default function ChatInput(
</div>
)}
<div className="flex w-full items-start justify-between gap-4 ">
<Input
<textarea
autoFocus
rows={2}
name="message"
placeholder="Type a message"
placeholder="请输入消息"
className="flex-1"
value={props.input}
onChange={props.handleInputChange}
@@ -119,7 +120,7 @@ export default function ChatInput(
<LlamaCloudSelector setRequestData={setRequestData} />
)}
<Button type="submit" disabled={props.isLoading || !props.input.trim()}>
Send message
</Button>
</div>
</form>
@@ -17,7 +17,7 @@ export function ChatEvents({
}) {
const [isOpen, setIsOpen] = useState(false);
const buttonLabel = isOpen ? "Hide events" : "Show events";
const buttonLabel = isOpen ? "隐藏" : "详情";
const EventIcon = isOpen ? (
<ChevronDown className="h-4 w-4" />
@@ -9,23 +9,33 @@ import {
import { useCopyToClipboard } from "../hooks/use-copy-to-clipboard";
import { SourceData } from "../index";
import PdfDialog from "../widgets/PdfDialog";
import { useClientConfig } from "../hooks/use-config";
const SCORE_THRESHOLD = 0.3;
function SourceNumberButton({ index }: { index: number }) {
function truncateNumber(num: number | undefined, precision: number): number {
if (num == undefined || num == 0) return 0;
const factor = Math.pow(10, precision);
return Math.trunc(num * factor) / factor;
}
function SourceNumberButton({ index, score }: { index: number, score: number | undefined }) {
return (
<div className="text-xs w-5 h-5 rounded-full bg-gray-100 mb-2 flex items-center justify-center hover:text-white hover:bg-primary hover:cursor-pointer">
{index + 1}
<div className="text-xs w-45 h-45 rounded-full bg-gray-100 mb-2 flex items-center justify-center hover:text-white hover:bg-primary hover:cursor-pointer">
{truncateNumber(score, 2)}
</div>
);
}
type NodeInfo = {
id: string;
score?: number;
text: string;
url?: string;
};
export function ChatSources({ data }: { data: SourceData }) {
const { backend } = useClientConfig();
const sources: NodeInfo[] = useMemo(() => {
// aggregate nodes by url or file_path (get the highest one by score)
const nodesByPath: { [path: string]: NodeInfo } = {};
@@ -36,6 +46,8 @@ export function ChatSources({ data }: { data: SourceData }) {
.forEach((node) => {
const nodeInfo = {
id: node.id,
score: node.score,
text: node.text,
url: node.url,
};
const key = nodeInfo.url ?? nodeInfo.id; // use id as key for UNKNOWN type
@@ -51,7 +63,7 @@ export function ChatSources({ data }: { data: SourceData }) {
return (
<div className="space-x-2 text-sm">
<span className="font-semibold">Sources:</span>
<span className="font-semibold">:</span>
<div className="inline-flex gap-1 items-center">
{sources.map((nodeInfo: NodeInfo, index: number) => {
if (nodeInfo.url?.endsWith(".pdf")) {
@@ -59,8 +71,8 @@ export function ChatSources({ data }: { data: SourceData }) {
<PdfDialog
key={nodeInfo.id}
documentId={nodeInfo.id}
url={nodeInfo.url!}
trigger={<SourceNumberButton index={index} />}
url={backend+nodeInfo.url}
trigger={<SourceNumberButton index={index} score={nodeInfo.score} />}
/>
);
}
@@ -68,9 +80,9 @@ export function ChatSources({ data }: { data: SourceData }) {
<div key={nodeInfo.id}>
<HoverCard>
<HoverCardTrigger>
<SourceNumberButton index={index} />
<SourceNumberButton index={index} score={nodeInfo.score}/>
</HoverCardTrigger>
<HoverCardContent className="w-[320px]">
<HoverCardContent className="w-[450px]">
<NodeInfo nodeInfo={nodeInfo} />
</HoverCardContent>
</HoverCard>
@@ -83,6 +95,7 @@ export function ChatSources({ data }: { data: SourceData }) {
}
function NodeInfo({ nodeInfo }: { nodeInfo: NodeInfo }) {
const { backend } = useClientConfig();
const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 1000 });
if (nodeInfo.url) {
@@ -92,10 +105,10 @@ function NodeInfo({ nodeInfo }: { nodeInfo: NodeInfo }) {
<div className="flex items-center my-2">
<a
className="hover:text-blue-900 truncate"
href={nodeInfo.url}
href={backend+nodeInfo.url}
target="_blank"
>
<span>{nodeInfo.url}</span>
<span>{nodeInfo.text}</span>
</a>
<Button
onClick={() => copyToClipboard(nodeInfo.url!)}
@@ -116,8 +129,7 @@ function NodeInfo({ nodeInfo }: { nodeInfo: NodeInfo }) {
// node generated by unknown loader, implement renderer by analyzing logged out metadata
return (
<p>
Sorry, unknown node type. Please add a new renderer in the NodeInfo
component.
, .
</p>
);
}
@@ -9,7 +9,7 @@ export default function ChatTools({ data }: { data: ToolData }) {
if (toolOutput.isError) {
return (
<div className="border-l-2 border-red-400 pl-2">
There was an error when calling the tool {toolCall.name} with input:{" "}
{toolCall.name} :{" "}
<br />
{JSON.stringify(toolCall.input)}
</div>
@@ -68,7 +68,7 @@ const CodeBlock: FC<Props> = memo(({ language, value }) => {
3,
true,
)}${fileExtension}`;
const fileName = window.prompt("Enter file name" || "", suggestedFileName);
const fileName = window.prompt("请输入文件名称" || "", suggestedFileName);
if (!fileName) {
// User pressed cancel on prompt.
@@ -99,7 +99,7 @@ const CodeBlock: FC<Props> = memo(({ language, value }) => {
<div className="flex items-center space-x-1">
<Button variant="ghost" onClick={downloadAsFile} size="icon">
<Download />
<span className="sr-only">Download</span>
<span className="sr-only"></span>
</Button>
<Button variant="ghost" size="icon" onClick={onCopy}>
{isCopied ? (
@@ -107,7 +107,7 @@ const CodeBlock: FC<Props> = memo(({ language, value }) => {
) : (
<Copy className="h-4 w-4" />
)}
<span className="sr-only">Copy code</span>
<span className="sr-only"></span>
</Button>
</div>
</div>
@@ -10,7 +10,7 @@ export interface ChatHandler {
data?: any;
},
) => void;
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
handleInputChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
reload?: () => void;
stop?: () => void;
onFileUpload?: (file: File) => Promise<void>;
@@ -63,7 +63,7 @@ export function useFile() {
...requestParams,
}),
});
if (!response.ok) throw new Error("Failed to upload document.");
if (!response.ok) throw new Error("上传文件时发生错误。");
return await response.json();
};
@@ -109,7 +109,7 @@ export function useFile() {
}
const filetype = docMineTypeMap[file.type];
if (!filetype) throw new Error("Unsupported document type.");
if (!filetype) throw new Error("不支持的文件类型。");
const newDoc: Omit<DocumentFile, "content"> = {
id: uuidv4(),
filetype,