import { getLogger } from "@logtape/logtape" import { ChatSessionModelFunctions, defineChatSessionFunction, GbnfJsonSchema, } from "npm:node-llama-cpp" type RemoteFunction = { name: string description: string parameters?: Readonly [key: string]: unknown } const remoteLogger = getLogger(["remote-functions"]) export async function fetchRemoteFunctions( host = Deno.env.get("REMOTE_FUNCTIONS_HOST") || "http://localhost:1337/", ): Promise { const url = new URL(host) url.pathname = url.pathname.replace(/\/$/, "") + "/client/functions.json" const response = await fetch(url.toString(), { headers: { "Content-Type": "application/json", Authorization: `Bearer ${Deno.env.get("REMOTE_FUNCTIONS_TOKEN") || ""}`, }, }) if (!response.ok) { throw new Error(`Failed to call remote function: ${response.statusText}`) } const functions = await response.json() as RemoteFunction[] if (!Array.isArray(functions)) { throw new Error("Invalid response format, expected an array of functions") } return functions.reduce((acc, func) => { if (!func.name || !func.description) { throw new Error( `Function ${JSON.stringify(func)} is missing required fields`, ) } acc[func.name] = defineChatSessionFunction({ description: func.description, params: func.parameters, handler: async (args) => { try { const url = new URL(host) url.pathname = url.pathname.replace(/\/$/, "") + "/client/remote-function/" + func.name const requestBody = { arguments: args, } const requestHeaders = { "Content-Type": "application/json", Authorization: `Bearer ${ Deno.env.get("REMOTE_FUNCTIONS_TOKEN") || "" }`, } remoteLogger.info("Calling backend:", { url: url.toString(), headers: requestHeaders, body: requestBody, }) const response = await fetch(url.toString(), { method: "POST", headers: requestHeaders, body: JSON.stringify(requestBody), }) remoteLogger.info("Response status:", { status: response.status }) if (!response.ok) { return ( `Failed to call remote function: ${response.statusText} ${await response .text()}` ) } const responseText = await response.text() remoteLogger.info("Response text:", { responseText }) return responseText } catch (error) { console.error( `Error calling remote function ${func.name}: \n`, error, ) throw error } }, }) return acc }, {} as Record>) }