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

Luisotee/messaging #24

Closed
wants to merge 75 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
5d1c846
Initial api
Luisotee Dec 3, 2024
2d7bc83
changed lc to llamaindex
Luisotee Dec 7, 2024
98a479b
Added Zep, refactored classifier
Luisotee Dec 8, 2024
626084a
Enhance classifier route to generate unique user IDs using UUID
Luisotee Dec 9, 2024
4ee6758
Refactor classifier functions to include conversation context and imp…
Luisotee Dec 10, 2024
a0336d9
Working whatsapp
Luisotee Dec 14, 2024
3017f48
Refactor imports in classifier, audio_utils, and memory modules for i…
Luisotee Dec 14, 2024
b9d04ec
Integrated AI api
Luisotee Dec 14, 2024
2c16eb5
Audio and image handling
Luisotee Dec 14, 2024
3bc40e5
Process response from crewai
Luisotee Dec 14, 2024
b8df9b1
Translate command responses and error messages to Portuguese for bett…
Luisotee Dec 14, 2024
af2debb
Linting
Luisotee Dec 15, 2024
1a0a711
Ignored some sherif linting rules
Luisotee Dec 15, 2024
b8b5393
Enhance logging and configuration for WhatsApp integration
Luisotee Dec 17, 2024
b0ae989
fix errors
Luisotee Dec 27, 2024
4447de4
Changed to messaging api between whatsapp and ai_api
Luisotee Dec 27, 2024
1568cba
Fix: audio messages not working
Luisotee Dec 29, 2024
e59c509
Removed get endpoint in messaging
Luisotee Dec 29, 2024
53ffbee
saving incoming and outgoing messages in supabase
Luisotee Dec 29, 2024
14b2664
Getting message history from supabase
Luisotee Dec 29, 2024
37ba821
trigger test
Luisotee Dec 30, 2024
d3125ee
Revert "trigger test"
Luisotee Jan 5, 2025
800e492
Add PORT configuration to .env.example and clean up message.ts format…
Luisotee Jan 6, 2025
98a73a1
Refactor classifier and grant routes to use API calls instead of crew…
Luisotee Jan 6, 2025
3ba2471
Mem0 initial cfg
Luisotee Jan 9, 2025
70b1436
Integrate Mem0ConversationManager for enhanced session management and…
Luisotee Jan 11, 2025
18fec06
Disable Mem0 memory manager and update user ID references in Supabase…
Luisotee Jan 11, 2025
e75841b
Add platform support to classifier and messaging routes
Luisotee Jan 11, 2025
2290309
Add Supabase ID handling for message processing and storage
Luisotee Jan 11, 2025
96e0e5f
Implement API call monitoring and logging for message processing
Luisotee Jan 16, 2025
638c460
Trigger working
Luisotee Jan 18, 2025
3f75e72
Add configuration management with TypeScript and Python support
Luisotee Jan 19, 2025
2660b85
Refactor WhatsApp constants and configuration for improved structure …
Luisotee Jan 19, 2025
ae3bf95
Refactor configuration schemas into separate types file for better or…
Luisotee Jan 19, 2025
210ade8
Update example configuration with local database settings and enable …
Luisotee Jan 19, 2025
a9e0463
Initial python config package
Luisotee Jan 21, 2025
c797d76
Python package cleanup
Luisotee Jan 21, 2025
9e71e0e
Replace some .env with config
Luisotee Jan 21, 2025
ccb0577
Update Astro configuration to use dynamic server port from config
Luisotee Jan 21, 2025
d9be9c4
Removed env from dashboard
Luisotee Jan 21, 2025
19711d3
Fix error in landingpage + remove .env
Luisotee Jan 21, 2025
4b4e4de
Fix: Variable error
Luisotee Jan 21, 2025
77989c7
Using config for message api
Luisotee Jan 22, 2025
5977a87
Use config in whatsapp
Luisotee Jan 22, 2025
ff86fea
Refactor: Remove .env.example and generate env file from config durin…
Luisotee Jan 22, 2025
a01eda5
Fixed trigger related issues
Luisotee Jan 22, 2025
0cee3f7
Refactor: Update Neo4j deployment to use environment variables and ad…
Luisotee Jan 22, 2025
1deaa50
Refactor: Update langtrace deployment to use a script for environment…
Luisotee Jan 22, 2025
60e659a
Refactor: Remove example environment files and update Supabase client…
Luisotee Jan 22, 2025
7a2d427
Refactor: Replace environment variables with centralized config for A…
Luisotee Jan 22, 2025
2399e27
Refactor: Remove unused example environment files from grant_plugin a…
Luisotee Jan 22, 2025
8b0f467
Refactor: Update example configuration with specific database credent…
Luisotee Jan 22, 2025
72938f8
Refactor: Add README documentation for Messaging API with features, i…
Luisotee Jan 22, 2025
9ade4ec
Lint
Luisotee Jan 22, 2025
ce1e95b
Refactor: Update GitHub Actions workflow to use Bun for dependency ma…
Luisotee Jan 22, 2025
8828845
Update stacks.yaml
Luisotee Jan 22, 2025
8c80db6
Refactor: Update GitHub Actions workflow to install UV and activate v…
Luisotee Jan 22, 2025
a8f6587
Refactor: Improve GitHub Actions workflow by reorganizing steps and a…
Luisotee Jan 22, 2025
29bdd3b
Refactor: Update CI workflow and configuration for PostgreSQL and Cli…
Luisotee Jan 22, 2025
6d860be
Refactor: Update CI workflow to set up UV and streamline dependency i…
Luisotee Jan 22, 2025
16e93d2
Refactor: Simplify CI workflow and update package versions; streamlin…
Luisotee Jan 22, 2025
26b7f45
Refactor: Update configuration for database ports and remove unused d…
Luisotee Jan 22, 2025
b5080b0
Refactor: Enhance CI workflow by skipping config packages during endp…
Luisotee Jan 22, 2025
96a14e2
Refactor: Update endpoint testing to skip config packages and improve…
Luisotee Jan 22, 2025
5161803
Fix lint errors, stack.yml
Luisotee Jan 23, 2025
0f5b47f
Fix whatsapp lint
Luisotee Jan 23, 2025
57343e5
Fixed analytics lint
Luisotee Jan 23, 2025
313f459
Lint supabase
Luisotee Jan 23, 2025
2a42d19
Build and lint errors
Luisotee Jan 23, 2025
5949a69
Enhance service readiness checks in CI workflow and add get-port script
Luisotee Jan 23, 2025
b56cf03
Fixed langtrace configuration
Luisotee Jan 23, 2025
6d76488
Remove redundant service readiness checks from deployment workflow
Luisotee Jan 23, 2025
9bdac0c
Add special handling for Neo4j in get-port script
Luisotee Jan 23, 2025
fc9288a
Enhance service endpoint testing with detailed logging and network ch…
Luisotee Jan 23, 2025
5abc0d6
Add health check for containers in deployment workflow
Luisotee Jan 23, 2025
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
Prev Previous commit
Next Next commit
Refactor: Replace environment variables with centralized config for A…
…PI keys and AI model
Luisotee committed Jan 22, 2025
commit 7a2d4279f90279008d76a01a1aa45c71a744bf34
5 changes: 0 additions & 5 deletions packages/simulator/.env.example

