diff --git a/autogpt_platform/backend/.gitignore b/autogpt_platform/backend/.gitignore index 9224c07d9e..6e688311a6 100644 --- a/autogpt_platform/backend/.gitignore +++ b/autogpt_platform/backend/.gitignore @@ -19,3 +19,6 @@ load-tests/*.json load-tests/*.log load-tests/node_modules/* migrations/*/rollback*.sql + +# Workspace files +workspaces/ diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ThinkingMessage/ThinkingMessage.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/ThinkingMessage/ThinkingMessage.tsx index 047c2277b0..34018f0292 100644 --- a/autogpt_platform/frontend/src/components/contextual/Chat/components/ThinkingMessage/ThinkingMessage.tsx +++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ThinkingMessage/ThinkingMessage.tsx @@ -1,6 +1,8 @@ +import { Progress } from "@/components/atoms/Progress/Progress"; import { cn } from "@/lib/utils"; import { useEffect, useRef, useState } from "react"; import { AIChatBubble } from "../AIChatBubble/AIChatBubble"; +import { useAsymptoticProgress } from "../ToolCallMessage/useAsymptoticProgress"; export interface ThinkingMessageProps { className?: string; @@ -11,18 +13,19 @@ export function ThinkingMessage({ className }: ThinkingMessageProps) { const [showCoffeeMessage, setShowCoffeeMessage] = useState(false); const timerRef = useRef(null); const coffeeTimerRef = useRef(null); + const progress = useAsymptoticProgress(showCoffeeMessage); useEffect(() => { if (timerRef.current === null) { timerRef.current = setTimeout(() => { setShowSlowLoader(true); - }, 8000); + }, 3000); } if (coffeeTimerRef.current === null) { coffeeTimerRef.current = setTimeout(() => { setShowCoffeeMessage(true); - }, 10000); + }, 8000); } return () => { @@ -49,9 +52,18 @@ export function ThinkingMessage({ className }: ThinkingMessageProps) {
{showCoffeeMessage ? ( - - This could take a few minutes, grab a coffee ☕️ - +
+
+
+ Working on it... + {Math.round(progress)}% +
+ +
+ + This could take a few minutes, grab a coffee ☕️ + +
) : showSlowLoader ? ( Taking a bit more time... diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolCallMessage/useAsymptoticProgress.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolCallMessage/useAsymptoticProgress.ts new file mode 100644 index 0000000000..cf1b89e7c4 --- /dev/null +++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ToolCallMessage/useAsymptoticProgress.ts @@ -0,0 +1,50 @@ +import { useEffect, useRef, useState } from "react"; + +/** + * Hook that returns a progress value that starts fast and slows down, + * asymptotically approaching but never reaching the max value. + * + * Uses a half-life formula: progress = max * (1 - 0.5^(time/halfLife)) + * This creates the "game loading bar" effect where: + * - 50% is reached at halfLifeSeconds + * - 75% is reached at 2 * halfLifeSeconds + * - 87.5% is reached at 3 * halfLifeSeconds + * - and so on... + * + * @param isActive - Whether the progress should be animating + * @param halfLifeSeconds - Time in seconds to reach 50% progress (default: 30) + * @param maxProgress - Maximum progress value to approach (default: 100) + * @param intervalMs - Update interval in milliseconds (default: 100) + * @returns Current progress value (0-maxProgress) + */ +export function useAsymptoticProgress( + isActive: boolean, + halfLifeSeconds = 30, + maxProgress = 100, + intervalMs = 100, +) { + const [progress, setProgress] = useState(0); + const elapsedTimeRef = useRef(0); + + useEffect(() => { + if (!isActive) { + setProgress(0); + elapsedTimeRef.current = 0; + return; + } + + const interval = setInterval(() => { + elapsedTimeRef.current += intervalMs / 1000; + // Half-life approach: progress = max * (1 - 0.5^(time/halfLife)) + // At t=halfLife: 50%, at t=2*halfLife: 75%, at t=3*halfLife: 87.5%, etc. + const newProgress = + maxProgress * + (1 - Math.pow(0.5, elapsedTimeRef.current / halfLifeSeconds)); + setProgress(newProgress); + }, intervalMs); + + return () => clearInterval(interval); + }, [isActive, halfLifeSeconds, maxProgress, intervalMs]); + + return progress; +}