History

misc stuff

Rhys Sullivan committed 8 months ago

Commit f045f3c1 parent 20f99de16 files · +744 −1741Browse files

Showing the changes to packages/codemode-core/src (4 of 16 files). Show all

Unified · Split

4 files
  1. packages/codemode-core/src/discovery.ts +205 −33
  2. packages/codemode-core/src/index.test.ts +78 −0
  3. packages/codemode-core/src/system-tools.ts +143 −167
  4. packages/codemode-core/src/types.ts +40 −0
packages/codemode-core/src/discovery.ts+205−33
@@ -6,10 +6,14 @@ import type { · 5 unmodified lines
6 DescribePrimitive,6 DescribePrimitive,
7 DiscoverPrimitive,7 DiscoverPrimitive,
8 DiscoveryPrimitives,8 DiscoveryPrimitives,
9 SearchHit,
9 ToolCatalog,10 ToolCatalog,
11 ToolCatalogEntry,
10 ToolDescriptor,12 ToolDescriptor,
11 ToolMap,13 ToolMap,
14 ToolNamespace,
12 ToolPath,15 ToolPath,
16 ToolSchemaBundle,
13} from "./types";17} from "./types";
14 18
15const tokenize = (value: string): string[] =>19const tokenize = (value: string): string[] =>
@@ -57,18 +61,117 @@ const namespaceFromPath = (path: string): string => { · 41 unmodified lines
57 return second ? `${first}.${second}` : first;61 return second ? `${first}.${second}` : first;
58};62};
59 63
64const namespaceFromCatalogEntry = (entry: ToolCatalogEntry): string =>
65 entry.namespace ?? namespaceFromPath(entry.descriptor.path);
66
67const searchableTextForEntry = (entry: ToolCatalogEntry): string =>
68 entry.searchText?.trim().toLowerCase() || searchableTextForTool(entry.descriptor);
69
70const scoreCatalogEntry = (
71 queryTokens: readonly string[],
72 entry: ToolCatalogEntry,
73): number => {
74 if (entry.score) {
75 return entry.score(queryTokens);
76 }
77
78 const haystack = searchableTextForEntry(entry);
79 return queryTokens.reduce(
80 (total, token) => total + (haystack.includes(token) ? 1 : 0),
81 0,
82 );
83};
84
85const mergeToolNamespaces = (
86 groups: ReadonlyArray<readonly ToolNamespace[]>,
87): ToolNamespace[] => {
88 const merged = new Map<string, ToolNamespace>();
89
90 for (const group of groups) {
91 for (const namespace of group) {
92 const existing = merged.get(namespace.namespace);
93 merged.set(namespace.namespace, {
94 namespace: namespace.namespace,
95 displayName: existing?.displayName ?? namespace.displayName,
96 ...(existing?.toolCount !== undefined || namespace.toolCount !== undefined
97 ? {
98 toolCount:
99 (existing?.toolCount ?? 0) + (namespace.toolCount ?? 0),
100 }
101 : {}),
102 });
103 }
104 }
105
106 return [...merged.values()].sort((left, right) =>
107 left.namespace.localeCompare(right.namespace),
108 );
109};
110
111const dedupeToolDescriptors = (
112 groups: ReadonlyArray<readonly ToolDescriptor[]>,
113): ToolDescriptor[] => {
114 const merged = new Map<string, ToolDescriptor>();
115
116 for (const group of groups) {
117 for (const descriptor of group) {
118 if (!merged.has(descriptor.path)) {
119 merged.set(descriptor.path, descriptor);
120 }
121 }
122 }
123
124 return [...merged.values()].sort((left, right) =>
125 left.path.localeCompare(right.path),
126 );
127};
128
129const dedupeSearchHits = (
130 groups: ReadonlyArray<readonly SearchHit[]>,
131): SearchHit[] => {
132 const merged = new Map<string, SearchHit>();
133
134 for (const group of groups) {
135 for (const hit of group) {
136 if (!merged.has(hit.path)) {
137 merged.set(hit.path, hit);
138 }
139 }
140 }
141
142 return [...merged.values()].sort(
143 (left, right) => right.score - left.score || left.path.localeCompare(right.path),
144 );
145};
146
60export function createToolCatalogFromTools(input: {147export function createToolCatalogFromTools(input: {
61 tools: ToolMap;148 tools: ToolMap;
62 defaultNamespace?: string;149 defaultNamespace?: string;
63}): ToolCatalog {150}): ToolCatalog {
64 const descriptors = toolDescriptorsFromTools({151 return createToolCatalogFromEntries({
65 tools: input.tools,152 entries: toolDescriptorsFromTools({
153 tools: input.tools,
154 }).map((descriptor) => ({
155 descriptor,
156 ...(input.defaultNamespace !== undefined
157 ? { namespace: input.defaultNamespace }
158 : {}),
159 })),
66 });160 });
67 const byPath = new Map(descriptors.map((descriptor) => [descriptor.path as string, descriptor]));161}
162
163export function createToolCatalogFromEntries(input: {
164 entries: ReadonlyArray<ToolCatalogEntry>;
165 getSchemaBundle?: (input: { id: string }) => Effect.Effect<ToolSchemaBundle | null, unknown>;
166}): ToolCatalog {
167 const entries = [...input.entries];
168 const byPath = new Map(
169 entries.map((entry) => [entry.descriptor.path as string, entry]),
170 );
68 const namespaceCounts = new Map<string, number>();171 const namespaceCounts = new Map<string, number>();
69 172
70 for (const descriptor of descriptors) {173 for (const entry of entries) {
71 const namespace = input.defaultNamespace ?? namespaceFromPath(descriptor.path);174 const namespace = namespaceFromCatalogEntry(entry);
72 namespaceCounts.set(namespace, (namespaceCounts.get(namespace) ?? 0) + 1);175 namespaceCounts.set(namespace, (namespaceCounts.get(namespace) ?? 0) + 1);
73 }176 }
74 177
@@ -81,23 +184,22 @@ export function createToolCatalogFromTools(input: { · 6 unmodified lines
81 ),184 ),
82 listTools: ({ namespace, query, limit, includeSchemas = false }) =>185 listTools: ({ namespace, query, limit, includeSchemas = false }) =>
83 Effect.succeed(186 Effect.succeed(
84 descriptors187 entries
85 .filter((descriptor) => {188 .filter((entry) =>
86 const descriptorNamespace = input.defaultNamespace ?? namespaceFromPath(descriptor.path);189 !namespace || namespaceFromCatalogEntry(entry) === namespace,
87 return !namespace || descriptorNamespace === namespace;190 )
88 })191 .filter((entry) => {
89 .filter((descriptor) => {
90 if (!query) {192 if (!query) {
91 return true;193 return true;
92 }194 }
93 195
94 const haystack = searchableTextForTool(descriptor);196 const haystack = searchableTextForEntry(entry);
95 return tokenize(query).every((token) => haystack.includes(token));197 return tokenize(query).every((token) => haystack.includes(token));
96 })198 })
97 .slice(0, limit)199 .slice(0, limit)
98 .map((descriptor) =>200 .map((entry) =>
99 projectDescriptor({201 projectDescriptor({
100 descriptor,202 descriptor: entry.descriptor,
101 includeSchemas,203 includeSchemas,
102 })204 })
103 ),205 ),
@@ -106,33 +208,27 @@ export function createToolCatalogFromTools(input: { · 2 unmodified lines
106 Effect.succeed(208 Effect.succeed(
107 byPath.get(path)209 byPath.get(path)
108 ? projectDescriptor({210 ? projectDescriptor({
109 descriptor: byPath.get(path)!,211 descriptor: byPath.get(path)!.descriptor,
110 includeSchemas,212 includeSchemas,
111 })213 })
112 : null,214 : null,
113 ),215 ),
114 getSchemaBundle: () => Effect.succeed(null),216 getSchemaBundle: ({ id }) =>
217 input.getSchemaBundle
218 ? input.getSchemaBundle({ id })
219 : Effect.succeed(null),
115 searchTools: ({ query, namespace, limit }) => {220 searchTools: ({ query, namespace, limit }) => {
116 const queryTokens = tokenize(query);221 const queryTokens = tokenize(query);
117 222
118 return Effect.succeed(223 return Effect.succeed(
119 descriptors224 entries
120 .filter((descriptor) => {225 .filter((entry) =>
121 const descriptorNamespace = input.defaultNamespace ?? namespaceFromPath(descriptor.path);226 !namespace || namespaceFromCatalogEntry(entry) === namespace,
122 return !namespace || descriptorNamespace === namespace;227 )
123 })228 .map((entry) => ({
124 .map((descriptor) => {229 path: entry.descriptor.path,
125 const haystack = searchableTextForTool(descriptor);230 score: scoreCatalogEntry(queryTokens, entry),
126 const score = queryTokens.reduce(231 }))
127 (total, token) => total + (haystack.includes(token) ? 1 : 0),
128 0,
129 );
130
131 return {
132 path: descriptor.path,
133 score,
134 };
135 })
136 .filter((hit) => hit.score > 0)232 .filter((hit) => hit.score > 0)
137 .sort((left, right) => right.score - left.score)233 .sort((left, right) => right.score - left.score)
138 .slice(0, limit),234 .slice(0, limit),
@@ -141,6 +237,82 @@ export function createToolCatalogFromTools(input: { · 2 unmodified lines
141 } satisfies ToolCatalog;237 } satisfies ToolCatalog;
142}238}
143 239
240export function mergeToolCatalogs(input: {
241 catalogs: ReadonlyArray<ToolCatalog>;
242}): ToolCatalog {
243 const catalogs = [...input.catalogs];
244
245 return {
246 listNamespaces: ({ limit }) =>
247 Effect.gen(function* () {
248 const groups = yield* Effect.forEach(
249 catalogs,
250 (catalog) => catalog.listNamespaces({ limit: Math.max(limit, limit * catalogs.length) }),
251 { concurrency: "unbounded" },
252 );
253
254 return mergeToolNamespaces(groups).slice(0, limit);
255 }),
256
257 listTools: ({ namespace, query, limit, includeSchemas = false }) =>
258 Effect.gen(function* () {
259 const groups = yield* Effect.forEach(
260 catalogs,
261 (catalog) =>
262 catalog.listTools({
263 ...(namespace !== undefined ? { namespace } : {}),
264 ...(query !== undefined ? { query } : {}),
265 limit: Math.max(limit, limit * catalogs.length),
266 includeSchemas,
267 }),
268 { concurrency: "unbounded" },
269 );
270
271 return dedupeToolDescriptors(groups).slice(0, limit);
272 }),
273
274 getToolByPath: ({ path, includeSchemas }) =>
275 Effect.gen(function* () {
276 for (const catalog of catalogs) {
277 const descriptor = yield* catalog.getToolByPath({ path, includeSchemas });
278 if (descriptor) {
279 return descriptor;
280 }
281 }
282
283 return null;
284 }),
285
286 getSchemaBundle: ({ id }) =>
287 Effect.gen(function* () {
288 for (const catalog of catalogs) {
289 const bundle = yield* catalog.getSchemaBundle({ id });
290 if (bundle) {
291 return bundle;
292 }
293 }
294
295 return null;
296 }),
297
298 searchTools: ({ query, namespace, limit }) =>
299 Effect.gen(function* () {
300 const groups = yield* Effect.forEach(
301 catalogs,
302 (catalog) =>
303 catalog.searchTools({
304 query,
305 ...(namespace !== undefined ? { namespace } : {}),
306 limit: Math.max(limit, limit * catalogs.length),
307 }),
308 { concurrency: "unbounded" },
309 );
310
311 return dedupeSearchHits(groups).slice(0, limit);
312 }),
313 } satisfies ToolCatalog;
314}
315
144export function createStaticDiscoveryFromTools(input: {316export function createStaticDiscoveryFromTools(input: {
145 tools: ToolMap;317 tools: ToolMap;
146 sourceKey?: string;318 sourceKey?: string;
packages/codemode-core/src/index.test.ts+78−0
@@ -9,6 +9,7 @@ import * as TestClock from "effect/TestClock"; · 8 unmodified lines
9import {9import {
10 allowAllToolInteractions,10 allowAllToolInteractions,
11 createToolCatalogDiscovery,11 createToolCatalogDiscovery,
12 createToolCatalogFromEntries,
12 createToolCatalogFromTools,13 createToolCatalogFromTools,
13 createStaticDiscoveryFromTools,14 createStaticDiscoveryFromTools,
14 createSystemToolMap,15 createSystemToolMap,
@@ -355,6 +356,83 @@ describe("codemode-core", () => { · 340 unmodified lines
355 }),356 }),
356 );357 );
357 358
359 it.effect("builds a tool catalog from descriptor entries", () =>
360 Effect.gen(function* () {
361 const catalog = createToolCatalogFromEntries({
362 entries: [
363 {
364 namespace: "github.repos",
365 searchText: "github repository details get repo",
366 descriptor: {
367 path: asToolPath("github.repos.getRepo"),
368 sourceKey: "source.github",
369 description: "Get repository details",
370 inputType: "{ owner: string; repo: string }",
371 outputType: "Repository",
372 inputSchemaJson: '{"type":"object"}',
373 },
374 },
375 {
376 namespace: "github.issues",
377 searchText: "github issues list repository issues",
378 score: (queryTokens) =>
379 queryTokens.includes("issues") && queryTokens.includes("repository")
380 ? 50
381 : 0,
382 descriptor: {
383 path: asToolPath("github.issues.list"),
384 sourceKey: "source.github",
385 description: "List repository issues",
386 inputType: "{ owner: string; repo: string }",
387 outputType: "Issue[]",
388 },
389 },
390 ],
391 getSchemaBundle: ({ id }) =>
392 Effect.succeed(
393 id === "bundle_1"
394 ? {
395 id,
396 kind: "json_schema_ref_map",
397 hash: "hash_1",
398 refsJson: "{}",
399 }
400 : null,
401 ),
402 });
403
404 const namespaces = yield* catalog.listNamespaces({ limit: 10 });
405 expect(namespaces).toEqual([
406 { namespace: "github.repos", toolCount: 1 },
407 { namespace: "github.issues", toolCount: 1 },
408 ]);
409
410 const listed = yield* catalog.listTools({
411 namespace: "github.repos",
412 query: "repository details",
413 limit: 10,
414 includeSchemas: false,
415 });
416 expect(listed[0]?.path).toBe("github.repos.getRepo");
417 expect(listed[0]?.inputSchemaJson).toBeUndefined();
418
419 const discovered = yield* catalog.searchTools({
420 query: "repository issues",
421 limit: 10,
422 });
423 expect(discovered[0]?.path).toBe("github.issues.list");
424
425 const described = yield* catalog.getToolByPath({
426 path: asToolPath("github.repos.getRepo"),
427 includeSchemas: true,
428 });
429 expect(described?.inputSchemaJson).toContain('"type"');
430
431 const bundle = yield* catalog.getSchemaBundle({ id: "bundle_1" });
432 expect(bundle?.id).toBe("bundle_1");
433 }),
434 );
435
358 it.effect("system tools can be composed as normal tools", () =>436 it.effect("system tools can be composed as normal tools", () =>
359 Effect.gen(function* () {437 Effect.gen(function* () {
360 const catalog = createToolCatalogFromTools({438 const catalog = createToolCatalogFromTools({
packages/codemode-core/src/system-tools.ts+143−167
@@ -3,11 +3,13 @@ import * as Schema from "effect/Schema"; · 2 unmodified lines
3 3
4import { createDiscoveryPrimitivesFromToolCatalog } from "./discovery";4import { createDiscoveryPrimitivesFromToolCatalog } from "./discovery";
5import { toTool } from "./tool-map";5import { toTool } from "./tool-map";
6import type {6import {
7 DiscoveryPrimitives,7 ToolDescriptorSchema,
8 ToolCatalog,8 ToolNamespaceSchema,
9 ToolMap,9 ToolSchemaBundleSchema,
10 ToolPath,10 type ToolCatalog,
11 type ToolMap,
12 type ToolPath,
11} from "./types";13} from "./types";
12 14
13const asToolPath = (value: string): ToolPath => value as ToolPath;15const asToolPath = (value: string): ToolPath => value as ToolPath;
@@ -20,13 +22,7 @@ const catalogNamespacesInputSchema = Schema.standardSchemaV1( · 6 unmodified lines
20 22
21const catalogNamespacesOutputSchema = Schema.standardSchemaV1(23const catalogNamespacesOutputSchema = Schema.standardSchemaV1(
22 Schema.Struct({24 Schema.Struct({
23 namespaces: Schema.Array(25 namespaces: Schema.Array(ToolNamespaceSchema),
24 Schema.Struct({
25 namespace: Schema.String,
26 displayName: Schema.optional(Schema.String),
27 toolCount: Schema.optional(Schema.Number),
28 }),
29 ),
30 }),26 }),
31);27);
32 28
@@ -41,23 +37,7 @@ const catalogToolsInputSchema = Schema.standardSchemaV1( · 8 unmodified lines
41 37
42const catalogToolsOutputSchema = Schema.standardSchemaV1(38const catalogToolsOutputSchema = Schema.standardSchemaV1(
43 Schema.Struct({39 Schema.Struct({
44 results: Schema.Array(40 results: Schema.Array(ToolDescriptorSchema),
45 Schema.Struct({
46 path: Schema.String,
47 sourceKey: Schema.String,
48 description: Schema.optional(Schema.String),
49 interaction: Schema.optional(Schema.String),
50 inputType: Schema.optional(Schema.String),
51 outputType: Schema.optional(Schema.String),
52 inputSchemaJson: Schema.optional(Schema.String),
53 outputSchemaJson: Schema.optional(Schema.String),
54 schemaBundleId: Schema.optional(Schema.String),
55 exampleInputJson: Schema.optional(Schema.String),
56 exampleOutputJson: Schema.optional(Schema.String),
57 providerKind: Schema.optional(Schema.String),
58 providerDataJson: Schema.optional(Schema.String),
59 }),
60 ),
61 }),41 }),
62);42);
63 43
@@ -69,7 +49,7 @@ const describeToolInputSchema = Schema.standardSchemaV1( · 5 unmodified lines
69);49);
70 50
71const describeToolOutputSchema = Schema.standardSchemaV1(51const describeToolOutputSchema = Schema.standardSchemaV1(
72 Schema.NullOr(Schema.Unknown),52 Schema.NullOr(ToolDescriptorSchema),
73);53);
74 54
75const describeSchemaBundleInputSchema = Schema.standardSchemaV1(55const describeSchemaBundleInputSchema = Schema.standardSchemaV1(
@@ -79,14 +59,7 @@ const describeSchemaBundleInputSchema = Schema.standardSchemaV1( · 3 unmodified lines
79);59);
80 60
81const describeSchemaBundleOutputSchema = Schema.standardSchemaV1(61const describeSchemaBundleOutputSchema = Schema.standardSchemaV1(
82 Schema.NullOr(62 Schema.NullOr(ToolSchemaBundleSchema),
83 Schema.Struct({
84 id: Schema.String,
85 kind: Schema.String,
86 hash: Schema.String,
87 refsJson: Schema.String,
88 }),
89 ),
90);63);
91 64
92const discoverInputSchema = Schema.standardSchemaV1(65const discoverInputSchema = Schema.standardSchemaV1(
@@ -97,17 +70,12 @@ const discoverInputSchema = Schema.standardSchemaV1( · 4 unmodified lines
97 }),70 }),
98);71);
99 72
100const discoverResultItemSchema = Schema.Struct({73const discoverResultItemSchema = Schema.extend(
101 path: Schema.String,74 ToolDescriptorSchema,
102 score: Schema.Number,75 Schema.Struct({
103 description: Schema.optional(Schema.String),76 score: Schema.Number,
104 interaction: Schema.optional(Schema.String),77 }),
105 inputType: Schema.optional(Schema.String),78);
106 outputType: Schema.optional(Schema.String),
107 inputSchemaJson: Schema.optional(Schema.String),
108 outputSchemaJson: Schema.optional(Schema.String),
109 schemaBundleId: Schema.optional(Schema.String),
110});
111 79
112const discoverOutputSchema = Schema.standardSchemaV1(80const discoverOutputSchema = Schema.standardSchemaV1(
113 Schema.Struct({81 Schema.Struct({
@@ -118,7 +86,8 @@ const discoverOutputSchema = Schema.standardSchemaV1( · 4 unmodified lines
118);86);
119 87
120export type CreateSystemToolMapInput = {88export type CreateSystemToolMapInput = {
121 catalog: ToolCatalog;89 catalog?: ToolCatalog;
90 getCatalog?: () => ToolCatalog;
122 sourceKey?: string;91 sourceKey?: string;
123};92};
124 93
@@ -126,130 +95,137 @@ export const createSystemToolMap = ( · 1 unmodified line
126 input: CreateSystemToolMapInput,95 input: CreateSystemToolMapInput,
127): ToolMap => {96): ToolMap => {
128 const sourceKey = input.sourceKey ?? "system";97 const sourceKey = input.sourceKey ?? "system";
129 const primitives: DiscoveryPrimitives = createDiscoveryPrimitivesFromToolCatalog({98 const getCatalog = (): ToolCatalog => {
130 catalog: input.catalog,99 if (input.catalog) {
131 });100 return input.catalog;
101 }
102 if (input.getCatalog) {
103 return input.getCatalog();
104 }
105
106 throw new Error("createSystemToolMap requires a catalog or getCatalog");
107 };
108 const getPrimitives = () =>
109 createDiscoveryPrimitivesFromToolCatalog({
110 catalog: getCatalog(),
111 }) as Required<ReturnType<typeof createDiscoveryPrimitivesFromToolCatalog>>;
132 112
133 const tools: ToolMap = {};113 const tools: ToolMap = {};
134 114
135 if (primitives.catalog) {115 tools["catalog.namespaces"] = toTool({
136 tools["catalog.namespaces"] = toTool({116 tool: {
137 tool: {117 description: "List available namespaces with display names and tool counts",
138 description: "List available namespaces with display names and tool counts",118 inputSchema: catalogNamespacesInputSchema,
139 inputSchema: catalogNamespacesInputSchema,119 outputSchema: catalogNamespacesOutputSchema,
140 outputSchema: catalogNamespacesOutputSchema,120 execute: ({ limit }: { limit?: number }) =>
141 execute: ({ limit }: { limit?: number }) =>121 Effect.runPromise(
142 Effect.runPromise(122 getPrimitives().catalog.namespaces({
143 primitives.catalog!.namespaces({123 ...(limit !== undefined ? { limit } : {}),
144 ...(limit !== undefined ? { limit } : {}),124 }),
145 }),125 ),
146 ),126 },
147 },127 metadata: {
148 metadata: {128 sourceKey,
149 sourceKey,129 interaction: "auto",
150 interaction: "auto",130 },
151 },131 });
152 });
153 132
154 tools["catalog.tools"] = toTool({133 tools["catalog.tools"] = toTool({
155 tool: {134 tool: {
156 description: "List tools with optional namespace and query filters",135 description: "List tools with optional namespace and query filters",
157 inputSchema: catalogToolsInputSchema,136 inputSchema: catalogToolsInputSchema,
158 outputSchema: catalogToolsOutputSchema,137 outputSchema: catalogToolsOutputSchema,
159 execute: (138 execute: (
160 input: {139 toolInput: {
161 namespace?: string;140 namespace?: string;
162 query?: string;141 query?: string;
163 limit?: number;142 limit?: number;
164 includeSchemas?: boolean;143 includeSchemas?: boolean;
165 },144 },
166 ) =>145 ) =>
167 Effect.runPromise(146 Effect.runPromise(
168 primitives.catalog!.tools({147 getPrimitives().catalog.tools({
169 ...(input.namespace !== undefined ? { namespace: input.namespace } : {}),148 ...(toolInput.namespace !== undefined
170 ...(input.query !== undefined ? { query: input.query } : {}),149 ? { namespace: toolInput.namespace }
171 ...(input.limit !== undefined ? { limit: input.limit } : {}),150 : {}),
172 ...(input.includeSchemas !== undefined151 ...(toolInput.query !== undefined ? { query: toolInput.query } : {}),
173 ? { includeSchemas: input.includeSchemas }152 ...(toolInput.limit !== undefined ? { limit: toolInput.limit } : {}),
174 : {}),153 ...(toolInput.includeSchemas !== undefined
175 }),154 ? { includeSchemas: toolInput.includeSchemas }
176 ),155 : {}),
177 },156 }),
178 metadata: {157 ),
179 sourceKey,158 },
180 interaction: "auto",159 metadata: {
181 },160 sourceKey,
182 });161 interaction: "auto",
183 }162 },
163 });
184 164
185 if (primitives.describe) {165 tools["describe.tool"] = toTool({
186 tools["describe.tool"] = toTool({166 tool: {
187 tool: {167 description: "Get metadata and optional schemas for a tool path",
188 description: "Get metadata and optional schemas for a tool path",168 inputSchema: describeToolInputSchema,
189 inputSchema: describeToolInputSchema,169 outputSchema: describeToolOutputSchema,
190 outputSchema: describeToolOutputSchema,170 execute: ({ path, includeSchemas }: { path: string; includeSchemas?: boolean }) =>
191 execute: ({ path, includeSchemas }: { path: string; includeSchemas?: boolean }) =>171 Effect.runPromise(
192 Effect.runPromise(172 getPrimitives().describe.tool({
193 primitives.describe!.tool({173 path: asToolPath(path),
194 path: asToolPath(path),174 ...(includeSchemas !== undefined ? { includeSchemas } : {}),
195 ...(includeSchemas !== undefined ? { includeSchemas } : {}),175 }),
196 }),176 ),
197 ),177 },
198 },178 metadata: {
199 metadata: {179 sourceKey,
200 sourceKey,180 interaction: "auto",
201 interaction: "auto",181 },
202 },182 });
203 });
204 183
205 tools["describe.schemaBundle"] = toTool({184 tools["describe.schemaBundle"] = toTool({
206 tool: {185 tool: {
207 description: "Get a shared schema bundle by id",186 description: "Get a shared schema bundle by id",
208 inputSchema: describeSchemaBundleInputSchema,187 inputSchema: describeSchemaBundleInputSchema,
209 outputSchema: describeSchemaBundleOutputSchema,188 outputSchema: describeSchemaBundleOutputSchema,
210 execute: ({ id }: { id: string }) =>189 execute: ({ id }: { id: string }) =>
211 Effect.runPromise(190 Effect.runPromise(
212 primitives.describe!.schemaBundle({191 getPrimitives().describe.schemaBundle({
213 id,192 id,
214 }),193 }),
215 ),194 ),
216 },195 },
217 metadata: {196 metadata: {
218 sourceKey,197 sourceKey,
219 interaction: "auto",198 interaction: "auto",
220 },199 },
221 });200 });
222 }
223 201
224 if (primitives.discover) {202 tools.discover = toTool({
225 tools.discover = toTool({203 tool: {
226 tool: {204 description: "Search tools by intent and return ranked matches",
227 description: "Search tools by intent and return ranked matches",205 inputSchema: discoverInputSchema,
228 inputSchema: discoverInputSchema,206 outputSchema: discoverOutputSchema,
229 outputSchema: discoverOutputSchema,207 execute: (
230 execute: (208 toolInput: {
231 input: {209 query: string;
232 query: string;210 limit?: number;
233 limit?: number;211 includeSchemas?: boolean;
234 includeSchemas?: boolean;212 },
235 },213 ) =>
236 ) =>214 Effect.runPromise(
237 Effect.runPromise(215 getPrimitives().discover({
238 primitives.discover!({216 query: toolInput.query,
239 query: input.query,217 ...(toolInput.limit !== undefined ? { limit: toolInput.limit } : {}),
240 ...(input.limit !== undefined ? { limit: input.limit } : {}),218 ...(toolInput.includeSchemas !== undefined
241 ...(input.includeSchemas !== undefined219 ? { includeSchemas: toolInput.includeSchemas }
242 ? { includeSchemas: input.includeSchemas }220 : {}),
243 : {}),221 }),
244 }),222 ),
245 ),223 },
246 },224 metadata: {
247 metadata: {225 sourceKey,
248 sourceKey,226 interaction: "auto",
249 interaction: "auto",227 },
250 },228 });
251 });
252 }
253 229
254 return tools;230 return tools;
255};231};
packages/codemode-core/src/types.ts+40−0
@@ -1,5 +1,6 @@
1import type { StandardSchemaV1 } from "@standard-schema/spec";1import type { StandardSchemaV1 } from "@standard-schema/spec";
2import type * as Effect from "effect/Effect";2import type * as Effect from "effect/Effect";
3import * as Schema from "effect/Schema";
3 4
4export type ToolPath = string & { readonly __toolPath: unique symbol };5export type ToolPath = string & { readonly __toolPath: unique symbol };
5 6
@@ -142,6 +143,25 @@ export type ToolDescriptor = { · 136 unmodified lines
142 providerDataJson?: string;143 providerDataJson?: string;
143};144};
144 145
146export const ToolDescriptorSchema = Schema.Struct({
147 path: Schema.String,
148 sourceKey: Schema.String,
149 description: Schema.optional(Schema.String),
150 interaction: Schema.optional(
151 Schema.Union(Schema.Literal("auto"), Schema.Literal("required")),
152 ),
153 elicitation: Schema.optional(Schema.Unknown),
154 inputType: Schema.optional(Schema.String),
155 outputType: Schema.optional(Schema.String),
156 inputSchemaJson: Schema.optional(Schema.String),
157 outputSchemaJson: Schema.optional(Schema.String),
158 schemaBundleId: Schema.optional(Schema.String),
159 exampleInputJson: Schema.optional(Schema.String),
160 exampleOutputJson: Schema.optional(Schema.String),
161 providerKind: Schema.optional(Schema.String),
162 providerDataJson: Schema.optional(Schema.String),
163});
164
145export type ToolSchemaBundle = {165export type ToolSchemaBundle = {
146 id: string;166 id: string;
147 kind: string;167 kind: string;
@@ -149,17 +169,37 @@ export type ToolSchemaBundle = { · 1 unmodified line
149 refsJson: string;169 refsJson: string;
150};170};
151 171
172export const ToolSchemaBundleSchema = Schema.Struct({
173 id: Schema.String,
174 kind: Schema.String,
175 hash: Schema.String,
176 refsJson: Schema.String,
177});
178
152export type ToolNamespace = {179export type ToolNamespace = {
153 namespace: string;180 namespace: string;
154 displayName?: string;181 displayName?: string;
155 toolCount?: number;182 toolCount?: number;
156};183};
157 184
185export const ToolNamespaceSchema = Schema.Struct({
186 namespace: Schema.String,
187 displayName: Schema.optional(Schema.String),
188 toolCount: Schema.optional(Schema.Number),
189});
190
158export type SearchHit = {191export type SearchHit = {
159 path: ToolPath;192 path: ToolPath;
160 score: number;193 score: number;
161};194};
162 195
196export type ToolCatalogEntry = {
197 descriptor: ToolDescriptor;
198 namespace?: string;
199 searchText?: string;
200 score?: (queryTokens: readonly string[]) => number;
201};
202
163export interface ToolCatalog {203export interface ToolCatalog {
164 listNamespaces(input: {204 listNamespaces(input: {
165 limit: number;205 limit: number;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t