# QV Session Manager
A simple, efficient session manager for persistent storage and management of conversations from [qv-ollama-sdk](https://github.com/quantyverse/qv-ollama-sdk) in an SQLite database.
## โจ Features
- ๐พ **Persistent storage** of conversations and messages in SQLite
- ๐ **Full-text search** in conversation contents and titles
- โฐ **Time-based search** by creation/update date
- ๐ **Conversation resumption** at any point
- ๐ **Conversation management** (list, delete, load)
- ๐ฏ **Minimal dependencies** (only Python stdlib + qv-ollama-sdk)
- ๐ **Simple API** with clear, intuitive methods
## ๐ ๏ธ Installation
### From PyPI
```bash
pip install qv-session-manager
or
uv add qv-session-manager
```
### Development installation
```bash
git clone https://github.com/quantyverse/qv-session-manager.git
cd qv-session-manager
pip install -e .
```
### Dependencies
- Python 3.10+
- qv-ollama-sdk
## ๐ Quickstart
```python
from qv_session_manager import SessionManager
from qv_ollama_sdk.domain.models import Conversation
# Initialize SessionManager
mgr = SessionManager(db_path="my_sessions.db")
# Create new conversation
conv = Conversation(title="Python Help", model_name="llama3")
conv.add_user_message("Explain Python lists to me!")
conv.add_assistant_message("Lists are ordered, mutable collections...")
# Save
mgr.save_conversation(conv, conv.messages)
# Load
loaded = mgr.load_conversation(str(conv.id))
print(f"Loaded: {loaded.title} with {len(loaded.messages)} messages")
# Search
results = mgr.search_conversations("lists")
print(f"Found: {len(results)} conversations")
```
## ๐ API Documentation
### SessionManager
#### Initialization
```python
SessionManager(db_path: str = "session_manager.db")
```
- `db_path`: Path to the SQLite database file
#### Methods
##### `save_conversation(conversation, messages)`
Saves a conversation and its messages.
- `conversation`: Conversation object (with `to_db_dict()` method)
- `messages`: List of Message objects (with `to_db_dict()` methods)
```python
mgr.save_conversation(conv, conv.messages)
```
##### `load_conversation(conversation_id: str) -> Conversation | None`
Loads a conversation with all messages.
- `conversation_id`: UUID of the conversation as a string
- **Returns**: Conversation object or None
```python
conv = mgr.load_conversation("550e8400-e29b-41d4-a716-446655440000")
```
##### `list_conversations() -> List[Dict[str, Any]]`
Lists all conversations (without messages).
- **Returns**: List of conversation dicts with metadata
```python
all_convs = mgr.list_conversations()
for conv in all_convs:
print(f"{conv['title']} - {conv['created_at']}")
```
##### `search_conversations(query: str) -> List[Dict[str, Any]]`
Full-text search in titles and message contents.
- `query`: Search term
- **Returns**: List of found conversation dicts
```python
results = mgr.search_conversations("Python")
```
##### `search_by_time(start: str = None, end: str = None) -> List[Dict[str, Any]]`
Time-based search for conversations.
- `start`: Start date (ISO format, e.g. "2025-01-20")
- `end`: End date (ISO format)
- **Returns**: List of conversation dicts
```python
# Conversations from today
today = datetime.now().strftime("%Y-%m-%d")
recent = mgr.search_by_time(start=today)
# Conversations from a period
results = mgr.search_by_time(start="2025-01-01", end="2025-01-31")
```
##### `resume_conversation(conversation_id: str) -> Dict[str, Any] | None`
Prepares conversation resumption.
- `conversation_id`: UUID of the conversation
- **Returns**: Dict with `conversation` and `last_message`
```python
resumed = mgr.resume_conversation(str(conv.id))
last_msg = resumed["last_message"]
print(f"Last message: {last_msg['content']}")
```
##### `delete_conversation(conversation_id: str)`
Deletes a conversation and all associated messages.
- `conversation_id`: UUID of the conversation
```python
mgr.delete_conversation(str(conv.id))
```
## ๐ก Advanced Examples
### Conversation management with metadata
```python
from datetime import datetime
from qv_session_manager import SessionManager
from qv_ollama_sdk.domain.models import Conversation
mgr = SessionManager()
# Conversation with metadata
conv = Conversation(
title="JavaScript Tutorial",
model_name="llama3",
metadata={
"topic": "web-development",
"difficulty": "beginner",
"language": "javascript"
}
)
# Add messages
conv.add_system_message("You are an experienced web developer.")
conv.add_user_message("Explain closures in JavaScript.")
# Save
mgr.save_conversation(conv, conv.messages)
# Search by topic
web_convs = [c for c in mgr.list_conversations()
if c.get('metadata', {}).get('topic') == 'web-development']
```
### Batch operations
```python
# Delete all conversations from a specific day
target_date = "2025-01-20"
old_convs = mgr.search_by_time(start=target_date, end=target_date)
for conv in old_convs:
mgr.delete_conversation(conv['id'])
print(f"Deleted: {conv['title']}")
```
### Conversation continuation
```python
# Load and extend an existing conversation
conv = mgr.load_conversation("existing-conversation-id")
if conv:
# Add new messages
conv.add_user_message("Can you explain that again?")
conv.add_assistant_message("Sure! Let me rephrase that...")
# Save updated version
mgr.save_conversation(conv, conv.messages)
```
## ๐งช Development & Testing
### Run tests
```bash
# All tests
pytest
# Specific test
pytest tests/test_session_manager.py
# With output
pytest -v -s
```
### Run demo
```bash
python examples/basic_usage.py
```
### Project structure
```
qv-session-manager/
โโโ src/qv_session_manager/
โ โโโ __init__.py
โ โโโ session_manager.py
โโโ tests/
โ โโโ test_session_manager.py
โโโ examples/
โ โโโ basic_usage.py
โโโ README.md
โโโ pyproject.toml
```
## ๐๏ธ Database Schema
The SQLite database uses the following schema:
```sql
-- Conversations table
CREATE TABLE conversations (
id TEXT PRIMARY KEY, -- UUID
title TEXT, -- Conversation title
created_at TEXT, -- ISO timestamp
updated_at TEXT, -- ISO timestamp
metadata TEXT -- JSON metadata
);
-- Messages table
CREATE TABLE messages (
id TEXT PRIMARY KEY, -- UUID
conversation_id TEXT, -- Reference to conversations.id
role TEXT, -- "system", "user", "assistant"
content TEXT, -- Message content
created_at TEXT, -- ISO timestamp
metadata TEXT, -- JSON metadata
FOREIGN KEY(conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
);
```
## ๐ค Integration with qv-ollama-sdk
```python
from qv_ollama_sdk.client import OllamaClient
from qv_session_manager import SessionManager
# Initialize clients
ollama = OllamaClient()
session_mgr = SessionManager()
# New conversation
conv = ollama.create_conversation(model="gemma3:1b")
conv.title = "Code Review Session"
# Chat with Ollama
response = ollama.chat(conv, "Explain Clean Code principles to me")
print(response.content)
# Persist session
session_mgr.save_conversation(conv, conv.messages)
# Later: load and continue session
loaded_conv = session_mgr.load_conversation(str(conv.id))
next_response = ollama.chat(loaded_conv, "Which tools do you recommend?")
```
## ๐ Roadmap
- [ ] Advanced search/filter functions (e.g. by metadata)
- [ ] Optional encryption of stored data
- [ ] Export/import of conversations (JSON, CSV)
- [ ] Performance optimizations for large datasets
- [ ] Async support for high-performance applications
## ๐ Error Handling
```python
try:
conv = mgr.load_conversation("invalid-id")
if conv is None:
print("Conversation not found")
except Exception as e:
print(f"Error loading: {e}")
```
## ๐ License
MIT License - see [LICENSE](LICENSE) for details.
## ๐ Support
- **Issues**: [GitHub Issues](https://github.com/quantyverse/qv-session-manager/issues)
- **Documentation**: This README
- **Examples**: See the `examples/` directory
Raw data
{
"_id": null,
"home_page": null,
"name": "qv-session-manager",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": "Thomas Bernhard <thomas@quantyverse.com>",
"keywords": "ollama, session-manager, conversation, sqlite, llm, chat, persistence",
"author": null,
"author_email": "Thomas Bernhard <thomas@quantyverse.com>",
"download_url": "https://files.pythonhosted.org/packages/04/57/5c601a6d12f2318c5fde913077b44a18f7726f903244a2cd728eec3c4c64/qv_session_manager-0.2.0.tar.gz",
"platform": null,
"description": "# QV Session Manager\r\n\r\nA simple, efficient session manager for persistent storage and management of conversations from [qv-ollama-sdk](https://github.com/quantyverse/qv-ollama-sdk) in an SQLite database.\r\n\r\n## \u2728 Features\r\n\r\n- \ud83d\udcbe **Persistent storage** of conversations and messages in SQLite\r\n- \ud83d\udd0d **Full-text search** in conversation contents and titles\r\n- \u23f0 **Time-based search** by creation/update date\r\n- \ud83d\udd04 **Conversation resumption** at any point\r\n- \ud83d\udccb **Conversation management** (list, delete, load)\r\n- \ud83c\udfaf **Minimal dependencies** (only Python stdlib + qv-ollama-sdk)\r\n- \ud83d\ude80 **Simple API** with clear, intuitive methods\r\n\r\n## \ud83d\udee0\ufe0f Installation\r\n\r\n### From PyPI \r\n```bash\r\npip install qv-session-manager\r\n\r\nor\r\n\r\nuv add qv-session-manager\r\n```\r\n\r\n### Development installation\r\n```bash\r\ngit clone https://github.com/quantyverse/qv-session-manager.git\r\ncd qv-session-manager\r\npip install -e .\r\n```\r\n\r\n### Dependencies\r\n- Python 3.10+\r\n- qv-ollama-sdk\r\n\r\n## \ud83d\ude80 Quickstart\r\n\r\n```python\r\nfrom qv_session_manager import SessionManager\r\nfrom qv_ollama_sdk.domain.models import Conversation\r\n\r\n# Initialize SessionManager\r\nmgr = SessionManager(db_path=\"my_sessions.db\")\r\n\r\n# Create new conversation\r\nconv = Conversation(title=\"Python Help\", model_name=\"llama3\")\r\nconv.add_user_message(\"Explain Python lists to me!\")\r\nconv.add_assistant_message(\"Lists are ordered, mutable collections...\")\r\n\r\n# Save\r\nmgr.save_conversation(conv, conv.messages)\r\n\r\n# Load\r\nloaded = mgr.load_conversation(str(conv.id))\r\nprint(f\"Loaded: {loaded.title} with {len(loaded.messages)} messages\")\r\n\r\n# Search\r\nresults = mgr.search_conversations(\"lists\")\r\nprint(f\"Found: {len(results)} conversations\")\r\n```\r\n\r\n## \ud83d\udcda API Documentation\r\n\r\n### SessionManager\r\n\r\n#### Initialization\r\n```python\r\nSessionManager(db_path: str = \"session_manager.db\")\r\n```\r\n- `db_path`: Path to the SQLite database file\r\n\r\n#### Methods\r\n\r\n##### `save_conversation(conversation, messages)`\r\nSaves a conversation and its messages.\r\n- `conversation`: Conversation object (with `to_db_dict()` method)\r\n- `messages`: List of Message objects (with `to_db_dict()` methods)\r\n\r\n```python\r\nmgr.save_conversation(conv, conv.messages)\r\n```\r\n\r\n##### `load_conversation(conversation_id: str) -> Conversation | None`\r\nLoads a conversation with all messages.\r\n- `conversation_id`: UUID of the conversation as a string\r\n- **Returns**: Conversation object or None\r\n\r\n```python\r\nconv = mgr.load_conversation(\"550e8400-e29b-41d4-a716-446655440000\")\r\n```\r\n\r\n##### `list_conversations() -> List[Dict[str, Any]]`\r\nLists all conversations (without messages).\r\n- **Returns**: List of conversation dicts with metadata\r\n\r\n```python\r\nall_convs = mgr.list_conversations()\r\nfor conv in all_convs:\r\n print(f\"{conv['title']} - {conv['created_at']}\")\r\n```\r\n\r\n##### `search_conversations(query: str) -> List[Dict[str, Any]]`\r\nFull-text search in titles and message contents.\r\n- `query`: Search term\r\n- **Returns**: List of found conversation dicts\r\n\r\n```python\r\nresults = mgr.search_conversations(\"Python\")\r\n```\r\n\r\n##### `search_by_time(start: str = None, end: str = None) -> List[Dict[str, Any]]`\r\nTime-based search for conversations.\r\n- `start`: Start date (ISO format, e.g. \"2025-01-20\")\r\n- `end`: End date (ISO format)\r\n- **Returns**: List of conversation dicts\r\n\r\n```python\r\n# Conversations from today\r\ntoday = datetime.now().strftime(\"%Y-%m-%d\")\r\nrecent = mgr.search_by_time(start=today)\r\n\r\n# Conversations from a period\r\nresults = mgr.search_by_time(start=\"2025-01-01\", end=\"2025-01-31\")\r\n```\r\n\r\n##### `resume_conversation(conversation_id: str) -> Dict[str, Any] | None`\r\nPrepares conversation resumption.\r\n- `conversation_id`: UUID of the conversation\r\n- **Returns**: Dict with `conversation` and `last_message`\r\n\r\n```python\r\nresumed = mgr.resume_conversation(str(conv.id))\r\nlast_msg = resumed[\"last_message\"]\r\nprint(f\"Last message: {last_msg['content']}\")\r\n```\r\n\r\n##### `delete_conversation(conversation_id: str)`\r\nDeletes a conversation and all associated messages.\r\n- `conversation_id`: UUID of the conversation\r\n\r\n```python\r\nmgr.delete_conversation(str(conv.id))\r\n```\r\n\r\n## \ud83d\udca1 Advanced Examples\r\n\r\n### Conversation management with metadata\r\n\r\n```python\r\nfrom datetime import datetime\r\nfrom qv_session_manager import SessionManager\r\nfrom qv_ollama_sdk.domain.models import Conversation\r\n\r\nmgr = SessionManager()\r\n\r\n# Conversation with metadata\r\nconv = Conversation(\r\n title=\"JavaScript Tutorial\",\r\n model_name=\"llama3\",\r\n metadata={\r\n \"topic\": \"web-development\", \r\n \"difficulty\": \"beginner\",\r\n \"language\": \"javascript\"\r\n }\r\n)\r\n\r\n# Add messages\r\nconv.add_system_message(\"You are an experienced web developer.\")\r\nconv.add_user_message(\"Explain closures in JavaScript.\")\r\n\r\n# Save\r\nmgr.save_conversation(conv, conv.messages)\r\n\r\n# Search by topic\r\nweb_convs = [c for c in mgr.list_conversations() \r\n if c.get('metadata', {}).get('topic') == 'web-development']\r\n```\r\n\r\n### Batch operations\r\n\r\n```python\r\n# Delete all conversations from a specific day\r\ntarget_date = \"2025-01-20\"\r\nold_convs = mgr.search_by_time(start=target_date, end=target_date)\r\n\r\nfor conv in old_convs:\r\n mgr.delete_conversation(conv['id'])\r\n print(f\"Deleted: {conv['title']}\")\r\n```\r\n\r\n### Conversation continuation\r\n\r\n```python\r\n# Load and extend an existing conversation\r\nconv = mgr.load_conversation(\"existing-conversation-id\")\r\n\r\nif conv:\r\n # Add new messages\r\n conv.add_user_message(\"Can you explain that again?\")\r\n conv.add_assistant_message(\"Sure! Let me rephrase that...\")\r\n \r\n # Save updated version\r\n mgr.save_conversation(conv, conv.messages)\r\n```\r\n\r\n## \ud83e\uddea Development & Testing\r\n\r\n### Run tests\r\n```bash\r\n# All tests\r\npytest\r\n\r\n# Specific test\r\npytest tests/test_session_manager.py\r\n\r\n# With output\r\npytest -v -s\r\n```\r\n\r\n### Run demo\r\n```bash\r\npython examples/basic_usage.py\r\n```\r\n\r\n### Project structure\r\n```\r\nqv-session-manager/\r\n\u251c\u2500\u2500 src/qv_session_manager/\r\n\u2502 \u251c\u2500\u2500 __init__.py\r\n\u2502 \u2514\u2500\u2500 session_manager.py\r\n\u251c\u2500\u2500 tests/\r\n\u2502 \u2514\u2500\u2500 test_session_manager.py\r\n\u251c\u2500\u2500 examples/\r\n\u2502 \u2514\u2500\u2500 basic_usage.py\r\n\u251c\u2500\u2500 README.md\r\n\u2514\u2500\u2500 pyproject.toml\r\n```\r\n\r\n## \ud83d\uddc4\ufe0f Database Schema\r\n\r\nThe SQLite database uses the following schema:\r\n\r\n```sql\r\n-- Conversations table\r\nCREATE TABLE conversations (\r\n id TEXT PRIMARY KEY, -- UUID\r\n title TEXT, -- Conversation title\r\n created_at TEXT, -- ISO timestamp\r\n updated_at TEXT, -- ISO timestamp \r\n metadata TEXT -- JSON metadata\r\n);\r\n\r\n-- Messages table\r\nCREATE TABLE messages (\r\n id TEXT PRIMARY KEY, -- UUID\r\n conversation_id TEXT, -- Reference to conversations.id\r\n role TEXT, -- \"system\", \"user\", \"assistant\"\r\n content TEXT, -- Message content\r\n created_at TEXT, -- ISO timestamp\r\n metadata TEXT, -- JSON metadata\r\n FOREIGN KEY(conversation_id) REFERENCES conversations(id) ON DELETE CASCADE\r\n);\r\n```\r\n\r\n## \ud83e\udd1d Integration with qv-ollama-sdk\r\n\r\n```python\r\nfrom qv_ollama_sdk.client import OllamaClient\r\nfrom qv_session_manager import SessionManager\r\n\r\n# Initialize clients\r\nollama = OllamaClient()\r\nsession_mgr = SessionManager()\r\n\r\n# New conversation\r\nconv = ollama.create_conversation(model=\"gemma3:1b\")\r\nconv.title = \"Code Review Session\"\r\n\r\n# Chat with Ollama\r\nresponse = ollama.chat(conv, \"Explain Clean Code principles to me\")\r\nprint(response.content)\r\n\r\n# Persist session\r\nsession_mgr.save_conversation(conv, conv.messages)\r\n\r\n# Later: load and continue session\r\nloaded_conv = session_mgr.load_conversation(str(conv.id))\r\nnext_response = ollama.chat(loaded_conv, \"Which tools do you recommend?\")\r\n```\r\n\r\n## \ud83d\udccb Roadmap\r\n\r\n- [ ] Advanced search/filter functions (e.g. by metadata)\r\n- [ ] Optional encryption of stored data\r\n- [ ] Export/import of conversations (JSON, CSV)\r\n- [ ] Performance optimizations for large datasets\r\n- [ ] Async support for high-performance applications\r\n\r\n## \ud83d\udc1b Error Handling\r\n\r\n```python\r\ntry:\r\n conv = mgr.load_conversation(\"invalid-id\")\r\n if conv is None:\r\n print(\"Conversation not found\")\r\nexcept Exception as e:\r\n print(f\"Error loading: {e}\")\r\n```\r\n\r\n## \ud83d\udcc4 License\r\n\r\nMIT License - see [LICENSE](LICENSE) for details.\r\n\r\n\r\n## \ud83d\udcde Support\r\n\r\n- **Issues**: [GitHub Issues](https://github.com/quantyverse/qv-session-manager/issues)\r\n- **Documentation**: This README\r\n- **Examples**: See the `examples/` directory\r\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Simple, efficient session manager for persistent storage of conversations from qv-ollama-sdk in SQLite",
"version": "0.2.0",
"project_urls": {
"Documentation": "https://github.com/quantyverse/qv-session-manager#readme",
"Homepage": "https://quantyverse.ai",
"Issues": "https://github.com/quantyverse/qv-session-manager/issues",
"Repository": "https://github.com/quantyverse/qv-session-manager.git"
},
"split_keywords": [
"ollama",
" session-manager",
" conversation",
" sqlite",
" llm",
" chat",
" persistence"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "8c69e3d70e6574602d6165cceef78db12d62565753e5d5d2cb415f3ee3d686e3",
"md5": "350fcdf3916e09da02ab438fbed6cc9a",
"sha256": "9cb6b3d24053f99f98002661bb7675842cb900ac7ae8d86d63e55035a3013e99"
},
"downloads": -1,
"filename": "qv_session_manager-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "350fcdf3916e09da02ab438fbed6cc9a",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 7681,
"upload_time": "2025-07-31T06:36:00",
"upload_time_iso_8601": "2025-07-31T06:36:00.189105Z",
"url": "https://files.pythonhosted.org/packages/8c/69/e3d70e6574602d6165cceef78db12d62565753e5d5d2cb415f3ee3d686e3/qv_session_manager-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "04575c601a6d12f2318c5fde913077b44a18f7726f903244a2cd728eec3c4c64",
"md5": "ab1c624559a485bf78e1f8b841ceb738",
"sha256": "f6bf535422ba56b678763df05f42056bf7ff6ca9f3729d61cbe37b36ec76b6f2"
},
"downloads": -1,
"filename": "qv_session_manager-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "ab1c624559a485bf78e1f8b841ceb738",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 11089,
"upload_time": "2025-07-31T06:36:01",
"upload_time_iso_8601": "2025-07-31T06:36:01.611421Z",
"url": "https://files.pythonhosted.org/packages/04/57/5c601a6d12f2318c5fde913077b44a18f7726f903244a2cd728eec3c4c64/qv_session_manager-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-31 06:36:01",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "quantyverse",
"github_project": "qv-session-manager#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "qv-session-manager"
}