Tool self-fetches were hairpinning to fam.ginnoir.com and failing, so list_calendars/create_event returned 500 and the model gave up.
This commit is contained in:
@@ -160,8 +160,20 @@ export async function runAgentChat(options: {
|
||||
|
||||
try {
|
||||
result = await executeTool(toolCall.function.name, toolCall.function.arguments);
|
||||
const parsed = JSON.parse(result) as { status?: number };
|
||||
const parsed = JSON.parse(result) as { status?: number; body?: unknown };
|
||||
status = typeof parsed.status === "number" ? parsed.status : 200;
|
||||
if (status >= 400) {
|
||||
logger.warn(
|
||||
{
|
||||
msg: "agent.chat.tool_error",
|
||||
round,
|
||||
name: toolCall.function.name,
|
||||
status,
|
||||
body: parsed.body,
|
||||
},
|
||||
"agent tool returned error",
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
status = 500;
|
||||
result = JSON.stringify({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import logger from "@/lib/logger";
|
||||
|
||||
type ApiCallResult = {
|
||||
status: number;
|
||||
body: unknown;
|
||||
@@ -5,8 +7,22 @@ type ApiCallResult = {
|
||||
|
||||
export type ToolExecutor = (name: string, argsJson: string) => Promise<string>;
|
||||
|
||||
/** Loopback base for in-process tool → /api/v1 calls. Avoids hairpinning to the public URL. */
|
||||
export function resolveInternalApiBase(request: Request): string {
|
||||
const configured = process.env.INTERNAL_API_BASE_URL?.trim();
|
||||
if (configured) return configured.replace(/\/$/, "");
|
||||
|
||||
const port = process.env.PORT?.trim() || "3000";
|
||||
const requestUrl = new URL(request.url);
|
||||
if (requestUrl.hostname === "localhost" || requestUrl.hostname === "127.0.0.1") {
|
||||
return requestUrl.origin;
|
||||
}
|
||||
|
||||
return `http://127.0.0.1:${port}`;
|
||||
}
|
||||
|
||||
export function createApiToolExecutor(request: Request): ToolExecutor {
|
||||
const origin = new URL(request.url).origin;
|
||||
const origin = resolveInternalApiBase(request);
|
||||
|
||||
return async (name: string, argsJson: string) => {
|
||||
const args = parseArgs(argsJson);
|
||||
@@ -522,14 +538,30 @@ async function callApi(
|
||||
path: string,
|
||||
body?: Record<string, unknown>,
|
||||
): Promise<ApiCallResult> {
|
||||
const response = await fetch(`${origin}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
cookie: request.headers.get("cookie") ?? "",
|
||||
},
|
||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
const url = `${origin}${path}`;
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
const cookie = request.headers.get("cookie");
|
||||
if (cookie) headers.cookie = cookie;
|
||||
const authorization = request.headers.get("authorization");
|
||||
if (authorization) headers.authorization = authorization;
|
||||
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
method,
|
||||
headers,
|
||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "fetch failed";
|
||||
logger.error(
|
||||
{ msg: "agent.tool.fetch_failed", method, url, error: message },
|
||||
"agent tool internal fetch failed",
|
||||
);
|
||||
return { status: 502, body: { error: `Internal API unreachable: ${message}`, url } };
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
let parsed: unknown = null;
|
||||
@@ -541,5 +573,12 @@ async function callApi(
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status >= 400) {
|
||||
logger.warn(
|
||||
{ msg: "agent.tool.api_error", method, url, status: response.status, body: parsed },
|
||||
"agent tool API error",
|
||||
);
|
||||
}
|
||||
|
||||
return { status: response.status, body: parsed };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user