Skip to content

Commit 9a2a512

Browse files
add genai model to frontend
1 parent cb7c0e9 commit 9a2a512

File tree

21 files changed

+548
-229
lines changed

21 files changed

+548
-229
lines changed

client/package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"react-colorful": "^5.6.1",
3333
"react-dom": "^18.2.0",
3434
"sonner": "^2.0.5",
35-
"tailwind-merge": "^3.2.0"
35+
"tailwind-merge": "^3.2.0",
36+
"@svgr/webpack": "^8.1.0"
3637
},
3738
"devDependencies": {
3839
"@eslint/eslintrc": "^3",

client/src/components/WhiteBoard.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,20 @@ export default function WhiteBoard() {
4848
const textNodes = selectedNodes.filter(node => node.type === 'text');
4949
if (textNodes.length === 0) return;
5050

51-
const nodeToUpdate = textNodes[0]; // first selected text node
51+
const nodeToUpdate = textNodes[0];
5252
const currentText = nodeToUpdate.data.label as string;
5353

54+
const endpointMap: Record<typeof action, string> = {
55+
complete: 'completion',
56+
summarize: 'summarization',
57+
rephrase: 'rephrase'
58+
};
59+
60+
const endpoint = endpointMap[action];
61+
const url = `http://localhost:8080/${endpoint}`;
62+
5463
try {
55-
const response = await fetch(`http://localhost:8080/api/llm/rephrase`, {
64+
const response = await fetch(url, {
5665
method: 'POST',
5766
headers: { 'Content-Type': 'application/json' },
5867
body: JSON.stringify({ user_text: [currentText] }),
@@ -67,11 +76,14 @@ export default function WhiteBoard() {
6776
setNodes((nds) =>
6877
nds.map((node) =>
6978
node.id === nodeToUpdate.id
70-
? {
79+
? {
7180
...node,
72-
data: {
81+
data: {
7382
...node.data,
74-
label: data.llm_response || '' // update node text from API response
83+
label:
84+
action === 'complete'
85+
? `${currentText} ${data.llm_response || ''}`
86+
: data.llm_response || ''
7587
}
7688
}
7789
: node
@@ -82,6 +94,7 @@ export default function WhiteBoard() {
8294
}
8395
};
8496

97+
8598

8699

87100
return (

compose.aws.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ services:
44
command:
55
- "--providers.docker=true"
66
- "--providers.docker.exposedByDefault=false"
7-
- "--entrypoints.web.address=:80"
7+
- "--entrypoints.web.address=:81"
88
- "--entrypoints.websecure.address=:443"
99
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
1010
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
@@ -14,7 +14,7 @@ services:
1414
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
1515
restart: unless-stopped
1616
ports:
17-
- "80:80"
17+
- "81:81"
1818
- "443:443"
1919
volumes:
2020
- /var/run/docker.sock:/var/run/docker.sock

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
restart: always
66
image: nginx:stable-alpine
77
healthcheck:
8-
test: "curl -f http://localhost:80 || exit 1"
8+
test: "curl -f http://localhost:81 || exit 1"
99
interval: 5s
1010
volumes:
1111
- ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
@@ -14,7 +14,7 @@ services:
1414
depends_on:
1515
- server
1616
ports:
17-
- "80:80"
17+
- "81:81"
1818
- "443:443"
1919
networks:
2020
- server

genai/app/main.py

Lines changed: 153 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,158 @@
1-
from contextlib import asynccontextmanager
2-
from fastapi import FastAPI
3-
from app.routes import root
4-
from app.db.client import WeaviateClientSingleton
1+
import os
2+
import json
3+
import requests
4+
from typing import Dict, Any, List, Optional
5+
from fastapi import FastAPI, HTTPException, APIRouter
6+
from pydantic import BaseModel, Field
7+
from langchain.llms.base import LLM
8+
from langchain_core.prompts import PromptTemplate
9+
from langchain.callbacks.manager import CallbackManagerForLLMRun
10+
import logging
11+
from fastapi.middleware.cors import CORSMiddleware
512

13+
# Setup logging
14+
logging.basicConfig(level=logging.INFO)
15+
logger = logging.getLogger(__name__)
616

7-
@asynccontextmanager
8-
async def lifespan(app: FastAPI):
9-
WeaviateClientSingleton.get_instance()
10-
print("Weaviate client initialized")
11-
yield
17+
router = APIRouter()
1218

19+
# Environment configuration
20+
CHAIR_API_KEY = "sk-a4370d697e294dac819c95ba5ffe0740"
21+
API_URL = "https://gpu.aet.cit.tum.de/api/chat/completions"
1322

14-
app = FastAPI(lifespan=lifespan)
23+
class OpenWebUILLM(LLM):
24+
api_url: str = API_URL
25+
api_key: str = CHAIR_API_KEY
26+
model_name: str = "llama3.3:latest"
27+
28+
@property
29+
def _llm_type(self) -> str:
30+
return "open_webui"
31+
32+
def _call(
33+
self,
34+
prompt: str,
35+
stop: Optional[List[str]] = None,
36+
run_manager: Optional[CallbackManagerForLLMRun] = None,
37+
**kwargs: Any,
38+
) -> str:
39+
if not self.api_key:
40+
raise ValueError("CHAIR_API_KEY environment variable is required")
41+
42+
headers = {
43+
"Authorization": f"Bearer {self.api_key}",
44+
"Content-Type": "application/json",
45+
}
46+
47+
# Updated payload format
48+
payload = {
49+
"model": self.model_name,
50+
"messages": [
51+
{
52+
"role": "user",
53+
"content": prompt
54+
}
55+
]
56+
}
57+
58+
try:
59+
logger.info(f"Sending request to OpenWebUI API: {payload}")
60+
response = requests.post(
61+
self.api_url,
62+
headers=headers,
63+
json=payload,
64+
timeout=30
65+
)
66+
67+
if response.status_code != 200:
68+
logger.error(f"API error: {response.status_code} - {response.text}")
69+
raise requests.RequestException(f"API returned {response.status_code}: {response.text}")
70+
71+
result = response.json()
72+
logger.info(f"Received response: {result}")
73+
74+
# Extract content from choices array
75+
if "choices" in result and len(result["choices"]) > 0:
76+
content = result["choices"][0]["message"]["content"]
77+
return content.strip()
78+
else:
79+
logger.error(f"Unexpected response format: {result}")
80+
raise ValueError(f"Unexpected response format: {result}")
81+
82+
except requests.RequestException as e:
83+
logger.error(f"API request failed: {str(e)}")
84+
raise Exception(f"API request failed: {str(e)}")
85+
# Initialize FastAPI app
86+
app = FastAPI(
87+
title="LLM Service",
88+
description="OpenWebUI powered LLM service for text operations",
89+
version="1.0.0"
90+
)
1591

16-
app.include_router(root.router)
92+
app.add_middleware(
93+
CORSMiddleware,
94+
allow_origins=["http://localhost:3001", "http://localhost:8000/api/llm"],
95+
allow_credentials=True,
96+
allow_methods=["*"],
97+
allow_headers=["*"],
98+
)
99+
100+
# Initialize LLM
101+
llm = OpenWebUILLM()
102+
103+
class TextRequest(BaseModel):
104+
user_text: List[str]
105+
106+
class TextResponse(BaseModel):
107+
llm_response: str
108+
109+
@router.post("/completion", response_model=TextResponse)
110+
async def complete_text(request: TextRequest):
111+
try:
112+
prompt = f"""Complete the following text naturally:
113+
{' '.join(request.user_text)}
114+
115+
Provide a natural continuation that maintains the style and context in one or two sentences.
116+
"""
117+
logger.info(f"Processing completion request for text: {request.user_text}")
118+
result = llm(prompt)
119+
logger.info(f"Generated completion: {result}")
120+
return TextResponse(llm_response=result)
121+
except Exception as e:
122+
logger.error(f"Completion error: {str(e)}")
123+
raise HTTPException(status_code=500, detail=str(e))
124+
125+
@router.post("/summarization", response_model=TextResponse)
126+
async def summarize_text(request: TextRequest):
127+
try:
128+
prompt = f"""Summarize the following text concisely:
129+
{' '.join(request.user_text)}
130+
"""
131+
result = llm(prompt)
132+
return TextResponse(llm_response=result)
133+
except Exception as e:
134+
logger.error(f"Summarization error: {str(e)}")
135+
raise HTTPException(status_code=500, detail=str(e))
136+
137+
@router.post("/rephrase", response_model=TextResponse)
138+
async def rephrase_text(request: TextRequest):
139+
try:
140+
logger.info(f"Received rephrase request: {request}")
141+
prompt = f"""Rephrase the following text while maintaining its meaning in one or two sentences:
142+
{' '.join(request.user_text)}
143+
"""
144+
result = llm(prompt)
145+
return TextResponse(llm_response=result)
146+
except Exception as e:
147+
logger.error(f"Rephrase error: {str(e)}")
148+
raise HTTPException(status_code=500, detail=str(e))
149+
150+
@router.get("/health")
151+
async def health_check():
152+
return {
153+
"status": "healthy",
154+
"model": llm.model_name,
155+
"api_url": llm.api_url
156+
}
157+
158+
app.include_router(router)

genai/app/routes/root.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,107 @@
1-
from fastapi import APIRouter
1+
from fastapi import APIRouter, HTTPException
2+
from pydantic import BaseModel
3+
from typing import List
4+
from fastapi.middleware.cors import CORSMiddleware
5+
import logging
6+
import requests
7+
from langchain.llms.base import LLM
8+
from langchain_core.prompts import PromptTemplate
9+
from langchain.callbacks.manager import CallbackManagerForLLMRun
10+
from typing import Optional, Any
211

312
router = APIRouter()
413

14+
# Setup logging
15+
logging.basicConfig(level=logging.INFO)
16+
logger = logging.getLogger(__name__)
517

18+
# Constants
19+
CHAIR_API_KEY = "add api"
20+
API_URL = "https://gpu.aet.cit.tum.de/api/chat/completions"
21+
22+
class OpenWebUILLM(LLM):
23+
api_url: str = API_URL
24+
api_key: str = CHAIR_API_KEY
25+
model_name: str = "llama3.3:latest"
26+
27+
@property
28+
def _llm_type(self) -> str:
29+
return "open_webui"
30+
31+
def _call(
32+
self,
33+
prompt: str,
34+
stop: Optional[List[str]] = None,
35+
run_manager: Optional[CallbackManagerForLLMRun] = None,
36+
**kwargs: Any,
37+
) -> str:
38+
headers = {
39+
"Authorization": f"Bearer {self.api_key}",
40+
"Content-Type": "application/json",
41+
}
42+
43+
payload = {
44+
"model": self.model_name,
45+
"messages": [{"role": "user", "content": prompt}]
46+
}
47+
48+
try:
49+
logger.info(f"Sending request: {payload}")
50+
response = requests.post(self.api_url, headers=headers, json=payload, timeout=30)
51+
response.raise_for_status()
52+
result = response.json()
53+
content = result["choices"][0]["message"]["content"]
54+
return content.strip()
55+
except Exception as e:
56+
logger.error(f"LLM call failed: {e}")
57+
raise
58+
59+
# LLM Instance
60+
llm = OpenWebUILLM()
61+
62+
# Request/Response models
63+
class TextRequest(BaseModel):
64+
user_text: List[str]
65+
66+
class TextResponse(BaseModel):
67+
llm_response: str
68+
69+
# Routes
670
@router.get("/")
771
def read_root():
872
return {"Hello": "World"}
73+
74+
@router.post("/completion", response_model=TextResponse)
75+
async def complete_text(request: TextRequest):
76+
prompt = f"Complete the following text naturally:\n{' '.join(request.user_text)}"
77+
try:
78+
result = llm(prompt)
79+
return TextResponse(llm_response=result)
80+
except Exception as e:
81+
raise HTTPException(status_code=500, detail=str(e))
82+
83+
@router.post("/summarization", response_model=TextResponse)
84+
async def summarize_text(request: TextRequest):
85+
prompt = f"Summarize the following text concisely:\n{' '.join(request.user_text)}"
86+
try:
87+
result = llm(prompt)
88+
return TextResponse(llm_response=result)
89+
except Exception as e:
90+
raise HTTPException(status_code=500, detail=str(e))
91+
92+
@router.post("/rephrase", response_model=TextResponse)
93+
async def rephrase_text(request: TextRequest):
94+
prompt = f"Rephrase the following text while maintaining its meaning:\n{' '.join(request.user_text)}"
95+
try:
96+
result = llm(prompt)
97+
return TextResponse(llm_response=result)
98+
except Exception as e:
99+
raise HTTPException(status_code=500, detail=str(e))
100+
101+
@router.get("/health")
102+
async def health_check():
103+
return {
104+
"status": "healthy",
105+
"model": llm.model_name,
106+
"api_url": llm.api_url
107+
}

0 commit comments

Comments
 (0)