Merge pull request #602 from kentcdodds/cursor/fix-pull-request-interpretation-and-tests-7a8f

fix: interpretation of unstructured content and tests
This commit is contained in:
bhosmer-ant 2025-07-11 11:31:23 -04:00 • committed by GitHub
commit 3acc6d891c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 119 additions and 45 deletions

View file

@ -22,46 +22,46 @@ const checkContentCompatibility = (
[key: string]: unknown; [key: string]: unknown;
}>, }>,
): { isCompatible: boolean; message: string } => { ): { isCompatible: boolean; message: string } => {
if ( // Look for at least one text content block that matches the structured content
unstructuredContent.length !== 1 || const textBlocks = unstructuredContent.filter(
unstructuredContent[0].type !== "text" (block) => block.type === "text",
) { );
if (textBlocks.length === 0) {
return { return {
isCompatible: false, isCompatible: false,
message: "Unstructured content is not a single text block", message: "No text blocks to match structured content",
}; };
} }
const textContent = unstructuredContent[0].text; // Check if any text block contains JSON that matches the structured content
if (!textContent) { for (const textBlock of textBlocks) {
return { const textContent = textBlock.text;
isCompatible: false, if (!textContent) {
message: "Text content is empty", continue;
}; }
}
try {
try { const parsedContent = JSON.parse(textContent);
const parsedContent = JSON.parse(textContent); const isEqual =
const isEqual = JSON.stringify(parsedContent) === JSON.stringify(structuredContent);
JSON.stringify(parsedContent) === JSON.stringify(structuredContent);
if (isEqual) {
if (isEqual) { return {
return { isCompatible: true,
isCompatible: true, message: `Structured content matches text block${textBlocks.length > 1 ? " (multiple blocks)" : ""}${unstructuredContent.length > textBlocks.length ? " + other content" : ""}`,
message: "Unstructured content matches structured content", };
}; }
} else { } catch {
return { // Continue to next text block if this one doesn't parse as JSON
isCompatible: false, continue;
message: "Parsed JSON does not match structured content",
};
} }
} catch {
return {
isCompatible: false,
message: "Unstructured content is not valid JSON",
};
} }
return {
isCompatible: false,
message: "No text block matches structured content",
};
}; };
const ToolResults = ({ const ToolResults = ({

View file

@ -251,10 +251,12 @@ describe("ToolsTab", () => {
}, },
}; };
it("should display structured content when present", () => { beforeEach(() => {
// Cache the tool's output schema so hasOutputSchema returns true // Cache the tool's output schema before each test
cacheToolOutputSchemas([toolWithOutputSchema]); cacheToolOutputSchemas([toolWithOutputSchema]);
});
it("should display structured content when present", () => {
const structuredResult = { const structuredResult = {
content: [], content: [],
structuredContent: { structuredContent: {
@ -263,6 +265,7 @@ describe("ToolsTab", () => {
}; };
renderToolsTab({ renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema, selectedTool: toolWithOutputSchema,
toolResult: structuredResult, toolResult: structuredResult,
}); });
@ -274,8 +277,6 @@ describe("ToolsTab", () => {
}); });
it("should show validation error for invalid structured content", () => { it("should show validation error for invalid structured content", () => {
cacheToolOutputSchemas([toolWithOutputSchema]);
const invalidResult = { const invalidResult = {
content: [], content: [],
structuredContent: { structuredContent: {
@ -284,6 +285,7 @@ describe("ToolsTab", () => {
}; };
renderToolsTab({ renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema, selectedTool: toolWithOutputSchema,
toolResult: invalidResult, toolResult: invalidResult,
}); });
@ -292,14 +294,13 @@ describe("ToolsTab", () => {
}); });
it("should show error when tool with output schema doesn't return structured content", () => { it("should show error when tool with output schema doesn't return structured content", () => {
cacheToolOutputSchemas([toolWithOutputSchema]);
const resultWithoutStructured = { const resultWithoutStructured = {
content: [{ type: "text", text: "some result" }], content: [{ type: "text", text: "some result" }],
// No structuredContent // No structuredContent
}; };
renderToolsTab({ renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema, selectedTool: toolWithOutputSchema,
toolResult: resultWithoutStructured, toolResult: resultWithoutStructured,
}); });
@ -312,14 +313,13 @@ describe("ToolsTab", () => {
}); });
it("should show unstructured content title when both structured and unstructured exist", () => { it("should show unstructured content title when both structured and unstructured exist", () => {
cacheToolOutputSchemas([toolWithOutputSchema]);
const resultWithBoth = { const resultWithBoth = {
content: [{ type: "text", text: '{"temperature": 25}' }], content: [{ type: "text", text: '{"temperature": 25}' }],
structuredContent: { temperature: 25 }, structuredContent: { temperature: 25 },
}; };
renderToolsTab({ renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema, selectedTool: toolWithOutputSchema,
toolResult: resultWithBoth, toolResult: resultWithBoth,
}); });
@ -344,26 +344,100 @@ describe("ToolsTab", () => {
}); });
it("should show compatibility check when tool has output schema", () => { it("should show compatibility check when tool has output schema", () => {
cacheToolOutputSchemas([toolWithOutputSchema]);
const compatibleResult = { const compatibleResult = {
content: [{ type: "text", text: '{"temperature": 25}' }], content: [{ type: "text", text: '{"temperature": 25}' }],
structuredContent: { temperature: 25 }, structuredContent: { temperature: 25 },
}; };
renderToolsTab({ renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema, selectedTool: toolWithOutputSchema,
toolResult: compatibleResult, toolResult: compatibleResult,
}); });
// Should show compatibility result // Should show compatibility result
expect( expect(
screen.getByText( screen.getByText(/structured content matches/i),
/matches structured content|not a single text block|not valid JSON|does not match/,
),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
it("should accept multiple content blocks with structured output", () => {
const multipleBlocksResult = {
content: [
{ type: "text", text: "Here is the weather data:" },
{ type: "text", text: '{"temperature": 25}' },
{ type: "text", text: "Have a nice day!" },
],
structuredContent: { temperature: 25 },
};
renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema,
toolResult: multipleBlocksResult,
});
// Should show compatible result with multiple blocks
expect(
screen.getByText(/structured content matches.*multiple/i),
).toBeInTheDocument();
});
it("should accept mixed content types with structured output", () => {
const mixedContentResult = {
content: [
{ type: "text", text: "Weather report:" },
{ type: "text", text: '{"temperature": 25}' },
{ type: "image", data: "base64data", mimeType: "image/png" },
],
structuredContent: { temperature: 25 },
};
renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema,
toolResult: mixedContentResult,
});
// Should render without crashing - the validation logic has been updated
expect(screen.getAllByText("weatherTool")).toHaveLength(2);
});
it("should reject when no text blocks match structured content", () => {
const noMatchResult = {
content: [
{ type: "text", text: "Some text" },
{ type: "text", text: '{"humidity": 60}' }, // Different structure
],
structuredContent: { temperature: 25 },
};
renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema,
toolResult: noMatchResult,
});
// Should render without crashing - the validation logic has been updated
expect(screen.getAllByText("weatherTool")).toHaveLength(2);
});
it("should reject when no text blocks are present", () => {
const noTextBlocksResult = {
content: [{ type: "image", data: "base64data", mimeType: "image/png" }],
structuredContent: { temperature: 25 },
};
renderToolsTab({
tools: [toolWithOutputSchema],
selectedTool: toolWithOutputSchema,
toolResult: noTextBlocksResult,
});
// Should render without crashing - the validation logic has been updated
expect(screen.getAllByText("weatherTool")).toHaveLength(2);
});
it("should not show compatibility check when tool has no output schema", () => { it("should not show compatibility check when tool has no output schema", () => {
const resultWithBoth = { const resultWithBoth = {
content: [{ type: "text", text: '{"data": "value"}' }], content: [{ type: "text", text: '{"data": "value"}' }],
@ -378,7 +452,7 @@ describe("ToolsTab", () => {
// Should not show any compatibility messages // Should not show any compatibility messages
expect( expect(
screen.queryByText( screen.queryByText(
/matches structured content|not a single text block|not valid JSON|does not match/, /structured content matches|no text blocks|no.*matches/i,
), ),
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}); });