removed bunch of old code, added /init bunch of cleanups
Build and Push Agent API / build (push) Successful in 4s
Build and Push Agent API / build (push) Successful in 4s
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Run the server (reads .env for config)
|
||||
uvicorn main:app --host 0.0.0.0 --port 8000
|
||||
|
||||
# Verify code compiles and imports cleanly
|
||||
python -c "import main; print('OK')"
|
||||
|
||||
# Syntax check a specific file
|
||||
python -m py_compile path/to/file.py
|
||||
|
||||
# Docker build
|
||||
docker build -t agents-api -f docker/Dockerfile .
|
||||
```
|
||||
|
||||
There is no test suite or linting setup yet.
|
||||
|
||||
## Architecture
|
||||
|
||||
This is a **Discord bot** powered by a LangGraph agent with a pluggable skill system. The only interaction surface is Discord DMs — there is no web chat UI.
|
||||
|
||||
```
|
||||
Discord DM → bot.py → LangGraph StateGraph → skills (tools) → external APIs
|
||||
│
|
||||
REST API (auth status, JellyStat)
|
||||
```
|
||||
|
||||
### Agent / skill system (`agents/`)
|
||||
|
||||
- **Agents** (`agents/__init__.py`) are thin wrappers pairing a system prompt with a list of skill names.
|
||||
- **Skills** (`agents/skills/__init__.py`) provide prompt fragments, OpenAI tool definitions, and an async `execute` callback. Each skill self-registers via `register()` at import time.
|
||||
- Agents and skills are loaded at startup by `load_all_agents()` in `main.py`, which triggers side-effecting imports of all agent/skill modules.
|
||||
|
||||
The media-agent (`agents/media_agent.py`) is the primary agent. Skills attached:
|
||||
- `seerr` — search, trending, discover, request, submit issues via Seerr API
|
||||
- `watch_history` — Jellyfin watch stats via the internal JellyStat API
|
||||
- `media_info` — base persona (prompt-only)
|
||||
- `triage` — fallback rules for unsupported actions (prompt-only)
|
||||
- `easter_eggs` — theme-aware persona flavours (prompt-only)
|
||||
|
||||
### LangGraph graph (`src/graph.py`)
|
||||
|
||||
Two-node StateGraph: `agent_node → tool_node → agent_node`. The agent node calls DeepSeek (OpenAI-compatible) with system prompt + tool defs. The tool node executes tool calls through the skill system via `execute_tool()`. A custom tool node is used — not LangChain's ToolNode.
|
||||
|
||||
State is `AgentState` (`src/state.py`): a TypedDict with `messages` (LangGraph `add_messages` reducer) and `discord_user_id`.
|
||||
|
||||
### Discord bot (`gateway/discord/bot.py`)
|
||||
|
||||
Runs in a background daemon thread with its own asyncio event loop (separate from FastAPI's). DMs only — no server/channel messages. Requires users to share a guild with the bot. Maintains per-user conversation history via `ConversationStore` (in-memory dict, last N exchanges). Supports `/login <service>` (Quick Connect) and `/logout <service>`.
|
||||
|
||||
### Auth system (`src/auth_store.py`, `gateway/auth/`)
|
||||
|
||||
SQLite-backed (WAL mode) with a single `user_auth` table keyed on `(discord_user_id, service)`. Stores opaque service tokens, never passwords. `AuthService` is a minimal ABC with `name`/`display_name` properties; `JellyfinAuth` is the only implementation, using Jellyfin Quick Connect (initiate → poll → exchange secret for token). The auth gate in `execute_tool()` checks `skill.requires_auth` before executing tools.
|
||||
|
||||
### REST API (`main.py`)
|
||||
|
||||
Minimal — only two routers remain:
|
||||
- `gateway/v1/auth.py` — `GET /api/v1/auth/Discord/status` (linked services lookup) and `POST /api/v1/auth/reset` (dev only)
|
||||
- `gateway/jellystat/api.py` — `GET /jellystat/{history,genres,summary}/{user_id}` called internally by the `watch_history` skill
|
||||
|
||||
### JellyStat (`gateway/jellystat/`)
|
||||
|
||||
PostgreSQL connection pool stored on `app.state.jellystat_pool`. Database functions (`startup-functions.sql`) are deployed on startup via `CREATE OR REPLACE FUNCTION`. The watch_history skill calls these endpoints over HTTP (localhost) rather than querying the DB directly, keeping DB credentials isolated from the skill layer.
|
||||
|
||||
### Seerr session caching (`agents/skills/seerr.py`)
|
||||
|
||||
`httpx.AsyncClient` instances are cached per event loop (the Discord bot thread has its own loop separate from FastAPI). Cookie-based auth for Seerr is obtained once at startup via a sync login (thread-safe with double-check locking), then reused across all event-loop-specific clients.
|
||||
|
||||
## Key patterns
|
||||
|
||||
- **Self-registration**: agents, skills, and auth services all register at import time via module-level function calls. New modules just need to be imported once (see `load_all_agents()` and `import gateway.auth.jellyfin` in `main.py`).
|
||||
- **Auth gate**: skills declare `requires_auth=["jellyfin"]` and the framework checks credentials before tool execution. Tools receive `_discord_user_id` injected into their args dict.
|
||||
- **TMDb IDs as source of truth**: media tools display `[tmdb:123456]` tags and prefer IDs over title matching. The system prompt instructs the LLM to always show and use these IDs.
|
||||
Reference in New Issue
Block a user