Claude-skill-registry-data manage-conversation-db
This skill should be used when implementing stateless conversation persistence, creating/loading conversations by ID, saving user/assistant messages, fetching history for agent input, and handling async database queries.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry-data
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/manage-conversation-db" ~/.claude/skills/majiayu000-claude-skill-registry-data-manage-conversation-db && rm -rf "$T"
manifest:
data/manage-conversation-db/SKILL.mdsource content
Manage Conversation DB Skill
This skill provides guidance for implementing conversation persistence in the database.
Purpose
Handle stateless conversation persistence:
- Create/load conversation by ID
- Save user/assistant messages with role and content
- Fetch history for agent input
- Async queries for performance
When to Use
Use this skill when:
- Implementing conversation storage layer
- Building message history retrieval
- Creating conversation management utilities
- Setting up async database operations for chat
Capabilities
- Conversation Lifecycle: Create new or load existing conversations
- Message Storage: Save messages with role, content, and metadata
- History Retrieval: Fetch conversation messages in chronological order
- Async Operations: Non-blocking database queries
- User Isolation: Conversations tied to user_id
Database Schema
from sqlmodel import SQLModel, Field, Relationship from datetime import datetime from typing import Optional, List class Conversation(SQLModel, table=True): id: str = Field(primary_key=True) user_id: str = Field(index=True) title: Optional[str] = None created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow) messages: List["Message"] = Relationship(back_populates="conversation") class Message(SQLModel, table=True): id: int = Field(primary_key=True, autoincrement=True) conversation_id: str = Field(foreign_key="conversation.id", index=True) role: str = Field(index=True) # "user", "assistant", "tool" content: str tool_name: Optional[str] = None tool_call_id: Optional[str] = None created_at: datetime = Field(default_factory=datetime.utcnow) conversation: Optional[Conversation] = Relationship(back_populates="messages")
Implementation Pattern
Conversation Service
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, desc class ConversationService: def __init__(self, session: AsyncSession): self.session = session async def get_or_create_conversation( self, conversation_id: Optional[str], user_id: str ) -> Conversation: """Get existing or create new conversation.""" if conversation_id: result = await self.session.execute( select(Conversation).where( Conversation.id == conversation_id, Conversation.user_id == user_id ) ) conversation = result.scalar_one_or_none() if conversation: return conversation # Create new conversation conversation = Conversation( id=conversation_id or str(uuid.uuid4()), user_id=user_id, title="New Chat" ) self.session.add(conversation) await self.session.commit() await self.session.refresh(conversation) return conversation async def save_message( self, conversation_id: str, role: str, content: str, tool_name: Optional[str] = None, tool_call_id: Optional[str] = None ) -> Message: """Save a message to the conversation.""" message = Message( conversation_id=conversation_id, role=role, content=content, tool_name=tool_name, tool_call_id=tool_call_id ) self.session.add(message) await self.session.commit() await self.session.refresh(message) return message async def get_conversation_history( self, conversation_id: str, user_id: str, limit: int = 50 ) -> List[Message]: """Fetch conversation messages for agent input.""" result = await self.session.execute( select(Message) .where(Message.conversation_id == conversation_id) .order_by(Message.created_at.asc()) .limit(limit) ) messages = result.scalars().all() # Verify user owns this conversation conv_result = await self.session.execute( select(Conversation).where(Conversation.id == conversation_id) ) conversation = conv_result.scalar_one_or_none() if not conversation or conversation.user_id != user_id: raise PermissionError("Conversation not found") return messages async def save_conversation_messages( self, conversation_id: str, user_id: str, messages: List[dict] ) -> None: """Save multiple messages atomically.""" for msg in messages: await self.save_message( conversation_id=conversation_id, role=msg["role"], content=msg["content"], tool_name=msg.get("tool_name"), tool_call_id=msg.get("tool_call_id") ) # Update conversation timestamp await self.session.execute( select(Conversation) .where(Conversation.id == conversation_id) ) # ... update updated_at
Helper Functions
async def build_message_array( db_messages: List[Message], user_message: str ) -> List[dict]: """Convert DB messages to agent input format.""" message_array = [{"role": "system", "content": SYSTEM_PROMPT}] for msg in db_messages: message_array.append({ "role": msg.role, "content": msg.content }) message_array.append({ "role": "user", "content": user_message }) return message_array def message_to_dict(message: Message) -> dict: """Convert Message to dictionary.""" return { "id": message.id, "role": message.role, "content": message.content, "tool_name": message.tool_name, "tool_call_id": message.tool_call_id, "created_at": message.created_at.isoformat() }
Async Session Management
from contextlib import asynccontextmanager from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker engine = create_async_engine(DATABASE_URL) async_session_maker = async_sessionmaker( engine, class_=AsyncSession, expire_on_commit=False ) @asynccontextmanager async def get_db_session(): async with async_session_maker() as session: try: yield session await session.commit() except Exception: await session.rollback() raise finally: await session.close()
Verification Checklist
- Conversations can be created and loaded by ID
- Messages saved with correct role and content
- History retrieved in chronological order
- User isolation enforced on conversations
- Async queries work without blocking
- Tool calls stored with metadata