Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env node
2
3import { MongoClient } from 'mongodb';
4
5// ANSI color codes
6const colors = {
7 reset: '\x1b[0m',
8 bright: '\x1b[1m',
9 red: '\x1b[31m',
10 green: '\x1b[32m',
11 yellow: '\x1b[33m',
12 blue: '\x1b[34m',
13 magenta: '\x1b[35m',
14 cyan: '\x1b[36m',
15 gray: '\x1b[90m',
16};
17
18// MongoDB connection details
19const MONGODB_CONNECTION_STRING = process.env.MONGODB_CONNECTION_STRING;
20const MONGODB_NAME = process.env.MONGODB_NAME || 'aesthetic';
21const COLLECTION_NAME = 'chat-system';
22
23// Moderation prompt - explicit URL handling
24const MODERATION_PROMPT = `Rate this chat message as PG-13 appropriate or not.
25
26ALWAYS REPLY 't' FOR URLS AND LINKS (even if they contain words like "live", "lyt", etc.)
27
28Block if message contains: sexual content, body functions, profanity, violence, drugs, hate speech.
29Allow: URLs (https://, http://), links, normal conversation.
30
31Reply with one letter: t (appropriate) or f (inappropriate)
32
33Message: "`;
34
35async function testMessage(message) {
36 const startTime = Date.now();
37
38 try {
39 const response = await fetch('http://localhost:11434/api/generate', {
40 method: 'POST',
41 headers: { 'Content-Type': 'application/json' },
42 body: JSON.stringify({
43 model: 'gemma2:2b',
44 prompt: MODERATION_PROMPT + '"' + message + '"',
45 stream: true,
46 options: {
47 num_ctx: 256,
48 temperature: 0,
49 num_predict: 5, // Just t or f
50 }
51 })
52 });
53
54 let fullResponse = '';
55 const reader = response.body.getReader();
56 const decoder = new TextDecoder();
57
58 process.stdout.write(` ${colors.gray}`);
59
60 while (true) {
61 const { done, value } = await reader.read();
62 if (done) break;
63
64 const chunk = decoder.decode(value);
65 const lines = chunk.split('\n').filter(line => line.trim());
66
67 for (const line of lines) {
68 try {
69 const json = JSON.parse(line);
70 if (json.response) {
71 fullResponse += json.response;
72 process.stdout.write(json.response);
73 }
74 } catch (e) {
75 // Skip invalid JSON lines
76 }
77 }
78 }
79
80 process.stdout.write(`${colors.reset}\n`);
81
82 const latency = Date.now() - startTime;
83 const response_clean = fullResponse.toLowerCase().trim();
84
85 // Extract decision - simple t or f
86 let cleanedResponse = response_clean;
87 if (cleanedResponse.includes('</think>')) {
88 cleanedResponse = cleanedResponse.split('</think>')[1]?.trim() || cleanedResponse;
89 }
90
91 let decision = '';
92
93 // Find t or f
94 if (cleanedResponse.startsWith('t')) {
95 decision = 't';
96 } else if (cleanedResponse.startsWith('f')) {
97 decision = 'f';
98 } else if (cleanedResponse.includes(' t')) {
99 decision = 't';
100 } else if (cleanedResponse.includes(' f')) {
101 decision = 'f';
102 }
103
104 // Show the full response as "sentiment" for debugging
105 const displayResponse = cleanedResponse.substring(0, 60);
106 console.log(` ${colors.cyan}[${displayResponse}]${colors.reset} ${colors.gray}→ ${decision || 'unknown'}${colors.reset}`);
107
108 // Check decision
109 let outcome = 'error';
110 if (decision === 't') {
111 outcome = 'pass';
112 } else if (decision === 'f') {
113 outcome = 'fail';
114 }
115
116 return { outcome, latency, response: fullResponse, sentiment: cleanedResponse };
117 } catch (error) {
118 const latency = Date.now() - startTime;
119 console.error(`\n${colors.red}Error testing message: ${error.message}${colors.reset}`);
120 return { outcome: 'error', rationale: error.message, latency };
121 }
122}
123
124async function main() {
125 const limit = parseInt(process.argv[2]) || 50;
126 const continuous = process.argv.includes('--continuous') || process.argv.includes('-c');
127
128 console.log(`${colors.cyan}${colors.bright}Connecting to MongoDB...${colors.reset}`);
129 const client = new MongoClient(MONGODB_CONNECTION_STRING);
130
131 try {
132 await client.connect();
133 console.log(`${colors.green}Connected to MongoDB${colors.reset}`);
134
135 const db = client.db(MONGODB_NAME);
136 const collection = db.collection(COLLECTION_NAME);
137
138 let offset = 0;
139 let totalPassCount = 0;
140 let totalFailCount = 0;
141 let totalErrorCount = 0;
142 let failedMessages = [];
143
144 do {
145 console.log(`\n${colors.magenta}${colors.bright}Fetching ${limit} messages (offset: ${offset}) from ${COLLECTION_NAME}...${colors.reset}\n`);
146 const messages = await collection
147 .find({ text: { $exists: true, $ne: '' } })
148 .sort({ when: -1 })
149 .skip(offset)
150 .limit(limit)
151 .toArray();
152
153 if (messages.length === 0) {
154 console.log(`${colors.yellow}No more messages to test${colors.reset}`);
155 break;
156 }
157
158 console.log(`${colors.blue}Found ${messages.length} messages to test${colors.reset}\n`);
159 console.log(colors.gray + '='.repeat(80) + colors.reset);
160
161 let passCount = 0;
162 let failCount = 0;
163 let errorCount = 0;
164
165 for (const [index, msg] of messages.entries()) {
166 const messageText = msg.text || '';
167 const timestamp = msg.when ? new Date(msg.when).toISOString() : 'unknown';
168 const user = msg.user || 'anonymous';
169
170 console.log(`\n${colors.blue}[${offset + index + 1}] ${colors.gray}${timestamp}${colors.reset}`);
171 console.log(`${colors.cyan}Message: "${messageText}"${colors.reset}`);
172
173 const result = await testMessage(messageText);
174
175 const latencyMs = result.latency || 0;
176 const latencyColor = latencyMs < 1000 ? colors.green : latencyMs < 3000 ? colors.yellow : colors.red;
177
178 if (result.outcome === 'pass') {
179 passCount++;
180 console.log(`${colors.green}${colors.bright}✅ PASS${colors.reset} ${latencyColor}⏱️ ${latencyMs}ms${colors.reset}`);
181 } else if (result.outcome === 'fail') {
182 failCount++;
183 console.log(`${colors.red}${colors.bright}❌ FAIL${colors.reset} ${latencyColor}⏱️ ${latencyMs}ms${colors.reset}`);
184 failedMessages.push({ messageText, rationale: result.sentiment || 'N/A', user, timestamp });
185 } else {
186 errorCount++;
187 console.log(`${colors.yellow}${colors.bright}⚠️ ERROR${colors.reset} ${latencyColor}⏱️ ${latencyMs}ms${colors.reset}`);
188 }
189
190 console.log(colors.gray + '-'.repeat(80) + colors.reset);
191 }
192
193 totalPassCount += passCount;
194 totalFailCount += failCount;
195 totalErrorCount += errorCount;
196
197 console.log(`\n${colors.magenta}Batch Summary:${colors.reset}`);
198 console.log(`${colors.green}✅ Passed: ${passCount}${colors.reset}`);
199 console.log(`${colors.red}❌ Failed: ${failCount}${colors.reset}`);
200 console.log(`${colors.yellow}⚠️ Errors: ${errorCount}${colors.reset}`);
201
202 offset += limit;
203
204 if (continuous && messages.length === limit) {
205 console.log(`\n${colors.cyan}Continuing to next batch...${colors.reset}`);
206 await new Promise(resolve => setTimeout(resolve, 1000));
207 } else {
208 break;
209 }
210
211 } while (continuous);
212
213 console.log('\n' + colors.bright + colors.magenta + '='.repeat(80) + colors.reset);
214 console.log(`${colors.bright}${colors.magenta}FINAL SUMMARY:${colors.reset}`);
215 console.log(`Total messages tested: ${offset}`);
216 console.log(`${colors.green}${colors.bright}✅ Passed: ${totalPassCount}${colors.reset}`);
217 console.log(`${colors.red}${colors.bright}❌ Failed: ${totalFailCount}${colors.reset}`);
218 console.log(`${colors.yellow}${colors.bright}⚠️ Errors: ${totalErrorCount}${colors.reset}`);
219
220 if (failedMessages.length > 0) {
221 console.log(`\n${colors.red}${colors.bright}FAILED MESSAGES (${failedMessages.length}):${colors.reset}`);
222 for (const [i, fail] of failedMessages.entries()) {
223 console.log(`\n${colors.red}${i + 1}.${colors.reset} ${colors.gray}${fail.timestamp}${colors.reset}`);
224 console.log(` ${colors.cyan}"${fail.messageText}"${colors.reset}`);
225 console.log(` ${colors.yellow}${fail.rationale}${colors.reset}`);
226 }
227 }
228
229 console.log('\n' + colors.magenta + '='.repeat(80) + colors.reset);
230
231 } catch (error) {
232 console.error(`${colors.red}Error: ${error.message}${colors.reset}`);
233 process.exit(1);
234 } finally {
235 await client.close();
236 console.log(`\n${colors.green}Disconnected from MongoDB${colors.reset}`);
237 }
238}
239
240main();