Skip to main content

Track and persist user interaction history across sessions — for AI chatbots, query tools, and any workflow requiring session continuity.

Project description

session-history-saver

Track and persist user interaction history across sessions — for AI chatbots, query tools, and any workflow requiring session continuity, auditing, or fine-tuning data collection.


Features

Feature Detail
Zero dependencies Pure Python stdlib — no third-party packages required
3 storage backends In-memory · JSON files · SQLite
Pluggable Bring your own backend by subclassing BaseStorage
Rich message model role, content, tokens, model, latency, status, metadata
Flexible API Direct calls · context manager · decorator
Export formats JSON · JSONL · CSV · Markdown · plain text
Env-variable config All settings overridable via SHS_* env vars
Thread-safe All backends use locking where required
Pagination + search Full-text search across message content

Installation

# From source
pip install .

# For development (includes pytest)
pip install -e ".[dev]"

Quick Start

from session_history_saver import SessionTracker

# SQLite backend (persists across restarts)
tracker = SessionTracker(backend="sqlite", db_path="./history.db")

# Start a session
session = tracker.start_session(user_id="alice", project_id="my-chatbot")

# Log messages
tracker.add_user_message(session.session_id, "Hello!")
tracker.add_assistant_message(
    session.session_id,
    "Hi! How can I help?",
    tokens=12, model="claude-3-sonnet", latency_ms=320.0
)

# End and persist
tracker.end_session(session.session_id)

Backends

In-Memory (testing / short-lived scripts)

tracker = SessionTracker(backend="memory")

Data lives only for the process lifetime. Ideal for unit tests.

JSON files

tracker = SessionTracker(backend="json", storage_dir="./sessions")
# Each session → ./sessions/<session_id>.json

Human-readable, portable, no database setup needed.

SQLite (recommended for production)

tracker = SessionTracker(backend="sqlite", db_path="./history.db")

Efficient, searchable, WAL-mode enabled for concurrent writes.

Custom backend

from session_history_saver import BaseStorage

class MyRedisStorage(BaseStorage):
    def save_session(self, session): ...
    def load_session(self, session_id): ...
    def delete_session(self, session_id): ...
    def list_sessions(self, **kwargs): ...
    def session_exists(self, session_id): ...

tracker = SessionTracker(custom_storage=MyRedisStorage())

Usage Patterns

1 — Direct API

session = tracker.start_session(
    user_id="alice",
    project_id="support-bot",
    title="Billing query",
    tags=["billing", "priority"],
    metadata={"channel": "web"},
)

tracker.add_message(session.session_id, role="user", content="I was double-charged.")
tracker.add_message(
    session.session_id,
    role="assistant",
    content="I can see the duplicate charge. Let me fix that.",
    tokens=18, model="claude-3-sonnet", latency_ms=410
)

tracker.end_session(session.session_id)

2 — Context manager

with tracker.track_session(user_id="bob", project_id="onboarding") as s:
    tracker.add_user_message(s.session_id, "How do I get started?")
    tracker.add_assistant_message(s.session_id, "Welcome! Let's begin with setup.")
# session is automatically ended on exit

3 — Decorator (wraps any function)

@tracker.track(project_id="search-service")
def run_query(question: str) -> str:
    return llm.generate(question)   # your LLM call here

answer = run_query("What is quantum entanglement?")
# First positional str arg → user message
# Return value             → assistant message

Message Parameters

Parameter Type Description
role str user, assistant, system, tool
content str Message body
tokens int Token count (for cost tracking)
model str Model name (e.g. claude-3-sonnet)
latency_ms float Response time in milliseconds
status str delivered, pending, error
metadata dict Arbitrary key-value pairs
message_id str Override auto-generated UUID
timestamp datetime Override auto-generated UTC time

Retrieval

# Get a single session
session = tracker.get_session(session_id)

# Get all messages
messages = tracker.get_messages(session_id)

# List with filters
sessions = tracker.list_sessions(
    user_id="alice",
    project_id="support-bot",
    tags=["billing"],
    limit=50,
    offset=0,
)

