Nagesh Muralidhar
midterm-submission
70c3b32
import React, { useState, useEffect, useRef } from 'react';
import '../App.css';
// Add Vite env type definition
interface ImportMetaEnv {
PROD: boolean;
DEV: boolean;
}
interface Message {
id: number;
text: string;
agent: string;
audio_file?: string;
title?: string;
description?: string;
category?: string;
}
interface DebateEntry {
speaker: string;
content: string;
}
interface ChatResponse {
debate_history: DebateEntry[];
supervisor_notes: string[];
final_podcast?: {
content: string;
audio_file: string;
title: string;
description: string;
};
}
// Use relative URLs in production, full URLs in development
const isDevelopment = window.location.hostname === 'localhost';
const API_URL = isDevelopment ? 'http://localhost:8000' : '';
const Home: React.FC = () => {
const [messages, setMessages] = useState<Message[]>([
{ id: 1, text: "Welcome! I'll help you create your own podcast content by exploring topics through an AI debate. Enter any topic of choice.", agent: "system" },
]);
const [inputMessage, setInputMessage] = useState('');
const [isLoading, setIsLoading] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const audioRef = useRef<HTMLAudioElement>(null);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const sendMessageToServer = async (message: string): Promise<ChatResponse> => {
try {
const response = await fetch(`${API_URL}/api/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: message,
agent_type: "believer",
context: {
podcast_id: null,
agent_chunks: [],
current_agent: "believer"
}
})
});
if (!response.ok) {
const errorData = await response.text();
console.error('Server error:', response.status, errorData);
throw new Error(`Server error (${response.status}): ${errorData}`);
}
const data: ChatResponse = await response.json();
return data;
} catch (error) {
console.error('Error sending message:', error);
throw error;
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!inputMessage.trim()) return;
const userMessage: Message = {
id: messages.length + 1,
text: inputMessage,
agent: "user"
};
setMessages(prev => [...prev, userMessage]);
setInputMessage('');
setIsLoading(true);
try {
const response = await sendMessageToServer(inputMessage);
if (response.debate_history) {
response.debate_history.forEach((entry) => {
if (entry.speaker && entry.content) {
const message: Message = {
id: messages.length + 1,
text: entry.content,
agent: entry.speaker
};
setMessages(prev => [...prev, message]);
}
});
}
if (response.supervisor_notes) {
response.supervisor_notes.forEach((note) => {
const supervisorMessage: Message = {
id: messages.length + 1,
text: note,
agent: "supervisor"
};
setMessages(prev => [...prev, supervisorMessage]);
});
}
if (response.final_podcast && response.final_podcast.audio_file) {
const filename = response.final_podcast.audio_file;
const [queryPart, descriptionPart, categoryWithExt] = filename.split('-');
const category = categoryWithExt.replace('.mp3', '');
const podcastMessage: Message = {
id: messages.length + 1,
text: response.final_podcast.content || "Podcast generated successfully!",
agent: "system",
audio_file: `/audio-files/${filename}`,
title: descriptionPart.replace(/_/g, ' ').replace(/^\w/, c => c.toUpperCase()),
description: `A debate exploring ${queryPart.replace(/_/g, ' ')}`,
category: category.replace(/_/g, ' ')
};
setMessages(prev => [...prev, podcastMessage]);
}
} catch (error) {
setMessages(prev => [...prev, {
id: prev.length + 1,
text: `Error: ${error.message}`,
agent: "system"
}]);
} finally {
setIsLoading(false);
}
};
return (
<div className="chat-container">
<div className="chat-messages">
{messages.map((message) => (
<div key={message.id} className={`message ${message.agent}-message`}>
<div className="message-content">
<div className="agent-icon">
{message.agent === "user" ? "πŸ‘€" :
message.agent === "system" ? "πŸ€–" :
message.agent === "believer" ? "πŸ’‘" :
message.agent === "skeptic" ? "πŸ€”" :
message.agent === "supervisor" ? "πŸ‘€" :
message.agent === "extractor" ? "πŸ”" : "πŸ’¬"}
</div>
<div className="message-text-content">
<div className="agent-name">{message.agent}</div>
<div className="message-text">{message.text}</div>
{message.audio_file && (
<div className="podcast-card">
<div className="podcast-content">
<h2 className="podcast-title">{message.title || "Generated Podcast"}</h2>
{message.category && (
<div className="category-pill">{message.category}</div>
)}
<p className="description">{message.description || "An AI-generated debate podcast exploring different perspectives"}</p>
<div className="audio-player">
<audio controls src={message.audio_file} ref={audioRef}>
Your browser does not support the audio element.
</audio>
</div>
</div>
</div>
)}
</div>
</div>
</div>
))}
{isLoading && (
<div className="message system-message">
<div className="message-content">
<div className="loading-dots">Debating</div>
<div className="loading-dots">This might take a few moments since 2 agents are fighting over getting the best insights for you</div>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
<form onSubmit={handleSubmit} className="chat-input-form">
<input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
placeholder="Type your message..."
className="chat-input"
disabled={isLoading}
/>
<button type="submit" className="chat-send-button" disabled={isLoading}>
Send
</button>
</form>
</div>
);
};
export default Home;