mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-22 10:25:20 -06:00
fix(skills): bound evaluator text without splitting surrogate pairs (#117673)
This commit is contained in:
@@ -759,6 +759,61 @@ describe("Skill Workshop proposal evaluation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not split surrogate pairs when bounding evaluator text", async () => {
|
||||
const workspaceDir = await tempDirs.make("openclaw-skill-evaluation-surrogate-");
|
||||
const proposal = await proposeCreateSkill({
|
||||
workspaceDir,
|
||||
agentId: "main",
|
||||
name: "Surrogate Bounds",
|
||||
description: "Bound evaluator text without splitting surrogates",
|
||||
content: "# Surrogate Bounds\n",
|
||||
});
|
||||
hookMocks.evaluate.mockResolvedValue([
|
||||
{
|
||||
evaluatorId: "emoji-bounds",
|
||||
pluginId: "evaluation-tests",
|
||||
status: "completed",
|
||||
result: {
|
||||
summary: `${"s".repeat(7_999)}🙂`,
|
||||
decisionReason: `${"r".repeat(1_999)}🙂`,
|
||||
metrics: { note: `${"m".repeat(3_999)}🙂` },
|
||||
},
|
||||
},
|
||||
{
|
||||
evaluatorId: "emoji-error",
|
||||
pluginId: "evaluation-tests",
|
||||
status: "error",
|
||||
error: `${"e".repeat(1_999)}🙂`,
|
||||
},
|
||||
]);
|
||||
|
||||
const evaluated = await evaluateSkillProposal({
|
||||
workspaceDir,
|
||||
agentId: "main",
|
||||
proposalId: proposal.record.id,
|
||||
expectedRevisionHash: proposal.revisionHash,
|
||||
});
|
||||
|
||||
expect(evaluated.evaluation.outcomes).toEqual([
|
||||
{
|
||||
evaluatorId: "emoji-bounds",
|
||||
pluginId: "evaluation-tests",
|
||||
status: "completed",
|
||||
result: {
|
||||
summary: "s".repeat(7_999),
|
||||
decisionReason: "r".repeat(1_999),
|
||||
metrics: { note: "m".repeat(3_999) },
|
||||
},
|
||||
},
|
||||
{
|
||||
evaluatorId: "emoji-error",
|
||||
pluginId: "evaluation-tests",
|
||||
status: "error",
|
||||
error: "e".repeat(1_999),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("rejects evaluator results that exceed the aggregate persistence budget", async () => {
|
||||
const workspaceDir = await tempDirs.make("openclaw-skill-evaluation-size-budget-");
|
||||
const proposal = await proposeCreateSkill({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
|
||||
import type {
|
||||
PluginHookSkillEvaluationFinding,
|
||||
PluginHookSkillProposalEvaluateResult,
|
||||
@@ -346,18 +347,19 @@ function normalizeMetrics(
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
normalized[key] = typeof value === "string" ? value.slice(0, 4_000) : value;
|
||||
normalized[key] = typeof value === "string" ? truncateUtf16Safe(value, 4_000) : value;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function boundedRequired(value: string, maxLength: number, fallback: string): string {
|
||||
const normalized = normalizeOptionalString(value) ?? fallback;
|
||||
return normalized.slice(0, maxLength);
|
||||
return truncateUtf16Safe(normalized, maxLength);
|
||||
}
|
||||
|
||||
function boundedOptional(value: string | undefined, maxLength: number): string | undefined {
|
||||
return normalizeOptionalString(value)?.slice(0, maxLength);
|
||||
const normalized = normalizeOptionalString(value);
|
||||
return normalized === undefined ? undefined : truncateUtf16Safe(normalized, maxLength);
|
||||
}
|
||||
|
||||
function storeOptions(env?: NodeJS.ProcessEnv) {
|
||||
|
||||
Reference in New Issue
Block a user