1
0
Fork 0

feat(gui): add local params and API stub so client can load without a server (#181)

This commit is contained in:
Sean Sube 2023-03-05 16:23:26 -06:00
parent 643f7bbd01
commit d5a3b0fed8
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
11 changed files with 366 additions and 126 deletions

View File

@ -1,5 +1,5 @@
{
"version": "0.7.1",
"version": "0.8.0",
"batch": {
"default": 1,
"min": 1,

View File

@ -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');

View File

@ -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
}
}
}

View File

@ -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.

59
gui/src/client/local.ts Normal file
View File

@ -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;

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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;
}
}

View File

@ -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,17 +140,49 @@ 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', () => {

View File

@ -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';
/**