INTEGRITY Dokumentace

Použití služby OpenAPI s Code Mode

Použijte OpenApiConnector ke zpřístupnění služby OpenAPI uvnitř trvalého runtime Code Mode. Konektor odvodí jednu metodu sandboxu pro každou operaci v dokumentu OpenAPI.

Model dokáže zjišťovat metody pomocí codemode.search() a vyžádat si zaměřené typy vstupu pomocí codemode.describe(). Kompletní dokument OpenAPI nemusí vstupovat do kontextu modelu.

Tato stránka popisuje Agenta, který využívá službu OpenAPI. Pokud chcete službu OpenAPI publikovat externím MCP klientům prostřednictvím search a execute, přečtěte si Sestavte server MCP typu vyhledej a spusť.

Předpoklady

Potřebujete projekt s trvalý runtime Code Mode nakonfigurováno. Nastavení runtime poskytuje vazbu Worker Loader a CodemodeRuntime export použitý v tomto průvodci.

Vytvořte konektor OpenAPI

  1. Přidejte dokument OpenAPI do svého projektu. Každé operaci přiřaďte jedinečné operationId aby vytvářel stabilní název metody sandboxu:

    src/orders-openapi.js
    export const ordersOpenApiSpec = {
    	openapi: "3.1.0",
    	info: { title: "Orders API", version: "1.0.0" },
    	paths: {
    		"/orders/{orderId}": {
    			get: {
    				operationId: "get_order",
    				summary: "Get an order by ID.",
    				parameters: [
    					{
    						name: "orderId",
    						in: "path",
    						required: true,
    						schema: { type: "string" },
    					},
    				],
    			},
    		},
    		"/orders": {
    			post: {
    				operationId: "create_order",
    				summary: "Create an order.",
    				requestBody: {
    					required: true,
    					content: {
    						"application/json": {
    							schema: {
    								type: "object",
    								properties: {
    									productId: { type: "string" },
    									quantity: { type: "integer" },
    								},
    								required: ["productId", "quantity"],
    							},
    						},
    					},
    				},
    			},
    		},
    	},
    };
    src/orders-openapi.ts
    export const ordersOpenApiSpec = {
    	openapi: "3.1.0",
    	info: { title: "Orders API", version: "1.0.0" },
    	paths: {
    		"/orders/{orderId}": {
    			get: {
    				operationId: "get_order",
    				summary: "Get an order by ID.",
    				parameters: [
    					{
    						name: "orderId",
    						in: "path",
    						required: true,
    						schema: { type: "string" },
    					},
    				],
    			},
    		},
    		"/orders": {
    			post: {
    				operationId: "create_order",
    				summary: "Create an order.",
    				requestBody: {
    					required: true,
    					content: {
    						"application/json": {
    							schema: {
    								type: "object",
    								properties: {
    									productId: { type: "string" },
    									quantity: { type: "integer" },
    								},
    								required: ["productId", "quantity"],
    							},
    						},
    					},
    				},
    			},
    		},
    	},
    } as const;
  2. Vytvořte konektor. Implementujte spec() k vrácení dokumentu a request() k odesílání ověřených požadavků na straně hostitele:

    src/orders-connector.js
    import { OpenApiConnector } from "@cloudflare/codemode";
    import { ordersOpenApiSpec } from "./orders-openapi";
    
    const API_ORIGIN = "https://api.example.com";
    
    export class OrdersConnector extends OpenApiConnector {
    	name() {
    		return "orders";
    	}
    
    	instructions() {
    		return "Use for reading and creating orders.";
    	}
    
    	spec() {
    		return ordersOpenApiSpec;
    	}
    
    	async request(options) {
    		if (!options.path.startsWith("/")) {
    			throw new Error("Orders API path must start with a slash");
    		}
    
    		const url = new URL(options.path, API_ORIGIN);
    		for (const [key, value] of Object.entries(options.params ?? {})) {
    			if (value !== undefined) {
    				url.searchParams.set(key, String(value));
    			}
    		}
    
    		const response = await fetch(url, {
    			method: options.method ?? "GET",
    			headers: {
    				...(options.body !== undefined
    					? { "Content-Type": "application/json" }
    					: {}),
    				...options.headers,
    				Authorization: `Bearer ${this.env.ORDERS_API_TOKEN}`,
    			},
    			body:
    				options.body === undefined ? undefined : JSON.stringify(options.body),
    		});
    
    		if (!response.ok) {
    			throw new Error(`Orders API request failed: ${response.status}`);
    		}
    		if (response.status === 204) return null;
    		return response.json();
    	}
    
    	tool(name, tool) {
    		if (name === "create_order") {
    			return { ...tool, requiresApproval: true };
    		}
    		return tool;
    	}
    }
    src/orders-connector.ts
    import {
    	OpenApiConnector,
    	type ConnectorTool,
    	type OpenApiRequestOptions,
    } from "@cloudflare/codemode";
    import { ordersOpenApiSpec } from "./orders-openapi";
    
    const API_ORIGIN = "https://api.example.com";
    
    export class OrdersConnector extends OpenApiConnector<Env> {
    	override name() {
    		return "orders";
    	}
    
    	protected override instructions() {
    		return "Use for reading and creating orders.";
    	}
    
    	protected override spec() {
    		return ordersOpenApiSpec;
    	}
    
    	protected override async request(options: OpenApiRequestOptions) {
    		if (!options.path.startsWith("/")) {
    			throw new Error("Orders API path must start with a slash");
    		}
    
    		const url = new URL(options.path, API_ORIGIN);
    		for (const [key, value] of Object.entries(options.params ?? {})) {
    			if (value !== undefined) {
    				url.searchParams.set(key, String(value));
    			}
    		}
    
    		const response = await fetch(url, {
    			method: options.method ?? "GET",
    			headers: {
    				...(options.body !== undefined
    					? { "Content-Type": "application/json" }
    					: {}),
    				...options.headers,
    				Authorization: `Bearer ${this.env.ORDERS_API_TOKEN}`,
    			},
    			body:
    				options.body === undefined
    					? undefined
    					: JSON.stringify(options.body),
    		});
    
    		if (!response.ok) {
    			throw new Error(`Orders API request failed: ${response.status}`);
    		}
    		if (response.status === 204) return null;
    		return response.json();
    	}
    
    	protected override tool(name: string, tool: ConnectorTool): ConnectorTool {
    		if (name === "create_order") {
    			return { ...tool, requiresApproval: true };
    		}
    		return tool;
    	}
    }

    Přihlašovací údaje zůstávají v hostitelském Workeru. Kód napsaný modelem dostává metody konektoru a jejich výsledky, nikoli ORDERS_API_TOKEN.

    tool() hook dekoruje odvozené operace. Tento příklad vyžaduje schválení před create_order se provede. Pomocí hooku můžete také přidat chování pro přehrání nebo vrácení zpět.

  3. Importujte konektor a přidejte ho do runtime:

    src/server.js
    import { AIChatAgent } from "@cloudflare/ai-chat";
    import {
    	createCodemodeRuntime,
    	DynamicWorkerExecutor,
    } from "@cloudflare/codemode";
    import { OrdersConnector } from "./orders-connector";
    
    export class Chat extends AIChatAgent {
    	#runtime() {
    		return createCodemodeRuntime({
    			ctx: this.ctx,
    			executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }),
    			connectors: [new OrdersConnector(this.ctx, this.env)],
    		});
    	}
    
    	async onChatMessage() {
    		const tools = { codemode: this.#runtime().tool() };
    		// Pass tools to your model call.
    	}
    }
    src/server.ts
    import { AIChatAgent } from "@cloudflare/ai-chat";
    import {
    	createCodemodeRuntime,
    	DynamicWorkerExecutor,
    } from "@cloudflare/codemode";
    import { OrdersConnector } from "./orders-connector";
    
    export class Chat extends AIChatAgent<Env> {
    	#runtime() {
    		return createCodemodeRuntime({
    			ctx: this.ctx,
    			executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }),
    			connectors: [new OrdersConnector(this.ctx, this.env)],
    		});
    	}
    
    	async onChatMessage() {
    		const tools = { codemode: this.#runtime().tool() };
    		// Pass tools to your model call.
    	}
    }
  4. Umožněte modelu objevit operace a volat vygenerované metody konektoru:

    async () => {
    	const matches = await codemode.search("get an order by ID");
    	const docs = await codemode.describe(matches.results[0].path);
    
    	const order = await orders.get_order({ orderId: "order-123" });
    	return { docs, order };
    };

