Skip to content

Commit cac93fe

Browse files
Implemented connection between LLM and Calendar
1 parent 1251632 commit cac93fe

File tree

2 files changed

+96
-37
lines changed

2 files changed

+96
-37
lines changed

calendar.js

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const process = require('process');
44
const { authenticate } = require('@google-cloud/local-auth');
55
const { google } = require('googleapis');
66

7+
console.log("Hello World!");
8+
79
// If modifying these scopes, delete token.json.
810
const SCOPES = ['https://www.googleapis.com/auth/calendar'];
911
// The file token.json stores the user's access and refresh tokens, and is
@@ -15,36 +17,31 @@ const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');
1517
async function createEvents(auth, eventBodiesArray) {
1618
const calendar = google.calendar({ version: 'v3', auth });
1719

18-
const promises = eventBodiesArray.map(eventBody => {
19-
return calendar.events.insert({
20-
calendarId: 'primary',
21-
resource: eventBody,
22-
}).then(event => {
20+
for (let eventBody of eventBodiesArray) {
21+
try {
22+
const event = await calendar.events.insert({
23+
calendarId: 'primary',
24+
resource: eventBody,
25+
});
2326
console.log(`Event created: ${event.data.htmlLink}`);
24-
return event; // Return event for further processing if needed.
25-
}).catch(error => {
27+
} catch (error) {
2628
console.error('Error creating event', error);
27-
return null; // Return null or appropriate error handling.
28-
});
29-
});
30-
31-
try {
32-
const results = await Promise.all(promises);
33-
// Optional: Process results or perform further actions here.
34-
} catch (error) {
35-
console.error('An error occurred with Promise.all', error);
29+
// Optionally, continue to the next iteration instead of stopping the loop.
30+
// continue;
31+
}
3632
}
3733
}
3834

3935
function createEventObjects(optionsArray) {
4036
// Initialize an array to hold all event objects
4137
let events = [];
4238

39+
console.log(optionsArray);
40+
4341
// Iterate over each set of options
4442
optionsArray.forEach(options => {
4543
let event = {
4644
'summary': options.summary,
47-
'location': options.location,
4845
'description': options.description,
4946
// Initialize start and end events without dateTime or date
5047
'start': {},
@@ -55,13 +52,9 @@ function createEventObjects(optionsArray) {
5552
if (options.allDay) {
5653
event.start.date = options.start;
5754
event.end.date = options.end;
58-
event.start.timeZone = 'GMT+2';
59-
event.end.timeZone = 'GMT+2';
6055
} else {
61-
event.start.dateTime = options.start;
62-
event.end.dateTime = options.end;
63-
event.start.timeZone = options.startTimeZone || 'Etc/GMT+2';
64-
event.end.timeZone = options.endTimeZone || 'Etc/GMT+2';
56+
event.start["dateTime"] = options.start + "+02:00";
57+
event.end["dateTime"] = options.end + "+02:00";
6558
}
6659

6760
// Add recurrence if provided
@@ -80,7 +73,8 @@ function createEventObjects(optionsArray) {
8073
// Add the constructed event to the events array
8174
events.push(event);
8275
});
83-
76+
console.log("The returned object:");
77+
console.log(events);
8478
return events; // Returns an array of event objects
8579
}
8680

@@ -146,6 +140,14 @@ async function authorize() {
146140
return client;
147141
}
148142

143+
/*
144+
authorize().then(auth => {
145+
const newEvent = createEventObjects([{ "summary": "😴 Rest Time", "description": "I'm going to take some time off to lie down and recharge 🔋.", "start": "2024-05-09T22:15:00", "end": "2024-05-09T23:15:00" }])//[{ "summary": "😴 Rest Time", "description": "I'm going to take some time off to lie down and recharge 🔋.", "start": { "dateTime": "2024-05-09T22:15:00+01:00" }, "end": { "dateTime": "2024-05-09T23:15:00+01:00" } }];
146+
console.log([{ "summary": "😴 Rest Time", "description": "I'm going to take some time off to lie down and recharge 🔋.", "start": { "dateTime": "2024-05-09T22:15:00+01:00" }, "end": { "dateTime": "2024-05-09T23:15:00+01:00" } }]);
147+
console.log(newEvent);
148+
createEvents(auth, newEvent).catch(console.error);
149+
}).catch(console.error);
150+
/*
149151
authorize().then(auth => {
150152
// Preparing the date format for an all-day event
151153
const startDate = new Date('2024-05-15').toISOString().substring(0, 10); // Converting to 'YYYY-MM-DD' format
@@ -165,4 +167,8 @@ authorize().then(auth => {
165167
// Assuming createEvent is a function that creates an event on Google Calendar using the provided authentication and event object
166168
createEvents(auth, newEvent).catch(console.error);
167169
168-
}).catch(console.error);
170+
}).catch(console.error);
171+
172+
*/
173+
174+
module.exports = { createEvents, createEventObjects, authorize }

interaction.js

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
let fs = require('fs');
2+
const { chat, auth } = require('googleapis/build/src/apis/chat');
23
const OpenAI = require('openai');
4+
const calendarjs = require('./calendar.js');
5+
36
let openAIKey = "";
47

58
try {
@@ -62,6 +65,7 @@ class ChatWindow {
6265
this.#messages.push(message);
6366
}
6467

68+
6569
async #streamResponse(messages) {
6670
let stream = await openai.chat.completions.create({
6771
'model': this.model,
@@ -94,14 +98,46 @@ class ChatWindow {
9498
chatHistory.scrollTo({ top: chatHistory.scrollHeight });
9599
}
96100

97-
#parseJSONToObject(jsonString) {
101+
async extractAndParseJSON(jsonString) {
102+
const normalizedJSONString = this.#extractJSONFromString(jsonString);
103+
return await this.#parseJSONToObject(normalizedJSONString);
104+
}
105+
106+
#extractJSONFromString(text) {
107+
const jsonPattern = /```json\s*([\s\S]*?)\s*```/; // Corrected regex pattern// Regex to extract content between ```json and ```
108+
const matches = jsonPattern.exec(text);
109+
110+
if (matches && matches[1]) {
111+
console.log(matches[1]);
112+
return matches[1]; // Returns the extracted JSON string
113+
} else {
114+
console.error('No JSON found in the string');
115+
return null;
116+
}
117+
}
118+
119+
async #parseJSONToObject(jsonString) {
98120
try {
99-
let obj = JSON.parse(jsonString);
100-
return obj;
121+
return JSON.parse(jsonString);
101122
} catch (e) {
102-
// Log error or handle it as needed
103-
console.error('Error parsing JSON', e);
104-
return null; // or return an empty object {}, based on your needs
123+
const fixedJsonPrompt = `This JSON array: '${jsonString}'\n\n` +
124+
`gives this error:\n'${e}'.\nplease fix it.`;
125+
126+
await this.sendUserMessage(fixedJsonPrompt);
127+
128+
const fixedJsonMessage = this.#messages.slice(-1)[0].content;
129+
console.log(`Fixed JSON: ${fixedJsonMessage}`);
130+
131+
const fixedJsonString = this.#extractJSONFromString(fixedJsonMessage);
132+
133+
try {
134+
return JSON.parse(fixedJsonString);
135+
} catch (error) {
136+
console.error('Error parsing JSON', error);
137+
// Consider throwing the error to indicate failure to the caller,
138+
// or handle it in an application-specific way if returning null is not desired.
139+
return null;
140+
}
105141
}
106142
}
107143

@@ -133,14 +169,14 @@ class ChatWindow {
133169
return this.#messages;
134170
}
135171

136-
sendUserMessage(prompt) {
172+
async sendUserMessage(prompt) {
137173
let userMessage = this.#makeMessage(this.#USER, prompt);
138174
this.#messagesAppend(userMessage);
139175
this.#generateMessageHTML(userMessage);
140176
let messages = this.getMessages();
141177
let emptyResponseMessage = this.#makeMessage(this.#ASSISTANT, "");
142178
this.#generateMessageHTML(emptyResponseMessage);
143-
this.#streamResponse(messages);
179+
await this.#streamResponse(messages);
144180
}
145181

146182
async sendSystemMessage(prompt) {
@@ -154,13 +190,14 @@ function sleep(ms) {
154190
}
155191

156192
const chatWindow = new ChatWindow(GPT4);
193+
const chatWindowCheap = new ChatWindow(GPT4);
157194
(async () => {
158195
await chatWindow.sendSystemMessage("Welcome to CalendarMe!");
159196
await sleep(1000);
160197
await chatWindow.sendSystemMessage("Please write your plans here in as much detail as you like.")
161198
})();
162199

163-
textInputBox.addEventListener('keydown', function (e) {
200+
textInputBox.addEventListener('keydown', async function (e) {
164201
if (e.key === 'Enter') {
165202
let today = new Date().toISOString().split('T')[0];
166203
let time = new Date().toTimeString().split(' ')[0];
@@ -170,12 +207,28 @@ textInputBox.addEventListener('keydown', function (e) {
170207

171208
textInputBox.value = "";
172209

173-
let constructedPrompt = planning_prompt//`Given the following current date and time: ${day}, ${today}T${time} and planning prompt: '${planning_prompt}', format the prompt's contents as JSON objects with the following keys: summary, location (Optional.), description, start, end, recurrence (Optional. array of RRULE strings), reminders (Optional. useDefault, overrides), timeZone (Etc/GMT+2), allDay (boolean), in an array that can be parsed to create calendar events. Please use 1-2 emojis per complex sentence in the title's lhs and description to make them more personal.`;
210+
let constructedPrompt = planning_prompt//`Given the following current date and time: ${day}, ${today}T${time} and planning prompt: '${planning_prompt}', format the prompt's contents as JSON objects with the following keys: summary, location (Optional.), description, start (ISO 8601), end, recurrence (Optional. array of RRULE strings), reminders (Optional. useDefault, overrides), timeZone (Etc/GMT+2), allDay (boolean), in an array that can be parsed to create calendar events. Please use 1-2 emojis per complex sentence in the title's lhs and description to make them more personal.`;
211+
174212

175213
if (chatWindow.getState() == CHAT_WINDOW_STATES.CREATE) {
176-
constructedPrompt = `Given the following current date and time: ${day}, ${today}T${time} and planning prompt: '${planning_prompt}', format the prompt's contents as JSON objects with the following keys: summary, location (Optional.), description, start, end, recurrence (Optional. array of RRULE strings), reminders (Optional. useDefault, overrides), timeZone (Etc/GMT+2), allDay (Optional. boolean), in an array that can be parsed to create calendar events. Please use 1-2 emojis per complex sentence in the title's lhs and description to make them more personal.`;
214+
constructedPrompt = `Given the following current date and time: ${day}, ${today}T${time}:00 and planning prompt: '${planning_prompt}', format the prompt's contents as JSON objects with the following keys: summary, description, start, end, in an array that can be parsed to create calendar events. Please use 1-2 emojis per complex sentence in the title's lhs and description to make them more personal.`;
177215
chatWindow.setState(CHAT_WINDOW_STATES.SPECIFY);
216+
chatWindow.sendUserMessage(constructedPrompt);
217+
} else {
218+
if (constructedPrompt == "yes") {
219+
let messages = chatWindow.getMessages()
220+
let events_message = messages.slice(-1)[0].content;
221+
let events_json = await chatWindow.extractAndParseJSON(events_message);
222+
let eventsToProcess = Array.isArray(events_json) ? events_json : [events_json];
223+
console.log("The created events array after events_json call");
224+
console.log(events_json);
225+
let events = calendarjs.createEventObjects(eventsToProcess);
226+
227+
calendarjs.authorize().then(auth => { calendarjs.createEvents(auth, events).catch(console.error); }).catch(console.error);
228+
(async () => {
229+
await chatWindow.sendSystemMessage("Event creation attempted!");
230+
})();
231+
}
178232
}
179-
chatWindow.sendUserMessage(constructedPrompt);
180233
}
181234
});

0 commit comments

Comments
 (0)