Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
fbfe676
Hackbot init
ReehalS Jan 4, 2026
28de7b2
Lint fixes
ReehalS Jan 30, 2026
b3627aa
Update to google also
ReehalS Jan 30, 2026
abcbcf8
Update hackbotSeed.mjs
ReehalS Feb 27, 2026
dd74403
Merge branch 'main' into hackbot
ReehalS Feb 27, 2026
c2a0545
Update hackbot knowledge to have admin UI and cleanup providers
ReehalS Feb 27, 2026
8bd3f9a
Make event fetching into a tool call rather than embedded into knowledge
ReehalS Feb 27, 2026
bfc11fd
Import knowledge via a doc
ReehalS Feb 27, 2026
8b48466
Add page aware bot
ReehalS Feb 27, 2026
21d31eb
Add ID to each section
ReehalS Feb 27, 2026
3a5e740
Add events, user info, and event cards
ReehalS Feb 27, 2026
fe2c60c
Hackbot UI
ReehalS Feb 27, 2026
00db8da
Decompose Hub Admin page
ReehalS Feb 27, 2026
8d4b8c3
Improve Hackbot functionality around meals, events, user preferences.
ReehalS Feb 27, 2026
9b6b4c5
Move Hackbot types
ReehalS Feb 27, 2026
342a19f
Decompose functions further
ReehalS Feb 27, 2026
5ed7021
Remove hackbot history on logout
ReehalS Feb 27, 2026
ab1d62d
Lint fixes
ReehalS Feb 27, 2026
23a2714
Merge branch 'main' into hackbot
ReehalS Feb 28, 2026
8c88210
Hackbot Vector search Fallback, prompt improvement, events sorting
ReehalS Feb 28, 2026
bf64f9a
Add starterkit info and scroll to page for starterkit
ReehalS Feb 28, 2026
6a1c433
Hackbot Improvements
ReehalS Mar 1, 2026
5096437
Delete old knowledge and improve current base set.
ReehalS Mar 1, 2026
7bbe5a5
Update vercel ai sdk version to v6 and model to gpt-5-mini
ReehalS Mar 1, 2026
924e6e7
Improve prompt caching, link+event filtering, usage metrics
ReehalS Mar 1, 2026
46f51d8
lint fixes
ReehalS Mar 1, 2026
beef356
Merge branch 'main' into hackbot
ReehalS Mar 12, 2026
0c28c30
revert changes to whats on main
ReehalS Mar 12, 2026
8253f30
Remove unused static route
ReehalS Mar 12, 2026
6e206db
Fix mentor-dir-help, times being pulled
ReehalS Mar 12, 2026
520eb2b
Prompt for event filtering
ReehalS Mar 12, 2026
371f9e8
Change knowledge dates to 2026 dates
ReehalS Mar 12, 2026
d0ffe29
Compact event cards
ReehalS Mar 12, 2026
4d20ba6
Fix duplications
ReehalS Mar 12, 2026
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
18 changes: 14 additions & 4 deletions .github/workflows/production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install node
uses: actions/setup-node@v4
with:
Expand All @@ -24,12 +24,19 @@ jobs:

- name: Install dependencies
run: npm install

- name: Run migrations
run: npx migrate-mongo up
env:
MONGODB_URI: ${{ secrets.MONGODB_URI }}

- name: Seed hackbot documentation
run: npm run hackbot:seed
env:
MONGODB_URI: ${{ secrets.MONGODB_URI }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL }}

- name: Install Vercel CLI
run: npm install --global vercel@latest

Expand All @@ -40,11 +47,14 @@ jobs:
printf "${{ secrets.HMAC_INVITE_SECRET }}" | vercel env add HMAC_INVITE_SECRET production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ secrets.SENDER_PWD }}" | vercel env add SENDER_PWD production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ secrets.CHECK_IN_CODE }}" | vercel env add CHECK_IN_CODE production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ secrets.OPENAI_API_KEY }}" | vercel env add OPENAI_API_KEY production --force --token=${{ secrets.VERCEL_TOKEN }}

