Infer event duration instead of defaulting every event to 30 minutes - #33
Open
greatxp117 wants to merge 2 commits into
Open
Infer event duration instead of defaulting every event to 30 minutes#33greatxp117 wants to merge 2 commits into
greatxp117 wants to merge 2 commits into
Conversation
Most forwarded emails give a start time but no end time. Today every one of those events lands on the calendar as DEFAULT_EVENT_LENGTH_MINUTES (30), so a dinner reservation and a standup get the same block. Two changes: - The prompt now asks the model to infer a realistic end_time from the kind of event when the text doesn't state one, with a table of typical durations to anchor it. Stated end times and durations are still used verbatim. All five few-shot examples had `end_time: undefined` for events with no stated end, which was training the model to leave it blank; they now show inferred end times consistent with the table. - When no end time can be inferred, fall back to the user's own Google Calendar "default event length" setting rather than a hardcoded constant. It's fetched alongside the existing calendarList.list() call, so it costs no extra latency, and needs no new OAuth scope. DEFAULT_EVENT_LENGTH_MINUTES remains the last resort if the setting can't be read. Adds UT12.5, which forwards a dinner reservation with no stated end time and asserts the resulting event is longer than an hour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Now that end times are inferred rather than defaulted, they're worth showing.
The confirmation email only printed the start, so there was no way to tell from
the reply whether the inferred duration was right without opening the event.
Adds formatEventDateRange, used by both the single-event and multi-event
replies. Same-day events collapse to one date with a start-end time
("Thursday, August 20th, 2026 at 6:00 PM - 8:00 PM CDT"); multi-day events
spell out both ends. Falls back to the old start-only string if the event
somehow has no end time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Owner
|
Thanks for the proposed change. There are a lot of conflicts and many changes here. What branch did you fork to create this? |
Owner
|
I think you want to merge to 'master' not 'main'. Sorry about that. |
Author
|
No worries — retargeted to Against
No rebase was needed — the branch was already 2 ahead / 0 behind |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
Most forwarded emails give a start time but no end time. When that happens, every event lands on the calendar as
DEFAULT_EVENT_LENGTH_MINUTES(30), so a dinner reservation and a standup get the same block. Start times are usually right; end times usually aren't.The root cause isn't really the constant — it's the prompt. All five few-shot examples in
getEventDatashowedend_time: undefinedfor events with no stated end time, which trains the model to leave it blank whenever the email doesn't spell it out. That's the common case, so the fallback fires almost every time.The change
1. Ask the model to infer a duration (
prompts.ts). Stated end times and durations are still used verbatim. When there isn't one, the prompt now asks for a realisticend_timebased on what kind of event it is, anchored by a short table of typical durations (coffee 30m, doctor 1h, dinner 2h, wedding 4h, and so on), with instructions to adjust for agenda length, attendee count, and distance. The five examples now show inferred end times consistent with that table.end_timeis still left undefined for events that are genuinely all-day or open-ended, or that have no start time.2. Better fallback when nothing can be inferred (
calendarHelper.ts). Google Calendar exposes the user's owndefaultEventLengthsetting, which is what Google itself uses for events created without a duration — a better fallback than a hardcoded constant. It's fetched alongside the existingcalendarList.list()call viaPromise.all, so it adds no latency, and it needs no new OAuth scope (the app already requests fullauth/calendar).DEFAULT_EVENT_LENGTH_MINUTESstays as the last resort if the setting can't be read.The ICS path is untouched — those events already carry real end times.
3. Show the end time in the confirmation email (
eventHandler.ts). Inferred durations are only useful if you can see them, and the reply printed the start time alone. A newformatEventDateRangehelper, used by both the single-event and multi-event replies, prints the range: same-day events collapse to one date (Thursday, August 20th, 2026 at 6:00 PM - 8:00 PM CDT), multi-day events spell out both ends, and it falls back to the old start-only string if an event has no end time.Testing
Adds
UT12.5, which forwards a restaurant confirmation with a start time and no end time, and asserts the created event is longer than an hour and at most four. The bounds are loose on purpose so it's a regression guard against the flat default rather than a brittle assertion about one model's exact output.npm run lintis clean.npm run buildhas 9 pre-existing@types/expresserrors inroutes.ts/drive/routes.tsonmain— same 9 before and after this branch, none in the files touched here.I wasn't able to run the full suite end to end (it needs live Google OAuth plus Resend and OpenRouter keys), so
UT12.5is unverified against the real model — worth a run before merging.🤖 Generated with Claude Code