Skip to content

Adding Cacheing - #48

Open
MGoradeCodes wants to merge 3 commits into
open-devhub:mainfrom
MGoradeCodes:main
Open

MGoradeCodes wants to merge 3 commits into
open-devhub:mainfrom
MGoradeCodes:main

Conversation

@MGoradeCodes

@MGoradeCodes MGoradeCodes commented Sep 4, 2026 •

Copy link
Copy Markdown

🚀 Feature: Added SQLite-Powered Semantic Caching for AI Answers

I have added a few comments to code if its easier to understand

Logic is kinda good and bad
since I stripped it off an another project : )

This PR implements a localized, lightweight Semantic Cache for the askai engine to drastically optimize latency, token usage, and API billing overhead.

IMPORTANT LOG:

Beetle AI is failing with 403: team is blocked: missing payment method. This appears to be an issue with the Beetle AI integration rather than the PR. (Look Into This Please)

💡 How It Works

Instead of checking for exact word-by-word matches, incoming single prompts are mapped using OpenAI's text-embedding-3-small vectors. We leverage a high-speed, local SQLite file embedded with the sqlite-vec binary extension to compute mathematical vector distance math locally.

  1. Standalone Query Check: When a user initializes a prompt (with no active continuous chat context or reference thread metadata), the system performs a localized search check.
  2. Threshold Match: If a historical query shares >90% semantic similarity (distance < 0.15), the engine serves the stored response instantly.
  3. Conversational Safety Isolation: If the user is in the middle of a continuous conversation history chain or replying to an active context block (ctx), the cache bypasses entirely to keep interaction flows coherent.

🛠️ Dependencies Added

  • better-sqlite3: Standard node driver utility wrapper.
  • sqlite-vec: Ultra-lightweight native vector math addition for SQLite virtual tables (vec0).

🔒 Safety Precautions Took

  • Appended ai_cache.db and its relative workspace journal strings to .gitignore to prevent any local data logs from accidental upstream tracking.
  • Wrapped database initialization to run silently inside Bun runtimes.

READ MEEE.!!!!!!!!!

@devhub-bot

devhub-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Some commit messages in this PR do not follow the Conventional Commits specification.

Expected format: type(scope): description

📋 Invalid commit messages
  • PR title does not follow Conventional Commits: Adding Cacheing
  • Commit f39d6e9 does not follow Conventional Commits: Adding Cacheing

Tip

Valid types:
feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

Note

This is just a friendly reminder and will not block the PR from being merged.

@beetle-ai

beetle-ai Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary by Beetle

This PR introduces a semantic caching layer to the RAEL Discord bot's AI command system. The implementation adds intelligent query caching using vector embeddings and SQLite, allowing the bot to recognize and reuse answers for semantically similar questions without making redundant API calls. This optimization reduces latency and API costs for frequently asked or similar questions.

📁 File Changes Summary (Consolidated across all commits):

File Status Changes Description
package.json Modified +4/-0 Added dependencies: better-sqlite3 (SQLite driver), openai (for embeddings), sqlite-vec (vector search), and @types/better-sqlite3 (TypeScript types)
package-lock.json Added +2035/-0 Complete lock file with all resolved dependency versions and transitive dependencies
.gitignore Modified +2/-0 Added cache database files: ai_cache.db and ai_cache.db-journal to prevent committing local cache data
src/utils/Cache.ts Added +65/-0 New semantic cache utility class using OpenAI embeddings and SQLite vector search for intelligent query matching
src/commands/ai/askai.ts Modified +26/-0 Integrated semantic cache into the ask AI command; checks cache before API calls and stores new queries for future reuse

Total Changes: 5 files changed, +2132 additions, -0 deletions

🗺️ Walkthrough:

graph TD
UserQuery["User Asks Question"]
CheckHistory{"Has Chat History?"}
CheckCache["Query Semantic Cache"]
CacheHit{"Similar Answer Found?"}
APICall["Call AI API"]
GetEmbedding["Generate Query Embedding OpenAI text-embedding-3-small"]
VectorSearch["Search SQLite Vector DB Cosine Distance < 0.15"]
StoreCache["Store Question + Answer + Embedding Vector"]
ReturnAnswer["Return Answer to User"]
UserQuery --> CheckHistory
CheckHistory -->|Yes| APICall
CheckHistory -->|No| CheckCache
CheckCache --> GetEmbedding
GetEmbedding --> VectorSearch
VectorSearch --> CacheHit
CacheHit -->|Yes| ReturnAnswer
CacheHit -->|No| APICall
APICall --> StoreCache
StoreCache --> ReturnAnswer
Loading

