TokenTrace / client /src /shared /cross /completionOptionsRow.ts
cccmmd
init: TokenTrace - LLM interpretability toolbox
76b5743
Raw
History Blame Contribute Delete
6.36 kB
import type { BaseType, Selection } from 'd3';
import { tr, trf } from '../lang/i18n-lite';
import type { PredictionAttributeModelVariant } from '../prediction_attribution/core/attributionResultCache';
import { showAlertDialog } from '../ui/dialog';
import { lsReadEnum, lsReadNumber, lsSet, lsWriteString } from '../storage/localStorageHelpers';
import {
DEFAULT_MAX_NEW_TOKENS,
finalizeMaxNewTokensInput,
formatMaxNewTokensParseError,
isMaxNewTokensRawValid,
MaxNewTokensParseError,
parseMaxNewTokens as parseMaxNewTokensShared,
syncMaxNewTokensInputSiteMax,
} from './maxNewTokensConfig';
import { validateMetricsElements } from './textMetricsUpdater';
const MODEL_VARIANT_SELECT_ID = 'completion_model_variant';
const MAX_NEW_TOKENS_INPUT_ID = 'completion_max_new_tokens';
export type CompletionOptionsRowOptions = {
isSkipChatTemplate: () => boolean;
metricModel: Selection<BaseType, unknown, HTMLElement, unknown>;
alertDialogTitle: string;
onStateChange: () => void;
adminMode: () => boolean;
modelVariantStorageKey: string;
maxNewTokensStorageKey: string;
urlModelVariant?: PredictionAttributeModelVariant | null;
};
export type CompletionOptionsRowApi = {
modelVariantSelect: HTMLSelectElement | null;
maxTokensInput: HTMLInputElement | null;
currentModelVariant: () => PredictionAttributeModelVariant;
currentMaxTokens: () => number;
parseMaxNewTokensFromField: () => number;
isMaxNewTokensInputValid: () => boolean;
readStoredModelVariant: () => PredictionAttributeModelVariant;
readStoredMaxTokens: () => number;
syncModelVariantUi: () => void;
syncIdleModelMetric: () => void;
syncMaxTokensUi: () => void;
normalizeMaxTokensField: () => boolean;
persistMaxTokens: () => void;
};
export function createCompletionOptionsRow(
options: CompletionOptionsRowOptions
): CompletionOptionsRowApi {
const modelVariantSelect = document.getElementById(
MODEL_VARIANT_SELECT_ID
) as HTMLSelectElement | null;
const maxTokensInput = document.getElementById(
MAX_NEW_TOKENS_INPUT_ID
) as HTMLInputElement | null;
const readStoredModelVariant = (): PredictionAttributeModelVariant =>
lsReadEnum(options.modelVariantStorageKey, ['base', 'instruct'] as const, 'instruct');
const readStoredMaxTokens = (): number =>
lsReadNumber(options.maxNewTokensStorageKey, DEFAULT_MAX_NEW_TOKENS, {
validate: (n) => isMaxNewTokensRawValid(String(n), options.adminMode()),
});
const currentModelVariant = (): PredictionAttributeModelVariant => {
if (!options.isSkipChatTemplate()) return 'instruct';
const v = modelVariantSelect?.value;
return v === 'base' || v === 'instruct' ? v : 'instruct';
};
const syncIdleModelMetric = (): void => {
if (!validateMetricsElements(options.metricModel)) return;
options.metricModel.text(`${tr('model')}: ${currentModelVariant()}`);
};
const syncModelVariantUi = (): void => {
if (!modelVariantSelect) return;
const skip = options.isSkipChatTemplate();
if (skip) {
modelVariantSelect.disabled = false;
modelVariantSelect.value = readStoredModelVariant();
} else {
modelVariantSelect.disabled = true;
modelVariantSelect.value = 'instruct';
}
syncIdleModelMetric();
};
const parseMaxNewTokensFromField = (): number => {
try {
return parseMaxNewTokensShared(maxTokensInput?.value ?? '', options.adminMode());
} catch (e) {
if (e instanceof MaxNewTokensParseError) {
throw new Error(formatMaxNewTokensParseError(e.code, tr, trf));
}
throw e;
}
};
const currentMaxTokens = (): number =>
parseMaxNewTokensShared(
maxTokensInput?.value ?? String(DEFAULT_MAX_NEW_TOKENS),
options.adminMode()
);
const isMaxNewTokensInputValid = (): boolean =>
isMaxNewTokensRawValid(maxTokensInput?.value ?? '', options.adminMode());
const normalizeMaxTokensField = (): boolean => {
const ok = finalizeMaxNewTokensInput(
maxTokensInput,
options.adminMode(),
(msg) => showAlertDialog(options.alertDialogTitle, msg),
tr,
trf
);
options.onStateChange();
return ok;
};
const persistMaxTokens = (): void => {
if (!maxTokensInput) return;
try {
const n = parseMaxNewTokensFromField();
lsSet(options.maxNewTokensStorageKey, String(n));
} catch {
/* 非法值不写 storage */
}
};
const syncMaxTokensUi = (): void => {
if (maxTokensInput) {
maxTokensInput.value = String(readStoredMaxTokens());
}
syncMaxNewTokensInputSiteMax(maxTokensInput, options.adminMode());
};
if (options.urlModelVariant && modelVariantSelect) {
modelVariantSelect.value = options.urlModelVariant;
} else if (modelVariantSelect) {
modelVariantSelect.value = readStoredModelVariant();
}
syncMaxTokensUi();
modelVariantSelect?.addEventListener('change', () => {
if (!options.isSkipChatTemplate()) return;
lsWriteString(options.modelVariantStorageKey, currentModelVariant());
syncIdleModelMetric();
options.onStateChange();
});
maxTokensInput?.addEventListener('change', () => {
if (!normalizeMaxTokensField()) return;
lsSet(
options.maxNewTokensStorageKey,
maxTokensInput?.value ?? String(DEFAULT_MAX_NEW_TOKENS)
);
options.onStateChange();
});
maxTokensInput?.addEventListener('input', () => options.onStateChange());
maxTokensInput?.addEventListener('blur', () => {
normalizeMaxTokensField();
});
return {
modelVariantSelect,
maxTokensInput,
currentModelVariant,
currentMaxTokens,
parseMaxNewTokensFromField,
isMaxNewTokensInputValid,
readStoredModelVariant,
readStoredMaxTokens,
syncModelVariantUi,
syncIdleModelMetric,
syncMaxTokensUi,
normalizeMaxTokensField,
persistMaxTokens,
};
}