Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d7ce399c8 | ||
|
|
8e38a568ef | ||
|
|
40663511b4 | ||
|
|
8f008c7fd2 | ||
|
|
d207594e3c | ||
|
|
523c5c43a0 | ||
|
|
822cdc9bf4 | ||
|
|
214dc4e725 | ||
|
|
acdde99a5c | ||
|
|
4e6f1da4f3 |
+7
-2
@@ -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=["*"]
|
||||
|
||||
@@ -13,15 +13,23 @@ jobs:
|
||||
- name: Login to Gitea Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.schweitz.net
|
||||
registry: git.schweitz.internal
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
provenance: false
|
||||
sbom: false
|
||||
tags: |
|
||||
git.schweitz.net/jpmschweitzer/tatlock:latest
|
||||
git.schweitz.net/jpmschweitzer/tatlock:${{ github.ref_name }}
|
||||
git.schweitz.internal/jpmschweitzer/tatlock:latest
|
||||
git.schweitz.internal/jpmschweitzer/tatlock:${{ github.ref_name }}
|
||||
|
||||
- name: Trigger Watchtower update
|
||||
if: success()
|
||||
run: |
|
||||
curl -sf -H "Authorization: Bearer ${{ secrets.WATCHTOWER_TOKEN }}" \
|
||||
http://watchtower:8080/v1/update
|
||||
|
||||
@@ -7,6 +7,65 @@ 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
|
||||
|
||||
- **Dependencies**: Add missing `pydantic-settings` (not included in pydantic-ai-slim)
|
||||
|
||||
## [1.2.4] - 2025-12-14
|
||||
|
||||
### Added
|
||||
|
||||
- **CI**: Trigger Watchtower update after successful image push
|
||||
|
||||
## [1.2.3] - 2025-12-14
|
||||
|
||||
### Fixed
|
||||
|
||||
- **CI**: Upgrade to build-push-action@v6, disable provenance and sbom for Gitea registry
|
||||
|
||||
## [1.2.2] - 2025-12-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- **CI**: Add `provenance: false` to docker/build-push-action to fix Gitea registry push
|
||||
|
||||
## [1.2.1] - 2025-12-13
|
||||
|
||||
### Changed
|
||||
|
||||
- **Dependency slimming**: Switched from `pydantic-ai` to `pydantic-ai-slim[openai]`
|
||||
- Removes unused LLM provider SDKs (anthropic, boto3, cohere, google-genai, groq, huggingface)
|
||||
- Production packages: 53 (down from ~158)
|
||||
- Production footprint: 178MB
|
||||
- Tatlock uses Ollama via OpenAI-compatible API, so only `openai` extra is needed
|
||||
- See `DEPENDENCY_SLIM.md` for rollback instructions
|
||||
|
||||
## [1.2.0] - 2025-12-13
|
||||
|
||||
### Added
|
||||
|
||||
@@ -432,7 +432,7 @@ For LLM agent development guidelines and architectural decisions, see [AGENTS.md
|
||||
|
||||
## Version
|
||||
|
||||
Current version: **1.2.0** - Phase F: Memory System (The Biographer)
|
||||
Current version: **1.3.2** - Biographer tool type hints fix
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "tatlock"
|
||||
version = "1.2.0"
|
||||
version = "1.3.2"
|
||||
description = "OpenAI-compatible API with Ollama backend"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
+9
-3
@@ -14,11 +14,17 @@ uvicorn[standard]>=0.38,<0.39
|
||||
# Latest: 2.12.4 (Nov 5, 2025) - No known CVEs
|
||||
pydantic>=2.11,<2.13
|
||||
|
||||
# Pydantic settings for configuration management
|
||||
# Required explicitly since pydantic-ai-slim doesn't include it
|
||||
# Latest: 2.12.0 (Dec 2025) - No known CVEs
|
||||
pydantic-settings>=2.12,<2.13
|
||||
|
||||
# AI/LLM integration
|
||||
# PydanticAI: Agent framework for using Pydantic with LLMs
|
||||
# Latest: 1.27.0 (Dec 5, 2025) - No known CVEs
|
||||
# Supports Ollama backend out of the box
|
||||
pydantic-ai>=1.27,<1.28
|
||||
# Using slim version with only openai extra (Ollama uses OpenAI-compatible API)
|
||||
# This avoids installing SDKs for anthropic, cohere, google, groq, huggingface, etc.
|
||||
# See DEPENDENCY_SLIM.md for rollback instructions if this breaks
|
||||
pydantic-ai-slim[openai]>=1.27,<1.28
|
||||
|
||||
# HTTP client for Ollama communication
|
||||
# Latest: 0.28.1 - No known CVEs
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
@@ -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 = []
|
||||
|
||||
Reference in New Issue
Block a user