Skip to content
6 changes: 3 additions & 3 deletions ts/packages/benchmarks/README.AUTOGEN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<!-- AUTOGEN:DOCS:START -->

<!-- AUTOGEN:DOCS:HASH:sha256=e39a7978a4c82b8e7cdcd51b133c84a11e0de1c52bad608a24755ce9920c2685 -->
<!-- AUTOGEN:DOCS:HASH:sha256=d94460dd448816fb1ae22304eb0522c647ecdc03c3c08d2738ad914689282f8d -->
<!-- AUTOGEN:DOCS:SOURCE: ./README.md (hand-written documentation; this file is the AI-generated companion) -->

# @typeagent/benchmarks — AI-generated documentation
Expand Down Expand Up @@ -52,10 +52,10 @@ _None._
- [./src/core/types.ts](./src/core/types.ts)
- [./src/translationBench/action-parameters-grader.generated.json](./src/translationBench/action-parameters-grader.generated.json)
- [./src/translationBench/catalog.generated.json](./src/translationBench/catalog.generated.json)
- _…and 31 more under `./src/`._
- _…and 32 more under `./src/`._

---

_Auto-generated against commit `8c60af9402ee0b07a5789e4c75ce3d8fabce65cd` on `2026-08-12T21:19:29.739Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/benchmarks docs:verify-links` to spot-check._
_Auto-generated against commit `d917a1fbedd52940d73141b0152b6ae508258b54` on `2026-08-12T23:28:04.947Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/benchmarks docs:verify-links` to spot-check._

<!-- AUTOGEN:DOCS:END -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
Comment thread
datduyng marked this conversation as resolved.
// Licensed under the MIT License.

import {
resolveTypeReference,
validateAction,
type ActionSchemaTypeDefinition,
} from "@typeagent/action-schema";

/**
* Build the object passed to validateAction for a TB gold action.
* - Injects required single-literal string-union fields (e.g. settings `id`)
* - Restores parameters:{} when the schema requires an empty parameters object
* after stripEmptyGoldPlaceholders dropped nested empties.
*/
export function translationBenchActionValidationPayload(
definition: ActionSchemaTypeDefinition,
action: {
actionName: string;
parameters?: Record<string, unknown>;
},
): Record<string, unknown> {
const payload: Record<string, unknown> = {
actionName: action.actionName,
};
for (const [name, field] of Object.entries(definition.type.fields)) {
if (name === "actionName" || name === "parameters") continue;
if (field.optional) continue;
const fieldType = resolveTypeReference(field.type);
if (
fieldType?.type === "string-union" &&
fieldType.typeEnum.length === 1
) {
payload[name] = fieldType.typeEnum[0];
}
}
const parametersField = definition.type.fields.parameters;
if (action.parameters !== undefined) {
payload.parameters = action.parameters;
} else if (parametersField !== undefined && !parametersField.optional) {
payload.parameters = {};
}
return payload;
}

export function validateTranslationBenchGoldAction(
definition: ActionSchemaTypeDefinition,
action: {
actionName: string;
parameters?: Record<string, unknown>;
},
): void {
validateAction(
definition,
translationBenchActionValidationPayload(definition, action),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
generateActionActionFunctionJsonSchemas,
parseToolsJsonSchema,
toJSONParsedActionSchema,
validateAction,
type ParsedActionSchema,
type ParsedActionSchemaJSON,
} from "@typeagent/action-schema";
import { validateTranslationBenchGoldAction } from "./actionValidation.js";
import type { SchemaTypeNames } from "@typeagent/agent-sdk";
import { z } from "zod";

Expand Down Expand Up @@ -2276,7 +2276,12 @@ function validateExpectedActions(
`${label} expects unknown existing TypeAgent action '${action.schemaName}.${action.actionName}'`,
);
}
validateAction(definition, action);
validateTranslationBenchGoldAction(definition, {
Comment thread
datduyng marked this conversation as resolved.
actionName: action.actionName,
...(action.parameters !== undefined
? { parameters: action.parameters }
: {}),
});
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import {
fromJSONParsedActionSchema,
validateAction,
} from "@typeagent/action-schema";
import { fromJSONParsedActionSchema } from "@typeagent/action-schema";
import { z } from "zod";

