acp-sdk
Version:
Agent Communication Protocol SDK
221 lines (219 loc) • 5.96 kB
JavaScript
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