mirror of https://github.com/kortix-ai/suna.git
chore(dev): show custom agent name on thread page
This commit is contained in:
parent
beeaffb533
commit
cb36e534b2
|
@ -72,6 +72,11 @@ class AgentResponse(BaseModel):
|
||||||
created_at: str
|
created_at: str
|
||||||
updated_at: str
|
updated_at: str
|
||||||
|
|
||||||
|
class ThreadAgentResponse(BaseModel):
|
||||||
|
agent: Optional[AgentResponse]
|
||||||
|
source: str # "thread", "default", "none", "missing"
|
||||||
|
message: str
|
||||||
|
|
||||||
def initialize(
|
def initialize(
|
||||||
_thread_manager: ThreadManager,
|
_thread_manager: ThreadManager,
|
||||||
_db: DBConnection,
|
_db: DBConnection,
|
||||||
|
@ -401,6 +406,78 @@ async def get_agent_run(agent_run_id: str, user_id: str = Depends(get_current_us
|
||||||
"error": agent_run_data['error']
|
"error": agent_run_data['error']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@router.get("/thread/{thread_id}/agent", response_model=ThreadAgentResponse)
|
||||||
|
async def get_thread_agent(thread_id: str, user_id: str = Depends(get_current_user_id_from_jwt)):
|
||||||
|
"""Get the agent details for a specific thread."""
|
||||||
|
logger.info(f"Fetching agent details for thread: {thread_id}")
|
||||||
|
client = await db.client
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Verify thread access and get thread data including agent_id
|
||||||
|
await verify_thread_access(client, thread_id, user_id)
|
||||||
|
thread_result = await client.table('threads').select('agent_id', 'account_id').eq('thread_id', thread_id).execute()
|
||||||
|
|
||||||
|
if not thread_result.data:
|
||||||
|
raise HTTPException(status_code=404, detail="Thread not found")
|
||||||
|
|
||||||
|
thread_data = thread_result.data[0]
|
||||||
|
thread_agent_id = thread_data.get('agent_id')
|
||||||
|
account_id = thread_data.get('account_id')
|
||||||
|
|
||||||
|
# If no agent_id is set in the thread, try to get the default agent
|
||||||
|
effective_agent_id = thread_agent_id
|
||||||
|
agent_source = "thread"
|
||||||
|
|
||||||
|
if not effective_agent_id:
|
||||||
|
# No agent set in thread, get default agent for the account
|
||||||
|
default_agent_result = await client.table('agents').select('agent_id').eq('account_id', account_id).eq('is_default', True).execute()
|
||||||
|
if default_agent_result.data:
|
||||||
|
effective_agent_id = default_agent_result.data[0]['agent_id']
|
||||||
|
agent_source = "default"
|
||||||
|
else:
|
||||||
|
# No default agent found
|
||||||
|
return {
|
||||||
|
"agent": None,
|
||||||
|
"source": "none",
|
||||||
|
"message": "No agent configured for this thread"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fetch the agent details
|
||||||
|
agent_result = await client.table('agents').select('*').eq('agent_id', effective_agent_id).eq('account_id', account_id).execute()
|
||||||
|
|
||||||
|
if not agent_result.data:
|
||||||
|
# Agent was deleted or doesn't exist
|
||||||
|
return {
|
||||||
|
"agent": None,
|
||||||
|
"source": "missing",
|
||||||
|
"message": f"Agent {effective_agent_id} not found or was deleted"
|
||||||
|
}
|
||||||
|
|
||||||
|
agent_data = agent_result.data[0]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"agent": AgentResponse(
|
||||||
|
agent_id=agent_data['agent_id'],
|
||||||
|
account_id=agent_data['account_id'],
|
||||||
|
name=agent_data['name'],
|
||||||
|
description=agent_data.get('description'),
|
||||||
|
system_prompt=agent_data['system_prompt'],
|
||||||
|
configured_mcps=agent_data.get('configured_mcps', []),
|
||||||
|
agentpress_tools=agent_data.get('agentpress_tools', {}),
|
||||||
|
is_default=agent_data.get('is_default', False),
|
||||||
|
created_at=agent_data['created_at'],
|
||||||
|
updated_at=agent_data['updated_at']
|
||||||
|
),
|
||||||
|
"source": agent_source,
|
||||||
|
"message": f"Using {agent_source} agent: {agent_data['name']}"
|
||||||
|
}
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error fetching agent for thread {thread_id}: {str(e)}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to fetch thread agent: {str(e)}")
|
||||||
|
|
||||||
@router.get("/agent-run/{agent_run_id}/stream")
|
@router.get("/agent-run/{agent_run_id}/stream")
|
||||||
async def stream_agent_run(
|
async def stream_agent_run(
|
||||||
agent_run_id: str,
|
agent_run_id: str,
|
||||||
|
|
|
@ -23,7 +23,6 @@ import {
|
||||||
} from '@/lib/api';
|
} from '@/lib/api';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { ChatInput } from '@/components/thread/chat-input/chat-input';
|
import { ChatInput } from '@/components/thread/chat-input/chat-input';
|
||||||
import { AgentSelector } from '@/components/thread/chat-input/agent-selector';
|
|
||||||
import { FileViewerModal } from '@/components/thread/file-viewer-modal';
|
import { FileViewerModal } from '@/components/thread/file-viewer-modal';
|
||||||
import { SiteHeader } from '@/components/thread/thread-site-header';
|
import { SiteHeader } from '@/components/thread/thread-site-header';
|
||||||
import {
|
import {
|
||||||
|
@ -47,6 +46,7 @@ import {
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
UnifiedMessage,
|
UnifiedMessage,
|
||||||
|
@ -63,6 +63,7 @@ import { useAgentRunsQuery, useStartAgentMutation, useStopAgentMutation } from '
|
||||||
import { useBillingStatusQuery } from '@/hooks/react-query/threads/use-billing-status';
|
import { useBillingStatusQuery } from '@/hooks/react-query/threads/use-billing-status';
|
||||||
import { useSubscription, isPlan } from '@/hooks/react-query/subscriptions/use-subscriptions';
|
import { useSubscription, isPlan } from '@/hooks/react-query/subscriptions/use-subscriptions';
|
||||||
import { SubscriptionStatus } from '@/components/thread/chat-input/_use-model-selection';
|
import { SubscriptionStatus } from '@/components/thread/chat-input/_use-model-selection';
|
||||||
|
import { useThreadAgent } from '@/hooks/react-query/agents/use-agents';
|
||||||
|
|
||||||
// Extend the base Message type with the expected database fields
|
// Extend the base Message type with the expected database fields
|
||||||
interface ApiMessageType extends BaseApiMessageType {
|
interface ApiMessageType extends BaseApiMessageType {
|
||||||
|
@ -136,6 +137,7 @@ export default function ThreadPage({
|
||||||
const messagesLoadedRef = useRef(false);
|
const messagesLoadedRef = useRef(false);
|
||||||
const agentRunsCheckedRef = useRef(false);
|
const agentRunsCheckedRef = useRef(false);
|
||||||
const previousAgentStatus = useRef<typeof agentStatus>('idle');
|
const previousAgentStatus = useRef<typeof agentStatus>('idle');
|
||||||
|
const { data: threadAgent, isLoading: threadAgentLoading, error: threadAgentError } = useThreadAgent(threadId);
|
||||||
|
|
||||||
const [externalNavIndex, setExternalNavIndex] = React.useState<number | undefined>(undefined);
|
const [externalNavIndex, setExternalNavIndex] = React.useState<number | undefined>(undefined);
|
||||||
|
|
||||||
|
@ -1223,19 +1225,27 @@ export default function ThreadPage({
|
||||||
"mx-auto",
|
"mx-auto",
|
||||||
isMobile ? "w-full px-4" : "max-w-3xl"
|
isMobile ? "w-full px-4" : "max-w-3xl"
|
||||||
)}>
|
)}>
|
||||||
<ChatInput
|
{threadAgentLoading || threadAgentError ? (
|
||||||
value={newMessage}
|
<div className="space-y-3">
|
||||||
onChange={setNewMessage}
|
<Skeleton className="h-4 w-32" />
|
||||||
onSubmit={handleSubmitMessage}
|
<Skeleton className="h-12 w-full rounded-lg" />
|
||||||
placeholder="Ask Suna anything..."
|
</div>
|
||||||
loading={isSending}
|
) : (
|
||||||
disabled={isSending || agentStatus === 'running' || agentStatus === 'connecting'}
|
<ChatInput
|
||||||
isAgentRunning={agentStatus === 'running' || agentStatus === 'connecting'}
|
value={newMessage}
|
||||||
onStopAgent={handleStopAgent}
|
onChange={setNewMessage}
|
||||||
autoFocus={!isLoading}
|
onSubmit={handleSubmitMessage}
|
||||||
onFileBrowse={handleOpenFileViewer}
|
placeholder={`Ask ${threadAgent?.agent?.name || 'Suna'} anything...`}
|
||||||
sandboxId={sandboxId || undefined}
|
loading={isSending}
|
||||||
/>
|
disabled={isSending || agentStatus === 'running' || agentStatus === 'connecting'}
|
||||||
|
isAgentRunning={agentStatus === 'running' || agentStatus === 'connecting'}
|
||||||
|
onStopAgent={handleStopAgent}
|
||||||
|
autoFocus={!isLoading}
|
||||||
|
onFileBrowse={handleOpenFileViewer}
|
||||||
|
sandboxId={sandboxId || undefined}
|
||||||
|
agentName={threadAgent?.agent?.name || 'Suna'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1257,6 +1267,7 @@ export default function ThreadPage({
|
||||||
renderAssistantMessage={toolViewAssistant}
|
renderAssistantMessage={toolViewAssistant}
|
||||||
renderToolResult={toolViewResult}
|
renderToolResult={toolViewResult}
|
||||||
isLoading={!initialLoadCompleted.current || isLoading}
|
isLoading={!initialLoadCompleted.current || isLoading}
|
||||||
|
agentName={threadAgentLoading ? 'Loading...' : (threadAgent?.agent?.name || 'Suna')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{sandboxId && (
|
{sandboxId && (
|
||||||
|
|
|
@ -129,7 +129,7 @@ export function AgentSelector({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-muted-foreground pl-6 line-clamp-2">
|
<span className="text-xs text-muted-foreground pl-6 line-clamp-2">
|
||||||
Your personal AI assistant
|
Your personal AI employee
|
||||||
</span>
|
</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{agents.length > 0 ? (
|
{agents.length > 0 ? (
|
||||||
|
@ -227,7 +227,7 @@ export function AgentSelector({
|
||||||
</span>
|
</span>
|
||||||
) : isUsingSuna ? (
|
) : isUsingSuna ? (
|
||||||
<span className="text-xs text-muted-foreground line-clamp-1 max-w-[150px]">
|
<span className="text-xs text-muted-foreground line-clamp-1 max-w-[150px]">
|
||||||
Your personal AI assistant
|
Your personal AI employee
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -254,7 +254,7 @@ export function AgentSelector({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-muted-foreground pl-6 line-clamp-2">
|
<span className="text-xs text-muted-foreground pl-6 line-clamp-2">
|
||||||
Your personal AI assistant
|
Your personal AI employee
|
||||||
</span>
|
</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{agents.length > 0 ? (
|
{agents.length > 0 ? (
|
||||||
|
|
|
@ -39,6 +39,7 @@ export interface ChatInputProps {
|
||||||
hideAttachments?: boolean;
|
hideAttachments?: boolean;
|
||||||
selectedAgentId?: string;
|
selectedAgentId?: string;
|
||||||
onAgentSelect?: (agentId: string | undefined) => void;
|
onAgentSelect?: (agentId: string | undefined) => void;
|
||||||
|
agentName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UploadedFile {
|
export interface UploadedFile {
|
||||||
|
@ -66,6 +67,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
||||||
hideAttachments = false,
|
hideAttachments = false,
|
||||||
selectedAgentId,
|
selectedAgentId,
|
||||||
onAgentSelect,
|
onAgentSelect,
|
||||||
|
agentName,
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
|
@ -263,7 +265,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
||||||
>
|
>
|
||||||
<div className="text-xs text-muted-foreground flex items-center gap-2">
|
<div className="text-xs text-muted-foreground flex items-center gap-2">
|
||||||
<Loader2 className="h-3 w-3 animate-spin" />
|
<Loader2 className="h-3 w-3 animate-spin" />
|
||||||
<span>Kortix Suna is working...</span>
|
<span>{agentName ? `${agentName} is working...` : 'Suna is working...'}</span>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -45,6 +45,7 @@ interface ToolCallSidePanelProps {
|
||||||
isSuccess?: boolean,
|
isSuccess?: boolean,
|
||||||
) => React.ReactNode;
|
) => React.ReactNode;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
|
agentName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolCallSnapshot {
|
interface ToolCallSnapshot {
|
||||||
|
@ -65,6 +66,7 @@ export function ToolCallSidePanel({
|
||||||
project,
|
project,
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
externalNavigateToIndex,
|
externalNavigateToIndex,
|
||||||
|
agentName
|
||||||
}: ToolCallSidePanelProps) {
|
}: ToolCallSidePanelProps) {
|
||||||
const [dots, setDots] = React.useState('');
|
const [dots, setDots] = React.useState('');
|
||||||
const [internalIndex, setInternalIndex] = React.useState(0);
|
const [internalIndex, setInternalIndex] = React.useState(0);
|
||||||
|
@ -233,7 +235,7 @@ export function ToolCallSidePanel({
|
||||||
<div className="ml-2 flex items-center gap-2">
|
<div className="ml-2 flex items-center gap-2">
|
||||||
<Computer className="h-4 w-4" />
|
<Computer className="h-4 w-4" />
|
||||||
<h2 className="text-md font-medium text-zinc-900 dark:text-zinc-100">
|
<h2 className="text-md font-medium text-zinc-900 dark:text-zinc-100">
|
||||||
Computer View
|
{agentName ? `${agentName}'s Computer` : 'Suna\'s Computer'}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
|
@ -269,7 +271,7 @@ export function ToolCallSidePanel({
|
||||||
<div className="ml-2 flex items-center gap-2">
|
<div className="ml-2 flex items-center gap-2">
|
||||||
<Computer className="h-4 w-4" />
|
<Computer className="h-4 w-4" />
|
||||||
<h2 className="text-md font-medium text-zinc-900 dark:text-zinc-100">
|
<h2 className="text-md font-medium text-zinc-900 dark:text-zinc-100">
|
||||||
Computer View
|
{agentName ? `${agentName}'s Computer` : 'Suna\'s Computer'}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
|
@ -330,7 +332,7 @@ export function ToolCallSidePanel({
|
||||||
<div className="ml-2 flex items-center gap-2">
|
<div className="ml-2 flex items-center gap-2">
|
||||||
<Computer className="h-4 w-4" />
|
<Computer className="h-4 w-4" />
|
||||||
<h2 className="text-md font-medium text-zinc-900 dark:text-zinc-100">
|
<h2 className="text-md font-medium text-zinc-900 dark:text-zinc-100">
|
||||||
Computer View
|
{agentName ? `${agentName}'s Computer` : 'Suna\'s Computer'}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { createQueryKeys } from "@/hooks/use-query";
|
import { createQueryKeys } from "@/hooks/use-query";
|
||||||
|
|
||||||
export const agentKeys = createQueryKeys({
|
export const agentKeys = createQueryKeys({
|
||||||
all: ['agents'] as const,
|
all: ['agents'] as const,
|
||||||
lists: () => [...agentKeys.all, 'list'] as const,
|
lists: () => [...agentKeys.all, 'list'] as const,
|
||||||
list: (filters?: Record<string, any>) => [...agentKeys.lists(), filters] as const,
|
list: (filters?: Record<string, any>) => [...agentKeys.lists(), filters] as const,
|
||||||
details: () => [...agentKeys.all, 'detail'] as const,
|
details: () => [...agentKeys.all, 'detail'] as const,
|
||||||
detail: (id: string) => [...agentKeys.details(), id] as const,
|
detail: (id: string) => [...agentKeys.details(), id] as const,
|
||||||
});
|
threadAgents: () => [...agentKeys.all, 'thread-agent'] as const,
|
||||||
|
threadAgent: (threadId: string) => [...agentKeys.threadAgents(), threadId] as const,
|
||||||
|
});
|
|
@ -2,7 +2,7 @@ import { createMutationHook, createQueryHook } from '@/hooks/use-query';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { agentKeys } from './keys';
|
import { agentKeys } from './keys';
|
||||||
import { Agent, AgentUpdateRequest, createAgent, deleteAgent, getAgent, getAgents, updateAgent } from './utils';
|
import { Agent, AgentUpdateRequest, createAgent, deleteAgent, getAgent, getAgents, getThreadAgent, updateAgent } from './utils';
|
||||||
|
|
||||||
export const useAgents = createQueryHook(
|
export const useAgents = createQueryHook(
|
||||||
agentKeys.list(),
|
agentKeys.list(),
|
||||||
|
@ -91,4 +91,16 @@ export const useOptimisticAgentUpdate = () => {
|
||||||
queryClient.invalidateQueries({ queryKey: agentKeys.detail(agentId) });
|
queryClient.invalidateQueries({ queryKey: agentKeys.detail(agentId) });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useThreadAgent = (threadId: string) => {
|
||||||
|
return createQueryHook(
|
||||||
|
agentKeys.threadAgent(threadId),
|
||||||
|
() => getThreadAgent(threadId),
|
||||||
|
{
|
||||||
|
enabled: !!threadId,
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
gcTime: 10 * 60 * 1000,
|
||||||
|
}
|
||||||
|
)();
|
||||||
};
|
};
|
|
@ -3,197 +3,234 @@ import { createClient } from "@/lib/supabase/client";
|
||||||
const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || '';
|
const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || '';
|
||||||
|
|
||||||
export type Agent = {
|
export type Agent = {
|
||||||
agent_id: string;
|
agent_id: string;
|
||||||
account_id: string;
|
account_id: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
system_prompt: string;
|
||||||
|
configured_mcps: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
config: Record<string, any>;
|
||||||
system_prompt: string;
|
}>;
|
||||||
configured_mcps: Array<{
|
agentpress_tools: Record<string, any>;
|
||||||
name: string;
|
is_default: boolean;
|
||||||
config: Record<string, any>;
|
created_at: string;
|
||||||
}>;
|
updated_at: string;
|
||||||
agentpress_tools: Record<string, any>;
|
};
|
||||||
is_default: boolean;
|
|
||||||
created_at: string;
|
export type ThreadAgentResponse = {
|
||||||
updated_at: string;
|
agent: Agent | null;
|
||||||
};
|
source: 'thread' | 'default' | 'none' | 'missing';
|
||||||
|
message: string;
|
||||||
export type AgentCreateRequest = {
|
};
|
||||||
|
|
||||||
|
export type AgentCreateRequest = {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
system_prompt: string;
|
||||||
|
configured_mcps?: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
config: Record<string, any>;
|
||||||
system_prompt: string;
|
}>;
|
||||||
configured_mcps?: Array<{
|
agentpress_tools?: Record<string, any>;
|
||||||
name: string;
|
is_default?: boolean;
|
||||||
config: Record<string, any>;
|
};
|
||||||
}>;
|
|
||||||
agentpress_tools?: Record<string, any>;
|
export type AgentUpdateRequest = {
|
||||||
is_default?: boolean;
|
name?: string;
|
||||||
};
|
description?: string;
|
||||||
|
system_prompt?: string;
|
||||||
export type AgentUpdateRequest = {
|
configured_mcps?: Array<{
|
||||||
name?: string;
|
name: string;
|
||||||
description?: string;
|
config: Record<string, any>;
|
||||||
system_prompt?: string;
|
}>;
|
||||||
configured_mcps?: Array<{
|
agentpress_tools?: Record<string, any>;
|
||||||
name: string;
|
is_default?: boolean;
|
||||||
config: Record<string, any>;
|
};
|
||||||
}>;
|
|
||||||
agentpress_tools?: Record<string, any>;
|
export const getAgents = async (): Promise<Agent[]> => {
|
||||||
is_default?: boolean;
|
try {
|
||||||
};
|
const supabase = createClient();
|
||||||
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
export const getAgents = async (): Promise<Agent[]> => {
|
|
||||||
try {
|
if (!session) {
|
||||||
const supabase = createClient();
|
throw new Error('You must be logged in to get agents');
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
|
||||||
|
|
||||||
if (!session) {
|
|
||||||
throw new Error('You must be logged in to get agents');
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(`${API_URL}/agents`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${session.access_token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
||||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const agents = await response.json();
|
|
||||||
console.log('[API] Fetched agents:', agents.length);
|
|
||||||
return agents;
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error fetching agents:', err);
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const response = await fetch(`${API_URL}/agents`, {
|
||||||
export const getAgent = async (agentId: string): Promise<Agent> => {
|
method: 'GET',
|
||||||
try {
|
headers: {
|
||||||
const supabase = createClient();
|
'Content-Type': 'application/json',
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
'Authorization': `Bearer ${session.access_token}`,
|
||||||
|
},
|
||||||
if (!session) {
|
});
|
||||||
throw new Error('You must be logged in to get agent details');
|
|
||||||
}
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
||||||
const response = await fetch(`${API_URL}/agents/${agentId}`, {
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${session.access_token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
||||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const agent = await response.json();
|
|
||||||
console.log('[API] Fetched agent:', agent.agent_id);
|
|
||||||
return agent;
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error fetching agent:', err);
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const agents = await response.json();
|
||||||
export const createAgent = async (agentData: AgentCreateRequest): Promise<Agent> => {
|
console.log('[API] Fetched agents:', agents.length);
|
||||||
try {
|
return agents;
|
||||||
const supabase = createClient();
|
} catch (err) {
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
console.error('Error fetching agents:', err);
|
||||||
|
throw err;
|
||||||
if (!session) {
|
}
|
||||||
throw new Error('You must be logged in to create an agent');
|
};
|
||||||
}
|
|
||||||
|
export const getAgent = async (agentId: string): Promise<Agent> => {
|
||||||
const response = await fetch(`${API_URL}/agents`, {
|
try {
|
||||||
method: 'POST',
|
const supabase = createClient();
|
||||||
headers: {
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${session.access_token}`,
|
if (!session) {
|
||||||
},
|
throw new Error('You must be logged in to get agent details');
|
||||||
body: JSON.stringify(agentData),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
||||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const agent = await response.json();
|
|
||||||
console.log('[API] Created agent:', agent.agent_id);
|
|
||||||
return agent;
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error creating agent:', err);
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const response = await fetch(`${API_URL}/agents/${agentId}`, {
|
||||||
export const updateAgent = async (agentId: string, agentData: AgentUpdateRequest): Promise<Agent> => {
|
method: 'GET',
|
||||||
try {
|
headers: {
|
||||||
const supabase = createClient();
|
'Content-Type': 'application/json',
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
'Authorization': `Bearer ${session.access_token}`,
|
||||||
|
},
|
||||||
if (!session) {
|
});
|
||||||
throw new Error('You must be logged in to update an agent');
|
|
||||||
}
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
||||||
const response = await fetch(`${API_URL}/agents/${agentId}`, {
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${session.access_token}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(agentData),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
||||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const agent = await response.json();
|
|
||||||
console.log('[API] Updated agent:', agent.agent_id);
|
|
||||||
return agent;
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error updating agent:', err);
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const agent = await response.json();
|
||||||
export const deleteAgent = async (agentId: string): Promise<void> => {
|
console.log('[API] Fetched agent:', agent.agent_id);
|
||||||
try {
|
return agent;
|
||||||
const supabase = createClient();
|
} catch (err) {
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
console.error('Error fetching agent:', err);
|
||||||
|
throw err;
|
||||||
if (!session) {
|
}
|
||||||
throw new Error('You must be logged in to delete an agent');
|
};
|
||||||
}
|
|
||||||
|
export const createAgent = async (agentData: AgentCreateRequest): Promise<Agent> => {
|
||||||
const response = await fetch(`${API_URL}/agents/${agentId}`, {
|
try {
|
||||||
method: 'DELETE',
|
const supabase = createClient();
|
||||||
headers: {
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${session.access_token}`,
|
if (!session) {
|
||||||
},
|
throw new Error('You must be logged in to create an agent');
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
||||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[API] Deleted agent:', agentId);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error deleting agent:', err);
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const response = await fetch(`${API_URL}/agents`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${session.access_token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(agentData),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
||||||
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const agent = await response.json();
|
||||||
|
console.log('[API] Created agent:', agent.agent_id);
|
||||||
|
return agent;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error creating agent:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateAgent = async (agentId: string, agentData: AgentUpdateRequest): Promise<Agent> => {
|
||||||
|
try {
|
||||||
|
const supabase = createClient();
|
||||||
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new Error('You must be logged in to update an agent');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${API_URL}/agents/${agentId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${session.access_token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(agentData),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
||||||
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const agent = await response.json();
|
||||||
|
console.log('[API] Updated agent:', agent.agent_id);
|
||||||
|
return agent;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error updating agent:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteAgent = async (agentId: string): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const supabase = createClient();
|
||||||
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new Error('You must be logged in to delete an agent');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${API_URL}/agents/${agentId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${session.access_token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
||||||
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[API] Deleted agent:', agentId);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting agent:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getThreadAgent = async (threadId: string): Promise<ThreadAgentResponse> => {
|
||||||
|
try {
|
||||||
|
const supabase = createClient();
|
||||||
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new Error('You must be logged in to get thread agent details');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${API_URL}/thread/${threadId}/agent`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${session.access_token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
||||||
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const threadAgent = await response.json();
|
||||||
|
console.log('[API] Fetched thread agent:', threadAgent.source, threadAgent.agent?.name);
|
||||||
|
return threadAgent;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching thread agent:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue