| |
| export const SITE_MAX_NEW_TOKENS = 300; |
|
|
| export const DEFAULT_MAX_NEW_TOKENS = 200; |
|
|
| export type MaxNewTokensParseErrorCode = 'empty' | 'invalid' | 'exceeds_site'; |
|
|
| export class MaxNewTokensParseError extends Error { |
| readonly code: MaxNewTokensParseErrorCode; |
|
|
| constructor(code: MaxNewTokensParseErrorCode) { |
| super(code); |
| this.code = code; |
| this.name = 'MaxNewTokensParseError'; |
| } |
| } |
|
|
| export function parseMaxNewTokens(raw: string, admin: boolean): number { |
| const t = raw.trim(); |
| if (t === '') { |
| throw new MaxNewTokensParseError('empty'); |
| } |
| if (!/^\d+$/.test(t)) { |
| throw new MaxNewTokensParseError('invalid'); |
| } |
| const n = parseInt(t, 10); |
| if (n <= 0) { |
| throw new MaxNewTokensParseError('invalid'); |
| } |
| if (!admin && n > SITE_MAX_NEW_TOKENS) { |
| throw new MaxNewTokensParseError('exceeds_site'); |
| } |
| return n; |
| } |
|
|
| export function isMaxNewTokensRawValid(raw: string, admin: boolean): boolean { |
| try { |
| parseMaxNewTokens(raw, admin); |
| return true; |
| } catch { |
| return false; |
| } |
| } |
|
|
| export function ensureMaxNewTokensInputNotEmpty(input: HTMLInputElement | null): void { |
| if (input && input.value.trim() === '') { |
| input.value = String(DEFAULT_MAX_NEW_TOKENS); |
| } |
| } |
|
|
| |
| export function syncMaxNewTokensInputSiteMax( |
| input: HTMLInputElement | null, |
| admin: boolean |
| ): void { |
| if (!input) return; |
| if (admin) { |
| input.removeAttribute('max'); |
| } else { |
| input.max = String(SITE_MAX_NEW_TOKENS); |
| } |
| } |
|
|
| type TrFn = (text: string) => string; |
| type TrfFn = (text: string, vars: Record<string, string | number>) => string; |
|
|
| export function formatMaxNewTokensParseError( |
| code: MaxNewTokensParseErrorCode, |
| tr: TrFn, |
| trf: TrfFn |
| ): string { |
| if (code === 'exceeds_site') { |
| return trf('Max new tokens must not exceed {limit}', { limit: SITE_MAX_NEW_TOKENS }); |
| } |
| return tr('Max new tokens must be a positive integer'); |
| } |
|
|
| |
| export function finalizeMaxNewTokensInput( |
| input: HTMLInputElement | null, |
| admin: boolean, |
| onAlert: (message: string) => void, |
| tr: TrFn, |
| trf: TrfFn |
| ): boolean { |
| ensureMaxNewTokensInputNotEmpty(input); |
| if (!input) return false; |
| try { |
| parseMaxNewTokens(input.value, admin); |
| return true; |
| } catch (e) { |
| if (!(e instanceof MaxNewTokensParseError)) throw e; |
| onAlert(formatMaxNewTokensParseError(e.code, tr, trf)); |
| input.value = |
| e.code === 'exceeds_site' |
| ? String(SITE_MAX_NEW_TOKENS) |
| : String(DEFAULT_MAX_NEW_TOKENS); |
| return false; |
| } |
| } |
|
|