mydocker/camera-llm/camera-memory/app/camera_memory.py

94 lines
3.0 KiB
Python

import os
import time
import base64
import requests
import ollama
import chromadb
from datetime import datetime
FRIGATE_URL = os.getenv("FRIGATE_URL", "http://frigate:5000")
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://ollama:11434")
CHROMA_URL = os.getenv("CHROMA_URL", "http://chromadb:8000")
CAMERAS = os.getenv("CAMERAS", "front_door").split(",")
INTERVAL = int(os.getenv("INTERVAL", "1"))
RETENTION_HOURS = int(os.getenv("RETENTION_HOURS", "72"))
ollama_client = ollama.Client(host=OLLAMA_URL)
chroma_host = CHROMA_URL.replace("http://", "").split(":")[0]
chroma_port = int(CHROMA_URL.split(":")[-1])
chroma = chromadb.HttpClient(host=chroma_host, port=chroma_port)
collection = chroma.get_or_create_collection(name="camera_frames", metadata={"hnsw:space": "cosine"})
def get_frame(camera):
try:
r = requests.get(f"{FRIGATE_URL}/api/{camera}/latest.jpg", timeout=5)
if r.status_code == 200:
return r.content
except Exception as e:
print(f"[ERROR] Frame grab {camera}: {e}")
return None
def describe(image_bytes):
try:
b64 = base64.b64encode(image_bytes).decode()
res = ollama_client.chat(
model='moondream',
messages=[{
'role': 'user',
'content': 'Describe briefly: people (gender, clothing, action), vehicles (color, type), animals, packages, unusual activity.',
'images': [b64]
}]
)
return res['message']['content']
except Exception as e:
print(f"[ERROR] Vision: {e}")
return ""
def embed(text):
res = ollama_client.embeddings(model='nomic-embed-text', prompt=text)
return res['embedding']
def store(camera, description, timestamp):
if not description.strip():
return
dt = datetime.fromtimestamp(timestamp)
doc_id = f"{camera}_{int(timestamp)}"
collection.add(
ids=[doc_id],
embeddings=[embed(description)],
documents=[description],
metadatas=[{"camera": camera, "timestamp": timestamp, "datetime": dt.isoformat(), "hour": dt.hour}]
)
print(f"[{dt.strftime('%H:%M:%S')}] {camera}: {description[:80]}...")
def cleanup_old():
cutoff = time.time() - (RETENTION_HOURS * 3600)
try:
collection.delete(where={"timestamp": {"$lt": cutoff}})
print(f"[CLEANUP] Removed entries older than {RETENTION_HOURS}h")
except Exception as e:
print(f"[CLEANUP ERROR] {e}")
def main():
print(f"Camera Memory Service")
print(f" Frigate: {FRIGATE_URL}")
print(f" Ollama: {OLLAMA_URL}")
print(f" Chroma: {CHROMA_URL}")
print(f" Cameras: {CAMERAS}")
print(f" Interval: {INTERVAL}s")
time.sleep(10)
iteration = 0
while True:
for camera in CAMERAS:
frame = get_frame(camera)
if frame:
desc = describe(frame)
store(camera, desc, time.time())
iteration += 1
if iteration % 3600 == 0:
cleanup_old()
time.sleep(INTERVAL)
if __name__ == "__main__":
main()