History

Use tools.search as the sole sandbox tool search API

Rhys Sullivan committed 7 months ago

Commit 05d78bc1 parent 4ac9df03 files · +24 −25Browse files

Unified · Split

3 files
  1. packages/core/execution/src/description.ts +6 −6
  2. packages/core/execution/src/index.ts +1 −2
  3. packages/core/execution/src/tool-invoker.test.ts +17 −17
packages/core/execution/src/description.ts+6−6
@@ -30,20 +30,20 @@ const formatDescription = ( · 29 unmodified lines
30 "",30 "",
31 "## Workflow",31 "## Workflow",
32 "",32 "",
33 '1. `const matches = await tools.discover({ query: "<intent + key nouns>", limit: 12 });`',33 '1. `const matches = await tools.search({ query: "<intent + key nouns>", limit: 12 });`',
34 '2. `const path = matches[0]?.path; if (!path) return "No matching tools found.";`',34 '2. `const path = matches[0]?.path; if (!path) return "No matching tools found.";`',
35 "3. `const details = await tools.describe.tool({ path });`",35 "3. `const details = await tools.describe.tool({ path });`",
36 "4. Use `details.inputTypeScript` / `details.outputTypeScript` and `details.typeScriptDefinitions` for compact shapes.",36 "4. Use `details.inputTypeScript` / `details.outputTypeScript` and `details.typeScriptDefinitions` for compact shapes.",
37 "5. Use `tools.executor.tools.search(...)` when you want ranked tool search explicitly, and `tools.executor.sources.list()` when you need configured source inventory.",37 "5. Use `tools.executor.sources.list()` when you need configured source inventory.",
38 "6. Call the tool: `const result = await tools.<path>(input);`",38 "6. Call the tool: `const result = await tools.<path>(input);`",
39 "",39 "",
40 "## Rules",40 "## Rules",
41 "",41 "",
42 "- `tools.discover()` and `tools.executor.tools.search()` return ranked matches, best-first. Use short intent phrases like `github issues`, `repo details`, or `create calendar event`.",42 "- `tools.search()` returns ranked matches, best-first. Use short intent phrases like `github issues`, `repo details`, or `create calendar event`.",
43 "- When you already know the namespace, narrow with `tools.discover({ namespace: \"github\", query: \"issues\" })` or `tools.executor.tools.search({ namespace: \"github\", query: \"issues\" })`.",43 "- When you already know the namespace, narrow with `tools.search({ namespace: \"github\", query: \"issues\" })`.",
44 "- Use `tools.executor.sources.list()` to inspect configured sources and their tool counts.",44 "- Use `tools.executor.sources.list()` to inspect configured sources and their tool counts.",
45 "- The `tools` object is a lazy proxy — `Object.keys(tools)` won't work. Use the discovery helpers instead.",45 "- The `tools` object is a lazy proxy — `Object.keys(tools)` won't work. Use `tools.search()` or `tools.executor.sources.list()` instead.",
46 "- Pass an object to system tools, e.g. `tools.discover({ query: \"...\" })`, `tools.executor.tools.search({ query: \"...\" })`, `tools.executor.sources.list()`, and `tools.describe.tool({ path })`.",46 "- Pass an object to system tools, e.g. `tools.search({ query: \"...\" })`, `tools.executor.sources.list()`, and `tools.describe.tool({ path })`.",
47 "- `tools.describe.tool()` returns compact TypeScript shapes. Use `inputTypeScript`, `outputTypeScript`, and `typeScriptDefinitions`.",47 "- `tools.describe.tool()` returns compact TypeScript shapes. Use `inputTypeScript`, `outputTypeScript`, and `typeScriptDefinitions`.",
48 "- Do not use `fetch` — all API calls go through `tools.*`.",48 "- Do not use `fetch` — all API calls go through `tools.*`.",
49 "- If execution pauses for interaction, resume it with the returned `resumePayload`.",49 "- If execution pauses for interaction, resume it with the returned `resumePayload`.",
packages/core/execution/src/index.ts+1−2
@@ -13,8 +13,7 @@ export { buildExecuteDescription } from "./description"; · 12 unmodified lines
13export { ExecutionToolError } from "./errors";13export { ExecutionToolError } from "./errors";
14export {14export {
15 makeExecutorToolInvoker,15 makeExecutorToolInvoker,
16 discoverTools,16 searchTools,
17 searchExecutorTools,
18 listExecutorSources,17 listExecutorSources,
19 describeTool,18 describeTool,
20} from "./tool-invoker";19} from "./tool-invoker";
packages/core/execution/src/tool-invoker.test.ts+17−17
@@ -10,7 +10,7 @@ import { · 9 unmodified lines
10 tool,10 tool,
11} from "@executor/sdk";11} from "@executor/sdk";
12import { createExecutionEngine } from "./engine";12import { createExecutionEngine } from "./engine";
13import { describeTool, discoverTools } from "./tool-invoker";13import { describeTool, searchTools } from "./tool-invoker";
14 14
15const EmptyInput = Schema.Struct({});15const EmptyInput = Schema.Struct({});
16const RepoInput = Schema.Struct({16const RepoInput = Schema.Struct({
@@ -95,16 +95,16 @@ describe("tool discovery", () => { · 78 unmodified lines
95 Effect.gen(function* () {95 Effect.gen(function* () {
96 const executor = yield* makeSearchExecutor();96 const executor = yield* makeSearchExecutor();
97 97
98 const githubMatches = yield* discoverTools(executor, "github issues", 5);98 const githubMatches = yield* searchTools(executor, "github issues", 5);
99 expect(githubMatches.map((match) => match.path)).toEqual([99 expect(githubMatches.map((match) => match.path)).toEqual([
100 "github.listRepositoryIssues",100 "github.listRepositoryIssues",
101 ]);101 ]);
102 expect(githubMatches[0]?.score ?? 0).toBeGreaterThan(0);102 expect(githubMatches[0]?.score ?? 0).toBeGreaterThan(0);
103 103
104 const repoMatches = yield* discoverTools(executor, "repo details", 5);104 const repoMatches = yield* searchTools(executor, "repo details", 5);
105 expect(repoMatches[0]?.path).toBe("github.getRepositoryDetails");105 expect(repoMatches[0]?.path).toBe("github.getRepositoryDetails");
106 106
107 const crmMatches = yield* discoverTools(executor, "crm create contact", 5);107 const crmMatches = yield* searchTools(executor, "crm create contact", 5);
108 expect(crmMatches[0]?.path).toBe("crm.createContact");108 expect(crmMatches[0]?.path).toBe("crm.createContact");
109 expect(crmMatches[0]?.score ?? 0).toBeGreaterThan(crmMatches[1]?.score ?? 0);109 expect(crmMatches[0]?.score ?? 0).toBeGreaterThan(crmMatches[1]?.score ?? 0);
110 }),110 }),
@@ -113,7 +113,7 @@ describe("tool discovery", () => { · 2 unmodified lines
113 it.effect("returns no matches for empty queries instead of listing arbitrary tools", () =>113 it.effect("returns no matches for empty queries instead of listing arbitrary tools", () =>
114 Effect.gen(function* () {114 Effect.gen(function* () {
115 const executor = yield* makeSearchExecutor();115 const executor = yield* makeSearchExecutor();
116 const matches = yield* discoverTools(executor, "", 5);116 const matches = yield* searchTools(executor, "", 5);
117 expect(matches).toEqual([]);117 expect(matches).toEqual([]);
118 }),118 }),
119 );119 );
@@ -122,14 +122,14 @@ describe("tool discovery", () => { · 2 unmodified lines
122 Effect.gen(function* () {122 Effect.gen(function* () {
123 const executor = yield* makeSearchExecutor();123 const executor = yield* makeSearchExecutor();
124 124
125 const githubOnly = yield* discoverTools(executor, "list", 5, {125 const githubOnly = yield* searchTools(executor, "list", 5, {
126 namespace: "github",126 namespace: "github",
127 });127 });
128 expect(githubOnly.map((match) => match.path)).toEqual([128 expect(githubOnly.map((match) => match.path)).toEqual([
129 "github.listRepositoryIssues",129 "github.listRepositoryIssues",
130 ]);130 ]);
131 131
132 const crmOnly = yield* discoverTools(executor, "list", 5, {132 const crmOnly = yield* searchTools(executor, "list", 5, {
133 namespace: "crm",133 namespace: "crm",
134 });134 });
135 expect(crmOnly.map((match) => match.path)).toEqual([135 expect(crmOnly.map((match) => match.path)).toEqual([
@@ -138,7 +138,7 @@ describe("tool discovery", () => { · 2 unmodified lines
138 138
139 const sandboxResult = yield* Effect.promise(() =>139 const sandboxResult = yield* Effect.promise(() =>
140 createExecutionEngine({ executor }).execute(140 createExecutionEngine({ executor }).execute(
141 'return await tools.discover({ namespace: "crm", query: "create contact", limit: 5 });',141 'return await tools.search({ namespace: "crm", query: "create contact", limit: 5 });',
142 { onElicitation: acceptAll },142 { onElicitation: acceptAll },
143 ),143 ),
144 );144 );
@@ -169,7 +169,7 @@ describe("tool discovery", () => { · 24 unmodified lines
169 169
170 const searched = yield* Effect.promise(() =>170 const searched = yield* Effect.promise(() =>
171 createExecutionEngine({ executor }).execute(171 createExecutionEngine({ executor }).execute(
172 'return await tools.executor.tools.search({ query: "list contacts", namespace: "crm", limit: 5 });',172 'return await tools.search({ query: "list contacts", namespace: "crm", limit: 5 });',
173 { onElicitation: acceptAll },173 { onElicitation: acceptAll },
174 ),174 ),
175 );175 );
@@ -219,7 +219,7 @@ describe("tool discovery", () => { · 43 unmodified lines
219 engine.execute(219 engine.execute(
220 [220 [
221 "try {",221 "try {",
222 " await tools.discover(\"github issues\");",222 " await tools.search(\"github issues\");",
223 " return \"unexpected\";",223 " return \"unexpected\";",
224 "} catch (error) {",224 "} catch (error) {",
225 " return error instanceof Error ? error.message : String(error);",225 " return error instanceof Error ? error.message : String(error);",
@@ -230,12 +230,12 @@ describe("tool discovery", () => { · 4 unmodified lines
230 );230 );
231 expect(invalid.error).toBeUndefined();231 expect(invalid.error).toBeUndefined();
232 expect(String(invalid.result)).toContain(232 expect(String(invalid.result)).toContain(
233 "tools.discover expects an object: { query?: string; namespace?: string; limit?: number }",233 "tools.search expects an object: { query?: string; namespace?: string; limit?: number }",
234 );234 );
235 235
236 const emptyQuery = yield* Effect.promise(() =>236 const emptyQuery = yield* Effect.promise(() =>
237 engine.execute(237 engine.execute(
238 "return await tools.discover({ query: \"\", limit: 5 });",238 "return await tools.search({ query: \"\", limit: 5 });",
239 { onElicitation: acceptAll },239 { onElicitation: acceptAll },
240 ),240 ),
241 );241 );
@@ -260,15 +260,15 @@ describe("tool discovery", () => { · 18 unmodified lines
260 "tools.describe.tool no longer accepts includeSchemas",260 "tools.describe.tool no longer accepts includeSchemas",
261 );261 );
262 262
263 const invalidExecutorSearch = yield* Effect.promise(() =>263 const invalidSearch = yield* Effect.promise(() =>
264 engine.execute(264 engine.execute(
265 'try { return await tools.executor.tools.search("crm"); } catch (error) { return error instanceof Error ? error.message : String(error); }',265 'try { return await tools.search("crm"); } catch (error) { return error instanceof Error ? error.message : String(error); }',
266 { onElicitation: acceptAll },266 { onElicitation: acceptAll },
267 ),267 ),
268 );268 );
269 expect(invalidExecutorSearch.error).toBeUndefined();269 expect(invalidSearch.error).toBeUndefined();
270 expect(String(invalidExecutorSearch.result)).toContain(270 expect(String(invalidSearch.result)).toContain(
271 "tools.executor.tools.search expects an object",271 "tools.search expects an object",
272 );272 );
273 }),273 }),
274 );274 );