mirror of https://github.com/kortix-ai/suna.git
Merge pull request #1165 from escapade-mckv/fix-agent-builder-2a
fix agent builder functionality
This commit is contained in:
commit
0250e786cd
|
@ -58,16 +58,18 @@ class ToolManager:
|
|||
self.thread_id = thread_id
|
||||
|
||||
def register_all_tools(self):
|
||||
self.thread_manager.add_tool(ExpandMessageTool, thread_id=self.thread_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(MessageTool)
|
||||
|
||||
self.thread_manager.add_tool(SandboxShellTool, project_id=self.project_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(SandboxFilesTool, project_id=self.project_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(SandboxBrowserTool, project_id=self.project_id, thread_id=self.thread_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(SandboxDeployTool, project_id=self.project_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(SandboxExposeTool, project_id=self.project_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(ExpandMessageTool, thread_id=self.thread_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(MessageTool)
|
||||
self.thread_manager.add_tool(SandboxWebSearchTool, project_id=self.project_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(SandboxVisionTool, project_id=self.project_id, thread_id=self.thread_id, thread_manager=self.thread_manager)
|
||||
self.thread_manager.add_tool(SandboxImageEditTool, project_id=self.project_id, thread_id=self.thread_id, thread_manager=self.thread_manager)
|
||||
|
||||
if config.RAPID_API_KEY:
|
||||
self.thread_manager.add_tool(DataProvidersTool)
|
||||
|
||||
|
@ -203,10 +205,10 @@ class PromptManager:
|
|||
sample_response = file.read()
|
||||
default_system_content = default_system_content + "\n\n <sample_assistant_response>" + sample_response + "</sample_assistant_response>"
|
||||
|
||||
if agent_config and agent_config.get('system_prompt'):
|
||||
system_content = agent_config['system_prompt'].strip()
|
||||
elif is_agent_builder:
|
||||
if is_agent_builder:
|
||||
system_content = get_agent_builder_prompt()
|
||||
elif agent_config and agent_config.get('system_prompt'):
|
||||
system_content = agent_config['system_prompt'].strip()
|
||||
else:
|
||||
system_content = default_system_content
|
||||
|
||||
|
|
|
@ -322,6 +322,7 @@ class CredentialProfileTool(AgentBuilderBaseTool):
|
|||
try:
|
||||
from uuid import UUID
|
||||
account_id = await self._get_current_account_id()
|
||||
client = await self.db.client
|
||||
|
||||
profile = await profile_service.get_profile(UUID(account_id), UUID(profile_id))
|
||||
if not profile:
|
||||
|
@ -329,26 +330,69 @@ class CredentialProfileTool(AgentBuilderBaseTool):
|
|||
if not profile.is_connected:
|
||||
return self.fail_response("Profile is not connected yet. Please connect the profile first.")
|
||||
|
||||
# Simplified implementation - agent profile tools are now handled elsewhere
|
||||
result = {
|
||||
'success': True,
|
||||
'enabled_tools': enabled_tools,
|
||||
'total_tools': len(enabled_tools),
|
||||
'version_name': 'simplified'
|
||||
}
|
||||
if not result.get("success", False):
|
||||
return self.fail_response("Failed to update agent profile tools")
|
||||
agent_result = await client.table('agents').select('current_version_id').eq('agent_id', self.agent_id).execute()
|
||||
if not agent_result.data or not agent_result.data[0].get('current_version_id'):
|
||||
return self.fail_response("Agent configuration not found")
|
||||
|
||||
version_msg = f"Profile '{profile.profile_name.value if hasattr(profile.profile_name, 'value') else str(profile.profile_name)}' updated with {len(enabled_tools)} tools"
|
||||
version_result = await client.table('agent_versions')\
|
||||
.select('config')\
|
||||
.eq('version_id', agent_result.data[0]['current_version_id'])\
|
||||
.maybe_single()\
|
||||
.execute()
|
||||
|
||||
if not version_result.data or not version_result.data.get('config'):
|
||||
return self.fail_response("Agent version configuration not found")
|
||||
|
||||
current_config = version_result.data['config']
|
||||
current_tools = current_config.get('tools', {})
|
||||
current_custom_mcps = current_tools.get('custom_mcp', [])
|
||||
|
||||
app_slug = profile.app_slug.value if hasattr(profile.app_slug, 'value') else str(profile.app_slug)
|
||||
|
||||
new_mcp_config = {
|
||||
'name': display_name or profile.display_name,
|
||||
'type': 'pipedream',
|
||||
'config': {
|
||||
'url': 'https://remote.mcp.pipedream.net',
|
||||
'headers': {
|
||||
'x-pd-app-slug': app_slug
|
||||
},
|
||||
'profile_id': profile_id
|
||||
},
|
||||
'enabledTools': enabled_tools
|
||||
}
|
||||
|
||||
updated_mcps = [mcp for mcp in current_custom_mcps
|
||||
if mcp.get('config', {}).get('profile_id') != profile_id]
|
||||
|
||||
updated_mcps.append(new_mcp_config)
|
||||
|
||||
current_tools['custom_mcp'] = updated_mcps
|
||||
current_config['tools'] = current_tools
|
||||
|
||||
from agent.versioning.version_service import get_version_service
|
||||
version_service = await get_version_service()
|
||||
new_version = await version_service.create_version(
|
||||
agent_id=self.agent_id,
|
||||
user_id=account_id,
|
||||
system_prompt=current_config.get('system_prompt', ''),
|
||||
configured_mcps=current_config.get('tools', {}).get('mcp', []),
|
||||
custom_mcps=updated_mcps,
|
||||
agentpress_tools=current_config.get('tools', {}).get('agentpress', {}),
|
||||
change_description=f"Configured {display_name or profile.display_name} with {len(enabled_tools)} tools"
|
||||
)
|
||||
|
||||
profile_name = profile.profile_name.value if hasattr(profile.profile_name, 'value') else str(profile.profile_name)
|
||||
return self.success_response({
|
||||
"message": version_msg,
|
||||
"enabled_tools": result.get("enabled_tools", []),
|
||||
"total_tools": result.get("total_tools", 0),
|
||||
"version_id": result.get("version_id"),
|
||||
"version_name": result.get("version_name")
|
||||
"message": f"Profile '{profile_name}' updated with {len(enabled_tools)} tools",
|
||||
"enabled_tools": enabled_tools,
|
||||
"total_tools": len(enabled_tools),
|
||||
"version_id": new_version.version_id,
|
||||
"version_name": new_version.version_name
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error configuring profile for agent: {e}", exc_info=True)
|
||||
return self.fail_response(f"Error configuring profile for agent: {str(e)}")
|
||||
|
||||
@openapi_schema({
|
||||
|
@ -409,12 +453,11 @@ class CredentialProfileTool(AgentBuilderBaseTool):
|
|||
version_service = await get_version_service()
|
||||
await version_service.create_version(
|
||||
agent_id=self.agent_id,
|
||||
user_id=self.account_id,
|
||||
user_id=account_id,
|
||||
system_prompt=current_config.get('system_prompt', ''),
|
||||
configured_mcps=current_config.get('tools', {}).get('mcp', []),
|
||||
custom_mcps=updated_mcps,
|
||||
agentpress_tools=current_config.get('tools', {}).get('agentpress', {}),
|
||||
version_name="Removed credential profile",
|
||||
change_description=f"Deleted credential profile {profile.display_name}"
|
||||
)
|
||||
except Exception as e:
|
||||
|
|
|
@ -120,7 +120,7 @@ export function ConfigurationTab({
|
|||
<div className="bg-muted rounded-xl h-10 w-10 flex items-center justify-center transition-all duration-300 group-hover:scale-105">
|
||||
<Settings className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-primary rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
|
||||
</div>
|
||||
<div className="text-left flex-1">
|
||||
<h4 className="font-semibold text-base text-foreground mb-1 group-hover:text-primary transition-colors duration-300">System Prompt</h4>
|
||||
|
@ -163,7 +163,7 @@ export function ConfigurationTab({
|
|||
<div className="bg-muted rounded-xl h-10 w-10 flex items-center justify-center transition-all duration-300 group-hover:scale-105">
|
||||
<Wrench className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-primary rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
|
||||
</div>
|
||||
<div className="text-left flex-1">
|
||||
<h4 className="font-semibold text-base text-foreground mb-1 group-hover:text-primary transition-colors duration-300">Default Tools</h4>
|
||||
|
@ -202,7 +202,7 @@ export function ConfigurationTab({
|
|||
<div className="bg-muted rounded-xl h-10 w-10 flex items-center justify-center transition-all duration-300 group-hover:scale-105">
|
||||
<Server className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-primary rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
|
||||
</div>
|
||||
<div className="text-left flex-1">
|
||||
<h4 className="font-semibold text-base text-foreground mb-1 group-hover:text-primary transition-colors duration-300">Integrations</h4>
|
||||
|
@ -248,7 +248,7 @@ export function ConfigurationTab({
|
|||
<div className="bg-muted rounded-xl h-10 w-10 flex items-center justify-center transition-all duration-300 group-hover:scale-105">
|
||||
<BookOpen className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-primary rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
|
||||
</div>
|
||||
<div className="text-left flex-1">
|
||||
<h4 className="font-semibold text-base text-foreground mb-1 group-hover:text-primary transition-colors duration-300">Knowledge Base</h4>
|
||||
|
@ -284,7 +284,7 @@ export function ConfigurationTab({
|
|||
<div className="bg-muted rounded-xl h-10 w-10 flex items-center justify-center transition-all duration-300 group-hover:scale-105">
|
||||
<Workflow className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-primary rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
|
||||
</div>
|
||||
<div className="text-left flex-1">
|
||||
<h4 className="font-semibold text-base text-foreground mb-1 group-hover:text-primary transition-colors duration-300">Workflows</h4>
|
||||
|
@ -320,7 +320,7 @@ export function ConfigurationTab({
|
|||
<div className="bg-muted rounded-xl h-10 w-10 flex items-center justify-center transition-all duration-300 group-hover:scale-105">
|
||||
<Zap className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-primary rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
|
||||
</div>
|
||||
<div className="text-left flex-1">
|
||||
<h4 className="font-semibold text-base text-foreground mb-1 group-hover:text-primary transition-colors duration-300">Triggers</h4>
|
||||
|
|
|
@ -311,6 +311,7 @@ export interface ThreadContentProps {
|
|||
agentName?: string;
|
||||
agentAvatar?: React.ReactNode;
|
||||
emptyStateComponent?: React.ReactNode; // Add custom empty state component prop
|
||||
threadMetadata?: any; // Add thread metadata prop
|
||||
}
|
||||
|
||||
export const ThreadContent: React.FC<ThreadContentProps> = ({
|
||||
|
@ -333,6 +334,7 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
|||
agentName = 'Suna',
|
||||
agentAvatar = <KortixLogo size={16} />,
|
||||
emptyStateComponent,
|
||||
threadMetadata,
|
||||
}) => {
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
@ -351,6 +353,66 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
|||
// In playback mode, we use visibleMessages instead of messages
|
||||
const displayMessages = readOnly && visibleMessages ? visibleMessages : messages;
|
||||
|
||||
// Helper function to get agent info robustly
|
||||
const getAgentInfo = useCallback(() => {
|
||||
// First check thread metadata for is_agent_builder flag
|
||||
if (threadMetadata?.is_agent_builder) {
|
||||
return {
|
||||
name: 'Agent Builder',
|
||||
avatar: (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded text-xs">
|
||||
<span className="text-lg">🤖</span>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// Then check recent messages for agent info
|
||||
const recentAssistantWithAgent = [...displayMessages].reverse().find(msg =>
|
||||
msg.type === 'assistant' && (msg.agents?.avatar || msg.agents?.avatar_color || msg.agents?.name)
|
||||
);
|
||||
|
||||
if (recentAssistantWithAgent?.agents?.name === 'Agent Builder') {
|
||||
return {
|
||||
name: 'Agent Builder',
|
||||
avatar: (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded text-xs">
|
||||
<span className="text-lg">🤖</span>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
if (recentAssistantWithAgent?.agents?.name) {
|
||||
const isSunaAgent = recentAssistantWithAgent.agents.name === 'Suna';
|
||||
const avatar = recentAssistantWithAgent.agents.avatar ? (
|
||||
<>
|
||||
{isSunaAgent ? (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded text-xs">
|
||||
<KortixLogo size={16} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded text-xs">
|
||||
<span className="text-lg">{recentAssistantWithAgent.agents.avatar}</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded text-xs">
|
||||
<KortixLogo size={16} />
|
||||
</div>
|
||||
);
|
||||
return {
|
||||
name: recentAssistantWithAgent.agents.name,
|
||||
avatar
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: agentName || 'Suna',
|
||||
avatar: agentAvatar
|
||||
};
|
||||
}, [threadMetadata, displayMessages, agentName, agentAvatar]);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!messagesContainerRef.current) return;
|
||||
const { scrollTop, scrollHeight, clientHeight } = messagesContainerRef.current;
|
||||
|
@ -624,44 +686,10 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
|||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center">
|
||||
<div className="rounded-md flex items-center justify-center relative">
|
||||
{(() => {
|
||||
const firstAssistantWithAgent = group.messages.find(msg =>
|
||||
msg.type === 'assistant' && (msg.agents?.avatar || msg.agents?.avatar_color)
|
||||
);
|
||||
|
||||
const isSunaAgent = firstAssistantWithAgent?.agents?.name === 'Suna';
|
||||
|
||||
if (firstAssistantWithAgent?.agents?.avatar) {
|
||||
const avatar = firstAssistantWithAgent.agents.avatar;
|
||||
return (
|
||||
<>
|
||||
{isSunaAgent ? (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded text-xs">
|
||||
<KortixLogo size={16} />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="h-5 w-5 flex items-center justify-center rounded text-xs"
|
||||
>
|
||||
<span className="text-lg">{avatar}</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <KortixLogo size={16} />;
|
||||
})()}
|
||||
{getAgentInfo().avatar}
|
||||
</div>
|
||||
<p className='ml-2 text-sm text-muted-foreground'>
|
||||
{(() => {
|
||||
const firstAssistantWithAgent = group.messages.find(msg =>
|
||||
msg.type === 'assistant' && msg.agents?.name
|
||||
);
|
||||
if (firstAssistantWithAgent?.agents?.name) {
|
||||
return firstAssistantWithAgent.agents.name;
|
||||
}
|
||||
return 'Suna';
|
||||
})()}
|
||||
{getAgentInfo().name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -887,9 +915,11 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
|||
{/* Logo positioned above the loader */}
|
||||
<div className="flex items-center">
|
||||
<div className="rounded-md flex items-center justify-center">
|
||||
{agentAvatar}
|
||||
{getAgentInfo().avatar}
|
||||
</div>
|
||||
<p className='ml-2 text-sm text-muted-foreground'>{agentName || 'Suna'}</p>
|
||||
<p className='ml-2 text-sm text-muted-foreground'>
|
||||
{getAgentInfo().name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Loader content */}
|
||||
|
@ -899,17 +929,17 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* For playback mode - Show tool call animation if active */}
|
||||
{readOnly && currentToolCall && (
|
||||
<div ref={latestMessageRef}>
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Logo positioned above the tool call */}
|
||||
<div className="flex justify-start">
|
||||
<div className="rounded-md flex items-center justify-center">
|
||||
{agentAvatar}
|
||||
{getAgentInfo().avatar}
|
||||
</div>
|
||||
<p className='ml-2 text-sm text-muted-foreground'>{agentName || 'Suna'}</p>
|
||||
<p className='ml-2 text-sm text-muted-foreground'>
|
||||
{getAgentInfo().name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Tool call content */}
|
||||
|
@ -932,9 +962,11 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
|||
{/* Logo positioned above the streaming indicator */}
|
||||
<div className="flex justify-start">
|
||||
<div className="rounded-md flex items-center justify-center">
|
||||
{agentAvatar}
|
||||
{getAgentInfo().avatar}
|
||||
</div>
|
||||
<p className='ml-2 text-sm text-muted-foreground'>{agentName || 'Suna'}</p>
|
||||
<p className='ml-2 text-sm text-muted-foreground'>
|
||||
{getAgentInfo().name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Streaming indicator content */}
|
||||
|
|
Loading…
Reference in New Issue