feat(gui): add local params and API stub so client can load without a server (#181)
This commit is contained in:
parent
643f7bbd01
commit
d5a3b0fed8
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.7.1",
|
||||
"version": "0.8.0",
|
||||
"batch": {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
|
|
|
@ -5,8 +5,7 @@ import { copy } from 'esbuild-plugin-copy';
|
|||
|
||||
function envTrue(key) {
|
||||
const val = (process.env[key] || '').toLowerCase();
|
||||
return val == '1' || val == 't' || val == 'true' || val == 'y' || val == 'yes';
|
||||
|
||||
return val === '1' || val === 't' || val === 'true' || val === 'y' || val === 'yes';
|
||||
}
|
||||
|
||||
const debug = envTrue('DEBUG');
|
||||
|
|
|
@ -3,17 +3,167 @@
|
|||
"root": "http://127.0.0.1:5000"
|
||||
},
|
||||
"params": {
|
||||
"version": "0.8.0",
|
||||
"batch": {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 5,
|
||||
"step": 1
|
||||
},
|
||||
"bottom": {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 512,
|
||||
"step": 8
|
||||
},
|
||||
"cfg": {
|
||||
"default": 6,
|
||||
"min": 1,
|
||||
"max": 30,
|
||||
"step": 0.1
|
||||
},
|
||||
"correction": {
|
||||
"default": "",
|
||||
"keys": []
|
||||
},
|
||||
"denoise": {
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 0.1
|
||||
},
|
||||
"eta": {
|
||||
"default": 0.0,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 0.01
|
||||
},
|
||||
"faceOutscale": {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 4,
|
||||
"step": 1
|
||||
},
|
||||
"faceStrength": {
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 0.1
|
||||
},
|
||||
"fillColor": {
|
||||
"default": "#000000",
|
||||
"keys": []
|
||||
},
|
||||
"filter": {
|
||||
"default": "none",
|
||||
"keys": []
|
||||
},
|
||||
"height": {
|
||||
"default": 512,
|
||||
"min": 256,
|
||||
"max": 1024,
|
||||
"step": 8
|
||||
},
|
||||
"inversion": {
|
||||
"default": "",
|
||||
"keys": []
|
||||
},
|
||||
"left": {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 512,
|
||||
"step": 8
|
||||
},
|
||||
"model": {
|
||||
"default": "stable-diffusion-onnx-v1-5"
|
||||
"default": "stable-diffusion-onnx-v1-5",
|
||||
"keys": []
|
||||
},
|
||||
"negativePrompt": {
|
||||
"default": "",
|
||||
"keys": []
|
||||
},
|
||||
"noise": {
|
||||
"default": "histogram",
|
||||
"keys": []
|
||||
},
|
||||
"outscale": {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 4,
|
||||
"step": 1
|
||||
},
|
||||
"platform": {
|
||||
"default": "amd"
|
||||
},
|
||||
"scheduler": {
|
||||
"default": "euler-a"
|
||||
"default": "amd",
|
||||
"keys": []
|
||||
},
|
||||
"prompt": {
|
||||
"default": "an astronaut eating a hamburger"
|
||||
"default": "an astronaut eating a hamburger",
|
||||
"keys": []
|
||||
},
|
||||
"right": {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 512,
|
||||
"step": 8
|
||||
},
|
||||
"scale": {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 4,
|
||||
"step": 1
|
||||
},
|
||||
"scheduler": {
|
||||
"default": "euler-a",
|
||||
"keys": []
|
||||
},
|
||||
"seed": {
|
||||
"default": -1,
|
||||
"min": -1,
|
||||
"max": 4294967295,
|
||||
"step": 1
|
||||
},
|
||||
"steps": {
|
||||
"default": 25,
|
||||
"min": 1,
|
||||
"max": 200,
|
||||
"step": 1
|
||||
},
|
||||
"strength": {
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 0.01
|
||||
},
|
||||
"tileOrder": {
|
||||
"default": "spiral",
|
||||
"keys": [
|
||||
"grid",
|
||||
"spiral"
|
||||
]
|
||||
},
|
||||
"top": {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 512,
|
||||
"step": 8
|
||||
},
|
||||
"upscaleOrder": {
|
||||
"default": "correction-first",
|
||||
"keys": [
|
||||
"correction-both",
|
||||
"correction-first",
|
||||
"correction-last"
|
||||
]
|
||||
},
|
||||
"upscaling": {
|
||||
"default": "",
|
||||
"keys": []
|
||||
},
|
||||
"width": {
|
||||
"default": 512,
|
||||
"min": 256,
|
||||
"max": 1024,
|
||||
"step": 8
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable max-lines */
|
||||
import { doesExist } from '@apextoaster/js-utils';
|
||||
|
||||
import { ServerParams } from './config.js';
|
||||
import { range } from './utils.js';
|
||||
import { ServerParams } from '../config.js';
|
||||
import { range } from '../utils.js';
|
||||
|
||||
/**
|
||||
* Shared parameters for anything using models, which is pretty much everything.
|
|
@ -0,0 +1,59 @@
|
|||
import { BaseError } from 'noicejs';
|
||||
import { ApiClient } from './api.js';
|
||||
|
||||
export class NoServerError extends BaseError {
|
||||
constructor() {
|
||||
super('cannot connect to server');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @TODO client-side inference with https://www.npmjs.com/package/onnxruntime-web
|
||||
*/
|
||||
export const LOCAL_CLIENT = {
|
||||
async masks() {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async blend(model, params, upscale) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async img2img(model, params, upscale) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async txt2img(model, params, upscale) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async inpaint(model, params, upscale) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async upscale(model, params, upscale) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async outpaint(model, params, upscale) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async noises() {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async params() {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async ready(key) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async cancel(key) {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async models() {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async platforms() {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async schedulers() {
|
||||
throw new NoServerError();
|
||||
},
|
||||
async strings() {
|
||||
return {};
|
||||
},
|
||||
} as ApiClient;
|
|
@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useHash } from 'react-use/lib/useHash';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { ImageResponse } from '../client.js';
|
||||
import { ImageResponse } from '../client/api.js';
|
||||
import { BLEND_SOURCES, ConfigContext, StateContext } from '../state.js';
|
||||
import { range, visibleIndex } from '../utils.js';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useMutation, useQuery } from 'react-query';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { ImageResponse } from '../client.js';
|
||||
import { ImageResponse } from '../client/api.js';
|
||||
import { POLL_TIME } from '../config.js';
|
||||
import { ClientContext, ConfigContext, StateContext } from '../state.js';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useQuery } from 'react-query';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { BaseImgParams } from '../../client.js';
|
||||
import { BaseImgParams } from '../../client/api.js';
|
||||
import { STALE_TIME } from '../../config.js';
|
||||
import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state.js';
|
||||
import { NumericField } from '../input/NumericField.js';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { doesExist, Maybe } from '@apextoaster/js-utils';
|
||||
import { merge } from 'lodash';
|
||||
import { Img2ImgParams, InpaintParams, ModelParams, OutpaintParams, STATUS_SUCCESS, Txt2ImgParams, UpscaleParams } from './client.js';
|
||||
import { Img2ImgParams, InpaintParams, ModelParams, OutpaintParams, STATUS_SUCCESS, Txt2ImgParams, UpscaleParams } from './client/api.js';
|
||||
|
||||
export interface ConfigNumber {
|
||||
default: number;
|
||||
|
@ -113,3 +113,16 @@ export function getApiRoot(config: Config): string {
|
|||
return config.api.root;
|
||||
}
|
||||
}
|
||||
|
||||
export function isDebug(): boolean {
|
||||
const query = new URLSearchParams(window.location.search);
|
||||
const debug = query.get('debug');
|
||||
|
||||
if (doesExist(debug)) {
|
||||
const val = debug.toLowerCase();
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return val === '1' || val === 't' || val === 'true' || val === 'y' || val === 'yes';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mustDefault, mustExist, timeout } from '@apextoaster/js-utils';
|
||||
import { createLogger } from 'browser-bunyan';
|
||||
import { mustDefault, mustExist, timeout, TimeoutError } from '@apextoaster/js-utils';
|
||||
import { createLogger, Logger } from 'browser-bunyan';
|
||||
import i18n from 'i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import * as React from 'react';
|
||||
|
@ -10,12 +10,21 @@ import { satisfies } from 'semver';
|
|||
import { createStore } from 'zustand';
|
||||
import { createJSONStorage, persist } from 'zustand/middleware';
|
||||
|
||||
import { makeClient } from './client.js';
|
||||
import { ApiClient, makeClient } from './client/api.js';
|
||||
import { LOCAL_CLIENT } from './client/local.js';
|
||||
import { ParamsVersionError } from './components/error/ParamsVersion.js';
|
||||
import { ServerParamsError } from './components/error/ServerParams.js';
|
||||
import { OnnxError } from './components/OnnxError.js';
|
||||
import { OnnxWeb } from './components/OnnxWeb.js';
|
||||
import { getApiRoot, loadConfig, mergeConfig, PARAM_VERSION } from './config.js';
|
||||
import {
|
||||
Config,
|
||||
getApiRoot,
|
||||
isDebug,
|
||||
loadConfig,
|
||||
mergeConfig,
|
||||
PARAM_VERSION,
|
||||
ServerParams,
|
||||
} from './config.js';
|
||||
import {
|
||||
ClientContext,
|
||||
ConfigContext,
|
||||
|
@ -30,29 +39,7 @@ import { I18N_STRINGS } from './strings/all.js';
|
|||
|
||||
export const INITIAL_LOAD_TIMEOUT = 5_000;
|
||||
|
||||
export async function main() {
|
||||
const logger = createLogger({
|
||||
name: 'onnx-web',
|
||||
level: 'debug',
|
||||
});
|
||||
|
||||
// load config from GUI server
|
||||
const config = await loadConfig();
|
||||
|
||||
// use that to create an API client
|
||||
const root = getApiRoot(config);
|
||||
const client = makeClient(root);
|
||||
|
||||
// prep react-dom
|
||||
const appElement = mustExist(document.getElementById('app'));
|
||||
const app = createRoot(appElement);
|
||||
|
||||
try {
|
||||
logger.info('getting image parameters from server');
|
||||
// load full params from the API server and merge with the initial client config
|
||||
const params = await timeout(INITIAL_LOAD_TIMEOUT, client.params());
|
||||
const version = mustDefault(params.version, '0.0.0');
|
||||
if (satisfies(version, PARAM_VERSION)) {
|
||||
export async function renderApp(config: Config, params: ServerParams, logger: Logger, client: ApiClient) {
|
||||
const completeConfig = mergeConfig(config, params);
|
||||
|
||||
// prep i18next
|
||||
|
@ -141,7 +128,7 @@ export async function main() {
|
|||
});
|
||||
|
||||
// go
|
||||
app.render(<QueryClientProvider client={query}>
|
||||
return <QueryClientProvider client={query}>
|
||||
<ClientContext.Provider value={client}>
|
||||
<ConfigContext.Provider value={completeConfig}>
|
||||
<LoggerContext.Provider value={reactLogger}>
|
||||
|
@ -153,18 +140,50 @@ export async function main() {
|
|||
</LoggerContext.Provider>
|
||||
</ConfigContext.Provider>
|
||||
</ClientContext.Provider>
|
||||
</QueryClientProvider>);
|
||||
</QueryClientProvider>;
|
||||
}
|
||||
|
||||
export async function main() {
|
||||
const debug = isDebug();
|
||||
const logger = createLogger({
|
||||
name: 'onnx-web',
|
||||
level: debug ? 'debug' : 'info',
|
||||
});
|
||||
|
||||
// load config from GUI server
|
||||
const config = await loadConfig();
|
||||
|
||||
// use that to create an API client
|
||||
const root = getApiRoot(config);
|
||||
const client = makeClient(root);
|
||||
|
||||
// prep react-dom
|
||||
const appElement = mustExist(document.getElementById('app'));
|
||||
const app = createRoot(appElement);
|
||||
|
||||
try {
|
||||
logger.info('getting image parameters from server');
|
||||
// load full params from the API server and merge with the initial client config
|
||||
const params = await timeout(INITIAL_LOAD_TIMEOUT, client.params());
|
||||
const version = mustDefault(params.version, '0.0.0');
|
||||
if (satisfies(version, PARAM_VERSION)) {
|
||||
app.render(await renderApp(config, params, logger, client));
|
||||
} else {
|
||||
app.render(<OnnxError root={root}>
|
||||
<ParamsVersionError root={root} version={version} />
|
||||
</OnnxError>);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof TimeoutError || (err instanceof Error && err.message.includes('Failed to fetch'))) {
|
||||
// params timed out, attempt to render without a server
|
||||
app.render(await renderApp(config, config.params as ServerParams, logger, LOCAL_CLIENT));
|
||||
} else {
|
||||
app.render(<OnnxError root={root}>
|
||||
<ServerParamsError root={root} error={err} />
|
||||
</OnnxError>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable max-lines */
|
||||
/* eslint-disable no-null/no-null */
|
||||
import { doesExist, Maybe } from '@apextoaster/js-utils';
|
||||
import { Maybe } from '@apextoaster/js-utils';
|
||||
import { Logger } from 'noicejs';
|
||||
import { createContext } from 'react';
|
||||
import { StateCreator, StoreApi } from 'zustand';
|
||||
|
@ -19,7 +19,7 @@ import {
|
|||
Txt2ImgParams,
|
||||
UpscaleParams,
|
||||
UpscaleReqParams,
|
||||
} from './client.js';
|
||||
} from './client/api.js';
|
||||
import { Config, ConfigFiles, ConfigState, ServerParams } from './config.js';
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue