mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-03-26 17:51:54 +01:00
update- reorg
This commit is contained in:
parent
dd494d2daa
commit
d32d1c6079
@ -826,7 +826,7 @@ export function ChatInputBar({
|
||||
chatState == "toolBuilding" ||
|
||||
chatState == "loading" ? (
|
||||
<StopGeneratingIcon
|
||||
size={10}
|
||||
size={8}
|
||||
className="text-emphasis m-auto text-white flex-none"
|
||||
/>
|
||||
) : (
|
||||
|
@ -76,9 +76,9 @@ import {
|
||||
StreamingPhase,
|
||||
StreamingPhaseText,
|
||||
useStreamingMessages,
|
||||
useOrderedPhases,
|
||||
} from "./StreamingMessages";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import RefinemenetBadge from "../refinmentBadge";
|
||||
|
||||
export const AgenticMessage = ({
|
||||
secondLevelAssistantMessage,
|
||||
@ -294,8 +294,8 @@ export const AgenticMessage = ({
|
||||
? docs
|
||||
: agenticDocs
|
||||
: agenticDocs && agenticDocs.length > 0
|
||||
? agenticDocs
|
||||
: docs
|
||||
? agenticDocs
|
||||
: docs
|
||||
}
|
||||
subQuestions={
|
||||
isViewingInitialAnswer
|
||||
@ -374,22 +374,6 @@ export const AgenticMessage = ({
|
||||
);
|
||||
}, [streamedContent, markdownComponents]);
|
||||
|
||||
const currentState = secondLevelSubquestions?.[0]
|
||||
? secondLevelSubquestions[0].answer
|
||||
? secondLevelSubquestions[0].is_complete
|
||||
? StreamingPhase.COMPLETE
|
||||
: StreamingPhase.ANSWER
|
||||
: secondLevelSubquestions[0].context_docs
|
||||
? StreamingPhase.CONTEXT_DOCS
|
||||
: secondLevelSubquestions[0].sub_queries
|
||||
? StreamingPhase.SUB_QUERIES
|
||||
: secondLevelSubquestions[0].question
|
||||
? StreamingPhase.WAITING
|
||||
: StreamingPhase.WAITING
|
||||
: StreamingPhase.WAITING;
|
||||
|
||||
const message = useOrderedPhases(currentState);
|
||||
|
||||
const includeMessageSwitcher =
|
||||
currentMessageInd !== undefined &&
|
||||
onMessageSelection &&
|
||||
@ -495,45 +479,16 @@ export const AgenticMessage = ({
|
||||
Answer
|
||||
</div>
|
||||
|
||||
{secondLevelSubquestions &&
|
||||
secondLevelSubquestions.length > 0 &&
|
||||
secondLevelGenerating ? (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="flex loading-text items-center gap-x-2 text-black text-sm font-medium cursor-pointer hover:text-blue-600 transition-colors duration-200">
|
||||
Refining answer...
|
||||
<FiChevronRight
|
||||
className="inline-block text-text-darker"
|
||||
size={16}
|
||||
/>
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80 p-4 bg-white shadow-lg rounded-md">
|
||||
<div className="space-y-4">
|
||||
<p className="text-lg leading-none font-semibold text-gray-800">
|
||||
{message}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
The answer is being refined based on
|
||||
additional context and analysis.
|
||||
</p>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setIsViewingInitialAnswer(
|
||||
!isViewingInitialAnswer
|
||||
)
|
||||
}
|
||||
size="sm"
|
||||
className="w-full"
|
||||
>
|
||||
{isViewingInitialAnswer
|
||||
? "See Live Updates"
|
||||
: "Hide Live Updates"}
|
||||
<FiGlobe className="inline-block mr-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{true ? (
|
||||
<RefinemenetBadge
|
||||
secondLevelSubquestions={secondLevelSubquestions}
|
||||
toggleInitialAnswerVieinwg={() => {
|
||||
setIsViewingInitialAnswer(
|
||||
!isViewingInitialAnswer
|
||||
);
|
||||
}}
|
||||
isViewingInitialAnswer={isViewingInitialAnswer}
|
||||
/>
|
||||
) : (
|
||||
secondLevelAssistantMessage && (
|
||||
<Badge
|
||||
|
@ -1,8 +1,12 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { OnyxDocument } from "@/lib/search/interfaces";
|
||||
import { ResultIcon } from "@/components/chat_search/sources/SourceCard";
|
||||
import {
|
||||
ResultIcon,
|
||||
SeeMoreBlock,
|
||||
} from "@/components/chat_search/sources/SourceCard";
|
||||
import { openDocument } from "@/lib/search/utils";
|
||||
import { buildDocumentSummaryDisplay } from "@/components/search/DocumentDisplay";
|
||||
import { ValidSources } from "@/lib/types";
|
||||
|
||||
interface SourcesDisplayProps {
|
||||
documents: OnyxDocument[];
|
||||
@ -103,19 +107,16 @@ export const SourcesDisplay: React.FC<SourcesDisplayProps> = ({
|
||||
))}
|
||||
|
||||
{hasMoreDocuments && (
|
||||
<button
|
||||
onClick={toggleDocumentSelection}
|
||||
className={`max-w-[260px] h-[80px] p-3 bg-[#f1eee8] hover:bg-[#ebe7de] cursor-pointer rounded-lg flex flex-col items-start justify-between transition-opacity duration-300 ${
|
||||
animateEntrance ? "opacity-100" : "opacity-100"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
{documents.slice(3, 6).map((doc, index) => (
|
||||
<ResultIcon key={index} doc={doc} size={14} />
|
||||
))}
|
||||
</div>
|
||||
<div className="text-[#4a4a4a] text-xs font-medium">Show All</div>
|
||||
</button>
|
||||
<SeeMoreBlock
|
||||
toggled={false}
|
||||
toggleDocumentSelection={toggleDocumentSelection}
|
||||
uniqueSources={
|
||||
Array.from(
|
||||
new Set(documents.map((doc) => doc.source_type))
|
||||
) as ValidSources[]
|
||||
}
|
||||
webSourceDomains={documents.map((doc) => doc.link)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,86 +31,12 @@ interface SubQuestionProgress {
|
||||
answerCharIndex: number;
|
||||
}
|
||||
|
||||
const PHASES_ORDER: StreamingPhase[] = [
|
||||
StreamingPhase.WAITING,
|
||||
StreamingPhase.SUB_QUERIES,
|
||||
StreamingPhase.CONTEXT_DOCS,
|
||||
StreamingPhase.ANSWER,
|
||||
StreamingPhase.COMPLETE,
|
||||
];
|
||||
|
||||
export const PHASE_MIN_MS = 800; // Minimum phase duration in ms
|
||||
|
||||
function canTransition(p: SubQuestionProgress) {
|
||||
return Date.now() - p.phaseStartTime >= PHASE_MIN_MS;
|
||||
}
|
||||
|
||||
export function useOrderedPhases(externalPhase: StreamingPhase) {
|
||||
const [phaseQueue, setPhaseQueue] = useState<StreamingPhase[]>([]);
|
||||
const [displayedPhase, setDisplayedPhase] = useState<StreamingPhase>(
|
||||
StreamingPhase.WAITING
|
||||
);
|
||||
const lastDisplayTimestampRef = useRef<number>(Date.now());
|
||||
|
||||
const getPhaseIndex = (phase: StreamingPhase) => {
|
||||
return PHASES_ORDER.indexOf(phase);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPhaseQueue((prevQueue) => {
|
||||
const lastQueuedPhase =
|
||||
prevQueue.length > 0 ? prevQueue[prevQueue.length - 1] : displayedPhase;
|
||||
|
||||
const lastQueuedIndex = getPhaseIndex(lastQueuedPhase);
|
||||
const externalIndex = getPhaseIndex(externalPhase);
|
||||
|
||||
if (externalIndex <= lastQueuedIndex) {
|
||||
return prevQueue;
|
||||
}
|
||||
|
||||
const missingPhases: StreamingPhase[] = [];
|
||||
for (let i = lastQueuedIndex + 1; i <= externalIndex; i++) {
|
||||
missingPhases.push(PHASES_ORDER[i]);
|
||||
}
|
||||
return [...prevQueue, ...missingPhases];
|
||||
});
|
||||
}, [externalPhase, displayedPhase]);
|
||||
|
||||
useEffect(() => {
|
||||
if (phaseQueue.length === 0) return;
|
||||
let rafId: number;
|
||||
|
||||
const processQueue = () => {
|
||||
const now = Date.now();
|
||||
const elapsed = now - lastDisplayTimestampRef.current;
|
||||
|
||||
// Keep this at 1000ms from the original example (unchanged),
|
||||
// but you can adjust if you want a different visible time in *this* component.
|
||||
if (elapsed >= 1000) {
|
||||
setPhaseQueue((prevQueue) => {
|
||||
if (prevQueue.length > 0) {
|
||||
const [next, ...rest] = prevQueue;
|
||||
setDisplayedPhase(next);
|
||||
lastDisplayTimestampRef.current = Date.now();
|
||||
return rest;
|
||||
}
|
||||
return prevQueue;
|
||||
});
|
||||
}
|
||||
rafId = requestAnimationFrame(processQueue);
|
||||
};
|
||||
|
||||
rafId = requestAnimationFrame(processQueue);
|
||||
return () => {
|
||||
if (rafId) {
|
||||
cancelAnimationFrame(rafId);
|
||||
}
|
||||
};
|
||||
}, [phaseQueue]);
|
||||
|
||||
return StreamingPhaseText[displayedPhase];
|
||||
}
|
||||
|
||||
const DOC_DELAY_MS = 100;
|
||||
|
||||
export const useStreamingMessages = (
|
||||
|
@ -308,8 +308,8 @@ const SubQuestionDisplay: React.FC<{
|
||||
status === ToggleState.Todo
|
||||
? "!border-4 border border-background-900 bg-background"
|
||||
: false
|
||||
? "bg-background border-3 border border-background-900 rotating-border"
|
||||
: "bg-background-900 flex items-center justify-center"
|
||||
? "bg-background border-3 border border-background-900 rotating-border"
|
||||
: "bg-background-900 flex items-center justify-center"
|
||||
}
|
||||
`}
|
||||
>
|
||||
@ -506,9 +506,12 @@ const SubQuestionsDisplay: React.FC<SubQuestionsDisplayProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (documents && documents.length > 0) {
|
||||
setTimeout(() => {
|
||||
setShownDocuments(documents);
|
||||
}, 1500);
|
||||
setTimeout(
|
||||
() => {
|
||||
setShownDocuments(documents);
|
||||
},
|
||||
overallAnswerGenerating ? 1500 : 0
|
||||
);
|
||||
}
|
||||
}, [documents]);
|
||||
|
||||
@ -639,7 +642,7 @@ const SubQuestionsDisplay: React.FC<SubQuestionsDisplayProps> = ({
|
||||
documents={documents}
|
||||
isLast={
|
||||
!showSummarizing &&
|
||||
memoizedSubQuestions.length > index + 1 &&
|
||||
memoizedSubQuestions.length == index + 1 &&
|
||||
!(
|
||||
showSecondLevel &&
|
||||
memoizedSecondLevelQuestions &&
|
||||
@ -689,7 +692,7 @@ const SubQuestionsDisplay: React.FC<SubQuestionsDisplayProps> = ({
|
||||
documents={documents}
|
||||
isLast={
|
||||
!showSummarizing &&
|
||||
memoizedSecondLevelQuestions.length > index + 1
|
||||
memoizedSecondLevelQuestions.length == index + 1
|
||||
}
|
||||
isFirst={false}
|
||||
setPresentingDocument={setPresentingDocument}
|
||||
|
152
web/src/app/chat/refinmentBadge.tsx
Normal file
152
web/src/app/chat/refinmentBadge.tsx
Normal file
@ -0,0 +1,152 @@
|
||||
"use client";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipTrigger,
|
||||
TooltipProvider,
|
||||
TooltipContent,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { FiChevronRight, FiGlobe } from "react-icons/fi";
|
||||
import {
|
||||
StreamingPhase,
|
||||
StreamingPhaseText,
|
||||
} from "./message/StreamingMessages";
|
||||
import { SubQuestionDetail } from "./interfaces";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
const PHASES_ORDER: StreamingPhase[] = [
|
||||
StreamingPhase.WAITING,
|
||||
StreamingPhase.SUB_QUERIES,
|
||||
StreamingPhase.CONTEXT_DOCS,
|
||||
StreamingPhase.ANSWER,
|
||||
StreamingPhase.COMPLETE,
|
||||
];
|
||||
|
||||
export function useOrderedPhases(externalPhase: StreamingPhase) {
|
||||
const [phaseQueue, setPhaseQueue] = useState<StreamingPhase[]>([]);
|
||||
const [displayedPhase, setDisplayedPhase] = useState<StreamingPhase>(
|
||||
StreamingPhase.WAITING
|
||||
);
|
||||
const lastDisplayTimestampRef = useRef<number>(Date.now());
|
||||
|
||||
const getPhaseIndex = (phase: StreamingPhase) => {
|
||||
return PHASES_ORDER.indexOf(phase);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPhaseQueue((prevQueue) => {
|
||||
const lastQueuedPhase =
|
||||
prevQueue.length > 0 ? prevQueue[prevQueue.length - 1] : displayedPhase;
|
||||
|
||||
const lastQueuedIndex = getPhaseIndex(lastQueuedPhase);
|
||||
const externalIndex = getPhaseIndex(externalPhase);
|
||||
|
||||
if (externalIndex <= lastQueuedIndex) {
|
||||
return prevQueue;
|
||||
}
|
||||
|
||||
const missingPhases: StreamingPhase[] = [];
|
||||
for (let i = lastQueuedIndex + 1; i <= externalIndex; i++) {
|
||||
missingPhases.push(PHASES_ORDER[i]);
|
||||
}
|
||||
return [...prevQueue, ...missingPhases];
|
||||
});
|
||||
}, [externalPhase, displayedPhase]);
|
||||
|
||||
useEffect(() => {
|
||||
if (phaseQueue.length === 0) return;
|
||||
let rafId: number;
|
||||
|
||||
const processQueue = () => {
|
||||
const now = Date.now();
|
||||
const elapsed = now - lastDisplayTimestampRef.current;
|
||||
|
||||
// Keep this at 1000ms from the original example (unchanged),
|
||||
// but you can adjust if you want a different visible time in *this* component.
|
||||
if (elapsed >= 1000) {
|
||||
setPhaseQueue((prevQueue) => {
|
||||
if (prevQueue.length > 0) {
|
||||
const [next, ...rest] = prevQueue;
|
||||
setDisplayedPhase(next);
|
||||
lastDisplayTimestampRef.current = Date.now();
|
||||
return rest;
|
||||
}
|
||||
return prevQueue;
|
||||
});
|
||||
}
|
||||
rafId = requestAnimationFrame(processQueue);
|
||||
};
|
||||
|
||||
rafId = requestAnimationFrame(processQueue);
|
||||
return () => {
|
||||
if (rafId) {
|
||||
cancelAnimationFrame(rafId);
|
||||
}
|
||||
};
|
||||
}, [phaseQueue]);
|
||||
|
||||
return StreamingPhaseText[displayedPhase];
|
||||
}
|
||||
|
||||
export default function RefinemenetBadge({
|
||||
secondLevelSubquestions,
|
||||
toggleInitialAnswerVieinwg,
|
||||
isViewingInitialAnswer,
|
||||
}: {
|
||||
secondLevelSubquestions?: SubQuestionDetail[] | null;
|
||||
toggleInitialAnswerVieinwg: () => void;
|
||||
isViewingInitialAnswer: boolean;
|
||||
}) {
|
||||
const currentState = secondLevelSubquestions?.[0]
|
||||
? secondLevelSubquestions[0].answer
|
||||
? secondLevelSubquestions[0].is_complete
|
||||
? StreamingPhase.COMPLETE
|
||||
: StreamingPhase.ANSWER
|
||||
: secondLevelSubquestions[0].context_docs
|
||||
? StreamingPhase.CONTEXT_DOCS
|
||||
: secondLevelSubquestions[0].sub_queries
|
||||
? StreamingPhase.SUB_QUERIES
|
||||
: secondLevelSubquestions[0].question
|
||||
? StreamingPhase.WAITING
|
||||
: StreamingPhase.WAITING
|
||||
: StreamingPhase.WAITING;
|
||||
|
||||
const message = useOrderedPhases(currentState);
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex loading-text items-center gap-x-2 text-black text-sm font-medium cursor-pointer hover:text-blue-600 transition-colors duration-200">
|
||||
Refining answer...
|
||||
<FiChevronRight
|
||||
className="inline-block text-text-darker"
|
||||
size={16}
|
||||
/>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="w-80 p-4 bg-white shadow-lg rounded-md">
|
||||
<div className="space-y-4">
|
||||
<p className="text-lg leading-none font-semibold text-gray-800">
|
||||
{message}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
The answer is being refined based on additional context and
|
||||
analysis.
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => toggleInitialAnswerVieinwg()}
|
||||
size="sm"
|
||||
className="w-full"
|
||||
>
|
||||
{isViewingInitialAnswer
|
||||
? "See Live Updates"
|
||||
: "Hide Live Updates"}
|
||||
<FiGlobe className="inline-block mr-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
@ -71,37 +71,29 @@ export function SeeMoreBlock({
|
||||
);
|
||||
const numOfWebSourcesToDisplay = 3 - filteredUniqueSources.length;
|
||||
return (
|
||||
<div
|
||||
<button
|
||||
onClick={toggleDocumentSelection}
|
||||
className={`
|
||||
cursor-pointer rounded-lg flex-none transition-all duration-500 hover:bg-background-dark/80 bg-background-dark/60 px-3 py-2
|
||||
`}
|
||||
className={`max-w-[260px] min-w-[100px] h-[80px] p-3 bg-[#f1eee8] hover:bg-[#ebe7de] cursor-pointer rounded-lg flex flex-col items-start justify-between transition-opacity duration-300`}
|
||||
>
|
||||
<div className="flex gap-y-2 flex-col items-start text-sm">
|
||||
<p
|
||||
onClick={toggleDocumentSelection}
|
||||
className="flex-1 mr-1 font-semibold text-text-900 overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
>
|
||||
{toggled ? "Hide Results" : "Show Sources"}
|
||||
</p>
|
||||
<div className="flex-shrink-0 flex gap-x-1 items-center">
|
||||
{filteredUniqueSources.slice(0, 3).map((source, index) => (
|
||||
<SourceIcon key={index} sourceType={source} iconSize={16} />
|
||||
<div className="flex items-center gap-1">
|
||||
{filteredUniqueSources.slice(0, 3).map((source, index) => (
|
||||
<SourceIcon key={index} sourceType={source} iconSize={14} />
|
||||
))}
|
||||
{webSourceDomains
|
||||
.slice(0, numOfWebSourcesToDisplay)
|
||||
.map((domain, index) => (
|
||||
<WebResultIcon key={index} url={domain} size={14} />
|
||||
))}
|
||||
{/* {webSourceDomains
|
||||
.slice(0, numOfWebSourcesToDisplay)
|
||||
.map((domain, ind) => (
|
||||
<WebResultIcon key={ind} url={domain} />
|
||||
))}
|
||||
<WebResultIcon url={domain} />
|
||||
))} */}
|
||||
{uniqueSources.length > 3 && (
|
||||
<span className="text-xs text-text-700 font-semibold ml-1">
|
||||
+{uniqueSources.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{uniqueSources.length > 3 && (
|
||||
<span className="text-xs text-[#4a4a4a] font-medium ml-1">
|
||||
+{uniqueSources.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-text-darker text-xs font-semibold">
|
||||
{toggled ? "Hide Results" : "Show All"}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user