Compare commits

..

3 Commits

Author SHA1 Message Date
pinks a251d5e965 fix README 2023-10-12 11:39:19 +02:00
pinks 6313c5d67e feat: workers api 2023-10-11 03:59:52 +02:00
pinks 55c53ac565 fix html 2023-10-11 03:56:08 +02:00
5 changed files with 117 additions and 7 deletions

View File

@ -15,8 +15,8 @@ You can put these in `.env` file or pass them as environment variables.
Required.
- `SD_API_URL` - URL to Stable Diffusion API. Only used on first run. Default:
`http://127.0.0.1:7860/`
- `TG_ADMIN_USERS` - Comma separated list of usernames of users that can use admin commands. Only
used on first run. Optional.
- `TG_ADMIN_USERNAMES` - Comma separated list of usernames of users that can use admin commands.
Only used on first run. Optional.
## Running
@ -25,9 +25,9 @@ You can put these in `.env` file or pass them as environment variables.
## Codegen
The Stable Diffusion API in `sd/sdApi.ts` is auto-generated. To regenerate it, first start your SD
The Stable Diffusion API in `app/sdApi.ts` is auto-generated. To regenerate it, first start your SD
WebUI with `--nowebui --api`, and then run:
```sh
deno run npm:openapi-typescript http://localhost:7861/openapi.json -o sd/sdApi.ts
deno run npm:openapi-typescript http://localhost:7861/openapi.json -o app/sdApi.ts
```

View File

@ -4,6 +4,7 @@ import { paramsRoute } from "./paramsRoute.ts";
import { sessionsRoute } from "./sessionsRoute.ts";
import { statsRoute } from "./statsRoute.ts";
import { usersRoute } from "./usersRoute.ts";
import { workersRoute } from "./workersRoute.ts";
export const serveApi = createLoggerMiddleware(
createPathFilter({
@ -12,6 +13,7 @@ export const serveApi = createLoggerMiddleware(
"users": usersRoute,
"settings/params": paramsRoute,
"stats": statsRoute,
"workers": workersRoute,
}),
{ filterStatus: (status) => status >= 400 },
);

103
api/workersRoute.ts Normal file
View File

@ -0,0 +1,103 @@
import { createEndpoint, createMethodFilter, createPathFilter } from "t_rest/server";
import { activeGenerationWorkers } from "../app/generationQueue.ts";
import { getConfig } from "../app/config.ts";
import * as SdApi from "../app/sdApi.ts";
import createOpenApiFetch from "openapi_fetch";
export const workersRoute = createPathFilter({
"": createMethodFilter({
"GET": createEndpoint(
{ query: null, body: null },
async () => {
const activeWorkers = activeGenerationWorkers;
const { sdInstances } = await getConfig();
const workers = Object.entries(sdInstances).map(([sdInstanceId, sdInstance]) => ({
id: sdInstanceId,
name: sdInstance.name ?? sdInstanceId,
maxResolution: sdInstance.maxResolution,
active: activeWorkers.has(sdInstanceId),
lastOnline: null,
imagesPerMinute: null,
pixelsPerSecond: null,
pixelStepsPerSecond: null,
}));
return {
status: 200,
body: { type: "application/json", data: workers },
};
},
),
}),
"{workerId}/loras": createMethodFilter({
GET: createEndpoint(
{ query: null, body: null },
async ({ params }) => {
const { sdInstances } = await getConfig();
const sdInstance = sdInstances[params.workerId];
if (!sdInstance) {
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
}
const sdClient = createOpenApiFetch<SdApi.paths>({ baseUrl: sdInstance.api.url });
const lorasResponse = await sdClient.GET("/sdapi/v1/loras", {
headers: sdInstance.api.auth ? { Authorization: sdInstance.api.auth } : undefined,
});
if (lorasResponse.error) {
return {
status: 500,
body: { type: "text/plain", data: `Loras request failed: ${lorasResponse["error"]}` },
};
}
const loras = (lorasResponse.data as Lora[]).map((lora) => ({
name: lora.name,
alias: lora.alias ?? null,
}));
return {
status: 200,
body: { type: "application/json", data: loras },
};
},
),
}),
"{workerId}/models": createMethodFilter({
GET: createEndpoint(
{ query: null, body: null },
async ({ params }) => {
const { sdInstances } = await getConfig();
const sdInstance = sdInstances[params.workerId];
if (!sdInstance) {
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
}
const sdClient = createOpenApiFetch<SdApi.paths>({ baseUrl: sdInstance.api.url });
const modelsResponse = await sdClient.GET("/sdapi/v1/sd-models", {
headers: sdInstance.api.auth ? { Authorization: sdInstance.api.auth } : undefined,
});
if (modelsResponse.error) {
return {
status: 500,
body: { type: "text/plain", data: `Models request failed: ${modelsResponse["error"]}` },
};
}
const models = modelsResponse.data.map((model) => ({
title: model.title,
modelName: model.model_name,
hash: model.hash,
sha256: model.sha256,
}));
return {
status: 200,
body: { type: "application/json", data: models },
};
},
),
}),
});
export interface Lora {
name: string;
alias: string | null;
metadata: object;
}

View File

@ -50,12 +50,18 @@ export async function getConfig(): Promise<Config> {
const configEntry = await db.get<Config>(["config"]);
const config = configEntry?.value;
return {
adminUsernames: config?.adminUsernames ?? [],
adminUsernames: config?.adminUsernames ?? Deno.env.get("TG_ADMIN_USERNAMES")?.split(",") ?? [],
pausedReason: config?.pausedReason ?? null,
maxUserJobs: config?.maxUserJobs ?? Infinity,
maxJobs: config?.maxJobs ?? Infinity,
defaultParams: config?.defaultParams ?? {},
sdInstances: config?.sdInstances ?? {},
sdInstances: config?.sdInstances ??
{
"local": {
api: { url: Deno.env.get("SD_API_URL") ?? "http://127.0.0.1:7860/" },
maxResolution: 1024 * 1024,
},
},
};
}

View File

@ -109,7 +109,6 @@ export function SettingsPage(props: { sessionId: string }) {
}))}
/>
</span>
span
</label>
<label className="flex flex-col items-stretch gap-1">
<span className="text-sm">