This file was deleted.

224 changes: 105 additions & 119 deletions packages/simulator/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,153 +1,139 @@
import { createHash } from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import fs from "node:fs";
import { createHash } from "node:crypto";
import Cerebras from "@cerebras/cerebras_cloud_sdk";
import { Groq } from "groq-sdk";
import { config } from "@eda/config";
import { logger } from "@eda/logger";
import { Groq } from "groq-sdk";
import type { Messages } from "./types";

const DEFAULT_APP_FOLDER = path.join(
os.homedir(),
".earth-defenders-assistant",
os.homedir(),
".earth-defenders-assistant",
);
const cerebrasKey = process.env.CEREBRAS_API_KEY;
const groqKey = process.env.GROQ_API_KEY;
let client: Cerebras | Groq;
let defaultModel: string;
if (cerebrasKey) {
client = new Cerebras({
apiKey: cerebrasKey,
});
defaultModel = "llama3.1-8b";
} else if (groqKey) {
client = new Groq({
apiKey: groqKey,
});
defaultModel = "llama-3.1-8b-instant";
} else {
throw new Error(
"Neither CEREBRAS_API_KEY nor GROQ_API_KEY is set in the environment.",
);
}

const MODEL = process.env.AI_MODEL || defaultModel;
// Replace environment variables with config
const groqKey = config.api_keys.groq;

// Initialize client with Groq by default
const client = new Groq({
apiKey: groqKey,
});

// Use the AI model from config
const MODEL = config.ai_models.standard.model;

interface CachedItem<T> {
data: T;
timestamp: number;
data: T;
timestamp: number;
}

interface CacheOptions {
duration?: number; // Duration in milliseconds
subfolder?: string; // Subfolder within DEFAULT_APP_FOLDER
duration?: number; // Duration in milliseconds
subfolder?: string; // Subfolder within DEFAULT_APP_FOLDER
}

// Default cache duration is 1 hour
const DEFAULT_CACHE_DURATION = 60 * 60 * 1000;

