feat: add default prompt support (#204)

* feat: add default prompt support

Add a new option `-d` for adding default prompt to the user prompt.

Allow user to generate images without input (in such case they must toggle the `default` option)

Add a new short cut `default` for quick accessing default prompt without any user input.

* feat: use respond prompt when the backend is sd-webui

Use respond prompt to replace the original prompt as feedback, because the prompt may be affected by some plugins (e.g. dynamic-prompts)

* Feat: add `defaultPromptSw` config and remove the `default` shortcut

* Feat: separate the auth lv requirements for default and normal usage

* Refactor: remove `default` shortcut

* Chore: change some descriptions
This commit is contained in:
Ninzore 2023-07-25 10:33:50 +01:00 committed by GitHub
parent 0e0c3a2ab1
commit 5c8a76c44b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 8 deletions

View File

@ -143,6 +143,8 @@ export interface PromptConfig {
basePrompt?: Computed<string>
negativePrompt?: Computed<string>
forbidden?: Computed<string>
defaultPromptSw?: boolean
defaultPrompt?: Computed<string>
placement?: Computed<'before' | 'after'>
latinOnly?: Computed<boolean>
translator?: boolean
@ -153,6 +155,8 @@ export const PromptConfig: Schema<PromptConfig> = Schema.object({
basePrompt: Schema.computed(Schema.string().role('textarea'), options).description('默认附加的标签。').default('masterpiece, best quality'),
negativePrompt: Schema.computed(Schema.string().role('textarea'), options).description('默认附加的反向标签。').default(ucPreset),
forbidden: Schema.computed(Schema.string().role('textarea'), options).description('违禁词列表。请求中的违禁词将会被自动删除。').default(''),
defaultPromptSw: Schema.boolean().description('是否启用默认标签。').default(false),
defaultPrompt: Schema.string().role('textarea', options).description('默认标签可以在用户无输入prompt时调用。可选在sd-webui中安装dynamic prompt插件配合使用以达到随机标签效果。').default(''),
placement: Schema.computed(Schema.union([
Schema.const('before').description('置于最前'),
Schema.const('after').description('置于最后'),
@ -202,6 +206,8 @@ export interface Config extends PromptConfig, ParamConfig {
token?: string
email?: string
password?: string
authLv?: Computed<number>
authLvDefault?: Computed<number>
output?: Computed<'minimal' | 'default' | 'verbose'>
features?: FeatureConfig
endpoint?: string
@ -273,6 +279,11 @@ export const Config = Schema.intersect([
}),
]),
Schema.object({
authLv: Schema.computed(Schema.natural(), options).description('使用画图全部功能所需要的权限等级。').default(0),
authLvDefault: Schema.computed(Schema.natural(), options).description('使用默认参数生成所需要的权限等级。').default(0),
}).description('权限设置'),
Schema.object({
features: Schema.object({}),
}).description('功能设置'),
@ -372,7 +383,15 @@ export function parseForbidden(input: string) {
const backslash = /@@__BACKSLASH__@@/g
export function parseInput(session: Session, input: string, config: Config, override: boolean): string[] {
export function parseInput(session: Session, input: string, config: Config, override: boolean, addDefault: boolean): string[] {
if (!input) {
return [
null,
[session.resolve(config.basePrompt), session.resolve(config.defaultPrompt)].join(','),
session.resolve(config.negativePrompt)
]
}
input = input
.replace(/\\\\/g, backslash.source)
.replace(//g, ',')
@ -446,6 +465,7 @@ export function parseInput(session: Session, input: string, config: Config, over
if (!override) {
appendToList(positive, session.resolve(config.basePrompt))
appendToList(negative, session.resolve(config.negativePrompt))
if (addDefault) appendToList(positive, session.resolve(config.defaultPrompt))
}
return [null, positive.join(', '), negative.join(', ')]

View File

@ -106,7 +106,22 @@ export function apply(ctx: Context, config: Config) {
.option('iterations', '-i <iterations:posint>', { fallback: 1, hidden: () => config.maxIterations <= 1 })
.option('batch', '-b <batch:option>', { fallback: 1, hidden: () => config.maxIterations <= 1 })
.action(async ({ session, options }, input) => {
if (!input?.trim()) return session.execute('help novelai')
if (config.defaultPromptSw) {
if (session.user.authority < session.resolve(config.authLvDefault)) {
return session.text('internal.low-authority')
}
if (session.user.authority < session.resolve(config.authLv)) {
input = ''
options = options.resolution ? { resolution: options.resolution } : {}
}
}
else if (
!config.defaultPromptSw &&
session.user.authority < session.resolve(config.authLv)
) return session.text('internal.low-auth')
const haveInput = input?.trim() ? true : false
if (!haveInput && !config.defaultPromptSw) return session.execute('help novelai')
// Check if the user is allowed to use this command.
// This code is originally written in the `resolution` function,
@ -126,7 +141,7 @@ export function apply(ctx: Context, config: Config) {
const allowImage = useFilter(config.features.image)(session)
let imgUrl: string, image: ImageData
if (!restricted(session)) {
if (!restricted(session) && haveInput) {
input = h('', h.transform(h.parse(input), {
image(attrs) {
if (!allowImage) throw new SessionError('commands.novelai.messages.invalid-content')
@ -144,11 +159,11 @@ export function apply(ctx: Context, config: Config) {
return session.text('.expect-prompt')
}
} else {
input = h('', h.transform(h.parse(input), {
input = haveInput ? h('', h.transform(h.parse(input), {
image(attrs) {
throw new SessionError('commands.novelai.messages.invalid-content')
},
})).toString(true)
})).toString(true) : input
delete options.enhance
delete options.steps
delete options.noise
@ -160,7 +175,7 @@ export function apply(ctx: Context, config: Config) {
return session.text('.expect-image')
}
if (config.translator && ctx.translator && !options.noTranslator) {
if (haveInput && config.translator && ctx.translator && !options.noTranslator) {
try {
input = await ctx.translator.translate({ input, target: 'en' })
} catch (err) {
@ -168,7 +183,9 @@ export function apply(ctx: Context, config: Config) {
}
}
const [errPath, prompt, uc] = parseInput(session, input, config, options.override)
const [errPath, prompt, uc] = parseInput(
session, input, config, options.override, config.defaultPromptSw
)
if (errPath) return session.text(errPath)
let token: string
@ -352,6 +369,7 @@ export function apply(ctx: Context, config: Config) {
}
}
let finalPrompt = prompt
const iterate = async () => {
const request = async () => {
const res = await ctx.http.axios(trimSlash(config.endpoint) + path, {
@ -365,6 +383,7 @@ export function apply(ctx: Context, config: Config) {
})
if (config.type === 'sd-webui') {
finalPrompt = (JSON.parse((res.data as StableDiffusionWebUI.Response).info)).prompt
return forceDataPrefix((res.data as StableDiffusionWebUI.Response).images[0])
}
if (config.type === 'stable-horde') {
@ -436,7 +455,7 @@ export function apply(ctx: Context, config: Config) {
}
}
result.children.push(h('message', attrs, lines.join('\n')))
result.children.push(h('message', attrs, `prompt = ${prompt}`))
result.children.push(h('message', attrs, `prompt = ${finalPrompt}`))
if (output === 'verbose') {
result.children.push(h('message', attrs, `undesired = ${uc}`))
}