Back to Openviking

Session Management

docs/en/concepts/08-session.md

0.4.178.3 KB
Original Source

Session Management

Session manages conversation messages, tracks context usage, and extracts long-term memories.

Overview

Lifecycle: Create → Interact → Commit

Getting a session by ID does not create it. Create the session first, then use client.session(session_id=...) to append messages or commit it.

python
session_info = client.create_session(session_id="chat_001")
session = client.session(session_id=session_info["session_id"])
session.add_message(role="user", content="...")
session.commit()

Core API

MethodDescription
add_message(role, content=None, parts=None, options=None, peer_id=None)Add message
commit()Commit: archive (sync) + summary generation and memory extraction (async background)
get_task(task_id)Query background task status

add_message

python
from openviking_sdk import ContextPart, ImagePart, TextPart

session.add_message(
    role="user",
    content="How to configure embedding?",
)

session.add_message(
    role="assistant",
    parts=[
        TextPart(text="Here's how..."),
        ContextPart(
            uri="viking://~/memories/profile.md",
            context_type="memory",
            abstract="User profile",
        ),
    ]
)

session.add_message(
    role="user",
    parts=[
        TextPart(text="Remember this studio layout."),
        ImagePart(url="https://example.com/studio.png", detail="auto"),
    ]
)

commit

python
result = session.commit()
# {
#   "status": "accepted",
#   "task_id": "uuid-xxx",
#   "archive_uri": "viking://user/{user_id}/sessions/.../history/archive_001",
#   "archived": True
# }

# Poll background task progress
task = client.get_task(task_id=result["task_id"])
# task["status"]: "pending" | "running" | "completed" | "failed"
# sum(task["result"]["memories_extracted"].values()): 3

Message Structure

Message

python
@dataclass
class Message:
    id: str              # msg_{UUID}
    role: str            # "user" | "assistant"
    parts: List[Part]    # Message parts
    created_at: datetime

Part Types

TypeDescription
TextPartText content
ImagePartImage URL content. During memory extraction, OpenViking can describe it with the configured VLM.
ContextPartContext reference (URI + abstract)
ToolPartTool call (input + output)

Compression Strategy

Archive Flow

commit() executes in two phases:

Phase 1 (synchronous, returns immediately):

  1. Increment compression_index
  2. Write messages to archive directory (messages.jsonl)
  3. Clear current messages list
  4. Return task_id

Phase 2 (asynchronous background): 5. Generate structured summary (LLM) → write .abstract.md and .overview.md 6. Extract long-term memories 7. Write memory_diff.json (memory change audit log) to archive directory 8. Update active_count 9. Write .done completion marker

Summary Format

markdown
# Session Summary

**One-line overview**: [Topic]: [Intent] | [Result] | [Status]

## Analysis
Key steps list

## Primary Request and Intent
User's core goal

## Key Concepts
Key technical concepts

## Pending Tasks
Unfinished tasks

Memory Extraction

Memory Types

After a session is committed, OpenViking uses the conversation and active memory policy to extract information that can improve future interactions. It stores the result in the current user's memory space. When a conversation involves a stable Peer, relevant memories can also be stored in that Peer's space.

OpenViking includes memory types such as profile, preferences, entities, events, identity, soul, cases, trajectories, and experiences, and supports custom types for application-specific needs. See Context Types for the complete purpose and path mapping.

Within memory_policy.memory_types, experiences enables the complete Agent Evolution pipeline and automatically activates cases and trajectories. If experiences is absent, explicitly supplied cases and trajectories entries are ignored without an error.

Extraction Flow

Messages → LLM Extract → Candidate Memories
              ↓
Vector Pre-filter → Find Similar Memories
              ↓
LLM Dedup Decision → candidate(skip/create/none) + item(merge/delete)
              ↓
Write to AGFS → Vectorize

Dedup Decisions

LevelDecisionDescription
CandidateskipCandidate is duplicate, skip and do nothing
CandidatecreateCreate candidate memory (optionally delete conflicting existing memories first)
CandidatenoneDo not create candidate; resolve existing memories by item decisions
Per-existing itemmergeMerge candidate content into specified existing memory
Per-existing itemdeleteDelete specified conflicting existing memory

Memory Diff

Each session.commit() writes a memory_diff.json to the archive directory, recording all memory changes from that commit for auditing and rollback.

json
{
  "archive_uri": "viking://user/{user_id}/sessions/{session_id}/history/archive_001",
  "extracted_at": "2026-04-21T10:00:00Z",
  "operations": {
    "adds": [
      {
        "uri": "memory/user/xxx/identity.md",
        "memory_type": "identity",
        "after": "Newly created file content"
      }
    ],
    "updates": [
      {
        "uri": "memory/user/xxx/context/project.md",
        "memory_type": "context",
        "before": "Content before modification",
        "after": "Content after modification"
      }
    ],
    "deletes": [
      {
        "uri": "memory/user/xxx/context/old.md",
        "memory_type": "context",
        "deleted_content": "Deleted file content"
      }
    ]
  },
  "skipped_operations": [
    {
      "memory_type": "events",
      "page_id": 101,
      "reason_code": "invalid_ranges",
      "reason": "No valid event range could be resolved"
    }
  ],
  "summary": {
    "total_adds": 1,
    "total_updates": 1,
    "total_deletes": 1,
    "total_skipped": 1
  }
}
FieldDescription
archive_uriArchive directory URI for this commit
extracted_atISO 8601 timestamp of extraction
operations.addsNew memories created (no before)
operations.updatesModified memories (with before and after)
operations.deletesDeleted memories (with deleted_content)
skipped_operationsIntentionally skipped operations and their stable reason codes; these are not file changes
summaryCounts per operation type

An empty memory_diff.json (all counts zero) is written when no applied or intentionally skipped operations occurred.

Storage Structure

viking://user/{user_id}/sessions/{session_id}/
├── messages.jsonl            # Current messages
├── .abstract.md              # Current abstract
├── .overview.md              # Current overview
├── history/
│   ├── archive_001/
│   │   ├── messages.jsonl    # Written in Phase 1
│   │   ├── .abstract.md      # Written in Phase 2 (background)
│   │   ├── .overview.md      # Written in Phase 2 (background)
│   │   ├── memory_diff.json  # Written in Phase 2 (background, memory change audit)
│   │   └── .done             # Phase 2 completion marker
│   └── archive_NNN/
└── tools/
    └── {tool_id}/tool.json

viking://~/memories/
├── profile.md
├── identity.md
├── soul.md
├── preferences/
├── entities/
├── events/
├── cases/
├── trajectories/
└── experiences/

viking://~/sessions/{session_id} uses the home alias and is expanded to viking://user/{user_id}/sessions/{session_id} for the authenticated caller. The uid-less spelling viking://user/sessions/{session_id} is no longer accepted and returns an error pointing at the viking://~/... form. The old viking://session/{session_id} form is still accepted as a backward-compatible alias for the same session path and is not a separate storage root.