Skip to content

Infer event duration instead of defaulting every event to 30 minutes - #33

Open
greatxp117 wants to merge 2 commits into
moeadham:masterfrom
greatxp117:feat/infer-event-duration
Open

Infer event duration instead of defaulting every event to 30 minutes#33
greatxp117 wants to merge 2 commits into
moeadham:masterfrom
greatxp117:feat/infer-event-duration

Conversation

@greatxp117

@greatxp117 greatxp117 commented Aug 19, 2026

Copy link
Copy Markdown

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 getEventData showed end_time: undefined for 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 realistic end_time based 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_time is 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 own defaultEventLength setting, which is what Google itself uses for events created without a duration — a better fallback than a hardcoded constant. It's fetched alongside the existing calendarList.list() call via Promise.all, so it adds no latency, and it needs no new OAuth scope (the app already requests full auth/calendar). DEFAULT_EVENT_LENGTH_MINUTES stays 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 new formatEventDateRange helper, 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 lint is clean. npm run build has 9 pre-existing @types/express errors in routes.ts/drive/routes.ts on main — 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.5 is unverified against the real model — worth a run before merging.

🤖 Generated with Claude Code

XavierMacbookAir and others added 2 commits August 19, 2026 10:35
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>
@moeadham

Copy link
Copy Markdown
Owner

Thanks for the proposed change. There are a lot of conflicts and many changes here. What branch did you fork to create this?

@moeadham

Copy link
Copy Markdown
Owner

I think you want to merge to 'master' not 'main'. Sorry about that.

@greatxp117
greatxp117 changed the base branch from main to master August 26, 2026 01:42
@greatxp117

Copy link
Copy Markdown
Author

No worries — retargeted to master. That was the issue: I'd branched off main, which is stale, so the PR was picking up ~30 unrelated upstream commits and showing conflicts.

Against master it's down to just my 2 commits and 7 files, and GitHub now reports it as mergeable:

  • README.md
  • functions/src/agents/calendar/calendarHelper.ts
  • functions/src/agents/calendar/eventHandler.ts
  • functions/src/agents/calendar/prompts.ts
  • functions/src/util/config.ts
  • functions/test/bindings/resendBindings.ts
  • functions/test/test.ts

No rebase was needed — the branch was already 2 ahead / 0 behind master. Ready for another look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants