Q97: external calls that report failure are failed calls
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a9620eaa5a
commit
989127bed3
5 files changed
+39
-11
No files matched your search
@@ -8,6 +8,7 @@ const FETCH_TIMEOUT_MS = 30_000;
|
||||
const MAX_REDIRECTS = 3;
|
||||
const MAX_RESPONSE_BYTES = 2_000_000;
|
||||
const SEARCH_RESULT_LIMIT = 8;
|
||||
const STATUS_EXCERPT_CHARS = 300;
|
||||
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AgentCompany/1.0";
|
||||
|
||||
/** Subset of the fetch contract the tools rely on; injectable so tests stay off the network. */
|
||||
@@ -126,6 +127,9 @@ async function guardedFetch(rawUrl: string, fetcher: WebFetcher): Promise<Guarde
|
||||
continue;
|
||||
}
|
||||
const text = await readBodyCapped(response);
|
||||
if (response.status < 200 || response.status >= 300) {
|
||||
throw new Error(`${url.toString()} answered with status ${response.status}: ${statusExcerpt(response.headers.get("content-type") ?? "", text)}`);
|
||||
}
|
||||
return {
|
||||
status: response.status,
|
||||
contentType: response.headers.get("content-type") ?? "",
|
||||
@@ -136,6 +140,12 @@ async function guardedFetch(rawUrl: string, fetcher: WebFetcher): Promise<Guarde
|
||||
throw new Error(`Too many redirects (more than ${MAX_REDIRECTS}).`);
|
||||
}
|
||||
|
||||
/** The start of an error page, as one line, so the agent sees why without the whole page. */
|
||||
function statusExcerpt(contentType: string, text: string): string {
|
||||
const plain = (looksLikeHtml(contentType, text) ? htmlToText(text) : text).replace(/\s+/g, " ").trim();
|
||||
return plain ? plain.slice(0, STATUS_EXCERPT_CHARS) : "(empty body)";
|
||||
}
|
||||
|
||||
function parseHttpUrl(rawUrl: string): URL {
|
||||
let url: URL;
|
||||
try {
|
||||
|
||||
@@ -26,10 +26,8 @@ export interface ComposioConnectedAccount {
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface ComposioToolExecutionResult {
|
||||
data: Record<string, unknown>;
|
||||
error: string | null;
|
||||
}
|
||||
/** Composio answers 200 for business failures too; only `successful` tells them apart. */
|
||||
export type ComposioToolExecutionResult = { successful: true; data: unknown } | { successful: false; error: string };
|
||||
|
||||
const MAX_RESPONSE_BYTES = 2_000_000;
|
||||
|
||||
@@ -142,10 +140,9 @@ export class ComposioClient {
|
||||
};
|
||||
if (input.connectedAccountId) body.connected_account_id = input.connectedAccountId;
|
||||
const response = await this.request("POST", url, body);
|
||||
return {
|
||||
data: typeof response === "object" && response !== null ? response as Record<string, unknown> : {},
|
||||
error: null,
|
||||
};
|
||||
if (typeof response.successful !== "boolean") throw new Error(`Composio returned no execution status for ${input.toolName}.`);
|
||||
if (response.successful) return { successful: true, data: response.data };
|
||||
return { successful: false, error: typeof response.error === "string" && response.error.trim() ? response.error.trim() : "Composio gave no reason." };
|
||||
}
|
||||
|
||||
private async request(
|
||||
|
||||
@@ -114,7 +114,10 @@ async function runAction(db: DatabaseSync, apiKey: string, orgId: string, agentI
|
||||
const input = typeof args.arguments === "object" && args.arguments !== null && !Array.isArray(args.arguments) ? args.arguments as Record<string, unknown> : null;
|
||||
if (!input) return "ERROR: arguments must be an object matching the action's input.";
|
||||
const result = await new ComposioClient(apiKey).executeTool({ toolName: action.name, arguments: input, userId: getComposioUserId(db, orgId), connectedAccountId: account.id });
|
||||
if (result.error) return `ERROR: ${result.error}`;
|
||||
if (!result.successful) {
|
||||
console.warn(`[connectors] ${action.name} failed: ${result.error.replace(/\s+/g, " ")}`);
|
||||
return `ERROR: ${action.name} failed: ${result.error}`;
|
||||
}
|
||||
return JSON.stringify(result.data);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user