Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ai-text-to-calendar extension #16186

Merged
merged 4 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions extensions/ai-text-to-calendar/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# AI Text to Calendar Changelog

## [Bug Fix and Enhancement] - 2025-01-15
- 🐞 Bug fix - preference api call and author id
- πŸ‘¨β€πŸ³ Customable service - fill your own LLM service endpoint and model name
- πŸ’¬ Multiple Language Support - Set your preferred language for events
- 🍧 Minor prompt adjustment

## [Initial Version] - 2024-12-09
25 changes: 21 additions & 4 deletions extensions/ai-text-to-calendar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,28 @@ AI Text to Calendar is a [Raycast](https://raycast.com/) extension that converts

Configure the extension via `Raycast Settings > Extensions > AI Text to Calendar`.

| Property | Label | Type | Required | Description |
| -------------- | -------------- | ------ | -------- | ------------------------------------------------ |
| `openAiApiKey` | OpenAI API Key | string | true | Your personal OpenAI API key |
| `language` | Language | string | true | Language of the input text (e.g., English, ζ—₯本θͺž) |
| Property | Label | Type | Required | Description |
| ---------- | ------------------ | ------ | -------- | -------------------------------------------------------------------------------------------------- |
| `apiKey` | AI Service API Key | string | true | Your personal AI service API key |
| `model` | Model Name | string | false | LLM model name, default is `gpt-4o-mini` |
| `language` | Language | string | false | Language of the output text, default is `English` |
| `endpoint` | Endpoint | string | false | LLM service endpoint (e.g., <https://api.deepseek.com/v1>), default is `https://api.openai.com/v1` |

## TODO

- [ ] User default settings (e.g., default date, time)
- [ ] With supplementary information (e.g., selected text + user input)
- [ ] Support for other calendar services (e.g., use Apple Script or Shortcut for Apple Calendar)

## License

This project is licensed under the MIT License.

## Example Test Cases

| Test Case | gpt-4o-mini works? | gpt-4o works? |
| ---------------------------------------------------- | ------------------ | ------------- |
| `play tennis with Mike tommorrow at 5pm in the park` | βœ“ | βœ“ |
| `Math class next Monday at 10am in lecture hall` | x | βœ“ |

Tips: try to use advanced LLM models for better results, especially for date reasoning.
4 changes: 2 additions & 2 deletions extensions/ai-text-to-calendar/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 40 additions & 6 deletions extensions/ai-text-to-calendar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
"title": "AI Text to Calendar",
"description": "Convert selected text to Google Calendar event with OpenAI",
"icon": "extension-icon.png",
"author": "tatsuhiko_imaizumi",
"author": "izm51",
"contributors": [
"ViGeng"
],
"categories": [
"Productivity"
],
Expand All @@ -17,18 +20,49 @@
"mode": "no-view",
"preferences": [
{
"name": "openAiApiKey",
"title": "OpenAI API Key",
"description": "API Key is used to authenticate with OpenAI API",
"name": "apiKey",
"title": "AI Service API Key",
"description": "API Key is used to authenticate with OpenAI or other AI service",
"type": "password",
"required": true
},
{
"name": "endpoint",
"title": "Custom API Endpoint",
"description": "Overide the default API endpoint",
"type": "textfield",
"default": "https://api.openai.com/v1",
"required": false
},
{
"name": "model",
"title": "Model Name",
"description": "Model name to use for the AI service",
"type": "textfield",
"default": "gpt-4o-mini",
"required": false
},
{
"name": "language",
"title": "Language",
"description": "Language of the output",
"type": "textfield",
"required": true
"type": "dropdown",
"default": "English",
"data": [
{ "title": "English","value": "English" },
{ "title": "Chinese", "value": "Chinese"},
{ "title": "Japanese", "value": "Japanese"},
{ "title": "Korean", "value": "Korean"},
{ "title": "Spanish", "value": "Spanish"},
{ "title": "French", "value": "French"},
{ "title": "German", "value": "German"},
{ "title": "Italian", "value": "Italian"},
{ "title": "Dutch", "value": "Dutch"},
{ "title": "Portuguese", "value": "Portuguese"},
{ "title": "Russian", "value": "Russian"},
{ "title": "Arabic", "value": "Arabic"}
],
"required": false
}
]
}
Expand Down
46 changes: 37 additions & 9 deletions extensions/ai-text-to-calendar/src/ai-text-to-calendar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { showHUD, Toast, showToast, getSelectedText, getPreferenceValues, Clipboard, open } from "@raycast/api";
import { Clipboard, getPreferenceValues, getSelectedText, open, showHUD, showToast, Toast } from "@raycast/api";
import OpenAI from "openai";

interface CalendarEvent {
Expand All @@ -13,11 +13,14 @@ interface CalendarEvent {

export default async function main() {
try {
const { openAiApiKey, language } = getPreferenceValues<Preferences.AiTextToCalendar>();
const apiKey = getPreferenceValues().apiKey;
const endpoint = getPreferenceValues().endpoint || "https://api.openai.com/v1";
const language = getPreferenceValues().language || "English";
const model = getPreferenceValues().model || "gpt-4o-mini";

showToast({ style: Toast.Style.Animated, title: "Extracting..." });
const selectedText = await getSelectedText();
const json = await ai(selectedText, openAiApiKey, language);
const json = await ai(selectedText, apiKey, language, endpoint, model);
if (!json) {
throw new Error("Extraction failed");
}
Expand All @@ -37,13 +40,25 @@ export default async function main() {
}
}

async function ai(text: string, openaiKey: string, language: string) {
async function ai(text: string, openaiKey: string, language: string, endpoint: string, model: string) {
// get current date and time to string format, to let the LLM know the current date and time
// date format: YYYY-MM-DD
const date_str = new Date().toISOString().split("T")[0];
// time format: HH:MM:SS
const time_str = new Date().toISOString().split("T")[1].split(".")[0];
// current week day
const week_day = new Date().getDay().toString();

console.log("date_str:", date_str);
console.log("time_str:", time_str);
console.log("week_day:", week_day);

const systemMessage = `\
Extract schedule information from the text provided by the user.
The output should be in the following JSON format.

{
title: string, // Event title
title: string, // Event title, should be descriptive and very concise
start_date: YYYYMMDD, // Start date
start_time: hhmmss, // Start time
end_date: YYYYMMDD, // End date
Expand All @@ -54,14 +69,15 @@ The output should be in the following JSON format.

Note:
* Output in ${language}
* Current date: ${date_str}, Current time: ${time_str}, Current week day: ${week_day}, try to set the event date and time based on the current date and time
* Do not include any content other than JSON format in the output
* If the organizer's name is known, include it in the title
* Ensure the location is easily identifiable
* If the end date and time are unknown, set it to 2 hours after the start date and time\
* If the duration is not specified, assume it is 2 hours
`;
const openai = new OpenAI({ apiKey: openaiKey });
const openai = new OpenAI({ apiKey: openaiKey, baseURL: endpoint });
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
model: model,
response_format: { type: "json_object" },
messages: [
{ role: "system", content: systemMessage },
Expand All @@ -75,6 +91,18 @@ Note:
}

function toURL(json: CalendarEvent) {
const url = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${json.title}&dates=${json.start_date}T${json.start_time}/${json.end_date}T${json.end_time}&details=${json.details}&location=${json.location}&trp=false`;
// Clean up and format dates/times - remove any non-numeric characters
const startDateTime = `${json.start_date.replace(/-/g, "")}T${json.start_time.replace(/:/g, "")}00`;
const endDateTime = `${json.end_date.replace(/-/g, "")}T${json.end_time.replace(/:/g, "")}00`;

// Encode parameters for URL safety
const params = {
text: encodeURIComponent(json.title),
dates: `${startDateTime}/${endDateTime}`,
details: encodeURIComponent(json.details),
location: encodeURIComponent(json.location),
};

const url = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${params.text}&dates=${params.dates}&details=${params.details}&location=${params.location}&trp=false`;
return url;
}