🎯 Key Changes:

  • Semantic Cache Implementation: New Cache class in src/utils/Cache.ts uses OpenAI's text-embedding-3-small model to convert questions into 1536-dimensional vectors, enabling similarity-based matching rather than exact string matching.
  • SQLite Vector Database: Leverages sqlite-vec extension to create a virtual table (semantic_cache) that stores questions, answers, and their vector embeddings, with efficient cosine distance-based similarity search.
  • Cache Integration in Ask Command: Modified src/commands/ai/askai.ts to check the semantic cache before making API calls. If a semantically similar question exists (cosine distance < 0.15), the cached answer is returned immediately.
  • Dependency Additions: Added better-sqlite3 for database operations, openai for embedding generation, and sqlite-vec for vector search capabilities.
  • Cache Persistence: Database files (ai_cache.db, ai_cache.db-journal) are gitignored to prevent committing local cache state.

📊 Impact Assessment:

  • Security:
  • OpenAI API key exposure risk if .env is not properly secured (embeddings require API calls)
  • SQLite database stored locally without encryption; sensitive cached data could be exposed if database file is compromised
  • No input validation on cached answers; malicious data stored in cache could be served to users
  • Performance:
  • Positive: Significant latency reduction for repeated or similar queries (cache lookup is near-instant vs. API call latency)
  • Positive: Reduced API costs by avoiding redundant calls for semantically similar questions
  • Concern: Embedding generation adds ~100-500ms overhead per new unique query; this is acceptable as it's only done once per unique question
  • Concern: Vector search with cosine distance calculation could become slow as cache grows; no pagination or cleanup mechanism implemented
  • Maintainability:
  • Cache class is well-isolated and reusable, but lacks error handling for database corruption or OpenAI API failures
  • Hardcoded similarity threshold (0.15) and embedding model name should be configurable
  • No logging for cache statistics (hit rate, size, etc.) to monitor effectiveness
  • Database schema is simple but lacks indexes for performance optimization at scale
  • Testing:
  • No test coverage for cache functionality
  • No validation of embedding quality or similarity threshold appropriateness
  • Edge cases not handled: empty cache, database file corruption, OpenAI API rate limits, concurrent access to SQLite database
  • Cache invalidation strategy is missing; old/stale answers could persist indefinitely
⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only. — Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

Comment thread src/commands/ai/askai.ts Resolved
Comment thread src/commands/ai/askai.ts
const history = getContext(userId);

const hasHistory = Array.isArray(history) && history.length > 0;
if (!hasHistory && !ctx) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undefined variable ctx in condition. Line 75 checks !ctx but ctx is not defined in the function scope. This will cause a ReferenceError at runtime. The condition logic appears incomplete or references a parameter that doesn't exist.

Confidence: 4/5

Suggested Fix

Verify what this condition should actually check. Based on the context, it seems like you want to check if there's no existing context for the user. The condition should likely be:

