| |
| |
|
|
| export interface LogitsLike { |
| dims: readonly number[]; |
| data: ArrayLike<number> & { subarray(begin: number, end: number): any; length: number }; |
| } |
|
|
| |
| |
| |
| |
| export function createTopPProcessor(topP = 0.95) { |
| if (!(topP > 0 && topP <= 1)) throw new Error('topP must be in (0, 1].'); |
| let scratch: Float32Array | undefined; |
|
|
| return (_inputIds: unknown, logits: LogitsLike) => { |
| if (topP === 1) return logits; |
| const vocab = logits.dims[logits.dims.length - 1]; |
| if (!scratch || scratch.length !== vocab) scratch = new Float32Array(vocab); |
| const t = scratch; |
|
|
| for (let offset = 0; offset < logits.data.length; offset += vocab) { |
| const row = logits.data.subarray(offset, offset + vocab) as Float32Array; |
|
|
| let max = -Infinity; |
| for (let i = 0; i < vocab; i++) max = Math.max(max, row[i]); |
| if (!Number.isFinite(max)) throw new Error('The model produced invalid sampling scores.'); |
|
|
| let total = 0; |
| for (let i = 0; i < vocab; i++) total += Math.exp(row[i] - max); |
| const allowedTail = (1 - topP) * total; |
|
|
| |
| let threshold = max - 8; |
| let count = 0; |
| let dropped = 0; |
| for (;;) { |
| count = 0; |
| dropped = 0; |
| for (let i = 0; i < vocab; i++) { |
| if (row[i] >= threshold) t[count++] = row[i]; |
| else dropped += Math.exp(row[i] - max); |
| } |
| if (dropped <= allowedTail) break; |
| threshold -= 8; |
| } |
|
|
| |
| t.subarray(0, count).sort(); |
| let f = 0; |
| while (f < count - 1) { |
| const next = dropped + Math.exp(t[f] - max); |
| if (next > allowedTail) break; |
| dropped = next; |
| f++; |
| } |
|
|
| |
| const cutoff = f ? t[f - 1] : threshold; |
| let ties = 0; |
| for (let i = f - 1; i >= 0 && t[i] === cutoff; i--) ties++; |
| for (let i = 0; i < vocab; i++) { |
| if (row[i] < cutoff || (row[i] === cutoff && ties-- > 0)) row[i] = -Infinity; |
| } |
| } |
| return logits; |
| }; |
| } |
|
|
| |
| export class RateMeter { |
| total = 0; |
| private firstAt: number | null = null; |
| private points: Array<{ at: number; total: number }> = []; |
|
|
| constructor( |
| private windowMs = 1000, |
| private minimumMs = 250, |
| ) {} |
|
|
| add(count: number, at: number) { |
| if (count <= 0) return; |
| this.firstAt ??= at; |
| this.total += count; |
| this.points.push({ at, total: this.total }); |
| this.prune(at); |
| } |
|
|
| private prune(now: number) { |
| const cutoff = now - this.windowMs; |
| while (this.points.length > 1 && this.points[1].at <= cutoff) this.points.shift(); |
| } |
|
|
| rate(now: number): number | null { |
| if (this.firstAt === null || now - this.firstAt < this.minimumMs) return null; |
| this.prune(now); |
| const span = Math.min(this.windowMs, now - this.firstAt); |
| return ((this.total - this.points[0].total) * 1000) / span; |
| } |
| } |
|
|