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
19} from "./openapi-tool-presentation";19} from "./openapi-tool-presentation";
20export {20export {
21 OpenApiToolInvocationError,21 OpenApiToolInvocationError,
22 createOpenApiToolFromDefinition,
22 createOpenApiToolsFromManifest,23 createOpenApiToolsFromManifest,
23 createOpenApiToolsFromSpec,24 createOpenApiToolsFromSpec,
24} from "./openapi-tools";25} from "./openapi-tools";
@@ -39,6 +40,9 @@ export { · 14 unmodified lines
39 OpenApiRequestBodyDocumentationSchema,40 OpenApiRequestBodyDocumentationSchema,
40 OpenApiResponseDocumentationSchema,41 OpenApiResponseDocumentationSchema,
41 OpenApiToolDocumentationSchema,42 OpenApiToolDocumentationSchema,
43 OpenApiToolProviderDataSchema,
44 OpenApiRefHintTableSchema,
45 OpenApiRefHintValueSchema,
42 type DiscoveryTypingPayload,46 type DiscoveryTypingPayload,
43 type OpenApiExtractedTool,47 type OpenApiExtractedTool,
44 type OpenApiHttpMethod,48 type OpenApiHttpMethod,
@@ -49,6 +53,9 @@ export { · 4 unmodified lines
49 type OpenApiParameterLocation,53 type OpenApiParameterLocation,
50 type OpenApiSpecInput,54 type OpenApiSpecInput,
51 type OpenApiToolManifest,55 type OpenApiToolManifest,
56 type OpenApiToolProviderData,
57 type OpenApiRefHintTable,
58 type OpenApiRefHintValue,
52 type OpenApiToolParameter,59 type OpenApiToolParameter,
53 type OpenApiToolRequestBody,60 type OpenApiToolRequestBody,
54} from "./openapi-types";61} from "./openapi-types";
packages/codemode-openapi/src/openapi-definitions.ts+3−1
@@ -5,6 +5,7 @@ import { · 4 unmodified lines
5 OpenApiHttpMethodSchema,5 OpenApiHttpMethodSchema,
6 OpenApiInvocationPayloadSchema,6 OpenApiInvocationPayloadSchema,
7 OpenApiToolDocumentationSchema,7 OpenApiToolDocumentationSchema,
8 OpenApiToolProviderDataSchema,
8 type OpenApiExtractedTool,9 type OpenApiExtractedTool,
9 type OpenApiToolManifest,10 type OpenApiToolManifest,
10} from "./openapi-types";11} from "./openapi-types";
@@ -250,5 +251,6 @@ export const openApiProviderDataJsonFromDefinition = ( · 239 unmodified lines
250 method: definition.method,251 method: definition.method,
251 path: definition.path,252 path: definition.path,
252 operationHash: definition.operationHash,253 operationHash: definition.operationHash,
254 invocation: definition.invocation,
253 documentation: definition.documentation,255 documentation: definition.documentation,
254 });256 } satisfies typeof OpenApiToolProviderDataSchema.Type);
packages/codemode-openapi/src/openapi-schema-refs.ts+10−4
@@ -1,4 +1,5 @@
1type JsonObject = Record<string, unknown>;1type JsonObject = Record<string, unknown>;
2type RefHintValue = string | JsonObject;
2 3
3type DiscoveryTypingLike = {4type DiscoveryTypingLike = {
4 inputSchemaJson?: string;5 inputSchemaJson?: string;
@@ -18,7 +19,7 @@ const parseJson = (value: string): unknown | null => { · 13 unmodified lines
18 19
19const resolveSchemaNode = (20const resolveSchemaNode = (
20 value: unknown,21 value: unknown,
21 refHintTable: Readonly<Record<string, string>>,22 refHintTable: Readonly<Record<string, RefHintValue>>,
22 parsedHintCache: Map<string, unknown>,23 parsedHintCache: Map<string, unknown>,
23 activeRefs: ReadonlySet<string>,24 activeRefs: ReadonlySet<string>,
24): unknown => {25): unknown => {
@@ -38,7 +39,12 @@ const resolveSchemaNode = ( · 13 unmodified lines
38 39
39 if (resolvedRefValue === undefined) {40 if (resolvedRefValue === undefined) {
40 const rawHint = refHintTable[ref];41 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;
42 parsedHintCache.set(ref, resolvedRefValue);48 parsedHintCache.set(ref, resolvedRefValue);
43 }49 }
44 50
@@ -88,7 +94,7 @@ const resolveSchemaNode = ( · 43 unmodified lines
88 94
89export const resolveSchemaJsonWithRefHints = (95export const resolveSchemaJsonWithRefHints = (
90 schemaJson: string | undefined,96 schemaJson: string | undefined,
91 refHintTable: Readonly<Record<string, string>> | undefined,97 refHintTable: Readonly<Record<string, RefHintValue>> | undefined,
92): string | null => {98): string | null => {
93 if (!schemaJson) {99 if (!schemaJson) {
94 return null;100 return null;
@@ -119,7 +125,7 @@ export const resolveSchemaJsonWithRefHints = ( · 24 unmodified lines
119 125
120export const resolveTypingSchemasWithRefHints = (126export const resolveTypingSchemasWithRefHints = (
121 typing: DiscoveryTypingLike | undefined,127 typing: DiscoveryTypingLike | undefined,
122 refHintTable: Readonly<Record<string, string>> | undefined,128 refHintTable: Readonly<Record<string, RefHintValue>> | undefined,
123): {129): {
124 inputSchemaJson: string | null;130 inputSchemaJson: string | null;
125 outputSchemaJson: string | null;131 outputSchemaJson: string | null;
packages/codemode-openapi/src/openapi-tool-presentation.ts+7−26
@@ -1,14 +1,13 @@
1import { typeSignatureFromSchemaJson } from "@executor/codemode-core";1import { typeSignatureFromSchemaJson } from "@executor/codemode-core";
2 2
3import {
4 resolveTypingSchemasWithRefHints,
5} from "./openapi-schema-refs";
6import type {3import type {
7 OpenApiExample,4 OpenApiExample,
8 OpenApiToolDocumentation,5 OpenApiToolDocumentation,
9 OpenApiToolManifest,
10} from "./openapi-types";6} from "./openapi-types";
11import type { OpenApiToolDefinition } from "./openapi-definitions";7import {
8 openApiProviderDataJsonFromDefinition,
9 type OpenApiToolDefinition,
10} from "./openapi-definitions";
12 11
13const parseJson = (value: string): unknown | undefined => {12const parseJson = (value: string): unknown | undefined => {
14 try {13 try {
@@ -104,15 +103,10 @@ export type OpenApiToolPresentation = { · 89 unmodified lines
104};103};
105 104
106export const buildOpenApiToolPresentation = (input: {105export const buildOpenApiToolPresentation = (input: {
107 manifest: OpenApiToolManifest;
108 definition: OpenApiToolDefinition;106 definition: OpenApiToolDefinition;
109}): OpenApiToolPresentation => {107}): OpenApiToolPresentation => {
110 const resolvedSchemas = resolveTypingSchemasWithRefHints(108 const inputSchemaJson = input.definition.typing?.inputSchemaJson;
111 input.definition.typing,109 const outputSchemaJson = input.definition.typing?.outputSchemaJson;
112 input.manifest.refHintTable,
113 );
114 const inputSchemaJson = resolvedSchemas.inputSchemaJson ?? undefined;
115 const outputSchemaJson = resolvedSchemas.outputSchemaJson ?? undefined;
116 const exampleInputJson = buildExampleInputJson(input.definition.documentation);110 const exampleInputJson = buildExampleInputJson(input.definition.documentation);
117 const exampleOutputJson = buildExampleOutputJson(input.definition.documentation);111 const exampleOutputJson = buildExampleOutputJson(input.definition.documentation);
118 112
@@ -123,19 +117,6 @@ export const buildOpenApiToolPresentation = (input: { · 4 unmodified lines
123 ...(outputSchemaJson ? { outputSchemaJson } : {}),117 ...(outputSchemaJson ? { outputSchemaJson } : {}),
124 ...(exampleInputJson ? { exampleInputJson } : {}),118 ...(exampleInputJson ? { exampleInputJson } : {}),
125 ...(exampleOutputJson ? { exampleOutputJson } : {}),119 ...(exampleOutputJson ? { exampleOutputJson } : {}),
126 providerDataJson: JSON.stringify({120 providerDataJson: openApiProviderDataJsonFromDefinition(input.definition),
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 }),
140 };121 };
141};122};
packages/codemode-openapi/src/openapi-tools.ts+112−73
@@ -19,11 +19,13 @@ import { · 18 unmodified lines
19 type OpenApiToolDefinition,19 type OpenApiToolDefinition,
20} from "./openapi-definitions";20} from "./openapi-definitions";
21import { buildOpenApiToolPresentation } from "./openapi-tool-presentation";21import { buildOpenApiToolPresentation } from "./openapi-tool-presentation";
22import { resolveSchemaJsonWithRefHints } from "./openapi-schema-refs";
22import {23import {
23 extractOpenApiManifest,24 extractOpenApiManifest,
24 type OpenApiExtractionError,25 type OpenApiExtractionError,
25} from "./openapi-extraction";26} from "./openapi-extraction";
26import {27import {
28 type OpenApiRefHintTable,
27 type OpenApiInvocationPayload,29 type OpenApiInvocationPayload,
28 type OpenApiSpecInput,30 type OpenApiSpecInput,
29 type OpenApiToolManifest,31 type OpenApiToolManifest,
@@ -256,13 +258,21 @@ const summarizeHttpResponseBody = (body: unknown): string | null => { · 226 unmodified lines
256 }258 }
257};259};
258 260
259const inputSchemaFromTypingJson = (inputSchemaJson: string | undefined) => {261const inputSchemaFromTypingJson = (input: {
260 if (!inputSchemaJson) {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) {
261 return unknownInputSchema;271 return unknownInputSchema;
262 }272 }
263 273
264 try {274 try {
265 return standardSchemaFromJsonSchema(JSON.parse(inputSchemaJson), {275 return standardSchemaFromJsonSchema(JSON.parse(resolvedSchemaJson), {
266 vendor: "openapi",276 vendor: "openapi",
267 fallback: unknownInputSchema,277 fallback: unknownInputSchema,
268 });278 });
@@ -396,6 +406,97 @@ const buildFetchRequest = (input: { · 127 unmodified lines
396const createToolPath = (namespace: string | undefined, definition: OpenApiToolDefinition): string =>406const createToolPath = (namespace: string | undefined, definition: OpenApiToolDefinition): string =>
397 namespace ? `${namespace}.${definition.toolId}` : definition.toolId;407 namespace ? `${namespace}.${definition.toolId}` : definition.toolId;
398 408
409export 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
420export 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
399export const createOpenApiToolsFromManifest = (500export const createOpenApiToolsFromManifest = (
400 input: CreateOpenApiToolsFromManifestInput,501 input: CreateOpenApiToolsFromManifestInput,
401): ToolMap => {502): ToolMap => {
@@ -410,77 +511,15 @@ export const createOpenApiToolsFromManifest = ( · 8 unmodified lines
410 511
411 for (const definition of definitions) {512 for (const definition of definitions) {
412 const toolPath = createToolPath(input.namespace, definition);513 const toolPath = createToolPath(input.namespace, definition);
413 const presentation = buildOpenApiToolPresentation({514 result[toolPath] = createOpenApiToolFromDefinition({
414 manifest: input.manifest,
415 definition,515 definition,
416 });516 path: toolPath,
417 517 sourceKey,
418 result[toolPath] = toTool({518 baseUrl,
419 tool: {519 defaultHeaders,
420 description: definition.description,520 credentialHeaders,
421 inputSchema: inputSchemaFromTypingJson(521 refHintTable: input.manifest.refHintTable,
422 presentation.inputSchemaJson ?? definition.typing?.inputSchemaJson,522 httpClientLayer,
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 },
484 });523 });
485 }524 }
486 525
packages/codemode-openapi/src/openapi-types.ts+32−0
@@ -63,6 +63,19 @@ export const DiscoveryTypingPayloadSchema = Schema.Struct({ · 62 unmodified lines
63 refHintKeys: Schema.optional(Schema.Array(Schema.String)),63 refHintKeys: Schema.optional(Schema.Array(Schema.String)),
64});64});
65 65
66export const OpenApiRefHintValueSchema = Schema.Union(
67 Schema.String,
68 Schema.Record({
69 key: Schema.String,
70 value: Schema.Unknown,
71 }),
72);
73
74export const OpenApiRefHintTableSchema = Schema.Record({
75 key: Schema.String,
76 value: OpenApiRefHintValueSchema,
77});
78
66export const OpenApiExampleSchema = Schema.Struct({79export const OpenApiExampleSchema = Schema.Struct({
67 valueJson: Schema.String,80 valueJson: Schema.String,
68 mediaType: Schema.optional(Schema.String),81 mediaType: Schema.optional(Schema.String),
@@ -97,6 +110,22 @@ export const OpenApiToolDocumentationSchema = Schema.Struct({ · 28 unmodified lines
97 response: Schema.optional(OpenApiResponseDocumentationSchema),110 response: Schema.optional(OpenApiResponseDocumentationSchema),
98});111});
99 112
113export 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
100export const OpenApiExtractedToolSchema = Schema.Struct({129export const OpenApiExtractedToolSchema = Schema.Struct({
101 toolId: Schema.String,130 toolId: Schema.String,
102 operationId: Schema.optional(Schema.String),131 operationId: Schema.optional(Schema.String),
@@ -129,10 +158,13 @@ export type OpenApiToolParameter = typeof OpenApiToolParameterSchema.Type; · 26 unmodified lines
129export type OpenApiToolRequestBody = typeof OpenApiToolRequestBodySchema.Type;158export type OpenApiToolRequestBody = typeof OpenApiToolRequestBodySchema.Type;
130export type OpenApiInvocationPayload = typeof OpenApiInvocationPayloadSchema.Type;159export type OpenApiInvocationPayload = typeof OpenApiInvocationPayloadSchema.Type;
131export type DiscoveryTypingPayload = typeof DiscoveryTypingPayloadSchema.Type;160export type DiscoveryTypingPayload = typeof DiscoveryTypingPayloadSchema.Type;
161export type OpenApiRefHintValue = typeof OpenApiRefHintValueSchema.Type;
162export type OpenApiRefHintTable = typeof OpenApiRefHintTableSchema.Type;
132export type OpenApiExample = typeof OpenApiExampleSchema.Type;163export type OpenApiExample = typeof OpenApiExampleSchema.Type;
133export type OpenApiParameterDocumentation = typeof OpenApiParameterDocumentationSchema.Type;164export type OpenApiParameterDocumentation = typeof OpenApiParameterDocumentationSchema.Type;
134export type OpenApiRequestBodyDocumentation = typeof OpenApiRequestBodyDocumentationSchema.Type;165export type OpenApiRequestBodyDocumentation = typeof OpenApiRequestBodyDocumentationSchema.Type;
135export type OpenApiResponseDocumentation = typeof OpenApiResponseDocumentationSchema.Type;166export type OpenApiResponseDocumentation = typeof OpenApiResponseDocumentationSchema.Type;
136export type OpenApiToolDocumentation = typeof OpenApiToolDocumentationSchema.Type;167export type OpenApiToolDocumentation = typeof OpenApiToolDocumentationSchema.Type;
168export type OpenApiToolProviderData = typeof OpenApiToolProviderDataSchema.Type;
137export type OpenApiExtractedTool = typeof OpenApiExtractedToolSchema.Type;169export type OpenApiExtractedTool = typeof OpenApiExtractedToolSchema.Type;
138export type OpenApiToolManifest = typeof OpenApiToolManifestSchema.Type;170export type OpenApiToolManifest = typeof OpenApiToolManifestSchema.Type;