File size: 3,684 Bytes
2606dde
5da61b4
ebac87f
e943a05
0e5c445
 
0c4cf03
e91b76c
0e5c445
 
e91b76c
 
e943a05
e91b76c
0e5c445
77399ca
e91b76c
 
 
 
 
e943a05
e91b76c
0e5c445
e91b76c
77399ca
 
e943a05
77399ca
 
1e5090f
77399ca
 
1e5090f
 
 
 
 
 
77399ca
e943a05
77399ca
 
 
 
 
dc86148
 
 
 
 
77399ca
0c4cf03
0e5c445
 
77399ca
 
0e5c445
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447c0ca
 
77399ca
447c0ca
b7b2c8c
 
447c0ca
 
ad02fa3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import type { BackendModel } from "./server/models";
import type { Message } from "./types/Message";
import { format } from "date-fns";
import type { WebSearch } from "./types/WebSearch";
import { downloadFile } from "./server/files/downloadFile";
import type { Conversation } from "./types/Conversation";

interface buildPromptOptions {
	messages: Pick<Message, "from" | "content" | "files">[];
	id?: Conversation["_id"];
	model: BackendModel;
	locals?: App.Locals;
	webSearch?: WebSearch;
	preprompt?: string;
	files?: File[];
	continue?: boolean;
}

export async function buildPrompt({
	messages,
	model,
	webSearch,
	preprompt,
	id,
}: buildPromptOptions): Promise<string> {
	let modifiedMessages = [...messages];

	if (webSearch && webSearch.context) {
		// find index of the last user message
		const lastUsrMsgIndex = modifiedMessages.map((el) => el.from).lastIndexOf("user");

		// combine all the other previous questions into one string
		const previousUserMessages = modifiedMessages.filter((el) => el.from === "user").slice(0, -1);
		const previousQuestions =
			previousUserMessages.length > 0
				? `Previous questions: \n${previousUserMessages
						.map(({ content }) => `- ${content}`)
						.join("\n")}`
				: "";

		const currentDate = format(new Date(), "MMMM d, yyyy");

		// update the last user message directly (that way if the last message is an assistant partial answer, we keep the beginning of that answer)
		modifiedMessages[lastUsrMsgIndex] = {
			from: "user",
			content: `I searched the web using the query: ${webSearch.searchQuery}. Today is ${currentDate} and here are the results:
=====================
${webSearch.context}
=====================
${previousQuestions}
Answer the question: ${messages[lastUsrMsgIndex].content}`,
		};
	}
	// section to handle potential files input
	if (model.multimodal) {
		modifiedMessages = await Promise.all(
			modifiedMessages.map(async (el) => {
				let content = el.content;

				if (el.from === "user") {
					if (el?.files && el.files.length > 0 && id) {
						const markdowns = await Promise.all(
							el.files.map(async (hash) => {
								try {
									const { content: image, mime } = await downloadFile(hash, id);
									const b64 = image.toString("base64");
									return `![](data:${mime};base64,${b64})})`;
								} catch (e) {
									console.error(e);
								}
							})
						);
						content += markdowns.join("\n ");
					} else {
						// if no image, append an empty white image
						content +=
							"\n![](data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAAQABADAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/igAoAKACgD/2Q==)";
					}
				}

				return { ...el, content };
			})
		);
	}

	return (
		model
			.chatPromptRender({ messages: modifiedMessages, preprompt })
			// Not super precise, but it's truncated in the model's backend anyway
			.split(" ")
			.slice(-(model.parameters?.truncate ?? 0))
			.join(" ")
	);
}