Skip to content

Commit

Permalink
refactor(Samantha): Transform project from assistant to Samantha
Browse files Browse the repository at this point in the history
- Strip away feature for HomeAssistant AI Summaries
- Update README
  • Loading branch information
thomashermine committed Nov 15, 2023
1 parent 07c7988 commit c093f43
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 344 deletions.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# assistant
# Samantha — Connecting OpenAI Assistant with my digital ecosystem

A NodeJS app bridging the gap between openai/chatgpt and various tools : Home-Assistant, calendar, emails
[Part of the OpenAI Projects of @thomashermine](https://thomashermine.notion.site/OpenAI-Projects-30b950137c124b3c8fd0622007a889f0?pvs=4)

Samantha is a NodeJS app connecting OpenAI custom Asssistants with various third-party services : Google Calendar, Gmail/Google Mail, Slack, Spotify, HomeAssistant,...
It can be invoked via an iOS Shortcuts, or use as an HomeAssistant Conversation Agent.

## Architecture

```mermaid
sequenceDiagram
participant EndUser as End User
participant NodeJSApp as Samantha NodeJS App
participant OpenAI as OpenAI Custom Assistant
EndUser->>NodeJSApp: HTTP POST message
NodeJSApp->>OpenAI: Send request to OpenAI
OpenAI->>NodeJSApp: Respond with actions to perform
NodeJSApp->>NodeJSApp: Perform Actions
NodeJSApp->>OpenAI: Send completion results of actions
OpenAI->>NodeJSApp: Respond with message
NodeJSApp->>EndUser: HTTP Response with message
```

## Documentation

[Complete Documentation](https://thomashermine.notion.site/Samantha-Connecting-OpenAI-Assistant-with-my-digital-ecosystem-202bff7ef3054ca5a5ceb9432e3e06d6?pvs=4)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"dependencies": {
"@esbuild-kit/cjs-loader": "^2.4.4",
"@google-cloud/local-auth": "2.1.0",
"body-parser": "^1.20.2",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"googleapis": "105",
Expand Down
145 changes: 0 additions & 145 deletions src/_helpers/summaries/devices.ts

This file was deleted.

48 changes: 0 additions & 48 deletions src/_helpers/summaries/job.ts

This file was deleted.

29 changes: 3 additions & 26 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
export const { NODE_ENV = "development" } = process.env;

// Default values depending on the environment
const DEFAULT_HOME_ASSISTANT_HOST =
NODE_ENV === "production"
? "http://supervisor/core"
: "http://homeassistant.local";
const DEFAULT_HOME_ASSISTANT_PORT = NODE_ENV === "production" ? "80" : "8123";
const DEFAULT_HOME_ASSISTANT_TOKEN = process.env.SUPERVISOR_TOKEN;
const DEFAULT_UPDATE_INTERVAL = NODE_ENV === "production" ? "3600000" : "10000"; // Every hour in production, every 5 seconds in dev

// =====================================================================================================================
// Config
// =====================================================================================================================
Expand All @@ -27,15 +18,9 @@ export const {
OPENAI_USER_FIRSTNAME,

// 3rd party : Home Assistant
HOME_ASSISTANT_HOST = DEFAULT_HOME_ASSISTANT_HOST,
HOME_ASSISTANT_PORT_RAW = DEFAULT_HOME_ASSISTANT_PORT,
HOME_ASSISTANT_TOKEN = DEFAULT_HOME_ASSISTANT_TOKEN,

HOME_ASSISTANT_SUMMARIES_UPDATE_INTERVAL:
HOME_ASSISTANT_SUMMARIES_UPDATE_INTERVAL_RAW = DEFAULT_UPDATE_INTERVAL,
HOME_ASSISTANT_SUMMARIES_DEVICE_ENTITY_PREFIX = "summary_device_", // Use for the summary entities we will create. will be followed by the device name
HOME_ASSISTANT_SUMMARIES_DEVICES_ENTITIES:
HOME_ASSISTANT_SUMMARIES_DEVICES_ENTITIES_RAW = "hall_tablet,iphone_thomas,iphone_caroline,macbookpro_thomas,roborock,octoprint,litterbox,toothbrush,pet_feeder,pet_fountain",
HOME_ASSISTANT_HOST,
HOME_ASSISTANT_PORT_RAW,
HOME_ASSISTANT_TOKEN,

HOME_ASSISTANT_MEDIA_PLAYER,

Expand All @@ -44,7 +29,6 @@ export const {
GOOGLE_CLIENT_SECRET,
GOOGLE_CLIENT_REDIRECT_URL = "http://localhost:80/oauth2callback",
GOOGLE_CLIENT_TOKEN_FILE_PATH = "token.json",
GOOGLE_CLIENT_CREDENTIALS_FILE_PATH = "credentials.json",
GOOGLE_CLIENT_SCOPES:
GOOGLE_CLIENT_SCOPES_RAW = "https://www.googleapis.com/auth/calendar.events", // Comma separated list of scopes

Expand All @@ -53,12 +37,5 @@ export const {

// Config parsing
// =====================================================================================================================
export const HOME_ASSISTANT_SUMMARIES_UPDATE_INTERVAL = parseInt(
HOME_ASSISTANT_SUMMARIES_UPDATE_INTERVAL_RAW,
10,
);
export const HOME_ASSISTANT_PORT = parseInt(HOME_ASSISTANT_PORT_RAW, 10);

export const HOME_ASSISTANT_SUMMARIES_DEVICES_ENTITIES =
HOME_ASSISTANT_SUMMARIES_DEVICES_ENTITIES_RAW.split(",");
export const GOOGLE_CLIENT_SCOPES = GOOGLE_CLIENT_SCOPES_RAW.split(",");
39 changes: 24 additions & 15 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
import "dotenv/config";

import { log } from "./_helpers/logs";
import { sleep } from "./_helpers/sleep";
import { init } from "./init";
import { summariesJob } from "./_helpers/summaries/job";
import { HOME_ASSISTANT_SUMMARIES_UPDATE_INTERVAL } from "./config";

import { launchAssistantConversation } from "./openai/assistant";
import {
createAssistantThread,
handleUserMessageOnThread,
} from "./openai/assistant";

async function main() {
// Init
// ===================================================================================================================
log("app", "info", "Starting up...");
const { ha, openai } = await init();
const { server } = await init();
log("app", "info", "Initialized.");

// Start the OpenAI Assistant Conversation
// ===================================================================================================================
await launchAssistantConversation("agenda");
const { threadId, assistantId, instructions } =
await createAssistantThread("agenda");

// Jobs
// ===================================================================================================================
// eslint-disable-next-line no-constant-condition
while (true) {
// TODO: Use a pub-sub model instead to update everytime one of the entities we summarize is updated
await summariesJob(ha, openai);
await sleep(HOME_ASSISTANT_SUMMARIES_UPDATE_INTERVAL);
}
server.post("/assistant/agenda/message", async (req, res) => {
console.log("req.body", req.body);
const { message } = req.body;
log("app", "debug", `Received message ${message}`);
const response = await handleUserMessageOnThread(
assistantId,
threadId,
message,
instructions,
);
res.status(201).json({ message: response });
});
log(
"app",
"info",
"Ready to make conversation with assistant. Send a POST request to /assistant/agenda/message with a message in the body to start the conversation.",
);
}

main();
10 changes: 3 additions & 7 deletions src/init.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "dotenv/config";
import express from "express";
import bodyParser from "body-parser";
import homeassistant from "homeassistant";
import OpenAI from "openai";
import prompt from "prompt";
Expand Down Expand Up @@ -36,7 +37,6 @@ export async function init(): Promise<{
}> {
// Home Assistant
// ===================================================================================================================
console.log(HOME_ASSISTANT_HOST, HOME_ASSISTANT_PORT, HOME_ASSISTANT_TOKEN);
const ha: homeassistant = new homeassistant({
host: HOME_ASSISTANT_HOST,
port: HOME_ASSISTANT_PORT,
Expand All @@ -57,12 +57,12 @@ export async function init(): Promise<{
// Express Server
// ===================================================================================================================
const server = express();
server.use(bodyParser());

// Register routes
googleHandleAuthCallback(server);

// Express Server
// ===================================================================================================================
// Listen
server.listen(PORT, (err) => {
if (err) throw err;
log("server", "info", `Listening on port ${PORT}`);
Expand All @@ -74,9 +74,5 @@ export async function init(): Promise<{
const google = await googleAuthorize();
googleSetClient(google);

// Prompt
// ===================================================================================================================
prompt.start();

return { ha, openai, google, server, prompt };
}
Loading

0 comments on commit c093f43

Please sign in to comment.