Skip to content

Commit 6650d48

Browse files
author
Pierre
authored
Merge pull request #43 from WorkflowAI/pierre-examples-calendar-event-extraction
add example of extracting calendar event
2 parents 8da35a6 + f1d0356 commit 6650d48

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
"""
2+
This example demonstrates how to create an agent that extracts calendar event information
3+
from email content or images. It shows how to:
4+
1. Parse email content to identify event details
5+
2. Extract structured calendar information (date, time, location)
6+
3. Handle different email formats and writing styles
7+
4. Process event information from images (posters, flyers)
8+
5. Return a well-structured calendar event object
9+
"""
10+
11+
import asyncio
12+
import base64
13+
import os
14+
from datetime import datetime
15+
from typing import Optional
16+
17+
from pydantic import BaseModel, Field
18+
19+
import workflowai
20+
from workflowai import Model, Run
21+
from workflowai.fields import File
22+
23+
24+
class EmailInput(BaseModel):
25+
"""Input model containing the email content to analyze."""
26+
subject: str = Field(
27+
description="The subject line of the email",
28+
)
29+
body: str = Field(
30+
description="The main content/body of the email",
31+
)
32+
33+
34+
class ImageInput(BaseModel):
35+
"""Input model containing an event poster or flyer image to analyze."""
36+
image: File = Field(
37+
description="An image of an event poster or flyer to analyze",
38+
)
39+
40+
41+
class CalendarEvent(BaseModel):
42+
"""Model containing the details of a calendar event."""
43+
title: Optional[str] = Field(
44+
default=None,
45+
description="The title/subject of the event",
46+
examples=["Team Meeting", "Product Review"],
47+
)
48+
start_time: Optional[datetime] = Field(
49+
default=None,
50+
description="The start time of the event in ISO format",
51+
examples=["2024-04-15T14:00:00-07:00"],
52+
)
53+
end_time: Optional[datetime] = Field(
54+
default=None,
55+
description="The end time of the event if specified",
56+
examples=["2024-04-15T15:00:00-07:00"],
57+
)
58+
location: Optional[str] = Field(
59+
default=None,
60+
description="The location or meeting room for the event",
61+
examples=["Conference Room A", "https://zoom.us/j/123456789"],
62+
)
63+
description: Optional[str] = Field(
64+
default=None,
65+
description="Additional details or description of the event",
66+
examples=["Monthly product review meeting to discuss Q2 progress"],
67+
)
68+
69+
70+
class CalendarEventOutput(BaseModel):
71+
"""Output model containing the extracted calendar event details if found."""
72+
calendar_event: Optional[CalendarEvent] = Field(
73+
default=None,
74+
description="The extracted calendar event details, or null if no event was found",
75+
)
76+
77+
78+
@workflowai.agent(
79+
id="calendar-event-extractor",
80+
model=Model.GPT_4O_MINI_LATEST,
81+
)
82+
async def extract_calendar_event_from_email(email_input: EmailInput) -> Run[CalendarEventOutput]:
83+
"""
84+
Extract calendar event details from email content.
85+
86+
Guidelines:
87+
1. Analyze both subject and body to identify event information:
88+
- Look for date and time information in both fields
89+
- Extract location and description if available
90+
- Combine information from both fields when needed
91+
- Return null for calendar_event if no event information is found
92+
93+
2. Handle different date/time formats:
94+
- Explicit dates ("April 15th, 2024")
95+
- Relative dates ("next Tuesday")
96+
- Various time formats ("3pm", "15:00", "3:00 PM PST")
97+
98+
3. Make reasonable assumptions when information is ambiguous:
99+
- Default to 1 hour duration if end time not specified
100+
- Use UTC if timezone not specified
101+
- Extract location if mentioned
102+
103+
4. Maintain accuracy and completeness:
104+
- Include all clearly stated information
105+
- Mark information as optional if not explicitly mentioned
106+
- Preserve timezone information when available
107+
"""
108+
...
109+
110+
111+
@workflowai.agent(
112+
id="calendar-event-extractor",
113+
model=Model.GPT_4O_MINI_LATEST,
114+
)
115+
async def extract_calendar_event_from_image(image_input: ImageInput) -> Run[CalendarEventOutput]:
116+
"""
117+
Extract calendar event details from an event poster or flyer image.
118+
119+
Guidelines:
120+
1. Analyze the image content to identify event details:
121+
- Look for prominent text showing date and time
122+
- Identify event title and location
123+
- Extract any additional description or details
124+
- Return null for calendar_event if no event information is found
125+
126+
2. Handle different date/time formats:
127+
- Explicit dates ("April 15th, 2024")
128+
- Various time formats ("3pm", "15:00", "3:00 PM PST")
129+
- Date ranges for multi-day events
130+
131+
3. Make reasonable assumptions when information is ambiguous:
132+
- Default to 1 hour duration if end time not specified
133+
- Use UTC if timezone not specified
134+
- Extract location if mentioned
135+
136+
4. Maintain accuracy and completeness:
137+
- Include all clearly visible information
138+
- Mark information as optional if not clearly visible
139+
- Preserve timezone information when available
140+
"""
141+
...
142+
143+
144+
async def main():
145+
# Example 1: Basic meeting invitation
146+
print("\nExample 1: Basic meeting invitation")
147+
print("-" * 50)
148+
149+
email1 = EmailInput(
150+
subject="Team Sync - Tomorrow at 2pm",
151+
body="""
152+
Hi team,
153+
154+
Let's sync up tomorrow at 2pm in Conference Room A to discuss our Q2 roadmap.
155+
156+
Please come prepared with your project updates.
157+
158+
Best,
159+
Sarah
160+
""",
161+
)
162+
163+
run = await extract_calendar_event_from_email(email1)
164+
print(run)
165+
166+
# Example 2: Virtual meeting with more details
167+
print("\nExample 2: Virtual meeting with more details")
168+
print("-" * 50)
169+
170+
email2 = EmailInput(
171+
subject="Invitation: Product Review Meeting (Apr 15, 2024)",
172+
body="""
173+
You are invited to our monthly product review meeting.
174+
175+
Date: April 15th, 2024
176+
Time: 2:00 PM - 3:30 PM PST
177+
Location: https://zoom.us/j/123456789
178+
179+
Agenda:
180+
1. Q1 metrics review
181+
2. Feature roadmap updates
182+
3. Customer feedback discussion
183+
""",
184+
)
185+
186+
run = await extract_calendar_event_from_email(email2)
187+
print(run)
188+
189+
# Example 3: Event poster image
190+
print("\nExample 3: Event poster image")
191+
print("-" * 50)
192+
193+
# Load the image file
194+
current_dir = os.path.dirname(os.path.abspath(__file__))
195+
image_path = os.path.join(current_dir, "assets", "poster.jpg")
196+
197+
# Verify the file exists
198+
if not os.path.exists(image_path):
199+
print(f"Image file not found at {image_path}")
200+
return
201+
202+
# Read and encode the image
203+
# Note: Using sync file read in example code is acceptable.
204+
# We avoid adding aiofiles dependency just for this example.
205+
with open(image_path, "rb") as f: # noqa: ASYNC230
206+
image_data = f.read()
207+
208+
# Create input with image
209+
image_input = ImageInput(
210+
image=File(
211+
content_type="image/jpeg",
212+
data=base64.b64encode(image_data).decode(),
213+
),
214+
)
215+
216+
run = await extract_calendar_event_from_image(image_input)
217+
print(run)
218+
219+
# Example 4: Email without calendar event
220+
print("\nExample 4: Email without calendar event")
221+
print("-" * 50)
222+
223+
email4 = EmailInput(
224+
subject="Weekly project status update",
225+
body="""
226+
Hi everyone,
227+
228+
Here's a quick update on our project progress:
229+
- Frontend team completed the new dashboard UI
230+
- Backend API documentation is now up to date
231+
- QA team found 3 minor bugs that are being fixed
232+
233+
Great work everyone!
234+
235+
Regards,
236+
Alex
237+
""",
238+
)
239+
240+
try:
241+
run = await extract_calendar_event_from_email(email4)
242+
print(run)
243+
except workflowai.WorkflowAIError as e:
244+
print(f"As expected, no calendar event found: {e!s}")
245+
246+
247+
if __name__ == "__main__":
248+
asyncio.run(main())

examples/assets/poster.jpg

67.5 KB
Loading

0 commit comments

Comments
 (0)