Skip to content

Commit

Permalink
feat: refactor tools as retriever / actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Neet-Nestor committed Oct 21, 2024
1 parent 11de742 commit a2839e4
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 90 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"devDependencies": {
"@babel/preset-env": "^7.25.4",
"@babel/preset-typescript": "^7.24.7",
"@types/chrome": "^0.0.278",
"@types/jest": "^29.5.12",
"@types/node": "^22.5.4",
"babel-loader": "^9.1.3",
Expand Down
130 changes: 130 additions & 0 deletions src/action.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { State } from "./state";
import { Tool } from "./tool";

export const replaceSelectedText = (
state: State,
Expand Down Expand Up @@ -46,3 +47,132 @@ export const appendTextToDocument = (
throw new Error("Not Implemented");
}
};

export async function createCalendarEvent(
state: State,
parameters: {
token: string;
summary: string;
location?: string;
description?: string;
startDateTime: string;
endDateTime: string;
timeZone?: string;
},
) {
try {
const timeZone =
parameters.timeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;

// Define the event payload (this structure follows Google Calendar API requirements)
const event = {
summary: parameters.summary,
location: parameters.location || "",
description: parameters.description || "",
start: {
dateTime: parameters.startDateTime,
timeZone, // Adjust this according to the user's time zone
},
end: {
dateTime: parameters.endDateTime,
timeZone, // Adjust this according to the user's time zone
},
};

// Make a POST request to insert the event
const response = await fetch(
"https://www.googleapis.com/calendar/v3/calendars/primary/events",
{
method: "POST",
headers: {
Authorization: `Bearer ${parameters.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(event),
},
);

if (!response.ok) {
throw new Error("Failed to create calendar event");
}

const newEvent = await response.json();
console.log("Event created:", newEvent);

return { status: "success", event: newEvent };
} catch (error) {
console.error("Error creating calendar event:", error);
}
}

export const actions: Record<string, Tool> = {
replaceSelectedText: {
name: "replaceSelectedText",
displayName: "Replace Selected Text",
description:
"Replace the user's current selected text content on the document with new text content.",
schema: {
type: "function",
function: {
name: "replaceSelectedText",
description:
"replaceSelectedText(newText: str) - Replace the user's current selected text content on the document with new text content.\\n\\n Args:\\n newText (str): New text content to replace the user's current selected text content.",
parameters: {
type: "object",
properties: {
newText: { type: "string" },
},
required: ["newText"],
},
},
},
implementation: replaceSelectedText,
},
appendTextToDocument: {
name: "appendTextToDocument",
displayName: "Append Text To Document",
description: "Append text content to the end of the document.",
schema: {
type: "function",
function: {
name: "appendTextToDocument",
description:
"appendTextToDocument(text: str) - Add some text content to the end of the document.\\n\\n Args:\\n text (str): Text content to be added to the end of the document.",
parameters: {
type: "object",
properties: {
text: { type: "string" },
},
required: ["text"],
},
},
},
implementation: appendTextToDocument,
},
createCalendarEvent: {
name: "createGoogleCalendarEvent",
displayName: "Create Google Calendar Event",
description: "Create a new event in the user's primary Google Calendar.",
schema: {
type: "function",
function: {
name: "createGoogleCalendarEvent",
description:
"createGoogleCalendarEvent(summary: string, startDateTime: string, endDateTime: string, location?: string, description?: string, timeZone?: string) - Creates a new event in the user's Google Calendar.\n\n Args:\n summary (str): Title of the event.\n startDateTime (str): Start date and time of the event (ISO 8601 format).\n endDateTime (str): End date and time of the event (ISO 8601 format).\n location (str, optional): Location of the event.\n description (str, optional): Description of the event.\n timeZone (str, optional): The timezone of the event.",
parameters: {
type: "object",
properties: {
summary: { type: "string" },
location: { type: "string", nullable: true },
description: { type: "string", nullable: true },
startDateTime: { type: "string" },
endDateTime: { type: "string" },
timeZone: { type: "string", nullable: true },
},
required: ["summary", "startDateTime", "endDateTime"],
},
},
},
implementation: createCalendarEvent,
},
};
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { retrievers } from "./retriever";
export { actions } from "./action";
export { tool, toolName, ToolName } from "./tool";
export { State } from "./state";
export * as retriever from "./retriever";
Expand Down
82 changes: 81 additions & 1 deletion src/retriever.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,88 @@
import { State } from "./state";
import { Tool } from "./tool";

export const getSelectedText = (state: State): string => {
export const getSelectedText = (state: State, parameters: {}): string => {
if (!state.currentSelection) {
return "";
}
return state.currentSelection.toString();
};

export async function getCalendarEvents(
state: State,
parameters: { token: string },
) {
try {
// API URL to fetch calendar events
const url =
"https://www.googleapis.com/calendar/v3/calendars/primary/events?maxResults=10&orderBy=startTime&singleEvents=true";

// Fetch the events from the user's primary Google Calendar
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${parameters.token}`,
"Content-Type": "application/json",
},
});

if (!response.ok) {
throw new Error("Failed to fetch calendar events");
}

const events = await response.json();

// Process and display events in popup (this is a basic example)
if (events.items) {
events.items.forEach((event: any) => {
const eventElement = document.createElement("div");
eventElement.textContent = `${event.summary} - ${event.start.dateTime || event.start.date}`;
document.body.appendChild(eventElement);
});
} else {
document.body.textContent = "No upcoming events found.";
}
} catch (error) {
console.error("Error fetching calendar events:", error);
document.body.textContent = "Error fetching events";
}
}

export const retrievers: Record<string, Tool> = {
getSelectedText: {
name: "getSelectedText",
displayName: "Get Selected Text",
description:
"Get the user's current selected text content on the document.",
schema: {
type: "function",
function: {
name: "getSelectedText",
description:
"getSelectedText() -> str - Get the user's current selected text content on the document, no parameter is needed.\\n\\n Returns:\\n str: The user's current selected text content on the document.",
parameters: { type: "object", properties: {}, required: [] },
},
},
implementation: getSelectedText,
},
getCalendarEvents: {
name: "getGoogleCalendarEvents",
displayName: "Get Google Calendar Events",
description:
"Fetch the user's upcoming events from their primary Google Calendar.",
schema: {
type: "function",
function: {
name: "getGoogleCalendarEvents",
description:
"getGoogleCalendarEvents(token: string) - Fetches up to 10 upcoming events from the user's Google Calendar.\n\n Returns:\n Array: List of upcoming events with event details.",
parameters: {
type: "object",
properties: {},
required: [],
},
},
},
implementation: getCalendarEvents,
},
};
41 changes: 13 additions & 28 deletions src/state.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,18 @@
export enum Scope {
DOM = "DOM",
}

export class State {
public currentSelection: Selection | undefined;
private scopeRegistration = {
DOM: this.registerDOM,
};

constructor(scopes: Scope[]) {
this.registerScopes(scopes);
}

private registerScopes(scopes: Scope[]) {
scopes.forEach((scope) => {
this.scopeRegistration[scope]();
});
}

private registerDOM() {
document.addEventListener("selectionchange", (): void => {
const selection = window.getSelection();
if (
selection &&
typeof selection.rangeCount !== "undefined" &&
selection.rangeCount > 0
) {
this.currentSelection = selection;
}
});
constructor() {
if (document) {
document.addEventListener("selectionchange", (): void => {
const selection = window.getSelection();
if (
selection &&
typeof selection.rangeCount !== "undefined" &&
selection.rangeCount > 0
) {
this.currentSelection = selection;
}
});
}
}
}
65 changes: 4 additions & 61 deletions src/tool.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,10 @@
import { appendTextToDocument, replaceSelectedText } from "./action";
import { getSelectedText } from "./retriever";
import { actions } from "./action";
import { retrievers } from "./retriever";
import { State } from "./state";

export const tool: Record<string, Tool> = {
getSelectedText: {
name: "getSelectedText",
displayName: "Get Selected Text",
description:
"Get the user's current selected text content on the document.",
schema: {
type: "function",
function: {
name: "getSelectedText",
description:
"getSelectedText() -> str - Get the user's current selected text content on the document, no parameter is needed.\\n\\n Returns:\\n str: The user's current selected text content on the document.",
parameters: { type: "object", properties: {}, required: [] },
},
},
implementation: getSelectedText,
},
replaceSelectedText: {
name: "replaceSelectedText",
displayName: "Replace Selected Text",
description:
"Replace the user's current selected text content on the document with new text content.",
schema: {
type: "function",
function: {
name: "replaceSelectedText",
description:
"replaceSelectedText(newText: str) - Replace the user's current selected text content on the document with new text content.\\n\\n Args:\\n newText (str): New text content to replace the user's current selected text content.",
parameters: {
type: "object",
properties: {
newText: { type: "string" },
},
required: ["newText"],
},
},
},
implementation: replaceSelectedText,
},
appendTextToDocument: {
name: "appendTextToDocument",
displayName: "Append Text To Document",
description: "Append text content to the end of the document.",
schema: {
type: "function",
function: {
name: "appendTextToDocument",
description:
"appendTextToDocument(text: str) - Add some text content to the end of the document.\\n\\n Args:\\n text (str): Text content to be added to the end of the document.",
parameters: {
type: "object",
properties: {
text: { type: "string" },
},
required: ["text"],
},
},
},
implementation: appendTextToDocument,
},
...retrievers,
...actions,
};

export const toolName = Object.keys(tool);
Expand Down

0 comments on commit a2839e4

Please sign in to comment.