diff --git a/.env.example b/.env.example index 247a807..47b0cca 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ # Google Gemini API Key # Get your API key from: https://makersuite.google.com/app/apikey GEMINI_API_KEY=your_gemini_api_key_here + +# Enable demo image conditioning (loads images from ./demo as context) +DEMO_ENABLED=false diff --git a/demo/description.txt b/demo/description.txt new file mode 100644 index 0000000..b566207 --- /dev/null +++ b/demo/description.txt @@ -0,0 +1 @@ +Find funding of a YC 2025 winter startup at https://www.ycombinator.com/companies?batch=Winter%202025. \ No newline at end of file diff --git a/demo/prompt.txt b/demo/prompt.txt new file mode 100644 index 0000000..e13066d --- /dev/null +++ b/demo/prompt.txt @@ -0,0 +1,3 @@ +Find funding of a YC 2025 winter startup at https://www.ycombinator.com/companies?batch=Winter%202025. + +Pick 3 startups from the YC 2025 batch, check how much they raised by doing a google search in a separate tab, and add their names and most recent raise to the google docs at https://docs.google.com/document/d/1IzFp0tiDhllwW_mdyCQKkvNY7uKFYNulge1A4gAvCYs/edit?tab=t.0 on a new line. \ No newline at end of file diff --git a/demo/step1.png b/demo/step1.png new file mode 100644 index 0000000..9e730b3 Binary files /dev/null and b/demo/step1.png differ diff --git a/demo/step2.png b/demo/step2.png new file mode 100644 index 0000000..5a9b8c5 Binary files /dev/null and b/demo/step2.png differ diff --git a/demo/step3.png b/demo/step3.png new file mode 100644 index 0000000..7a2781c Binary files /dev/null and b/demo/step3.png differ diff --git a/demo/step4.png b/demo/step4.png new file mode 100644 index 0000000..a17254a Binary files /dev/null and b/demo/step4.png differ diff --git a/demo/step5.png b/demo/step5.png new file mode 100644 index 0000000..e18f131 Binary files /dev/null and b/demo/step5.png differ diff --git a/demo/step6.png b/demo/step6.png new file mode 100644 index 0000000..69b857f Binary files /dev/null and b/demo/step6.png differ diff --git a/docker-compose.yml b/docker-compose.yml index 1f8d966..144c120 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,9 +46,11 @@ services: volumes: - ./screenshots:/tmp/screenshots - ./logs:/app/logs + - ./demo:/app/demo:ro environment: - GEMINI_API_KEY=${GEMINI_API_KEY} - MCP_SERVER_URL=http://playwright-browser:3001 + - DEMO_ENABLED=${DEMO_ENABLED:-false} depends_on: playwright-browser: condition: service_healthy diff --git a/services/nextjs-webapp/.gitignore b/services/nextjs-webapp/.gitignore new file mode 100644 index 0000000..e985853 --- /dev/null +++ b/services/nextjs-webapp/.gitignore @@ -0,0 +1 @@ +.vercel diff --git a/services/nextjs-webapp/src/components/ChatWindow.tsx b/services/nextjs-webapp/src/components/ChatWindow.tsx index 06ce033..ad30406 100644 --- a/services/nextjs-webapp/src/components/ChatWindow.tsx +++ b/services/nextjs-webapp/src/components/ChatWindow.tsx @@ -5,9 +5,11 @@ import RecordingMetadataModal from './RecordingMetadataModal' interface Message { id: string - role: 'user' | 'assistant' | 'system' + role: 'user' | 'assistant' | 'system' | 'memory' content: string timestamp: Date + thumbnail?: string + imageCount?: number } interface ChatWindowProps { @@ -67,9 +69,36 @@ export default function ChatWindow({ isRecording = false }: ChatWindowProps) { }, ]) setIsLoading(false) + } else if (data.type === 'memory_injected') { + // Memory was injected - show it immediately + if (data.metadata) { + setMessages((prev) => [ + ...prev, + { + id: Date.now().toString() + '-memory', + role: 'memory', + content: `Memory retrieved: ${data.metadata.description}`, + timestamp: new Date(), + thumbnail: data.metadata.thumbnail, + imageCount: data.metadata.image_count, + }, + ]) + } } else if (data.type === 'status') { - // Handle status updates (e.g., "thinking", "executing action") + // Handle status updates (e.g., "thinking", "executing action", "interrupt_received") console.log('Status:', data.content) + if (data.content === 'interrupt_received') { + // Show a system message that the interrupt was received + setMessages((prev) => [ + ...prev, + { + id: Date.now().toString() + '-interrupt', + role: 'system', + content: 'Interrupt received - will be applied at next iteration', + timestamp: new Date(), + }, + ]) + } } else if (data.type === 'recording_status') { // Handle recording status updates if (data.session_id) { @@ -158,7 +187,7 @@ export default function ChatWindow({ isRecording = false }: ChatWindowProps) { }, [isRecording, isConnected]) const sendMessage = () => { - if (!input.trim() || !isConnected || isLoading) return + if (!input.trim() || !isConnected) return const userMessage: Message = { id: Date.now().toString(), @@ -168,14 +197,24 @@ export default function ChatWindow({ isRecording = false }: ChatWindowProps) { } setMessages((prev) => [...prev, userMessage]) - setIsLoading(true) - wsRef.current?.send( - JSON.stringify({ - type: 'message', - content: input.trim(), - }) - ) + // If already loading, send as interrupt; otherwise send as new message + if (isLoading) { + wsRef.current?.send( + JSON.stringify({ + type: 'interrupt', + content: input.trim(), + }) + ) + } else { + setIsLoading(true) + wsRef.current?.send( + JSON.stringify({ + type: 'message', + content: input.trim(), + }) + ) + } setInput('') } @@ -279,10 +318,26 @@ export default function ChatWindow({ isRecording = false }: ChatWindowProps) { ? 'bg-blue-600 text-white' : message.role === 'system' ? 'bg-gray-800 text-gray-300 text-sm italic' + : message.role === 'memory' + ? 'bg-purple-900 text-purple-100 border border-purple-700' : 'bg-gray-700 text-white' }`} > -

{message.content}

+ {message.role === 'memory' && message.thumbnail && ( +
+ Memory thumbnail + {message.imageCount && ( + + +{message.imageCount - 1} more images + + )} +
+ )} +

{message.content}

{message.timestamp.toLocaleTimeString()} @@ -314,16 +369,20 @@ export default function ChatWindow({ isRecording = false }: ChatWindowProps) {