update- reorg

This commit is contained in:
pablodanswer 2025-01-23 12:13:44 -08:00 committed by Evan Lohn
parent dd494d2daa
commit d32d1c6079
7 changed files with 211 additions and 182 deletions

View File

@ -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"
/>
) : (

View File

@ -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

View File

@ -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>

View File

@ -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 = (

View File

@ -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}

View 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>
);
}

View File

@ -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>
);
}