Chování odvozené metody

OpenApiConnector používá sanitizovaný operationId jako název metody. Pokud operace nemá žádný operationId, odvodí název z metody HTTP a cesty. Definujte jedinečná ID operací, aby názvy metod zůstaly stabilní a nedocházelo ke kolizím.

Každá vygenerovaná metoda přijímá jeden objekt:

Konektor dosadí parametry cesty a předá normalizovaný { path, method, params, body, headers } objekt na request().

Aktuální konektor odvozuje vstupní typy, ale typy odpovědí neodvozuje ze schémat odpovědí OpenAPI. Vygenerované metody proto vracejí unknown pokud vaše aplikace neposkytuje konkrétnější deklarace prostřednictvím jiné implementace konektoru.

Úniková cesta požadavku

Každý konektor OpenAPI navíc zpřístupňuje nízkoúrovňové request() metodu sandboxu. Použijte ji, když dokument OpenAPI nepopisuje operaci, kterou model potřebuje:

const result = await orders.request({
	path: "/orders",
	method: "GET",
	params: { status: "processing" },
});

Pokud jsou k dispozici, dávejte přednost metodám odvozených operací. Poskytují objevitelné popisy a generované vstupní typy.

exposeSpec() vrací false ve výchozím nastavení. Přepište ho tak, aby vracel true pouze tehdy, když kód napsaný modelem potřebuje přístup k surovému dokumentu OpenAPI. Velké dokumenty mohou vytvořit velké výsledky a trvalé záznamy v logu.