Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Compatible with nai-diffusion-3 #213

Merged
merged 8 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@koishijs/plugin-help": "^2.3.3",
"@koishijs/translator": "^1.1.1",
"@koishijs/vitepress": "^3.0.1",
"@types/adm-zip": "^0.5.5",
"@types/libsodium-wrappers-sumo": "^0.7.7",
"@types/node": "^20.8.9",
"atsc": "^1.2.2",
Expand All @@ -78,6 +79,7 @@
"vitepress": "1.0.0-rc.4"
},
"dependencies": {
"adm-zip": "^0.5.10",
"image-size": "^1.0.2",
"libsodium-wrappers-sumo": "^0.7.13"
}
Expand Down
15 changes: 9 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const modelMap = {
safe: 'safe-diffusion',
nai: 'nai-diffusion',
furry: 'nai-diffusion-furry',
'nai-v3': 'nai-diffusion-3',
} as const

export const orientMap = {
Expand All @@ -20,9 +21,10 @@ export const orientMap = {
export const hordeModels = require('../data/horde-models.json') as string[]

const ucPreset = [
'nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers',
'extra digit, fewer digits, cropped, worst quality, low quality',
'normal quality, jpeg artifacts, signature, watermark, username, blurry',
// Replace with the prompt words that come with novelai
'nsfw, lowres, {bad}, error, fewer, extra, missing, worst quality',
'jpeg artifacts, bad quality, watermark, unfinished, displeasing',
'chromatic aberration, signature, extra digits, artistic error, username, scan, [abstract]'
MaikoTan marked this conversation as resolved.
Show resolved Hide resolved
].join(', ')

type Model = keyof typeof modelMap
Expand Down Expand Up @@ -405,9 +407,10 @@ export function parseInput(session: Session, input: string, config: Config, over
.split('\\{').map(s => s.replace(/\{/g, '(')).join('\\{')
.split('\\}').map(s => s.replace(/\}/g, ')')).join('\\}')
} else {
input = input
.split('\\(').map(s => s.replace(/\(/g, '{')).join('\\(')
.split('\\)').map(s => s.replace(/\)/g, '}')).join('\\)')
// emmmmm doesn't know why substituting curly brackets makes the picture ugly, so it's temporarily commented
// input = input
// .split('\\(').map(s => s.replace(/\(/g, '{')).join('\\(')
// .split('\\)').map(s => s.replace(/\)/g, '}')).join('\\)')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is going on with these lines, could you show me why isn't it working?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example:

commented:

ac7f4927d4146f9d816f2181f9af1359
315d7ecc66e2258410c6381e4cec1a3e
5b6e089d3b94b9883fd52104895d7d47
4fd993bde4d150c7107f874c8ae17b1d
ba74e1f51d0cf0a0bb09ba414831896a

uncommented

image
image
image
image
image

Color performance is not as good in uncommented cases,probably my supervisor's feelings

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is because NovelAI doesn't support vector multiplier, but using the {keyword} and [keyword] to strengthen or weaken the vectors.
So what you're writing is {solo:1.2} rather than {{{{solo}}}}, will it causing NovelAI failed to parse your prompt? Can you try these prompt on their website with the same parameters see whether you'd get the same result?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, it's working!

with correct vectors:

image
image

with wrong vector:

image
image

}

input = input
Expand Down
25 changes: 25 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ImageData, StableDiffusionWebUI } from './types'
import { closestMultiple, download, forceDataPrefix, getImageSize, login, NetworkError, project, resizeInput, Size } from './utils'
import {} from '@koishijs/translator'
import {} from '@koishijs/plugin-help'
import AdmZip from 'adm-zip'

export * from './config'

Expand Down Expand Up @@ -302,13 +303,23 @@ export function apply(ctx: Context, config: Config) {
}
})()

const getResponseType = () => {
if (config.type === "stable-horde" || config.type === "sd-webui") {
return "json"
}
return "arraybuffer"
}

const getPayload = () => {
switch (config.type) {
case 'login':
case 'token':
case 'naifu': {
parameters.sampler = sampler.sd2nai(options.sampler)
parameters.image = image?.base64 // NovelAI / NAIFU accepts bare base64 encoded image
// The latest interface changes uc to negative_prompt, so that needs to be changed here as well
parameters.negative_prompt = parameters.uc
initialencounter marked this conversation as resolved.
Show resolved Hide resolved
delete parameters.uc
if (config.type === 'naifu') return parameters
return { model, input: prompt, parameters: omit(parameters, ['prompt']) }
}
Expand Down Expand Up @@ -379,6 +390,8 @@ export function apply(ctx: Context, config: Config) {
const res = await ctx.http.axios(trimSlash(config.endpoint) + path, {
method: 'POST',
timeout: config.requestTimeout,
// Since novelai's latest interface returns an application/x-zip-compressed, a responseType must be passed in
responseType: getResponseType(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is only one exception here, you could just use the conditional expression here:


```suggestion
            responseType: ['login', 'token'].include(config.type) ? 'arraybuffer' : 'json',

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a neat and elegant suggestion, but I think writing a method will be more maintainable in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but in my thought this part would never goes complicated since there are only json or arrabuffer that we could handle here.

headers: {
...config.headers,
...getHeaders(),
Expand Down Expand Up @@ -412,6 +425,18 @@ export function apply(ctx: Context, config: Config) {
// event: newImage
// id: 1
// data:

if (res.headers['content-type'] === 'application/x-zip-compressed') {
const buffer = Buffer.from(res.data, 'binary'); // Ensure 'binary' encoding
const zip = new AdmZip(buffer);

// Gets all files in the ZIP file
const zipEntries = zip.getEntries();
const firstImageBuffer = zip.readFile(zipEntries[0]);
MaikoTan marked this conversation as resolved.
Show resolved Hide resolved
const b64 = Buffer.from(firstImageBuffer).toString('base64')
return forceDataPrefix(b64, "image/png")
MaikoTan marked this conversation as resolved.
Show resolved Hide resolved
}

return forceDataPrefix(res.data?.slice(27))
}

Expand Down