Skip to content

Add an Open Design provider (per-model usage from its local event logs) #558

Description

@ozymandiashh

Summary

Open Design (nexu-io/open-design) is a local-first, open-source AI design tool that turns prompts into design artifacts by orchestrating the coding-agent CLIs you already use (Codex, Hermes, Cursor, OpenCode, Copilot, Qwen, Kimi, and more). When you build with it, it spends real model tokens, but that spend is currently invisible to codeburn. I would like to add an open-design provider, and I have it implemented and validated against my own Open Design history. Filing first per the contributing guide to confirm you would welcome it before opening the PR.

Why it is invisible today

Open Design does not write the underlying CLIs' session stores (it does not, for example, drop files into ~/.codex/sessions the way the Codex CLI does). Instead it keeps its own per-run event log. So none of codeburn's existing providers see Open Design usage, even when the run is driven by a model codeburn already prices.

Where Open Design stores usage (verified on real data, 26 runs)

One append-only JSONL event log per run:

~/Library/Application Support/Open Design/namespaces/<ns>/data/runs/<run-uuid>/events.jsonl   # macOS
~/.config/Open Design/namespaces/<ns>/data/runs/<run-uuid>/events.jsonl                       # Linux
%APPDATA%/Open Design/namespaces/<ns>/data/runs/<run-uuid>/events.jsonl                        # Windows

Each line is { id, event, data, timestamp } (timestamp is epoch milliseconds, a number). The fields that matter:

  • event: "start" carries data.model (the run's initial model).
  • event: "agent", data.type: "status" carries data.model when the model changes mid-run (a single run can switch backends, e.g. it tries openai-codex:gpt-5.5 then settles on glm-5.2).
  • event: "agent", data.type: "usage" carries data.usage = { input_tokens, output_tokens, cached_read_tokens, thought_tokens, total_tokens }. The model is NOT on the usage event, so it has to be correlated to the last-seen model.

Two facts I verified from the raw numbers and which matter for correct pricing:

  1. total_tokens == input_tokens + output_tokens, and cached_read_tokens is a subset of input_tokens (the cache-read portion is already inside the input count). So the uncached input is input_tokens - cached_read_tokens; charging the full input_tokens plus cached_read_tokens again would double-bill the cached portion.
  2. data.costUsd is usually null for these backends, so cost has to be computed from tokens via the existing pricing table.

What the provider does

  • Discovers namespaces/*/data/runs/*/events.jsonl across the three platforms, with a CODEBURN_OPEN_DESIGN_DIR override for tests.
  • Streams each run, tracking the current model from start and status events, and emits one call per usage event attributed to the model active at that point. This gives correct per-model attribution even in mixed-model runs.
  • Maps tokens onto the existing pricing engine: uncached input, output, cache-read, and reasoning (thought_tokens), with the cache-read amount subtracted from input before pricing (the same normalization codex.ts already does).
  • Resolves openai-codex:gpt-5.5 to gpt-5.5 pricing via a one-line alias; glm-5.2 already resolves in the snapshot.

Coverage and an honest ceiling

This captures everything Open Design actually records as usage. Backends that emit a usage frame (in my data: glm-5.2, openai-codex:gpt-5.5) are fully accounted with a per-field token breakdown. Backends/CLIs that do not emit usage at all (aborted runs, and CLIs that simply do not report token counts) have no token data anywhere on disk, so they cannot be priced. That is a limit of the upstream CLI, not of the provider, and such rows are left out rather than guessed.

Status

Implemented as a single new provider file plus registration, one pricing alias, and a fixture-based test. On my real Open Design history it reports per-model spend (e.g. GLM-5.2 with the correct uncached-input and cache-read split). Happy to open the PR if you are open to it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions