Recently Written · git

ideamine

An idea inbox for Claude Code: /idea saves ideas at zero tokens; Claude triages them and routes each to the cheapest model that can build it.

git clone https://github.com/equwal/ideamine

Log | Files | Refs


tests/fixtures/fake-server.js (2528 bytes)

1 // A stand-in for the dashboard server in the tests: an OpenAI-compatible POST /v1/embeddings, and
2 // PUT for the files that `ideamine publish` uploads. The vectors are a hashed bag of words, so
3 // texts that share words are similar, and the same text always gets the same vector.
4 
5 import crypto from 'node:crypto';
6 import http from 'node:http';
7 
8 export const DIM = 64;
9 
10 export function fakeVector(text) {
11   const vec = new Array(DIM).fill(0);
12   const words = String(text).toLowerCase().replace(/^search_(query|document): /, '').match(/[\p{L}\p{N}]{3,}/gu) || ['empty'];
13   for (const w of words) vec[crypto.createHash('sha1').update(w).digest().readUInt16LE(0) % DIM] += 1;
14   return vec;
15 }
16 
17 /**
18  * Start the server on a free port. Options: `maxChars` makes it refuse longer inputs the way
19  * llama.cpp does; `down` makes it answer 503. Returns { url, inputs, files, close, options }.
20  */
21 export async function startFakeServer(options = {}) {
22   const inputs = []; // every text that /v1/embeddings got, in order
23   const files = new Map(); // path -> body of each PUT
24   const server = http.createServer((req, res) => {
25     let body = '';
26     req.setEncoding('utf8');
27     req.on('data', (chunk) => (body += chunk));
28     req.on('end', () => {
29       if (req.method === 'PUT') {
30         files.set(req.url, body);
31         res.writeHead(201).end();
32         return;
33       }
34       if (req.method !== 'POST' || req.url !== '/v1/embeddings') return res.writeHead(404).end();
35       if (options.down) return res.writeHead(503).end('{"error":"model is loading"}');
36       const { input } = JSON.parse(body);
37       const texts = Array.isArray(input) ? input : [input];
38       inputs.push(...texts);
39       const long = texts.find((t) => options.maxChars && t.length > options.maxChars);
40       if (long) {
41         const error = { code: 500, message: `input (${long.length} tokens) is too large to process. increase the physical batch size`, type: 'server_error' };
42         return res.writeHead(500, { 'content-type': 'application/json' }).end(JSON.stringify({ error }));
43       }
44       const data = texts.map((t, index) => ({ object: 'embedding', index, embedding: fakeVector(t) }));
45       res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify({ object: 'list', data }));
46     });
47   });
48   await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
49   const url = `http://127.0.0.1:${server.address().port}`;
50   return { url, inputs, files, options, close: () => new Promise((resolve) => server.close(resolve)) };
51 }