printf "${{ vars.ENV_URL }}" | vercel env add BASE_URL production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.INVITE_DEADLINE }}" | vercel env add INVITE_DEADLINE production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.SENDER_EMAIL }}" | vercel env add SENDER_EMAIL production --force --token=${{ secrets.VERCEL_TOKEN }}

printf "${{ vars.OPENAI_MODEL }}" | vercel env add OPENAI_MODEL production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.OPENAI_EMBEDDING_MODEL }}" | vercel env add OPENAI_EMBEDDING_MODEL production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.OPENAI_MAX_TOKENS }}" | vercel env add OPENAI_MAX_TOKENS production --force --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
Expand All @@ -56,4 +66,4 @@ jobs:
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

- name: Success
run: echo "🚀 Deploy successful - BLAST OFF WOO! (woot woot) !!! 🐕 🐕 🐕 🚀 "
run: echo "🚀 Deploy successful - BLAST OFF WOO! (woot woot) !!! 🐕 🐕 🐕 🚀 "
15 changes: 13 additions & 2 deletions .github/workflows/staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install node
uses: actions/setup-node@v4
with:
Expand All @@ -26,12 +26,19 @@ jobs:

- name: Install dependencies
run: npm install

- name: Run migrations
run: npx migrate-mongo up
env:
MONGODB_URI: ${{ secrets.MONGODB_URI }}

- name: Seed hackbot documentation
run: npm run hackbot:seed
env:
MONGODB_URI: ${{ secrets.MONGODB_URI }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL }}

- name: Install Vercel CLI
run: npm install --global vercel@latest

Expand All @@ -42,10 +49,14 @@ jobs:
printf "${{ secrets.HMAC_INVITE_SECRET }}" | vercel env add HMAC_INVITE_SECRET production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ secrets.SENDER_PWD }}" | vercel env add SENDER_PWD production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ secrets.CHECK_IN_CODE }}" | vercel env add CHECK_IN_CODE production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ secrets.OPENAI_API_KEY }}" | vercel env add OPENAI_API_KEY production --force --token=${{ secrets.VERCEL_TOKEN }}

printf "${{ vars.ENV_URL }}" | vercel env add BASE_URL production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.INVITE_DEADLINE }}" | vercel env add INVITE_DEADLINE production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.SENDER_EMAIL }}" | vercel env add SENDER_EMAIL production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.OPENAI_MODEL }}" | vercel env add OPENAI_MODEL production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.OPENAI_EMBEDDING_MODEL }}" | vercel env add OPENAI_EMBEDDING_MODEL production --force --token=${{ secrets.VERCEL_TOKEN }}
printf "${{ vars.OPENAI_MAX_TOKENS }}" | vercel env add OPENAI_MAX_TOKENS production --force --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
Expand Down
39 changes: 39 additions & 0 deletions app/(api)/_actions/hackbot/clearKnowledgeDocs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use server';

import { getDatabase } from '@utils/mongodb/mongoClient.mjs';

export interface ClearKnowledgeDocsResult {
ok: boolean;
deletedKnowledge: number;
deletedEmbeddings: number;
error?: string;
}

export default async function clearKnowledgeDocs(): Promise<ClearKnowledgeDocsResult> {
try {
const db = await getDatabase();

const knowledgeResult = await db
.collection('hackbot_knowledge')
.deleteMany({});

const embeddingsResult = await db
.collection('hackbot_docs')
.deleteMany({ _id: { $regex: '^knowledge-' } });

return {
ok: true,
deletedKnowledge: knowledgeResult.deletedCount,
deletedEmbeddings: embeddingsResult.deletedCount,
};
} catch (e) {
const msg = e instanceof Error ? e.message : 'Unknown error';
console.error('[clearKnowledgeDocs] Error:', msg);
return {
ok: false,
deletedKnowledge: 0,
deletedEmbeddings: 0,
error: msg,
};
}
}
30 changes: 30 additions & 0 deletions app/(api)/_actions/hackbot/deleteKnowledgeDoc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use server';

import { getDatabase } from '@utils/mongodb/mongoClient.mjs';
import { ObjectId } from 'mongodb';

export interface DeleteKnowledgeDocResult {
ok: boolean;
error?: string;
}

