Wroclaw
7225739527
note that I've marked Promises awaiting as a warn, because I don't want to be bothered with it for now. I also edited all files to accomodate with the new rules. I should also think find a way to type-safely import Commands directory, another time
109 lines
3 KiB
TypeScript
109 lines
3 KiB
TypeScript
import { ChatCompletionFunctions, ChatCompletionRequestMessage, ChatCompletionRequestMessageFunctionCall } from "openai";
|
|
|
|
import config from "./config";
|
|
|
|
type parameterMap = {
|
|
string: string,
|
|
number: number,
|
|
};
|
|
|
|
type nameTypeMap = {[name: string]: keyof parameterMap} | Record<string, never>;
|
|
|
|
type OpenAIFunctionRequestData<T extends nameTypeMap> = {
|
|
[name in keyof T]: T[name];
|
|
};
|
|
|
|
/**
|
|
* Represents the function that can be ran by the OpenAI model
|
|
*/
|
|
export interface OpenAIFunction<T extends nameTypeMap = nameTypeMap> {
|
|
name: string,
|
|
description?: string,
|
|
parameters: {
|
|
type: "object",
|
|
properties: T extends Record<string, never> ? Record<string, never> : {
|
|
[name in T[string]]: {
|
|
type: T[name],
|
|
description?: string,
|
|
}
|
|
},
|
|
required?: Array<keyof T>,
|
|
},
|
|
}
|
|
|
|
export abstract class OpenAIFunction<T extends nameTypeMap = nameTypeMap> {
|
|
getSettings(): ChatCompletionFunctions {
|
|
return {
|
|
name: this.name,
|
|
description: this.description,
|
|
parameters: this.parameters,
|
|
};
|
|
}
|
|
|
|
abstract execute(data: OpenAIFunctionRequestData<T>): string;
|
|
}
|
|
|
|
/*
|
|
* Manages functions for the OpenAI
|
|
**/
|
|
export default class FunctionManager {
|
|
store = new Map<string, OpenAIFunction>();
|
|
|
|
constructor() {
|
|
this.store.set("getTime", new GetTime());
|
|
}
|
|
|
|
public getFunctions(): ChatCompletionFunctions[] {
|
|
const rvalue: ChatCompletionFunctions[] = [];
|
|
for (const [, value] of this.store) {
|
|
rvalue.push(value.getSettings());
|
|
}
|
|
return rvalue;
|
|
}
|
|
|
|
public handleFunction(request: ChatCompletionRequestMessageFunctionCall): ChatCompletionRequestMessage {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
let parsedArguments: any;
|
|
|
|
const functionToRun = this.store.get(request.name ?? "");
|
|
|
|
// check if the function is registered
|
|
if (!functionToRun) {
|
|
return {
|
|
role: "system",
|
|
content: "Only use functions that were provided to you",
|
|
};
|
|
}
|
|
|
|
try {
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
parsedArguments = JSON.parse(request.arguments ?? "");
|
|
}
|
|
catch (e) {
|
|
console.error("Function arguments raw: " + request.arguments);
|
|
throw new Error(`Failed to parse the function JSON arguments when running function [${request.name}]`, {cause: e});
|
|
}
|
|
// FIXME: Verify if the parsedArguments matches the requested function argument declaration.
|
|
return {
|
|
role: "function",
|
|
name: request.name,
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
content: functionToRun.execute(parsedArguments),
|
|
};
|
|
}
|
|
}
|
|
|
|
// buildins
|
|
|
|
class GetTime extends OpenAIFunction<Record<string, never>> {
|
|
name = "getTime";
|
|
description = "Gets current date and time with a timezone attached";
|
|
parameters = {
|
|
type: "object" as const,
|
|
properties: {} as Record<string, never>,
|
|
};
|
|
|
|
execute(): string {
|
|
return `${Intl.DateTimeFormat().resolvedOptions().timeZone}): ${new Date().toLocaleString("en-US", config.calendarConfig)}`;
|
|
}
|
|
}
|