Skip to main content
StringThread is the atomic data element in adaptive_harmony. A thread is a sequence of turns (role + content), combined with turn weights for training and optional metadata (metric feedback, ground truth labels, or any custom key-value pairs).

Create a StringThread

Builder methods

Each method returns a new StringThread with the turn appended:

Access turns and content

get_turns() returns every turn as a (role, content) tuple. For multimodal turns, images are represented as <|image|> in the string content. messages() returns all turns except the final one if it has the assistant role. This is useful when you need to split a thread into prompt and completion.

Multimodal StringThread

The difference with text-only is that content becomes a list of fragments instead of a plain string. There are two fragment types: Use StringThread.from_fragments() to create a multimodal thread. Don’t forget the await! from_fragments is async because it loads and decodes images.
You can also pass fragments as plain dictionaries:
A text-only StringThread is equivalent to a fragment thread with a single TextFragment:
Only user and system roles can contain images. The assistant role must be text-only.
The fragment format in Harmony differs from the chat completions API. In Harmony, image fragments use {"type": "image", "url": "..."}, while the chat completions API uses {"type": "image_url", "image_url": {"url": "..."}}.

Image encoding

ImageFragment expects a url field with the full data URI. To base64-encode a local image, use the built-in helper:
image_to_base64 returns the raw base64 string (without the data:... prefix). It also allows you to resize images and convert to grayscale:

Supported image formats

The formats accepted depend on the context:
  • In recipes (adaptive_harmony): most image formats are supported (PNG, JPEG, GIF, WebP, BMP, TIFF, etc.). Images can be loaded from file paths, HTTP URLs, or data: URIs.
  • Via the chat completions API (SDK / OpenAI client): only PNG, JPEG, GIF, and WebP are accepted, and only data: URIs, HTTP URLs are rejected.
We recommend using PNG or JPEG for maximum compatibility.

Turn weighting

During training, turn weights control how much each turn contributes to the loss. A weight of 0.0 means the model does not learn from that turn, while 1.0 means it contributes fully. This is how you tell the model which parts of a conversation to learn from: typically you want the model to learn from assistant responses, not from user prompts or system messages. By default, turns added with .assistant() get a weight of 1.0 and all other roles get 0.0. When you load a dataset that contains completions, with_weight_last_assistant_turn() is applied automatically: only the final assistant turn is weighted. You can override this after loading using one of the methods below.

Weighting methods

Each method returns a new StringThread with updated weights:

Inspect weights