mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
Master dimensions move from a 2048 long-edge rule to a total-pixel budget (normalizedImageMaxPixels, default 2048x2048) with an 8192 long-edge cap, so extreme aspect ratios keep short-edge resolution. The shared quality ladder and lazy execution move to encoding.ts, review-round doc fixes land across attachment and llm packages, and the superseded facts in the unified-image-pipeline note now describe the shipped routing.
84 lines
3.2 KiB
TypeScript
84 lines
3.2 KiB
TypeScript
/** Shared quality ladder and lazy candidate execution for normalization and request-image encoders. */
|
|
|
|
import type { Sharp } from 'sharp'
|
|
|
|
/** Shared ladder for both encoders: spaced so each step buys a real size reduction. */
|
|
export const IMAGE_ENCODING_QUALITIES = [85, 75, 60] as const
|
|
/** Fixed lossy-WebP effort; deeper search costs 3-4x encode time for about 5% size. */
|
|
export const WEBP_ENCODING_EFFORT = 0
|
|
|
|
/** One ladder output carrying its complete bytes and exact facts. */
|
|
export interface EncodedImage {
|
|
data: Uint8Array
|
|
mediaType: 'image/jpeg' | 'image/webp'
|
|
width: number
|
|
height: number
|
|
}
|
|
|
|
async function encode(pipeline: Sharp, mediaType: EncodedImage['mediaType'], quality: number): Promise<EncodedImage> {
|
|
const encoded = mediaType === 'image/webp'
|
|
? pipeline.webp({ quality, effort: WEBP_ENCODING_EFFORT })
|
|
: pipeline.jpeg({ quality })
|
|
const { data, info } = await encoded.toBuffer({ resolveWithObject: true })
|
|
return { data: new Uint8Array(data), mediaType, width: info.width, height: info.height }
|
|
}
|
|
|
|
/**
|
|
* Build the lazy quality ladder for one prepared pipeline: WebP keeps a source
|
|
* alpha channel, everything else is JPEG.
|
|
* @param prepared - sized sRGB pipeline; cloned per candidate.
|
|
* @param hasAlpha - decoded source alpha fact selecting the codec.
|
|
* @returns encoders ordered from highest to lowest ladder quality.
|
|
*/
|
|
export function encodingLadder(prepared: Sharp, hasAlpha: boolean): Array<() => Promise<EncodedImage>> {
|
|
const mediaType = hasAlpha ? 'image/webp' : 'image/jpeg'
|
|
return IMAGE_ENCODING_QUALITIES.map(quality => (
|
|
() => encode(prepared.clone(), mediaType, quality)
|
|
))
|
|
}
|
|
|
|
/** One encoded candidate carrying its complete bytes. */
|
|
export interface EncodedCandidate {
|
|
data: Uint8Array
|
|
}
|
|
|
|
/** Result of exhausting candidates at one raster size without a fitting output. */
|
|
export interface ExhaustedEncoding<T extends EncodedCandidate> {
|
|
smallest: T
|
|
}
|
|
|
|
/**
|
|
* Execute encoding candidates in preference order and stop after the first fitting output.
|
|
* @param attempts - lazy encoders ordered from preferred to fallback representation.
|
|
* @param maxBytes - positive encoded-byte target.
|
|
* @returns the first fitting candidate, otherwise the smallest completed fallback.
|
|
*/
|
|
export async function encodeFirstWithinLimit<T extends EncodedCandidate>(
|
|
attempts: readonly (() => Promise<T>)[],
|
|
maxBytes: number,
|
|
): Promise<T | ExhaustedEncoding<T>> {
|
|
const [first, ...remaining] = attempts
|
|
if (first === undefined) throw new Error('image encoding requires at least one candidate')
|
|
let smallest = await first()
|
|
if (smallest.data.byteLength <= maxBytes) return smallest
|
|
for (const attempt of remaining) {
|
|
const candidate = await attempt()
|
|
if (candidate.data.byteLength <= maxBytes) return candidate
|
|
if (candidate.data.byteLength < smallest.data.byteLength) {
|
|
smallest = candidate
|
|
}
|
|
}
|
|
return { smallest }
|
|
}
|
|
|
|
/**
|
|
* Whether a lazy encoding result exhausted every candidate at one size.
|
|
* @param result - first fitting candidate or exhausted result.
|
|
* @returns whether every candidate exceeded the byte target.
|
|
*/
|
|
export function isExhaustedEncoding<T extends EncodedCandidate>(
|
|
result: T | ExhaustedEncoding<T>,
|
|
): result is ExhaustedEncoding<T> {
|
|
return 'smallest' in result
|
|
}
|