diff --git a/backend/main.py b/backend/main.py index 3bb4f0f6b..f3d8371b0 100644 --- a/backend/main.py +++ b/backend/main.py @@ -12,6 +12,7 @@ import mimetypes from fastapi import FastAPI, Request, Depends, status from fastapi.staticfiles import StaticFiles +from fastapi.responses import JSONResponse from fastapi import HTTPException from fastapi.middleware.wsgi import WSGIMiddleware from fastapi.middleware.cors import CORSMiddleware @@ -123,15 +124,6 @@ app.state.MODELS = {} origins = ["*"] -app.add_middleware( - CORSMiddleware, - allow_origins=origins, - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - - # Custom middleware to add security headers # class SecurityHeadersMiddleware(BaseHTTPMiddleware): # async def dispatch(self, request: Request, call_next): @@ -253,6 +245,7 @@ class PipelineMiddleware(BaseHTTPMiddleware): model for model in app.state.MODELS.values() if "pipeline" in model + and "type" in model["pipeline"] and model["pipeline"]["type"] == "filter" and ( model["pipeline"]["pipelines"] == ["*"] @@ -276,10 +269,8 @@ class PipelineMiddleware(BaseHTTPMiddleware): except: pass - print(sorted_filters) - for filter in sorted_filters: - + r = None try: urlIdx = filter["urlIdx"] @@ -289,11 +280,10 @@ class PipelineMiddleware(BaseHTTPMiddleware): if key != "": headers = {"Authorization": f"Bearer {key}"} r = requests.post( - f"{url}/filter", + f"{url}/{filter['id']}/filter", headers=headers, json={ "user": user, - "model": filter["id"], "body": data, }, ) @@ -303,7 +293,20 @@ class PipelineMiddleware(BaseHTTPMiddleware): except Exception as e: # Handle connection error here print(f"Connection error: {e}") - pass + + if r is not None: + try: + res = r.json() + if "detail" in res: + return JSONResponse( + status_code=r.status_code, + content=res, + ) + except: + pass + + else: + pass modified_body_bytes = json.dumps(data).encode("utf-8") # Replace the request body with the modified one @@ -328,6 +331,15 @@ class PipelineMiddleware(BaseHTTPMiddleware): app.add_middleware(PipelineMiddleware) +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + @app.middleware("http") async def check_url(request: Request, call_next): if len(app.state.MODELS) == 0: @@ -436,7 +448,7 @@ async def get_models(user=Depends(get_verified_user)): models = [ model for model in models - if "pipeline" not in model or model["pipeline"]["type"] != "filter" + if "pipeline" not in model or model["pipeline"].get("type", None) != "filter" ] if app.state.config.ENABLE_MODEL_FILTER: @@ -452,6 +464,164 @@ async def get_models(user=Depends(get_verified_user)): return {"data": models} +@app.get("/api/pipelines") +async def get_pipelines(user=Depends(get_admin_user)): + models = await get_all_models() + pipelines = [model for model in models if "pipeline" in model] + return {"data": pipelines} + + +@app.get("/api/pipelines/{pipeline_id}/valves") +async def get_pipeline_valves(pipeline_id: str, user=Depends(get_admin_user)): + models = await get_all_models() + if pipeline_id in app.state.MODELS and "pipeline" in app.state.MODELS[pipeline_id]: + pipeline = app.state.MODELS[pipeline_id] + + r = None + try: + urlIdx = pipeline["urlIdx"] + + url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx] + key = openai_app.state.config.OPENAI_API_KEYS[urlIdx] + + if key != "": + headers = {"Authorization": f"Bearer {key}"} + r = requests.get(f"{url}/{pipeline['id']}/valves", headers=headers) + + r.raise_for_status() + data = r.json() + + return {**data} + except Exception as e: + # Handle connection error here + print(f"Connection error: {e}") + + detail = "Pipeline not found" + + if r is not None: + try: + res = r.json() + if "detail" in res: + detail = res["detail"] + except: + pass + + raise HTTPException( + status_code=( + r.status_code if r is not None else status.HTTP_404_NOT_FOUND + ), + detail=detail, + ) + + else: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Pipeline not found", + ) + + +@app.get("/api/pipelines/{pipeline_id}/valves/spec") +async def get_pipeline_valves_spec(pipeline_id: str, user=Depends(get_admin_user)): + models = await get_all_models() + if pipeline_id in app.state.MODELS and "pipeline" in app.state.MODELS[pipeline_id]: + pipeline = app.state.MODELS[pipeline_id] + + r = None + try: + urlIdx = pipeline["urlIdx"] + + url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx] + key = openai_app.state.config.OPENAI_API_KEYS[urlIdx] + + if key != "": + headers = {"Authorization": f"Bearer {key}"} + r = requests.get(f"{url}/{pipeline['id']}/valves/spec", headers=headers) + + r.raise_for_status() + data = r.json() + + return {**data} + except Exception as e: + # Handle connection error here + print(f"Connection error: {e}") + + detail = "Pipeline not found" + if r is not None: + try: + res = r.json() + if "detail" in res: + detail = res["detail"] + except: + pass + + raise HTTPException( + status_code=( + r.status_code if r is not None else status.HTTP_404_NOT_FOUND + ), + detail=detail, + ) + else: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Pipeline not found", + ) + + +@app.post("/api/pipelines/{pipeline_id}/valves/update") +async def update_pipeline_valves( + pipeline_id: str, form_data: dict, user=Depends(get_admin_user) +): + models = await get_all_models() + + if pipeline_id in app.state.MODELS and "pipeline" in app.state.MODELS[pipeline_id]: + pipeline = app.state.MODELS[pipeline_id] + + r = None + try: + urlIdx = pipeline["urlIdx"] + + url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx] + key = openai_app.state.config.OPENAI_API_KEYS[urlIdx] + + if key != "": + headers = {"Authorization": f"Bearer {key}"} + r = requests.post( + f"{url}/{pipeline['id']}/valves/update", + headers=headers, + json={**form_data}, + ) + + r.raise_for_status() + data = r.json() + + return {**data} + except Exception as e: + # Handle connection error here + print(f"Connection error: {e}") + + detail = "Pipeline not found" + + if r is not None: + try: + res = r.json() + if "detail" in res: + detail = res["detail"] + except: + pass + + raise HTTPException( + status_code=( + r.status_code if r is not None else status.HTTP_404_NOT_FOUND + ), + detail=detail, + ) + else: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Pipeline not found", + ) + + @app.get("/api/config") async def get_app_config(): # Checking and Handling the Absence of 'ui' in CONFIG_DATA diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts index dc51abd52..ffa5c6bbb 100644 --- a/src/lib/apis/index.ts +++ b/src/lib/apis/index.ts @@ -49,6 +49,129 @@ export const getModels = async (token: string = '') => { return models; }; +export const getPipelines = async (token: string = '') => { + let error = null; + + const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + error = err; + return null; + }); + + if (error) { + throw error; + } + + let pipelines = res?.data ?? []; + return pipelines; +}; + +export const getPipelineValves = async (token: string = '', pipeline_id: string) => { + let error = null; + + const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + error = err; + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + +export const getPipelineValvesSpec = async (token: string = '', pipeline_id: string) => { + let error = null; + + const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves/spec`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + error = err; + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + +export const updatePipelineValves = async ( + token: string = '', + pipeline_id: string, + valves: object +) => { + let error = null; + + const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves/update`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify(valves) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + + if ('detail' in err) { + error = err.detail; + } else { + error = err; + } + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const getBackendConfig = async () => { let error = null; diff --git a/src/lib/components/admin/Settings/Pipelines.svelte b/src/lib/components/admin/Settings/Pipelines.svelte new file mode 100644 index 000000000..dda33d055 --- /dev/null +++ b/src/lib/components/admin/Settings/Pipelines.svelte @@ -0,0 +1,168 @@ + + +
diff --git a/src/lib/components/admin/SettingsModal.svelte b/src/lib/components/admin/SettingsModal.svelte index 38a2602b6..78f48cdfc 100644 --- a/src/lib/components/admin/SettingsModal.svelte +++ b/src/lib/components/admin/SettingsModal.svelte @@ -8,6 +8,7 @@ import Banners from '$lib/components/admin/Settings/Banners.svelte'; import { toast } from 'svelte-sonner'; + import Pipelines from './Settings/Pipelines.svelte'; const i18n = getContext('i18n'); @@ -149,33 +150,65 @@