mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
refactor(packages): deslop shared runtime helpers (#162089)
* refactor(packages): deslop shared runtime helpers * refactor(packages): retain nonmutating array reversals
This commit is contained in:
parent
9a44a9a7f3
commit
1367f8d9d3
6 changed files with 27 additions and 46 deletions
|
|
@ -816,14 +816,8 @@ async function launchParallelToolCalls(
|
|||
const result: ParallelToolCallLaunches = { started: [], completed: [] };
|
||||
let cursor = 0;
|
||||
let finish!: () => void;
|
||||
let finished = false;
|
||||
const done = new Promise<void>((resolve) => {
|
||||
finish = () => {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
finish = resolve;
|
||||
});
|
||||
const launchNext = () => {
|
||||
const current = ready[cursor++];
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ export function findAssistantTranscriptRoleHeaderSpans(
|
|||
return [];
|
||||
}
|
||||
const spans: AssistantTranscriptRoleHeaderSpan[] = [];
|
||||
const sortedExcludedRanges = [...excludedRanges].toSorted(
|
||||
const sortedExcludedRanges = excludedRanges.toSorted(
|
||||
(left, right) => left.start - right.start || left.end - right.end,
|
||||
);
|
||||
let excludedRangeIndex = 0;
|
||||
|
|
|
|||
|
|
@ -188,31 +188,30 @@ export function readTagToken(
|
|||
}
|
||||
|
||||
const raw = html.slice(start + 1, end);
|
||||
const body = raw;
|
||||
let pos = 0;
|
||||
const closing = body[pos] === "/";
|
||||
const closing = raw[pos] === "/";
|
||||
if (closing) {
|
||||
pos += 1;
|
||||
}
|
||||
if (!isTagNameStartChar(body[pos] ?? "")) {
|
||||
if (!isTagNameStartChar(raw[pos] ?? "")) {
|
||||
return { token: null, next: end + 1 };
|
||||
}
|
||||
|
||||
const nameStart = pos;
|
||||
while (
|
||||
pos < body.length &&
|
||||
!isAsciiWhitespace(body.charAt(pos)) &&
|
||||
body[pos] !== "/" &&
|
||||
body[pos] !== ">"
|
||||
pos < raw.length &&
|
||||
!isAsciiWhitespace(raw.charAt(pos)) &&
|
||||
raw[pos] !== "/" &&
|
||||
raw[pos] !== ">"
|
||||
) {
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
const attrs = closing ? "" : body.slice(pos);
|
||||
const attrs = closing ? "" : raw.slice(pos);
|
||||
return {
|
||||
token: {
|
||||
closing,
|
||||
name: body
|
||||
name: raw
|
||||
.slice(nameStart, pos)
|
||||
.replace(/\0|[A-Z]/g, (ch) => (ch === "\0" ? "\uFFFD" : asciiLower(ch))),
|
||||
raw,
|
||||
|
|
|
|||
|
|
@ -511,7 +511,7 @@ function injectSpoilersIntoInline(tokens: MarkdownToken[]): MarkdownToken[] {
|
|||
const usableDelims = totalDelims - (totalDelims % 2);
|
||||
|
||||
const result: MarkdownToken[] = [];
|
||||
const state = { spoilerOpen: false };
|
||||
let spoilerOpen = false;
|
||||
let consumedDelims = 0;
|
||||
|
||||
for (const token of tokens) {
|
||||
|
|
@ -530,9 +530,7 @@ function injectSpoilersIntoInline(tokens: MarkdownToken[]): MarkdownToken[] {
|
|||
while (index < content.length) {
|
||||
const next = content.indexOf("||", index);
|
||||
if (next === -1) {
|
||||
if (index < content.length) {
|
||||
result.push(createTextToken(token, content.slice(index)));
|
||||
}
|
||||
result.push(createTextToken(token, content.slice(index)));
|
||||
break;
|
||||
}
|
||||
if (consumedDelims >= usableDelims) {
|
||||
|
|
@ -543,9 +541,9 @@ function injectSpoilersIntoInline(tokens: MarkdownToken[]): MarkdownToken[] {
|
|||
result.push(createTextToken(token, content.slice(index, next)));
|
||||
}
|
||||
consumedDelims += 1;
|
||||
state.spoilerOpen = !state.spoilerOpen;
|
||||
spoilerOpen = !spoilerOpen;
|
||||
result.push({
|
||||
type: state.spoilerOpen ? "spoiler_open" : "spoiler_close",
|
||||
type: spoilerOpen ? "spoiler_open" : "spoiler_close",
|
||||
});
|
||||
index = next + 2;
|
||||
}
|
||||
|
|
@ -741,11 +739,6 @@ function renderInlineCode(state: RenderState, content: string) {
|
|||
target.styles.push({ start, end: start + content.length, style: "code" });
|
||||
}
|
||||
|
||||
function resolveFenceLanguage(info: string | undefined): string | undefined {
|
||||
const language = info?.trim().split(/\s+/, 1)[0]?.trim();
|
||||
return language || undefined;
|
||||
}
|
||||
|
||||
function renderCodeBlock(
|
||||
state: RenderState,
|
||||
content: string,
|
||||
|
|
@ -755,13 +748,13 @@ function renderCodeBlock(
|
|||
sourceMap?: [number, number] | null,
|
||||
codeClosed?: boolean,
|
||||
) {
|
||||
let code = content ?? "";
|
||||
let code = content;
|
||||
if (!code.endsWith("\n")) {
|
||||
code = `${code}\n`;
|
||||
}
|
||||
const target = resolveRenderTarget(state);
|
||||
const start = target.text.length;
|
||||
const language = resolveFenceLanguage(info);
|
||||
const language = info?.trim().split(/\s+/, 1)[0] || undefined;
|
||||
target.text += code;
|
||||
target.styles.push(
|
||||
createStyleSpan({
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ export function createReasoningTagTextPartitioner(): ReasoningTagTextPartitioner
|
|||
const codeSpans = ownership.codeSpans;
|
||||
nonFinalRetainStart = ownership.retainStart;
|
||||
nonFinalOpenEndedCode = codeSpans.some(([, spanEnd]) => spanEnd === source.length);
|
||||
if (!codeSpans.some(([, spanEnd]) => spanEnd === source.length)) {
|
||||
if (!nonFinalOpenEndedCode) {
|
||||
compactCommittedSource(ownership.retainStart);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
indexOfAsciiMarkerIgnoreCase,
|
||||
isAsciiMarkerPrefixIgnoreCase,
|
||||
isXmlishNameChar,
|
||||
type JsonObjectScanState,
|
||||
scanJsonObject,
|
||||
skipLineIndentation,
|
||||
skipWhitespace,
|
||||
|
|
@ -75,11 +76,8 @@ type StandalonePlainTextToolCallCandidate = {
|
|||
type ScannedCallSequence = TextRange & { activeStart?: number; overCap: boolean };
|
||||
type XmlSuppressor = { carry: string; kind: "xml"; phase: "body" | "parameter" };
|
||||
|
||||
type JsonSuppressor = {
|
||||
type JsonSuppressor = JsonObjectScanState & {
|
||||
carry: string;
|
||||
depth: number;
|
||||
escaped: boolean;
|
||||
inString: boolean;
|
||||
kind: "json";
|
||||
optionalClosings?: readonly string[];
|
||||
phase: "closing" | "opening" | "payload";
|
||||
|
|
@ -578,9 +576,8 @@ function createSyntheticTextDelta(
|
|||
text: string,
|
||||
partial?: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
const event = eventTemplate(template);
|
||||
return {
|
||||
...event,
|
||||
...eventTemplate(template),
|
||||
type: "text_delta",
|
||||
delta: text,
|
||||
...(partial ? { partial } : {}),
|
||||
|
|
@ -1108,9 +1105,9 @@ export async function* normalizePlainTextToolCallStreamEvents(
|
|||
let forceScrubTerminal = false;
|
||||
let sawStreamStart = false;
|
||||
let preserveTerminalContentIndexes = false;
|
||||
const heldTextStarts = new Map<string, Record<string, unknown>>();
|
||||
const lineStarts = new Map<string, boolean>();
|
||||
const emittedTextUnits = new Map<string, number>();
|
||||
const heldTextStarts = new Map<number, Record<string, unknown>>();
|
||||
const lineStarts = new Map<number, boolean>();
|
||||
const emittedTextUnits = new Map<number, number>();
|
||||
const protectionChunks: string[] = [];
|
||||
let protectionContextLength = 0;
|
||||
let protectionContextOverflow = false;
|
||||
|
|
@ -1212,7 +1209,6 @@ export async function* normalizePlainTextToolCallStreamEvents(
|
|||
});
|
||||
return normalized?.kind === "scrubbed" ? normalized : undefined;
|
||||
};
|
||||
const eventKey = (record: Record<string, unknown>) => String(eventContentIndex(record));
|
||||
const sanitizeEventPartial = (
|
||||
record: Record<string, unknown>,
|
||||
forceKnownCandidates = false,
|
||||
|
|
@ -1272,7 +1268,7 @@ export async function* normalizePlainTextToolCallStreamEvents(
|
|||
: typeof record.content === "string"
|
||||
? record.content
|
||||
: undefined;
|
||||
const key = eventKey(record);
|
||||
const key = eventContentIndex(record);
|
||||
if (type === "text_start" && (text === undefined || text === "") && !pending) {
|
||||
const previous = heldTextStarts.get(key);
|
||||
if (previous) {
|
||||
|
|
@ -1578,12 +1574,11 @@ export async function* normalizePlainTextToolCallStreamEvents(
|
|||
if (classification.kind === "false-positive") {
|
||||
yield* replayFalsePositiveCandidate(pending);
|
||||
const replayText = pending.buffer;
|
||||
const replayedCandidate = pending;
|
||||
pending = undefined;
|
||||
if (replayText) {
|
||||
overCapSequenceOpen = false;
|
||||
lineStarts.set(key, nextAtLineStart(lineStarts.get(key) ?? true, replayText));
|
||||
advanceProtectionContext(replayedCandidate.buffer);
|
||||
advanceProtectionContext(replayText);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1693,7 +1688,7 @@ export async function* normalizePlainTextToolCallStreamEvents(
|
|||
if (template) {
|
||||
const projectedText = projectedTextForEvent(pending.template, normalized);
|
||||
const sanitizedText = projectedText ?? classification.text;
|
||||
const emittedUnits = emittedTextUnits.get(eventKey(pending.template)) ?? 0;
|
||||
const emittedUnits = emittedTextUnits.get(eventContentIndex(pending.template)) ?? 0;
|
||||
const novelText = sanitizedText.slice(projectedText ? emittedUnits : 0);
|
||||
if (novelText) {
|
||||
yield createSyntheticTextDelta(template, novelText, normalized.message);
|
||||
|
|
@ -1832,7 +1827,7 @@ export async function* normalizePlainTextToolCallStreamEvents(
|
|||
for await (const event of normalizeEvents()) {
|
||||
const record = asOptionalObjectRecord(event);
|
||||
if (record?.type === "text_delta" && typeof record.delta === "string") {
|
||||
const key = eventKey(record);
|
||||
const key = eventContentIndex(record);
|
||||
const previous = emittedTextUnits.get(key) ?? 0;
|
||||
emittedTextUnits.set(key, previous + record.delta.length);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue