Build Admix

KINDAreplaces $10/mosaves $120/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A multi-model chat client where the engineering is concurrent streaming from providers that behave differently, and the design constraint is that side-by-side comparison must not be presented as a form of verification.

estimated effort weekendthe files for this build are in the project pack

ProvidersA thin adapter per provider with a shared streaming interfaceConcurrencyRequests issued in parallel with independent failureCostToken counting per request against current published pricingComparisonDifference highlighting, never a scored winnerStorageLocal, with per-conversation provider choice

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the client and the concurrent streaming.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. per token; budget for development being more expensive than you expect

    Why The comparison is uninteresting with fewer, and provider differences in streaming, errors and refusals only appear across several.

    Get it Accounts with three providers, using the smallest models while developing since every test costs money three times over.

    Verify All three stream a response successfully

  3. have readyfree to obtain

    Why The cost display is only useful if it is right, and provider pricing changes without much notice.

    Get it Take current published pricing per model, record it with the date you checked and the source.

    Verify A dated pricing record exists per model with a source link

  4. decidefree to decide

    Why This is a category where costs multiply by the number of providers and run away quietly, and the limit is enforced in code.

    Get it Decide a monthly figure you would be comfortable spending before you have sent anything.

    Verify A figure is written down and configured before the first request

  5. have readyfree

    Why The build declines to judge correctness, but you need to see for yourself how often models agree confidently and wrongly, since that is the reason for the constraint.

    Get it Write ten prompts with verifiable answers, including two where the answer is obscure enough that models are likely to guess.

    Verify At least one prompt produces confident agreement on a wrong answer

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- conversations(id, title, created_at, providers_json)
- turns(id, conversation_id, ordinal, prompt, at)
- responses(id, turn_id, provider, model, body, finished, error, latency_ms, tokens_in, tokens_out, cost_minor)
- pricing(provider, model, input_per_million_minor, output_per_million_minor, effective_from, checked_on)
- differences(turn_id, kind, detail_json) · kind is factual_disagreement, refusal_divergence or format_divergence
- budgets(period, limit_minor, spent_minor) · because this is a category where costs run away quietly
- There is deliberately no column scoring which response was best, because a stored score would be displayed and it would be invented.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DB_PATHrequired~/.local/share/admix/chats.dbConversations and cost records. Contains whatever you asked about.
KEYS_ENCRYPTION_KEYsecretrequireda keychain referenceEncrypts provider keys at rest. Without it they sit in plain text.
PRICING_PATHrequired./config/pricing.jsonDated pricing per model. The file that keeps the cost display honest.
PRICING_RECHECK_DAYSrequired30After this a pricing entry is surfaced as stale.
MONTHLY_BUDGET_MINORrequired2000Checked before issuing. Refuses rather than warns.
SHOW_RANKINGrequiredneverPresent as a constant so the answer is visible. A ranking would be invented.

The build, in order

  1. Several providers, one interface

    One prompt streams from three providers concurrently.

    1. Thin. Each provider's streaming format and error shape differ.

      Files src/providers/

    2. The slowest provider should not set the pace for the others.

      Files src/stream.ts

    3. They mean different things and conflating them hides useful information.

      Files src/normalise.ts

    done when · tick each as it passes
    watch out
    • Waiting for all providers before showing anything makes the whole interface as slow as the slowest one, which is often several times the fastest. Stream each independently and let them finish at their own pace, showing which are still running.
  2. Knowing what it cost

    Every request shows its actual cost against current pricing.

    1. Both directions. Output is usually the expensive half.

      Files src/tokens.ts

    2. Hard-coded prices go quietly wrong and are trusted anyway.

      Files config/pricing.json

    3. A cost display that is wrong is worse than none.

      Files src/pricing.ts

    done when · tick each as it passes
    watch out
    • Hard-coding prices means the figures silently become wrong when a provider changes them, and a tool whose cost display is wrong is worse than one with no cost display, because it is trusted. Keep pricing as dated data with a checked date and flag entries that have gone stale.
  3. Showing difference without implying a winner

    You can see where models disagree and are not told who is right.

    1. Wording differs always. Substance differing is the interesting case.

      Files src/diff.ts

    2. They agree. That is the whole claim.

      Files src/compare.ts

    3. Its absence is the design. A score would be invented.

      Files tests/no-ranking.test.ts

    done when · tick each as it passes
    watch out
    • Any score, badge or ordering that suggests one answer is better will be read as a verdict, and there is no basis for one. Show the differences and let the reader judge, and if you cannot resist the urge to rank, remember the ranking would be produced by another model with the same limitations.
  4. Spending that cannot run away

    A budget stops requests before the bill surprises you.

    1. Refusing is the point. A warning after the fact is a receipt.

      Files src/budget.ts

    2. Four providers is four times. Obvious and still surprising.

      Files src/estimate.ts

    3. And recorded, so the pattern is visible to you.

      Files src/budget.ts

    done when · tick each as it passes
    watch out
    • Sending one prompt to four providers costs four times as much, which is obvious and still surprises people at the end of the month, particularly with long context. Show the multiplied cost before sending, not after.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more ai assistants.