# Full-text search across message content
results = tracker.search("double-charged", limit=20)

# Stats — global
print(tracker.stats())
# {'total_sessions': 42, 'total_messages': 310, 'total_tokens': 18200, 'active_sessions': 1}

# Stats — single session
print(tracker.stats(session_id))
# {'message_count': 4, 'total_tokens': 56, 'duration_seconds': 12.3, ...}

Export

from session_history_saver import to_json, to_csv, to_jsonl, to_markdown, to_plain_text, save_to_file

session = tracker.get_session(session_id)

# JSON string
json_str = to_json(session)

# JSONL (batch of sessions — great for fine-tuning datasets)
jsonl_str = to_jsonl(tracker.list_sessions(project_id="my-app"))
save_to_file(jsonl_str, "./finetune_data.jsonl")

# CSV
csv_str = to_csv([session])
save_to_file(csv_str, "./export.csv")

# Markdown (human-readable audit trail)
md = to_markdown(session, include_metadata=True)
save_to_file(md, "./session_report.md")

# Plain text (one line per turn)
print(to_plain_text(session))

Configuration

Via constructor

tracker = SessionTracker(
    backend="sqlite",           # memory | json | sqlite
    db_path="./history.db",    # SQLite path
    storage_dir="./sessions",  # JSON directory
    auto_save=True,            # persist on every add_message
    default_user_id="system",  # fallback user
    default_project_id="prod", # fallback project
)

Via SessionHistoryConfig + environment variables

export SHS_BACKEND=sqlite
export SHS_DB_PATH=/var/data/history.db
export SHS_DEFAULT_PROJECT=my-app
export SHS_AUTO_SAVE=true
from session_history_saver import SessionHistoryConfig, SessionTracker

cfg = SessionHistoryConfig()                  # reads env vars automatically
tracker = SessionTracker(**cfg.to_tracker_kwargs())

Environment variable reference

Variable Default Description
SHS_BACKEND sqlite Storage backend
SHS_DB_PATH ./session_history.db SQLite DB path
SHS_STORAGE_DIR ./session_history JSON file directory
SHS_AUTO_SAVE true Auto-persist on every message
SHS_DEFAULT_USER Fallback user ID
SHS_DEFAULT_PROJECT Fallback project ID

Running Tests

pip install -e ".[dev]"
pytest tests/ -v --tb=short

Project Structure

session_history_saver/
├── session_history_saver/
│   ├── __init__.py      # Public API
│   ├── models.py        # Message, Session data models
│   ├── storage.py       # InMemoryStorage, JSONStorage, SQLiteStorage
│   ├── tracker.py       # SessionTracker — main interface
│   ├── exporters.py     # to_json, to_csv, to_markdown, …
│   └── config.py        # SessionHistoryConfig
├── tests/
│   └── test_session_history.py
├── examples/
│   └── usage.py
├── pyproject.toml
└── README.md

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

session_history_saver-1.0.0.tar.gz (22.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

session_history_saver-1.0.0-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file session_history_saver-1.0.0.tar.gz.

File metadata

  • Download URL: session_history_saver-1.0.0.tar.gz
  • Upload date:
  • Size: 22.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for session_history_saver-1.0.0.tar.gz
Algorithm Hash digest
SHA256 29ddcdb87c42c0cd3b75049a699546168fac0a4fc6fae9aff4a0b6e5de0cb546
MD5 ef798bf62bc1b4a65da178af02c3531e
BLAKE2b-256 bccc4b1563e0738fc0afec2f87ff096865059fa308e635e1a8c745d960a1f3ea

See more details on using hashes here.

File details

Details for the file session_history_saver-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for session_history_saver-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a282a74d85723ddd73cbd7c572faef1d9de0c4e706172089fdc5c8412670ca7a
MD5 f66c521acb4a5c7afd354460cd6339e7
BLAKE2b-256 766ca05d6514d1e3cd6306756cb9d0e0ecfbfbf47e603528c4e3b92ce4ba9668

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page