mirror of https://github.com/kortix-ai/suna.git
461 lines
14 KiB
TypeScript
461 lines
14 KiB
TypeScript
import { createClient } from "@/lib/supabase/client";
|
|
import { isFlagEnabled } from "@/lib/feature-flags";
|
|
|
|
const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || '';
|
|
|
|
export type Agent = {
|
|
agent_id: string;
|
|
account_id: string;
|
|
name: string;
|
|
description?: string;
|
|
system_prompt: string;
|
|
configured_mcps: Array<{
|
|
name: string;
|
|
config: Record<string, any>;
|
|
}>;
|
|
custom_mcps?: Array<{
|
|
name: string;
|
|
type: 'json' | 'sse';
|
|
config: Record<string, any>;
|
|
enabledTools: string[];
|
|
}>;
|
|
agentpress_tools: Record<string, any>;
|
|
is_default: boolean;
|
|
is_public?: boolean;
|
|
marketplace_published_at?: string;
|
|
download_count?: number;
|
|
tags?: string[];
|
|
created_at: string;
|
|
updated_at: string;
|
|
avatar?: string;
|
|
avatar_color?: string;
|
|
};
|
|
|
|
export type PaginationInfo = {
|
|
page: number;
|
|
limit: number;
|
|
total: number;
|
|
pages: number;
|
|
};
|
|
|
|
export type AgentsResponse = {
|
|
agents: Agent[];
|
|
pagination: PaginationInfo;
|
|
};
|
|
|
|
export type AgentsParams = {
|
|
page?: number;
|
|
limit?: number;
|
|
search?: string;
|
|
sort_by?: string;
|
|
sort_order?: string;
|
|
has_default?: boolean;
|
|
has_mcp_tools?: boolean;
|
|
has_agentpress_tools?: boolean;
|
|
tools?: string;
|
|
};
|
|
|
|
export type ThreadAgentResponse = {
|
|
agent: Agent | null;
|
|
source: 'thread' | 'default' | 'none' | 'missing';
|
|
message: string;
|
|
};
|
|
|
|
export type AgentCreateRequest = {
|
|
name: string;
|
|
description?: string;
|
|
system_prompt: string;
|
|
configured_mcps?: Array<{
|
|
name: string;
|
|
config: Record<string, any>;
|
|
}>;
|
|
custom_mcps?: Array<{
|
|
name: string;
|
|
type: 'json' | 'sse';
|
|
config: Record<string, any>;
|
|
enabledTools: string[];
|
|
}>;
|
|
agentpress_tools?: Record<string, any>;
|
|
is_default?: boolean;
|
|
};
|
|
|
|
export type AgentUpdateRequest = {
|
|
name?: string;
|
|
description?: string;
|
|
system_prompt?: string;
|
|
configured_mcps?: Array<{
|
|
name: string;
|
|
config: Record<string, any>;
|
|
}>;
|
|
custom_mcps?: Array<{
|
|
name: string;
|
|
type: 'json' | 'sse';
|
|
config: Record<string, any>;
|
|
enabledTools: string[];
|
|
}>;
|
|
agentpress_tools?: Record<string, any>;
|
|
is_default?: boolean;
|
|
};
|
|
|
|
export const getAgents = async (params: AgentsParams = {}): Promise<AgentsResponse> => {
|
|
try {
|
|
const agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
const supabase = createClient();
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
|
|
if (!session) {
|
|
throw new Error('You must be logged in to get agents');
|
|
}
|
|
|
|
const queryParams = new URLSearchParams();
|
|
if (params.page) queryParams.append('page', params.page.toString());
|
|
if (params.limit) queryParams.append('limit', params.limit.toString());
|
|
if (params.search) queryParams.append('search', params.search);
|
|
if (params.sort_by) queryParams.append('sort_by', params.sort_by);
|
|
if (params.sort_order) queryParams.append('sort_order', params.sort_order);
|
|
if (params.has_default !== undefined) queryParams.append('has_default', params.has_default.toString());
|
|
if (params.has_mcp_tools !== undefined) queryParams.append('has_mcp_tools', params.has_mcp_tools.toString());
|
|
if (params.has_agentpress_tools !== undefined) queryParams.append('has_agentpress_tools', params.has_agentpress_tools.toString());
|
|
if (params.tools) queryParams.append('tools', params.tools);
|
|
|
|
const url = `${API_URL}/agents${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
|
|
|
|
const response = await fetch(url, {
|
|
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 result = await response.json();
|
|
console.log('[API] Fetched agents:', result.agents?.length || 0, 'total:', result.pagination?.total || 0);
|
|
return result;
|
|
} catch (err) {
|
|
console.error('Error fetching agents:', err);
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
export const getAgent = async (agentId: string): Promise<Agent> => {
|
|
try {
|
|
const agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
const supabase = createClient();
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
|
|
if (!session) {
|
|
throw new Error('You must be logged in to get agent details');
|
|
}
|
|
|
|
const response = await fetch(`${API_URL}/agents/${agentId}`, {
|
|
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;
|
|
}
|
|
};
|
|
|
|
export const createAgent = async (agentData: AgentCreateRequest): Promise<Agent> => {
|
|
try {
|
|
const agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
const supabase = createClient();
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
|
|
if (!session) {
|
|
throw new Error('You must be logged in to create an agent');
|
|
}
|
|
|
|
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 agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
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 agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
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 agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
const supabase = createClient();
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
|
|
if (!session) {
|
|
throw new Error('You must be logged in to get thread agent');
|
|
}
|
|
|
|
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 agent = await response.json();
|
|
console.log('[API] Fetched thread agent:', threadId);
|
|
return agent;
|
|
} catch (err) {
|
|
console.error('Error fetching thread agent:', err);
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
export const getAgentBuilderChatHistory = async (agentId: string): Promise<{messages: any[], thread_id: string | null}> => {
|
|
try {
|
|
const agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
const supabase = createClient();
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
|
|
if (!session) {
|
|
throw new Error('You must be logged in to get agent builder chat history');
|
|
}
|
|
|
|
const response = await fetch(`${API_URL}/agents/${agentId}/builder-chat-history`, {
|
|
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 data = await response.json();
|
|
console.log('[API] Fetched agent builder chat history:', agentId, data.messages.length);
|
|
return data;
|
|
} catch (err) {
|
|
console.error('Error fetching agent builder chat history:', err);
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
// Agent Builder Chat Types
|
|
export type AgentBuilderMessage = {
|
|
role: 'user' | 'assistant';
|
|
content: string;
|
|
};
|
|
|
|
export type AgentBuilderConfig = {
|
|
name?: string;
|
|
description?: string;
|
|
system_prompt?: string;
|
|
agentpress_tools?: Record<string, { enabled: boolean; description: string }>;
|
|
configured_mcps?: Array<{ name: string; qualifiedName: string; config: any; enabledTools?: string[] }>;
|
|
avatar?: string;
|
|
avatar_color?: string;
|
|
};
|
|
|
|
export type AgentBuilderChatRequest = {
|
|
message: string;
|
|
conversation_history: AgentBuilderMessage[];
|
|
agent_id: string;
|
|
partial_config?: AgentBuilderConfig;
|
|
};
|
|
|
|
export type AgentBuilderStreamData = {
|
|
type: 'content' | 'config' | 'done' | 'error';
|
|
content?: string;
|
|
config?: AgentBuilderConfig;
|
|
next_step?: string;
|
|
error?: string;
|
|
};
|
|
|
|
export const startAgentBuilderChat = async (
|
|
request: AgentBuilderChatRequest,
|
|
onData: (data: AgentBuilderStreamData) => void,
|
|
onComplete: () => void,
|
|
signal?: AbortSignal
|
|
): Promise<void> => {
|
|
try {
|
|
const agentPlaygroundEnabled = await isFlagEnabled('custom_agents');
|
|
if (!agentPlaygroundEnabled) {
|
|
throw new Error('Custom agents is not enabled');
|
|
}
|
|
const supabase = createClient();
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
|
|
if (!session) {
|
|
throw new Error('You must be logged in to use the agent builder');
|
|
}
|
|
|
|
const response = await fetch(`${API_URL}/agents/builder/chat/${request.agent_id}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${session.access_token}`,
|
|
},
|
|
body: JSON.stringify({
|
|
message: request.message,
|
|
conversation_history: request.conversation_history,
|
|
partial_config: request.partial_config
|
|
}),
|
|
signal,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const reader = response.body?.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
if (!reader) {
|
|
throw new Error('No response body');
|
|
}
|
|
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
const lines = chunk.split('\n');
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith('data: ')) {
|
|
try {
|
|
const data = JSON.parse(line.slice(6));
|
|
onData(data);
|
|
|
|
if (data.type === 'done') {
|
|
onComplete();
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
console.error('Error parsing SSE data:', e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('Error in agent builder chat:', err);
|
|
throw err;
|
|
}
|
|
};
|
|
|