Commit Graph

10 Commits

Author SHA1 Message Date
Carlos Garcia
93f2a101fa refactor: remove scripted file intercept — LLM owns all responses
Previously ab_ai_mail.py intercepted file uploads before reaching the
LLM and responded with a hardcoded clarification template. The LLM had
no involvement in the file upload response.

Changes:
- ab_ai_mail.py: remove _post_file_clarification, _find_pending_attachments,
  _describe_zip, and the two-step pending-attachment lookup. All messages
  (text, files, or both) are dispatched to the agent service immediately.
  Files with no text pass an empty message — the LLM decides what to do.
- upload.py: default message changed from hardcoded receipt instruction
  to '' so the LLM determines intent from file content.
- master_agent._synthesize: always runs through the LLM for both single
  and multi-agent cases — no raw templates reach the user.
- master_system.txt: add FILE UPLOADS routing rule so the LLM knows to
  route receipts to expenses_agent without asking for clarification.

New flow: upload → parse → LLM classifies → agent acts → LLM synthesizes
natural response → user sees it. Zero scripted intercepts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 21:05:38 -04:00
Carlos Garcia
8d1727b498 feat: sysops_agent — Docker/git self-management with auto-heal
Adds a new specialist agent that gives the AI system control over its
own infrastructure:

- sysops_tools.py: docker SDK (ps/logs/restart) + git CLI (pull/status/log)
  + Odoo channel notifier for autonomous action broadcasts
- sysops_agent.py: BaseAgent subclass handling on-demand chat requests,
  auto_heal() triggered by health failures, and sweep() for audits
- Background auto-heal loop (main.py): runs every 2 minutes, calls
  _get_failing_systems() and triggers auto_heal() when degraded
- health.py: extracted _get_failing_systems() helper reused by both
  the /health/detailed endpoint and the auto-heal loop
- docker-compose.yml: mount docker socket + /root/odoo workspace +
  SSH keys for git authentication
- Dockerfile: add git to apt-get
- requirements.txt: add docker==7.1.0 Python SDK

Auto-heal behavior:
  - Detects failing containers, restarts them, notifies all bot DM channels
  - Ollama (192.168.2.9) is flagged as external and skipped
  - On-demand via chat: "restart agent", "check logs", "pull latest code"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:01:57 -04:00
Carlos Garcia
bb1e93fabb fix: widen actions_taken to list[Any] and improve bot error replies
DispatchResponse declared actions_taken as list[dict] but agents return
list[str], causing a 422 on every successful upload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:31:45 -04:00
Carlos Garcia
cf3fe5e0a5 fix: await get_all() in registry router and align get_all key names
The /registry/agents endpoint was 500 on every call because
AgentRegistry.get_all() is async but was called without await.
Also aligns get_all() dict keys (name, domain) with what the router reads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 13:38:06 -04:00
Carlos Garcia
8a9d772b8e fix: increase timeout and parallelize receipt processing
- ab_ai_bot: raise requests.post timeout 120s -> 600s so long OCR+LLM
  runs don't silently drop the reply in Discuss
- upload: run parse_upload in ThreadPoolExecutor so tesseract OCR
  doesn't block the FastAPI event loop
- expenses_agent: parse all receipts concurrently with asyncio.gather
  (Ollama semaphore caps parallelism at 2); reduces 13-receipt LLM
  time from ~39s sequential to ~20s parallel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 01:50:12 -04:00
Carlos Garcia
4b7223a139 feat: file upload + expense report creation from Discuss attachments
- Discuss bot now reads ir.attachment from incoming messages; file-only
  messages no longer silently dropped
- ZIP files are described (contents listed) and bot asks clarifying
  question before acting; user's follow-up reply looks back for pending
  attachments so files don't need to be re-uploaded
- receipt_parser: extracts text from ZIP (recursive), JPG/PNG/etc (OCR),
  PDF (pdfplumber), HTML, TXT
- expenses_agent: full rewrite fixing broken method signatures; adds
  create_expense_sheet / create_expense / attach_receipt flow driven by
  LLM receipt parsing (Ollama, HIPAA-locked)
- master_agent: extra_context threads receipts + user_id into directives
- FastAPI /upload multipart endpoint; registered in main.py
- Odoo /ai/upload controller proxies files to agent service
- ab_ai_bot: dispatch_message_with_files() for multipart uploads

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 01:02:24 -04:00
Carlos Garcia
4cb94b18f1 fix(agent): align /dispatch with MasterAgent.handle_message signature
The router was calling handle_message(user_id, message, context, session_id)
but MasterAgent accepts (user_id, channel_id, message, directive_id) and
returns MasterResponse{response, status, ...} with no .reply or
.agent_reports fields. Discuss DMs to the bot crashed with TypeError.