function createCache<T>(options: CacheOptions = {}) {
const {
duration = DEFAULT_CACHE_DURATION,
subfolder = 'simulator/.cache'
} = options;

const cacheDir = path.join(DEFAULT_APP_FOLDER, subfolder);
logger.debug(`Cache directory: ${cacheDir}`);

async function get(key: string): Promise<T | null> {
try {
const cacheFile = path.join(cacheDir, `${key}.json`);
logger.debug(`Attempting to read cache file: ${cacheFile}`);
const data = await fs.promises.readFile(cacheFile, 'utf8');
const cached = JSON.parse(data) as CachedItem<T>;

if (Date.now() - cached.timestamp < duration) {
logger.debug(`Cache hit for key: ${key}`);
return cached.data;
}
logger.debug(`Cache expired for key: ${key}`);
return null;
} catch (error) {
console.error(error);
logger.debug(`Cache miss for key: ${key}`, { error });
return null;
}
}
const { duration = DEFAULT_CACHE_DURATION, subfolder = "simulator/.cache" } =
options;

const cacheDir = path.join(DEFAULT_APP_FOLDER, subfolder);
logger.debug(`Cache directory: ${cacheDir}`);

async function set(key: string, data: T): Promise<void> {
const item: CachedItem<T> = {
data,
timestamp: Date.now()
};

logger.debug(`Creating cache directory: ${cacheDir}`);
await fs.promises.mkdir(cacheDir, { recursive: true });

const cacheFile = path.join(cacheDir, `${key}.json`);
logger.debug(`Writing to cache file: ${cacheFile}`);
await fs.promises.writeFile(
cacheFile,
JSON.stringify(item)
);
async function get(key: string): Promise<T | null> {
try {
const cacheFile = path.join(cacheDir, `${key}.json`);
logger.debug(`Attempting to read cache file: ${cacheFile}`);
const data = await fs.promises.readFile(cacheFile, "utf8");
const cached = JSON.parse(data) as CachedItem<T>;

if (Date.now() - cached.timestamp < duration) {
logger.debug(`Cache hit for key: ${key}`);
return cached.data;
}
logger.debug(`Cache expired for key: ${key}`);
return null;
} catch (error) {
console.error(error);
logger.debug(`Cache miss for key: ${key}`, { error });
return null;
}
}

async function set(key: string, data: T): Promise<void> {
const item: CachedItem<T> = {
data,
timestamp: Date.now(),
};

logger.debug(`Creating cache directory: ${cacheDir}`);
await fs.promises.mkdir(cacheDir, { recursive: true });

const cacheFile = path.join(cacheDir, `${key}.json`);
logger.debug(`Writing to cache file: ${cacheFile}`);
await fs.promises.writeFile(cacheFile, JSON.stringify(item));
}

return { get, set };
return { get, set };
}

// Create cache instance for message generation
const messageCache = createCache<{
id: string;
timestamp: number;
[key: string]: unknown;
id: string;
timestamp: number;
[key: string]: unknown;
}>();

export async function generateWhatsAppMessage(messages: Messages) {
const cacheKey = createHash('md5')
.update(JSON.stringify(messages))
.digest('hex');

logger.debug(`Generated cache key: ${cacheKey}`);
const cacheKey = createHash("md5")
.update(JSON.stringify(messages))
.digest("hex");

logger.debug(`Generated cache key: ${cacheKey}`);

try {
// Try to get from cache first
logger.debug("Attempting to retrieve from cache");
const cached = await messageCache.get(cacheKey);
if (cached) {
logger.debug("Found message in cache");
return cached;
}

try {
// Try to get from cache first
logger.debug('Attempting to retrieve from cache');
const cached = await messageCache.get(cacheKey);
if (cached) {
logger.debug('Found message in cache');
return cached;
}

logger.debug('Cache miss, generating new message');
const params = {
messages,
model: MODEL,
response_format: {
type: "json_object",
},
};

logger.debug('Calling AI service', { model: MODEL });
const generatedMessage = await client.chat.completions.create(params);
if (!generatedMessage.choices[0]?.message?.content) {
logger.error('Invalid response from AI service');
throw new Error("Invalid response from OpenAI");
}

const content = generatedMessage.choices[0].message.content;
const payload = {
...JSON.parse(content),
id: crypto.randomUUID(),
timestamp: Date.now(),
};

logger.debug('Caching generated message');
await messageCache.set(cacheKey, payload);

return payload;
} catch (error) {
logger.error("Error in message generation:", { error });
throw error;
logger.debug("Cache miss, generating new message");
const params: ChatCompletionCreateParamsNonStreaming = {
messages: messages.map((msg) => ({
role: msg.role,
content: msg.content,
})),
model: MODEL,
response_format: { type: "json_object" },
};

logger.debug("Calling AI service", { model: MODEL });
const generatedMessage = await client.chat.completions.create(params);
if (!generatedMessage.choices[0]?.message?.content) {
logger.error("Invalid response from AI service");
throw new Error("Invalid response from OpenAI");
}

const content = generatedMessage.choices[0].message.content;
const payload = {
...JSON.parse(content),
id: crypto.randomUUID(),
timestamp: Date.now(),
};

logger.debug("Caching generated message");
await messageCache.set(cacheKey, payload);

return payload;
} catch (error) {
logger.error("Error in message generation:", { error });
throw error;
}
}