55
56/**
57 * The API info object that is used to describe the API to the GPT.
58 */
59export interface ApiInfo {
60 /**
61 * The URL of the API. This should match the URL of your Val.
62 */
63 url: string;
64
65 /**
66 * The name of the API. It gives the GPT an idea about the purpose of the API.
67 */
68 title: string;
69
70 /**
71 * A short description of the API. It gives the GPT an idea about the purpose of the API.
72 */
73 description: string;
74
75 /**
76 * The version of the API. Required by the OpenAPI spec.
77 */
78 version: string;
93 return zodToJsonSchema(schema, {
94 name: "schema",
95 target: "openApi3",
96 }).definitions?.schema ?? null;
97}
125
126/**
127 * Describes the paths of an OpenAPI spec.
128 */
129type Paths = {
136
137/**
138 * Assembles the paths of an OpenAPI spec from endpoint definitions.
139 * @param endpoints The endpoint definitions to use.
140 * @returns The paths of the OpenAPI spec.
141 */
142function getPathsDesc(endpoints: EndpointDefinition[]): Paths {
178
179/**
180 * Generates an OpenAPI spec for the defined API.
181 * @param url The URL of the API.
182 * @param title The title of the API.
183 * @param description The description of the API.
184 * @param version The version of the API.
185 * @param endpoints The endpoint definitions to use.
186 * @returns The OpenAPI spec.
187 */
188function getOpenApiSpec(
189 url: string,
190 title: string,
194) {
195 return {
196 openapi: "3.1.0",
197 info: {
198 title,
210
211/**
212 * A class representing a GPT API. It provides methods for registering
213 * endpoints and serving the API.
214 */
215export class GptApi {
216 private app = new Hono();
217 private info: ApiInfo;
218 private endpoints: EndpointDefinition[] = [];
219
220 /**
221 * Creates a new GptApi instance.
222 * @param info Configuration for the API.
223 */
224 constructor(info: ApiInfo) {
225 this.info = info;
226 this.app.get("/gpt/schema", (ctx) => {
227 const spec = getOpenApiSpec(
228 this.info.url,
229 this.info.title,
250
251 this.app.get("/", (ctx) => {
252 return ctx.text(`OpenAPI spec: ${this.info.url}/gpt/schema\nPrivacy policy: ${this.info.url}/privacypolicy\n`);
253 });
254 }
262 jsonToNothing<TRequestSchema extends z.Schema>(
263 endpointDef: InEndpointDefinition<TRequestSchema>,
264 handler: (ctx: Context, reqBody: z.infer<TRequestSchema>, apiKey: string | null) => Promise<number>,
265 ) {
266 const handlerWrapper = async (ctx: Context) => {
271 // TODO: Handle invalid data format
272
273 const apiKey = this.extractApiKey(ctx);
274 const statusCode = await handler(ctx, parsedRequestData, apiKey);
275 return ctx.body(null, statusCode as StatusCode);
276 };
294 nothingToJson<TResponseSchema extends z.Schema>(
295 endpointDef: OutEndpointDefinition<TResponseSchema>,
296 handler: (ctx: Context, apiKey: string | null) => Promise<z.infer<TResponseSchema>>,
297 ) {
298 const handlerWrapper = async (ctx: Context) => {
299 const apiKey = this.extractApiKey(ctx);
300 const response = await handler(ctx, apiKey);
301
302 // Validate response data
327 ctx: Context,
328 reqBody: z.infer<TRequestSchema>,
329 apiKey: string | null,
330 ) => Promise<z.infer<TResponseSchema>>,
331 ) {
338 // TODO: Handle invalid data format
339
340 const apiKey = this.extractApiKey(ctx);
341 const response = await handler(ctx, parsedRequestData, apiKey);
342
343 // Validate response data
388
389 /**
390 * Returns a function that can be used to serve the API.
391 *
392 * @example ValTown usage:
393 * ```ts
394 * export default gptApi.serve();
395 * ```
396 *
397 * @example Deno usage:
398 * ```
399 * const { fetch } = gptApi.serve();
400 * export default { fetch };
401 * ```
406
407 /**
408 * Extracts API key from Bearer Auth header.
409 */
410 private extractApiKey(ctx: Context): string | null {
411 const authHeader = ctx.req.header("Authorization");
412 if (!authHeader || !authHeader.startsWith("Bearer ")) {
414 }
415
416 const apiKey = authHeader.split(" ")[1];
417 return apiKey;
418 }
419}