Now the router:
- Derives directive_id from session_id (or generates one)
- Pulls channel_id out of req.context
- Maps MasterResponse.response -> DispatchResponse.reply
- Returns an empty agent_reports list (the field is reserved for future use;
  per-agent reports aren't part of MasterResponse)
2026-04-24 23:06:24 -04:00
Carlos Garcia
c769fca79f fix: resolve all 5 startup constructor errors + add DB retry
Fixes all errors reported in docker compose logs agent-service:

1. config.py: add ollama_max_concurrent, claude_timeout, claude_max_concurrent
   fields so LLMRouter(config=settings) can read them without AttributeError.

2. main.py - LLM router: drop manual OllamaBackend/ClaudeBackend construction;
   call LLMRouter(config=settings, pg_pool=pool) to match class signature.
   Fixes: OllamaBackend.__init__() unexpected kwarg 'base_url'.

3. main.py - DB: add 5-attempt retry with 2s backoff and redacted DSN logging.
   Fixes: connection refused race on startup before Postgres accepts connections.

4. main.py - AgentRegistry: call AgentRegistry() with no args (class takes none),
   then await agent_registry.load_from_odoo(odoo) to populate active agents.
   Fixes: AgentRegistry.__init__() unexpected kwarg 'odoo'.

5. main.py - PeerBus: pass registry=agent_registry at construction; register
   specialist agents on agent_registry (not peer_bus, which has no register()).
   peer_bus.py: make directive_id optional (default None) — bus is a singleton
   at startup; directive_id is only needed per-request.
   Fixes: PeerBus.__init__() missing positional args 'registry' and 'directive_id'.

6. main.py - MasterAgent: drop unexpected peer_bus= kwarg from constructor call.
   Fixes: MasterAgent.__init__() unexpected kwarg 'peer_bus'.

7. mcp_router.py: pass NotificationOptions() instance instead of None.
   Fixes: AttributeError 'NoneType' has no attribute 'tools_changed' (was applied
   in running container but not committed; now committed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:48:23 -04:00
ActiveBlue Build
66b114cdcf feat(mcp): add MCP gateway — 14 tools over SSE, all agent calls forced local
Architecture:
- agent_service/mcp/tools.py: 14 Tool definitions with JSON schemas
    dispatch, finance_query, accounting_query, crm_query, sales_query,
    project_query, elearning_query, expenses_query, employees_query,
    get_health, list_agents, trigger_sweep, get_pending_approvals, approve_directive
- agent_service/mcp/server.py: mcp.Server with list_tools + call_tool handlers
- agent_service/routers/mcp_router.py: Starlette routes at /mcp/sse + /mcp/messages
- main.py: mounts MCP routes alongside existing FastAPI routers (graceful fallback if mcp not installed)

Privacy guarantee (enforced in server.py, not by convention):
- _force_local_context() sets llm_router._privacy_mode = 'local' before EVERY agent call
- _restore_mode() restores original mode after the tool returns
- HIPAA agents (finance, accounting, expenses, employees) were already Ollama-only;
  MCP adds a second enforcement layer for all 8 agents
- MCP client (e.g. Claude Code CLI) receives only tool results — no LLM completions cross the boundary

Usage (Claude Code CLI):
  claude mcp add --transport sse http://192.168.2.47:8001/mcp/sse
  or copy claude_mcp_config.json to ~/.claude/mcp_servers.json

requirements.txt: added mcp==1.3.0
tests/test_mcp_server.py: 13 tests covering tool count, schemas, HIPAA labelling, privacy override

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 16:45:49 -04:00
ActiveBlue Build
430ab966b2 feat(service): add FastAPI agent service with 5 routers and Docker setup
- config.py: pydantic-settings with all env vars, privacy mode, per-agent overrides
- app_state.py: global singletons (pool, master agent, registry, llm_router, sweep)
- main.py: FastAPI lifespan startup — DB pool, LLM router, Odoo client, agents, master
- routers/dispatch.py: POST /dispatch with rate limiting and webhook secret auth
- routers/approval.py: GET /approval/pending, POST /approval/respond
- routers/registry.py: GET/POST /registry/agents, POST /registry/backend overrides
- routers/sweep.py: POST /sweep trigger, GET /sweep/status
- routers/health.py: GET /health, GET /health/detailed (DB/Odoo/Ollama checks)
- requirements.txt: pinned deps (fastapi, uvicorn, asyncpg, anthropic, alembic)
- Dockerfile: python:3.11-slim, single uvicorn worker
- docker-compose.yml: agent-service + postgres:15, bound to 192.168.2.47:8001

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 17:54:28 -04:00