Repository: Anil-matcha/Chat-With-Excel Branch: main Commit: cb12973823b1 Files: 45 Total size: 181.3 KB Directory structure: gitextract_79z3ryl8/ ├── .gitignore ├── README.md ├── client/ │ ├── .gitignore │ ├── app/ │ │ ├── agents/ │ │ │ ├── [agent_id]/ │ │ │ │ ├── [conversation_id]/ │ │ │ │ │ └── page.js │ │ │ │ ├── page.js │ │ │ │ └── profile/ │ │ │ │ └── page.js │ │ │ ├── create/ │ │ │ │ └── page.js │ │ │ ├── edit/ │ │ │ │ └── [id]/ │ │ │ │ └── page.js │ │ │ ├── loading.js │ │ │ └── page.js │ │ ├── globals.css │ │ ├── layout.js │ │ └── page.js │ ├── components/ │ │ └── AgentClientWrapper.js │ ├── context/ │ │ └── fetchAgentData.js │ ├── jsconfig.json │ ├── next.config.mjs │ ├── package.json │ └── postcss.config.mjs ├── package.json ├── packages/ │ └── agents/ │ ├── .gitignore │ ├── README.md │ ├── THEME_SETUP.md │ ├── babel.config.json │ ├── package.json │ ├── postcss.config.js │ ├── src/ │ │ ├── AgentProfile.jsx │ │ ├── AiAgent.jsx │ │ ├── CreatePage.jsx │ │ ├── EditPage.jsx │ │ ├── components/ │ │ │ ├── AgentThemeProvider.jsx │ │ │ ├── CreateAgent.jsx │ │ │ ├── EditAgent.jsx │ │ │ ├── ProfileAgent.jsx │ │ │ └── themes.jsx │ │ ├── index.js │ │ ├── tailwind.css │ │ └── utils/ │ │ └── server.js │ └── tailwind.config.js └── server/ ├── .gitignore ├── app/ │ ├── main.py │ ├── routers/ │ │ ├── __init__.py │ │ └── agent_proxy.py │ └── utils/ │ └── agent_helper.py └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules ================================================ FILE: README.md ================================================ # Open-Poe-AI Open-source, self-hosted alternative to [Poe AI](https://poe.com) — chat with multiple large language models from a single interface, on your own infrastructure. Poe (by Quora) is a hosted aggregator that puts GPT, Claude, Gemini, Grok, DeepSeek, Llama, Mistral and image/video models behind one chat UI. **Open-Poe-AI** is the self-hosted version: bring your own API keys, run it on your own server, and keep full control of prompts, conversations, and data. ## Features - **Multi-model chat** — unified interface for OpenAI, Anthropic, Google, Mistral, DeepSeek, xAI, Meta Llama, and any OpenAI-compatible endpoint (including local models via Ollama / vLLM / LM Studio). - **Multi-bot conversations** — query several models in the same thread and compare answers side by side. - **Custom bots** — build and share bots with their own system prompts, tools, and knowledge bases. - **Group chat** — multiple users and multiple AI models in one shared conversation. - **Multimodal** — text, image generation, vision, and audio; pluggable adapters for image/video model providers. - **Bring your own keys** — no subscription, no rate caps beyond the providers you use. - **Self-hosted** — Docker Compose for one-command deploy; works on a laptop, VPS, or Kubernetes. - **Open protocol** — bot server API so anyone can host their own bot and plug it in. ## Status Early work in progress. Contributions welcome. ## Quick start ```bash git clone https://github.com/Anil-matcha/Open-Poe-AI.git cd Open-Poe-AI cp .env.example .env # add your provider API keys docker compose up -d ``` Then open http://localhost:3000. ## License MIT ================================================ FILE: client/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.* .yarn/* !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/versions # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* # env files (can opt-in for committing if needed) .env* # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts package-lock.json ================================================ FILE: client/app/agents/[agent_id]/[conversation_id]/page.js ================================================ import { cookies } from "next/headers"; import { fetchAgentData, fetchHistoryData } from "@/context/fetchAgentData"; import AgentClientWrapper from "@/components/AgentClientWrapper"; export default async function Page({ params }) { const { agent_id, conversation_id } = await params; const cookieStore = await cookies(); const cookieStr = cookieStore.toString(); // Fetch agent and history in parallel const [agentDetails, initialHistory] = await Promise.all([ fetchAgentData(agent_id, cookieStr, true), fetchHistoryData(agent_id, conversation_id, cookieStr) ]); return ( ); } ================================================ FILE: client/app/agents/[agent_id]/page.js ================================================ import { cookies } from "next/headers"; import { fetchAgentData } from "@/context/fetchAgentData"; import AgentClientWrapper from "@/components/AgentClientWrapper"; export default async function Page({ params }) { const { agent_id } = await params; const cookieStore = await cookies(); // Fetch agent by slug const agentDetails = await fetchAgentData(agent_id, cookieStore.toString(), true); return ( ); } ================================================ FILE: client/app/agents/[agent_id]/profile/page.js ================================================ "use client"; import { AgentProfile } from "ai-agent"; import "ai-agent/dist/tailwind.css"; import { useParams } from "next/navigation"; // Mock user context to pass into components const mockUseUser = () => ({ user: { name: "Tester", username: "dev_user", profile_photo: "", } }); export default function AgentProfileRoute() { const params = useParams(); const agent_id = params.agent_id; return (
); } ================================================ FILE: client/app/agents/create/page.js ================================================ "use client"; import { CreateAgentPage } from "ai-agent"; import "ai-agent/dist/tailwind.css"; // Mock user context to pass into components const mockUseUser = () => ({ user: { name: "Tester", username: "dev_user", profile_photo: "", } }); export default function CreateAgentRoute() { return (
); } ================================================ FILE: client/app/agents/edit/[id]/page.js ================================================ "use client"; import { EditAgentPage } from "ai-agent"; import "ai-agent/dist/tailwind.css"; import { useParams } from "next/navigation"; // Mock user context to pass into components const mockUseUser = () => ({ user: { name: "Tester", username: "dev_user", profile_photo: "", } }); export default function EditAgentRoute() { const params = useParams(); const id = params.id; return (
); } ================================================ FILE: client/app/agents/loading.js ================================================ export default function Loading() { return (

Syncing Library...

); } ================================================ FILE: client/app/agents/page.js ================================================ "use client"; import { useState, useEffect, useCallback } from "react"; import Link from "next/link"; import axios from "axios"; import { RiRobot2Fill, RiUser3Line, RiLayoutGridLine, RiStarLine, RiSearchLine, RiArrowRightUpLine, RiAddLine, RiInformationLine } from "react-icons/ri"; const AgentCard = ({ agent, category }) => ( {/* Large Image Top (muapiapp style) */}
{agent.icon_url ? ( {agent.name} ) : ( )}
{/* Content Bottom */}

{agent.name}

{category}

{agent.description || "Specialized AI Intelligence Unit for complex workflows."}

Ready
ACTIVE HUB
); const CategorySection = ({ title, icon: Icon, agents, categoryLabel }) => { const items = Array.isArray(agents) ? agents : []; if (items.length === 0) return null; return (

{title}

{items.length}
{items.map(a => ( ))}
); }; export default function AgentsLibrary() { const [agents, setAgents] = useState([]); const [templates, setTemplates] = useState([]); const [featured, setFeatured] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [searchTerm, setSearchTerm] = useState(""); const [activeTab, setActiveTab] = useState("workforce"); // Removed "all" const fetchAllData = useCallback(async () => { try { setLoading(true); setError(null); const BASE_URL = "/api/agents"; const [userRes, templatesRes, featuredRes] = await Promise.all([ axios.get(`${BASE_URL}/user/agents`), axios.get(`${BASE_URL}/templates/agents`), axios.get(`${BASE_URL}/featured/agents`) ]); setAgents(Array.isArray(userRes.data) ? userRes.data : []); setTemplates(Array.isArray(templatesRes.data) ? templatesRes.data : []); setFeatured(Array.isArray(featuredRes.data) ? featuredRes.data : []); } catch (err) { console.error(err); setError("Synchronizing failed. Check connectivity."); } finally { setLoading(false); } }, []); useEffect(() => { fetchAllData(); }, [fetchAllData]); const filterAgents = (list) => { const arr = Array.isArray(list) ? list : []; if (!searchTerm) return arr; return arr.filter(a => (a.name && a.name.toLowerCase().includes(searchTerm.toLowerCase())) || (a.description && a.description.toLowerCase().includes(searchTerm.toLowerCase())) ); }; const filteredAgents = filterAgents(agents); const filteredTemplates = filterAgents(templates); const filteredFeatured = filterAgents(featured); const TABS = [ { id: "workforce", label: "My Workforce", icon: RiUser3Line }, { id: "templates", label: "Templates", icon: RiLayoutGridLine }, { id: "featured", label: "Featured", icon: RiStarLine }, ]; return (

Agent Library

Manage Intelligence Workflow

setSearchTerm(e.target.value)} className="w-full bg-white border border-slate-200 rounded-xl pl-9 pr-4 py-2 text-sm outline-none focus:border-blue-500/40 focus:ring-4 focus:ring-blue-500/5 transition-all font-medium text-slate-900监测:text-slate-400" />
Create
{/* Tab Strip */} {loading ? (

Syncing Library...

) : error ? (

Connection Error

{error}

) : (
{activeTab === "workforce" && ( )} {activeTab === "templates" && ( )} {activeTab === "featured" && ( )} {(activeTab === "workforce" ? filteredAgents : activeTab === "templates" ? filteredTemplates : filteredFeatured).length === 0 && (

No units found

Adjust filters or architect a new unit

)}
)}
); } ================================================ FILE: client/app/globals.css ================================================ @import "tailwindcss"; :root { --background: #ffffff; --foreground: #171717; } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); } @media (prefers-color-scheme: dark) { :root { --background: #0a0a0a; --foreground: #ededed; } } body { background: var(--background); color: var(--foreground); font-family: Arial, Helvetica, sans-serif; } ================================================ FILE: client/app/layout.js ================================================ import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); export const metadata = { title: "Vibe-Agents Client", description: "Testing Vibe-Agents proxy and frontend package", }; export default function RootLayout({ children }) { return ( {children} ); } ================================================ FILE: client/app/page.js ================================================ "use client"; import React from "react"; import Link from "next/link"; import { RiRobot2Fill, RiTeamLine, RiFlashlightLine, RiNodeTree } from "react-icons/ri"; export default function WelcomePage() { return (
{/* Premium Background Effects */}
{/* Subtle Noise Texture */}
{/* Badge */}
Next Gen AI Engine
{/* Hero Title */}

Vibe-Agents
Intelligent Workforce

{/* Description */}

Unleash autonomous AI agents that think, collaborate, and execute. The ultimate platform for architecting specialized intelligence into your workflows.

{/* CTA Section */}
Browse Agent Library Architect New Agent
{/* Features Grid */}

Multi-Agent Teams

Build swarms of specialized agents that collaborate on complex tasks through natural language.

Instant Execution

From code generation to visual design, agents execute with unprecedented speed and accuracy.

Modular Logic

Deeply integrated with Vibe-Workflow for seamless bridging between human intent and automated logic.

); } ================================================ FILE: client/components/AgentClientWrapper.js ================================================ "use client"; import { AiAgent } from "ai-agent"; import "ai-agent/dist/tailwind.css"; // Mock user context to pass into AiAgent const mockUseUser = () => ({ user: { name: "Tester", username: "dev_user", profile_photo: "", } }); export default function AgentClientWrapper({ initialAgentDetails, initialHistory = null }) { return (
); } ================================================ FILE: client/context/fetchAgentData.js ================================================ export async function fetchAgentData(id, cookieHeader, byName = false) { const endpoint = `http://127.0.0.1:8000/api/agents/by-slug/${id}`; const res = await fetch(endpoint, { cache: 'no-store', headers: { 'Cookie': cookieHeader || '', }, }); if (!res.ok) return null; return await res.json(); } export async function fetchHistoryData(agentSlug, conversationId, cookieHeader) { const endpoint = `http://127.0.0.1:8000/api/agents/by-slug/${agentSlug}/${conversationId}`; const res = await fetch(endpoint, { cache: 'no-store', headers: { 'Cookie': cookieHeader || '', }, }); if (!res.ok) return null; return await res.json(); } ================================================ FILE: client/jsconfig.json ================================================ { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": [ "./*" ] } } } ================================================ FILE: client/next.config.mjs ================================================ /** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ['ai-agent'], images: { remotePatterns: [ { protocol: 'https', hostname: 'cdn.muapi.ai', port: '', pathname: '/**', }, ], }, async rewrites() { return [ { source: '/api/:path*', destination: 'http://localhost:8000/api/:path*', }, ]; }, }; export default nextConfig; ================================================ FILE: client/package.json ================================================ { "name": "vibe-agents-client", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "eslint" }, "dependencies": { "axios": "^1.13.2", "next": "16.1.1", "react": "19.2.3", "react-dom": "19.2.3", "react-icons": "^5.5.0", "react-hot-toast": "^2.4.1", "reactflow": "^11.11.4", "ai-agent": "*" }, "devDependencies": { "@tailwindcss/postcss": "^4", "eslint": "^9", "eslint-config-next": "16.1.1", "tailwindcss": "^4" } } ================================================ FILE: client/postcss.config.mjs ================================================ const config = { plugins: { "@tailwindcss/postcss": {}, }, }; export default config; ================================================ FILE: package.json ================================================ { "name": "vibe-agents-monorepo", "version": "1.0.0", "private": true, "workspaces": [ "packages/*", "client", "server" ], "scripts": { "dev:app": "npm run dev -w client", "build:app": "npm run build -w client", "build:lib": "npm run build -w packages/agents", "install:all": "npm install" } } ================================================ FILE: packages/agents/.gitignore ================================================ node_modules package-lock.json dist ================================================ FILE: packages/agents/README.md ================================================ # AI Agent A premium React component for AI interactions. ## Installation To use this component in another project, run: ```bash npm install git+https://github.com/jaiprasad04/ai-agent.git ``` ## Usage ### 1. Import the Component ```javascript import { AiAgent } from 'ai-agent'; ``` ### 2. Import the Styles You must import the CSS file for the component to look correct: ```javascript import 'ai-agent/dist/tailwind.css'; ``` ### 3. Example ```jsx import React from 'react'; import { AiAgent } from 'ai-agent'; import 'ai-agent/dist/tailwind.css'; function App() { return (
); } export default App; ``` ## Development If you want to modify the component: 1. Clone the repo 2. Run `npm install` 3. Run `npm run build` to update the `dist/` folder ================================================ FILE: packages/agents/THEME_SETUP.md ================================================ # Theme Setup Guide This library uses `next-themes` to manage light and dark modes. For the theme switching buttons to work correctly, you must wrap your application with the provided `AgentThemeProvider`. ## 1. Wrap your Application In your root layout or `App` component, import and use `AgentThemeProvider`. ### Next.js (App Router) In your `layout.js`: ```jsx import { AgentThemeProvider } from 'ai-agent'; export default function RootLayout({ children }) { return ( {children} ) } ``` > [!IMPORTANT] > Adding `suppressHydrationWarning` to the `` tag is required by `next-themes` to avoid hydration mismatch errors. ### Other React Apps Wrap your main component: ```jsx import { AgentThemeProvider } from 'ai-agent'; ReactDOM.render( , document.getElementById('root') ); ``` ## 2. Tailwind Configuration Ensure your `tailwind.config.js` in the **consuming project** includes `darkMode: 'class'`: ```javascript module.exports = { darkMode: 'class', // ... rest of your config } ``` ## 3. Usage Once the provider is in place, the theme switching logic inside `HomepageNavbar` (and other components) will work automatically, toggling the `.dark` class on the `` element. ================================================ FILE: packages/agents/babel.config.json ================================================ { "presets": [ "@babel/preset-env", ["@babel/preset-react", { "runtime": "automatic" }] ] } ================================================ FILE: packages/agents/package.json ================================================ { "name": "ai-agent", "version": "1.0.0", "description": "AI Agent", "main": "dist/index.js", "module": "dist/index.js", "files": [ "dist" ], "scripts": { "build:css": "tailwindcss -i ./src/tailwind.css -o ./dist/tailwind.css --minify", "build": "npm run build:css && babel src --out-dir dist --extensions .js,.jsx" }, "license": "MIT", "dependencies": { "axios": "^1.7.0", "next-themes": "^0.4.6", "react-hot-toast": "^2.5.2", "react-icons": "^5.0.1", "react-markdown": "^9.0.0", "react-toastify": "^11.0.5", "reactflow": "^11.11.4", "remark-gfm": "^4.0.0" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "devDependencies": { "@babel/cli": "^7.28.3", "@babel/preset-env": "^7.28.5", "@babel/preset-react": "^7.28.5", "autoprefixer": "^10.4.14", "postcss": "^8.4.24", "tailwindcss": "^3.3.3" } } ================================================ FILE: packages/agents/postcss.config.js ================================================ module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, } ================================================ FILE: packages/agents/src/AgentProfile.jsx ================================================ "use client"; import { Toaster } from "react-hot-toast"; import ProfileAgent from "./components/ProfileAgent"; const AgentProfile = ({ useUser, usedIn = "muapiapp" }) => { return (
); }; export default AgentProfile; ================================================ FILE: packages/agents/src/AiAgent.jsx ================================================ "use client"; import React, { useState, useEffect, useRef } from "react"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import axios from "axios"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import { IoSend, IoChevronBack, IoColorPalette, IoAdd, IoHeart, IoHeartOutline, IoChatbubbleEllipsesSharp } from "react-icons/io5"; import { HiLightBulb } from "react-icons/hi2"; import { MdTerminal, MdPerson, MdClose, MdEdit, MdContentCopy, MdCheck, MdFullscreen, MdFileDownload, MdImage } from "react-icons/md"; import { RiRobot2Fill } from "react-icons/ri"; import { HiOutlinePencilAlt } from "react-icons/hi"; import { BiLoaderAlt } from "react-icons/bi"; import { VscDebugAlt } from "react-icons/vsc"; import { themes } from "./components/themes"; import { FaAngleRight } from "react-icons/fa6"; const BASE_URL = "/api/agents"; // "https://api.muapi.ai/agents"; const formatMessageTime = (date) => { if (!date) return ""; return new Intl.DateTimeFormat("en-US", { hour: "numeric", minute: "2-digit", hour12: true, }).format(new Date(date)); }; const getDateHeader = (date) => { const d = new Date(date); const now = new Date(); const yesterday = new Date(now); yesterday.setDate(now.getDate() - 1); if (d.toDateString() === now.toDateString()) return "Today"; if (d.toDateString() === yesterday.toDateString()) return "Yesterday"; return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: d.getFullYear() !== now.getFullYear() ? "numeric" : undefined, }); }; const parseMessageContent = (text) => { if (!text) return []; const urlRegex = /(https?:\/\/[^\s]+)/g; const parts = []; let lastIndex = 0; let match; while ((match = urlRegex.exec(text)) !== null) { const start = match.index; const end = start + match[0].length; const url = match[0]; if (start > lastIndex) { parts.push({ type: "text", content: text.substring(lastIndex, start) }); } const cleanUrl = url.split("?")[0].toLowerCase(); const isImage = /\.(jpg|jpeg|png|gif|webp|svg)$/i.test(cleanUrl); const isVideo = /\.(mp4|webm|mov|ogg)$/i.test(cleanUrl); const isAudio = /\.(mp3|wav|mpeg)$/i.test(cleanUrl); if (isImage) { parts.push({ type: "image", url }); } else if (isVideo) { parts.push({ type: "video", url }); } else if (isAudio) { parts.push({ type: "audio", url }); } else { parts.push({ type: "text", content: url }); } lastIndex = end; } if (lastIndex < text.length) { parts.push({ type: "text", content: text.substring(lastIndex) }); } return parts; }; const CopyButton = ({ text }) => { const [copied, setCopied] = useState(false); const handleCopy = async () => { try { await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { console.error("Failed to copy text: ", err); } }; return ( ); }; const ChatPage = ({ initialAgentDetails, useUser, usedIn = "muapiapp", useSidebar, searchQuery = "", setSearchQuery = () => {}, getSearchItems = () => {}, initialHistory = null, }) => { const { id: routeAgentId, agent_id, agent_name, conversation_id: routeConversationId } = useParams(); const effectiveAgentId = agent_id || agent_name || routeAgentId; const lowerAgentSlug = effectiveAgentId?.toLowerCase(); const effectiveConversationId = routeConversationId; const router = useRouter(); const userContext = useUser ? useUser() : {}; let userName = "User"; let userProfile = null; if (usedIn === "vadoo") { const { serverDetails } = userContext; userName = serverDetails?.user_details?.name || "User"; userProfile = serverDetails?.user_details?.profile; } else if (usedIn === "muapiapp") { // muapiapp const { user } = userContext; userName = user?.username || user?.name || "User"; userProfile = user?.profile_photo; } const [messages, setMessages] = useState(() => { if (initialHistory && initialHistory.history) { return initialHistory.history.map((msg, i) => { let ts = msg.timestamp || initialHistory.created_at || new Date(); if (typeof ts === 'string' && ts.includes('T') && !ts.endsWith('Z') && !ts.includes('+')) { ts += 'Z'; } return { ...msg, id: msg.id || `${msg.role}_${Date.now()}_${i}`, timestamp: ts }; }); } return []; }); const [input, setInput] = useState(""); const [isStreaming, setIsStreaming] = useState(() => { if (typeof window !== 'undefined' && effectiveConversationId) { return !!sessionStorage.getItem('pending_first_msg'); } return false; }); const [agentDetails, setAgentDetails] = useState(initialAgentDetails || null); const [error, setError] = useState(null); const [debugLogs, setDebugLogs] = useState([]); const [showDebug, setShowDebug] = useState(false); const conversationIdRef = useRef(null); const [showDropdown, setShowDropdown] = useState(false); const [showThemeDropdown, setShowThemeDropdown] = useState(false); const [selectedMedia, setSelectedMedia] = useState(null); const [downloadingUrl, setDownloadingUrl] = useState(null); const [currentTheme, setCurrentTheme] = useState(() => { const themeData = initialAgentDetails?.theme; if (typeof themeData === 'string' && themes[themeData]) { return themes[themeData]; } if (themeData && typeof themeData === 'object' && themeData.colors) { return themeData; } return themes.cosmic; }); const textareaRef = useRef(null); const scrollRef = useRef(null); const [attachments, setAttachments] = useState([]); const [isUploading, setIsUploading] = useState(false); const [uploadProgress, setUploadProgress] = useState(0); const [isDragging, setIsDragging] = useState(false); const fileInputRef = useRef(null); const currentAssistantMsgRef = useRef({ content: "", thoughts: "", status: [], suggestions: [], }); const [showCustomColorPanel, setShowCustomColorPanel] = useState(false); const [isMounted, setIsMounted] = useState(false); const [liked, setLiked] = useState(agentDetails ? agentDetails?.has_liked : false); const [likeCount, setLikeCount] = useState(agentDetails ? agentDetails?.like_count : 0); useEffect(() => { setIsMounted(true); }, []); useEffect(() => { const fetchHistory = async () => { if (messages.length > 0) { conversationIdRef.current = effectiveConversationId; return; } if (effectiveConversationId && lowerAgentSlug) { const pending = sessionStorage.getItem('pending_first_msg'); if (pending) { try { const { convId } = JSON.parse(pending); if (convId === effectiveConversationId) { return; } } catch (e) {} } try { let endpoint = `${BASE_URL}/by-slug/${lowerAgentSlug}/${effectiveConversationId}`; const res = await axios.get(endpoint); if (res.data && res.data.history) { const hydratedMessages = res.data.history.map((msg, i) => { let ts = msg.timestamp || res.data.created_at || new Date(); if (typeof ts === 'string' && ts.includes('T') && !ts.endsWith('Z') && !ts.includes('+')) { ts += 'Z'; } return { ...msg, id: msg.id || `${msg.role}_${Date.now()}_${i}`, timestamp: ts }; }); if (hydratedMessages.length > 0) { setMessages(hydratedMessages); } conversationIdRef.current = effectiveConversationId; } } catch (err) { console.error("Failed to fetch conversation history:", err); } } }; fetchHistory(); }, [effectiveConversationId, lowerAgentSlug]); const handleCustomColorChange = (part, color) => { const updatedTheme = { ...currentTheme, id: 'custom', name: 'Custom Theme', colors: { ...currentTheme.colors, [part]: color } }; setCurrentTheme(updatedTheme); }; const handleThemeSync = async (theme) => { try { await axios.put(`${BASE_URL}/by-slug/${lowerAgentSlug}`, { theme: theme }); } catch (err) { console.error("Failed to save theme:", err); } setShowCustomColorPanel(false); }; const generateCssVariables = (theme) => { const c = theme?.colors || themes.cosmic.colors; return { "--bg-primary": c.background, "--text-primary": c.foreground, "--text-secondary": c.muted, "--border-color": c.border, "--component-bg": c.componentBg, "--component-hover": c.componentHover, "--header-bg": c.headerBg, "--user-bubble": c.userBubble, "--user-text": c.userText, "--agent-bubble": c.agentBubble, "--agent-text": c.agentText, "--input-bg": c.inputBg, "--accent": c.accent, "--accent-text": c.accentText, "--font-family": "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", }; }; const handleDownloadFile = async (file_url, filename = "download") => { if (!file_url) { toast.error("File URL not found"); return; } setDownloadingUrl(file_url); try { const response = await axios.post("/api/workflow/cloudfront-signed-url", { url: file_url } ); const signed_url = response.data.signed_url; const fetchResponse = await fetch(signed_url, { mode: "cors" }); const blob = await fetchResponse.blob(); const url = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); } catch (err) { console.error("Download failed:", err); toast.error(`Download failed: ${err.message}`); } finally { setDownloadingUrl(null); } }; useEffect(() => { if (agentDetails?.theme && themes[agentDetails.theme]) { setCurrentTheme(themes[agentDetails.theme]); } }, [agentDetails]); useEffect(() => { if (initialAgentDetails) { setAgentDetails(initialAgentDetails); } else { // fetchAgentDetails(); } }, [lowerAgentSlug, initialAgentDetails]); useEffect(() => { const checkPendingMessage = async () => { if (effectiveConversationId) { const pending = sessionStorage.getItem('pending_first_msg'); if (pending) { try { const { convId, text, attachments: pendingAttachments } = JSON.parse(pending); if (convId === effectiveConversationId) { sessionStorage.removeItem('pending_first_msg'); setTimeout(() => { handleSendMessage(null, text, pendingAttachments); }, 100); } } catch (e) { console.error("Failed to parse pending message", e); } } } }; checkPendingMessage(); }, [effectiveConversationId]); useEffect(() => { if (textareaRef.current) { textareaRef.current.style.height = 'auto'; textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`; } }, [input]); useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth", }); } }, [messages]); const fetchAgentDetails = async () => { try { const endpoint = `${BASE_URL}/by-slug/${lowerAgentSlug}`; const response = await axios.get(endpoint); setAgentDetails(response.data); } catch (err) { setAgentDetails({ name: "Autonomous Agent", description: "MuAPI Powered Intelligence.", }); } }; const uploadFile = async (file) => { if (!file) return; if (file.size > 10 * 1024 * 1024) { setError("File size too large (max 10MB)"); return; } try { setUploadProgress(0); setIsUploading(true); const response = await axios.get("/api/app/get_file_upload_url", { params: { filename: file.name } }); const { url, fields } = response.data; const formData = new FormData(); Object.entries(fields).forEach(([key, value]) => { formData.append(key, value); }); formData.append("file", file); await axios.post(url, formData, { headers: { "Content-Type": "multipart/form-data" }, onUploadProgress: (progressEvent) => { const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total); setUploadProgress(percent); } }); const prefix = "https://cdn.muapi.ai/"; const uploadedUrl = prefix + fields.key; setAttachments(prev => [...prev, uploadedUrl]); } catch (err) { console.error("Upload failed", err); setError("Failed to upload image."); } finally { setIsUploading(false); setUploadProgress(0); if (fileInputRef.current) fileInputRef.current.value = ""; } }; const handleFileUpload = (e) => { const file = e.target.files[0]; uploadFile(file); }; const handleDragOver = (e) => { e.preventDefault(); setIsDragging(true); }; const handleDragLeave = (e) => { e.preventDefault(); setIsDragging(false); }; const handleDrop = (e) => { e.preventDefault(); setIsDragging(false); const file = e.dataTransfer.files[0]; if (file && file.type.startsWith("image/")) { uploadFile(file); } else if (file) { setError("Please only upload image files."); } }; const removeAttachment = (url) => { setAttachments(prev => prev.filter(item => item !== url)); }; const handleThemeChange = async (theme) => { setCurrentTheme(theme); handleThemeSync(theme); }; const handleLike = async () => { const newLiked = !liked; const prevLikeCount = likeCount; // Optimistic update setLiked(newLiked); setLikeCount(prev => newLiked ? prev + 1 : prev - 1); try { const res = await axios.post(`/api/agents/by-slug/${lowerAgentSlug}/like?is_like=${newLiked}`); setLiked(res.data.has_liked); setLikeCount(res.data.like_count); } catch (err) { console.error("Failed to sync like:", err); // Rollback setLiked(!newLiked); setLikeCount(prevLikeCount); } }; const handleNewChat = () => { if (lowerAgentSlug) { router.push(`/agents/${lowerAgentSlug}`); } }; const handleSendMessage = async (e, overrideText = null, overrideAttachments = null) => { if (e) e.preventDefault(); const userText = overrideText || input; const currentAttachments = overrideAttachments || (overrideText ? [] : attachments); if (!userText.trim()) return; if (isStreaming && !overrideText) return; if (overrideText) setIsStreaming(false); const userMessage = { role: "user", content: userText, attachments: [...currentAttachments], timestamp: new Date(), }; setMessages((prev) => [...prev, userMessage]); if (!overrideText) { setAttachments([]); setInput(""); } setIsStreaming(true); setError(null); setDebugLogs([]); const assistantMsgId = `asst_${Date.now()}`; currentAssistantMsgRef.current = { id: assistantMsgId, role: "assistant", content: "", thoughts: "", status: [], suggestions: [], timestamp: new Date(), }; setMessages((prev) => [...prev, { ...currentAssistantMsgRef.current }]); try { let currentConvId = conversationIdRef.current || effectiveConversationId; if (!currentConvId && !overrideText) { const newConvId = crypto.randomUUID(); conversationIdRef.current = newConvId; sessionStorage.setItem('pending_first_msg', JSON.stringify({ convId: newConvId, text: userText, attachments: currentAttachments, timestamp: new Date().toISOString() })); if (lowerAgentSlug) { router.replace(`/agents/${lowerAgentSlug}/${newConvId}`); } return; } const initialRes = await axios.post( `${BASE_URL}/by-slug/${lowerAgentSlug}/chat`, { message: userText, stream: false, conversation_id: currentConvId, attachments: userMessage.attachments, } ); const { request_id } = initialRes.data; if (!request_id) throw new Error("No Request ID returned from agent"); const pollInterval = 1000; let isComplete = false; let errors = 0; while (!isComplete && errors < 5) { try { const pollRes = await axios.get(`/api/api/v1/predictions/${request_id}/result`); const data = pollRes.data; // data format from backend execute_agent_chat_background: // { // conversation_id, // messages: [{role, content...}, {type:'pulse'...}], // status_text, // is_complete, // suggestions, // error // } if (data.conversation_id) conversationIdRef.current = data.conversation_id; const incomingMessages = data.messages || []; let newContent = ""; let newThoughts = ""; let newStatus = []; incomingMessages.forEach(msg => { if (msg.role === "assistant" && msg.content) { newContent = msg.content; } if (msg.type === "pulse" && msg.content) { newStatus.push(msg.content); } if (msg.role === "assistant" && msg.thoughts) { newThoughts = msg.thoughts; } }); currentAssistantMsgRef.current.content = newContent; currentAssistantMsgRef.current.status = newStatus; currentAssistantMsgRef.current.suggestions = data.suggestions || []; setMessages((prev) => { const index = prev.findIndex((m) => m.id === assistantMsgId); if (index !== -1) { const newMessages = [...prev]; newMessages[index] = { ...newMessages[index], content: newContent, status: newStatus, suggestions: data.suggestions || [], }; return newMessages; } return prev; }); if (data.status === "failed") { throw new Error(data.error || "Agent execution failed"); } if (data.status === "completed" || data.status === "succeeded" || data.is_complete) { isComplete = true; } else { await new Promise(r => setTimeout(r, pollInterval)); } } catch (pollErr) { console.error("Polling error", pollErr); errors++; await new Promise(r => setTimeout(r, 2000)); } } if (errors >= 5) throw new Error("Lost connection to agent process"); } catch (err) { console.log("Agent error:", err); let errorMessage = err.message || "Something went wrong. Check browser console"; if (err.response) { const { status, data } = err.response; errorMessage = data?.error || "Not enough credits"; } else { errorMessage = err.message; } setError(errorMessage); if (!currentAssistantMsgRef.current.content) { setMessages((prev) => prev.filter((m) => m.id !== assistantMsgId)); } } finally { setIsStreaming(false); } }; return (
{isMounted && (