Skip to content

Commit f6cf933

Browse files
committed
Add tool calls for updateTimetable and deleteTimetable
1 parent 01c454a commit f6cf933

File tree

3 files changed

+148
-2
lines changed

3 files changed

+148
-2
lines changed

course-matrix/backend/src/constants/availableFunctions.ts

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { supabase } from "../db/setupDb";
22
import { Request } from "express";
33

4-
export type FunctionNames = "getTimetables"; // Add all possible function names here
4+
// Add all possible function names here
5+
export type FunctionNames = "getTimetables" | "updateTimetable" | "deleteTimetable";
56

67
type AvailableFunctions = {
78
[K in FunctionNames]: (args: any, req: Request) => Promise<any>;
@@ -40,4 +41,124 @@ export const availableFunctions: AvailableFunctions = {
4041
return { status: 400, error: error };
4142
}
4243
},
44+
updateTimetable: async (args: any, req: Request) => {
45+
try {
46+
const { id, timetable_title, semester } = args;
47+
48+
if (!timetable_title && !semester) {
49+
return {
50+
status: 400,
51+
error:
52+
"New timetable title or semester is required when updating a timetable",
53+
};
54+
}
55+
56+
//Retrieve the authenticated user
57+
const user_id = (req as any).user.id;
58+
59+
//Retrieve users allowed to access the timetable
60+
const { data: timetableUserData, error: timetableUserError } =
61+
await supabase
62+
.schema("timetable")
63+
.from("timetables")
64+
.select("*")
65+
.eq("id", id)
66+
.eq("user_id", user_id)
67+
.maybeSingle();
68+
69+
const timetable_user_id = timetableUserData?.user_id;
70+
71+
if (timetableUserError)
72+
return {status: 400, error: timetableUserError.message };
73+
74+
//Validate timetable validity:
75+
if (!timetableUserData || timetableUserData.length === 0) {
76+
return {status: 404, error: "Calendar id not found" };
77+
}
78+
79+
//Validate user access
80+
if (user_id !== timetable_user_id) {
81+
return { status: 401, error: "Unauthorized access to timetable events" };
82+
}
83+
84+
let updateData: any = {};
85+
if (timetable_title) updateData.timetable_title = timetable_title;
86+
if (semester) updateData.semester = semester;
87+
88+
//Update timetable title, for authenticated user only
89+
let updateTimetableQuery = supabase
90+
.schema("timetable")
91+
.from("timetables")
92+
.update(updateData)
93+
.eq("id", id)
94+
.eq("user_id", user_id)
95+
.select();
96+
97+
const { data: timetableData, error: timetableError } =
98+
await updateTimetableQuery;
99+
100+
if (timetableError)
101+
return { status: 400, error: timetableError.message };
102+
103+
// If no records were updated due to non-existence timetable or it doesn't belong to the user.
104+
if (!timetableData || timetableData.length === 0) {
105+
return {
106+
status: 404,
107+
error: "Timetable not found or you are not authorized to update it",
108+
};
109+
}
110+
return { status: 200, data: timetableData };
111+
} catch (error) {
112+
return { status: 500, error: error };
113+
}
114+
},
115+
deleteTimetable: async (args: any, req: Request) => {
116+
try {
117+
const { id } = args;
118+
119+
// Retrieve the authenticated user
120+
const user_id = (req as any).user.id;
121+
122+
//Retrieve users allowed to access the timetable
123+
const { data: timetableUserData, error: timetableUserError } =
124+
await supabase
125+
.schema("timetable")
126+
.from("timetables")
127+
.select("*")
128+
.eq("id", id)
129+
.eq("user_id", user_id)
130+
.maybeSingle();
131+
const timetable_user_id = timetableUserData?.user_id;
132+
133+
if (timetableUserError)
134+
return { status: 400, error: timetableUserError.message };
135+
136+
//Validate timetable validity:
137+
if (!timetableUserData || timetableUserData.length === 0) {
138+
return { status: 404, error: "Calendar id not found" };
139+
}
140+
141+
//Validate user access
142+
if (user_id !== timetable_user_id) {
143+
return { status: 401, error: "Unauthorized access to timetable events" };
144+
}
145+
146+
// Delete only if the timetable belongs to the authenticated user
147+
let deleteTimetableQuery = supabase
148+
.schema("timetable")
149+
.from("timetables")
150+
.delete()
151+
.eq("id", id)
152+
.eq("user_id", user_id);
153+
154+
const { error: timetableError } = await deleteTimetableQuery;
155+
156+
if (timetableError)
157+
return { status: 400, error: timetableError.message };
158+
159+
return { status: 200, data: "Timetable successfully deleted"};
160+
} catch (error) {
161+
return { status: 500, error: error };
162+
}
163+
}
43164
};

course-matrix/backend/src/constants/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,5 @@ namespaceToMinResults.set("programs", 5);
5050
export const CHATBOT_MEMORY_THRESHOLD = 3;
5151

5252
export const CHATBOT_TIMETABLE_CMD = "/timetable";
53+
54+
export const CHATBOT_TOOL_CALL_MAX_STEPS = 3;

course-matrix/backend/src/controllers/aiController.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
import {
2727
CHATBOT_MEMORY_THRESHOLD,
2828
CHATBOT_TIMETABLE_CMD,
29+
CHATBOT_TOOL_CALL_MAX_STEPS,
2930
} from "../constants/constants";
3031
import { namespaceToMinResults } from "../constants/constants";
3132
import OpenAI from "openai";
@@ -254,8 +255,30 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
254255
return await availableFunctions.getTimetables(args, req);
255256
},
256257
}),
258+
updateTimetable: tool({
259+
description:
260+
"Update a user's timetable by title and/or semester",
261+
parameters: z.object({
262+
id: z.number().positive(),
263+
timetable_title: z.string().optional(),
264+
semester: z.enum(["Fall 2025", "Summer 2025", "Winter 2026"]).optional(),
265+
}),
266+
execute: async (args) => {
267+
return await availableFunctions.updateTimetable(args, req);
268+
}
269+
}),
270+
deleteTimetable: tool({
271+
description:
272+
"Delete a user's timetable",
273+
parameters: z.object({
274+
id: z.number().positive(),
275+
}),
276+
execute: async (args) => {
277+
return await availableFunctions.deleteTimetable(args, req)
278+
}
279+
})
257280
},
258-
maxSteps: 3, // Controls how many back and forths the model can take with user or calling multiple tools
281+
maxSteps: CHATBOT_TOOL_CALL_MAX_STEPS, // Controls how many back and forths the model can take with user or calling multiple tools
259282
experimental_repairToolCall: async ({
260283
toolCall,
261284
tools,

0 commit comments

Comments
 (0)