Add LLM endpoints, web frontend, and rate limiting config
- Added OpenAI-compatible LLM endpoints to API backend - Introduced web frontend with Jinja2 templates and static assets - Implemented API proxy routes in web service - Added sample db.json data for items, users, orders, reviews, categories, llm_requests - Updated ADC and Helm configs for separate AI and standard rate limiting - Upgraded FastAPI, Uvicorn, and added httpx, Jinja2, python-multipart dependencies - Added API configuration modal and client-side JS for web app
This commit is contained in:
199
api/db.json
Normal file
199
api/db.json
Normal file
@@ -0,0 +1,199 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Gaming Laptop RTX 4090",
|
||||
"description": "High-performance gaming laptop with RTX 4090",
|
||||
"price": 2999.99,
|
||||
"in_stock": true,
|
||||
"category": "electronics",
|
||||
"tags": ["gaming", "laptop", "nvidia"]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Mechanical Keyboard RGB",
|
||||
"description": "Cherry MX switches with RGB backlighting",
|
||||
"price": 149.99,
|
||||
"in_stock": true,
|
||||
"category": "peripherals",
|
||||
"tags": ["keyboard", "mechanical", "rgb"]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Wireless Gaming Mouse",
|
||||
"description": "25K DPI wireless gaming mouse",
|
||||
"price": 89.99,
|
||||
"in_stock": false,
|
||||
"category": "peripherals",
|
||||
"tags": ["mouse", "wireless", "gaming"]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "4K Gaming Monitor 32\"",
|
||||
"description": "144Hz refresh rate, HDR support",
|
||||
"price": 599.99,
|
||||
"in_stock": true,
|
||||
"category": "displays",
|
||||
"tags": ["monitor", "4k", "gaming"]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Gaming Headset 7.1",
|
||||
"description": "Surround sound gaming headset with noise cancellation",
|
||||
"price": 129.99,
|
||||
"in_stock": true,
|
||||
"category": "audio",
|
||||
"tags": ["headset", "audio", "gaming"]
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com",
|
||||
"active": true,
|
||||
"role": "user",
|
||||
"created_at": "2024-01-15T10:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"username": "jane_smith",
|
||||
"email": "jane@example.com",
|
||||
"active": true,
|
||||
"role": "admin",
|
||||
"created_at": "2024-02-20T14:22:00Z"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"username": "bob_wilson",
|
||||
"email": "bob@example.com",
|
||||
"active": false,
|
||||
"role": "user",
|
||||
"created_at": "2024-03-10T09:15:00Z"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"username": "alice_johnson",
|
||||
"email": "alice@example.com",
|
||||
"active": true,
|
||||
"role": "moderator",
|
||||
"created_at": "2024-04-05T16:45:00Z"
|
||||
}
|
||||
],
|
||||
"orders": [
|
||||
{
|
||||
"id": 1,
|
||||
"user_id": 1,
|
||||
"items": [
|
||||
{"item_id": 1, "quantity": 1, "price": 2999.99},
|
||||
{"item_id": 2, "quantity": 1, "price": 149.99}
|
||||
],
|
||||
"total": 3149.98,
|
||||
"status": "shipped",
|
||||
"created_at": "2024-09-15T12:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"user_id": 2,
|
||||
"items": [
|
||||
{"item_id": 4, "quantity": 2, "price": 599.99}
|
||||
],
|
||||
"total": 1199.98,
|
||||
"status": "delivered",
|
||||
"created_at": "2024-09-20T15:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"user_id": 4,
|
||||
"items": [
|
||||
{"item_id": 5, "quantity": 1, "price": 129.99},
|
||||
{"item_id": 3, "quantity": 1, "price": 89.99}
|
||||
],
|
||||
"total": 219.98,
|
||||
"status": "pending",
|
||||
"created_at": "2024-10-01T10:15:00Z"
|
||||
}
|
||||
],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 1,
|
||||
"item_id": 1,
|
||||
"user_id": 1,
|
||||
"rating": 5,
|
||||
"comment": "Amazing laptop! Best purchase ever!",
|
||||
"created_at": "2024-09-20T14:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"item_id": 2,
|
||||
"user_id": 2,
|
||||
"rating": 4,
|
||||
"comment": "Great keyboard, but a bit loud",
|
||||
"created_at": "2024-09-22T09:45:00Z"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"item_id": 4,
|
||||
"user_id": 2,
|
||||
"rating": 5,
|
||||
"comment": "Crystal clear display, perfect for gaming",
|
||||
"created_at": "2024-09-25T18:20:00Z"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"item_id": 5,
|
||||
"user_id": 4,
|
||||
"rating": 4,
|
||||
"comment": "Good sound quality, comfortable to wear",
|
||||
"created_at": "2024-10-02T11:00:00Z"
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "electronics",
|
||||
"description": "Electronic devices and gadgets"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "peripherals",
|
||||
"description": "Computer peripherals and accessories"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "displays",
|
||||
"description": "Monitors and display devices"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "audio",
|
||||
"description": "Audio devices and accessories"
|
||||
}
|
||||
],
|
||||
"llm_requests": [
|
||||
{
|
||||
"id": 1,
|
||||
"user_id": 1,
|
||||
"model": "videogame-expert",
|
||||
"prompt": "What are the best RPG games of 2024?",
|
||||
"tokens_used": 250,
|
||||
"timestamp": "2024-10-05T10:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"user_id": 2,
|
||||
"model": "videogame-expert",
|
||||
"prompt": "Recommend me games similar to Dark Souls",
|
||||
"tokens_used": 180,
|
||||
"timestamp": "2024-10-05T11:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"user_id": 1,
|
||||
"model": "videogame-expert",
|
||||
"prompt": "What's the best strategy for Elden Ring bosses?",
|
||||
"tokens_used": 320,
|
||||
"timestamp": "2024-10-05T14:15:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
90
api/main.py
90
api/main.py
@@ -1,8 +1,15 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
import uvicorn
|
||||
from datetime import datetime
|
||||
from fastapi import FastAPI, HTTPException
|
||||
import os
|
||||
import httpx
|
||||
|
||||
# OpenAI API configuration
|
||||
OPENAI_API_BASE = os.getenv("OPENAI_API_BASE", "http://localhost/api")
|
||||
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "your-api-key")
|
||||
DEFAULT_MODEL = os.getenv("DEFAULT_LLM_MODEL", "your-model-id")
|
||||
|
||||
app = FastAPI(
|
||||
title="API Demo Application",
|
||||
@@ -119,5 +126,84 @@ async def create_user(user: User):
|
||||
users_db.append(user_dict)
|
||||
return user_dict
|
||||
|
||||
# LLM endpoints
|
||||
class LLMRequest(BaseModel):
|
||||
prompt: str
|
||||
max_tokens: Optional[int] = 150
|
||||
temperature: Optional[float] = 0.7
|
||||
model: Optional[str] = DEFAULT_MODEL
|
||||
|
||||
class LLMResponse(BaseModel):
|
||||
response: str
|
||||
tokens_used: int
|
||||
model: str
|
||||
timestamp: str
|
||||
|
||||
@app.post("/llm/chat", response_model=LLMResponse, tags=["LLM"])
|
||||
async def llm_chat(request: LLMRequest):
|
||||
"""
|
||||
LLM Chat endpoint - connects to OpenAI-compatible API (Open WebUI)
|
||||
This endpoint is rate limited by AI token usage via API7 Gateway
|
||||
"""
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
f"{OPENAI_API_BASE}/chat/completions",
|
||||
headers={
|
||||
"Authorization": f"Bearer {OPENAI_API_KEY}",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
json={
|
||||
"model": request.model,
|
||||
"messages": [
|
||||
{"role": "user", "content": request.prompt}
|
||||
],
|
||||
"max_tokens": request.max_tokens,
|
||||
"temperature": request.temperature
|
||||
},
|
||||
timeout=30.0
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# Extract response and token usage
|
||||
llm_response = data["choices"][0]["message"]["content"]
|
||||
tokens_used = data.get("usage", {}).get("total_tokens", 0)
|
||||
|
||||
return LLMResponse(
|
||||
response=llm_response,
|
||||
tokens_used=tokens_used,
|
||||
model=request.model,
|
||||
timestamp=datetime.now().isoformat()
|
||||
)
|
||||
except httpx.HTTPStatusError as e:
|
||||
raise HTTPException(status_code=e.response.status_code, detail=f"OpenAI API error: {e.response.text}")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"LLM service error: {str(e)}")
|
||||
|
||||
@app.get("/llm/models", tags=["LLM"])
|
||||
async def list_llm_models():
|
||||
"""List available LLM models"""
|
||||
return {
|
||||
"models": [
|
||||
{"id": "videogame-expert", "name": "Videogame Expert", "max_tokens": 4096, "provider": "Open WebUI"}
|
||||
],
|
||||
"default_model": DEFAULT_MODEL,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@app.get("/llm/health", tags=["LLM"])
|
||||
async def llm_health():
|
||||
"""LLM service health check"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "llm-api",
|
||||
"provider": "Open WebUI",
|
||||
"endpoint": OPENAI_API_BASE,
|
||||
"default_model": DEFAULT_MODEL,
|
||||
"rate_limit": "ai-rate-limiting enabled (100 tokens/60s)",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host="0.0.0.0", port=8001)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
fastapi==0.104.1
|
||||
uvicorn[standard]==0.24.0
|
||||
fastapi==0.109.0
|
||||
uvicorn==0.27.0
|
||||
pydantic==2.5.0
|
||||
httpx==0.26.0
|
||||
|
||||
Reference in New Issue
Block a user