Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
130d15a2
Commit
130d15a2
authored
May 28, 2024
by
Timothy J. Baek
Browse files
feat: pipeline valves
parent
0bef1b44
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
265 additions
and
14 deletions
+265
-14
backend/main.py
backend/main.py
+116
-0
src/lib/apis/index.ts
src/lib/apis/index.ts
+89
-0
src/lib/components/admin/Settings/Pipelines.svelte
src/lib/components/admin/Settings/Pipelines.svelte
+60
-14
No files found.
backend/main.py
View file @
130d15a2
...
@@ -471,6 +471,122 @@ async def get_pipelines(user=Depends(get_admin_user)):
...
@@ -471,6 +471,122 @@ async def get_pipelines(user=Depends(get_admin_user)):
return
{
"data"
:
pipelines
}
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
]
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
}
"
)
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
"Pipeline not found"
,
)
return
{
"data"
:
pipeline
[
"pipeline"
][
"valves"
]}
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
]
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
}
"
)
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
"Pipeline not found"
,
)
return
{
"data"
:
pipeline
[
"pipeline"
][
"valves"
]}
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
]
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
}
"
)
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
"Pipeline not found"
,
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
"Pipeline not found"
,
)
@
app
.
get
(
"/api/config"
)
@
app
.
get
(
"/api/config"
)
async
def
get_app_config
():
async
def
get_app_config
():
# Checking and Handling the Absence of 'ui' in CONFIG_DATA
# Checking and Handling the Absence of 'ui' in CONFIG_DATA
...
...
src/lib/apis/index.ts
View file @
130d15a2
...
@@ -78,6 +78,95 @@ export const getPipelines = async (token: string = '') => {
...
@@ -78,6 +78,95 @@ export const getPipelines = async (token: string = '') => {
return
pipelines
;
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
);
error
=
err
;
return
null
;
});
if
(
error
)
{
throw
error
;
}
return
res
;
};
export
const
getBackendConfig
=
async
()
=>
{
export
const
getBackendConfig
=
async
()
=>
{
let
error
=
null
;
let
error
=
null
;
...
...
src/lib/components/admin/Settings/Pipelines.svelte
View file @
130d15a2
...
@@ -2,21 +2,33 @@
...
@@ -2,21 +2,33 @@
import { v4 as uuidv4 } from 'uuid';
import { v4 as uuidv4 } from 'uuid';
import { getContext, onMount } from 'svelte';
import { getContext, onMount } from 'svelte';
import { models } from '$lib/stores';
import type { Writable } from 'svelte/store';
import type { Writable } from 'svelte/store';
import type { i18n as i18nType } from 'i18next';
import type { i18n as i18nType } from 'i18next';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Switch from '$lib/components/common/Switch.svelte';
import { stringify } from 'postcss';
import { stringify } from 'postcss';
import { getPipelines } from '$lib/apis';
import { getPipelineValves, getPipelines } from '$lib/apis';
import Spinner from '$lib/components/common/Spinner.svelte';
const i18n: Writable<i18nType> = getContext('i18n');
const i18n: Writable<i18nType> = getContext('i18n');
export let saveHandler: Function;
export let saveHandler: Function;
let pipelines = [];
let pipelines = null;
let valves = null;
let selectedPipelineIdx = 0;
let selectedPipelineIdx = 0;
$: if (
pipelines !== null &&
pipelines.length > 0 &&
pipelines[selectedPipelineIdx] !== undefined &&
pipelines[selectedPipelineIdx].pipeline.valves
) {
(async () => {
valves = await getPipelineValves(localStorage.token, pipelines[selectedPipelineIdx].id);
})();
}
onMount(async () => {
onMount(async () => {
pipelines = await getPipelines(localStorage.token);
pipelines = await getPipelines(localStorage.token);
});
});
...
@@ -28,21 +40,55 @@
...
@@ -28,21 +40,55 @@
saveHandler();
saveHandler();
}}
}}
>
>
<div class=" space-y-
3
pr-1.5 overflow-y-scroll max-h-80 h-full">
<div class=" space-y-
2
pr-1.5 overflow-y-scroll max-h-80 h-full">
<div class=" space-y-3 pr-1.5">
{#if pipelines !== null}
<div class="flex w-full justify-between mb-2">
<div class="flex w-full justify-between mb-2">
<div class=" self-center text-sm font-semibold">
<div class=" self-center text-sm font-semibold">
{$i18n.t('Pipeline
Valve
s')}
{$i18n.t('Pipelines')}
</div>
</div>
</div>
</div>
<div class="flex flex-col space-y-1">
<div class="space-y-2">
{#each pipelines as pipeline}
{#if pipelines.length > 0}
<div class=" flex justify-between">
<div class="flex gap-2">
{JSON.stringify(pipeline)}
<div class="flex-1 pb-1">
<select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedPipelineIdx}
placeholder={$i18n.t('Select an Ollama instance')}
>
{#each pipelines as pipeline, idx}
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{pipeline.name}</option>
{/each}
</select>
</div>
</div>
</div>
{/each}
{/if}
<div class="text-sm font-medium">{$i18n.t('Valves')}</div>
<div class="space-y-2">
{#if pipelines[selectedPipelineIdx].pipeline.valves}
{#if valves}
{#each Object.keys(valves) as valve, idx}
<div>{valve}</div>
{/each}
{:else}
<Spinner className="size-5" />
{/if}
{:else}
<div>No valves</div>
{/if}
</div>
</div>
{:else if pipelines !== null && pipelines.length === 0}
<div>Pipelines Not Detected</div>
{:else}
<div class="flex h-full justify-center">
<div class="my-auto">
<Spinner className="size-6" />
</div>
</div>
</div>
</div>
{/if}
</div>
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
<div class="flex justify-end pt-3 text-sm font-medium">
<button
<button
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment