FLUX.1 Kontext Max as a custom tool for Discourse AI

AI-generated summary

sam introduces FLUX.1 Kontext, a new image generation and editing model from Black Forest Labs, which is more affordable than OpenAI’s models and delivers high-quality results. The post explains how to integrate FLUX.1 Kontext into Discourse AI as a custom tool by signing up at bfl.ai, obtaining an API key, and purchasing credits. Key steps include defining the tool in /admin/plugins/discourse-ai/ai-tools with parameters like prompt, input_image, seed, and aspect_ratio. The tool’s JavaScript implementation uses http.post to send requests to FLUX’s API, polls for results, and uploads the generated image to Discourse using base64 encoding. Advanced features showcased include making external HTTP requests, handling base64 payloads, and dynamically inserting generated images into posts via chain.setCustomRaw. The post also references Discourse AI PR #1391, which adds necessary functionality for this integration. Two example images generated by the tool are displayed: one of a man near a window with a guitar, and another of an animated man in a sunlit room with plants. This integration demonstrates Discourse AI’s flexibility in supporting third-party AI models with custom tooling.

Last week, Flux released a very impressive model called FLUX.1 Kontext.

Announcement Blog by Black Forest Labs

It is particularly interesting because it is slightly cheaper than the OpenAI models, which are available via the designer persona, and it has excellent results.

In Action

In this post, I wanted to share how you can add the tool to do so, and walk through some advanced features in Discourse AI.

The tool to do the job

To define the tool, you will need to sign up at https://bfl.ai, generate an API key, and purchase some credits.

With this in place:

Define a new custom tool in /admin/plugins/discourse-ai/ai-tools

Description

Advanced image creator and editor - capable of editing Discourse uploads denoted as upload://…

Summary

Edits or creates images using FLUX Kontext

Parameters

  • prompt: string: Describe what you want to generate. 2-3 sentences, be detailed for best results (required)
  • input_image: string: an upload://… which you wish to modify
  • seed: number: The random seed. If you wish to keep outputs in the same style, keep the number the same
  • aspect_ratio: string: The aspect ratio of the image, must be between 21:9 and 9:21. For square images, use 1:1. Defaults to 16:9

Script

const apiKey = YOUR_API_KEY;
const apiUrl = "https://api.us1.bfl.ai/v1/flux-kontext-max"; 

function invoke(params) {
  let seed = parseInt(params.seed);
  if (!(seed > 0)) {
    seed = Math.floor(Math.random() * 1000000) + 1;
  }

  const body = {
    prompt: params.prompt,
    seed: seed,
    aspect_ratio: params.aspect_ratio || "16:9"
  };

  // Add input_image if provided
  if (params.input_image) {
    body.input_image = upload.getBase64(params.input_image);
  }

  const result = http.post(apiUrl, {
    headers: {
      "x-key": apiKey,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(body)
  });

  if (result.status !== 200) {
    return { error: `API request failed with status ${result.status}`, body: body };
  }
  
  const parsed = JSON.parse(result.body);
  const pollingUrl = parsed.polling_url;
  
  let pollResult = JSON.parse(http.get(pollingUrl).body);
  let checks = 0;
  
  while (pollResult.status === "Pending" && checks < 30) {
      sleep(1000);
      pollResult = JSON.parse(http.get(pollingUrl).body);
      checks++;
  } 
  
  let image;
  
  if (pollResult.status === "Ready") {
      const imageUrl = pollResult.result.sample;
      const base64 = http.get(imageUrl, { base64Encode: true }).body;
      image = upload.create("generated_image.jpg", base64);
      
      const raw = `\n\n![${params.prompt}](${image.short_url})`;
  
      chain.setCustomRaw(raw);
   }
  
  return { 
    result: "Image generated successfully", 
    seed: seed,
    aspect_ratio: params.aspect_ratio || "16:9",
    output_image: image?.short_url
  };
}

function details() {
  return "Generated image using Segmind's Flux Kontext Max model";
}

Commentary

This showcases some of the more advanced tool facilities, including quite a few added in https://github.com/discourse/discourse-ai/pull/1391, which will be required prior to this working.

  1. Making POST requests with http.post — custom tools can post to any URL!
const result = http.post(apiUrl, {
  headers: {
    "x-key": apiKey,
    "Content-Type": "application/json"
  },
  body: JSON.stringify(body)
});
  1. Support for base64-encoded payloads in the API

Get Base64 encoded upload:

body.input_image = upload.getBase64(params.input_image);

Get the result of an HTTP request in Base64:

const base64 = http.get(imageUrl, { base64Encode: true }).body;

Create an upload from a base64 string:

image = upload.create("generated_image.jpg", base64);
  1. Forcing rendering on a post to avoid guesswork and save tokens:
chain.setCustomRaw(raw);
  1. The API involves polling; Discourse AI provides a sleep primitive to wait between polls:
while (pollResult.status === "Pending" && checks < 30) {
  sleep(1000);
  pollResult = JSON.parse(http.get(pollingUrl).body);
  checks++;
}

Hope you find this helpful! Feel free to ask questions or share ideas!

4 Likes