feat: agent platform initial commit

- FastAPI + HTMX UI: dashboard, agents CRUD, chat, audit, tools
- SQLite schema: agents, conversations, messages, audit, sessions
- Code-agent sync (agents/*.py -> DB on startup)
- MCP client with health check
- Token auth (Bearer + session)
- LiteLLM integration via llm.py
- Docker Compose: agent-platform + mcp-tools services
- Smoke tests pass: /health 200, /api/agents 200, / 401
This commit is contained in:
Leopold
2026-07-05 22:18:00 +02:00
commit 64b840c94c
30 changed files with 2690 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
"""LiteLLM-Wrapper mit Token-Tracking."""
import litellm
from typing import List, Dict, Optional
from config import settings
if settings.LLM_API_KEY:
litellm.api_key = settings.LLM_API_KEY
if settings.LLM_API_BASE:
litellm.api_base = settings.LLM_API_BASE
class LLMResponse:
def __init__(self, content: str, tool_calls: Optional[List[Dict]] = None, usage: Optional[Dict] = None):
self.content = content
self.tool_calls = tool_calls or []
self.usage = usage or {}
@property
def input_tokens(self) -> int:
return self.usage.get("prompt_tokens", 0)
@property
def output_tokens(self) -> int:
return self.usage.get("completion_tokens", 0)
@property
def total_tokens(self) -> int:
return self.input_tokens + self.output_tokens
@property
def finish_reason(self) -> str:
return self.usage.get("finish_reason", "stop")
async def chat(
messages: List[Dict[str, str]],
tools: Optional[List[Dict]] = None,
model: Optional[str] = None,
temperature: float = 0.7,
max_tokens: int = 2000,
) -> LLMResponse:
"""LLM-Call via LiteLLM.
messages: Liste von {role, content} Dicts.
tools: Optional, MCP-Tool-Definitionen für Function-Calling.
"""
model = model or settings.LLM_MODEL
kwargs = {
"model": model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
}
if tools:
kwargs["tools"] = tools
response = await litellm.acompletion(**kwargs)
message = response.choices[0].message
tool_calls = []
if hasattr(message, "tool_calls") and message.tool_calls:
for tc in message.tool_calls:
tool_calls.append({
"id": tc.id,
"name": tc.function.name,
"arguments": tc.function.arguments,
})
usage = {}
if hasattr(response, "usage") and response.usage:
usage = {
"prompt_tokens": response.usage.prompt_tokens or 0,
"completion_tokens": response.usage.completion_tokens or 0,
"total_tokens": response.usage.total_tokens or 0,
"finish_reason": response.choices[0].finish_reason or "stop",
}
return LLMResponse(
content=message.content or "",
tool_calls=tool_calls,
usage=usage,
)