Readme

Allows for automatic generation of Hono API compatible with GPTs. Endpoints' inputs and outputs need to be specified via types from which the Open API spec is generated automatically and available via /gpt/schema endpoint.

⚠️ Breaking changes introduced in v23 & 24:

  • nothingToJson doesn't take the generic TResponse anymore. The type is inferred from the endpoint definition. The endpoint definition doesn't need the requestSchema, requestDesc and responseDesc defined anymore. The descriptions are inferred from the schema description.
  • jsonToJson doesn't take the generic TRequest and TResponse anymore. Types are inferred from the endpoint definition. The endpoint definition doesn't need the requestDesc and responseDesc defined anymore. The descriptions are inferred from the schema description.

Usage example:

import { GptApi } from "https://esm.town/v/xkonti/gptApiFramework"; import { z } from "npm:zod"; /** * COMMON TYPES */ const ResponseCommandSchema = z.object({ feedback: z.string().describe("Feedback regarding submitted action"), command: z.string().describe("The command for the Mediator AI to follow strictly"), data: z.string().optional().describe("Additional data related to the given command"), }).describe("Contains feedback and further instructions to follow"); /** * INITIALIZE API */ const api = new GptApi({ url: "https://xkonti-planoverseerai.web.val.run", title: "Overseer AI API", description: "The API for interacting with the Overseer AI", version: "1.0.0", policyGetter: async () => { const { markdownToPrettyPage } = await import("https://esm.town/v/xkonti/markdownToHtmlPage?v=5"); return await markdownToPrettyPage("# Privacy Policy\n\n## Section 1..."); }, }); /** * REQUIREMENTS GATHERING ENDPOINTS */ api.nothingToJson({ verb: "POST", path: "/newproblem", operationId: "new-problem", desc: "Endpoint for informing Overseer AI about a new problem presented by the User", responseSchema: ResponseCommandSchema, responseDesc: "Instruction on how to proceed with the new problem", }, async (ctx) => { return { feedback: "User input downloaded. Problem analysis is required.", command: await getPrompt("analyze-problem"), data: "", }; }); export default api.serve();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import type { Context } from "npm:hono@3";
import { Hono } from "npm:hono@3";
import { z } from "npm:zod";
import { zodToJsonSchema } from "npm:zod-to-json-schema";
export type HttpVerb = "get" | "post" | "put" | "delete" | "patch";
/**
* Base interface for defining an endpoint.
* This does not include request or response schemas.
*/
export interface EndpointDefBase {
verb: HttpVerb;
path: string;
desc: string;
operationId: string;
}
/**
* Used o define an endpoint with request and response schemas
* along with descriptions for each.
*/
export interface EndpointDefinition extends EndpointDefBase {
requestSchema: z.Schema | null;
requestDesc: string | null;
responseSchema: z.Schema | null;
responseDesc: string | null;
}
/**
* Used to define an input-only endpoint with a request schema,
* but no response schema.
*/
export interface InEndpointDefinition<TRequestSchema extends z.Schema> extends EndpointDefBase {
requestSchema: TRequestSchema;
}
/**
* Used to define an output-only endpoint with a response schema,
* but no request schema.
*/
export interface OutEndpointDefinition<TResponseSchema extends z.Schema> extends EndpointDefBase {
responseSchema: TResponseSchema;
}
/**
* Used to define an input/output endpoint with both a request and
* response schema.
*/
export interface InOutEndpointDefinition<TRequestSchema extends z.Schema, TResponseSchema extends z.Schema>
extends EndpointDefBase {
requestSchema: TRequestSchema;
responseSchema: TResponseSchema;
}
/**
* The API info object that is used to describe the API to the GPT.
*/
export interface ApiInfo {
/**
* The URL of the API. This should match the URL of your Val.
*/
url: string;
/**
* The name of the API. It gives the GPT an idea about the purpose of the API.
*/
title: string;
/**
* A short description of the API. It gives the GPT an idea about the purpose of the API.
*/
description: string;
/**
* The version of the API. Required by the OpenAPI spec.
*/
version: string;
/**
* An optional function that returns the policy to be used available at `/privacypolicy`.
*/
policyGetter?: (() => string) | (() => Promise<string>);
}
/**
* Returns the JSON schema for a Zod schema.
* @param schema The Zod schema to convert.
* @returns The generated JSON schema.
*/
function getSchemaDesc(schema: z.Schema | null) {
if (!schema) return null;
return zodToJsonSchema(schema, {
name: "schema",
target: "openApi3",
}).definitions?.schema ?? null;
}
/**
* Describes the schema of an endpoint.
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
9
xkonti avatar

Version 23 changelog: ⚠️ Breaking changes:

  • nothingToJson doesn't take the generic TResponse anymore. The type is inferred from the endpoint definition. The endpoint definition doesn't need the requestSchema/requestDesc defined anymore.
  • jsonToJson doesn't take the generic TRequest and TResponse anymore. Types are inferred from the endpoint definition.

Other changes:

  • Added jsonToNothing that accepts an input, but can respond only with an HTTP code.
xkonti avatar

Version 24 changelog

⚠️ Breaking change: jsonToNothing, nothingToJson and jsonToJson don't accept the request/response descriptions. The descriptions are automatically extracted from request/response schemas.

xkonti avatar

Version 25 changelog

The dto in request handlers has been renamed to reqBody.

xkonti avatar

Version 26 changelog

Added config option policyGetter which takes a function that returns the privacy policy. This will automatically create an API endpoint /policy that will return it on demand. The policy getter is a function so that the policy can be imported on-demand.

xkonti avatar

Version 27 changelog

The policyGetter can be an async function now. This supports passing it functions like this:

... , policyGetter: async () => { const { policy } = await import("https://esm.town/v/user/val"); return policy; },
xkonti avatar

Version 29 changelog

If the provided privacy policy ends with </html>, the /privacypolicy endpoint will return it as HTML content instead of plaintext.

xkonti avatar

Version 32 changelog

  • Merged schema builder into the Val
  • Improved TypeScript type definitions
  • Http verbs are now lowercase
xkonti avatar

Version 36 changelog

  • Added a default GET for / path that deisplays URLs of privacy policy and OpenAPI spec
xkonti avatar

Version 67 changelog

  • Improved documentation
v67
May 29, 2024