History

rework sources

Rhys Sullivan committed 8 months ago

Commit d61734b1 parent 8a8be7991 files · +8145 −4932Browse files

Showing the changes to packages/codemode-openapi/src (6 of 91 files). Show all

Unified · Split

6 files
  1. packages/codemode-openapi/src/index.ts +7 −0
  2. packages/codemode-openapi/src/openapi-definitions.ts +3 −1
  3. packages/codemode-openapi/src/openapi-schema-refs.ts +10 −4
  4. packages/codemode-openapi/src/openapi-tool-presentation.ts +7 −26
  5. packages/codemode-openapi/src/openapi-tools.ts +112 −73
  6. packages/codemode-openapi/src/openapi-types.ts +32 −0
packages/codemode-openapi/src/index.ts+7−0
@@ -19,6 +19,7 @@ export { · 18 unmodified lines
1919 } from "./openapi-tool-presentation";
2020 export {
2121 OpenApiToolInvocationError,
22+ createOpenApiToolFromDefinition,
2223 createOpenApiToolsFromManifest,
2324 createOpenApiToolsFromSpec,
2425 } from "./openapi-tools";
@@ -39,6 +40,9 @@ export { · 14 unmodified lines
3940 OpenApiRequestBodyDocumentationSchema,
4041 OpenApiResponseDocumentationSchema,
4142 OpenApiToolDocumentationSchema,
43+ OpenApiToolProviderDataSchema,
44+ OpenApiRefHintTableSchema,
45+ OpenApiRefHintValueSchema,
4246 type DiscoveryTypingPayload,
4347 type OpenApiExtractedTool,
4448 type OpenApiHttpMethod,
@@ -49,6 +53,9 @@ export { · 4 unmodified lines
4953 type OpenApiParameterLocation,
5054 type OpenApiSpecInput,
5155 type OpenApiToolManifest,
56+ type OpenApiToolProviderData,
57+ type OpenApiRefHintTable,
58+ type OpenApiRefHintValue,
5259 type OpenApiToolParameter,
5360 type OpenApiToolRequestBody,
5461 } from "./openapi-types";
packages/codemode-openapi/src/openapi-definitions.ts+3−1
@@ -5,6 +5,7 @@ import { · 4 unmodified lines
55 OpenApiHttpMethodSchema,
66 OpenApiInvocationPayloadSchema,
77 OpenApiToolDocumentationSchema,
8+ OpenApiToolProviderDataSchema,
89 type OpenApiExtractedTool,
910 type OpenApiToolManifest,
1011 } from "./openapi-types";
@@ -250,5 +251,6 @@ export const openApiProviderDataJsonFromDefinition = ( · 239 unmodified lines
250251 method: definition.method,
251252 path: definition.path,
252253 operationHash: definition.operationHash,
254+ invocation: definition.invocation,
253255 documentation: definition.documentation,
254− });
256+ } satisfies typeof OpenApiToolProviderDataSchema.Type);
packages/codemode-openapi/src/openapi-schema-refs.ts+10−4
@@ -1,4 +1,5 @@
11 type JsonObject = Record<string, unknown>;
2+type RefHintValue = string | JsonObject;
23
34 type DiscoveryTypingLike = {
45 inputSchemaJson?: string;
@@ -18,7 +19,7 @@ const parseJson = (value: string): unknown | null => { · 13 unmodified lines
1819
1920 const resolveSchemaNode = (
2021 value: unknown,
21− refHintTable: Readonly<Record<string, string>>,
22+ refHintTable: Readonly<Record<string, RefHintValue>>,
2223 parsedHintCache: Map<string, unknown>,
2324 activeRefs: ReadonlySet<string>,
2425 ): unknown => {
@@ -38,7 +39,12 @@ const resolveSchemaNode = ( · 13 unmodified lines
3839
3940 if (resolvedRefValue === undefined) {
4041 const rawHint = refHintTable[ref];
41− resolvedRefValue = typeof rawHint === "string" ? parseJson(rawHint) : null;
42+ resolvedRefValue =
43+ typeof rawHint === "string"
44+ ? parseJson(rawHint)
45+ : isJsonObject(rawHint)
46+ ? rawHint
47+ : null;
4248 parsedHintCache.set(ref, resolvedRefValue);
4349 }
4450
@@ -88,7 +94,7 @@ const resolveSchemaNode = ( · 43 unmodified lines
8894
8995 export const resolveSchemaJsonWithRefHints = (
9096 schemaJson: string | undefined,
91− refHintTable: Readonly<Record<string, string>> | undefined,
97+ refHintTable: Readonly<Record<string, RefHintValue>> | undefined,
9298 ): string | null => {
9399 if (!schemaJson) {
94100 return null;
@@ -119,7 +125,7 @@ export const resolveSchemaJsonWithRefHints = ( · 24 unmodified lines
119125
120126 export const resolveTypingSchemasWithRefHints = (
121127 typing: DiscoveryTypingLike | undefined,
122− refHintTable: Readonly<Record<string, string>> | undefined,
128+ refHintTable: Readonly<Record<string, RefHintValue>> | undefined,
123129 ): {
124130 inputSchemaJson: string | null;
125131 outputSchemaJson: string | null;
packages/codemode-openapi/src/openapi-tool-presentation.ts+7−26
@@ -1,14 +1,13 @@
11 import { typeSignatureFromSchemaJson } from "@executor/codemode-core";
22
3−import {
4− resolveTypingSchemasWithRefHints,
5−} from "./openapi-schema-refs";
63 import type {
74 OpenApiExample,
85 OpenApiToolDocumentation,
9− OpenApiToolManifest,
106 } from "./openapi-types";
11−import type { OpenApiToolDefinition } from "./openapi-definitions";
7+import {
8+ openApiProviderDataJsonFromDefinition,
9+ type OpenApiToolDefinition,
10+} from "./openapi-definitions";
1211
1312 const parseJson = (value: string): unknown | undefined => {
1413 try {
@@ -104,15 +103,10 @@ export type OpenApiToolPresentation = { · 89 unmodified lines
104103 };
105104
106105 export const buildOpenApiToolPresentation = (input: {
107− manifest: OpenApiToolManifest;
108106 definition: OpenApiToolDefinition;
109107 }): OpenApiToolPresentation => {
110− const resolvedSchemas = resolveTypingSchemasWithRefHints(
111− input.definition.typing,
112− input.manifest.refHintTable,
113− );
114− const inputSchemaJson = resolvedSchemas.inputSchemaJson ?? undefined;
115− const outputSchemaJson = resolvedSchemas.outputSchemaJson ?? undefined;
108+ const inputSchemaJson = input.definition.typing?.inputSchemaJson;
109+ const outputSchemaJson = input.definition.typing?.outputSchemaJson;
116110 const exampleInputJson = buildExampleInputJson(input.definition.documentation);
117111 const exampleOutputJson = buildExampleOutputJson(input.definition.documentation);
118112
@@ -123,19 +117,6 @@ export const buildOpenApiToolPresentation = (input: { · 4 unmodified lines
123117 ...(outputSchemaJson ? { outputSchemaJson } : {}),
124118 ...(exampleInputJson ? { exampleInputJson } : {}),
125119 ...(exampleOutputJson ? { exampleOutputJson } : {}),
126− providerDataJson: JSON.stringify({
127− kind: "openapi",
128− toolId: input.definition.toolId,
129− rawToolId: input.definition.rawToolId,
130− operationId: input.definition.operationId,
131− group: input.definition.group,
132− leaf: input.definition.leaf,
133− tags: input.definition.tags,
134− versionSegment: input.definition.versionSegment,
135− method: input.definition.method,
136− path: input.definition.path,
137− operationHash: input.definition.operationHash,
138− documentation: input.definition.documentation,
139− }),
120+ providerDataJson: openApiProviderDataJsonFromDefinition(input.definition),
140121 };
141122 };
packages/codemode-openapi/src/openapi-tools.ts+112−73
@@ -19,11 +19,13 @@ import { · 18 unmodified lines
1919 type OpenApiToolDefinition,
2020 } from "./openapi-definitions";
2121 import { buildOpenApiToolPresentation } from "./openapi-tool-presentation";
22+import { resolveSchemaJsonWithRefHints } from "./openapi-schema-refs";
2223 import {
2324 extractOpenApiManifest,
2425 type OpenApiExtractionError,
2526 } from "./openapi-extraction";
2627 import {
28+ type OpenApiRefHintTable,
2729 type OpenApiInvocationPayload,
2830 type OpenApiSpecInput,
2931 type OpenApiToolManifest,
@@ -256,13 +258,21 @@ const summarizeHttpResponseBody = (body: unknown): string | null => { · 226 unmodified lines
256258 }
257259 };
258260
259−const inputSchemaFromTypingJson = (inputSchemaJson: string | undefined) => {
260− if (!inputSchemaJson) {
261+const inputSchemaFromTypingJson = (input: {
262+ inputSchemaJson: string | undefined;
263+ refHintTable?: Readonly<OpenApiRefHintTable>;
264+}) => {
265+ const resolvedSchemaJson = resolveSchemaJsonWithRefHints(
266+ input.inputSchemaJson,
267+ input.refHintTable,
268+ ) ?? input.inputSchemaJson;
269+
270+ if (!resolvedSchemaJson) {
261271 return unknownInputSchema;
262272 }
263273
264274 try {
265− return standardSchemaFromJsonSchema(JSON.parse(inputSchemaJson), {
275+ return standardSchemaFromJsonSchema(JSON.parse(resolvedSchemaJson), {
266276 vendor: "openapi",
267277 fallback: unknownInputSchema,
268278 });
@@ -396,6 +406,97 @@ const buildFetchRequest = (input: { · 127 unmodified lines
396406 const createToolPath = (namespace: string | undefined, definition: OpenApiToolDefinition): string =>
397407 namespace ? `${namespace}.${definition.toolId}` : definition.toolId;
398408
409+export type CreateOpenApiToolFromDefinitionInput = {
410+ definition: OpenApiToolDefinition;
411+ path: string;
412+ sourceKey: string;
413+ baseUrl: string;
414+ defaultHeaders?: Readonly<Record<string, string>>;
415+ credentialHeaders?: Readonly<Record<string, string>>;
416+ refHintTable?: Readonly<OpenApiRefHintTable>;
417+ httpClientLayer?: Layer.Layer<HttpClient.HttpClient, never, never>;
418+};
419+
420+export const createOpenApiToolFromDefinition = (
421+ input: CreateOpenApiToolFromDefinitionInput,
422+) => {
423+ const defaultHeaders = input.defaultHeaders ?? {};
424+ const credentialHeaders = input.credentialHeaders ?? {};
425+ const httpClientLayer = input.httpClientLayer ?? FetchHttpClient.layer;
426+ const presentation = buildOpenApiToolPresentation({
427+ definition: input.definition,
428+ });
429+
430+ return toTool({
431+ tool: {
432+ description: input.definition.description,
433+ inputSchema: inputSchemaFromTypingJson({
434+ inputSchemaJson: presentation.inputSchemaJson ?? input.definition.typing?.inputSchemaJson,
435+ refHintTable: input.refHintTable,
436+ }),
437+ execute: async (args: unknown) => {
438+ const decodedArgs = asToolArgs(args);
439+ const request = buildFetchRequest({
440+ payload: input.definition.invocation,
441+ args: decodedArgs,
442+ baseUrl: input.baseUrl,
443+ defaultHeaders,
444+ credentialHeaders,
445+ });
446+
447+ return Effect.runPromise(
448+ Effect.gen(function* () {
449+ const client = yield* HttpClient.HttpClient;
450+ let clientRequest = HttpClientRequest.make(
451+ request.method as Parameters<typeof HttpClientRequest.make>[0],
452+ )(request.url, {
453+ headers: request.headers,
454+ });
455+
456+ if (request.body !== undefined) {
457+ clientRequest = HttpClientRequest.bodyText(
458+ clientRequest,
459+ request.body,
460+ request.headers["content-type"],
461+ );
462+ }
463+
464+ const response = yield* client.execute(clientRequest).pipe(
465+ Effect.mapError((cause) =>
466+ cause instanceof Error ? cause : new Error(String(cause)),
467+ ),
468+ );
469+ const body = yield* decodeHttpClientResponseBody(response);
470+
471+ if (response.status < 200 || response.status >= 300) {
472+ throw new OpenApiToolInvocationError({
473+ operation: "http_response",
474+ message: `OpenAPI request failed with HTTP ${response.status}`,
475+ details: summarizeHttpResponseBody(body),
476+ });
477+ }
478+
479+ return body;
480+ }).pipe(
481+ Effect.provide(httpClientLayer),
482+ ),
483+ );
484+ },
485+ },
486+ metadata: {
487+ sourceKey: input.sourceKey,
488+ inputType: presentation.inputType,
489+ outputType: presentation.outputType,
490+ inputSchemaJson: presentation.inputSchemaJson,
491+ outputSchemaJson: presentation.outputSchemaJson,
492+ exampleInputJson: presentation.exampleInputJson,
493+ exampleOutputJson: presentation.exampleOutputJson,
494+ providerKind: "openapi",
495+ providerDataJson: presentation.providerDataJson,
496+ },
497+ });
498+};
499+
399500 export const createOpenApiToolsFromManifest = (
400501 input: CreateOpenApiToolsFromManifestInput,
401502 ): ToolMap => {
@@ -410,77 +511,15 @@ export const createOpenApiToolsFromManifest = ( · 8 unmodified lines
410511
411512 for (const definition of definitions) {
412513 const toolPath = createToolPath(input.namespace, definition);
413− const presentation = buildOpenApiToolPresentation({
414− manifest: input.manifest,
514+ result[toolPath] = createOpenApiToolFromDefinition({
415515 definition,
416− });
417−
418− result[toolPath] = toTool({
419− tool: {
420− description: definition.description,
421− inputSchema: inputSchemaFromTypingJson(
422− presentation.inputSchemaJson ?? definition.typing?.inputSchemaJson,
423− ),
424− execute: async (args: unknown) => {
425− const decodedArgs = asToolArgs(args);
426− const request = buildFetchRequest({
427− payload: definition.invocation,
428− args: decodedArgs,
429− baseUrl,
430− defaultHeaders,
431− credentialHeaders,
432− });
433−
434− return Effect.runPromise(
435− Effect.gen(function* () {
436− const client = yield* HttpClient.HttpClient;
437− let clientRequest = HttpClientRequest.make(
438− request.method as Parameters<typeof HttpClientRequest.make>[0],
439− )(request.url, {
440− headers: request.headers,
441− });
442−
443− if (request.body !== undefined) {
444− clientRequest = HttpClientRequest.bodyText(
445− clientRequest,
446− request.body,
447− request.headers["content-type"],
448− );
449− }
450−
451− const response = yield* client.execute(clientRequest).pipe(
452− Effect.mapError((cause) =>
453− cause instanceof Error ? cause : new Error(String(cause)),
454− ),
455− );
456− const body = yield* decodeHttpClientResponseBody(response);
457−
458− if (response.status < 200 || response.status >= 300) {
459− throw new OpenApiToolInvocationError({
460− operation: "http_response",
461− message: `OpenAPI request failed with HTTP ${response.status}`,
462− details: summarizeHttpResponseBody(body),
463− });
464− }
465−
466− return body;
467− }).pipe(
468− Effect.provide(httpClientLayer),
469− ),
470− );
471− },
472− },
473− metadata: {
474− sourceKey,
475− inputType: presentation.inputType,
476− outputType: presentation.outputType,
477− inputSchemaJson: presentation.inputSchemaJson,
478− outputSchemaJson: presentation.outputSchemaJson,
479− exampleInputJson: presentation.exampleInputJson,
480− exampleOutputJson: presentation.exampleOutputJson,
481− providerKind: "openapi",
482− providerDataJson: presentation.providerDataJson,
483− },
516+ path: toolPath,
517+ sourceKey,
518+ baseUrl,
519+ defaultHeaders,
520+ credentialHeaders,
521+ refHintTable: input.manifest.refHintTable,
522+ httpClientLayer,
484523 });
485524 }
486525
packages/codemode-openapi/src/openapi-types.ts+32−0
@@ -63,6 +63,19 @@ export const DiscoveryTypingPayloadSchema = Schema.Struct({ · 62 unmodified lines
6363 refHintKeys: Schema.optional(Schema.Array(Schema.String)),
6464 });
6565
66+export const OpenApiRefHintValueSchema = Schema.Union(
67+ Schema.String,
68+ Schema.Record({
69+ key: Schema.String,
70+ value: Schema.Unknown,
71+ }),
72+);
73+
74+export const OpenApiRefHintTableSchema = Schema.Record({
75+ key: Schema.String,
76+ value: OpenApiRefHintValueSchema,
77+});
78+
6679 export const OpenApiExampleSchema = Schema.Struct({
6780 valueJson: Schema.String,
6881 mediaType: Schema.optional(Schema.String),
@@ -97,6 +110,22 @@ export const OpenApiToolDocumentationSchema = Schema.Struct({ · 28 unmodified lines
97110 response: Schema.optional(OpenApiResponseDocumentationSchema),
98111 });
99112
113+export const OpenApiToolProviderDataSchema = Schema.Struct({
114+ kind: Schema.Literal("openapi"),
115+ toolId: Schema.String,
116+ rawToolId: Schema.String,
117+ operationId: Schema.optional(Schema.String),
118+ group: Schema.String,
119+ leaf: Schema.String,
120+ tags: Schema.Array(Schema.String),
121+ versionSegment: Schema.optional(Schema.String),
122+ method: OpenApiHttpMethodSchema,
123+ path: Schema.String,
124+ operationHash: Schema.String,
125+ invocation: OpenApiInvocationPayloadSchema,
126+ documentation: Schema.optional(OpenApiToolDocumentationSchema),
127+});
128+
100129 export const OpenApiExtractedToolSchema = Schema.Struct({
101130 toolId: Schema.String,
102131 operationId: Schema.optional(Schema.String),
@@ -129,10 +158,13 @@ export type OpenApiToolParameter = typeof OpenApiToolParameterSchema.Type; · 26 unmodified lines
129158 export type OpenApiToolRequestBody = typeof OpenApiToolRequestBodySchema.Type;
130159 export type OpenApiInvocationPayload = typeof OpenApiInvocationPayloadSchema.Type;
131160 export type DiscoveryTypingPayload = typeof DiscoveryTypingPayloadSchema.Type;
161+export type OpenApiRefHintValue = typeof OpenApiRefHintValueSchema.Type;
162+export type OpenApiRefHintTable = typeof OpenApiRefHintTableSchema.Type;
132163 export type OpenApiExample = typeof OpenApiExampleSchema.Type;
133164 export type OpenApiParameterDocumentation = typeof OpenApiParameterDocumentationSchema.Type;
134165 export type OpenApiRequestBodyDocumentation = typeof OpenApiRequestBodyDocumentationSchema.Type;
135166 export type OpenApiResponseDocumentation = typeof OpenApiResponseDocumentationSchema.Type;
136167 export type OpenApiToolDocumentation = typeof OpenApiToolDocumentationSchema.Type;
168+export type OpenApiToolProviderData = typeof OpenApiToolProviderDataSchema.Type;
137169 export type OpenApiExtractedTool = typeof OpenApiExtractedToolSchema.Type;
138170 export type OpenApiToolManifest = typeof OpenApiToolManifestSchema.Type;