Back to Documentation
AMADEV Docs
Architecture

Session Persistence

Chat session persistence system — request flow, database models, CRUD API

Session Persistence

Amadev auto-saves chat history to PostgreSQL. Every request to /v1/chat/completions can optionally (or automatically) save the user message and assistant response.


How It Works

Request Flow

Code
POST /v1/chat/completions
  body: { session_id?: "abc123", messages: [...], ... }
                          │
                    ┌─────┴─────┐
                    │            │
               Has ID?      No ID?
                    │            │
              ┌─────┘            └─────┐
              │                        │
         Lookup conv              Auto-create
         verify userId             new conv
              │                        │
              └─────┬──────────────────┘
                    │
              conversation.id
                    │
         ┌──────────┴──────────┐
         │                     │
    Save user msg          Save assistant
    (await first)          (void after)
         │                     │
         └──────────┬──────────┘
                    │
            Response includes
            session_id: "..."

Listing: GET /v1/conversations
Detail:  GET /v1/conversations/:id

Database Models

Code
model Conversation {
  id        String        @id @default(cuid())
  title     String        @default("New Chat")
  userId    String?
  user      User?         @relation(...)
  messages  ChatMessage[]
  createdAt DateTime      @default(now())
  updatedAt DateTime      @updatedAt
}

model ChatMessage {
  id             String       @id @default(cuid())
  conversationId String
  conversation   Conversation @relation(...)
  role           String       // "user" or "assistant"
  parts          Json         // [{ type: "text", text: "..." }]
  createdAt      DateTime     @default(now())
}

API Endpoints

POST /v1/chat/completions

Save chat messages by including session_id:

Code
{
  "model": "groq/compound",
  "session_id": "existing-session-id",
  "messages": [
    { "role": "user", "content": "Hello" }
  ]
}

Response includes session_id:

Code
{
  "session_id": "abc123",
  "choices": [{ "message": { "content": "Hi there!" } }]
}

If session_id is omitted (non-streaming), a new conversation is auto-created.

Conversation CRUD

MethodEndpointDescription
GET/v1/conversations?limit=50&offset=0List conversations
POST/v1/conversationsCreate conversation { title }
GET/v1/conversations/:idGet with all messages
PATCH/v1/conversations/:idUpdate title { title }
DELETE/v1/conversations/:idDelete + cascade messages

Agent Sessions

Agent-managed conversations:

MethodEndpointDescription
GET/v1/sessions/:id/messagesList agent session messages
POST/v1/sessions/:id/messagesAdd message to agent session

Implementation Details

ensureConversation(conversationId?, userId?)

  • If conversationId provided → lookup & verify ownership
  • If not provided → prisma.conversation.create()
  • Returns { id, isNew }

saveChatMessage(conversationId, role, content)

  • Creates ChatMessage record with parts: [{ type: "text", text: content }]
  • Auto-title: if message count ≤ 2, uses first user message as title (truncated at 80 chars)
  • Updates conversation updatedAt timestamp

Ordering Guarantee

User messages are saved with await before proceeding, assistant messages are saved with void. This ensures:

Code
user → assistant → user → assistant → ...

Streaming Caveat

Streaming (stream: true) only saves sessions if session_id is explicitly provided. Auto-creation is disabled for streaming to avoid saving incomplete conversations.


Viewing Conversations

From Code

Code
const convs = await prisma.conversation.findMany({
  where: { userId: "..." },
  orderBy: { updatedAt: "desc" },
  include: { _count: { select: { messages: true } } },
});

From Admin

Visit /admin/usage to see session activity in the daily usage chart.

From API

Code
curl -H "X-API-Key: amallm_..." /v1/conversations
curl -H "X-API-Key: amallm_..." /v1/conversations/:id