Suggested change
if (!hasHistory && !ctx) {
if (!hasHistory) {

Or if you need to check for a specific parameter, ensure that parameter is defined in the function signature and passed correctly.

Comment thread src/commands/ai/askai.ts Resolved
Comment thread src/commands/ai/askai.ts
Comment on lines 74 to +109
@@ -84,6 +104,12 @@ export default {
return;
}


if (!hasHistory && !ctx) {
await semanticCache.set(question, result.text);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Race condition in cache operations. Multiple concurrent requests for the same question could all miss the cache (line 76) and make duplicate API calls, then all attempt to store results (line 109). Without locking or deduplication, this defeats the purpose of caching and wastes API calls.

Confidence: 4/5

Suggested Fix

Implement request deduplication by tracking in-flight requests:

Suggested change
const inFlightRequests = new Map<string, Promise<string>>();
// In cache retrieval section:
if (!hasHistory) {
const cachedAnswer = await semanticCache.get(question);
if (cachedAnswer) {
addToContext(userId, "user", question);
addToContext(userId, "assistant", cachedAnswer);
await message.reply({ content: pretty(cachedAnswer) });
return;
}
// Check if request is already in flight
if (inFlightRequests.has(question)) {
const result = await inFlightRequests.get(question);
addToContext(userId, "user", question);
addToContext(userId, "assistant", result);
await message.reply({ content: pretty(result) });
return;
}

This prevents duplicate API calls for the same question when multiple requests arrive simultaneously.

📍 This suggestion applies to lines 74-109

Comment thread src/utils/Cache.ts Outdated
Comment on lines +9 to +20
constructor() {
this.db = new Database('ai_cache.db');
sqliteVec.load(this.db);

// Simplification: One single table for everything (text data + vector fields combined)
this.db.prepare(`
CREATE VIRTUAL TABLE IF NOT EXISTS semantic_cache USING vec0(
question TEXT,
answer TEXT,
question_vector float[1536]
);
`).run();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Database connection is never closed, causing resource leaks. The constructor initializes a SQLite database connection but there's no destructor, close method, or cleanup mechanism. In a long-running Discord bot, this connection will accumulate and eventually exhaust system resources.

Confidence: 4/5

Suggested Fix

Add a close method and ensure it's called when the bot shuts down:

Suggested change
constructor() {
this.db = new Database('ai_cache.db');
sqliteVec.load(this.db);
// Simplification: One single table for everything (text data + vector fields combined)
this.db.prepare(`
CREATE VIRTUAL TABLE IF NOT EXISTS semantic_cache USING vec0(
question TEXT,
answer TEXT,
question_vector float[1536]
);
`).run();
export class Cache {
private db: Database.Database;
private openai = new OpenAI();
constructor() {
this.db = new Database('ai_cache.db');
sqliteVec.load(this.db);
this.db.prepare(`
CREATE VIRTUAL TABLE IF NOT EXISTS semantic_cache USING vec0(
question TEXT,
answer TEXT,
question_vector float[1536]
);
`).run();
}
public close(): void {
this.db.close();
}

Then ensure cache.close() is called in your bot's shutdown handler.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/utils/Cache.ts, add a public close() method that calls this.db.close() to properly clean up the database connection. Then update the bot's shutdown handler to call cache.close() when the bot is shutting down to prevent resource leaks.

📍 This suggestion applies to lines 9-20

Comment thread src/utils/Cache.ts Outdated
Comment on lines +42 to +46
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
WHERE vec_distance_cosine(question_vector, ?) < 0.15
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(Buffer.from(vector.buffer), Buffer.from(vector.buffer)) as { answer: string } | undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant vector distance calculation reduces performance. The query calculates vec_distance_cosine(question_vector, ?) twice - once in the WHERE clause (line 44) and again in the ORDER BY clause (line 45). This causes the distance to be computed twice for every row, wasting CPU cycles.

Confidence: 4/5

Suggested Fix

Restructure the query to calculate distance only once using a subquery or CTE:

Suggested change
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
WHERE vec_distance_cosine(question_vector, ?) < 0.15
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(Buffer.from(vector.buffer), Buffer.from(vector.buffer)) as { answer: string } | undefined;
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
WHERE vec_distance_cosine(question_vector, ?) < 0.15
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(Buffer.from(vector.buffer), Buffer.from(vector.buffer)) as { answer: string } | undefined;

Actually, better-sqlite3 doesn't support CTEs efficiently. Instead, accept that SQLite will optimize this, but consider if the threshold check is necessary - you could remove the WHERE clause and just ORDER BY distance, then check the result:

Suggested change
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
WHERE vec_distance_cosine(question_vector, ?) < 0.15
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(Buffer.from(vector.buffer), Buffer.from(vector.buffer)) as { answer: string } | undefined;
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(Buffer.from(vector.buffer)) as { answer: string } | undefined;
if (match && this.calculateDistance(vector) < 0.15) {
return match.answer;
}
return null;

Or keep the current approach if SQLite's query optimizer handles it well. Monitor query performance in production.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/utils/Cache.ts line 42-46, the vec_distance_cosine function is called twice in the same query (WHERE and ORDER BY clauses). Optimize this by either removing the WHERE clause threshold check and validating the distance in code, or by restructuring the query to calculate distance only once. Benchmark the performance impact.

📍 This suggestion applies to lines 42-46

Comment thread src/utils/Cache.ts
Comment on lines +39 to +52
public async get(userQuestion: string): Promise<string | null> {
try {
const vector = await this.getEmbedding(userQuestion);
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
WHERE vec_distance_cosine(question_vector, ?) < 0.15
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(Buffer.from(vector.buffer), Buffer.from(vector.buffer)) as { answer: string } | undefined;

return match ? match.answer : null;
} catch {
return null;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache lacks user isolation, enabling cross-user data leakage. The get() method retrieves cached answers globally without any user context. Combined with the global cache instance in askai.ts, this means cached answers from one user could be served to another user, creating a privacy/security vulnerability where sensitive information leaks between users.

Confidence: 5/5

Suggested Fix

Modify the cache to accept and store user context:

Suggested change
public async get(userQuestion: string): Promise<string | null> {
try {
const vector = await this.getEmbedding(userQuestion);
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
WHERE vec_distance_cosine(question_vector, ?) < 0.15
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(Buffer.from(vector.buffer), Buffer.from(vector.buffer)) as { answer: string } | undefined;
return match ? match.answer : null;
} catch {
return null;
}
}
public async get(userId: string, userQuestion: string): Promise<string | null> {
try {
const vector = await this.getEmbedding(userQuestion);
const match = this.db.prepare(`
SELECT answer FROM semantic_cache
WHERE user_id = ? AND vec_distance_cosine(question_vector, ?) < 0.15
ORDER BY vec_distance_cosine(question_vector, ?) ASC LIMIT 1;
`).get(userId, Buffer.from(vector.buffer), Buffer.from(vector.buffer)) as { answer: string } | undefined;
return match ? match.answer : null;
} catch {
return null;
}

Also update the table schema to include user_id and the set() method to accept and store userId.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/utils/Cache.ts, add user isolation to the cache. Modify the semantic_cache table schema to include a user_id column, update both get() and set() methods to accept userId as a parameter, and filter all queries by user_id to prevent cross-user data leakage.

📍 This suggestion applies to lines 39-52

Comment thread src/utils/Cache.ts
Comment on lines +49 to +51
} catch {
return null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silent error suppression hides critical failures. The get() method catches all errors and returns null without logging. If the database is corrupted, the embedding API fails, or other critical errors occur, they're silently ignored, making debugging difficult and potentially causing the cache to fail silently in production.

Confidence: 4/5

Suggested Fix

Log errors appropriately while still handling them gracefully:

Suggested change
} catch {
return null;
}
} catch (err) {
console.error('[Cache Get Failed]:', err);
return null;
}

This matches the error handling pattern already used in the set() method (line 62).

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/utils/Cache.ts line 49-51, add error logging to the catch block in the get() method, similar to the logging in the set() method at line 62. This will help with debugging cache failures in production.

📍 This suggestion applies to lines 49-51

@goldstac

goldstac commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

hey gng

@goldstac

goldstac commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@MGoradeCodes

@goldstac

goldstac commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

beetle-ai did some checking then it like found some stuff lol it gives us prompts to tell ai to fix mind if you fix

@goldstac

goldstac commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@calebephrem

@goldstac

goldstac commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

super linter :sus
lets see

@devhub-bot

devhub-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Linting checks did not pass for this PR.

Run: View logs

Tip

Make sure to check the following before pushing:

  • code formatting issues
  • code quality / linting errors
  • unused or broken imports
  • syntax or type issues (if applicable)
  • secret leaks or exposed credentials
  • security / dependency vulnerabilities
  • invalid YAML / JSON / config files

Then fix the issues, commit, and push again.

Note

This is just a friendly reminder and will not block the PR from being merged.

@goldstac

goldstac commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

linting failed ic

@MGoradeCodes

Copy link
Copy Markdown
Author

What changes to do you want me to make?

@beetle-ai

beetle-ai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Beetle is reviewing the changes — Let’s see what you’ve done!

🔍 Under Review

Commits (1)
  • 1269164 — Fixed Formating and Linter Issues : ) (MGoradeCodes)
Files Changed (2)
  • src/commands/ai/askai.ts
  • src/utils/Cache.ts

⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only. — Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

@beetle-ai

beetle-ai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Beetle is reviewing the changes — Let’s see what you’ve done!

🔍 Under Review

Commits (1)
  • 339467f — Fixed Spelling Error Which Caused Errors (MGoradeCodes)
Files Changed (1)
  • src/commands/ai/askai.ts

⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only. — Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

@devhub-bot

devhub-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Warning

Linting checks did not pass for this PR.

Run: View logs

Tip

Make sure to check the following before pushing:

  • code formatting issues
  • code quality / linting errors
  • unused or broken imports
  • syntax or type issues (if applicable)
  • secret leaks or exposed credentials
  • security / dependency vulnerabilities
  • invalid YAML / JSON / config files

Then fix the issues, commit, and push again.

Note

This is just a friendly reminder and will not block the PR from being merged.

@calebephrem calebephrem added the invalid This doesn't seem right label Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

invalid This doesn't seem right

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants