diff --git a/backend/main.py b/backend/main.py index 714080830..542a06071 100644 --- a/backend/main.py +++ b/backend/main.py @@ -480,6 +480,92 @@ async def get_pipelines_list(user=Depends(get_admin_user)): } +class AddPipelineForm(BaseModel): + url: str + urlIdx: int + + +@app.post("/api/pipelines/add") +async def add_pipeline(form_data: AddPipelineForm, user=Depends(get_admin_user)): + + r = None + try: + urlIdx = form_data.urlIdx + + url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx] + key = openai_app.state.config.OPENAI_API_KEYS[urlIdx] + + headers = {"Authorization": f"Bearer {key}"} + r = requests.post( + f"{url}/pipelines/add", headers=headers, json={"url": form_data.url} + ) + + 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, + ) + + +class DeletePipelineForm(BaseModel): + id: str + urlIdx: int + + +@app.delete("/api/pipelines/delete") +async def delete_pipeline(form_data: DeletePipelineForm, user=Depends(get_admin_user)): + + r = None + try: + urlIdx = form_data.urlIdx + + url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx] + key = openai_app.state.config.OPENAI_API_KEYS[urlIdx] + + headers = {"Authorization": f"Bearer {key}"} + r = requests.delete( + f"{url}/pipelines/delete", headers=headers, json={"id": form_data.id} + ) + + 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, + ) + + @app.get("/api/pipelines") async def get_pipelines(urlIdx: Optional[int] = None, user=Depends(get_admin_user)): models = await get_all_models() diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts index b7f5fabb2..f2150fdd6 100644 --- a/src/lib/apis/index.ts +++ b/src/lib/apis/index.ts @@ -78,6 +78,78 @@ export const getPipelinesList = async (token: string = '') => { return pipelines; }; +export const downloadPipeline = async (token: string, url: string, urlIdx: string) => { + let error = null; + + const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/add`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + url: url, + urlIdx: urlIdx + }) + }) + .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 deletePipeline = async (token: string, id: string, urlIdx: string) => { + let error = null; + + const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/delete`, { + method: 'DELETE', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + id: id, + urlIdx: urlIdx + }) + }) + .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 getPipelines = async (token: string, urlIdx?: string) => { let error = null; diff --git a/src/lib/components/admin/Settings/Pipelines.svelte b/src/lib/components/admin/Settings/Pipelines.svelte index 3e7f5ee75..0e0d6f976 100644 --- a/src/lib/components/admin/Settings/Pipelines.svelte +++ b/src/lib/components/admin/Settings/Pipelines.svelte @@ -12,7 +12,9 @@ updatePipelineValves, getPipelines, getModels, - getPipelinesList + getPipelinesList, + downloadPipeline, + deletePipeline } from '$lib/apis'; import Spinner from '$lib/components/common/Spinner.svelte'; @@ -21,6 +23,8 @@ export let saveHandler: Function; + let downloading = false; + let PIPELINES_LIST = null; let selectedPipelinesUrlIdx = ''; @@ -30,6 +34,8 @@ let valves_spec = null; let selectedPipelineIdx = null; + let pipelineDownloadUrl = ''; + const updateHandler = async () => { const pipeline = pipelines[selectedPipelineIdx]; @@ -80,6 +86,43 @@ } }; + const addPipelineHandler = async () => { + downloading = true; + const res = await downloadPipeline( + localStorage.token, + pipelineDownloadUrl, + selectedPipelinesUrlIdx + ).catch((error) => { + toast.error(error); + return null; + }); + + if (res) { + toast.success('Pipeline downloaded successfully'); + setPipelines(); + models.set(await getModels(localStorage.token)); + } + + downloading = false; + }; + + const deletePipelineHandler = async () => { + const res = await deletePipeline( + localStorage.token, + pipelines[selectedPipelineIdx].id, + selectedPipelinesUrlIdx + ).catch((error) => { + toast.error(error); + return null; + }); + + if (res) { + toast.success('Pipeline deleted successfully'); + setPipelines(); + models.set(await getModels(localStorage.token)); + } + }; + onMount(async () => { PIPELINES_LIST = await getPipelinesList(localStorage.token); @@ -97,7 +140,7 @@ updateHandler(); }} > -
+
{#if PIPELINES_LIST !== null}
@@ -133,6 +176,75 @@
{/if} +
+
+ {$i18n.t('Install from Github URL')} +
+
+
+ +
+ +
+
+
{#if pipelines !== null} @@ -145,7 +257,7 @@
{#if pipelines.length > 0}
-
+