Run models on sensible generation defaults out of the box

A fresh install had no generation settings until someone set one, so every
model ran on whatever its provider happened to use. Each harness now has a
default (text 0.3 temperature, vision 0.2, image the provider's own), shown
in the model settings modal so what a model runs on is never a guess.

The defaults are only sent to a model whose catalog lists the field, so a
provider that says nothing about its models still gets no parameter it
might reject; anything saved on a harness or bot wins over the default.
This commit is contained in:
2026-10-22 04:56:10 +01:00
parent c2553a4746
commit 8edff8988a
6 changed files with 150 additions and 13 deletions
+7
View File
@@ -1,5 +1,6 @@
import { describe, expect, test } from "bun:test";
import {
harnessParameterDefault,
HarnessParameters,
harnessParametersFromOptions,
modelTakesParameter,
@@ -40,6 +41,12 @@ describe("harness parameters", () => {
).toBe(false);
});
test("ships a literal text default and a lookup vision default", () => {
expect(harnessParameterDefault({ harness: "text", key: "temperature" })).toBe(0.3);
expect(harnessParameterDefault({ harness: "vision", key: "temperature" })).toBe(0);
expect(harnessParameterDefault({ harness: "text", key: "seed" })).toBeUndefined();
});
test("a model that lists no parameters takes them all", () => {
expect(
modelTakesParameter({ parameter: temperature, supported: [] }),
+70
View File
@@ -118,6 +118,76 @@ export const HarnessParameterHarnesses = ["text", "vision"] as const;
export const HarnessParameterKeys = HarnessParameters.map((parameter) => parameter.key);
/**
* What each chat harness uses when nobody set anything, so a fresh install
* is ready to go: bots are literal and repeatable (`Cobble`'s job is doing
* the work it was asked for, not improvising), while media reading is a
* lookup, so it stays at 0.
*/
export const HarnessParameterDefaults: Partial<
Record<
(typeof HarnessParameterHarnesses)[number],
Partial<Record<HarnessParameterKey, HarnessParameterValue>>
>
> = {
text: { temperature: 0.3 },
vision: { temperature: 0 },
};
/**
* One setting's value when the harness's own has none.
*/
export function harnessParameterDefault({
harness,
key,
}: {
harness: (typeof HarnessParameterHarnesses)[number];
key: HarnessParameterKey;
}) {
return HarnessParameterDefaults[harness]?.[key];
}
/**
* A setting's value for a harness: what it was saved as, else the app's
* default. `saved` says which, since a default is only used on a model
* that says it takes the field (a saved one is sent to any model whose
* catalog doesn't rule it out).
*/
export function harnessParameterValue({
harness,
params,
key,
}: {
harness: (typeof HarnessParameterHarnesses)[number];
params?: Record<string, unknown>;
key: HarnessParameterKey;
}): { value?: HarnessParameterValue; saved: boolean } {
const saved = params?.[key];
if (typeof saved == "number" || (typeof saved == "string" && saved)) {
return { value: saved, saved: true };
}
return { value: harnessParameterDefault({ harness, key }), saved: false };
}
/**
* Whether a model's provider says it takes a setting (no guessing).
*/
export function modelAdvertisesParameter({
parameter,
supported,
}: {
parameter: HarnessParameter;
supported?: string[];
}) {
return Boolean(
supported?.includes(
"advertised" in parameter ? parameter.advertised : parameter.key,
),
);
}
/**
* The generation settings a saved `options` object holds.
*/