UNPKG

acp-sdk

Version:

Agent Communication Protocol SDK

221 lines (219 loc) 5.96 kB
import { v4 } from 'uuid'; import { ErrorModel } from '../models/errors.js'; import { Event } from '../models/models.js'; import { PingResponse, AgentsListResponse, AgentsReadResponse, RunCreateResponse, RunReadResponse, RunEventsListResponse, RunResumeResponse } from '../models/schemas.js'; import { BaseError, FetchError, HTTPError, ACPError } from './errors.js'; import { createEventSource } from './sse.js'; import { inputToMessages } from './utils.js'; import { getTracer } from '../instrumentation.js'; class Client { #baseUrl; #fetch; #sessionId; constructor(init) { this.#fetch = init?.fetch ?? globalThis.fetch.bind(globalThis); this.#baseUrl = normalizeBaseUrl(init?.baseUrl ?? ""); this.#sessionId = init?.sessionId; } get sessionId() { return this.#sessionId; } async withSession(cb, sessionId = v4()) { return await getTracer().startActiveSpan( "session", { attributes: { "acp.session": sessionId } }, async (span) => { try { const client = new Client({ fetch: this.#fetch, baseUrl: this.#baseUrl, sessionId }); return await cb(client); } finally { span.end(); } } ); } async #fetcher(url, options) { let response; try { response = await this.#fetch(this.#baseUrl + url, options); await this.#handleErrorResponse(response); return await response.json(); } catch (err) { if (err instanceof BaseError || err instanceof Error && err.name === "AbortError") { throw err; } throw new FetchError(err.message ?? "fetch failed", response, { cause: err }); } } async #fetchEventSource(url, options) { let eventSource; try { eventSource = await createEventSource({ url: this.#baseUrl + url, fetch: this.#fetch, options }); } catch (err) { throw new FetchError( err.message ?? "fetch failed", void 0, { cause: err } ); } await this.#handleErrorResponse(eventSource.response); return eventSource; } async #handleErrorResponse(response) { if (response.ok) return; const text = await response.text(); let data; try { data = JSON.parse(text); } catch { throw new HTTPError(response, text); } const result = ErrorModel.safeParse(data); if (result.success) { throw new ACPError(result.data); } throw new HTTPError(response, data); } async *#processEventSource(eventSource) { for await (const message of eventSource.consume()) { const event = Event.parse(JSON.parse(message.data)); if (event.type === "error") { throw new ACPError(event.error); } yield event; } } async ping() { const data = await this.#fetcher("/ping", { method: "GET" }); PingResponse.parse(data); } async agents() { const data = await this.#fetcher("/agents", { method: "GET" }); return AgentsListResponse.parse(data).agents; } async agent(name) { const data = await this.#fetcher(`/agents/${name}`, { method: "GET" }); return AgentsReadResponse.parse(data); } async runSync(agentName, input) { const data = await this.#fetcher( "/runs", jsonPost({ agent_name: agentName, input: inputToMessages(input), mode: "sync", session_id: this.#sessionId }) ); return RunCreateResponse.parse(data); } async runAsync(agentName, input) { const data = await this.#fetcher( "/runs", jsonPost({ agent_name: agentName, input: inputToMessages(input), mode: "async", session_id: this.#sessionId }) ); return RunCreateResponse.parse(data); } async *runStream(agentName, input, signal) { const eventSource = await this.#fetchEventSource( "/runs", jsonPost( { agent_name: agentName, input: inputToMessages(input), mode: "stream", session_id: this.#sessionId }, { signal } ) ); for await (const event of this.#processEventSource(eventSource)) { yield event; } } async runStatus(runId) { const data = await this.#fetcher(`/runs/${runId}`, { method: "GET" }); return RunReadResponse.parse(data); } async runEvents(runId) { const data = await this.#fetcher(`/runs/${runId}/events`, { method: "GET" }); return RunEventsListResponse.parse(data).events; } async runCancel(runId) { const data = await this.#fetcher(`/runs/${runId}/cancel`, { method: "POST" }); return RunReadResponse.parse(data); } async runResumeSync(runId, awaitResume) { const data = await this.#fetcher( `/runs/${runId}`, jsonPost({ await_resume: awaitResume, mode: "sync" }) ); return RunResumeResponse.parse(data); } async runResumeAsync(runId, awaitResume) { const data = await this.#fetcher( `/runs/${runId}`, jsonPost({ await_resume: awaitResume, mode: "async" }) ); return RunResumeResponse.parse(data); } async *runResumeStream(runId, awaitResume, signal) { const eventSource = await this.#fetchEventSource( `/runs/${runId}`, jsonPost( { await_resume: awaitResume, mode: "stream" }, { signal } ) ); for await (const event of this.#processEventSource(eventSource)) { yield event; } } } const normalizeBaseUrl = (url) => { if (url.endsWith("/")) { return url.slice(0, -1); } return url; }; const jsonPost = (json, init) => { return { ...init, method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(json) }; }; export { Client }; //# sourceMappingURL=client.js.map //# sourceMappingURL=client.js.map