Compare commits

...
4 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.5 9d7ce399c8 fix(memory): biographer tool type hints for Ollama (v1.3.2)
Build and Push / build (release) Successful in 51s
- Change `str | None` to `str` with empty default for memory_type
- Remove `keywords` parameter from store_insight (auto-generated anyway)
- Ollama's OpenAI API doesn't handle union types with None properly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 15:04:10 +01:00
jpmschweitzerandClaude Opus 4.5 8e38a568ef fix(memory): add biographer to delegation wrappers (v1.3.1)
Build and Push / build (release) Successful in 50s
- Add delegate_to_biographer to household registry delegation map
- Was returning raw tools which caused Ollama "invalid message content type: nil"
- Add Qdrant host/port to .env.example

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 14:56:32 +01:00
jpmschweitzerandClaude Opus 4.5 40663511b4 feat: memory system fixes and Redis config cleanup (v1.3.0)
Build and Push / build (release) Successful in 26s
- Fix Qdrant client to use query_points API (qdrant-client >= 1.10)
- Rename REDIS_DB to REDIS_BENCHMARK_DB for clarity
- Update Redis defaults to match stack allocation (benchmark=6, memory=1)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 14:36:56 +01:00
jpmschweitzer 8f008c7fd2 no longer needed 2025-12-14 14:05:07 +01:00
9 changed files with 50 additions and 98 deletions
+7 -2
View File
@@ -21,13 +21,18 @@ SEARXNG_TIMEOUT=30
# Redis Configuration
REDIS_HOST=redis-shared
REDIS_PORT=6379
REDIS_DB=1
REDIS_MEMORY_DB=1
REDIS_BENCHMARK_DB=6
REDIS_TIMEOUT=5
# Qdrant Configuraton
QDRANT_HOST=qdrant
QDRANT_PORT=6333
# Logging
LOG_LEVEL=INFO
ENABLE_BENCHMARKS=true
# Note: Log format is auto-selected based on ENVIRONMENT (console for dev, json for production)
# CORS (comma-separated list)
CORS_ORIGINS=*
CORS_ORIGINS=["*"]
+24
View File
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [1.3.2] - 2025-12-14
### Fixed
- **Memory**: Fix biographer tool type hints for Ollama compatibility (remove `| None` union types)
## [1.3.1] - 2025-12-14
### Fixed
- **Memory**: Add biographer to delegation wrappers (was returning raw tools causing Ollama error)
- **Config**: Add Qdrant host/port to .env.example
## [1.3.0] - 2025-12-14
### Fixed
- **Memory**: Update Qdrant client to use `query_points` API (qdrant-client >= 1.10)
### Changed
- **Config**: Rename `REDIS_DB` to `REDIS_BENCHMARK_DB` for clarity
- **Config**: Update Redis defaults to match stack allocation (benchmark=6, memory=1)
## [1.2.5] - 2025-12-14
### Fixed
-72
View File
@@ -1,72 +0,0 @@
# Dependency Slimming: pydantic-ai → pydantic-ai-slim
**Date**: 2025-12-13
**Version**: Post v1.2.0
## Change
Switched from `pydantic-ai` to `pydantic-ai-slim[openai]` to reduce container image size.
### Before
```
pydantic-ai>=1.27,<1.28
```
This installs SDKs for ALL LLM providers:
- anthropic
- boto3 + botocore (AWS Bedrock)
- cohere
- google-genai + google-auth
- groq
- huggingface-hub
Total packages: ~158
### After
```
pydantic-ai-slim[openai]>=1.27,<1.28
```
Only installs the OpenAI-compatible SDK. Ollama works through this interface.
Expected packages: ~80-90 (significant reduction)
## Why This Works
Tatlock uses Ollama exclusively, which implements the OpenAI-compatible API. The code uses:
```python
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
model = OpenAIChatModel(
model_name=config.OLLAMA_DEFAULT_MODEL,
provider=OllamaProvider(base_url=f"{config.OLLAMA_HOST}/v1")
)
```
This pattern only requires the `openai` extra, not the full pydantic-ai package.
## Rollback Instructions
If this change breaks things:
1. Revert requirements.txt:
```diff
- pydantic-ai-slim[openai]>=1.27,<1.28
+ pydantic-ai>=1.27,<1.28
```
2. Reinstall dependencies:
```bash
pip install -r requirements.txt
```
3. Delete this file once confirmed stable.
## Testing Checklist
- [ ] Unit tests pass
- [ ] Integration tests pass (with Ollama running)
- [ ] Wakeup script e2e test passes
- [ ] Container builds successfully
- [ ] Container runs correctly
+1 -1
View File
@@ -432,7 +432,7 @@ For LLM agent development guidelines and architectural decisions, see [AGENTS.md
## Version
Current version: **1.2.5** - Dependency fix
Current version: **1.3.2** - Biographer tool type hints fix
---
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "tatlock"
version = "1.2.5"
version = "1.3.2"
description = "OpenAI-compatible API with Ollama backend"
requires-python = ">=3.12"
dependencies = []
+6 -11
View File
@@ -25,7 +25,7 @@ logger = get_logger(__name__)
async def recall_semantic(
query: str,
memory_type: str | None = None,
memory_type: str = "",
limit: int = 5,
) -> str:
"""
@@ -64,7 +64,7 @@ async def recall_semantic(
user=user,
query_vector=query_vector,
limit=limit,
memory_type=memory_type,
memory_type=memory_type if memory_type else None,
)
if not results:
@@ -111,7 +111,6 @@ async def recall_semantic(
async def store_insight(
key: str,
value: str,
keywords: list[str] | None = None,
importance: float = 0.5,
) -> str:
"""
@@ -128,7 +127,6 @@ async def store_insight(
Args:
key: Short identifier for the memory (e.g., "car", "employer", "pet")
value: The actual information to remember
keywords: Optional keywords for better search (auto-extracted if not provided)
importance: How important is this? 0.0 (trivial) to 1.0 (critical)
Returns:
@@ -137,15 +135,12 @@ async def store_insight(
Examples:
store_insight("car", "User drives a Tesla Model 3")
store_insight("employer", "Works at Acme Corp as software engineer", importance=0.8)
store_insight("coffee", "Prefers oat milk lattes", keywords=["coffee", "drink", "preference"])
"""
try:
# Auto-generate keywords if not provided
if not keywords:
keywords = [key]
# Extract simple keywords from value
words = value.lower().split()
keywords.extend([w for w in words if len(w) > 4][:5])
# Auto-generate keywords from key and value
keywords = [key]
words = value.lower().split()
keywords.extend([w for w in words if len(w) > 4][:5])
success = await memory_service.store_fact(
key=key,
+5 -5
View File
@@ -101,9 +101,9 @@ class Config(BaseSettings):
default=6379,
description="Redis server port"
)
REDIS_DB: int = Field(
default=1,
description="Redis database number"
REDIS_BENCHMARK_DB: int = Field(
default=6,
description="Redis database number for benchmarks"
)
REDIS_TIMEOUT: int = Field(
default=5,
@@ -146,7 +146,7 @@ class Config(BaseSettings):
# Redis Memory Database (separate from benchmarks)
REDIS_MEMORY_DB: int = Field(
default=2,
default=1,
description="Redis database number for memory cache"
)
REDIS_MEMORY_TTL_HOURS: int = Field(
@@ -170,7 +170,7 @@ class Config(BaseSettings):
@property
def redis_url(self) -> str:
"""Construct Redis connection URL for benchmarks."""
return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_BENCHMARK_DB}"
@property
def redis_memory_url(self) -> str:
+2 -2
View File
@@ -223,12 +223,12 @@ class HouseholdRegistry:
>>> # Returns: [delegate_to_librarian, calculate, datetime, ...]
>>> # Instead of: [hybrid_search, search_wiki, create_wiki_page, ... (16 tools)]
"""
from src.agents.delegation import delegate_to_librarian
from src.agents.delegation import delegate_to_librarian, delegate_to_biographer
# Map of expert names to their delegation wrappers
delegation_wrappers = {
"librarian": delegate_to_librarian,
# Future: "memory": delegate_to_memory,
"biographer": delegate_to_biographer,
# Future: "home_automation": delegate_to_home_automation,
}
+4 -4
View File
@@ -232,14 +232,14 @@ class MemoryQdrantClient:
]
)
# Search
results = self._client.search(
# Search using new Query API (qdrant-client >= 1.10)
results = self._client.query_points(
collection_name=collection_name,
query_vector=query_vector,
query=query_vector,
limit=limit,
query_filter=query_filter,
score_threshold=score_threshold,
)
).points
# Format results
memories = []