mirror of https://github.com/kortix-ai/suna.git
chore(dev): cleanup ux of workflows
This commit is contained in:
parent
f0440892ba
commit
2ff3c3cd65
|
@ -196,6 +196,8 @@ async def trigger_workflow_webhook(
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"workflow_id": workflow.id,
|
"workflow_id": workflow.id,
|
||||||
"workflow_name": workflow.name,
|
"workflow_name": workflow.name,
|
||||||
|
"is_workflow_execution": True,
|
||||||
|
"workflow_run_name": f"Workflow Run: {workflow.name}",
|
||||||
"triggered_by": "WEBHOOK",
|
"triggered_by": "WEBHOOK",
|
||||||
"execution_id": execution_id
|
"execution_id": execution_id
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,8 @@ async def _create_workflow_thread_for_api(
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"workflow_id": workflow.id,
|
"workflow_id": workflow.id,
|
||||||
"workflow_name": workflow.name,
|
"workflow_name": workflow.name,
|
||||||
"is_workflow_execution": True
|
"is_workflow_execution": True,
|
||||||
|
"workflow_run_name": f"Workflow Run: {workflow.name}"
|
||||||
},
|
},
|
||||||
"created_at": datetime.now(timezone.utc).isoformat()
|
"created_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,42 @@ class WorkflowConverter:
|
||||||
|
|
||||||
logger.info(f"Final enabled_tools list: {enabled_tools}")
|
logger.info(f"Final enabled_tools list: {enabled_tools}")
|
||||||
|
|
||||||
|
# Extract model from input node configuration, default to Claude 3.5 Sonnet
|
||||||
|
selected_model = "anthropic/claude-3-5-sonnet-latest"
|
||||||
|
if input_config:
|
||||||
|
# Look for model in input node data
|
||||||
|
for node in nodes:
|
||||||
|
if node.get('type') == 'inputNode':
|
||||||
|
node_data = node.get('data', {})
|
||||||
|
if node_data.get('model'):
|
||||||
|
selected_model = node_data['model']
|
||||||
|
# Ensure the model ID has the correct format
|
||||||
|
if not selected_model.startswith(('anthropic/', 'openai/', 'google/', 'meta-llama/', 'mistralai/', 'deepseek/')):
|
||||||
|
# Map common model names to their full IDs
|
||||||
|
model_mapping = {
|
||||||
|
'claude-sonnet-4': 'anthropic/claude-sonnet-4-20250514',
|
||||||
|
'claude-sonnet-3.7': 'anthropic/claude-3-7-sonnet-latest',
|
||||||
|
'claude-3.5': 'anthropic/claude-3-5-sonnet-latest',
|
||||||
|
'claude-3-5-sonnet-latest': 'anthropic/claude-3-5-sonnet-latest',
|
||||||
|
'claude-3-5-sonnet-20241022': 'anthropic/claude-3-5-sonnet-20241022',
|
||||||
|
'claude-3-5-haiku-latest': 'anthropic/claude-3-5-haiku-latest',
|
||||||
|
'gpt-4o': 'openai/gpt-4o',
|
||||||
|
'gpt-4o-mini': 'openai/gpt-4o-mini',
|
||||||
|
'gpt-4.1': 'openai/gpt-4.1',
|
||||||
|
'gpt-4.1-mini': 'gpt-4.1-mini',
|
||||||
|
'deepseek-chat': 'openrouter/deepseek/deepseek-chat',
|
||||||
|
'deepseek': 'openrouter/deepseek/deepseek-chat',
|
||||||
|
'deepseek-r1': 'openrouter/deepseek/deepseek-r1',
|
||||||
|
'gemini-2.0-flash-exp': 'google/gemini-2.0-flash-exp',
|
||||||
|
'gemini-flash-2.5': 'openrouter/google/gemini-2.5-flash-preview-05-20',
|
||||||
|
'gemini-2.5-flash:thinking': 'openrouter/google/gemini-2.5-flash-preview-05-20:thinking',
|
||||||
|
'gemini-2.5-pro-preview': 'openrouter/google/gemini-2.5-pro-preview',
|
||||||
|
'gemini-2.5-pro': 'openrouter/google/gemini-2.5-pro-preview',
|
||||||
|
'qwen3': 'openrouter/qwen/qwen3-235b-a22b'
|
||||||
|
}
|
||||||
|
selected_model = model_mapping.get(selected_model, f"anthropic/{selected_model}")
|
||||||
|
break
|
||||||
|
|
||||||
agent_step = WorkflowStep(
|
agent_step = WorkflowStep(
|
||||||
id="main_agent_step",
|
id="main_agent_step",
|
||||||
name="Workflow Agent",
|
name="Workflow Agent",
|
||||||
|
@ -69,7 +105,7 @@ class WorkflowConverter:
|
||||||
"tool_name": "workflow_agent",
|
"tool_name": "workflow_agent",
|
||||||
"system_prompt": workflow_prompt,
|
"system_prompt": workflow_prompt,
|
||||||
"agent_id": metadata.get("agent_id"),
|
"agent_id": metadata.get("agent_id"),
|
||||||
"model": "anthropic/claude-3-5-sonnet-latest",
|
"model": selected_model,
|
||||||
"max_iterations": 10,
|
"max_iterations": 10,
|
||||||
"input_prompt": input_config.prompt if input_config else "",
|
"input_prompt": input_config.prompt if input_config else "",
|
||||||
"tools": enabled_tools,
|
"tools": enabled_tools,
|
||||||
|
@ -254,21 +290,33 @@ class WorkflowConverter:
|
||||||
"""Generate a comprehensive system prompt that describes the workflow."""
|
"""Generate a comprehensive system prompt that describes the workflow."""
|
||||||
|
|
||||||
prompt_parts = [
|
prompt_parts = [
|
||||||
"You are an AI agent executing a workflow. Follow these instructions carefully:",
|
f"You are Suna - Workflows, an autonomous AI Agent created by the Kortix team, specialized in executing visual workflows.",
|
||||||
|
"",
|
||||||
|
"# 1. CORE IDENTITY & CAPABILITIES",
|
||||||
|
"You are a workflow-specialized autonomous agent capable of executing complex visual workflows across domains including information gathering, content creation, software development, data analysis, and problem-solving. You operate within the Suna platform and have access to a comprehensive toolkit for workflow execution.",
|
||||||
|
"",
|
||||||
|
"# 2. EXECUTION ENVIRONMENT",
|
||||||
|
"",
|
||||||
|
"## 2.1 SYSTEM INFORMATION",
|
||||||
|
f"- CURRENT YEAR: 2025",
|
||||||
|
f"- UTC DATE: {datetime.now().strftime('%Y-%m-%d')}",
|
||||||
|
f"- UTC TIME: {datetime.now().strftime('%H:%M:%S')}",
|
||||||
|
"- TIME CONTEXT: When searching for latest news or time-sensitive information, ALWAYS use these current date/time values as reference points. Never use outdated information or assume different dates.",
|
||||||
|
"- PLATFORM: Suna - Workflows",
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add input prompt if available
|
# Add input prompt if available
|
||||||
if input_config and input_config.prompt:
|
if input_config and input_config.prompt:
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"## Workflow Input Prompt",
|
"# 3. WORKFLOW INPUT PROMPT",
|
||||||
input_config.prompt,
|
input_config.prompt,
|
||||||
"",
|
"",
|
||||||
])
|
])
|
||||||
|
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"## Workflow Overview",
|
"# 4. WORKFLOW OVERVIEW",
|
||||||
"This workflow was created visually and consists of the following components:",
|
"This workflow was created visually in Suna and consists of the following components:",
|
||||||
""
|
""
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -303,7 +351,7 @@ class WorkflowConverter:
|
||||||
# Add trigger information
|
# Add trigger information
|
||||||
if input_config:
|
if input_config:
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"## Trigger Configuration",
|
"# 5. TRIGGER CONFIGURATION",
|
||||||
f"**Trigger Type**: {input_config.trigger_type}",
|
f"**Trigger Type**: {input_config.trigger_type}",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -327,17 +375,7 @@ class WorkflowConverter:
|
||||||
prompt_parts.append("")
|
prompt_parts.append("")
|
||||||
|
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"## Execution Instructions",
|
"# 6. AVAILABLE TOOLS",
|
||||||
"",
|
|
||||||
"Execute this workflow by following these steps:",
|
|
||||||
"1. Start with the input or trigger conditions",
|
|
||||||
"2. Process each component in the logical order defined by the connections",
|
|
||||||
"3. Use the available tools as specified in the workflow",
|
|
||||||
"4. Follow the data flow between components",
|
|
||||||
"5. Provide clear output at each step",
|
|
||||||
"6. Handle errors gracefully and provide meaningful feedback",
|
|
||||||
"",
|
|
||||||
"## Available Tools",
|
|
||||||
"You have access to the following tools based on the workflow configuration:"
|
"You have access to the following tools based on the workflow configuration:"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -405,7 +443,7 @@ class WorkflowConverter:
|
||||||
if mcp_tool_descriptions:
|
if mcp_tool_descriptions:
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"",
|
"",
|
||||||
"### MCP Server Tools",
|
"## MCP Server Tools",
|
||||||
"The following tools are available from MCP servers:"
|
"The following tools are available from MCP servers:"
|
||||||
])
|
])
|
||||||
prompt_parts.extend(mcp_tool_descriptions)
|
prompt_parts.extend(mcp_tool_descriptions)
|
||||||
|
@ -416,7 +454,7 @@ class WorkflowConverter:
|
||||||
if xml_examples:
|
if xml_examples:
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"",
|
"",
|
||||||
"## Tool Usage Examples",
|
"# 7. TOOL USAGE EXAMPLES",
|
||||||
"Use the following XML format to call tools. Each tool call must be wrapped in <function_calls> tags:",
|
"Use the following XML format to call tools. Each tool call must be wrapped in <function_calls> tags:",
|
||||||
"",
|
"",
|
||||||
xml_examples
|
xml_examples
|
||||||
|
@ -424,16 +462,52 @@ class WorkflowConverter:
|
||||||
|
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"",
|
"",
|
||||||
"## Workflow Execution",
|
"# 8. COMMUNICATION PROTOCOLS",
|
||||||
"When executing this workflow:",
|
"",
|
||||||
|
"## 8.1 COMMUNICATION GUIDELINES",
|
||||||
|
"- **Core Principle**: Communicate proactively, directly, and descriptively throughout your responses.",
|
||||||
|
"- **Narrative-Style Communication**: Integrate descriptive Markdown-formatted text directly in your responses before, between, and after tool calls",
|
||||||
|
"- **Communication Structure**: Use headers, brief paragraphs, and formatting for enhanced readability",
|
||||||
|
"- Balance detail with conciseness - be informative without being verbose",
|
||||||
|
"",
|
||||||
|
"## 8.2 MESSAGE TYPES & USAGE",
|
||||||
|
"- **Direct Narrative**: Embed clear, descriptive text directly in your responses explaining your actions, reasoning, and observations",
|
||||||
|
"- **'ask' tool (USER CAN RESPOND)**: Use ONLY for essential needs requiring user input (clarification, confirmation, options, missing info, validation). This blocks execution until user responds.",
|
||||||
|
"- **Minimize blocking operations ('ask')**: Maximize narrative descriptions in your regular responses.",
|
||||||
|
"",
|
||||||
|
"## 8.3 ATTACHMENT PROTOCOL",
|
||||||
|
"- When using the 'ask' tool, ALWAYS attach ALL visualizations, markdown files, charts, graphs, reports, and any viewable content created",
|
||||||
|
"- Include the 'attachments' parameter with file paths when sharing resources",
|
||||||
|
"- Remember: If the user should SEE it, you must ATTACH it with the 'ask' tool",
|
||||||
|
"",
|
||||||
|
"# 9. WORKFLOW EXECUTION INSTRUCTIONS",
|
||||||
|
"",
|
||||||
|
"Execute this workflow by following these steps:",
|
||||||
|
"1. **Start with the input or trigger conditions** - Begin execution based on the configured trigger",
|
||||||
|
"2. **Process each component in logical order** - Follow the visual connections defined in the workflow",
|
||||||
|
"3. **Use available tools as specified** - Employ the tools configured in this workflow using the XML format shown above",
|
||||||
|
"4. **Follow the data flow between components** - Respect the connections and dependencies between workflow nodes",
|
||||||
|
"5. **Provide clear output at each step** - Use narrative updates to keep the user informed of progress",
|
||||||
|
"6. **Handle errors gracefully** - Provide meaningful feedback and suggest alternatives when steps fail",
|
||||||
|
"7. **Complete the workflow successfully** - Deliver the expected output as defined by the workflow configuration",
|
||||||
|
"",
|
||||||
|
"## 9.1 EXECUTION BEST PRACTICES",
|
||||||
"- Follow the logical flow defined by the visual connections",
|
"- Follow the logical flow defined by the visual connections",
|
||||||
"- Use tools in the order and manner specified using the XML format shown above",
|
"- Use tools in the order and manner specified using the XML format shown above",
|
||||||
"- For MCP tools, use the exact tool names and formats shown in the examples",
|
"- For MCP tools, use the exact tool names and formats shown in the examples",
|
||||||
"- Provide clear, step-by-step output",
|
"- Provide clear, step-by-step output with narrative updates",
|
||||||
"- If any step fails, explain what went wrong and suggest alternatives",
|
"- If any step fails, explain what went wrong and suggest alternatives",
|
||||||
"- Complete the workflow by providing the expected output",
|
"- Complete the workflow by providing the expected output",
|
||||||
|
"- Use the 'ask' tool when you need essential user input to proceed",
|
||||||
|
"- Attach all relevant files and visualizations when using the 'ask' tool",
|
||||||
"",
|
"",
|
||||||
"Begin execution when the user provides input or triggers the workflow."
|
"## 9.2 COMPLETION PROTOCOLS",
|
||||||
|
"- Use 'ask' tool when you need essential user input to proceed (USER CAN RESPOND)",
|
||||||
|
"- Provide narrative updates frequently to keep the user informed without requiring their input",
|
||||||
|
"- Attach all deliverables, visualizations, and viewable content when using 'ask'",
|
||||||
|
"- Signal completion appropriately based on workflow requirements",
|
||||||
|
"",
|
||||||
|
"**Begin execution when the user provides input or triggers the workflow. You are operating within the Suna platform as a specialized workflow execution agent.**"
|
||||||
])
|
])
|
||||||
|
|
||||||
return "\n".join(prompt_parts)
|
return "\n".join(prompt_parts)
|
||||||
|
|
|
@ -54,6 +54,7 @@ class WorkflowExecutor:
|
||||||
|
|
||||||
main_step = workflow.steps[0]
|
main_step = workflow.steps[0]
|
||||||
system_prompt = main_step.config.get("system_prompt", "")
|
system_prompt = main_step.config.get("system_prompt", "")
|
||||||
|
selected_model = main_step.config.get("model", "anthropic/claude-3-5-sonnet-latest")
|
||||||
|
|
||||||
if variables:
|
if variables:
|
||||||
variables_text = "\n\n## Workflow Variables\n"
|
variables_text = "\n\n## Workflow Variables\n"
|
||||||
|
@ -100,7 +101,7 @@ class WorkflowExecutor:
|
||||||
thread_id=thread_id,
|
thread_id=thread_id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
stream=True,
|
stream=True,
|
||||||
model_name="anthropic/claude-3-5-sonnet-latest",
|
model_name=selected_model,
|
||||||
enable_thinking=False,
|
enable_thinking=False,
|
||||||
reasoning_effort="low",
|
reasoning_effort="low",
|
||||||
enable_context_manager=True,
|
enable_context_manager=True,
|
||||||
|
@ -460,7 +461,8 @@ class WorkflowExecutor:
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"workflow_id": workflow.id,
|
"workflow_id": workflow.id,
|
||||||
"workflow_name": workflow.name,
|
"workflow_name": workflow.name,
|
||||||
"is_workflow_execution": True
|
"is_workflow_execution": True,
|
||||||
|
"workflow_run_name": f"Workflow Run: {workflow.name}"
|
||||||
},
|
},
|
||||||
"created_at": datetime.now(timezone.utc).isoformat()
|
"created_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,11 +77,17 @@ export function SidebarSearch() {
|
||||||
const project = projectsById.get(projectId);
|
const project = projectsById.get(projectId);
|
||||||
if (!project) continue;
|
if (!project) continue;
|
||||||
|
|
||||||
|
// Check if this is a workflow thread and use the workflow name if available
|
||||||
|
let displayName = project.name || 'Unnamed Project';
|
||||||
|
if (thread.metadata?.is_workflow_execution && thread.metadata?.workflow_run_name) {
|
||||||
|
displayName = thread.metadata.workflow_run_name;
|
||||||
|
}
|
||||||
|
|
||||||
// Add to our list
|
// Add to our list
|
||||||
threadsWithProjects.push({
|
threadsWithProjects.push({
|
||||||
threadId: thread.thread_id,
|
threadId: thread.thread_id,
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
projectName: project.name || 'Unnamed Project',
|
projectName: displayName,
|
||||||
url: `/projects/${projectId}/thread/${thread.thread_id}`,
|
url: `/projects/${projectId}/thread/${thread.thread_id}`,
|
||||||
updatedAt:
|
updatedAt:
|
||||||
thread.updated_at || project.updated_at || new Date().toISOString(),
|
thread.updated_at || project.updated_at || new Date().toISOString(),
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { memo, useState } from "react";
|
import { memo, useState } from "react";
|
||||||
import { Handle, Position, NodeProps } from "@xyflow/react";
|
import { Handle, Position, NodeProps } from "@xyflow/react";
|
||||||
import { Play, Settings, Clock, Webhook, User, ChevronDown, ChevronUp } from "lucide-react";
|
import { Play, Settings, Clock, Webhook, User, ChevronDown, ChevronUp, Brain } from "lucide-react";
|
||||||
import { CardContent, CardHeader } from "@/components/ui/card";
|
import { CardContent, CardHeader } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
@ -16,10 +16,13 @@ import { WebhookConfigDialog } from "../webhooks/WebhookConfigDialog";
|
||||||
import { WebhookConfig } from "../webhooks/types";
|
import { WebhookConfig } from "../webhooks/types";
|
||||||
import { ScheduleConfigDialog } from "../scheduling/ScheduleConfigDialog";
|
import { ScheduleConfigDialog } from "../scheduling/ScheduleConfigDialog";
|
||||||
import { ScheduleConfig } from "../scheduling/types";
|
import { ScheduleConfig } from "../scheduling/types";
|
||||||
|
import { ModelSelector } from "@/components/thread/chat-input/model-selector";
|
||||||
|
import { useModelSelection } from "@/components/thread/chat-input/_use-model-selection";
|
||||||
|
|
||||||
interface InputNodeData {
|
interface InputNodeData {
|
||||||
label?: string;
|
label?: string;
|
||||||
prompt?: string;
|
prompt?: string;
|
||||||
|
model?: string;
|
||||||
trigger_type?: 'MANUAL' | 'WEBHOOK' | 'SCHEDULE';
|
trigger_type?: 'MANUAL' | 'WEBHOOK' | 'SCHEDULE';
|
||||||
schedule_config?: ScheduleConfig;
|
schedule_config?: ScheduleConfig;
|
||||||
webhook_config?: WebhookConfig;
|
webhook_config?: WebhookConfig;
|
||||||
|
@ -34,6 +37,23 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
|
||||||
const [isScheduleDialogOpen, setIsScheduleDialogOpen] = useState(false);
|
const [isScheduleDialogOpen, setIsScheduleDialogOpen] = useState(false);
|
||||||
const { updateNodeData, workflowId } = useWorkflow();
|
const { updateNodeData, workflowId } = useWorkflow();
|
||||||
|
|
||||||
|
// Use the model selection hook
|
||||||
|
const {
|
||||||
|
selectedModel,
|
||||||
|
setSelectedModel,
|
||||||
|
allModels,
|
||||||
|
canAccessModel,
|
||||||
|
subscriptionStatus,
|
||||||
|
refreshCustomModels
|
||||||
|
} = useModelSelection();
|
||||||
|
|
||||||
|
// Initialize model if not set
|
||||||
|
const currentModel = nodeData.model || selectedModel;
|
||||||
|
|
||||||
|
const handleModelChange = (modelId: string) => {
|
||||||
|
updateNodeData(id, { model: modelId });
|
||||||
|
};
|
||||||
|
|
||||||
const getTriggerIcon = () => {
|
const getTriggerIcon = () => {
|
||||||
switch (nodeData.trigger_type) {
|
switch (nodeData.trigger_type) {
|
||||||
case 'SCHEDULE':
|
case 'SCHEDULE':
|
||||||
|
@ -131,6 +151,12 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
|
||||||
{nodeData.prompt || "No prompt configured"}
|
{nodeData.prompt || "No prompt configured"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs font-medium text-muted-foreground">Model</Label>
|
||||||
|
<p className="text-sm mt-1 text-foreground">
|
||||||
|
{allModels.find(m => m.id === currentModel)?.label || currentModel || "Claude Sonnet 4"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-xs font-medium text-muted-foreground">Trigger</Label>
|
<Label className="text-xs font-medium text-muted-foreground">Trigger</Label>
|
||||||
<p className="text-sm mt-1 text-foreground">
|
<p className="text-sm mt-1 text-foreground">
|
||||||
|
@ -162,6 +188,23 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label className="text-sm font-medium flex items-center gap-2">
|
||||||
|
<Brain className="h-4 w-4" />
|
||||||
|
LLM Model *
|
||||||
|
</Label>
|
||||||
|
<div className="border rounded-lg p-2">
|
||||||
|
<ModelSelector
|
||||||
|
selectedModel={currentModel}
|
||||||
|
onModelChange={handleModelChange}
|
||||||
|
modelOptions={allModels}
|
||||||
|
canAccessModel={canAccessModel}
|
||||||
|
subscriptionStatus={subscriptionStatus}
|
||||||
|
refreshCustomModels={refreshCustomModels}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-sm font-medium">Trigger Type *</Label>
|
<Label className="text-sm font-medium">Trigger Type *</Label>
|
||||||
<Select
|
<Select
|
||||||
|
|
|
@ -111,10 +111,15 @@ export const processThreadsWithProjects = (
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let displayName = project.name || 'Unnamed Project';
|
||||||
|
if (thread.metadata?.is_workflow_execution && thread.metadata?.workflow_run_name) {
|
||||||
|
displayName = thread.metadata.workflow_run_name;
|
||||||
|
}
|
||||||
|
|
||||||
threadsWithProjects.push({
|
threadsWithProjects.push({
|
||||||
threadId: thread.thread_id,
|
threadId: thread.thread_id,
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
projectName: project.name || 'Unnamed Project',
|
projectName: displayName,
|
||||||
url: `/projects/${projectId}/thread/${thread.thread_id}`,
|
url: `/projects/${projectId}/thread/${thread.thread_id}`,
|
||||||
updatedAt:
|
updatedAt:
|
||||||
thread.updated_at || project.updated_at || new Date().toISOString(),
|
thread.updated_at || project.updated_at || new Date().toISOString(),
|
||||||
|
|
|
@ -11,6 +11,9 @@ export type Thread = {
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
workflow_id?: string;
|
workflow_id?: string;
|
||||||
|
workflow_name?: string;
|
||||||
|
workflow_run_name?: string;
|
||||||
|
is_workflow_execution?: boolean;
|
||||||
agent_id?: string;
|
agent_id?: string;
|
||||||
is_agent_builder?: boolean;
|
is_agent_builder?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
|
|
@ -256,6 +256,7 @@ export const threadsApi = {
|
||||||
project_id: thread.project_id,
|
project_id: thread.project_id,
|
||||||
created_at: thread.created_at,
|
created_at: thread.created_at,
|
||||||
updated_at: thread.updated_at,
|
updated_at: thread.updated_at,
|
||||||
|
metadata: thread.metadata,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return { data: mappedThreads, error: null };
|
return { data: mappedThreads, error: null };
|
||||||
|
|
|
@ -493,6 +493,7 @@ export const getThreads = async (projectId?: string): Promise<Thread[]> => {
|
||||||
project_id: thread.project_id,
|
project_id: thread.project_id,
|
||||||
created_at: thread.created_at,
|
created_at: thread.created_at,
|
||||||
updated_at: thread.updated_at,
|
updated_at: thread.updated_at,
|
||||||
|
metadata: thread.metadata,
|
||||||
}));
|
}));
|
||||||
return mappedThreads;
|
return mappedThreads;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue