mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(code-runtime): bound hostile output accounting
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
/** JSON string-prefix accounting for the outer-output ledger. @module @deepseek-ai/dsh-code-runtime-worker/output-json */
|
||||
|
||||
import type { CodeJsonValue } from '@deepseek-ai/dsh-code-runtime'
|
||||
|
||||
/** Control characters with a two-byte short JSON escape instead of `\u00XX`. */
|
||||
const SHORT_ESCAPE_CODES = new Set([0x08, 0x09, 0x0a, 0x0c, 0x0d])
|
||||
|
||||
@@ -13,6 +15,69 @@ function serializedCharacterBytes(character: string): number {
|
||||
return Buffer.byteLength(character, 'utf8')
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure one JSON string without materializing its complete escaped form.
|
||||
* @param text - the candidate string.
|
||||
* @param maxBytes - largest serialized size the caller can admit.
|
||||
* @returns Exact serialized bytes, or `undefined` as soon as the cap is crossed.
|
||||
*/
|
||||
export function jsonStringBytesUpTo(text: string, maxBytes: number): number | undefined {
|
||||
if (maxBytes < 2) return undefined
|
||||
let bytes = 2
|
||||
for (const character of text) {
|
||||
bytes += serializedCharacterBytes(character)
|
||||
if (bytes > maxBytes) return undefined
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure one lossless JSON value without allocating its serialized form.
|
||||
* @param value - already validated lossless JSON.
|
||||
* @param maxBytes - largest serialized size the caller can admit.
|
||||
* @returns Exact serialized bytes, or `undefined` as soon as the cap is crossed.
|
||||
*/
|
||||
export function jsonValueBytesUpTo(value: CodeJsonValue, maxBytes: number): number | undefined {
|
||||
if (value === null) return maxBytes >= 4 ? 4 : undefined
|
||||
if (typeof value === 'string') return jsonStringBytesUpTo(value, maxBytes)
|
||||
if (typeof value === 'number') {
|
||||
const bytes = Buffer.byteLength(String(value), 'utf8')
|
||||
return bytes <= maxBytes ? bytes : undefined
|
||||
}
|
||||
if (typeof value === 'boolean') {
|
||||
const bytes = value ? 4 : 5
|
||||
return bytes <= maxBytes ? bytes : undefined
|
||||
}
|
||||
|
||||
let bytes = 2
|
||||
if (bytes > maxBytes) return undefined
|
||||
if (Array.isArray(value)) {
|
||||
for (let index = 0; index < value.length; index++) {
|
||||
if (index > 0 && ++bytes > maxBytes) return undefined
|
||||
const item = value[index]
|
||||
if (item === undefined) return undefined
|
||||
const itemBytes = jsonValueBytesUpTo(item, maxBytes - bytes)
|
||||
if (itemBytes === undefined) return undefined
|
||||
bytes += itemBytes
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
let entries = 0
|
||||
for (const [key, item] of Object.entries(value)) {
|
||||
if (entries > 0 && ++bytes > maxBytes) return undefined
|
||||
const keyBytes = jsonStringBytesUpTo(key, maxBytes - bytes)
|
||||
if (keyBytes === undefined) return undefined
|
||||
bytes += keyBytes + 1
|
||||
if (bytes > maxBytes) return undefined
|
||||
const itemBytes = jsonValueBytesUpTo(item, maxBytes - bytes)
|
||||
if (itemBytes === undefined) return undefined
|
||||
bytes += itemBytes
|
||||
entries += 1
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the longest code-point-aligned prefix whose JSON string encoding,
|
||||
* including its surrounding quotes, fits `maxBytes`.
|
||||
@@ -23,7 +88,6 @@ function serializedCharacterBytes(character: string): number {
|
||||
*/
|
||||
export function truncateJsonStringBytes(text: string, maxBytes: number): string {
|
||||
if (maxBytes < 2) return ''
|
||||
if (Buffer.byteLength(JSON.stringify(text), 'utf8') <= maxBytes) return text
|
||||
let bytes = 2
|
||||
let end = 0
|
||||
for (const character of text) {
|
||||
@@ -32,5 +96,5 @@ export function truncateJsonStringBytes(text: string, maxBytes: number): string
|
||||
bytes += cost
|
||||
end += character.length
|
||||
}
|
||||
return text.slice(0, end)
|
||||
return end === text.length ? text : text.slice(0, end)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user