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
adf9ccb5
Unverified
Commit
adf9ccb5
authored
Mar 23, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Mar 23, 2024
Browse files
Merge pull request #1275 from open-webui/comfyui
feat: comfyui support
parents
cbaaacfb
98624a40
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
434 additions
and
39 deletions
+434
-39
backend/apps/images/main.py
backend/apps/images/main.py
+102
-12
backend/apps/images/utils/comfyui.py
backend/apps/images/utils/comfyui.py
+228
-0
backend/config.py
backend/config.py
+1
-0
src/lib/apis/images/index.ts
src/lib/apis/images/index.ts
+5
-5
src/lib/components/chat/Settings/Images.svelte
src/lib/components/chat/Settings/Images.svelte
+81
-18
src/lib/components/common/ImagePreview.svelte
src/lib/components/common/ImagePreview.svelte
+17
-4
No files found.
backend/apps/images/main.py
View file @
adf9ccb5
...
@@ -18,6 +18,8 @@ from utils.utils import (
...
@@ -18,6 +18,8 @@ from utils.utils import (
get_current_user
,
get_current_user
,
get_admin_user
,
get_admin_user
,
)
)
from
apps.images.utils.comfyui
import
ImageGenerationPayload
,
comfyui_generate_image
from
utils.misc
import
calculate_sha256
from
utils.misc
import
calculate_sha256
from
typing
import
Optional
from
typing
import
Optional
from
pydantic
import
BaseModel
from
pydantic
import
BaseModel
...
@@ -26,7 +28,7 @@ import uuid
...
@@ -26,7 +28,7 @@ import uuid
import
base64
import
base64
import
json
import
json
from
config
import
CACHE_DIR
,
AUTOMATIC1111_BASE_URL
from
config
import
CACHE_DIR
,
AUTOMATIC1111_BASE_URL
,
COMFYUI_BASE_URL
IMAGE_CACHE_DIR
=
Path
(
CACHE_DIR
).
joinpath
(
"./image/generations/"
)
IMAGE_CACHE_DIR
=
Path
(
CACHE_DIR
).
joinpath
(
"./image/generations/"
)
...
@@ -49,6 +51,8 @@ app.state.MODEL = ""
...
@@ -49,6 +51,8 @@ app.state.MODEL = ""
app
.
state
.
AUTOMATIC1111_BASE_URL
=
AUTOMATIC1111_BASE_URL
app
.
state
.
AUTOMATIC1111_BASE_URL
=
AUTOMATIC1111_BASE_URL
app
.
state
.
COMFYUI_BASE_URL
=
COMFYUI_BASE_URL
app
.
state
.
IMAGE_SIZE
=
"512x512"
app
.
state
.
IMAGE_SIZE
=
"512x512"
app
.
state
.
IMAGE_STEPS
=
50
app
.
state
.
IMAGE_STEPS
=
50
...
@@ -71,32 +75,48 @@ async def update_config(form_data: ConfigUpdateForm, user=Depends(get_admin_user
...
@@ -71,32 +75,48 @@ async def update_config(form_data: ConfigUpdateForm, user=Depends(get_admin_user
return
{
"engine"
:
app
.
state
.
ENGINE
,
"enabled"
:
app
.
state
.
ENABLED
}
return
{
"engine"
:
app
.
state
.
ENGINE
,
"enabled"
:
app
.
state
.
ENABLED
}
class
UrlUpdateForm
(
BaseModel
):
class
EngineUrlUpdateForm
(
BaseModel
):
url
:
str
AUTOMATIC1111_BASE_URL
:
Optional
[
str
]
=
None
COMFYUI_BASE_URL
:
Optional
[
str
]
=
None
@
app
.
get
(
"/url"
)
@
app
.
get
(
"/url"
)
async
def
get_automatic1111_url
(
user
=
Depends
(
get_admin_user
)):
async
def
get_engine_url
(
user
=
Depends
(
get_admin_user
)):
return
{
"AUTOMATIC1111_BASE_URL"
:
app
.
state
.
AUTOMATIC1111_BASE_URL
}
return
{
"AUTOMATIC1111_BASE_URL"
:
app
.
state
.
AUTOMATIC1111_BASE_URL
,
"COMFYUI_BASE_URL"
:
app
.
state
.
COMFYUI_BASE_URL
,
}
@
app
.
post
(
"/url/update"
)
@
app
.
post
(
"/url/update"
)
async
def
update_
automatic1111
_url
(
async
def
update_
engine
_url
(
form_data
:
UrlUpdateForm
,
user
=
Depends
(
get_admin_user
)
form_data
:
Engine
UrlUpdateForm
,
user
=
Depends
(
get_admin_user
)
):
):
if
form_data
.
url
==
""
:
if
form_data
.
AUTOMATIC1111_BASE_URL
==
None
:
app
.
state
.
AUTOMATIC1111_BASE_URL
=
AUTOMATIC1111_BASE_URL
app
.
state
.
AUTOMATIC1111_BASE_URL
=
AUTOMATIC1111_BASE_URL
else
:
else
:
url
=
form_data
.
url
.
strip
(
"/"
)
url
=
form_data
.
AUTOMATIC1111_BASE_URL
.
strip
(
"/"
)
try
:
try
:
r
=
requests
.
head
(
url
)
r
=
requests
.
head
(
url
)
app
.
state
.
AUTOMATIC1111_BASE_URL
=
url
app
.
state
.
AUTOMATIC1111_BASE_URL
=
url
except
Exception
as
e
:
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
400
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
))
raise
HTTPException
(
status_code
=
400
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
))
if
form_data
.
COMFYUI_BASE_URL
==
None
:
app
.
state
.
COMFYUI_BASE_URL
=
COMFYUI_BASE_URL
else
:
url
=
form_data
.
COMFYUI_BASE_URL
.
strip
(
"/"
)
try
:
r
=
requests
.
head
(
url
)
app
.
state
.
COMFYUI_BASE_URL
=
url
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
400
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
))
return
{
return
{
"AUTOMATIC1111_BASE_URL"
:
app
.
state
.
AUTOMATIC1111_BASE_URL
,
"AUTOMATIC1111_BASE_URL"
:
app
.
state
.
AUTOMATIC1111_BASE_URL
,
"COMFYUI_BASE_URL"
:
app
.
state
.
COMFYUI_BASE_URL
,
"status"
:
True
,
"status"
:
True
,
}
}
...
@@ -186,6 +206,18 @@ def get_models(user=Depends(get_current_user)):
...
@@ -186,6 +206,18 @@ def get_models(user=Depends(get_current_user)):
{
"id"
:
"dall-e-2"
,
"name"
:
"DALL·E 2"
},
{
"id"
:
"dall-e-2"
,
"name"
:
"DALL·E 2"
},
{
"id"
:
"dall-e-3"
,
"name"
:
"DALL·E 3"
},
{
"id"
:
"dall-e-3"
,
"name"
:
"DALL·E 3"
},
]
]
elif
app
.
state
.
ENGINE
==
"comfyui"
:
r
=
requests
.
get
(
url
=
f
"
{
app
.
state
.
COMFYUI_BASE_URL
}
/object_info"
)
info
=
r
.
json
()
return
list
(
map
(
lambda
model
:
{
"id"
:
model
,
"name"
:
model
},
info
[
"CheckpointLoaderSimple"
][
"input"
][
"required"
][
"ckpt_name"
][
0
],
)
)
else
:
else
:
r
=
requests
.
get
(
r
=
requests
.
get
(
url
=
f
"
{
app
.
state
.
AUTOMATIC1111_BASE_URL
}
/sdapi/v1/sd-models"
url
=
f
"
{
app
.
state
.
AUTOMATIC1111_BASE_URL
}
/sdapi/v1/sd-models"
...
@@ -207,6 +239,8 @@ async def get_default_model(user=Depends(get_admin_user)):
...
@@ -207,6 +239,8 @@ async def get_default_model(user=Depends(get_admin_user)):
try
:
try
:
if
app
.
state
.
ENGINE
==
"openai"
:
if
app
.
state
.
ENGINE
==
"openai"
:
return
{
"model"
:
app
.
state
.
MODEL
if
app
.
state
.
MODEL
else
"dall-e-2"
}
return
{
"model"
:
app
.
state
.
MODEL
if
app
.
state
.
MODEL
else
"dall-e-2"
}
elif
app
.
state
.
ENGINE
==
"comfyui"
:
return
{
"model"
:
app
.
state
.
MODEL
if
app
.
state
.
MODEL
else
""
}
else
:
else
:
r
=
requests
.
get
(
url
=
f
"
{
app
.
state
.
AUTOMATIC1111_BASE_URL
}
/sdapi/v1/options"
)
r
=
requests
.
get
(
url
=
f
"
{
app
.
state
.
AUTOMATIC1111_BASE_URL
}
/sdapi/v1/options"
)
options
=
r
.
json
()
options
=
r
.
json
()
...
@@ -221,10 +255,12 @@ class UpdateModelForm(BaseModel):
...
@@ -221,10 +255,12 @@ class UpdateModelForm(BaseModel):
def
set_model_handler
(
model
:
str
):
def
set_model_handler
(
model
:
str
):
if
app
.
state
.
ENGINE
==
"openai"
:
if
app
.
state
.
ENGINE
==
"openai"
:
app
.
state
.
MODEL
=
model
app
.
state
.
MODEL
=
model
return
app
.
state
.
MODEL
return
app
.
state
.
MODEL
if
app
.
state
.
ENGINE
==
"comfyui"
:
app
.
state
.
MODEL
=
model
return
app
.
state
.
MODEL
else
:
else
:
r
=
requests
.
get
(
url
=
f
"
{
app
.
state
.
AUTOMATIC1111_BASE_URL
}
/sdapi/v1/options"
)
r
=
requests
.
get
(
url
=
f
"
{
app
.
state
.
AUTOMATIC1111_BASE_URL
}
/sdapi/v1/options"
)
options
=
r
.
json
()
options
=
r
.
json
()
...
@@ -272,12 +308,31 @@ def save_b64_image(b64_str):
...
@@ -272,12 +308,31 @@ def save_b64_image(b64_str):
return
None
return
None
def
save_url_image
(
url
):
image_id
=
str
(
uuid
.
uuid4
())
file_path
=
IMAGE_CACHE_DIR
.
joinpath
(
f
"
{
image_id
}
.png"
)
try
:
r
=
requests
.
get
(
url
)
r
.
raise_for_status
()
with
open
(
file_path
,
"wb"
)
as
image_file
:
image_file
.
write
(
r
.
content
)
return
image_id
except
Exception
as
e
:
print
(
f
"Error saving image:
{
e
}
"
)
return
None
@
app
.
post
(
"/generations"
)
@
app
.
post
(
"/generations"
)
def
generate_image
(
def
generate_image
(
form_data
:
GenerateImageForm
,
form_data
:
GenerateImageForm
,
user
=
Depends
(
get_current_user
),
user
=
Depends
(
get_current_user
),
):
):
width
,
height
=
tuple
(
map
(
int
,
app
.
state
.
IMAGE_SIZE
.
split
(
"x"
)))
r
=
None
r
=
None
try
:
try
:
if
app
.
state
.
ENGINE
==
"openai"
:
if
app
.
state
.
ENGINE
==
"openai"
:
...
@@ -315,12 +370,47 @@ def generate_image(
...
@@ -315,12 +370,47 @@ def generate_image(
return
images
return
images
elif
app
.
state
.
ENGINE
==
"comfyui"
:
data
=
{
"prompt"
:
form_data
.
prompt
,
"width"
:
width
,
"height"
:
height
,
"n"
:
form_data
.
n
,
}
if
app
.
state
.
IMAGE_STEPS
!=
None
:
data
[
"steps"
]
=
app
.
state
.
IMAGE_STEPS
if
form_data
.
negative_prompt
!=
None
:
data
[
"negative_prompt"
]
=
form_data
.
negative_prompt
data
=
ImageGenerationPayload
(
**
data
)
res
=
comfyui_generate_image
(
app
.
state
.
MODEL
,
data
,
user
.
id
,
app
.
state
.
COMFYUI_BASE_URL
,
)
print
(
res
)
images
=
[]
for
image
in
res
[
"data"
]:
image_id
=
save_url_image
(
image
[
"url"
])
images
.
append
({
"url"
:
f
"/cache/image/generations/
{
image_id
}
.png"
})
file_body_path
=
IMAGE_CACHE_DIR
.
joinpath
(
f
"
{
image_id
}
.json"
)
with
open
(
file_body_path
,
"w"
)
as
f
:
json
.
dump
(
data
.
model_dump
(
exclude_none
=
True
),
f
)
print
(
images
)
return
images
else
:
else
:
if
form_data
.
model
:
if
form_data
.
model
:
set_model_handler
(
form_data
.
model
)
set_model_handler
(
form_data
.
model
)
width
,
height
=
tuple
(
map
(
int
,
app
.
state
.
IMAGE_SIZE
.
split
(
"x"
)))
data
=
{
data
=
{
"prompt"
:
form_data
.
prompt
,
"prompt"
:
form_data
.
prompt
,
"batch_size"
:
form_data
.
n
,
"batch_size"
:
form_data
.
n
,
...
...
backend/apps/images/utils/comfyui.py
0 → 100644
View file @
adf9ccb5
import
websocket
# NOTE: websocket-client (https://github.com/websocket-client/websocket-client)
import
uuid
import
json
import
urllib.request
import
urllib.parse
import
random
from
pydantic
import
BaseModel
from
typing
import
Optional
COMFYUI_DEFAULT_PROMPT
=
"""
{
"3": {
"inputs": {
"seed": 0,
"steps": 20,
"cfg": 8,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1,
"model": [
"4",
0
],
"positive": [
"6",
0
],
"negative": [
"7",
0
],
"latent_image": [
"5",
0
]
},
"class_type": "KSampler",
"_meta": {
"title": "KSampler"
}
},
"4": {
"inputs": {
"ckpt_name": "model.safetensors"
},
"class_type": "CheckpointLoaderSimple",
"_meta": {
"title": "Load Checkpoint"
}
},
"5": {
"inputs": {
"width": 512,
"height": 512,
"batch_size": 1
},
"class_type": "EmptyLatentImage",
"_meta": {
"title": "Empty Latent Image"
}
},
"6": {
"inputs": {
"text": "Prompt",
"clip": [
"4",
1
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"7": {
"inputs": {
"text": "Negative Prompt",
"clip": [
"4",
1
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"8": {
"inputs": {
"samples": [
"3",
0
],
"vae": [
"4",
2
]
},
"class_type": "VAEDecode",
"_meta": {
"title": "VAE Decode"
}
},
"9": {
"inputs": {
"filename_prefix": "ComfyUI",
"images": [
"8",
0
]
},
"class_type": "SaveImage",
"_meta": {
"title": "Save Image"
}
}
}
"""
def
queue_prompt
(
prompt
,
client_id
,
base_url
):
print
(
"queue_prompt"
)
p
=
{
"prompt"
:
prompt
,
"client_id"
:
client_id
}
data
=
json
.
dumps
(
p
).
encode
(
"utf-8"
)
req
=
urllib
.
request
.
Request
(
f
"
{
base_url
}
/prompt"
,
data
=
data
)
return
json
.
loads
(
urllib
.
request
.
urlopen
(
req
).
read
())
def
get_image
(
filename
,
subfolder
,
folder_type
,
base_url
):
print
(
"get_image"
)
data
=
{
"filename"
:
filename
,
"subfolder"
:
subfolder
,
"type"
:
folder_type
}
url_values
=
urllib
.
parse
.
urlencode
(
data
)
with
urllib
.
request
.
urlopen
(
f
"
{
base_url
}
/view?
{
url_values
}
"
)
as
response
:
return
response
.
read
()
def
get_image_url
(
filename
,
subfolder
,
folder_type
,
base_url
):
print
(
"get_image"
)
data
=
{
"filename"
:
filename
,
"subfolder"
:
subfolder
,
"type"
:
folder_type
}
url_values
=
urllib
.
parse
.
urlencode
(
data
)
return
f
"
{
base_url
}
/view?
{
url_values
}
"
def
get_history
(
prompt_id
,
base_url
):
print
(
"get_history"
)
with
urllib
.
request
.
urlopen
(
f
"
{
base_url
}
/history/
{
prompt_id
}
"
)
as
response
:
return
json
.
loads
(
response
.
read
())
def
get_images
(
ws
,
prompt
,
client_id
,
base_url
):
prompt_id
=
queue_prompt
(
prompt
,
client_id
,
base_url
)[
"prompt_id"
]
output_images
=
[]
while
True
:
out
=
ws
.
recv
()
if
isinstance
(
out
,
str
):
message
=
json
.
loads
(
out
)
if
message
[
"type"
]
==
"executing"
:
data
=
message
[
"data"
]
if
data
[
"node"
]
is
None
and
data
[
"prompt_id"
]
==
prompt_id
:
break
# Execution is done
else
:
continue
# previews are binary data
history
=
get_history
(
prompt_id
,
base_url
)[
prompt_id
]
for
o
in
history
[
"outputs"
]:
for
node_id
in
history
[
"outputs"
]:
node_output
=
history
[
"outputs"
][
node_id
]
if
"images"
in
node_output
:
for
image
in
node_output
[
"images"
]:
url
=
get_image_url
(
image
[
"filename"
],
image
[
"subfolder"
],
image
[
"type"
],
base_url
)
output_images
.
append
({
"url"
:
url
})
return
{
"data"
:
output_images
}
class
ImageGenerationPayload
(
BaseModel
):
prompt
:
str
negative_prompt
:
Optional
[
str
]
=
""
steps
:
Optional
[
int
]
=
None
seed
:
Optional
[
int
]
=
None
width
:
int
height
:
int
n
:
int
=
1
def
comfyui_generate_image
(
model
:
str
,
payload
:
ImageGenerationPayload
,
client_id
,
base_url
):
host
=
base_url
.
replace
(
"http://"
,
""
).
replace
(
"https://"
,
""
)
comfyui_prompt
=
json
.
loads
(
COMFYUI_DEFAULT_PROMPT
)
comfyui_prompt
[
"4"
][
"inputs"
][
"ckpt_name"
]
=
model
comfyui_prompt
[
"5"
][
"inputs"
][
"batch_size"
]
=
payload
.
n
comfyui_prompt
[
"5"
][
"inputs"
][
"width"
]
=
payload
.
width
comfyui_prompt
[
"5"
][
"inputs"
][
"height"
]
=
payload
.
height
# set the text prompt for our positive CLIPTextEncode
comfyui_prompt
[
"6"
][
"inputs"
][
"text"
]
=
payload
.
prompt
comfyui_prompt
[
"7"
][
"inputs"
][
"text"
]
=
payload
.
negative_prompt
if
payload
.
steps
:
comfyui_prompt
[
"3"
][
"inputs"
][
"steps"
]
=
payload
.
steps
comfyui_prompt
[
"3"
][
"inputs"
][
"seed"
]
=
(
payload
.
seed
if
payload
.
seed
else
random
.
randint
(
0
,
18446744073709551614
)
)
try
:
ws
=
websocket
.
WebSocket
()
ws
.
connect
(
f
"ws://
{
host
}
/ws?clientId=
{
client_id
}
"
)
print
(
"WebSocket connection established."
)
except
Exception
as
e
:
print
(
f
"Failed to connect to WebSocket server:
{
e
}
"
)
return
None
try
:
images
=
get_images
(
ws
,
comfyui_prompt
,
client_id
,
base_url
)
except
Exception
as
e
:
print
(
f
"Error while receiving images:
{
e
}
"
)
images
=
None
ws
.
close
()
return
images
backend/config.py
View file @
adf9ccb5
...
@@ -376,3 +376,4 @@ WHISPER_MODEL_DIR = os.getenv("WHISPER_MODEL_DIR", f"{CACHE_DIR}/whisper/models"
...
@@ -376,3 +376,4 @@ WHISPER_MODEL_DIR = os.getenv("WHISPER_MODEL_DIR", f"{CACHE_DIR}/whisper/models"
####################################
####################################
AUTOMATIC1111_BASE_URL
=
os
.
getenv
(
"AUTOMATIC1111_BASE_URL"
,
""
)
AUTOMATIC1111_BASE_URL
=
os
.
getenv
(
"AUTOMATIC1111_BASE_URL"
,
""
)
COMFYUI_BASE_URL
=
os
.
getenv
(
"COMFYUI_BASE_URL"
,
""
)
src/lib/apis/images/index.ts
View file @
adf9ccb5
...
@@ -139,7 +139,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
...
@@ -139,7 +139,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
return
res
.
OPENAI_API_KEY
;
return
res
.
OPENAI_API_KEY
;
};
};
export
const
get
AUTOMATIC1111
Url
=
async
(
token
:
string
=
''
)
=>
{
export
const
get
ImageGenerationEngine
Url
s
=
async
(
token
:
string
=
''
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
IMAGES_API_BASE_URL
}
/url`
,
{
const
res
=
await
fetch
(
`
${
IMAGES_API_BASE_URL
}
/url`
,
{
...
@@ -168,10 +168,10 @@ export const getAUTOMATIC1111Url = async (token: string = '') => {
...
@@ -168,10 +168,10 @@ export const getAUTOMATIC1111Url = async (token: string = '') => {
throw
error
;
throw
error
;
}
}
return
res
.
AUTOMATIC1111_BASE_URL
;
return
res
;
};
};
export
const
update
AUTOMATIC1111
Url
=
async
(
token
:
string
=
''
,
url
:
string
)
=>
{
export
const
update
ImageGenerationEngine
Url
s
=
async
(
token
:
string
=
''
,
url
s
:
object
=
{}
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
IMAGES_API_BASE_URL
}
/url/update`
,
{
const
res
=
await
fetch
(
`
${
IMAGES_API_BASE_URL
}
/url/update`
,
{
...
@@ -182,7 +182,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) =>
...
@@ -182,7 +182,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) =>
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
},
},
body
:
JSON
.
stringify
({
body
:
JSON
.
stringify
({
url
:
url
...
urls
})
})
})
})
.
then
(
async
(
res
)
=>
{
.
then
(
async
(
res
)
=>
{
...
@@ -203,7 +203,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) =>
...
@@ -203,7 +203,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) =>
throw
error
;
throw
error
;
}
}
return
res
.
AUTOMATIC1111_BASE_URL
;
return
res
;
};
};
export
const
getImageSize
=
async
(
token
:
string
=
''
)
=>
{
export
const
getImageSize
=
async
(
token
:
string
=
''
)
=>
{
...
...
src/lib/components/chat/Settings/Images.svelte
View file @
adf9ccb5
...
@@ -4,14 +4,14 @@
...
@@ -4,14 +4,14 @@
import { createEventDispatcher, onMount, getContext } from 'svelte';
import { createEventDispatcher, onMount, getContext } from 'svelte';
import { config, user } from '$lib/stores';
import { config, user } from '$lib/stores';
import {
import {
getAUTOMATIC1111Url,
getImageGenerationModels,
getImageGenerationModels,
getDefaultImageGenerationModel,
getDefaultImageGenerationModel,
updateDefaultImageGenerationModel,
updateDefaultImageGenerationModel,
getImageSize,
getImageSize,
getImageGenerationConfig,
getImageGenerationConfig,
updateImageGenerationConfig,
updateImageGenerationConfig,
updateAUTOMATIC1111Url,
getImageGenerationEngineUrls,
updateImageGenerationEngineUrls,
updateImageSize,
updateImageSize,
getImageSteps,
getImageSteps,
updateImageSteps,
updateImageSteps,
...
@@ -31,6 +31,8 @@
...
@@ -31,6 +31,8 @@
let enableImageGeneration = false;
let enableImageGeneration = false;
let AUTOMATIC1111_BASE_URL = '';
let AUTOMATIC1111_BASE_URL = '';
let COMFYUI_BASE_URL = '';
let OPENAI_API_KEY = '';
let OPENAI_API_KEY = '';
let selectedModel = '';
let selectedModel = '';
...
@@ -49,16 +51,38 @@
...
@@ -49,16 +51,38 @@
});
});
};
};
const updateAUTOMATIC1111UrlHandler = async () => {
const updateUrlHandler = async () => {
const res = await updateAUTOMATIC1111Url(localStorage.token, AUTOMATIC1111_BASE_URL).catch(
if (imageGenerationEngine === 'comfyui') {
(error) => {
const res = await updateImageGenerationEngineUrls(localStorage.token, {
COMFYUI_BASE_URL: COMFYUI_BASE_URL
}).catch((error) => {
toast.error(error);
toast.error(error);
console.log(error);
return null;
return null;
});
if (res) {
COMFYUI_BASE_URL = res.COMFYUI_BASE_URL;
await getModels();
if (models) {
toast.success($i18n.t('Server connection verified'));
}
} else {
({ COMFYUI_BASE_URL } = await getImageGenerationEngineUrls(localStorage.token));
}
}
);
} else {
const res = await updateImageGenerationEngineUrls(localStorage.token, {
AUTOMATIC1111_BASE_URL: AUTOMATIC1111_BASE_URL
}).catch((error) => {
toast.error(error);
return null;
});
if (res) {
if (res) {
AUTOMATIC1111_BASE_URL = res;
AUTOMATIC1111_BASE_URL = res
.AUTOMATIC1111_BASE_URL
;
await getModels();
await getModels();
...
@@ -66,7 +90,8 @@
...
@@ -66,7 +90,8 @@
toast.success($i18n.t('Server connection verified'));
toast.success($i18n.t('Server connection verified'));
}
}
} else {
} else {
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
({ AUTOMATIC1111_BASE_URL } = await getImageGenerationEngineUrls(localStorage.token));
}
}
}
};
};
const updateImageGeneration = async () => {
const updateImageGeneration = async () => {
...
@@ -101,7 +126,11 @@
...
@@ -101,7 +126,11 @@
imageGenerationEngine = res.engine;
imageGenerationEngine = res.engine;
enableImageGeneration = res.enabled;
enableImageGeneration = res.enabled;
}
}
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
const URLS = await getImageGenerationEngineUrls(localStorage.token);
AUTOMATIC1111_BASE_URL = URLS.AUTOMATIC1111_BASE_URL;
COMFYUI_BASE_URL = URLS.COMFYUI_BASE_URL;
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
imageSize = await getImageSize(localStorage.token);
imageSize = await getImageSize(localStorage.token);
...
@@ -154,6 +183,7 @@
...
@@ -154,6 +183,7 @@
}}
}}
>
>
<option value="">{$i18n.t('Default (Automatic1111)')}</option>
<option value="">{$i18n.t('Default (Automatic1111)')}</option>
<option value="comfyui">{$i18n.t('ComfyUI')}</option>
<option value="openai">{$i18n.t('Open AI (Dall-E)')}</option>
<option value="openai">{$i18n.t('Open AI (Dall-E)')}</option>
</select>
</select>
</div>
</div>
...
@@ -171,6 +201,9 @@
...
@@ -171,6 +201,9 @@
if (imageGenerationEngine === '' && AUTOMATIC1111_BASE_URL === '') {
if (imageGenerationEngine === '' && AUTOMATIC1111_BASE_URL === '') {
toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
enableImageGeneration = false;
enableImageGeneration = false;
} else if (imageGenerationEngine === 'comfyui' && COMFYUI_BASE_URL === '') {
toast.error($i18n.t('ComfyUI Base URL is required.'));
enableImageGeneration = false;
} else if (imageGenerationEngine === 'openai' && OPENAI_API_KEY === '') {
} else if (imageGenerationEngine === 'openai' && OPENAI_API_KEY === '') {
toast.error($i18n.t('OpenAI API Key is required.'));
toast.error($i18n.t('OpenAI API Key is required.'));
enableImageGeneration = false;
enableImageGeneration = false;
...
@@ -204,12 +237,10 @@
...
@@ -204,12 +237,10 @@
/>
/>
</div>
</div>
<button
<button
class="px-
3
bg-gray-
2
00 hover:bg-gray-
3
00 dark:bg-gray-
60
0 dark:hover:bg-gray-
7
00 rounded-lg transition"
class="px-
2.5
bg-gray-
1
00 hover:bg-gray-
200 text-gray-8
00 dark:bg-gray-
85
0 dark:hover:bg-gray-
800 dark:text-gray-1
00 rounded-lg transition"
type="button"
type="button"
on:click={() => {
on:click={() => {
// updateOllamaAPIUrlHandler();
updateUrlHandler();
updateAUTOMATIC1111UrlHandler();
}}
}}
>
>
<svg
<svg
...
@@ -237,6 +268,37 @@
...
@@ -237,6 +268,37 @@
{$i18n.t('(e.g. `sh webui.sh --api`)')}
{$i18n.t('(e.g. `sh webui.sh --api`)')}
</a>
</a>
</div>
</div>
{:else if imageGenerationEngine === 'comfyui'}
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('ComfyUI Base URL')}</div>
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
bind:value={COMFYUI_BASE_URL}
/>
</div>
<button
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
type="button"
on:click={() => {
updateUrlHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
{:else if imageGenerationEngine === 'openai'}
{:else if imageGenerationEngine === 'openai'}
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('OpenAI API Key')}</div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('OpenAI API Key')}</div>
<div class="flex w-full">
<div class="flex w-full">
...
@@ -261,6 +323,7 @@
...
@@ -261,6 +323,7 @@
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedModel}
bind:value={selectedModel}
placeholder={$i18n.t('Select a model')}
placeholder={$i18n.t('Select a model')}
required
>
>
{#if !selectedModel}
{#if !selectedModel}
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
...
...
src/lib/components/common/ImagePreview.svelte
View file @
adf9ccb5
...
@@ -2,6 +2,22 @@
...
@@ -2,6 +2,22 @@
export let show = false;
export let show = false;
export let src = '';
export let src = '';
export let alt = '';
export let alt = '';
const downloadImage = (url, filename) => {
fetch(url)
.then((response) => response.blob())
.then((blob) => {
const objectUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = objectUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(objectUrl);
})
.catch((error) => console.error('Error downloading image:', error));
};
</script>
</script>
{#if show}
{#if show}
...
@@ -35,10 +51,7 @@
...
@@ -35,10 +51,7 @@
<button
<button
class=" p-5"
class=" p-5"
on:click={() => {
on:click={() => {
const a = document.createElement('a');
downloadImage(src, 'Image.png');
a.href = src;
a.download = 'Image.png';
a.click();
}}
}}
>
>
<svg
<svg
...
...
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