Refactor Stability AI Image options builder methods
- Deprecate builder methods with the prefix `with` - Update docs and references
This commit is contained in:
committed by
Mark Pollack
parent
18eb4c3c61
commit
9748820727
@@ -55,20 +55,20 @@ public class StabilityAiImageModel implements ImageModel {
|
||||
private static StabilityAiApi.GenerateImageRequest getGenerateImageRequest(ImagePrompt stabilityAiImagePrompt,
|
||||
StabilityAiImageOptions optionsToUse) {
|
||||
return new StabilityAiApi.GenerateImageRequest.Builder()
|
||||
.withTextPrompts(stabilityAiImagePrompt.getInstructions()
|
||||
.textPrompts(stabilityAiImagePrompt.getInstructions()
|
||||
.stream()
|
||||
.map(message -> new StabilityAiApi.GenerateImageRequest.TextPrompts(message.getText(),
|
||||
message.getWeight()))
|
||||
.collect(Collectors.toList()))
|
||||
.withHeight(optionsToUse.getHeight())
|
||||
.withWidth(optionsToUse.getWidth())
|
||||
.withCfgScale(optionsToUse.getCfgScale())
|
||||
.withClipGuidancePreset(optionsToUse.getClipGuidancePreset())
|
||||
.withSampler(optionsToUse.getSampler())
|
||||
.withSamples(optionsToUse.getN())
|
||||
.withSeed(optionsToUse.getSeed())
|
||||
.withSteps(optionsToUse.getSteps())
|
||||
.withStylePreset(optionsToUse.getStylePreset())
|
||||
.height(optionsToUse.getHeight())
|
||||
.width(optionsToUse.getWidth())
|
||||
.cfgScale(optionsToUse.getCfgScale())
|
||||
.clipGuidancePreset(optionsToUse.getClipGuidancePreset())
|
||||
.sampler(optionsToUse.getSampler())
|
||||
.samples(optionsToUse.getN())
|
||||
.seed(optionsToUse.getSeed())
|
||||
.steps(optionsToUse.getSteps())
|
||||
.stylePreset(optionsToUse.getStylePreset())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -124,32 +124,31 @@ public class StabilityAiImageModel implements ImageModel {
|
||||
}
|
||||
StabilityAiImageOptions.Builder builder = StabilityAiImageOptions.builder()
|
||||
// Handle portable image options
|
||||
.withModel(ModelOptionsUtils.mergeOption(runtimeOptions.getModel(), defaultOptions.getModel()))
|
||||
.withN(ModelOptionsUtils.mergeOption(runtimeOptions.getN(), defaultOptions.getN()))
|
||||
.withResponseFormat(ModelOptionsUtils.mergeOption(runtimeOptions.getResponseFormat(),
|
||||
.model(ModelOptionsUtils.mergeOption(runtimeOptions.getModel(), defaultOptions.getModel()))
|
||||
.N(ModelOptionsUtils.mergeOption(runtimeOptions.getN(), defaultOptions.getN()))
|
||||
.responseFormat(ModelOptionsUtils.mergeOption(runtimeOptions.getResponseFormat(),
|
||||
defaultOptions.getResponseFormat()))
|
||||
.withWidth(ModelOptionsUtils.mergeOption(runtimeOptions.getWidth(), defaultOptions.getWidth()))
|
||||
.withHeight(ModelOptionsUtils.mergeOption(runtimeOptions.getHeight(), defaultOptions.getHeight()))
|
||||
.withStylePreset(ModelOptionsUtils.mergeOption(runtimeOptions.getStyle(), defaultOptions.getStyle()))
|
||||
.width(ModelOptionsUtils.mergeOption(runtimeOptions.getWidth(), defaultOptions.getWidth()))
|
||||
.height(ModelOptionsUtils.mergeOption(runtimeOptions.getHeight(), defaultOptions.getHeight()))
|
||||
.stylePreset(ModelOptionsUtils.mergeOption(runtimeOptions.getStyle(), defaultOptions.getStyle()))
|
||||
// Always set the stability-specific defaults
|
||||
.withCfgScale(defaultOptions.getCfgScale())
|
||||
.withClipGuidancePreset(defaultOptions.getClipGuidancePreset())
|
||||
.withSampler(defaultOptions.getSampler())
|
||||
.withSeed(defaultOptions.getSeed())
|
||||
.withSteps(defaultOptions.getSteps())
|
||||
.withStylePreset(defaultOptions.getStylePreset());
|
||||
.cfgScale(defaultOptions.getCfgScale())
|
||||
.clipGuidancePreset(defaultOptions.getClipGuidancePreset())
|
||||
.sampler(defaultOptions.getSampler())
|
||||
.seed(defaultOptions.getSeed())
|
||||
.steps(defaultOptions.getSteps())
|
||||
.stylePreset(defaultOptions.getStylePreset());
|
||||
if (runtimeOptions instanceof StabilityAiImageOptions) {
|
||||
StabilityAiImageOptions stabilityOptions = (StabilityAiImageOptions) runtimeOptions;
|
||||
// Handle Stability AI specific image options
|
||||
builder
|
||||
.withCfgScale(
|
||||
ModelOptionsUtils.mergeOption(stabilityOptions.getCfgScale(), defaultOptions.getCfgScale()))
|
||||
.withClipGuidancePreset(ModelOptionsUtils.mergeOption(stabilityOptions.getClipGuidancePreset(),
|
||||
.cfgScale(ModelOptionsUtils.mergeOption(stabilityOptions.getCfgScale(), defaultOptions.getCfgScale()))
|
||||
.clipGuidancePreset(ModelOptionsUtils.mergeOption(stabilityOptions.getClipGuidancePreset(),
|
||||
defaultOptions.getClipGuidancePreset()))
|
||||
.withSampler(ModelOptionsUtils.mergeOption(stabilityOptions.getSampler(), defaultOptions.getSampler()))
|
||||
.withSeed(ModelOptionsUtils.mergeOption(stabilityOptions.getSeed(), defaultOptions.getSeed()))
|
||||
.withSteps(ModelOptionsUtils.mergeOption(stabilityOptions.getSteps(), defaultOptions.getSteps()))
|
||||
.withStylePreset(ModelOptionsUtils.mergeOption(stabilityOptions.getStylePreset(),
|
||||
.sampler(ModelOptionsUtils.mergeOption(stabilityOptions.getSampler(), defaultOptions.getSampler()))
|
||||
.seed(ModelOptionsUtils.mergeOption(stabilityOptions.getSeed(), defaultOptions.getSeed()))
|
||||
.steps(ModelOptionsUtils.mergeOption(stabilityOptions.getSteps(), defaultOptions.getSteps()))
|
||||
.stylePreset(ModelOptionsUtils.mergeOption(stabilityOptions.getStylePreset(),
|
||||
defaultOptions.getStylePreset()));
|
||||
}
|
||||
|
||||
|
||||
@@ -137,51 +137,141 @@ public class StabilityAiApi {
|
||||
|
||||
}
|
||||
|
||||
public Builder textPrompts(List<TextPrompts> textPrompts) {
|
||||
this.textPrompts = textPrompts;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder height(Integer height) {
|
||||
this.height = height;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder width(Integer width) {
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder cfgScale(Float cfgScale) {
|
||||
this.cfgScale = cfgScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clipGuidancePreset(String clipGuidancePreset) {
|
||||
this.clipGuidancePreset = clipGuidancePreset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sampler(String sampler) {
|
||||
this.sampler = sampler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder samples(Integer samples) {
|
||||
this.samples = samples;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder seed(Long seed) {
|
||||
this.seed = seed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder steps(Integer steps) {
|
||||
this.steps = steps;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder stylePreset(String stylePreset) {
|
||||
this.stylePreset = stylePreset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #textPrompts(List)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withTextPrompts(List<TextPrompts> textPrompts) {
|
||||
this.textPrompts = textPrompts;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #height(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withHeight(Integer height) {
|
||||
this.height = height;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #width(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withWidth(Integer width) {
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #cfgScale(Float)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withCfgScale(Float cfgScale) {
|
||||
this.cfgScale = cfgScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #clipGuidancePreset(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withClipGuidancePreset(String clipGuidancePreset) {
|
||||
this.clipGuidancePreset = clipGuidancePreset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #sampler(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSampler(String sampler) {
|
||||
this.sampler = sampler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #samples(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSamples(Integer samples) {
|
||||
this.samples = samples;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #seed(Long)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSeed(Long seed) {
|
||||
this.seed = seed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #steps(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSteps(Integer steps) {
|
||||
this.steps = steps;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #stylePreset(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withStylePreset(String stylePreset) {
|
||||
this.stylePreset = stylePreset;
|
||||
return this;
|
||||
|
||||
@@ -434,66 +434,183 @@ public class StabilityAiImageOptions implements ImageOptions {
|
||||
this.options = new StabilityAiImageOptions();
|
||||
}
|
||||
|
||||
public Builder N(Integer n) {
|
||||
this.options.setN(n);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder model(String model) {
|
||||
this.options.setModel(model);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder width(Integer width) {
|
||||
this.options.setWidth(width);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder height(Integer height) {
|
||||
this.options.setHeight(height);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder responseFormat(String responseFormat) {
|
||||
this.options.setResponseFormat(responseFormat);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder cfgScale(Float cfgScale) {
|
||||
this.options.setCfgScale(cfgScale);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clipGuidancePreset(String clipGuidancePreset) {
|
||||
this.options.setClipGuidancePreset(clipGuidancePreset);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sampler(String sampler) {
|
||||
this.options.setSampler(sampler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder seed(Long seed) {
|
||||
this.options.setSeed(seed);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder steps(Integer steps) {
|
||||
this.options.setSteps(steps);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder samples(Integer samples) {
|
||||
this.options.setN(samples);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder stylePreset(String stylePreset) {
|
||||
this.options.setStylePreset(stylePreset);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder stylePreset(StyleEnum styleEnum) {
|
||||
this.options.setStylePreset(styleEnum.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #N(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withN(Integer n) {
|
||||
this.options.setN(n);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #model(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withModel(String model) {
|
||||
this.options.setModel(model);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #width(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withWidth(Integer width) {
|
||||
this.options.setWidth(width);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #height(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withHeight(Integer height) {
|
||||
this.options.setHeight(height);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #responseFormat(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withResponseFormat(String responseFormat) {
|
||||
this.options.setResponseFormat(responseFormat);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #cfgScale(Float)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withCfgScale(Float cfgScale) {
|
||||
this.options.setCfgScale(cfgScale);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #clipGuidancePreset(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withClipGuidancePreset(String clipGuidancePreset) {
|
||||
this.options.setClipGuidancePreset(clipGuidancePreset);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #sampler(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSampler(String sampler) {
|
||||
this.options.setSampler(sampler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #seed(Long)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSeed(Long seed) {
|
||||
this.options.setSeed(seed);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #steps(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSteps(Integer steps) {
|
||||
this.options.setSteps(steps);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #samples(Integer)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withSamples(Integer samples) {
|
||||
this.options.setN(samples);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #stylePreset(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withStylePreset(String stylePreset) {
|
||||
this.options.setStylePreset(stylePreset);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #stylePreset(StyleEnum)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-M5")
|
||||
public Builder withStylePreset(StyleEnum styleEnum) {
|
||||
this.options.setStylePreset(styleEnum.toString());
|
||||
return this;
|
||||
|
||||
@@ -56,14 +56,14 @@ public class StabilityAiApiIT {
|
||||
.of(new StabilityAiApi.GenerateImageRequest.TextPrompts(
|
||||
"A light cream colored mini golden doodle holding a sign that says 'Heading to BARCADE !'", 0.5f));
|
||||
var builder = StabilityAiApi.GenerateImageRequest.builder()
|
||||
.withTextPrompts(textPrompts)
|
||||
.withHeight(1024)
|
||||
.withWidth(1024)
|
||||
.withCfgScale(7f)
|
||||
.withSamples(1)
|
||||
.withSeed(123L)
|
||||
.withSteps(30)
|
||||
.withStylePreset("photographic");
|
||||
.textPrompts(textPrompts)
|
||||
.height(1024)
|
||||
.width(1024)
|
||||
.cfgScale(7f)
|
||||
.samples(1)
|
||||
.seed(123L)
|
||||
.steps(30)
|
||||
.stylePreset("photographic");
|
||||
StabilityAiApi.GenerateImageRequest request = builder.build();
|
||||
StabilityAiApi.GenerateImageResponse response = this.stabilityAiApi.generateImage(request);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class StabilityAiImageModelIT {
|
||||
void imageAsBase64Test() throws IOException {
|
||||
|
||||
StabilityAiImageOptions imageOptions = StabilityAiImageOptions.builder()
|
||||
.withStylePreset(StyleEnum.PHOTOGRAPHIC)
|
||||
.stylePreset(StyleEnum.PHOTOGRAPHIC)
|
||||
.build();
|
||||
|
||||
var instructions = """
|
||||
|
||||
@@ -33,32 +33,32 @@ public class StabilityAiImageOptionsTests {
|
||||
StabilityAiApi stabilityAiApi = mock(StabilityAiApi.class);
|
||||
// Default options
|
||||
StabilityAiImageOptions defaultOptions = StabilityAiImageOptions.builder()
|
||||
.withN(1)
|
||||
.withModel("default-model")
|
||||
.withWidth(512)
|
||||
.withHeight(512)
|
||||
.withResponseFormat("image/png")
|
||||
.withCfgScale(7.0f)
|
||||
.withClipGuidancePreset("FAST_BLUE")
|
||||
.withSampler("DDIM")
|
||||
.withSeed(1234L)
|
||||
.withSteps(30)
|
||||
.withStylePreset("3d-model")
|
||||
.N(1)
|
||||
.model("default-model")
|
||||
.width(512)
|
||||
.height(512)
|
||||
.responseFormat("image/png")
|
||||
.cfgScale(7.0f)
|
||||
.clipGuidancePreset("FAST_BLUE")
|
||||
.sampler("DDIM")
|
||||
.seed(1234L)
|
||||
.steps(30)
|
||||
.stylePreset("3d-model")
|
||||
.build();
|
||||
|
||||
// Runtime options with different values
|
||||
StabilityAiImageOptions runtimeOptions = StabilityAiImageOptions.builder()
|
||||
.withN(2)
|
||||
.withModel("runtime-model")
|
||||
.withWidth(1024)
|
||||
.withHeight(768)
|
||||
.withResponseFormat("application/json")
|
||||
.withCfgScale(14.0f)
|
||||
.withClipGuidancePreset("FAST_GREEN")
|
||||
.withSampler("DDPM")
|
||||
.withSeed(5678L)
|
||||
.withSteps(50)
|
||||
.withStylePreset("anime")
|
||||
.N(2)
|
||||
.model("runtime-model")
|
||||
.width(1024)
|
||||
.height(768)
|
||||
.responseFormat("application/json")
|
||||
.cfgScale(14.0f)
|
||||
.clipGuidancePreset("FAST_GREEN")
|
||||
.sampler("DDPM")
|
||||
.seed(5678L)
|
||||
.steps(50)
|
||||
.stylePreset("anime")
|
||||
.build();
|
||||
|
||||
StabilityAiImageModel imageModel = new StabilityAiImageModel(stabilityAiApi, defaultOptions);
|
||||
@@ -86,9 +86,9 @@ public class StabilityAiImageOptionsTests {
|
||||
|
||||
StabilityAiApi stabilityAiApi = mock(StabilityAiApi.class);
|
||||
StabilityAiImageOptions defaultOptions = StabilityAiImageOptions.builder()
|
||||
.withN(1)
|
||||
.withModel("default-model")
|
||||
.withCfgScale(7.0f)
|
||||
.N(1)
|
||||
.model("default-model")
|
||||
.cfgScale(7.0f)
|
||||
.build();
|
||||
|
||||
StabilityAiImageModel imageModel = new StabilityAiImageModel(stabilityAiApi, defaultOptions);
|
||||
@@ -107,10 +107,10 @@ public class StabilityAiImageOptionsTests {
|
||||
|
||||
StabilityAiApi stabilityAiApi = mock(StabilityAiApi.class);
|
||||
StabilityAiImageOptions defaultOptions = StabilityAiImageOptions.builder()
|
||||
.withN(1)
|
||||
.withModel("default-model")
|
||||
.withWidth(512)
|
||||
.withCfgScale(7.0f)
|
||||
.N(1)
|
||||
.model("default-model")
|
||||
.width(512)
|
||||
.cfgScale(7.0f)
|
||||
.build();
|
||||
|
||||
// Create a non-StabilityAi ImageOptions implementation
|
||||
|
||||
@@ -88,10 +88,10 @@ For example to override the Stability AI specific options such as quality and th
|
||||
ImageResponse response = stabilityaiImageModel.call(
|
||||
new ImagePrompt("A light cream colored mini golden doodle",
|
||||
StabilityAiImageOptions.builder()
|
||||
.withStylePreset("cinematic")
|
||||
.withN(4)
|
||||
.withHeight(1024)
|
||||
.withWidth(1024).build())
|
||||
.stylePreset("cinematic")
|
||||
.N(4)
|
||||
.height(1024)
|
||||
.width(1024).build())
|
||||
|
||||
);
|
||||
----
|
||||
|
||||
@@ -43,7 +43,7 @@ public class StabilityAiAutoConfigurationIT {
|
||||
this.contextRunner.run(context -> {
|
||||
ImageModel imageModel = context.getBean(ImageModel.class);
|
||||
StabilityAiImageOptions imageOptions = StabilityAiImageOptions.builder()
|
||||
.withStylePreset(StyleEnum.PHOTOGRAPHIC)
|
||||
.stylePreset(StyleEnum.PHOTOGRAPHIC)
|
||||
.build();
|
||||
|
||||
var instructions = """
|
||||
|
||||
Reference in New Issue
Block a user