diff --git a/python/valuecell/core/conversation/conversation_store.py b/python/valuecell/core/conversation/conversation_store.py index 6baf5ff21..3a02a0165 100644 --- a/python/valuecell/core/conversation/conversation_store.py +++ b/python/valuecell/core/conversation/conversation_store.py @@ -132,6 +132,7 @@ async def _ensure_initialized(self): conversation_id TEXT PRIMARY KEY, user_id TEXT NOT NULL, title TEXT, + agent_name TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'active' @@ -149,6 +150,7 @@ def _row_to_conversation(row: sqlite3.Row) -> Conversation: conversation_id=row["conversation_id"], user_id=row["user_id"], title=row["title"], + agent_name=row["agent_name"], created_at=datetime.fromisoformat(row["created_at"]), updated_at=datetime.fromisoformat(row["updated_at"]), status=row["status"], @@ -161,13 +163,14 @@ async def save_conversation(self, conversation: Conversation) -> None: await db.execute( """ INSERT OR REPLACE INTO conversations ( - conversation_id, user_id, title, created_at, updated_at, status - ) VALUES (?, ?, ?, ?, ?, ?) + conversation_id, user_id, title, agent_name, created_at, updated_at, status + ) VALUES (?, ?, ?, ?, ?, ?, ?) """, ( conversation.conversation_id, conversation.user_id, conversation.title, + conversation.agent_name, conversation.created_at.isoformat(), conversation.updated_at.isoformat(), conversation.status.value diff --git a/python/valuecell/core/conversation/manager.py b/python/valuecell/core/conversation/manager.py index 6c833ebd2..3173842c1 100644 --- a/python/valuecell/core/conversation/manager.py +++ b/python/valuecell/core/conversation/manager.py @@ -35,12 +35,14 @@ async def create_conversation( user_id: str, title: Optional[str] = None, conversation_id: Optional[str] = None, + agent_name: Optional[str] = None, ) -> Conversation: """Create new conversation""" conversation = Conversation( conversation_id=conversation_id or generate_conversation_id(), user_id=user_id, title=title, + agent_name=agent_name, ) await self.conversation_store.save_conversation(conversation) return conversation diff --git a/python/valuecell/core/conversation/models.py b/python/valuecell/core/conversation/models.py index 33ff1dcce..54cde8998 100644 --- a/python/valuecell/core/conversation/models.py +++ b/python/valuecell/core/conversation/models.py @@ -23,6 +23,7 @@ class Conversation(BaseModel): conversation_id: str = Field(..., description="Unique conversation identifier") user_id: str = Field(..., description="User ID") title: Optional[str] = Field(None, description="Conversation title") + agent_name: Optional[str] = Field(None, description="Agent name") created_at: datetime = Field( default_factory=datetime.now, description="Creation time" ) diff --git a/python/valuecell/core/conversation/tests/test_conversation_store.py b/python/valuecell/core/conversation/tests/test_conversation_store.py index 23223840c..95174c578 100644 --- a/python/valuecell/core/conversation/tests/test_conversation_store.py +++ b/python/valuecell/core/conversation/tests/test_conversation_store.py @@ -561,6 +561,7 @@ def __getitem__(self, key): "conversation_id": "conv-123", "user_id": "user-123", "title": "Test Title", + "agent_name": "Agent-1", "created_at": now.isoformat(), "updated_at": now.isoformat(), "status": "active", @@ -571,6 +572,7 @@ def __getitem__(self, key): assert conversation.conversation_id == "conv-123" assert conversation.user_id == "user-123" + assert conversation.agent_name == "Agent-1" assert conversation.title == "Test Title" assert conversation.status == "active" diff --git a/python/valuecell/core/coordinate/orchestrator.py b/python/valuecell/core/coordinate/orchestrator.py index 69528873b..a455cf85a 100644 --- a/python/valuecell/core/coordinate/orchestrator.py +++ b/python/valuecell/core/coordinate/orchestrator.py @@ -199,6 +199,7 @@ async def process_user_input( """ conversation_id = user_input.meta.conversation_id user_id = user_input.meta.user_id + agent_name = user_input.target_agent_name try: # Ensure conversation exists @@ -207,7 +208,7 @@ async def process_user_input( ) if not conversation: await self.conversation_manager.create_conversation( - user_id, conversation_id=conversation_id + user_id, conversation_id=conversation_id, agent_name=agent_name ) conversation = await self.conversation_manager.get_conversation( conversation_id @@ -663,7 +664,9 @@ async def _execute_plan_with_input_support( "phase": SubagentConversationPhase.START.value, } await self.conversation_manager.create_conversation( - plan.user_id, conversation_id=task.conversation_id + plan.user_id, + conversation_id=task.conversation_id, + agent_name=task.agent_name, ) if task.handoff_from_super_agent: subagent_conv_start_component = ( diff --git a/python/valuecell/server/api/schemas/conversation.py b/python/valuecell/server/api/schemas/conversation.py index 39ed1ae6a..c3ba79f0c 100644 --- a/python/valuecell/server/api/schemas/conversation.py +++ b/python/valuecell/server/api/schemas/conversation.py @@ -36,6 +36,7 @@ class MessageData(BaseModel): payload: Optional[Dict[str, Any]] = Field(None, description="Message payload") role: Optional[str] = Field(None, description="Role for simple event format") item_id: Optional[str] = Field(None, description="Item ID for simple event format") + agent_name: Optional[str] = Field(None, description="Name of the agent") class MessageEvent(BaseModel): diff --git a/python/valuecell/server/db/init_db.py b/python/valuecell/server/db/init_db.py index 26cfdacd8..fe7e24290 100644 --- a/python/valuecell/server/db/init_db.py +++ b/python/valuecell/server/db/init_db.py @@ -116,6 +116,52 @@ def create_tables(self) -> bool: try: logger.info("Creating database tables...") Base.metadata.create_all(bind=self.engine) + + # Create conversation-related tables that are not in SQLAlchemy models + logger.info("Creating conversation-related tables...") + with self.engine.connect() as conn: + # Create conversations table + conn.execute( + text(""" + CREATE TABLE IF NOT EXISTS conversations ( + conversation_id TEXT PRIMARY KEY, + user_id TEXT, + title TEXT, + agent_name TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + status TEXT DEFAULT 'active' + ) + """) + ) + + # Create conversation_items table + conn.execute( + text(""" + CREATE TABLE IF NOT EXISTS conversation_items ( + item_id TEXT PRIMARY KEY, + role TEXT NOT NULL, + event TEXT NOT NULL, + conversation_id TEXT NOT NULL, + thread_id TEXT, + task_id TEXT, + payload TEXT, + agent_name TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + ) + + # Create index for conversation_items + conn.execute( + text(""" + CREATE INDEX IF NOT EXISTS idx_item_conv_time + ON conversation_items(conversation_id, created_at) + """) + ) + + conn.commit() + logger.info("Database tables created successfully") return True diff --git a/python/valuecell/server/services/conversation_service.py b/python/valuecell/server/services/conversation_service.py index 8702e4489..d5dc4b152 100644 --- a/python/valuecell/server/services/conversation_service.py +++ b/python/valuecell/server/services/conversation_service.py @@ -44,35 +44,14 @@ async def get_conversation_list( # Apply pagination total = len(conversations) - paginated_conversations = conversations[offset : offset + limit] # Convert to response format conversation_items = [] - for conv in paginated_conversations: - # Get the latest item to extract agent_name - latest_items = await self.item_store.get_items( - conversation_id=conv.conversation_id, limit=1 - ) - - agent_name = "unknown" - if latest_items: - # Try to extract agent_name from the latest item's metadata - latest_item = latest_items[0] - if hasattr(latest_item, "metadata") and latest_item.metadata: - extracted_name = latest_item.metadata.get("agent_name") - if extracted_name: - agent_name = extracted_name - elif hasattr(latest_item, "agent_name") and latest_item.agent_name: - agent_name = latest_item.agent_name - - # Ensure agent_name is never None or empty - if not agent_name or agent_name is None: - agent_name = "unknown" - + for conv in conversations: conversation_item = ConversationListItem( conversation_id=conv.conversation_id, title=conv.title or f"Conversation {conv.conversation_id}", - agent_name=agent_name, + agent_name=conv.agent_name, update_time=conv.updated_at.isoformat() if conv.updated_at else conv.created_at.isoformat(),