From 2677d381ce91f2ad4ee62df6afc26cd4025e91ca Mon Sep 17 00:00:00 2001 From: TimHoogervorst Date: Sun, 10 May 2026 18:35:56 +0200 Subject: [PATCH] Refactor API structure: move chat functionality to v1 router, implement dependency injection for OpenAI client, and set up application state management --- agent_api/core/llm.py | 7 --- agent_api/main.py | 84 -------------------------- {agent_api => api}/__init__.py | 0 api/dependencies.py | 7 +++ {agent_api/core => api/v1}/__init__.py | 0 api/v1/chat.py | 70 +++++++++++++++++++++ core/__init__.py | 0 {agent_api/core => core}/config.py | 2 +- core/llm.py | 9 +++ docker/Dockerfile | 2 +- main.py | 30 +++++++++ 11 files changed, 118 insertions(+), 93 deletions(-) delete mode 100644 agent_api/core/llm.py delete mode 100644 agent_api/main.py rename {agent_api => api}/__init__.py (100%) create mode 100644 api/dependencies.py rename {agent_api/core => api/v1}/__init__.py (100%) create mode 100644 api/v1/chat.py create mode 100644 core/__init__.py rename {agent_api/core => core}/config.py (63%) create mode 100644 core/llm.py create mode 100644 main.py diff --git a/agent_api/core/llm.py b/agent_api/core/llm.py deleted file mode 100644 index b567c56..0000000 --- a/agent_api/core/llm.py +++ /dev/null @@ -1,7 +0,0 @@ -from openai import OpenAI -from .config import DEEPSEEK_API_KEY - -client = OpenAI( - api_key=DEEPSEEK_API_KEY, - base_url="https://api.deepseek.com" -) \ No newline at end of file diff --git a/agent_api/main.py b/agent_api/main.py deleted file mode 100644 index 28b510e..0000000 --- a/agent_api/main.py +++ /dev/null @@ -1,84 +0,0 @@ -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware -from pydantic import BaseModel -from .core.llm import client - -app = FastAPI() - -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -class ChatRequest(BaseModel): - message: str - session_id: str | None = None - - -def run_agent(message: str, session_id: str | None = None): - response = client.chat.completions.create( - model="deepseek-chat", - messages=[ - {"role": "system", "content": "You are a helpful agent."}, - {"role": "user", "content": message} - ] - ) - return response.choices[0].message.content - - -@app.get("/") -def root(): - return {"status": "ok"} - - -@app.post("/chat") -def chat(req: ChatRequest): - response = run_agent(req.message, req.session_id) - - return { - "response": response, - "session_id": req.session_id - } - - -@app.get("/v1/models") -def list_models(): - return { - "object": "list", - "data": [ - { - "id": "agent-model", - "object": "model", - "created": 0, - "owned_by": "local-agent" - } - ] - } - -@app.get("/models") -def legacy_models(): - return list_models() - -@app.post("/chat/completions") -def chat_completions(req: dict): - messages = req["messages"] - user_message = messages[-1]["content"] - - response = run_agent(user_message) - - return { - "id": "chatcmpl-local", - "object": "chat.completion", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": response - } - } - ] - } \ No newline at end of file diff --git a/agent_api/__init__.py b/api/__init__.py similarity index 100% rename from agent_api/__init__.py rename to api/__init__.py diff --git a/api/dependencies.py b/api/dependencies.py new file mode 100644 index 0000000..86b37b1 --- /dev/null +++ b/api/dependencies.py @@ -0,0 +1,7 @@ +from fastapi import Request +from openai import OpenAI + + +def get_llm_client(request: Request) -> OpenAI: + """FastAPI dependency – returns the singleton OpenAI client from app.state.""" + return request.app.state.llm_client diff --git a/agent_api/core/__init__.py b/api/v1/__init__.py similarity index 100% rename from agent_api/core/__init__.py rename to api/v1/__init__.py diff --git a/api/v1/chat.py b/api/v1/chat.py new file mode 100644 index 0000000..b470095 --- /dev/null +++ b/api/v1/chat.py @@ -0,0 +1,70 @@ +from fastapi import APIRouter, Body, Depends +from openai import OpenAI +from pydantic import BaseModel + +from api.dependencies import get_llm_client + +router = APIRouter() + + +class ChatRequest(BaseModel): + message: str + session_id: str | None = None + + +def run_agent(client: OpenAI, message: str, session_id: str | None = None) -> str: + response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": "You are a helpful agent."}, + {"role": "user", "content": message}, + ], + ) + return response.choices[0].message.content + + +@router.get("/") +def root(): + return {"status": "ok"} + + +@router.post("/chat") +def chat(req: ChatRequest, client: OpenAI = Depends(get_llm_client)): + response = run_agent(client, req.message, req.session_id) + return {"response": response, "session_id": req.session_id} + + +@router.get("/models") +def list_models(): + return { + "object": "list", + "data": [ + { + "id": "agent-model", + "object": "model", + "created": 0, + "owned_by": "local-agent", + } + ], + } + + +@router.post("/chat/completions") +def chat_completions( + payload: dict = Body(...), + client: OpenAI = Depends(get_llm_client), +): + messages = payload["messages"] + user_message = messages[-1]["content"] + response = run_agent(client, user_message) + + return { + "id": "chatcmpl-local", + "object": "chat.completion", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": response}, + } + ], + } \ No newline at end of file diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/agent_api/core/config.py b/core/config.py similarity index 63% rename from agent_api/core/config.py rename to core/config.py index 61f89df..7783ffb 100644 --- a/agent_api/core/config.py +++ b/core/config.py @@ -2,6 +2,6 @@ from dotenv import load_dotenv from pathlib import Path import os -load_dotenv(Path(__file__).resolve().parent.parent.parent / ".env") +load_dotenv(Path(__file__).resolve().parent.parent / ".env") DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY") \ No newline at end of file diff --git a/core/llm.py b/core/llm.py new file mode 100644 index 0000000..b388407 --- /dev/null +++ b/core/llm.py @@ -0,0 +1,9 @@ +from openai import OpenAI + + +def create_client(api_key: str) -> OpenAI: + """Factory for an OpenAI-compatible client pointed at DeepSeek.""" + return OpenAI( + api_key=api_key, + base_url="https://api.deepseek.com", + ) \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 43ca64e..24fb622 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -10,4 +10,4 @@ COPY . /Agents ENV PYTHONPATH=/Agents -CMD ["uvicorn", "agent_api.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..091c16f --- /dev/null +++ b/main.py @@ -0,0 +1,30 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from api.v1.chat import router as v1_router +from core.config import DEEPSEEK_API_KEY +from core.llm import create_client + + +app = FastAPI() + +# --------------------------------------------------------------------------- +# Middleware +# --------------------------------------------------------------------------- +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# --------------------------------------------------------------------------- +# Singletons (stored on app.state so every module can reach them via Depends) +# --------------------------------------------------------------------------- +app.state.llm_client = create_client(DEEPSEEK_API_KEY) + +# --------------------------------------------------------------------------- +# Routers +# --------------------------------------------------------------------------- +app.include_router(v1_router, prefix="/v1")