Skip to main content
Glama
research-agent-research.tsx9.17 kB
"use client" import React from "react" import { useEffect, useRef, useState } from "react" import { Button } from "@/components/ui/button" import { Textarea } from "@/components/ui/textarea" import { Send, StopCircle, RefreshCw } from "lucide-react" import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" import { SourcesList } from "@/components/sources-list" import { SearchSuggestions } from "@/components/search-suggestions" import { ProviderSelector } from "@/components/provider-selector" import { Switch } from "@/components/ui/switch" import { Label } from "@/components/ui/label" import type { ProviderType, Message } from "@/lib/types" import { useAgentResearchContext } from "@/contexts/agent-research-context" import { LikeButton } from "@/components/like-button" import { ChatMessageSingle } from "./chat-message-single" import { ModelConfigDialog } from "./model-config-dialog" import { AgentModelSelector } from "./agent-model-selector" import { ModelConfigDisplay } from "./model-config-display" import { ThinkingStickerAnimated } from "./thinking-sticker-animated" import { useResearchAgent } from "@/hooks/research/agent/research/use-research-agent" interface ResearchAgentResearchProps { initialProviderId?: ProviderType } export function ResearchAgentResearch({ initialProviderId = "gemini" }: ResearchAgentResearchProps) { const [input, setInput] = useState("") const [isActive, setIsActive] = useState(true) const [providerId, setProviderId] = useState<ProviderType>(initialProviderId) const { providers, updateSelectedAgentModel } = useAgentResearchContext() const research = useResearchAgent() const formRef = useRef<HTMLFormElement>(null) const textareaRef = useRef<HTMLTextAreaElement>(null) const provider = providers.find((p) => p.id === providerId) useEffect(() => { const defaultModel = providers.find((p) => p.id === initialProviderId)!.models.find((m) => m.isDefault) || providers.find((p) => p.id === initialProviderId)!.models[0] updateSelectedAgentModel("research", initialProviderId, defaultModel.id) }, [initialProviderId]) const isLoading = isActive && (research.status === "streaming" || research.status === "submitted") const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { setInput(e.target.value) } const handleSearchSuggestionClick = (suggestion: string) => { setInput(suggestion) } const toggleActive = () => { setIsActive((prev) => !prev) } const handleProviderChange = (newProviderId: ProviderType) => { if (isLoading) { research.stop() } setProviderId(newProviderId) const provider = providers.find((p) => p.id === newProviderId) if (provider && provider.models.length > 0) { const defaultModel = provider.models.find((m) => m.isDefault) || provider.models[0] updateSelectedAgentModel("research", newProviderId, defaultModel.id) } } const handleModelChange = (modelId: string) => { updateSelectedAgentModel("research", providerId, modelId) } const handleConfigChange = (config: any, showToast = false) => { if (research.updateModelConfig) { research.updateModelConfig(config, showToast) } } const handleSubmit = async (e?: React.FormEvent) => { e?.preventDefault() if (!input.trim() || !isActive) return await research.sendMessage(input) setInput("") } const stopGenerating = () => { if (isActive && (research.status === "streaming" || research.status === "submitted")) { research.stop() } } const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => { if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { formRef.current?.requestSubmit() } } const messages_length = research.messages ? research.messages.length : 0 return ( <div className="flex flex-col h-[calc(100vh-7rem)] mx-auto"> <div className="flex-1"> <Card className={`h-full flex flex-col ${!isActive ? "opacity-50" : ""}`}> <CardHeader className="pb-0"> <div className="flex items-center justify-between mb-2"> <div className="flex items-center gap-3"> <CardTitle className="text-lg">{provider?.name || providerId}</CardTitle> <ThinkingStickerAnimated /> <LikeButton initialCount={Math.floor(Math.random() * 200) + 100} /> </div> <div className="flex items-center space-x-2"> {research.modelConfig && ( <div className="flex items-center gap-1"> <ModelConfigDisplay config={research.modelConfig} /> <ModelConfigDialog config={research.modelConfig} onConfigChange={handleConfigChange} disabled={isLoading} /> </div> )} <Button variant="outline" size="sm" onClick={() => research.resetAgent()} disabled={!isActive || research.status === "streaming" || research.status === "submitted"} className="mr-2" > <RefreshCw className="h-4 w-4 mr-1" /> New Chat </Button> <Switch id="provider-active" checked={isActive} onCheckedChange={toggleActive} disabled={research.status === "streaming" || research.status === "submitted"} /> <Label htmlFor="provider-active">Active</Label> </div> </div> <div className="grid grid-cols-2 gap-2"> <ProviderSelector selectedProvider={providerId} onProviderChange={handleProviderChange} disabled={research.status === "streaming" || research.status === "submitted"} /> <AgentModelSelector agentType="research" providerId={providerId} disabled={research.status === "streaming" || research.status === "submitted"} onModelChange={handleModelChange} /> </div> </CardHeader> <CardContent className="flex-1 overflow-y-auto h-[calc(100vh-16rem)] max-h-[calc(100vh-19rem)]"> <div className="relative space-y-4"> <div className=""> {research.messages.map((message: Message, index) => ( <ChatMessageSingle key={message.id} message={message} status={research.status} isLast={index === messages_length - 1} className="rounded-3xl m-1 font-mono" useMarkdown={true} /> ))} </div> {research.sources && research.sources.length > 0 && research.status === "ready" && ( <SourcesList sources={research.sources} /> )} {research.searchSuggestions && research.searchSuggestions.length > 0 && research.status === "ready" && ( <SearchSuggestions suggestions={research.searchSuggestions} reasoning={research.searchSuggestionsReasoning} confidence={research.searchSuggestionsConfidence} onSuggestionClick={handleSearchSuggestionClick} /> )} </div> </CardContent> </Card> </div> <div className="p-4 w-full"> <form ref={formRef} onSubmit={handleSubmit} className="flex flex-col gap-2 w-full items-center"> <div className="relative w-full max-w-[768px] mx-auto"> <Textarea ref={textareaRef} value={input} onChange={handleInputChange} onKeyDown={handleKeyDown} placeholder="Ask a question..." className="min-h-24 resize-none pr-20" disabled={isLoading} /> <div className="absolute right-2 bottom-2"> {isLoading ? ( <Button type="button" size="icon" variant="ghost" onClick={stopGenerating} className="h-8 w-8"> <StopCircle className="h-4 w-4" /> <span className="sr-only">Stop generating</span> </Button> ) : ( <Button type="submit" size="icon" disabled={!input.trim() || !isActive} className="h-8 w-8"> <Send className="h-4 w-4" /> <span className="sr-only">Send message</span> </Button> )} </div> </div> <div className="text-xs text-muted-foreground text-right w-full max-w-[768px]"> Press <kbd className="rounded border px-1 py-0.5 bg-muted">Ctrl</kbd> +{" "} <kbd className="rounded border px-1 py-0.5 bg-muted">Enter</kbd> to send </div> </form> </div> </div> ) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/myeong-ga/research-agent-mcp-0.36-pro-preview-06-01'

If you have feedback or need assistance with the MCP directory API, please join our Discord server