update
This commit is contained in:
@@ -295,12 +295,27 @@ test("text-to-cad build123d guidance is available from the original skill", asyn
|
||||
});
|
||||
|
||||
test("text-to-cad CAD-SkillX references are available through documentation lookup", async () => {
|
||||
const result = await readTextToCadDocumentation("references/cad-skillx/planning/mounting-plate.planning.md");
|
||||
const result = await readTextToCadDocumentation("references/cad-skillx/planning/mounting-plate.planning.md", {
|
||||
cadSkillxEnabled: true,
|
||||
});
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.equal(result.content.includes("Mounting Plate Planning Skill"), true);
|
||||
});
|
||||
|
||||
test("text-to-cad CAD-SkillX references are disabled by the code switch", async () => {
|
||||
const result = await readTextToCadDocumentation("references/cad-skillx/planning/mounting-plate.planning.md");
|
||||
assert.equal(result.ok, false);
|
||||
if (result.ok) return;
|
||||
assert.equal(result.error.includes("disabled"), true);
|
||||
|
||||
const skill = await readTextToCadDocumentation("skill");
|
||||
assert.equal(skill.ok, true);
|
||||
if (!skill.ok) return;
|
||||
assert.equal(skill.content.includes("skill-pack-curated-20260807"), false);
|
||||
assert.equal(skill.content.includes("CAD-SkillX generated optimization references are disabled"), true);
|
||||
});
|
||||
|
||||
test("text-to-cad documentation lookup rejects paths outside CAD-SkillX references", async () => {
|
||||
const result = await readTextToCadDocumentation("references/cad-skillx/../build123d-modeling.md");
|
||||
assert.equal(result.ok, false);
|
||||
@@ -317,7 +332,7 @@ test("new build123d generation is bound to the router token and exact API docs",
|
||||
const source = fs.readFileSync(chatSourcePath, "utf8");
|
||||
assert.equal(source.includes("read_text_to_cad_docs: tool"), true);
|
||||
assert.equal(source.includes("read_build123d_api: tool"), true);
|
||||
assert.equal(source.includes("loadRequiredTextToCadDocumentation()"), true);
|
||||
assert.equal(source.includes("loadRequiredTextToCadDocumentation({ cadSkillxEnabled: useCadSkillx })"), true);
|
||||
assert.equal(source.includes("Studio-loaded text-to-cad/build123d base documentation"), true);
|
||||
assert.equal(source.includes("build123d generation requires text-to-cad docs"), false);
|
||||
assert.equal(source.includes("routeToken: z.string().optional()"), true);
|
||||
@@ -326,6 +341,19 @@ test("new build123d generation is bound to the router token and exact API docs",
|
||||
assert.equal(source.includes("服务端已自动补读缺失 API"), true);
|
||||
});
|
||||
|
||||
test("CAD-SkillX code switch is off and not exposed as a UI toggle", () => {
|
||||
const uiSource = fs.readFileSync(path.join(process.cwd(), "src", "components", "agent-studio.tsx"), "utf8");
|
||||
const routeSource = fs.readFileSync(path.join(process.cwd(), "src", "app", "api", "chat", "route.ts"), "utf8");
|
||||
const chatSource = fs.readFileSync(chatSourcePath, "utf8");
|
||||
assert.equal(uiSource.includes("cad-agent-studio.cad-skillx-enabled"), false);
|
||||
assert.equal(uiSource.includes("cadSkillxEnabled"), false);
|
||||
assert.equal(routeSource.includes("cadSkillxEnabled"), false);
|
||||
assert.equal(chatSource.includes("const CAD_SKILLX_ENABLED = false;"), true);
|
||||
assert.equal(chatSource.includes("const useCadSkillx = CAD_SKILLX_ENABLED;"), true);
|
||||
assert.equal(chatSource.includes("CAD-SkillX generated optimization references are disabled"), true);
|
||||
assert.equal(chatSource.includes("readTextToCadDocumentation(document, { cadSkillxEnabled: useCadSkillx })"), true);
|
||||
});
|
||||
|
||||
test("build123d API preflight ignores non-build123d imported helpers", () => {
|
||||
const candidates = build123dCallCandidates([
|
||||
"from pathlib import Path",
|
||||
|
||||
@@ -386,6 +386,7 @@ const SIMPLECADAPI_REFERENCE_ROOT = path.join(SIMPLECADAPI_SKILL_ROOT, "referenc
|
||||
const SIMPLECADAPI_REQUIRED_DOCS = ["skill", "api/README.md", "stdlib/README.md"];
|
||||
const TEXT_TO_CAD_SKILL_ROOT = path.join(process.cwd(), "..", "text-to-cad", "skills", "cad");
|
||||
const TEXT_TO_CAD_REQUIRED_DOCS = ["skill", "references/build123d-modeling.md", "references/step-generation.md"];
|
||||
const CAD_SKILLX_ENABLED = false;
|
||||
|
||||
type SimpleCadApiDocumentationResult =
|
||||
| {
|
||||
@@ -512,7 +513,21 @@ type TextToCadDocumentationResult =
|
||||
| { ok: true; document: string; path: string; content: string }
|
||||
| { ok: false; document: string; error: string; suggestedDocuments: string[] };
|
||||
|
||||
export async function readTextToCadDocumentation(document: string): Promise<TextToCadDocumentationResult> {
|
||||
type TextToCadDocumentationOptions = {
|
||||
cadSkillxEnabled?: boolean;
|
||||
};
|
||||
|
||||
function stripCadSkillxReferences(content: string) {
|
||||
return content
|
||||
.replace(/\n?<!-- cad-skillx:start:[\s\S]*?<!-- cad-skillx:end:[^>]*-->\n?/g, "\n\n")
|
||||
.trimEnd();
|
||||
}
|
||||
|
||||
export async function readTextToCadDocumentation(
|
||||
document: string,
|
||||
options: TextToCadDocumentationOptions = {},
|
||||
): Promise<TextToCadDocumentationResult> {
|
||||
const cadSkillxEnabled = options.cadSkillxEnabled ?? CAD_SKILLX_ENABLED;
|
||||
const normalized = normalizeTextToCadDocument(document);
|
||||
const fixedDocuments: Record<string, string> = {
|
||||
skill: path.join(TEXT_TO_CAD_SKILL_ROOT, "SKILL.md"),
|
||||
@@ -535,6 +550,17 @@ export async function readTextToCadDocumentation(document: string): Promise<Text
|
||||
&& !path.isAbsolute(cadSkillxRelative),
|
||||
);
|
||||
const target = fixedDocuments[normalized || ""] || (cadSkillxDocument ? cadSkillxTarget : "");
|
||||
if (cadSkillxDocument && !cadSkillxEnabled) {
|
||||
return {
|
||||
ok: false,
|
||||
document: normalized || String(document || ""),
|
||||
error: "CAD-SkillX optimization references are disabled for this request.",
|
||||
suggestedDocuments: [
|
||||
"skill",
|
||||
...Object.keys(fixedDocuments).filter((key) => key !== "skill"),
|
||||
],
|
||||
};
|
||||
}
|
||||
if (!normalized || !target) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -550,11 +576,19 @@ export async function readTextToCadDocumentation(document: string): Promise<Text
|
||||
};
|
||||
}
|
||||
try {
|
||||
const content = await fs.readFile(target, "utf8");
|
||||
return {
|
||||
ok: true,
|
||||
document: normalized,
|
||||
path: target,
|
||||
content: await fs.readFile(target, "utf8"),
|
||||
content: normalized === "skill" && !cadSkillxEnabled
|
||||
? [
|
||||
stripCadSkillxReferences(content),
|
||||
"",
|
||||
"## CAD-SkillX references",
|
||||
"CAD-SkillX generated optimization references are disabled for this request. Do not read or use `references/cad-skillx/` documents.",
|
||||
].join("\n")
|
||||
: content,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
@@ -566,8 +600,10 @@ export async function readTextToCadDocumentation(document: string): Promise<Text
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRequiredTextToCadDocumentation() {
|
||||
const results = await Promise.all(TEXT_TO_CAD_REQUIRED_DOCS.map(readTextToCadDocumentation));
|
||||
async function loadRequiredTextToCadDocumentation(options: TextToCadDocumentationOptions = {}) {
|
||||
const results = await Promise.all(
|
||||
TEXT_TO_CAD_REQUIRED_DOCS.map((document) => readTextToCadDocumentation(document, options)),
|
||||
);
|
||||
const unavailable = results.filter((result) => !result.ok);
|
||||
if (unavailable.length) {
|
||||
throw new Error(`Studio could not load required text-to-cad documentation: ${unavailable.map((result) => result.document).join(", ")}.`);
|
||||
@@ -667,7 +703,16 @@ export function build123dCallCandidates(source: string) {
|
||||
return [...new Set([...candidates, ...memberReferences])].slice(0, 64);
|
||||
}
|
||||
|
||||
function buildSystemPrompt(viewerContext: unknown[], attachments: unknown[], taskContext = "") {
|
||||
function buildSystemPrompt(
|
||||
viewerContext: unknown[],
|
||||
attachments: unknown[],
|
||||
taskContext = "",
|
||||
options: { cadSkillxEnabled?: boolean } = {},
|
||||
) {
|
||||
const cadSkillxEnabled = options.cadSkillxEnabled ?? CAD_SKILLX_ENABLED;
|
||||
const build123dDocumentationInstruction = cadSkillxEnabled
|
||||
? "When route_cad_request returns build123d_python, Studio automatically loads the text-to-cad skill, build123d-modeling, and step-generation documents into the route result. Do not repeat those reads. If the request matches a CAD-SkillX entry listed there, use read_text_to_cad_docs only for the relevant Planning/Functional/Atomic reference. Before writing source, call read_build123d_api once with every build123d symbol you plan to use. It reads the active build123d runtime's actual signature and docstring. Follow those signatures exactly: do not guess argument order, namespaces, or keyword names. Use build123d enum members exactly, for example Align.CENTER and Mode.SUBTRACT, not strings such as \"CENTER\"."
|
||||
: "When route_cad_request returns build123d_python, Studio automatically loads the text-to-cad skill, build123d-modeling, and step-generation documents into the route result. Do not repeat those reads. CAD-SkillX generated optimization references are disabled for this request: do not call read_text_to_cad_docs for references/cad-skillx/... and do not use CAD-SkillX Planning/Functional/Atomic guidance. Before writing source, call read_build123d_api once with every build123d symbol you plan to use. It reads the active build123d runtime's actual signature and docstring. Follow those signatures exactly: do not guess argument order, namespaces, or keyword names. Use build123d enum members exactly, for example Align.CENTER and Mode.SUBTRACT, not strings such as \"CENTER\".";
|
||||
return [
|
||||
"You are the CAD Agent Studio assistant.",
|
||||
"You help create and modify CAD models through a real server-side CAD generation tool.",
|
||||
@@ -676,7 +721,7 @@ function buildSystemPrompt(viewerContext: unknown[], attachments: unknown[], tas
|
||||
"Do not rely on hardcoded templates, canned examples, mock data, fake filenames, or imaginary viewer state.",
|
||||
"For every new text/image CAD model, call route_cad_request once before writing native source. CAD Router selects SimpleCADAPI only for catalogued standard primary parts; every other new part is text-to-cad/build123d. Use its returned sourceKind in generate_cad. Do not choose the backend yourself and do not read backend documentation for routing.",
|
||||
"When route_cad_request returns simplecadapi_python, first read the original SimpleCADAPI docs through read_simplecadapi_docs: skill, api/README.md, and stdlib/README.md. Then read the exact original API or stdlib Markdown page for every SimpleCADAPI function used in the source. Follow the documented signatures literally; do not invent namespaces or API names. Documentation paths come from the README links, not Python namespaces: for example simplecadapi.ql.value is documented as api/value.md, not ql/value.md. The server rejects source that calls a nonexistent SimpleCADAPI attribute.",
|
||||
"When route_cad_request returns build123d_python, Studio automatically loads the text-to-cad skill, build123d-modeling, and step-generation documents into the route result. Do not repeat those reads. If the request matches a CAD-SkillX entry listed there, use read_text_to_cad_docs only for the relevant Planning/Functional/Atomic reference. Before writing source, call read_build123d_api once with every build123d symbol you plan to use. It reads the active build123d runtime's actual signature and docstring. Follow those signatures exactly: do not guess argument order, namespaces, or keyword names. Use build123d enum members exactly, for example Align.CENTER and Mode.SUBTRACT, not strings such as \"CENTER\".",
|
||||
build123dDocumentationInstruction,
|
||||
"When STEP files are attached, the source is teacher and acceptance truth only. Use reconstruct_uploaded_step; never import the teacher as model geometry.",
|
||||
"All uploaded files are available to you. Images are attached directly to the latest user message when the selected provider/model supports vision. STEP/STP and binary files are available through inspect_uploaded_file.",
|
||||
"When the user asks to model from an uploaded image, inspect the image directly from the image part. If the selected provider/model cannot process images, say that clearly and ask the user to switch to a vision-capable OpenAI-compatible model.",
|
||||
@@ -936,6 +981,7 @@ export async function streamAgentResponse({
|
||||
selectedTaskId?: string;
|
||||
conversationId?: string;
|
||||
}) {
|
||||
const useCadSkillx = CAD_SKILLX_ENABLED;
|
||||
const requestedConversationId = String(conversationId || "").trim();
|
||||
const normalizedConversationId = requestedConversationId ? safeConversationId(requestedConversationId) : "";
|
||||
const requestMessages = messages;
|
||||
@@ -1058,7 +1104,7 @@ export async function streamAgentResponse({
|
||||
build123dApiSymbolsRead.clear();
|
||||
routeDecisions.clear();
|
||||
const build123dBaseDocs = decision.selectedBackend === "build123d"
|
||||
? await loadRequiredTextToCadDocumentation()
|
||||
? await loadRequiredTextToCadDocumentation({ cadSkillxEnabled: useCadSkillx })
|
||||
: [];
|
||||
for (const document of build123dBaseDocs) {
|
||||
textToCadDocsRead.add(document.document);
|
||||
@@ -1077,6 +1123,7 @@ export async function streamAgentResponse({
|
||||
...(build123dBaseDocs.length ? {
|
||||
backendContext: {
|
||||
source: "Studio-loaded text-to-cad/build123d base documentation",
|
||||
cadSkillxEnabled: useCadSkillx,
|
||||
documents: build123dBaseDocs.map(({ document, content }) => ({ document, content })),
|
||||
},
|
||||
} : {}),
|
||||
@@ -1102,13 +1149,15 @@ export async function streamAgentResponse({
|
||||
description: [
|
||||
"Read an additional original text-to-cad/build123d reference when the Studio-loaded base documentation identifies a need.",
|
||||
"The base skill, build123d modeling, and STEP generation references are already returned by route_cad_request for build123d routes.",
|
||||
"Use this for relevant Markdown files below references/cad-skillx/ or optional positioning/inspection references.",
|
||||
useCadSkillx
|
||||
? "Use this for relevant Markdown files below references/cad-skillx/ or optional positioning/inspection references."
|
||||
: "CAD-SkillX optimization references are disabled; use this only for optional non-cad-skillx references such as positioning or inspection.",
|
||||
].join(" "),
|
||||
inputSchema: z.object({
|
||||
document: z.string().describe("Listed text-to-cad skill document key or references/cad-skillx/... Markdown path."),
|
||||
}),
|
||||
execute: async ({ document }) => {
|
||||
const result = await readTextToCadDocumentation(document);
|
||||
const result = await readTextToCadDocumentation(document, { cadSkillxEnabled: useCadSkillx });
|
||||
if (result.ok) textToCadDocsRead.add(result.document);
|
||||
return result;
|
||||
},
|
||||
@@ -1600,7 +1649,9 @@ export async function streamAgentResponse({
|
||||
});
|
||||
const result = streamText({
|
||||
model: languageModel,
|
||||
system: buildSystemPrompt(viewerContext, uploadedAttachments, taskContext),
|
||||
system: buildSystemPrompt(viewerContext, uploadedAttachments, taskContext, {
|
||||
cadSkillxEnabled: useCadSkillx,
|
||||
}),
|
||||
tools: cadTools,
|
||||
stopWhen: isStepCount(16),
|
||||
messages: modelMessages,
|
||||
|
||||
Reference in New Issue
Block a user