Skip to content

Commit

Permalink
File chatbot initial concept
Browse files Browse the repository at this point in the history
  • Loading branch information
Krzysztof Filipów committed Apr 9, 2024
1 parent 3f485bd commit 48684fd
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 205 deletions.
46 changes: 46 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",
"formidable": "^3.5.1",
"officeparser": "^4.0.8",
"openai": "^4.29.2",
"zod": "^3.22.4"
Expand All @@ -31,6 +32,7 @@
"@tsconfig/strictest": "^2.0.3",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/formidable": "^3.4.5",
"@types/node": "^20.11.30",
"nodemon": "^3.1.0",
"rimraf": "^5.0.5",
Expand Down
13 changes: 0 additions & 13 deletions src/calculations/loanPrincipal.ts

This file was deleted.

12 changes: 0 additions & 12 deletions src/calculations/loanTime.ts

This file was deleted.

4 changes: 0 additions & 4 deletions src/calculations/monthlyInterestRate.ts

This file was deleted.

48 changes: 2 additions & 46 deletions src/chat/get-init.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,19 @@
import { z } from "zod";
import { makeGetEndpoint } from "../middleware/validation/makeGetEndpoint.js";
import { fileReader, parseFileReaderResponse } from "../util/fileReader.js";
import { OPENAI_MODEL, PROMPT_FILE_NAME, RESPONSE_FORMAT, openai } from "./index.js";

//TODO: Rework type inference of fileReader
export const init = makeGetEndpoint(z.any(), async (_request, response) => {
let messages: string[] = []
const promptFile = await fileReader(PROMPT_FILE_NAME);
let jsonPrompt;

if(RESPONSE_FORMAT.type === "json_object"){

jsonPrompt = await fileReader('json-format-prompt.docx');
if(parseFileReaderResponse(jsonPrompt)){
messages.push(jsonPrompt.content);
}
else{
return response.status(500).send({
status: 500,
message: jsonPrompt.error
})
}
}

if(parseFileReaderResponse(promptFile)){
messages.push(promptFile.content);
}
else{
return response.status(500).send({
status: 500,
message: promptFile.error
})
}


const completion = await openai.chat.completions.create({
messages: messages.map(message => ({role: "system", content: message})),
model: OPENAI_MODEL,
response_format: RESPONSE_FORMAT
});
console.log(completion.choices[0]?.message);
return response
.status(200)
.send([
{
role: "system",
content: promptFile.content
},
{
role: "system",
content: jsonPrompt?.content !== undefined ? jsonPrompt.content : ""
},
{
role: 'system',
content: "Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous. Do not call functions until all arguments are provided by the user. Include all arguments in the response."
content: "Ask the user to provide the content of the file they want to chat about. Talk only about the content of the provided file."
},
{
role: "assistant",
content: "Hallo! Als virtueller Assistent beantworte ich gerne Ihre Fragen rund um die Baufinanzierung und helfe Ihnen bei der Berechnung der Kreditsumme anhand des Zeitpunkts und der Höhe der monatlichen Rate bzw. der Kreditlaufzeit anhand der Höhe und der Höhe der monatlichen Rate."
content: "Hallo! Bitte laden Sie die Datei hoch, über die Sie chatten möchten."
}
]);
});
2 changes: 2 additions & 0 deletions src/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import OpenAI from "openai";
import { ChatCompletionCreateParams, ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.js";
import { init } from "./get-init.js";
import { newMessage } from "./post-new-message.js";
import fileUpload from "./upload-file.js";

/**
* Change the prompt file, the model or the response format here
Expand All @@ -21,6 +22,7 @@ chatRouter.post('/newMessage', newMessage);
chatRouter.get("/test")

chatRouter.get('/init', init);
chatRouter.post('/fileUpload', fileUpload)


export {
Expand Down
52 changes: 5 additions & 47 deletions src/chat/post-new-message.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { z } from "zod";
import { makePostEndpoint } from "../middleware/validation/makePostEndpoint.js";
import { OPENAI_MODEL, RESPONSE_FORMAT, openai } from "./index.js";
import tools from "./tools.js";
import {ChatCompletionMessageParam, ChatCompletionTool, ChatCompletionToolMessageParam } from "openai/resources/index.js";
import { ChatCompletionMessageParam } from "openai/resources/index.js";

const ChatCompletionRole = z.union([z.literal('user'), z.literal('system'), z.literal('assistant')]);
export type ChatCompletionRole = z.infer<typeof ChatCompletionRole>;
Expand All @@ -22,58 +21,17 @@ const makeApiRequest = async (messages: ChatCompletionMessageParam[]) => {
messages,
model: OPENAI_MODEL,
response_format: RESPONSE_FORMAT,
tools: Object.values(tools).map(val => val.definition),
});
}

//check if all required arguments provided by openai api are present and are numbers
const validateArguments = (args: any, definition?: ChatCompletionTool) => {
return undefined === Object.keys(definition?.function?.parameters?.['properties'] as any)
?.map(property => args[property])
?.find(value => value == null || typeof value !== 'number');
}

const processMessages = async (messages: ChatCompletionMessageParam[], response: any) => {
const completion = await makeApiRequest(messages);
const chatResponse = completion.choices[0];
if (chatResponse?.finish_reason === 'tool_calls') {
const toolCalls: ChatCompletionToolMessageParam[] = chatResponse.message.tool_calls?.map(call => {
let content: string;
try {
const choosenFunction = tools[call.function.name]?.fn;
if (!choosenFunction) {
content = 'Failed to calculate - unknown function'
} else {
const args = JSON.parse(call.function.arguments);
if (validateArguments(args, tools[call.function.name]?.definition)) {
console.log(`Running function ${call.function.name} with arguments ${JSON.stringify(args)}.`);
const result = choosenFunction(args);
content = isNaN(result) ? 'Unable to calculate for given arguments, change arguments and try again.' : result?.toString();
} else {
content = 'Failed to calculate due to incorrect arguments'
}

}
} catch (error) {
console.error(error);
content = 'Failed to calculate due to an error';
}
console.log(`Executing ${call.function.name}. Result: ${content}`);
return ({
role: 'tool',
content: content,
tool_call_id: call.id,
})
}) ?? [];
const msgs = messages.concat([chatResponse.message, ...toolCalls]);
processMessages(msgs, response)
} else {
console.log(chatResponse);
if(!chatResponse){
return response.status(500).send("Got no response from the bot");
}
return response.status(200).send(chatResponse.message);
console.log(chatResponse);
if(!chatResponse){
return response.status(500).send("Got no response from the bot");
}
return response.status(200).send(chatResponse.message);
}

export const newMessage = makePostEndpoint(MessageHistory, async (request, response) => {
Expand Down
80 changes: 0 additions & 80 deletions src/chat/tools.ts

This file was deleted.

Loading

0 comments on commit 48684fd

Please sign in to comment.