mirror of https://github.com/kortix-ai/suna.git
rem legacy workflows, db unused schema cleanup wip
This commit is contained in:
parent
5157765f52
commit
ef087747ab
|
@ -291,19 +291,17 @@ async def start_agent(
|
|||
client = await db.client
|
||||
|
||||
await verify_thread_access(client, thread_id, user_id)
|
||||
thread_result = await client.table('threads').select('project_id', 'account_id', 'agent_id', 'metadata').eq('thread_id', thread_id).execute()
|
||||
thread_result = await client.table('threads').select('project_id', 'account_id', 'metadata').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]
|
||||
project_id = thread_data.get('project_id')
|
||||
account_id = thread_data.get('account_id')
|
||||
thread_agent_id = thread_data.get('agent_id')
|
||||
thread_metadata = thread_data.get('metadata', {})
|
||||
|
||||
structlog.contextvars.bind_contextvars(
|
||||
project_id=project_id,
|
||||
account_id=account_id,
|
||||
thread_agent_id=thread_agent_id,
|
||||
thread_metadata=thread_metadata,
|
||||
)
|
||||
|
||||
|
@ -316,11 +314,10 @@ async def start_agent(
|
|||
|
||||
# Load agent configuration with version support
|
||||
agent_config = None
|
||||
effective_agent_id = body.agent_id or thread_agent_id # Use provided agent_id or the one stored in thread
|
||||
effective_agent_id = body.agent_id # Optional agent ID from request
|
||||
|
||||
logger.info(f"[AGENT LOAD] Agent loading flow:")
|
||||
logger.info(f" - body.agent_id: {body.agent_id}")
|
||||
logger.info(f" - thread_agent_id: {thread_agent_id}")
|
||||
logger.info(f" - effective_agent_id: {effective_agent_id}")
|
||||
|
||||
if effective_agent_id:
|
||||
|
@ -359,7 +356,7 @@ async def start_agent(
|
|||
logger.info(f"Using agent {agent_config['name']} ({effective_agent_id}) version {agent_config.get('version_name', 'v1')}")
|
||||
else:
|
||||
logger.info(f"Using agent {agent_config['name']} ({effective_agent_id}) - no version data")
|
||||
source = "request" if body.agent_id else "thread"
|
||||
source = "request" if body.agent_id else "fallback"
|
||||
else:
|
||||
logger.info(f"[AGENT LOAD] No effective_agent_id, will try default agent")
|
||||
|
||||
|
@ -399,8 +396,6 @@ async def start_agent(
|
|||
logger.info(f"[AGENT LOAD] Final agent_config: {agent_config is not None}")
|
||||
if agent_config:
|
||||
logger.info(f"[AGENT LOAD] Agent config keys: {list(agent_config.keys())}")
|
||||
|
||||
if body.agent_id and body.agent_id != thread_agent_id and agent_config:
|
||||
logger.info(f"Using agent {agent_config['agent_id']} for this agent run (thread remains agent-agnostic)")
|
||||
|
||||
can_use, model_message, allowed_models = await can_use_model(client, account_id, model_name)
|
||||
|
@ -516,8 +511,8 @@ async def get_agent_run(agent_run_id: str, user_id: str = Depends(get_current_us
|
|||
|
||||
@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. Since threads are now agent-agnostic,
|
||||
this returns the most recently used agent or the default agent."""
|
||||
"""Get the agent details for a specific thread. Since threads are fully agent-agnostic,
|
||||
this returns the most recently used agent from agent_runs only."""
|
||||
structlog.contextvars.bind_contextvars(
|
||||
thread_id=thread_id,
|
||||
)
|
||||
|
@ -525,21 +520,20 @@ async def get_thread_agent(thread_id: str, user_id: str = Depends(get_current_us
|
|||
client = await db.client
|
||||
|
||||
try:
|
||||
# Verify thread access and get thread data including agent_id
|
||||
# Verify thread access and get thread data
|
||||
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()
|
||||
thread_result = await client.table('threads').select('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')
|
||||
|
||||
effective_agent_id = None
|
||||
agent_source = "none"
|
||||
|
||||
# First, try to get the most recently used agent from agent_runs
|
||||
# Get the most recently used agent from agent_runs
|
||||
recent_agent_result = await client.table('agent_runs').select('agent_id', 'agent_version_id').eq('thread_id', thread_id).not_.is_('agent_id', 'null').order('created_at', desc=True).limit(1).execute()
|
||||
if recent_agent_result.data:
|
||||
effective_agent_id = recent_agent_result.data[0]['agent_id']
|
||||
|
@ -547,26 +541,12 @@ async def get_thread_agent(thread_id: str, user_id: str = Depends(get_current_us
|
|||
agent_source = "recent"
|
||||
logger.info(f"Found most recently used agent: {effective_agent_id} (version: {recent_version_id})")
|
||||
|
||||
# If no recent agent, fall back to thread default agent
|
||||
elif thread_agent_id:
|
||||
effective_agent_id = thread_agent_id
|
||||
agent_source = "thread"
|
||||
logger.info(f"Using thread default agent: {effective_agent_id}")
|
||||
|
||||
# If no thread agent, try to get the default agent for the account
|
||||
else:
|
||||
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"
|
||||
logger.info(f"Using account default agent: {effective_agent_id}")
|
||||
|
||||
# If still no agent found
|
||||
# If no agent found in agent_runs
|
||||
if not effective_agent_id:
|
||||
return {
|
||||
"agent": None,
|
||||
"source": "none",
|
||||
"message": "No agent configured for this thread. Threads are agent-agnostic - you can select any agent."
|
||||
"message": "No agent has been used in this thread yet. Threads are agent-agnostic - use /agent/start to select an agent."
|
||||
}
|
||||
|
||||
# Fetch the agent details
|
||||
|
|
|
@ -126,60 +126,6 @@ async def get_thread_knowledge_base(
|
|||
logger.error(f"Error getting knowledge base for thread {thread_id}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Failed to retrieve knowledge base")
|
||||
|
||||
@router.post("/threads/{thread_id}", response_model=KnowledgeBaseEntryResponse)
|
||||
async def create_knowledge_base_entry(
|
||||
thread_id: str,
|
||||
entry_data: CreateKnowledgeBaseEntryRequest,
|
||||
user_id: str = Depends(get_current_user_id_from_jwt)
|
||||
):
|
||||
if not await is_enabled("knowledge_base"):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="This feature is not available at the moment."
|
||||
)
|
||||
|
||||
"""Create a new knowledge base entry for a thread"""
|
||||
try:
|
||||
client = await db.client
|
||||
thread_result = await client.table('threads').select('account_id').eq('thread_id', thread_id).execute()
|
||||
if not thread_result.data:
|
||||
raise HTTPException(status_code=404, detail="Thread not found")
|
||||
|
||||
account_id = thread_result.data[0]['account_id']
|
||||
|
||||
insert_data = {
|
||||
'thread_id': thread_id,
|
||||
'account_id': account_id,
|
||||
'name': entry_data.name,
|
||||
'description': entry_data.description,
|
||||
'content': entry_data.content,
|
||||
'usage_context': entry_data.usage_context
|
||||
}
|
||||
|
||||
result = await client.table('knowledge_base_entries').insert(insert_data).execute()
|
||||
|
||||
if not result.data:
|
||||
raise HTTPException(status_code=500, detail="Failed to create knowledge base entry")
|
||||
|
||||
created_entry = result.data[0]
|
||||
|
||||
return KnowledgeBaseEntryResponse(
|
||||
entry_id=created_entry['entry_id'],
|
||||
name=created_entry['name'],
|
||||
description=created_entry['description'],
|
||||
content=created_entry['content'],
|
||||
usage_context=created_entry['usage_context'],
|
||||
is_active=created_entry['is_active'],
|
||||
content_tokens=created_entry.get('content_tokens'),
|
||||
created_at=created_entry['created_at'],
|
||||
updated_at=created_entry['updated_at']
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating knowledge base entry for thread {thread_id}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Failed to create knowledge base entry")
|
||||
|
||||
@router.get("/agents/{agent_id}", response_model=KnowledgeBaseListResponse)
|
||||
async def get_agent_knowledge_base(
|
||||
|
@ -495,155 +441,6 @@ async def get_agent_knowledge_base_context(
|
|||
logger.error(f"Error getting knowledge base context for agent {agent_id}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Failed to retrieve agent knowledge base context")
|
||||
|
||||
@router.put("/{entry_id}", response_model=KnowledgeBaseEntryResponse)
|
||||
async def update_knowledge_base_entry(
|
||||
entry_id: str,
|
||||
entry_data: UpdateKnowledgeBaseEntryRequest,
|
||||
user_id: str = Depends(get_current_user_id_from_jwt)
|
||||
):
|
||||
if not await is_enabled("knowledge_base"):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="This feature is not available at the moment."
|
||||
)
|
||||
|
||||
"""Update a knowledge base entry (works for both thread and agent entries)"""
|
||||
try:
|
||||
client = await db.client
|
||||
entry_result = await client.table('knowledge_base_entries').select('*').eq('entry_id', entry_id).execute()
|
||||
table_name = 'knowledge_base_entries'
|
||||
|
||||
if not entry_result.data:
|
||||
entry_result = await client.table('agent_knowledge_base_entries').select('*').eq('entry_id', entry_id).execute()
|
||||
table_name = 'agent_knowledge_base_entries'
|
||||
|
||||
if not entry_result.data:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base entry not found")
|
||||
|
||||
update_data = {}
|
||||
if entry_data.name is not None:
|
||||
update_data['name'] = entry_data.name
|
||||
if entry_data.description is not None:
|
||||
update_data['description'] = entry_data.description
|
||||
if entry_data.content is not None:
|
||||
update_data['content'] = entry_data.content
|
||||
if entry_data.usage_context is not None:
|
||||
update_data['usage_context'] = entry_data.usage_context
|
||||
if entry_data.is_active is not None:
|
||||
update_data['is_active'] = entry_data.is_active
|
||||
|
||||
if not update_data:
|
||||
raise HTTPException(status_code=400, detail="No fields to update")
|
||||
|
||||
result = await client.table(table_name).update(update_data).eq('entry_id', entry_id).execute()
|
||||
|
||||
if not result.data:
|
||||
raise HTTPException(status_code=500, detail="Failed to update knowledge base entry")
|
||||
|
||||
updated_entry = result.data[0]
|
||||
|
||||
return KnowledgeBaseEntryResponse(
|
||||
entry_id=updated_entry['entry_id'],
|
||||
name=updated_entry['name'],
|
||||
description=updated_entry['description'],
|
||||
content=updated_entry['content'],
|
||||
usage_context=updated_entry['usage_context'],
|
||||
is_active=updated_entry['is_active'],
|
||||
content_tokens=updated_entry.get('content_tokens'),
|
||||
created_at=updated_entry['created_at'],
|
||||
updated_at=updated_entry['updated_at'],
|
||||
source_type=updated_entry.get('source_type'),
|
||||
source_metadata=updated_entry.get('source_metadata'),
|
||||
file_size=updated_entry.get('file_size'),
|
||||
file_mime_type=updated_entry.get('file_mime_type')
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating knowledge base entry {entry_id}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Failed to update knowledge base entry")
|
||||
|
||||
@router.delete("/{entry_id}")
|
||||
async def delete_knowledge_base_entry(
|
||||
entry_id: str,
|
||||
user_id: str = Depends(get_current_user_id_from_jwt)
|
||||
):
|
||||
if not await is_enabled("knowledge_base"):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="This feature is not available at the moment."
|
||||
)
|
||||
|
||||
"""Delete a knowledge base entry (works for both thread and agent entries)"""
|
||||
try:
|
||||
client = await db.client
|
||||
|
||||
entry_result = await client.table('knowledge_base_entries').select('entry_id').eq('entry_id', entry_id).execute()
|
||||
table_name = 'knowledge_base_entries'
|
||||
|
||||
if not entry_result.data:
|
||||
entry_result = await client.table('agent_knowledge_base_entries').select('entry_id').eq('entry_id', entry_id).execute()
|
||||
table_name = 'agent_knowledge_base_entries'
|
||||
|
||||
if not entry_result.data:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base entry not found")
|
||||
|
||||
result = await client.table(table_name).delete().eq('entry_id', entry_id).execute()
|
||||
|
||||
return {"message": "Knowledge base entry deleted successfully"}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting knowledge base entry {entry_id}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Failed to delete knowledge base entry")
|
||||
|
||||
@router.get("/{entry_id}", response_model=KnowledgeBaseEntryResponse)
|
||||
async def get_knowledge_base_entry(
|
||||
entry_id: str,
|
||||
user_id: str = Depends(get_current_user_id_from_jwt)
|
||||
):
|
||||
if not await is_enabled("knowledge_base"):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="This feature is not available at the moment."
|
||||
)
|
||||
"""Get a specific knowledge base entry (works for both thread and agent entries)"""
|
||||
try:
|
||||
client = await db.client
|
||||
|
||||
result = await client.table('knowledge_base_entries').select('*').eq('entry_id', entry_id).execute()
|
||||
|
||||
if not result.data:
|
||||
result = await client.table('agent_knowledge_base_entries').select('*').eq('entry_id', entry_id).execute()
|
||||
|
||||
if not result.data:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base entry not found")
|
||||
|
||||
entry = result.data[0]
|
||||
|
||||
return KnowledgeBaseEntryResponse(
|
||||
entry_id=entry['entry_id'],
|
||||
name=entry['name'],
|
||||
description=entry['description'],
|
||||
content=entry['content'],
|
||||
usage_context=entry['usage_context'],
|
||||
is_active=entry['is_active'],
|
||||
content_tokens=entry.get('content_tokens'),
|
||||
created_at=entry['created_at'],
|
||||
updated_at=entry['updated_at'],
|
||||
source_type=entry.get('source_type'),
|
||||
source_metadata=entry.get('source_metadata'),
|
||||
file_size=entry.get('file_size'),
|
||||
file_mime_type=entry.get('file_mime_type')
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting knowledge base entry {entry_id}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Failed to retrieve knowledge base entry")
|
||||
|
||||
@router.get("/threads/{thread_id}/context")
|
||||
async def get_knowledge_base_context(
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
-- Migration: Remove recordings, devices tables and responses field from agent_runs
|
||||
-- This migration cleans up unused tables and fields
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Drop recordings table first (has foreign key to devices)
|
||||
DROP TABLE IF EXISTS public.recordings CASCADE;
|
||||
|
||||
-- Drop devices table
|
||||
DROP TABLE IF EXISTS public.devices CASCADE;
|
||||
|
||||
-- Remove responses column from agent_runs table
|
||||
ALTER TABLE agent_runs DROP COLUMN IF EXISTS responses;
|
||||
|
||||
COMMIT;
|
|
@ -0,0 +1,30 @@
|
|||
BEGIN;
|
||||
|
||||
-- Remove old workflow execution and step tables
|
||||
-- These are no longer needed since steps are now stored as JSON in agent_workflows.steps
|
||||
-- and executions can be tracked differently if needed
|
||||
|
||||
-- Drop workflow step executions first (has foreign keys to other tables)
|
||||
DROP TABLE IF EXISTS workflow_step_executions CASCADE;
|
||||
|
||||
-- Drop workflow executions
|
||||
DROP TABLE IF EXISTS workflow_executions CASCADE;
|
||||
|
||||
-- Drop workflow steps
|
||||
DROP TABLE IF EXISTS workflow_steps CASCADE;
|
||||
|
||||
-- Drop the related enum types that are no longer needed
|
||||
DROP TYPE IF EXISTS workflow_step_type CASCADE;
|
||||
DROP TYPE IF EXISTS workflow_execution_status CASCADE;
|
||||
|
||||
-- Clean up any related indexes that might still exist
|
||||
DROP INDEX IF EXISTS idx_workflow_steps_workflow_id CASCADE;
|
||||
DROP INDEX IF EXISTS idx_workflow_steps_order CASCADE;
|
||||
DROP INDEX IF EXISTS idx_workflow_executions_workflow_id CASCADE;
|
||||
DROP INDEX IF EXISTS idx_workflow_executions_agent_id CASCADE;
|
||||
DROP INDEX IF EXISTS idx_workflow_executions_status CASCADE;
|
||||
DROP INDEX IF EXISTS idx_workflow_executions_started_at CASCADE;
|
||||
DROP INDEX IF EXISTS idx_workflow_step_executions_execution_id CASCADE;
|
||||
DROP INDEX IF EXISTS idx_workflow_step_executions_step_id CASCADE;
|
||||
|
||||
COMMIT;
|
|
@ -0,0 +1,6 @@
|
|||
-- Remove unused tables from schema cleanup
|
||||
-- Drop dependent tables first, then main tables with CASCADE to handle any remaining dependencies
|
||||
DROP TABLE IF EXISTS knowledge_base_usage_log CASCADE;
|
||||
DROP TABLE IF EXISTS knowledge_base_entries CASCADE;
|
||||
DROP TABLE IF EXISTS trigger_events CASCADE;
|
||||
DROP TABLE IF EXISTS custom_trigger_providers CASCADE;
|
|
@ -0,0 +1,19 @@
|
|||
-- Migration: Remove agent_id and agent_version_id from threads table
|
||||
-- This makes threads truly agent-agnostic - agent selection is handled only through agent/initiate and agent/start endpoints
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Drop indexes first
|
||||
DROP INDEX IF EXISTS idx_threads_agent_id;
|
||||
DROP INDEX IF EXISTS idx_threads_agent_version;
|
||||
|
||||
-- Remove agent_id column from threads table
|
||||
ALTER TABLE threads DROP COLUMN IF EXISTS agent_id;
|
||||
|
||||
-- Remove agent_version_id column from threads table
|
||||
ALTER TABLE threads DROP COLUMN IF EXISTS agent_version_id;
|
||||
|
||||
-- Update comment to reflect that threads are now completely agent-agnostic
|
||||
COMMENT ON TABLE threads IS 'Conversation threads - completely agent-agnostic. Agent selection handled via /agent/initiate and /agent/start endpoints.';
|
||||
|
||||
COMMIT;
|
|
@ -68,20 +68,7 @@ class WorkflowResponse(BaseModel):
|
|||
created_at: str
|
||||
updated_at: str
|
||||
|
||||
class WorkflowExecutionResponse(BaseModel):
|
||||
id: str
|
||||
workflow_id: str
|
||||
agent_id: str
|
||||
thread_id: Optional[str]
|
||||
status: str
|
||||
started_at: str
|
||||
completed_at: Optional[str]
|
||||
duration_seconds: Optional[float]
|
||||
triggered_by: str
|
||||
input_data: Optional[Dict[str, Any]]
|
||||
output_data: Optional[Dict[str, Any]]
|
||||
error_message: Optional[str]
|
||||
created_at: str
|
||||
|
||||
|
||||
class WorkflowExecuteRequest(BaseModel):
|
||||
input_data: Optional[Dict[str, Any]] = None
|
||||
|
@ -159,20 +146,6 @@ async def get_agent_workflows(
|
|||
steps = []
|
||||
if workflow_data.get('steps'):
|
||||
steps = convert_json_to_steps(workflow_data['steps'])
|
||||
else:
|
||||
workflow_steps_result = await client.table('workflow_steps').select('*').eq('workflow_id', workflow_data['id']).order('step_order').execute()
|
||||
for step_data in workflow_steps_result.data:
|
||||
steps.append(WorkflowStepResponse(
|
||||
id=step_data['id'],
|
||||
name=step_data['name'],
|
||||
description=step_data.get('description'),
|
||||
type=step_data['type'],
|
||||
config=step_data.get('config', {}),
|
||||
conditions=step_data.get('conditions'),
|
||||
order=step_data['step_order'],
|
||||
created_at=step_data['created_at'],
|
||||
updated_at=step_data['updated_at']
|
||||
))
|
||||
|
||||
workflows.append(WorkflowResponse(
|
||||
id=workflow_data['id'],
|
||||
|
@ -264,8 +237,6 @@ async def update_agent_workflow(
|
|||
if workflow_data.steps is not None:
|
||||
steps_json = convert_steps_to_json(workflow_data.steps)
|
||||
update_data['steps'] = steps_json
|
||||
|
||||
await client.table('workflow_steps').delete().eq('workflow_id', workflow_id).execute()
|
||||
|
||||
if update_data:
|
||||
await client.table('agent_workflows').update(update_data).eq('id', workflow_id).execute()
|
||||
|
@ -276,20 +247,6 @@ async def update_agent_workflow(
|
|||
steps = []
|
||||
if workflow_data.get('steps'):
|
||||
steps = convert_json_to_steps(workflow_data['steps'])
|
||||
else:
|
||||
workflow_steps_result = await client.table('workflow_steps').select('*').eq('workflow_id', workflow_id).order('step_order').execute()
|
||||
for step_data in workflow_steps_result.data:
|
||||
steps.append(WorkflowStepResponse(
|
||||
id=step_data['id'],
|
||||
name=step_data['name'],
|
||||
description=step_data.get('description'),
|
||||
type=step_data['type'],
|
||||
config=step_data.get('config', {}),
|
||||
conditions=step_data.get('conditions'),
|
||||
order=step_data['step_order'],
|
||||
created_at=step_data['created_at'],
|
||||
updated_at=step_data['updated_at']
|
||||
))
|
||||
|
||||
return WorkflowResponse(
|
||||
id=workflow_data['id'],
|
||||
|
@ -409,37 +366,7 @@ async def execute_agent_workflow(
|
|||
}
|
||||
)
|
||||
|
||||
@router.get("/agents/{agent_id}/workflows/{workflow_id}/executions")
|
||||
async def get_workflow_executions(
|
||||
agent_id: str,
|
||||
workflow_id: str,
|
||||
user_id: str = Depends(get_current_user_id_from_jwt),
|
||||
limit: int = Query(20, ge=1, le=100)
|
||||
):
|
||||
db = await get_db_connection()
|
||||
client = await db.client
|
||||
|
||||
executions_result = await client.table('workflow_executions').select('*').eq('workflow_id', workflow_id).order('created_at', desc=True).limit(limit).execute()
|
||||
|
||||
executions = []
|
||||
for execution_data in executions_result.data:
|
||||
executions.append(WorkflowExecutionResponse(
|
||||
id=execution_data['id'],
|
||||
workflow_id=execution_data['workflow_id'],
|
||||
agent_id=execution_data['agent_id'],
|
||||
thread_id=execution_data.get('thread_id'),
|
||||
status=execution_data['status'],
|
||||
started_at=execution_data['started_at'],
|
||||
completed_at=execution_data.get('completed_at'),
|
||||
duration_seconds=execution_data.get('duration_seconds'),
|
||||
triggered_by=execution_data['triggered_by'],
|
||||
input_data=execution_data.get('input_data'),
|
||||
output_data=execution_data.get('output_data'),
|
||||
error_message=execution_data.get('error_message'),
|
||||
created_at=execution_data['created_at']
|
||||
))
|
||||
|
||||
return executions
|
||||
|
||||
|
||||
@router.post("/agents/{agent_id}/workflows/{workflow_id}/webhook")
|
||||
async def trigger_workflow_webhook(
|
||||
|
|
|
@ -400,9 +400,6 @@ class WorkflowExecutor:
|
|||
await self._validate_workflow_execution(account_id)
|
||||
|
||||
await self._session_manager.start_sandbox(project_id)
|
||||
execution_id = await self._create_workflow_execution_record(
|
||||
workflow_id, agent_id, thread_id, workflow_input, trigger_result
|
||||
)
|
||||
|
||||
await self._create_workflow_message(thread_id, workflow_config, workflow_input)
|
||||
agent_run_id = await self._start_workflow_agent_execution(
|
||||
|
@ -411,7 +408,6 @@ class WorkflowExecutor:
|
|||
|
||||
return {
|
||||
"success": True,
|
||||
"execution_id": execution_id,
|
||||
"thread_id": thread_id,
|
||||
"agent_run_id": agent_run_id,
|
||||
"message": "Workflow execution started successfully"
|
||||
|
@ -439,22 +435,11 @@ class WorkflowExecutor:
|
|||
if workflow_config.get('steps'):
|
||||
steps_json = workflow_config['steps']
|
||||
else:
|
||||
steps_json = await self._get_legacy_workflow_steps(workflow_id)
|
||||
steps_json = []
|
||||
|
||||
return workflow_config, steps_json
|
||||
|
||||
async def _get_legacy_workflow_steps(self, workflow_id: str) -> list:
|
||||
client = await self._db.client
|
||||
workflow_steps_result = await client.table('workflow_steps').select('*').eq('workflow_id', workflow_id).order('step_order').execute()
|
||||
|
||||
return [{
|
||||
'name': step_data['name'],
|
||||
'description': step_data.get('description'),
|
||||
'type': step_data['type'],
|
||||
'config': step_data.get('config', {}),
|
||||
'conditions': step_data.get('conditions'),
|
||||
'order': step_data['step_order']
|
||||
} for step_data in workflow_steps_result.data]
|
||||
|
||||
|
||||
async def _get_agent_data(self, agent_id: str) -> Tuple[Dict[str, Any], str]:
|
||||
from agent.versioning.domain.entities import AgentId
|
||||
|
@ -547,26 +532,7 @@ class WorkflowExecutor:
|
|||
if not can_run:
|
||||
raise Exception(f"Billing check failed: {billing_message}")
|
||||
|
||||
async def _create_workflow_execution_record(
|
||||
self,
|
||||
workflow_id: str,
|
||||
agent_id: str,
|
||||
thread_id: str,
|
||||
workflow_input: Dict[str, Any],
|
||||
trigger_result: TriggerResult
|
||||
) -> str:
|
||||
client = await self._db.client
|
||||
|
||||
execution_result = await client.table('workflow_executions').insert({
|
||||
'workflow_id': workflow_id,
|
||||
'agent_id': agent_id,
|
||||
'thread_id': thread_id,
|
||||
'triggered_by': trigger_result.execution_variables.variables.get('triggered_by', 'trigger'),
|
||||
'status': 'running',
|
||||
'input_data': workflow_input
|
||||
}).execute()
|
||||
|
||||
return execution_result.data[0]['id']
|
||||
|
||||
|
||||
async def _create_workflow_message(
|
||||
self,
|
||||
|
|
|
@ -129,9 +129,9 @@ export default function ThreadPage({
|
|||
const agent = threadAgentData?.agent;
|
||||
const workflowId = threadQuery.data?.metadata?.workflow_id;
|
||||
|
||||
// Set initial selected agent from thread data
|
||||
// Set initial selected agent from thread data (most recently used agent)
|
||||
useEffect(() => {
|
||||
if (threadAgentData?.agent && !selectedAgentId) {
|
||||
if (threadAgentData?.agent && !selectedAgentId && threadAgentData.source === 'recent') {
|
||||
setSelectedAgentId(threadAgentData.agent.agent_id);
|
||||
}
|
||||
}, [threadAgentData, selectedAgentId]);
|
||||
|
@ -282,10 +282,10 @@ export default function ThreadPage({
|
|||
|
||||
const agentPromise = startAgentMutation.mutateAsync({
|
||||
threadId,
|
||||
options: {
|
||||
options: selectedAgentId ? {
|
||||
...options,
|
||||
agent_id: selectedAgentId
|
||||
}
|
||||
} : options
|
||||
});
|
||||
|
||||
const results = await Promise.allSettled([messagePromise, agentPromise]);
|
||||
|
|
|
@ -312,7 +312,10 @@ export const AgentBuilderChat = React.memo(function AgentBuilderChat({
|
|||
|
||||
const agentPromise = startAgentMutation.mutateAsync({
|
||||
threadId,
|
||||
options
|
||||
options: {
|
||||
...options,
|
||||
agent_id: agentId
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.allSettled([messagePromise, agentPromise]);
|
||||
|
|
|
@ -213,7 +213,10 @@ export const AgentPreview = ({ agent, agentMetadata }: AgentPreviewProps) => {
|
|||
try {
|
||||
const agentResult = await startAgentMutation.mutateAsync({
|
||||
threadId: result.thread_id,
|
||||
options
|
||||
options: {
|
||||
...options,
|
||||
agent_id: agent.agent_id
|
||||
}
|
||||
});
|
||||
console.log('[PREVIEW] Agent started manually:', agentResult);
|
||||
setAgentRunId(agentResult.agent_run_id);
|
||||
|
@ -280,7 +283,10 @@ export const AgentPreview = ({ agent, agentMetadata }: AgentPreviewProps) => {
|
|||
|
||||
const agentPromise = startAgentMutation.mutateAsync({
|
||||
threadId,
|
||||
options
|
||||
options: {
|
||||
...options,
|
||||
agent_id: agent.agent_id
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.allSettled([messagePromise, agentPromise]);
|
||||
|
|
|
@ -74,7 +74,7 @@ export type AgentsParams = {
|
|||
|
||||
export type ThreadAgentResponse = {
|
||||
agent: Agent | null;
|
||||
source: 'thread' | 'default' | 'none' | 'missing';
|
||||
source: 'recent' | 'none' | 'missing';
|
||||
message: string;
|
||||
};
|
||||
|
||||
|
|
|
@ -77,28 +77,6 @@ export function useKnowledgeBaseEntry(entryId: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export function useKnowledgeBaseContext(threadId: string, maxTokens = 4000) {
|
||||
const { getHeaders } = useAuthHeaders();
|
||||
|
||||
return useQuery({
|
||||
queryKey: knowledgeBaseKeys.context(threadId),
|
||||
queryFn: async () => {
|
||||
const headers = await getHeaders();
|
||||
const url = new URL(`${API_URL}/knowledge-base/threads/${threadId}/context`);
|
||||
url.searchParams.set('max_tokens', maxTokens.toString());
|
||||
|
||||
const response = await fetch(url.toString(), { headers });
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(error || 'Failed to fetch knowledge base context');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
},
|
||||
enabled: !!threadId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateKnowledgeBaseEntry() {
|
||||
const queryClient = useQueryClient();
|
||||
|
|
|
@ -24,7 +24,7 @@ export const useStartAgentMutation = () =>
|
|||
enable_thinking?: boolean;
|
||||
reasoning_effort?: string;
|
||||
stream?: boolean;
|
||||
agent_id?: string;
|
||||
agent_id?: string; // Optional again
|
||||
};
|
||||
}) => startAgent(threadId, options),
|
||||
{
|
||||
|
|
|
@ -14,7 +14,6 @@ export type Thread = {
|
|||
workflow_name?: string;
|
||||
workflow_run_name?: string;
|
||||
is_workflow_execution?: boolean;
|
||||
agent_id?: string;
|
||||
is_agent_builder?: boolean;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
|
|
@ -321,6 +321,7 @@ export const agentApi = {
|
|||
enable_thinking?: boolean;
|
||||
reasoning_effort?: string;
|
||||
stream?: boolean;
|
||||
agent_id?: string; // Optional again
|
||||
}
|
||||
): Promise<{ agent_run_id: string } | null> {
|
||||
const result = await backendApi.post(
|
||||
|
|
|
@ -627,7 +627,7 @@ export const startAgent = async (
|
|||
enable_thinking?: boolean;
|
||||
reasoning_effort?: string;
|
||||
stream?: boolean;
|
||||
agent_id?: string;
|
||||
agent_id?: string; // Optional again
|
||||
},
|
||||
): Promise<{ agent_run_id: string }> => {
|
||||
try {
|
||||
|
@ -656,7 +656,6 @@ export const startAgent = async (
|
|||
enable_thinking: false,
|
||||
reasoning_effort: 'low',
|
||||
stream: true,
|
||||
agent_id: undefined,
|
||||
};
|
||||
|
||||
const finalOptions = { ...defaultOptions, ...options };
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
from kortix import Agent, ModelSettings, function_tool
|
||||
|
||||
|
||||
agent = Agent(
|
||||
name="Haiku agent",
|
||||
instructions="Always respond in haiku form",
|
||||
model="o3-mini",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
assistant = client.beta.assistants.create(
|
||||
name="Math Tutor",
|
||||
instructions="You are a personal math tutor. Answer questions briefly, in a sentence or less.",
|
||||
model="gpt-4o",
|
||||
)
|
Loading…
Reference in New Issue