+1
docker-compose.yml
+1
docker-compose.yml
+39
-43
src/handlers/messages.ts
+39
-43
src/handlers/messages.ts
···
18
18
19
19
type SupportedFunctionCall = typeof c.SUPPORTED_FUNCTION_CALLS[number];
20
20
21
-
async function generateAIResponse(parsedConversation: string) {
21
+
async function generateAIResponse(parsedContext: string, messages: {
22
+
role: string;
23
+
parts: {
24
+
text: string;
25
+
}[];
26
+
}[]) {
22
27
const config = {
23
28
model: env.GEMINI_MODEL,
24
29
config: {
···
37
42
],
38
43
},
39
44
{
40
-
role: "user" as const,
45
+
role: "model" as const,
41
46
parts: [
42
47
{
43
-
text:
44
-
`Below is the yaml for the current conversation. The last message is the one to respond to. The post is the current one you are meant to be analyzing.
45
-
46
-
${parsedConversation}`,
48
+
text: parsedContext,
47
49
},
48
50
],
49
51
},
52
+
...messages,
50
53
];
51
54
52
55
let inference = await c.ai.models.generateContent({
···
99
102
return inference;
100
103
}
101
104
102
-
async function sendResponse(
103
-
conversation: Conversation,
104
-
text: string,
105
-
): Promise<void> {
106
-
if (exceedsGraphemes(text)) {
107
-
multipartResponse(conversation, text);
108
-
} else {
109
-
conversation.sendMessage({
110
-
text,
111
-
});
112
-
}
113
-
}
114
-
115
105
export async function handler(message: ChatMessage): Promise<void> {
116
106
const conversation = await message.getConversation();
117
107
// ? Conversation should always be able to be found, but just in case:
···
132
122
return;
133
123
}
134
124
135
-
const today = new Date();
136
-
today.setHours(0, 0, 0, 0);
137
-
const tomorrow = new Date(today);
138
-
tomorrow.setDate(tomorrow.getDate() + 1);
125
+
if (message.senderDid != env.ADMIN_DID) {
126
+
const todayStart = new Date();
127
+
todayStart.setHours(0, 0, 0, 0);
139
128
140
-
const dailyCount = await db
141
-
.select({ count: count(messages.id) })
142
-
.from(messages)
143
-
.where(
144
-
and(
145
-
eq(messages.did, message.senderDid),
146
-
gte(messages.created_at, today),
147
-
lt(messages.created_at, tomorrow),
148
-
),
149
-
);
129
+
const dailyCount = await db
130
+
.select({ count: count(messages.id) })
131
+
.from(messages)
132
+
.where(
133
+
and(
134
+
eq(messages.did, message.senderDid),
135
+
gte(messages.created_at, todayStart),
136
+
),
137
+
);
150
138
151
-
if (dailyCount[0]!.count >= env.DAILY_QUERY_LIMIT) {
152
-
conversation.sendMessage({
153
-
text: c.QUOTA_EXCEEDED_MESSAGE,
154
-
});
155
-
return;
139
+
if (dailyCount[0]!.count >= env.DAILY_QUERY_LIMIT) {
140
+
conversation.sendMessage({
141
+
text: c.QUOTA_EXCEEDED_MESSAGE,
142
+
});
143
+
return;
144
+
}
156
145
}
157
146
158
147
logger.success("Found conversation");
···
160
149
text: "...",
161
150
});
162
151
163
-
const parsedConversation = await parseConversation(conversation);
164
-
165
-
logger.info("Parsed conversation: ", parsedConversation);
152
+
const parsedConversation = await parseConversation(conversation, message);
166
153
167
154
try {
168
-
const inference = await generateAIResponse(parsedConversation);
155
+
const inference = await generateAIResponse(
156
+
parsedConversation.context,
157
+
parsedConversation.messages,
158
+
);
169
159
if (!inference) {
170
160
throw new Error("Failed to generate text. Returned undefined.");
171
161
}
···
176
166
logger.success("Generated text:", inference.text);
177
167
saveMessage(conversation, env.DID, inference.text!);
178
168
179
-
await sendResponse(conversation, responseText);
169
+
if (exceedsGraphemes(responseText)) {
170
+
multipartResponse(conversation, responseText);
171
+
} else {
172
+
conversation.sendMessage({
173
+
text: responseText,
174
+
});
175
+
}
180
176
}
181
177
} catch (error) {
182
178
logger.error("Error in post handler:", error);
+21
-26
src/utils/conversation.ts
+21
-26
src/utils/conversation.ts
···
14
14
/*
15
15
Utilities
16
16
*/
17
-
const resolveDid = (convo: Conversation, did: string) =>
18
-
convo.members.find((actor) => actor.did == did)!;
19
-
20
17
const getUserDid = (convo: Conversation) =>
21
18
convo.members.find((actor) => actor.did != env.DID)!;
22
19
···
29
26
/*
30
27
Conversations
31
28
*/
32
-
async function initConvo(convo: Conversation) {
29
+
async function initConvo(convo: Conversation, initialMessage: ChatMessage) {
33
30
const user = getUserDid(convo);
34
-
35
-
const initialMessage = (await convo.getMessages()).messages[0] as
36
-
| ChatMessage
37
-
| undefined;
38
-
if (!initialMessage) {
39
-
throw new Error("Failed to get initial message of conversation");
40
-
}
41
31
42
32
const postUri = await parseMessagePostUri(initialMessage);
43
33
if (!postUri) {
···
87
77
return convo;
88
78
}
89
79
90
-
export async function parseConversation(convo: Conversation) {
80
+
export async function parseConversation(
81
+
convo: Conversation,
82
+
latestMessage: ChatMessage,
83
+
) {
91
84
let row = await getConvo(convo.id);
92
85
if (!row) {
93
-
row = await initConvo(convo);
86
+
row = await initConvo(convo, latestMessage);
94
87
} else {
95
-
const latestMessage = (await convo.getMessages())
96
-
.messages[0] as ChatMessage;
97
-
98
88
const postUri = await parseMessagePostUri(latestMessage);
99
89
if (postUri) {
100
90
const [updatedRow] = await db
···
128
118
129
119
let parseResult = null;
130
120
try {
131
-
parseResult = yaml.dump({
132
-
post: await parsePost(post, true),
121
+
parseResult = {
122
+
context: yaml.dump({
123
+
post: await parsePost(post, true),
124
+
}),
133
125
messages: convoMessages.map((message) => {
134
-
const profile = resolveDid(convo, message.did);
126
+
const role = message.did == env.DID ? "model" : "user";
135
127
136
128
return {
137
-
user: profile.displayName
138
-
? `${profile.displayName} (${profile.handle})`
139
-
: `Handle: ${profile.handle}`,
140
-
text: message.text,
129
+
role,
130
+
parts: [
131
+
{
132
+
text: message.text,
133
+
},
134
+
],
141
135
};
142
136
}),
143
-
});
137
+
};
144
138
} catch (e) {
145
139
convo.sendMessage({
146
140
text:
···
169
163
.where(
170
164
and(
171
165
eq(messages.conversationId, convo.id),
172
-
eq(messages.postUri, convo!.postUri),
166
+
eq(messages.postUri, convo.postUri),
167
+
eq(messages.revision, convo.revision),
173
168
),
174
169
)
175
170
.limit(15);
···
192
187
.values({
193
188
conversationId: _convo.id,
194
189
postUri: _convo.postUri,
195
-
revision: _convo.postUri,
190
+
revision: _convo.revision,
196
191
did,
197
192
text,
198
193
});