import {
Expand All @@ -23,6 +20,7 @@ import {
type TranslationBenchActionShapePolicy,
} from "./actionShape.js";
import { stripEmptyGoldPlaceholders } from "./goldParameterHygiene.js";
import { validateTranslationBenchGoldAction } from "./actionValidation.js";

export interface TranslationBenchGeneratedCase {
id: string;
Expand Down Expand Up @@ -230,7 +228,7 @@ export function parseTranslationBenchGeneratedCandidate(
`${path} must contain only the scheduled target action`,
);
}
validateAction(definition, {
validateTranslationBenchGoldAction(definition, {
actionName: context.targetAction.actionName,
...(action.parameters !== undefined
? { parameters: action.parameters }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from "./utteranceDisambiguation.js";
export * from "./catalogGenerator/index.js";
export { seedQaJsonlAdapter } from "./adapters/seedQaJsonlAdapter.js";
export * from "./goldParameterHygiene.js";
export * from "./actionValidation.js";
153 changes: 153 additions & 0 deletions ts/packages/benchmarks/test/translationBench.actionValidation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { describe, expect, it } from "@jest/globals";
import type {
ActionSchemaTypeDefinition,
SchemaObjectFields,
} from "@typeagent/action-schema";

import {
translationBenchActionValidationPayload,
validateTranslationBenchGoldAction,
} from "../src/translationBench/synthesizer/actionValidation.js";

function makeDefinition(
fields: SchemaObjectFields,
): ActionSchemaTypeDefinition {
// Runtime action schemas may carry extra required literals (e.g. settings `id`)
// beyond the narrowed ActionSchemaObject field map.
return {
alias: false,
name: "TestAction",
type: {
type: "object",
fields,
},
} as ActionSchemaTypeDefinition;
}

describe("translationBench action validation payload", () => {
it("injects required inline single-literal string-union fields", () => {
const definition = makeDefinition({
actionName: {
type: {
type: "string-union",
typeEnum: ["dimBrightNessAction"],
},
},
id: {
type: {
type: "string-union",
typeEnum: ["settings/dimBrightness"],
},
},
parameters: {
type: {
type: "object",
fields: {
originalRequest: { type: { type: "string" } },
},
},
},
});
const payload = translationBenchActionValidationPayload(definition, {
actionName: "dimBrightNessAction",
parameters: { originalRequest: "dim the screen" },
});
expect(payload).toEqual({
actionName: "dimBrightNessAction",
id: "settings/dimBrightness",
parameters: { originalRequest: "dim the screen" },
});
});

it("resolves type-reference aliases before injecting single literals", () => {
const definition = makeDefinition({
actionName: {
type: {
type: "string-union",
typeEnum: ["adjustMultiMonitorLayoutAction"],
},
},
id: {
type: {
type: "type-reference",
name: "AdjustMultiMonitorLayoutId",
definition: {
alias: true,
name: "AdjustMultiMonitorLayoutId",
type: {
type: "string-union",
typeEnum: ["settings/adjustMultiMonitorLayout"],
},
},
},
},
parameters: {
type: {
type: "object",
fields: {
originalRequest: { type: { type: "string" } },
},
},
},
});
const payload = translationBenchActionValidationPayload(definition, {
actionName: "adjustMultiMonitorLayoutAction",
parameters: { originalRequest: "arrange monitors" },
});
expect(payload.id).toBe("settings/adjustMultiMonitorLayout");
});

it("restores required empty parameters when gold dropped them", () => {
const definition = makeDefinition({
actionName: {
type: { type: "string-union", typeEnum: ["noopAction"] },
},
parameters: {
type: { type: "object", fields: {} },
},
});
const payload = translationBenchActionValidationPayload(definition, {
actionName: "noopAction",
});
expect(payload).toEqual({
actionName: "noopAction",
parameters: {},
});
});

it("does not inject optional single-literal fields", () => {
const definition = makeDefinition({
actionName: {
type: { type: "string-union", typeEnum: ["demoAction"] },
},
tag: {
optional: true,
type: { type: "string-union", typeEnum: ["only"] },
},
});
const payload = translationBenchActionValidationPayload(definition, {
actionName: "demoAction",
});
expect(payload).toEqual({ actionName: "demoAction" });
expect(payload).not.toHaveProperty("tag");
});

it("validateTranslationBenchGoldAction accepts restored empty parameters", () => {
const definition = makeDefinition({
actionName: {
type: { type: "string-union", typeEnum: ["noopAction"] },
},
parameters: {
type: { type: "object", fields: {} },
},
});
expect(() =>
validateTranslationBenchGoldAction(definition, {
actionName: "noopAction",
}),
).not.toThrow();
});
});
Loading