Tom Newby

Lazy Cal: Natural language to add calendar events

2024 October 09

View Demo: Lazy Cal


Brief

Lazy Cal is a simple prototype that uses gpt-4o-mini to convert natural language into a calendar event schema. From this it then generates a URL that links into Google Calendar with some details prefilled.


The build

This was pretty simple, building ontop of Cloudflare Pages & Functions, reusing UI components from English Tutor.

The entirety of the LLM work is shown in the code below. I didn't bother setting up a Langsmith experiment for this, so I'm running on vibe checks - but it seems to work OK.

import {ChatOpenAI} from '@langchain/openai';
import {z} from 'zod';
import {format} from 'date-fns/format';
import {formatISO} from 'date-fns/formatISO';
import {HumanMessage, SystemMessage} from '@langchain/core/messages';

const eventSchema = z.object({
    title: z.string().describe('The title of the event'),
    start_date: z.string().describe('The start date and time of the event (ISO 8061 format with timezone offset)'),
    end_date: z.string().describe('The end date and time of the event (ISO 8061 format with timezone offset)'),
    location: z.string().nullable().describe('The location of the event'),
});

export type ParsedEvent = z.infer<typeof eventSchema>;

const responseSchema = z.object({
    event: eventSchema.nullable(),
});

const exampleOutput = {
    "event": {
        "title": "Go Karting",
        "start_date": "2024-08-10T18:30:00+10:00",
        "end_Date": "2024-08-10T20:30:00+10:00",
        "location": "Slideways Go Karting World"
    }
};

const SystemPrompt =
    `
Extract information for a calendar event from provided instructions and return it as a structured JSON object.

# Steps

1. **Identify Components**: Carefully read the instructions to identify key components: title, start datetime, end datetime, and location.
2. **Extract Details**:
   - **Title**: Determine the main subject or purpose of the event.
   - **Start Date and Time**: Identify the date and time when the event is set to begin.
   - **End Date and Time**: Determine the date and time when the event is set to conclude.
   - **Location**: Extract any mention of where the event will take place, this is optional.
3. **Format Data**: Organize these details into a JSON structure with appropriate labels.

# Output Format

Output should be formatted as a JSON object with the following properties:

- \`title\`: a string
- \`start_date\`: a string following the \`ISO 8061\` format (with timezone offsets)
- \`end_date\`: a string following the \`ISO 8061\` format (with timezone offsets)
- \`location\`: a string (location can be null if not specified)

# Example output
${JSON.stringify(exampleOutput, null, 2)}

# Notes

- Assume 1 hour event duration if not specified
- Dinners are at 6:30pm unless specified
- Carefully handle variations in time and date formats from input instructions. 
- Ensure chronological order between start and end dates.
- Handle optional location data gracefully, including or returning null based on input.
`;

export async function parseCalendar(openAiKey: string, message: string): Promise<ParsedEvent | null> {
    const model = new ChatOpenAI({
        model: 'gpt-4o-mini-2024-07-18',
        apiKey: openAiKey,
        temperature: 0.9
    }).withStructuredOutput(responseSchema, {strict: true});

    const now = new Date();

    const currentTime = `
    The current time is:
    
    - ${format(now, 'EEEE do MMMM yyyy h:mmaaaaa\'m\'')}
    - ${formatISO(now)}
    - Timezone: Australia/Brisbane (GMT+10)
    `

    const messages = [
        new SystemMessage(SystemPrompt),
        new SystemMessage(currentTime),
        new HumanMessage(message),
    ];

    const {event} = await model.invoke(messages);

    return event;
}

What I'd like to add

I'd like to actually integrate with Google Calendar so that it can directly add calendar events. If I do that, I'd also like to explore loading event from the calendar and generally leaning into a natural language enabled calendar mini app.

I'm interested in the idea of having an LLM build it's own UI specific to the task at hand. So asking the calendar to check next weekend's availability - I want to see just Saturday & Sunday.

Or perhaps "What do my weekends look like for the next few weeks" and it shows me ~4 weekends worth of dates.

Which is a very different UI to a calendar view for a week of work etc.