export default async function deleteKnowledgeDoc(
id: string
): Promise<DeleteKnowledgeDocResult> {
try {
const db = await getDatabase();
const objectId = new ObjectId(id);

await db.collection('hackbot_knowledge').deleteOne({ _id: objectId });
await db.collection('hackbot_docs').deleteOne({ _id: `knowledge-${id}` });

console.log(`[deleteKnowledgeDoc] Deleted ${id}`);
return { ok: true };
} catch (e) {
console.error('[deleteKnowledgeDoc] Error', e);
return {
ok: false,
error: e instanceof Error ? e.message : 'Failed to delete document',
};
}
}
17 changes: 17 additions & 0 deletions app/(api)/_actions/hackbot/getHackerProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use server';

import { auth } from '@/auth';
import type { HackerProfile } from '@typeDefs/hackbot';

export type { HackerProfile };

export async function getHackerProfile(): Promise<HackerProfile | null> {
const session = await auth();
if (!session?.user) return null;
const user = session.user as any;
return {
name: user.name ?? undefined,
position: user.position ?? undefined,
is_beginner: user.is_beginner ?? undefined,
};
}
50 changes: 50 additions & 0 deletions app/(api)/_actions/hackbot/getKnowledgeDocs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use server';

import { getDatabase } from '@utils/mongodb/mongoClient.mjs';
import { HackDocType } from '@typeDefs/hackbot';

export interface KnowledgeDoc {
id: string;
type: HackDocType;
title: string;
content: string;
url: string | null;
createdAt: string;
updatedAt: string;
}

export interface GetKnowledgeDocsResult {
ok: boolean;
docs: KnowledgeDoc[];
error?: string;
}

export default async function getKnowledgeDocs(): Promise<GetKnowledgeDocsResult> {
try {
const db = await getDatabase();
const raw = await db
.collection('hackbot_knowledge')
.find({})
.sort({ updatedAt: -1 })
.toArray();

const docs: KnowledgeDoc[] = raw.map((d: any) => ({
id: String(d._id),
type: d.type,
title: d.title,
content: d.content,
url: d.url ?? null,
createdAt: d.createdAt?.toISOString?.() ?? new Date().toISOString(),
updatedAt: d.updatedAt?.toISOString?.() ?? new Date().toISOString(),
}));

return { ok: true, docs };
} catch (e) {
console.error('[getKnowledgeDocs] Error', e);
return {
ok: false,
docs: [],
error: e instanceof Error ? e.message : 'Failed to load knowledge docs',
};
}
}
59 changes: 59 additions & 0 deletions app/(api)/_actions/hackbot/getUsageMetrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use server';

import { getDatabase } from '@utils/mongodb/mongoClient.mjs';

export type UsagePeriod = '24h' | '7d' | '30d';

export interface UsageMetrics {
totalRequests: number;
totalPromptTokens: number;
totalCompletionTokens: number;
totalCachedTokens: number;
/** 0–1 fraction of prompt tokens that were served from cache */
cacheHitRate: number;
}

export async function getUsageMetrics(
period: UsagePeriod = '24h'
): Promise<UsageMetrics> {
const hours = period === '24h' ? 24 : period === '7d' ? 168 : 720;
const since = new Date(Date.now() - hours * 60 * 60 * 1000);

const db = await getDatabase();
const [result] = await db
.collection('hackbot_usage')
.aggregate([
{ $match: { timestamp: { $gte: since } } },
{
$group: {
_id: null,
totalRequests: { $sum: 1 },
totalPromptTokens: { $sum: '$promptTokens' },
totalCompletionTokens: { $sum: '$completionTokens' },
totalCachedTokens: { $sum: '$cachedPromptTokens' },
},
},
])
.toArray();

if (!result) {
return {
totalRequests: 0,
totalPromptTokens: 0,
totalCompletionTokens: 0,
totalCachedTokens: 0,
cacheHitRate: 0,
};
}

return {
totalRequests: result.totalRequests,
totalPromptTokens: result.totalPromptTokens,
totalCompletionTokens: result.totalCompletionTokens,
totalCachedTokens: result.totalCachedTokens,
cacheHitRate:
result.totalPromptTokens > 0
? result.totalCachedTokens / result.totalPromptTokens
: 0,
};
}
Loading
Loading