|
import { getLogger } from "@logtape/logtape"
|
|
import {
|
|
ChatSessionModelFunctions,
|
|
defineChatSessionFunction,
|
|
GbnfJsonSchema,
|
|
} from "npm:node-llama-cpp"
|
|
|
|
type RemoteFunction = {
|
|
name: string
|
|
description: string
|
|
parameters?: Readonly<GbnfJsonSchema>
|
|
[key: string]: unknown
|
|
}
|
|
|
|
const remoteLogger = getLogger(["remote-functions"])
|
|
|
|
export async function fetchRemoteFunctions(
|
|
host = Deno.env.get("REMOTE_FUNCTIONS_HOST") ||
|
|
"http://localhost:1337/",
|
|
): Promise<ChatSessionModelFunctions> {
|
|
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<string, ReturnType<typeof defineChatSessionFunction>>)
|
|
}
|