Skip to content

Commit 89f010e

Browse files
committed
Enhance Tool Explorer and OpenAPI Handling
- Update styling logic in the ToolRow and GroupNode components to differentiate selected and source tools, improving visual clarity. - Modify OpenAPI preparation to always include parameter schemas, ensuring consistent schema handling. - Add tests to validate input and output hints for OpenAPI tools, ensuring accurate type representation. - Update OpenAPI cache versioning to reflect recent changes and improvements in tool discovery. - Refactor workspace tool inventory logic to streamline state management and improve performance.
1 parent 20de84f commit 89f010e

8 files changed

Lines changed: 97 additions & 11 deletions

File tree

executor/apps/web/src/components/tools/explorer-groups.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ export function GroupNode({
8080
"sticky bg-background/95 backdrop-blur-sm",
8181
isExpanded && "border-b border-border/30",
8282
isGroupSelected
83-
? "bg-primary/10 ring-1 ring-primary/20"
83+
? isSource
84+
? "bg-primary/10 ring-1 ring-primary/20"
85+
: "bg-accent/20 ring-1 ring-accent/30"
8486
: "hover:bg-accent/30",
8587
)}
8688
style={{

executor/apps/web/src/components/tools/explorer-rows.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ const ToolRow = memo(function ToolRow({
5050
className={cn(
5151
"flex items-center gap-2 px-2 py-1.5 transition-colors cursor-pointer group/tool",
5252
expanded
53-
? "sticky bg-background/95 backdrop-blur-sm bg-accent/30"
53+
? selected
54+
? "sticky bg-accent ring-1 ring-accent/30"
55+
: "sticky bg-card"
5456
: selected
55-
? "bg-primary/5 ring-1 ring-primary/10"
57+
? "bg-accent ring-1 ring-accent/30"
5658
: "hover:bg-accent/20",
5759
)}
5860
style={{

executor/packages/core/src/openapi-prepare.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export async function prepareOpenApiSpec(
233233
{
234234
includeSchemas: profile === "full",
235235
includeTypeHints: true,
236-
includeParameterSchemas: profile === "full",
236+
includeParameterSchemas: true,
237237
resolveSchemaRefs: profile === "full",
238238
},
239239
);

executor/packages/core/src/openapi-typing.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ describe("OpenAPI schema-first typing", () => {
100100
expect(getTool!.typing?.outputSchema).toBeDefined();
101101
expect(getTool!.typing?.requiredInputKeys ?? []).toContain("id");
102102
expect(getTool!.typing?.previewInputKeys ?? []).toContain("include");
103+
expect(getTool!.typing?.inputHint).toContain("id: string");
104+
expect(getTool!.typing?.inputHint).toContain("include?:");
103105
expect(getTool!.typing?.typedRef).toBeDefined();
104106
});
105107

executor/packages/core/src/openapi/real-specs.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,4 +301,34 @@ describe("real-world OpenAPI specs", () => {
301301
},
302302
300_000,
303303
);
304+
305+
test(
306+
"openai: assistants cancel run keeps path parameter types in inventory mode",
307+
async () => {
308+
const openAiUrl = "https://app.stainless.com/api/spec/documented/openai/openapi.documented.yml";
309+
const prepared = await prepareOpenApiSpec(openAiUrl, "openai", {
310+
includeDts: false,
311+
profile: "inventory",
312+
});
313+
314+
const tools = buildOpenApiToolsFromPrepared(
315+
{
316+
type: "openapi",
317+
name: "openai",
318+
spec: openAiUrl,
319+
baseUrl: prepared.servers[0] || "https://api.openai.com",
320+
},
321+
prepared,
322+
);
323+
324+
const tool = tools.find((t) => t.path === "openai.assistants.cancel_run");
325+
expect(tool).toBeDefined();
326+
327+
const inputHint = tool!.typing?.inputHint ?? "";
328+
expect(inputHint).toContain("thread_id: string");
329+
expect(inputHint).toContain("run_id: string");
330+
expect(inputHint.includes("unknown")).toBe(false);
331+
},
332+
300_000,
333+
);
304334
});

executor/packages/core/src/tool-discovery.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,61 @@ test("discover depth 0 keeps full signatures", async () => {
412412
);
413413
});
414414

415+
test("discover prefers schema over lossy typed hints", async () => {
416+
const tool = createDiscoverTool([
417+
{
418+
path: "openai.batch.create_batch",
419+
description: "Create batch",
420+
approval: "required",
421+
source: "openapi:openai",
422+
typing: {
423+
inputHint: "{ input_file_id: ...; endpoint: ... }",
424+
outputHint: "{ id: string; errors?: { message?:... } }",
425+
inputSchema: {
426+
type: "object",
427+
properties: {
428+
input_file_id: { type: "string" },
429+
endpoint: { type: "string" },
430+
completion_window: { type: "string" },
431+
},
432+
required: ["input_file_id", "endpoint", "completion_window"],
433+
},
434+
outputSchema: {
435+
type: "object",
436+
properties: {
437+
id: { type: "string" },
438+
errors: {
439+
type: "object",
440+
properties: {
441+
message: { type: "string" },
442+
},
443+
},
444+
},
445+
required: ["id"],
446+
},
447+
},
448+
run: async () => ({ id: "b_123" }),
449+
} satisfies ToolDefinition,
450+
]);
451+
452+
const result = await tool.run(
453+
{ query: "create batch", depth: 2 },
454+
{ taskId: "t", workspaceId: TEST_WORKSPACE_ID, isToolAllowed: () => true },
455+
) as {
456+
results: Array<{
457+
signatureInfo: {
458+
input: string;
459+
output: string;
460+
};
461+
}>;
462+
};
463+
464+
expect(result.results[0]?.signatureInfo.input).toContain("input_file_id: string");
465+
expect(result.results[0]?.signatureInfo.input.includes("...")).toBe(false);
466+
expect(result.results[0]?.signatureInfo.output).toContain("message?: string");
467+
expect(result.results[0]?.signatureInfo.output.includes("...")).toBe(false);
468+
});
469+
415470
test("discover returns null bestPath when there are no matches", async () => {
416471
const tool = createDiscoverTool([
417472
{

executor/packages/database/src/runtime/tool_source_loading.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030

3131
const OPENAPI_SPEC_CACHE_TTL_MS = 5 * 60 * 60_000;
3232

33-
const OPENAPI_PREPARED_CACHE_VERSION = "openapi_v5";
33+
const OPENAPI_PREPARED_CACHE_VERSION = "openapi_v6";
3434

3535
const openApiAuthModeSchema = z.enum(["static", "account", "workspace", "organization"]);
3636

executor/packages/database/src/runtime/workspace_tools.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,11 +1504,6 @@ export async function rebuildWorkspaceToolInventoryForContext(
15041504
sources.filter((source) => source.enabled),
15051505
);
15061506

1507-
const alreadyReady = Boolean(
1508-
state?.readyBuildId
1509-
&& !state?.buildingBuildId
1510-
&& state?.signature === registrySignature,
1511-
);
15121507
const alreadyBuildingTarget = Boolean(
15131508
state?.buildingBuildId
15141509
&& state?.buildingSignature === registrySignature,
@@ -1523,7 +1518,7 @@ export async function rebuildWorkspaceToolInventoryForContext(
15231518
&& Date.now() - buildingStartedAt > REGISTRY_BUILD_STALE_MS,
15241519
);
15251520

1526-
if (alreadyReady || (alreadyBuildingTarget && !staleBuildingTarget)) {
1521+
if (alreadyBuildingTarget && !staleBuildingTarget) {
15271522
return { rebuilt: false };
15281523
}
15291524

0 commit comments

Comments
 (0)