@hasna/brains 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +170 -0
- package/dist/cli/index.js +635 -998
- package/dist/db/index.d.ts.map +1 -1
- package/dist/index.js +142 -4294
- package/dist/lib/gatherers/index.d.ts +0 -3
- package/dist/lib/gatherers/index.d.ts.map +1 -1
- package/dist/lib/index.d.ts +0 -3
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/providers/openai.d.ts +0 -2
- package/dist/lib/providers/openai.d.ts.map +1 -1
- package/dist/lib/providers/thinker-labs.d.ts +0 -1
- package/dist/lib/providers/thinker-labs.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +195 -4584
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +9 -4638
- package/package.json +2 -1
- package/dist/lib/config.d.ts +0 -11
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/gatherers/assistants.d.ts +0 -3
- package/dist/lib/gatherers/assistants.d.ts.map +0 -1
- package/dist/lib/gatherers/economy.d.ts +0 -3
- package/dist/lib/gatherers/economy.d.ts.map +0 -1
- package/dist/lib/gatherers/protocol.d.ts +0 -29
- package/dist/lib/gatherers/protocol.d.ts.map +0 -1
- package/dist/lib/gatherers/recordings.d.ts +0 -3
- package/dist/lib/gatherers/recordings.d.ts.map +0 -1
- package/dist/lib/gatherers/registry.d.ts +0 -7
- package/dist/lib/gatherers/registry.d.ts.map +0 -1
- package/dist/lib/gatherers/researcher.d.ts +0 -3
- package/dist/lib/gatherers/researcher.d.ts.map +0 -1
- package/dist/lib/gatherers/styles.d.ts +0 -3
- package/dist/lib/gatherers/styles.d.ts.map +0 -1
- package/dist/lib/gatherers/tickets.d.ts +0 -3
- package/dist/lib/gatherers/tickets.d.ts.map +0 -1
- package/dist/lib/retry.d.ts +0 -7
- package/dist/lib/retry.d.ts.map +0 -1
- package/dist/lib/schemas.d.ts +0 -87
- package/dist/lib/schemas.d.ts.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -5,28 +5,45 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
20
38
|
var __export = (target, all) => {
|
|
21
39
|
for (var name in all)
|
|
22
40
|
__defProp(target, name, {
|
|
23
41
|
get: all[name],
|
|
24
42
|
enumerable: true,
|
|
25
43
|
configurable: true,
|
|
26
|
-
set: (
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
47
|
var __require = import.meta.require;
|
|
31
48
|
|
|
32
49
|
// node_modules/commander/lib/error.js
|
|
@@ -2063,440 +2080,94 @@ var require_commander = __commonJS((exports) => {
|
|
|
2063
2080
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2064
2081
|
});
|
|
2065
2082
|
|
|
2066
|
-
//
|
|
2067
|
-
var
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2083
|
+
// node_modules/commander/esm.mjs
|
|
2084
|
+
var import__ = __toESM(require_commander(), 1);
|
|
2085
|
+
var {
|
|
2086
|
+
program,
|
|
2087
|
+
createCommand,
|
|
2088
|
+
createArgument,
|
|
2089
|
+
createOption,
|
|
2090
|
+
CommanderError,
|
|
2091
|
+
InvalidArgumentError,
|
|
2092
|
+
InvalidOptionArgumentError,
|
|
2093
|
+
Command,
|
|
2094
|
+
Argument,
|
|
2095
|
+
Option,
|
|
2096
|
+
Help
|
|
2097
|
+
} = import__.default;
|
|
2076
2098
|
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
function taskToStatusUpdateExample(task) {
|
|
2096
|
-
if (!task.completed_at && task.status === "pending")
|
|
2097
|
-
return null;
|
|
2098
|
-
const id = task.short_id ?? task.id;
|
|
2099
|
-
return {
|
|
2100
|
-
messages: [
|
|
2101
|
-
{ role: "system", content: SYSTEM_PROMPT },
|
|
2102
|
-
{ role: "user", content: `Mark task ${id} as ${task.status}` },
|
|
2103
|
-
{ role: "assistant", content: `Task ${id} has been updated to status: ${task.status}. ${task.completed_at ? `Completed at: ${task.completed_at}` : ""}`.trim() }
|
|
2104
|
-
]
|
|
2105
|
-
};
|
|
2106
|
-
}
|
|
2107
|
-
function taskToSearchExample(tasks, query) {
|
|
2108
|
-
const matched = tasks.filter((t) => t.title.toLowerCase().includes(query.toLowerCase())).slice(0, 5);
|
|
2109
|
-
return {
|
|
2110
|
-
messages: [
|
|
2111
|
-
{ role: "system", content: SYSTEM_PROMPT },
|
|
2112
|
-
{ role: "user", content: `Search tasks for: "${query}"` },
|
|
2113
|
-
{
|
|
2114
|
-
role: "assistant",
|
|
2115
|
-
content: matched.length > 0 ? `Found ${matched.length} task(s):
|
|
2116
|
-
${matched.map((t) => `- [${t.short_id ?? t.id}] ${t.title} (${t.status})`).join(`
|
|
2117
|
-
`)}` : `No tasks found matching "${query}".`
|
|
2099
|
+
// node_modules/drizzle-orm/entity.js
|
|
2100
|
+
var entityKind = Symbol.for("drizzle:entityKind");
|
|
2101
|
+
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
2102
|
+
function is(value, type) {
|
|
2103
|
+
if (!value || typeof value !== "object") {
|
|
2104
|
+
return false;
|
|
2105
|
+
}
|
|
2106
|
+
if (value instanceof type) {
|
|
2107
|
+
return true;
|
|
2108
|
+
}
|
|
2109
|
+
if (!Object.prototype.hasOwnProperty.call(type, entityKind)) {
|
|
2110
|
+
throw new Error(`Class "${type.name ?? "<unknown>"}" doesn't look like a Drizzle entity. If this is incorrect and the class is provided by Drizzle, please report this as a bug.`);
|
|
2111
|
+
}
|
|
2112
|
+
let cls = Object.getPrototypeOf(value).constructor;
|
|
2113
|
+
if (cls) {
|
|
2114
|
+
while (cls) {
|
|
2115
|
+
if (entityKind in cls && cls[entityKind] === type[entityKind]) {
|
|
2116
|
+
return true;
|
|
2118
2117
|
}
|
|
2119
|
-
|
|
2120
|
-
};
|
|
2121
|
-
}
|
|
2122
|
-
async function gatherFromTodos(options = {}) {
|
|
2123
|
-
const dbPath = join3(homedir3(), ".todos", "todos.db");
|
|
2124
|
-
const db = new Database3(dbPath, { readonly: true, create: false });
|
|
2125
|
-
try {
|
|
2126
|
-
let query = "SELECT * FROM tasks WHERE 1=1";
|
|
2127
|
-
const params = [];
|
|
2128
|
-
if (options.since) {
|
|
2129
|
-
query += " AND created_at >= ?";
|
|
2130
|
-
params.push(options.since.toISOString());
|
|
2131
|
-
}
|
|
2132
|
-
query += " ORDER BY created_at DESC";
|
|
2133
|
-
if (options.limit) {
|
|
2134
|
-
query += " LIMIT ?";
|
|
2135
|
-
params.push(options.limit * 2);
|
|
2136
|
-
}
|
|
2137
|
-
const tasks = db.query(query).all(...params);
|
|
2138
|
-
const examples = [];
|
|
2139
|
-
for (const task of tasks) {
|
|
2140
|
-
examples.push(taskToCreateExample(task));
|
|
2141
|
-
const statusEx = taskToStatusUpdateExample(task);
|
|
2142
|
-
if (statusEx)
|
|
2143
|
-
examples.push(statusEx);
|
|
2144
|
-
}
|
|
2145
|
-
const searchTerms = ["urgent", "fix", "implement", "create", "update", "review"];
|
|
2146
|
-
for (const term of searchTerms) {
|
|
2147
|
-
examples.push(taskToSearchExample(tasks, term));
|
|
2118
|
+
cls = Object.getPrototypeOf(cls);
|
|
2148
2119
|
}
|
|
2149
|
-
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
2150
|
-
return {
|
|
2151
|
-
source: "todos",
|
|
2152
|
-
examples: finalExamples,
|
|
2153
|
-
count: finalExamples.length
|
|
2154
|
-
};
|
|
2155
|
-
} finally {
|
|
2156
|
-
db.close();
|
|
2157
2120
|
}
|
|
2121
|
+
return false;
|
|
2158
2122
|
}
|
|
2159
|
-
var SYSTEM_PROMPT = "You are a task management assistant that helps users create, update, search, and manage tasks and projects.";
|
|
2160
|
-
var init_todos = () => {};
|
|
2161
|
-
|
|
2162
|
-
// src/lib/gatherers/mementos.ts
|
|
2163
|
-
var exports_mementos = {};
|
|
2164
|
-
__export(exports_mementos, {
|
|
2165
|
-
gatherFromMementos: () => gatherFromMementos
|
|
2166
|
-
});
|
|
2167
|
-
import { Database as Database4 } from "bun:sqlite";
|
|
2168
|
-
import { homedir as homedir4 } from "os";
|
|
2169
|
-
import { join as join4 } from "path";
|
|
2170
|
-
function memoryToRecallExample(memory) {
|
|
2171
|
-
return {
|
|
2172
|
-
messages: [
|
|
2173
|
-
{ role: "system", content: SYSTEM_PROMPT2 },
|
|
2174
|
-
{ role: "user", content: `What do you remember about "${memory.key}"?` },
|
|
2175
|
-
{
|
|
2176
|
-
role: "assistant",
|
|
2177
|
-
content: memory.summary ? `${memory.value}
|
|
2178
2123
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
if (options.limit) {
|
|
2227
|
-
query += " LIMIT ?";
|
|
2228
|
-
params.push(options.limit * 3);
|
|
2229
|
-
}
|
|
2230
|
-
const memories = db.query(query).all(...params);
|
|
2231
|
-
const examples = [];
|
|
2232
|
-
for (const memory of memories) {
|
|
2233
|
-
examples.push(memoryToRecallExample(memory));
|
|
2234
|
-
examples.push(memoryToSaveExample(memory));
|
|
2235
|
-
}
|
|
2236
|
-
const categories = [...new Set(memories.map((m) => m.category))];
|
|
2237
|
-
for (const category of categories) {
|
|
2238
|
-
examples.push(memoryToSearchExample(memories, category));
|
|
2239
|
-
}
|
|
2240
|
-
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
2241
|
-
return {
|
|
2242
|
-
source: "mementos",
|
|
2243
|
-
examples: finalExamples,
|
|
2244
|
-
count: finalExamples.length
|
|
2245
|
-
};
|
|
2246
|
-
} finally {
|
|
2247
|
-
db.close();
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
var SYSTEM_PROMPT2 = "You are an AI assistant with persistent memory. You can remember and recall information across sessions to provide better, more personalized assistance.";
|
|
2251
|
-
var init_mementos = () => {};
|
|
2252
|
-
|
|
2253
|
-
// src/lib/gatherers/conversations.ts
|
|
2254
|
-
var exports_conversations = {};
|
|
2255
|
-
__export(exports_conversations, {
|
|
2256
|
-
gatherFromConversations: () => gatherFromConversations
|
|
2257
|
-
});
|
|
2258
|
-
import { Database as Database5 } from "bun:sqlite";
|
|
2259
|
-
import { homedir as homedir5 } from "os";
|
|
2260
|
-
import { join as join5 } from "path";
|
|
2261
|
-
function windowToExample(window2) {
|
|
2262
|
-
if (window2.length < 2)
|
|
2263
|
-
return null;
|
|
2264
|
-
const messages = [
|
|
2265
|
-
{ role: "system", content: SYSTEM_PROMPT3 }
|
|
2266
|
-
];
|
|
2267
|
-
for (let i = 0;i < window2.length - 1; i++) {
|
|
2268
|
-
const msg = window2[i];
|
|
2269
|
-
if (!msg)
|
|
2270
|
-
continue;
|
|
2271
|
-
const role = i % 2 === 0 ? "user" : "assistant";
|
|
2272
|
-
messages.push({
|
|
2273
|
-
role,
|
|
2274
|
-
content: `[${msg.from_agent} \u2192 ${msg.to_agent ?? msg.space ?? "all"}]: ${msg.content}`
|
|
2275
|
-
});
|
|
2276
|
-
}
|
|
2277
|
-
const last = window2[window2.length - 1];
|
|
2278
|
-
if (!last)
|
|
2279
|
-
return null;
|
|
2280
|
-
messages.push({
|
|
2281
|
-
role: "assistant",
|
|
2282
|
-
content: `[${last.from_agent} \u2192 ${last.to_agent ?? last.space ?? "all"}]: ${last.content}`
|
|
2283
|
-
});
|
|
2284
|
-
return { messages };
|
|
2285
|
-
}
|
|
2286
|
-
async function gatherFromConversations(options = {}) {
|
|
2287
|
-
const dbPath = join5(homedir5(), ".conversations", "messages.db");
|
|
2288
|
-
const db = new Database5(dbPath, { readonly: true, create: false });
|
|
2289
|
-
try {
|
|
2290
|
-
let query = "SELECT * FROM messages WHERE 1=1";
|
|
2291
|
-
const params = [];
|
|
2292
|
-
if (options.since) {
|
|
2293
|
-
query += " AND created_at >= ?";
|
|
2294
|
-
params.push(options.since.toISOString());
|
|
2295
|
-
}
|
|
2296
|
-
query += " ORDER BY session_id, created_at ASC";
|
|
2297
|
-
const allMessages = db.query(query).all(...params);
|
|
2298
|
-
const sessions = new Map;
|
|
2299
|
-
for (const msg of allMessages) {
|
|
2300
|
-
const msgs = sessions.get(msg.session_id) ?? [];
|
|
2301
|
-
msgs.push(msg);
|
|
2302
|
-
sessions.set(msg.session_id, msgs);
|
|
2303
|
-
}
|
|
2304
|
-
const examples = [];
|
|
2305
|
-
const windowSize = 4;
|
|
2306
|
-
for (const [, sessionMsgs] of sessions) {
|
|
2307
|
-
if (sessionMsgs.length < 2)
|
|
2308
|
-
continue;
|
|
2309
|
-
for (let start = 0;start <= sessionMsgs.length - 2; start++) {
|
|
2310
|
-
const end = Math.min(start + windowSize, sessionMsgs.length);
|
|
2311
|
-
const window2 = sessionMsgs.slice(start, end);
|
|
2312
|
-
const example = windowToExample(window2);
|
|
2313
|
-
if (example)
|
|
2314
|
-
examples.push(example);
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
2318
|
-
return {
|
|
2319
|
-
source: "conversations",
|
|
2320
|
-
examples: finalExamples,
|
|
2321
|
-
count: finalExamples.length
|
|
2322
|
-
};
|
|
2323
|
-
} finally {
|
|
2324
|
-
db.close();
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
var SYSTEM_PROMPT3 = "You are a helpful AI assistant participating in multi-agent conversations. You communicate clearly and collaboratively with other agents and users.";
|
|
2328
|
-
var init_conversations = () => {};
|
|
2329
|
-
|
|
2330
|
-
// src/lib/gatherers/sessions.ts
|
|
2331
|
-
var exports_sessions2 = {};
|
|
2332
|
-
__export(exports_sessions2, {
|
|
2333
|
-
gatherFromSessions: () => gatherFromSessions
|
|
2334
|
-
});
|
|
2335
|
-
import { readdir, readFile, stat } from "fs/promises";
|
|
2336
|
-
import { existsSync as existsSync2 } from "fs";
|
|
2337
|
-
import { join as join6 } from "path";
|
|
2338
|
-
import { homedir as homedir6 } from "os";
|
|
2339
|
-
function extractText(content) {
|
|
2340
|
-
if (typeof content === "string")
|
|
2341
|
-
return content;
|
|
2342
|
-
return content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join(`
|
|
2343
|
-
`).trim();
|
|
2344
|
-
}
|
|
2345
|
-
async function gatherFromSessions(options = {}) {
|
|
2346
|
-
const { limit: limit2 = 1000 } = options;
|
|
2347
|
-
const examples = [];
|
|
2348
|
-
const claudeDir = join6(homedir6(), ".claude", "projects");
|
|
2349
|
-
if (!existsSync2(claudeDir)) {
|
|
2350
|
-
return { source: "sessions", examples: [], count: 0 };
|
|
2351
|
-
}
|
|
2352
|
-
const projectDirs = await readdir(claudeDir).catch(() => []);
|
|
2353
|
-
for (const projectDir of projectDirs) {
|
|
2354
|
-
if (examples.length >= limit2)
|
|
2355
|
-
break;
|
|
2356
|
-
const projectPath = join6(claudeDir, projectDir);
|
|
2357
|
-
const files = await readdir(projectPath).catch(() => []);
|
|
2358
|
-
for (const file of files) {
|
|
2359
|
-
if (examples.length >= limit2)
|
|
2360
|
-
break;
|
|
2361
|
-
if (!file.endsWith(".jsonl"))
|
|
2362
|
-
continue;
|
|
2363
|
-
const filePath = join6(projectPath, file);
|
|
2364
|
-
if (options.since) {
|
|
2365
|
-
const fileStat = await stat(filePath).catch(() => null);
|
|
2366
|
-
if (fileStat && fileStat.mtime < options.since)
|
|
2367
|
-
continue;
|
|
2368
|
-
}
|
|
2369
|
-
const content = await readFile(filePath, "utf-8").catch(() => "");
|
|
2370
|
-
if (!content.trim())
|
|
2371
|
-
continue;
|
|
2372
|
-
const lines = content.trim().split(`
|
|
2373
|
-
`);
|
|
2374
|
-
const turns = [];
|
|
2375
|
-
for (const line of lines) {
|
|
2376
|
-
try {
|
|
2377
|
-
const entry = JSON.parse(line);
|
|
2378
|
-
if ((entry.type === "user" || entry.type === "human") && entry.message?.content) {
|
|
2379
|
-
const text2 = extractText(entry.message.content);
|
|
2380
|
-
if (text2.trim())
|
|
2381
|
-
turns.push({ role: "user", content: text2.trim() });
|
|
2382
|
-
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
2383
|
-
const text2 = extractText(entry.message.content);
|
|
2384
|
-
if (text2.trim())
|
|
2385
|
-
turns.push({ role: "assistant", content: text2.trim() });
|
|
2386
|
-
}
|
|
2387
|
-
} catch {}
|
|
2388
|
-
}
|
|
2389
|
-
const windowSize = 6;
|
|
2390
|
-
for (let start = 0;start < turns.length - 1 && examples.length < limit2; start++) {
|
|
2391
|
-
const window2 = turns.slice(start, start + windowSize);
|
|
2392
|
-
if (!window2[0] || window2[0].role !== "user")
|
|
2393
|
-
continue;
|
|
2394
|
-
const lastAssistantIdx = window2.map((t) => t.role).lastIndexOf("assistant");
|
|
2395
|
-
if (lastAssistantIdx < 1)
|
|
2396
|
-
continue;
|
|
2397
|
-
const usedTurns = window2.slice(0, lastAssistantIdx + 1);
|
|
2398
|
-
examples.push({
|
|
2399
|
-
messages: [
|
|
2400
|
-
{ role: "system", content: SYSTEM_PROMPT4 },
|
|
2401
|
-
...usedTurns
|
|
2402
|
-
]
|
|
2403
|
-
});
|
|
2404
|
-
}
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
return { source: "sessions", examples, count: examples.length };
|
|
2408
|
-
}
|
|
2409
|
-
var SYSTEM_PROMPT4 = "You are Claude Code, an AI assistant built by Anthropic that helps developers with coding, architecture, debugging, and software engineering tasks.";
|
|
2410
|
-
var init_sessions = () => {};
|
|
2411
|
-
|
|
2412
|
-
// node_modules/commander/esm.mjs
|
|
2413
|
-
var import__ = __toESM(require_commander(), 1);
|
|
2414
|
-
var {
|
|
2415
|
-
program,
|
|
2416
|
-
createCommand,
|
|
2417
|
-
createArgument,
|
|
2418
|
-
createOption,
|
|
2419
|
-
CommanderError,
|
|
2420
|
-
InvalidArgumentError,
|
|
2421
|
-
InvalidOptionArgumentError,
|
|
2422
|
-
Command,
|
|
2423
|
-
Argument,
|
|
2424
|
-
Option,
|
|
2425
|
-
Help
|
|
2426
|
-
} = import__.default;
|
|
2427
|
-
|
|
2428
|
-
// node_modules/drizzle-orm/entity.js
|
|
2429
|
-
var entityKind = Symbol.for("drizzle:entityKind");
|
|
2430
|
-
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
2431
|
-
function is(value, type) {
|
|
2432
|
-
if (!value || typeof value !== "object") {
|
|
2433
|
-
return false;
|
|
2434
|
-
}
|
|
2435
|
-
if (value instanceof type) {
|
|
2436
|
-
return true;
|
|
2437
|
-
}
|
|
2438
|
-
if (!Object.prototype.hasOwnProperty.call(type, entityKind)) {
|
|
2439
|
-
throw new Error(`Class "${type.name ?? "<unknown>"}" doesn't look like a Drizzle entity. If this is incorrect and the class is provided by Drizzle, please report this as a bug.`);
|
|
2440
|
-
}
|
|
2441
|
-
let cls = Object.getPrototypeOf(value).constructor;
|
|
2442
|
-
if (cls) {
|
|
2443
|
-
while (cls) {
|
|
2444
|
-
if (entityKind in cls && cls[entityKind] === type[entityKind]) {
|
|
2445
|
-
return true;
|
|
2446
|
-
}
|
|
2447
|
-
cls = Object.getPrototypeOf(cls);
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
return false;
|
|
2451
|
-
}
|
|
2452
|
-
|
|
2453
|
-
// node_modules/drizzle-orm/column.js
|
|
2454
|
-
class Column {
|
|
2455
|
-
constructor(table, config) {
|
|
2456
|
-
this.table = table;
|
|
2457
|
-
this.config = config;
|
|
2458
|
-
this.name = config.name;
|
|
2459
|
-
this.keyAsName = config.keyAsName;
|
|
2460
|
-
this.notNull = config.notNull;
|
|
2461
|
-
this.default = config.default;
|
|
2462
|
-
this.defaultFn = config.defaultFn;
|
|
2463
|
-
this.onUpdateFn = config.onUpdateFn;
|
|
2464
|
-
this.hasDefault = config.hasDefault;
|
|
2465
|
-
this.primary = config.primaryKey;
|
|
2466
|
-
this.isUnique = config.isUnique;
|
|
2467
|
-
this.uniqueName = config.uniqueName;
|
|
2468
|
-
this.uniqueType = config.uniqueType;
|
|
2469
|
-
this.dataType = config.dataType;
|
|
2470
|
-
this.columnType = config.columnType;
|
|
2471
|
-
this.generated = config.generated;
|
|
2472
|
-
this.generatedIdentity = config.generatedIdentity;
|
|
2473
|
-
}
|
|
2474
|
-
static [entityKind] = "Column";
|
|
2475
|
-
name;
|
|
2476
|
-
keyAsName;
|
|
2477
|
-
primary;
|
|
2478
|
-
notNull;
|
|
2479
|
-
default;
|
|
2480
|
-
defaultFn;
|
|
2481
|
-
onUpdateFn;
|
|
2482
|
-
hasDefault;
|
|
2483
|
-
isUnique;
|
|
2484
|
-
uniqueName;
|
|
2485
|
-
uniqueType;
|
|
2486
|
-
dataType;
|
|
2487
|
-
columnType;
|
|
2488
|
-
enumValues = undefined;
|
|
2489
|
-
generated = undefined;
|
|
2490
|
-
generatedIdentity = undefined;
|
|
2491
|
-
config;
|
|
2492
|
-
mapFromDriverValue(value) {
|
|
2493
|
-
return value;
|
|
2494
|
-
}
|
|
2495
|
-
mapToDriverValue(value) {
|
|
2496
|
-
return value;
|
|
2497
|
-
}
|
|
2498
|
-
shouldDisableInsert() {
|
|
2499
|
-
return this.config.generated !== undefined && this.config.generated.type !== "byDefault";
|
|
2124
|
+
// node_modules/drizzle-orm/column.js
|
|
2125
|
+
class Column {
|
|
2126
|
+
constructor(table, config) {
|
|
2127
|
+
this.table = table;
|
|
2128
|
+
this.config = config;
|
|
2129
|
+
this.name = config.name;
|
|
2130
|
+
this.keyAsName = config.keyAsName;
|
|
2131
|
+
this.notNull = config.notNull;
|
|
2132
|
+
this.default = config.default;
|
|
2133
|
+
this.defaultFn = config.defaultFn;
|
|
2134
|
+
this.onUpdateFn = config.onUpdateFn;
|
|
2135
|
+
this.hasDefault = config.hasDefault;
|
|
2136
|
+
this.primary = config.primaryKey;
|
|
2137
|
+
this.isUnique = config.isUnique;
|
|
2138
|
+
this.uniqueName = config.uniqueName;
|
|
2139
|
+
this.uniqueType = config.uniqueType;
|
|
2140
|
+
this.dataType = config.dataType;
|
|
2141
|
+
this.columnType = config.columnType;
|
|
2142
|
+
this.generated = config.generated;
|
|
2143
|
+
this.generatedIdentity = config.generatedIdentity;
|
|
2144
|
+
}
|
|
2145
|
+
static [entityKind] = "Column";
|
|
2146
|
+
name;
|
|
2147
|
+
keyAsName;
|
|
2148
|
+
primary;
|
|
2149
|
+
notNull;
|
|
2150
|
+
default;
|
|
2151
|
+
defaultFn;
|
|
2152
|
+
onUpdateFn;
|
|
2153
|
+
hasDefault;
|
|
2154
|
+
isUnique;
|
|
2155
|
+
uniqueName;
|
|
2156
|
+
uniqueType;
|
|
2157
|
+
dataType;
|
|
2158
|
+
columnType;
|
|
2159
|
+
enumValues = undefined;
|
|
2160
|
+
generated = undefined;
|
|
2161
|
+
generatedIdentity = undefined;
|
|
2162
|
+
config;
|
|
2163
|
+
mapFromDriverValue(value) {
|
|
2164
|
+
return value;
|
|
2165
|
+
}
|
|
2166
|
+
mapToDriverValue(value) {
|
|
2167
|
+
return value;
|
|
2168
|
+
}
|
|
2169
|
+
shouldDisableInsert() {
|
|
2170
|
+
return this.config.generated !== undefined && this.config.generated.type !== "byDefault";
|
|
2500
2171
|
}
|
|
2501
2172
|
}
|
|
2502
2173
|
|
|
@@ -3783,9 +3454,9 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
|
|
|
3783
3454
|
|
|
3784
3455
|
// src/cli/index.ts
|
|
3785
3456
|
import { randomUUID } from "crypto";
|
|
3786
|
-
import { readFileSync as
|
|
3787
|
-
import { join as
|
|
3788
|
-
import { homedir as
|
|
3457
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
3458
|
+
import { join as join6 } from "path";
|
|
3459
|
+
import { homedir as homedir6 } from "os";
|
|
3789
3460
|
|
|
3790
3461
|
// node_modules/drizzle-orm/bun-sqlite/driver.js
|
|
3791
3462
|
import { Database } from "bun:sqlite";
|
|
@@ -6068,48 +5739,10 @@ function drizzle(...params) {
|
|
|
6068
5739
|
drizzle2.mock = mock;
|
|
6069
5740
|
})(drizzle || (drizzle = {}));
|
|
6070
5741
|
|
|
6071
|
-
// node_modules/drizzle-orm/migrator.js
|
|
6072
|
-
import crypto from "crypto";
|
|
6073
|
-
import fs from "fs";
|
|
6074
|
-
function readMigrationFiles(config) {
|
|
6075
|
-
const migrationFolderTo = config.migrationsFolder;
|
|
6076
|
-
const migrationQueries = [];
|
|
6077
|
-
const journalPath = `${migrationFolderTo}/meta/_journal.json`;
|
|
6078
|
-
if (!fs.existsSync(journalPath)) {
|
|
6079
|
-
throw new Error(`Can't find meta/_journal.json file`);
|
|
6080
|
-
}
|
|
6081
|
-
const journalAsString = fs.readFileSync(`${migrationFolderTo}/meta/_journal.json`).toString();
|
|
6082
|
-
const journal = JSON.parse(journalAsString);
|
|
6083
|
-
for (const journalEntry of journal.entries) {
|
|
6084
|
-
const migrationPath = `${migrationFolderTo}/${journalEntry.tag}.sql`;
|
|
6085
|
-
try {
|
|
6086
|
-
const query = fs.readFileSync(`${migrationFolderTo}/${journalEntry.tag}.sql`).toString();
|
|
6087
|
-
const result = query.split("--> statement-breakpoint").map((it) => {
|
|
6088
|
-
return it;
|
|
6089
|
-
});
|
|
6090
|
-
migrationQueries.push({
|
|
6091
|
-
sql: result,
|
|
6092
|
-
bps: journalEntry.breakpoints,
|
|
6093
|
-
folderMillis: journalEntry.when,
|
|
6094
|
-
hash: crypto.createHash("sha256").update(query).digest("hex")
|
|
6095
|
-
});
|
|
6096
|
-
} catch {
|
|
6097
|
-
throw new Error(`No file ${migrationPath} found in ${migrationFolderTo} folder`);
|
|
6098
|
-
}
|
|
6099
|
-
}
|
|
6100
|
-
return migrationQueries;
|
|
6101
|
-
}
|
|
6102
|
-
|
|
6103
|
-
// node_modules/drizzle-orm/bun-sqlite/migrator.js
|
|
6104
|
-
function migrate(db, config) {
|
|
6105
|
-
const migrations = readMigrationFiles(config);
|
|
6106
|
-
db.dialect.migrate(migrations, db.session, config);
|
|
6107
|
-
}
|
|
6108
|
-
|
|
6109
5742
|
// src/db/index.ts
|
|
6110
5743
|
import { Database as Database2 } from "bun:sqlite";
|
|
6111
5744
|
import { mkdirSync } from "fs";
|
|
6112
|
-
import { dirname, join
|
|
5745
|
+
import { dirname, join } from "path";
|
|
6113
5746
|
import { homedir } from "os";
|
|
6114
5747
|
|
|
6115
5748
|
// src/db/schema.ts
|
|
@@ -6161,55 +5794,52 @@ var DEFAULT_DB_PATH = join(homedir(), ".brains", "brains.db");
|
|
|
6161
5794
|
function ensureDir(filePath) {
|
|
6162
5795
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
6163
5796
|
}
|
|
5797
|
+
function createTables(sqlite) {
|
|
5798
|
+
sqlite.exec(`
|
|
5799
|
+
CREATE TABLE IF NOT EXISTS fine_tuned_models (
|
|
5800
|
+
id TEXT PRIMARY KEY,
|
|
5801
|
+
base_model TEXT NOT NULL,
|
|
5802
|
+
name TEXT NOT NULL,
|
|
5803
|
+
provider TEXT NOT NULL,
|
|
5804
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
5805
|
+
fine_tune_job_id TEXT,
|
|
5806
|
+
display_name TEXT,
|
|
5807
|
+
description TEXT,
|
|
5808
|
+
collection TEXT,
|
|
5809
|
+
tags TEXT,
|
|
5810
|
+
created_at INTEGER NOT NULL,
|
|
5811
|
+
updated_at INTEGER NOT NULL
|
|
5812
|
+
);
|
|
5813
|
+
|
|
5814
|
+
CREATE TABLE IF NOT EXISTS training_jobs (
|
|
5815
|
+
id TEXT PRIMARY KEY,
|
|
5816
|
+
model_id TEXT NOT NULL REFERENCES fine_tuned_models(id),
|
|
5817
|
+
provider TEXT NOT NULL,
|
|
5818
|
+
status TEXT NOT NULL,
|
|
5819
|
+
started_at INTEGER NOT NULL,
|
|
5820
|
+
finished_at INTEGER,
|
|
5821
|
+
metrics TEXT,
|
|
5822
|
+
error TEXT
|
|
5823
|
+
);
|
|
5824
|
+
|
|
5825
|
+
CREATE TABLE IF NOT EXISTS training_datasets (
|
|
5826
|
+
id TEXT PRIMARY KEY,
|
|
5827
|
+
source TEXT NOT NULL,
|
|
5828
|
+
file_path TEXT NOT NULL,
|
|
5829
|
+
example_count INTEGER NOT NULL,
|
|
5830
|
+
created_at INTEGER NOT NULL,
|
|
5831
|
+
used_in_job_id TEXT REFERENCES training_jobs(id)
|
|
5832
|
+
);
|
|
5833
|
+
`);
|
|
5834
|
+
}
|
|
6164
5835
|
function getDb(dbPath) {
|
|
6165
5836
|
const resolvedPath = dbPath ?? DEFAULT_DB_PATH;
|
|
6166
5837
|
ensureDir(resolvedPath);
|
|
6167
5838
|
const sqlite = new Database2(resolvedPath);
|
|
6168
5839
|
sqlite.run("PRAGMA journal_mode = WAL");
|
|
6169
5840
|
sqlite.run("PRAGMA foreign_keys = ON");
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
const migrationsFolder = resolve(import.meta.dir, "../../drizzle");
|
|
6173
|
-
migrate(db, { migrationsFolder });
|
|
6174
|
-
} catch {
|
|
6175
|
-
sqlite.exec(`
|
|
6176
|
-
CREATE TABLE IF NOT EXISTS fine_tuned_models (
|
|
6177
|
-
id TEXT PRIMARY KEY,
|
|
6178
|
-
base_model TEXT NOT NULL,
|
|
6179
|
-
name TEXT NOT NULL,
|
|
6180
|
-
provider TEXT NOT NULL,
|
|
6181
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
6182
|
-
fine_tune_job_id TEXT,
|
|
6183
|
-
display_name TEXT,
|
|
6184
|
-
description TEXT,
|
|
6185
|
-
collection TEXT,
|
|
6186
|
-
tags TEXT,
|
|
6187
|
-
created_at INTEGER NOT NULL,
|
|
6188
|
-
updated_at INTEGER NOT NULL
|
|
6189
|
-
);
|
|
6190
|
-
|
|
6191
|
-
CREATE TABLE IF NOT EXISTS training_jobs (
|
|
6192
|
-
id TEXT PRIMARY KEY,
|
|
6193
|
-
model_id TEXT NOT NULL REFERENCES fine_tuned_models(id),
|
|
6194
|
-
provider TEXT NOT NULL,
|
|
6195
|
-
status TEXT NOT NULL,
|
|
6196
|
-
started_at INTEGER NOT NULL,
|
|
6197
|
-
finished_at INTEGER,
|
|
6198
|
-
metrics TEXT,
|
|
6199
|
-
error TEXT
|
|
6200
|
-
);
|
|
6201
|
-
|
|
6202
|
-
CREATE TABLE IF NOT EXISTS training_datasets (
|
|
6203
|
-
id TEXT PRIMARY KEY,
|
|
6204
|
-
source TEXT NOT NULL,
|
|
6205
|
-
file_path TEXT NOT NULL,
|
|
6206
|
-
example_count INTEGER NOT NULL,
|
|
6207
|
-
created_at INTEGER NOT NULL,
|
|
6208
|
-
used_in_job_id TEXT REFERENCES training_jobs(id)
|
|
6209
|
-
);
|
|
6210
|
-
`);
|
|
6211
|
-
}
|
|
6212
|
-
return db;
|
|
5841
|
+
createTables(sqlite);
|
|
5842
|
+
return drizzle(sqlite, { schema: exports_schema });
|
|
6213
5843
|
}
|
|
6214
5844
|
|
|
6215
5845
|
// node_modules/openai/internal/qs/formats.mjs
|
|
@@ -7300,8 +6930,8 @@ function _addRequestID(value, response) {
|
|
|
7300
6930
|
|
|
7301
6931
|
class APIPromise extends Promise {
|
|
7302
6932
|
constructor(responsePromise, parseResponse = defaultParseResponse) {
|
|
7303
|
-
super((
|
|
7304
|
-
|
|
6933
|
+
super((resolve) => {
|
|
6934
|
+
resolve(null);
|
|
7305
6935
|
});
|
|
7306
6936
|
this.responsePromise = responsePromise;
|
|
7307
6937
|
this.parseResponse = parseResponse;
|
|
@@ -7812,7 +7442,7 @@ var startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i;
|
|
|
7812
7442
|
var isAbsoluteURL = (url) => {
|
|
7813
7443
|
return startsWithSchemeRegexp.test(url);
|
|
7814
7444
|
};
|
|
7815
|
-
var sleep = (ms) => new Promise((
|
|
7445
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7816
7446
|
var validatePositiveInteger = (name, n) => {
|
|
7817
7447
|
if (typeof n !== "number" || !Number.isInteger(n)) {
|
|
7818
7448
|
throw new OpenAIError(`${name} must be an integer`);
|
|
@@ -8177,12 +7807,12 @@ class EventStream {
|
|
|
8177
7807
|
_EventStream_errored.set(this, false);
|
|
8178
7808
|
_EventStream_aborted.set(this, false);
|
|
8179
7809
|
_EventStream_catchingPromiseCreated.set(this, false);
|
|
8180
|
-
__classPrivateFieldSet3(this, _EventStream_connectedPromise, new Promise((
|
|
8181
|
-
__classPrivateFieldSet3(this, _EventStream_resolveConnectedPromise,
|
|
7810
|
+
__classPrivateFieldSet3(this, _EventStream_connectedPromise, new Promise((resolve, reject) => {
|
|
7811
|
+
__classPrivateFieldSet3(this, _EventStream_resolveConnectedPromise, resolve, "f");
|
|
8182
7812
|
__classPrivateFieldSet3(this, _EventStream_rejectConnectedPromise, reject, "f");
|
|
8183
7813
|
}), "f");
|
|
8184
|
-
__classPrivateFieldSet3(this, _EventStream_endPromise, new Promise((
|
|
8185
|
-
__classPrivateFieldSet3(this, _EventStream_resolveEndPromise,
|
|
7814
|
+
__classPrivateFieldSet3(this, _EventStream_endPromise, new Promise((resolve, reject) => {
|
|
7815
|
+
__classPrivateFieldSet3(this, _EventStream_resolveEndPromise, resolve, "f");
|
|
8186
7816
|
__classPrivateFieldSet3(this, _EventStream_rejectEndPromise, reject, "f");
|
|
8187
7817
|
}), "f");
|
|
8188
7818
|
__classPrivateFieldGet3(this, _EventStream_connectedPromise, "f").catch(() => {});
|
|
@@ -8234,11 +7864,11 @@ class EventStream {
|
|
|
8234
7864
|
return this;
|
|
8235
7865
|
}
|
|
8236
7866
|
emitted(event) {
|
|
8237
|
-
return new Promise((
|
|
7867
|
+
return new Promise((resolve, reject) => {
|
|
8238
7868
|
__classPrivateFieldSet3(this, _EventStream_catchingPromiseCreated, true, "f");
|
|
8239
7869
|
if (event !== "error")
|
|
8240
7870
|
this.once("error", reject);
|
|
8241
|
-
this.once(event,
|
|
7871
|
+
this.once(event, resolve);
|
|
8242
7872
|
});
|
|
8243
7873
|
}
|
|
8244
7874
|
async done() {
|
|
@@ -8396,7 +8026,7 @@ class AssistantStream extends EventStream {
|
|
|
8396
8026
|
if (done) {
|
|
8397
8027
|
return { value: undefined, done: true };
|
|
8398
8028
|
}
|
|
8399
|
-
return new Promise((
|
|
8029
|
+
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: undefined, done: true });
|
|
8400
8030
|
}
|
|
8401
8031
|
const chunk = pushQueue.shift();
|
|
8402
8032
|
return { value: chunk, done: false };
|
|
@@ -9676,11 +9306,11 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
9676
9306
|
}
|
|
9677
9307
|
return this._addChatCompletion(__classPrivateFieldGet6(this, _ChatCompletionStream_instances, "m", _ChatCompletionStream_endRequest).call(this));
|
|
9678
9308
|
}
|
|
9679
|
-
[(_ChatCompletionStream_params = new WeakMap, _ChatCompletionStream_choiceEventStates = new WeakMap, _ChatCompletionStream_currentChatCompletionSnapshot = new WeakMap, _ChatCompletionStream_instances = new WeakSet, _ChatCompletionStream_beginRequest = function
|
|
9309
|
+
[(_ChatCompletionStream_params = new WeakMap, _ChatCompletionStream_choiceEventStates = new WeakMap, _ChatCompletionStream_currentChatCompletionSnapshot = new WeakMap, _ChatCompletionStream_instances = new WeakSet, _ChatCompletionStream_beginRequest = function _ChatCompletionStream_beginRequest2() {
|
|
9680
9310
|
if (this.ended)
|
|
9681
9311
|
return;
|
|
9682
9312
|
__classPrivateFieldSet5(this, _ChatCompletionStream_currentChatCompletionSnapshot, undefined, "f");
|
|
9683
|
-
}, _ChatCompletionStream_getChoiceEventState = function
|
|
9313
|
+
}, _ChatCompletionStream_getChoiceEventState = function _ChatCompletionStream_getChoiceEventState2(choice) {
|
|
9684
9314
|
let state = __classPrivateFieldGet6(this, _ChatCompletionStream_choiceEventStates, "f")[choice.index];
|
|
9685
9315
|
if (state) {
|
|
9686
9316
|
return state;
|
|
@@ -9695,7 +9325,7 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
9695
9325
|
};
|
|
9696
9326
|
__classPrivateFieldGet6(this, _ChatCompletionStream_choiceEventStates, "f")[choice.index] = state;
|
|
9697
9327
|
return state;
|
|
9698
|
-
}, _ChatCompletionStream_addChunk = function
|
|
9328
|
+
}, _ChatCompletionStream_addChunk = function _ChatCompletionStream_addChunk2(chunk) {
|
|
9699
9329
|
if (this.ended)
|
|
9700
9330
|
return;
|
|
9701
9331
|
const completion = __classPrivateFieldGet6(this, _ChatCompletionStream_instances, "m", _ChatCompletionStream_accumulateChatCompletion).call(this, chunk);
|
|
@@ -9762,7 +9392,7 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
9762
9392
|
}
|
|
9763
9393
|
}
|
|
9764
9394
|
}
|
|
9765
|
-
}, _ChatCompletionStream_emitToolCallDoneEvent = function
|
|
9395
|
+
}, _ChatCompletionStream_emitToolCallDoneEvent = function _ChatCompletionStream_emitToolCallDoneEvent2(choiceSnapshot, toolCallIndex) {
|
|
9766
9396
|
const state = __classPrivateFieldGet6(this, _ChatCompletionStream_instances, "m", _ChatCompletionStream_getChoiceEventState).call(this, choiceSnapshot);
|
|
9767
9397
|
if (state.done_tool_calls.has(toolCallIndex)) {
|
|
9768
9398
|
return;
|
|
@@ -9785,7 +9415,7 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
9785
9415
|
} else {
|
|
9786
9416
|
assertNever2(toolCallSnapshot.type);
|
|
9787
9417
|
}
|
|
9788
|
-
}, _ChatCompletionStream_emitContentDoneEvents = function
|
|
9418
|
+
}, _ChatCompletionStream_emitContentDoneEvents = function _ChatCompletionStream_emitContentDoneEvents2(choiceSnapshot) {
|
|
9789
9419
|
const state = __classPrivateFieldGet6(this, _ChatCompletionStream_instances, "m", _ChatCompletionStream_getChoiceEventState).call(this, choiceSnapshot);
|
|
9790
9420
|
if (choiceSnapshot.message.content && !state.content_done) {
|
|
9791
9421
|
state.content_done = true;
|
|
@@ -9807,7 +9437,7 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
9807
9437
|
state.logprobs_refusal_done = true;
|
|
9808
9438
|
this._emit("logprobs.refusal.done", { refusal: choiceSnapshot.logprobs.refusal });
|
|
9809
9439
|
}
|
|
9810
|
-
}, _ChatCompletionStream_endRequest = function
|
|
9440
|
+
}, _ChatCompletionStream_endRequest = function _ChatCompletionStream_endRequest2() {
|
|
9811
9441
|
if (this.ended) {
|
|
9812
9442
|
throw new OpenAIError(`stream has ended, this shouldn't happen`);
|
|
9813
9443
|
}
|
|
@@ -9818,13 +9448,13 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
9818
9448
|
__classPrivateFieldSet5(this, _ChatCompletionStream_currentChatCompletionSnapshot, undefined, "f");
|
|
9819
9449
|
__classPrivateFieldSet5(this, _ChatCompletionStream_choiceEventStates, [], "f");
|
|
9820
9450
|
return finalizeChatCompletion(snapshot, __classPrivateFieldGet6(this, _ChatCompletionStream_params, "f"));
|
|
9821
|
-
}, _ChatCompletionStream_getAutoParseableResponseFormat = function
|
|
9451
|
+
}, _ChatCompletionStream_getAutoParseableResponseFormat = function _ChatCompletionStream_getAutoParseableResponseFormat2() {
|
|
9822
9452
|
const responseFormat = __classPrivateFieldGet6(this, _ChatCompletionStream_params, "f")?.response_format;
|
|
9823
9453
|
if (isAutoParsableResponseFormat(responseFormat)) {
|
|
9824
9454
|
return responseFormat;
|
|
9825
9455
|
}
|
|
9826
9456
|
return null;
|
|
9827
|
-
}, _ChatCompletionStream_accumulateChatCompletion = function
|
|
9457
|
+
}, _ChatCompletionStream_accumulateChatCompletion = function _ChatCompletionStream_accumulateChatCompletion2(chunk) {
|
|
9828
9458
|
var _a, _b, _c, _d;
|
|
9829
9459
|
let snapshot = __classPrivateFieldGet6(this, _ChatCompletionStream_currentChatCompletionSnapshot, "f");
|
|
9830
9460
|
const { choices, ...rest } = chunk;
|
|
@@ -9961,7 +9591,7 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
9961
9591
|
if (done) {
|
|
9962
9592
|
return { value: undefined, done: true };
|
|
9963
9593
|
}
|
|
9964
|
-
return new Promise((
|
|
9594
|
+
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: undefined, done: true });
|
|
9965
9595
|
}
|
|
9966
9596
|
const chunk = pushQueue.shift();
|
|
9967
9597
|
return { value: chunk, done: false };
|
|
@@ -11050,11 +10680,11 @@ class ResponseStream extends EventStream {
|
|
|
11050
10680
|
}
|
|
11051
10681
|
return __classPrivateFieldGet7(this, _ResponseStream_instances, "m", _ResponseStream_endRequest).call(this);
|
|
11052
10682
|
}
|
|
11053
|
-
[(_ResponseStream_params = new WeakMap, _ResponseStream_currentResponseSnapshot = new WeakMap, _ResponseStream_finalResponse = new WeakMap, _ResponseStream_instances = new WeakSet, _ResponseStream_beginRequest = function
|
|
10683
|
+
[(_ResponseStream_params = new WeakMap, _ResponseStream_currentResponseSnapshot = new WeakMap, _ResponseStream_finalResponse = new WeakMap, _ResponseStream_instances = new WeakSet, _ResponseStream_beginRequest = function _ResponseStream_beginRequest2() {
|
|
11054
10684
|
if (this.ended)
|
|
11055
10685
|
return;
|
|
11056
10686
|
__classPrivateFieldSet6(this, _ResponseStream_currentResponseSnapshot, undefined, "f");
|
|
11057
|
-
}, _ResponseStream_addEvent = function
|
|
10687
|
+
}, _ResponseStream_addEvent = function _ResponseStream_addEvent2(event, starting_after) {
|
|
11058
10688
|
if (this.ended)
|
|
11059
10689
|
return;
|
|
11060
10690
|
const maybeEmit = (name, event2) => {
|
|
@@ -11102,7 +10732,7 @@ class ResponseStream extends EventStream {
|
|
|
11102
10732
|
maybeEmit(event.type, event);
|
|
11103
10733
|
break;
|
|
11104
10734
|
}
|
|
11105
|
-
}, _ResponseStream_endRequest = function
|
|
10735
|
+
}, _ResponseStream_endRequest = function _ResponseStream_endRequest2() {
|
|
11106
10736
|
if (this.ended) {
|
|
11107
10737
|
throw new OpenAIError(`stream has ended, this shouldn't happen`);
|
|
11108
10738
|
}
|
|
@@ -11114,7 +10744,7 @@ class ResponseStream extends EventStream {
|
|
|
11114
10744
|
const parsedResponse = finalizeResponse(snapshot, __classPrivateFieldGet7(this, _ResponseStream_params, "f"));
|
|
11115
10745
|
__classPrivateFieldSet6(this, _ResponseStream_finalResponse, parsedResponse, "f");
|
|
11116
10746
|
return parsedResponse;
|
|
11117
|
-
}, _ResponseStream_accumulateResponse = function
|
|
10747
|
+
}, _ResponseStream_accumulateResponse = function _ResponseStream_accumulateResponse2(event) {
|
|
11118
10748
|
let snapshot = __classPrivateFieldGet7(this, _ResponseStream_currentResponseSnapshot, "f");
|
|
11119
10749
|
if (!snapshot) {
|
|
11120
10750
|
if (event.type !== "response.created") {
|
|
@@ -11210,7 +10840,7 @@ class ResponseStream extends EventStream {
|
|
|
11210
10840
|
if (done) {
|
|
11211
10841
|
return { value: undefined, done: true };
|
|
11212
10842
|
}
|
|
11213
|
-
return new Promise((
|
|
10843
|
+
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: undefined, done: true });
|
|
11214
10844
|
}
|
|
11215
10845
|
const event = pushQueue.shift();
|
|
11216
10846
|
return { value: event, done: false };
|
|
@@ -11697,183 +11327,92 @@ var _deployments_endpoints = new Set([
|
|
|
11697
11327
|
var openai_default = OpenAI;
|
|
11698
11328
|
|
|
11699
11329
|
// src/lib/providers/openai.ts
|
|
11700
|
-
import { readFileSync
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
import { homedir as homedir2 } from "os";
|
|
11706
|
-
var CONFIG_KEYS = ["OPENAI_API_KEY", "THINKER_LABS_API_KEY", "THINKER_LABS_BASE_URL"];
|
|
11707
|
-
var CONFIG_PATH = join2(homedir2(), ".brains", "config.json");
|
|
11708
|
-
function readConfigFile() {
|
|
11709
|
-
if (!existsSync(CONFIG_PATH))
|
|
11710
|
-
return {};
|
|
11711
|
-
try {
|
|
11712
|
-
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
11713
|
-
} catch {
|
|
11714
|
-
return {};
|
|
11330
|
+
import { readFileSync } from "fs";
|
|
11331
|
+
function getClient() {
|
|
11332
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
11333
|
+
if (!apiKey) {
|
|
11334
|
+
throw new Error("OPENAI_API_KEY environment variable is required");
|
|
11715
11335
|
}
|
|
11716
|
-
}
|
|
11717
|
-
function writeConfigFile(data) {
|
|
11718
|
-
mkdirSync2(dirname2(CONFIG_PATH), { recursive: true });
|
|
11719
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2) + `
|
|
11720
|
-
`, "utf-8");
|
|
11721
|
-
}
|
|
11722
|
-
function getConfigValue(key) {
|
|
11723
|
-
if (process.env[key])
|
|
11724
|
-
return process.env[key];
|
|
11725
|
-
return readConfigFile()[key];
|
|
11726
|
-
}
|
|
11727
|
-
function setConfigValue(key, value) {
|
|
11728
|
-
const data = readConfigFile();
|
|
11729
|
-
data[key] = value;
|
|
11730
|
-
writeConfigFile(data);
|
|
11731
|
-
}
|
|
11732
|
-
function listConfig() {
|
|
11733
|
-
const file = readConfigFile();
|
|
11734
|
-
return CONFIG_KEYS.map((key) => {
|
|
11735
|
-
if (process.env[key])
|
|
11736
|
-
return { key, value: process.env[key], source: "env" };
|
|
11737
|
-
if (file[key])
|
|
11738
|
-
return { key, value: file[key], source: "file" };
|
|
11739
|
-
return { key, value: "", source: "unset" };
|
|
11740
|
-
});
|
|
11741
|
-
}
|
|
11742
|
-
function deleteConfigValue(key) {
|
|
11743
|
-
const data = readConfigFile();
|
|
11744
|
-
delete data[key];
|
|
11745
|
-
writeConfigFile(data);
|
|
11746
|
-
}
|
|
11747
|
-
|
|
11748
|
-
// src/lib/retry.ts
|
|
11749
|
-
var RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
|
|
11750
|
-
function isRetryableError(err) {
|
|
11751
|
-
if (err instanceof Error) {
|
|
11752
|
-
if (err.message.includes("fetch failed") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT")) {
|
|
11753
|
-
return true;
|
|
11754
|
-
}
|
|
11755
|
-
const match = err.message.match(/^(\d{3})\s/);
|
|
11756
|
-
if (match) {
|
|
11757
|
-
const status = parseInt(match[1], 10);
|
|
11758
|
-
return RETRYABLE_STATUS_CODES.has(status);
|
|
11759
|
-
}
|
|
11760
|
-
const tlMatch = err.message.match(/API error (\d{3})/);
|
|
11761
|
-
if (tlMatch) {
|
|
11762
|
-
const status = parseInt(tlMatch[1], 10);
|
|
11763
|
-
return RETRYABLE_STATUS_CODES.has(status);
|
|
11764
|
-
}
|
|
11765
|
-
}
|
|
11766
|
-
return false;
|
|
11767
|
-
}
|
|
11768
|
-
async function withRetry(fn, options = {}) {
|
|
11769
|
-
const { maxAttempts = 3, baseDelayMs = 1000, onRetry } = options;
|
|
11770
|
-
let lastError = new Error("Unknown error");
|
|
11771
|
-
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
11772
|
-
try {
|
|
11773
|
-
return await fn();
|
|
11774
|
-
} catch (err) {
|
|
11775
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
11776
|
-
if (attempt === maxAttempts || !isRetryableError(lastError)) {
|
|
11777
|
-
throw lastError;
|
|
11778
|
-
}
|
|
11779
|
-
const delayMs = baseDelayMs * Math.pow(2, attempt - 1);
|
|
11780
|
-
onRetry?.(attempt, delayMs, lastError);
|
|
11781
|
-
await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
11782
|
-
}
|
|
11783
|
-
}
|
|
11784
|
-
throw lastError;
|
|
11785
|
-
}
|
|
11786
|
-
|
|
11787
|
-
// src/lib/providers/openai.ts
|
|
11788
|
-
function getClient() {
|
|
11789
|
-
const apiKey = getConfigValue("OPENAI_API_KEY");
|
|
11790
|
-
if (!apiKey) {
|
|
11791
|
-
throw new Error("OPENAI_API_KEY is not set. Run: brains config set OPENAI_API_KEY <key>");
|
|
11792
|
-
}
|
|
11793
|
-
return new openai_default({ apiKey });
|
|
11336
|
+
return new openai_default({ apiKey });
|
|
11794
11337
|
}
|
|
11795
11338
|
async function uploadTrainingFile(filePath) {
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11339
|
+
const client = getClient();
|
|
11340
|
+
const fileContent = readFileSync(filePath);
|
|
11341
|
+
const blob2 = new Blob([fileContent], { type: "application/jsonl" });
|
|
11342
|
+
const file = new File([blob2], filePath.split("/").pop() ?? "training.jsonl", { type: "application/jsonl" });
|
|
11343
|
+
const response = await client.files.create({
|
|
11344
|
+
file,
|
|
11345
|
+
purpose: "fine-tune"
|
|
11803
11346
|
});
|
|
11347
|
+
return { fileId: response.id };
|
|
11804
11348
|
}
|
|
11805
11349
|
async function createFineTuneJob(fileId, baseModel, suffix) {
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
}
|
|
11350
|
+
const client = getClient();
|
|
11351
|
+
const params = {
|
|
11352
|
+
training_file: fileId,
|
|
11353
|
+
model: baseModel
|
|
11354
|
+
};
|
|
11355
|
+
if (suffix) {
|
|
11356
|
+
params.suffix = suffix;
|
|
11357
|
+
}
|
|
11358
|
+
const response = await client.fineTuning.jobs.create(params);
|
|
11359
|
+
return { jobId: response.id, status: response.status };
|
|
11814
11360
|
}
|
|
11815
11361
|
async function getFineTuneStatus(jobId) {
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
error: response.error?.message ?? undefined
|
|
11825
|
-
};
|
|
11826
|
-
});
|
|
11362
|
+
const client = getClient();
|
|
11363
|
+
const response = await client.fineTuning.jobs.retrieve(jobId);
|
|
11364
|
+
return {
|
|
11365
|
+
jobId: response.id,
|
|
11366
|
+
status: response.status,
|
|
11367
|
+
fineTunedModel: response.fine_tuned_model ?? undefined,
|
|
11368
|
+
error: response.error?.message ?? undefined
|
|
11369
|
+
};
|
|
11827
11370
|
}
|
|
11828
11371
|
async function listFineTunedModels() {
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
}));
|
|
11838
|
-
});
|
|
11372
|
+
const client = getClient();
|
|
11373
|
+
const jobs = await client.fineTuning.jobs.list();
|
|
11374
|
+
return jobs.data.map((job) => ({
|
|
11375
|
+
id: job.id,
|
|
11376
|
+
model: job.fine_tuned_model ?? job.model,
|
|
11377
|
+
status: job.status,
|
|
11378
|
+
created: job.created_at
|
|
11379
|
+
}));
|
|
11839
11380
|
}
|
|
11840
11381
|
|
|
11841
11382
|
// src/lib/providers/thinker-labs.ts
|
|
11842
11383
|
var DEFAULT_BASE_URL = "https://api.thinkerlabs.ai/v1";
|
|
11843
11384
|
function getConfig() {
|
|
11844
|
-
const apiKey =
|
|
11845
|
-
const baseUrl =
|
|
11385
|
+
const apiKey = process.env.THINKER_LABS_API_KEY;
|
|
11386
|
+
const baseUrl = process.env.THINKER_LABS_BASE_URL ?? DEFAULT_BASE_URL;
|
|
11846
11387
|
if (!apiKey)
|
|
11847
|
-
throw new Error("THINKER_LABS_API_KEY
|
|
11388
|
+
throw new Error("THINKER_LABS_API_KEY environment variable is required");
|
|
11848
11389
|
return { apiKey, baseUrl };
|
|
11849
11390
|
}
|
|
11850
11391
|
async function request(method, path, body, file) {
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
return res.json();
|
|
11876
|
-
});
|
|
11392
|
+
const { apiKey, baseUrl } = getConfig();
|
|
11393
|
+
const headers = { Authorization: `Bearer ${apiKey}` };
|
|
11394
|
+
let fetchBody;
|
|
11395
|
+
if (file) {
|
|
11396
|
+
const form = new FormData;
|
|
11397
|
+
form.append("file", new Blob([file.data], { type: "text/plain" }), file.name);
|
|
11398
|
+
if (body) {
|
|
11399
|
+
for (const [k, v] of Object.entries(body)) {
|
|
11400
|
+
form.append(k, v);
|
|
11401
|
+
}
|
|
11402
|
+
}
|
|
11403
|
+
fetchBody = form;
|
|
11404
|
+
} else if (body) {
|
|
11405
|
+
headers["Content-Type"] = "application/json";
|
|
11406
|
+
fetchBody = JSON.stringify(body);
|
|
11407
|
+
}
|
|
11408
|
+
const res = await fetch(`${baseUrl}${path}`, { method, headers, body: fetchBody });
|
|
11409
|
+
if (!res.ok) {
|
|
11410
|
+
const text2 = await res.text();
|
|
11411
|
+
throw new Error(`Thinker Labs API error ${res.status}: ${text2}`);
|
|
11412
|
+
}
|
|
11413
|
+
if (res.status === 204)
|
|
11414
|
+
return;
|
|
11415
|
+
return res.json();
|
|
11877
11416
|
}
|
|
11878
11417
|
async function uploadTrainingData(filePath) {
|
|
11879
11418
|
const fileContent = await Bun.file(filePath).text();
|
|
@@ -11986,18 +11525,360 @@ function printInfo(message) {
|
|
|
11986
11525
|
console.log(chalk.dim(" " + message));
|
|
11987
11526
|
}
|
|
11988
11527
|
|
|
11528
|
+
// src/lib/gatherers/todos.ts
|
|
11529
|
+
import { Database as Database3 } from "bun:sqlite";
|
|
11530
|
+
import { homedir as homedir2 } from "os";
|
|
11531
|
+
import { join as join2 } from "path";
|
|
11532
|
+
var SYSTEM_PROMPT = "You are a task management assistant that helps users create, update, search, and manage tasks and projects.";
|
|
11533
|
+
function taskToCreateExample(task) {
|
|
11534
|
+
const userMsg = `Create a task: ${task.title}${task.description ? `
|
|
11535
|
+
|
|
11536
|
+
Description: ${task.description}` : ""}`;
|
|
11537
|
+
const taskDetails = {
|
|
11538
|
+
id: task.short_id ?? task.id,
|
|
11539
|
+
title: task.title,
|
|
11540
|
+
description: task.description ?? "",
|
|
11541
|
+
status: task.status,
|
|
11542
|
+
priority: task.priority,
|
|
11543
|
+
tags: JSON.parse(task.tags ?? "[]"),
|
|
11544
|
+
created_at: task.created_at
|
|
11545
|
+
};
|
|
11546
|
+
return {
|
|
11547
|
+
messages: [
|
|
11548
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
11549
|
+
{ role: "user", content: userMsg },
|
|
11550
|
+
{ role: "assistant", content: `Created task: ${JSON.stringify(taskDetails, null, 2)}` }
|
|
11551
|
+
]
|
|
11552
|
+
};
|
|
11553
|
+
}
|
|
11554
|
+
function taskToStatusUpdateExample(task) {
|
|
11555
|
+
if (!task.completed_at && task.status === "pending")
|
|
11556
|
+
return null;
|
|
11557
|
+
const id = task.short_id ?? task.id;
|
|
11558
|
+
return {
|
|
11559
|
+
messages: [
|
|
11560
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
11561
|
+
{ role: "user", content: `Mark task ${id} as ${task.status}` },
|
|
11562
|
+
{ role: "assistant", content: `Task ${id} has been updated to status: ${task.status}. ${task.completed_at ? `Completed at: ${task.completed_at}` : ""}`.trim() }
|
|
11563
|
+
]
|
|
11564
|
+
};
|
|
11565
|
+
}
|
|
11566
|
+
function taskToSearchExample(tasks, query) {
|
|
11567
|
+
const matched = tasks.filter((t) => t.title.toLowerCase().includes(query.toLowerCase())).slice(0, 5);
|
|
11568
|
+
return {
|
|
11569
|
+
messages: [
|
|
11570
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
11571
|
+
{ role: "user", content: `Search tasks for: "${query}"` },
|
|
11572
|
+
{
|
|
11573
|
+
role: "assistant",
|
|
11574
|
+
content: matched.length > 0 ? `Found ${matched.length} task(s):
|
|
11575
|
+
${matched.map((t) => `- [${t.short_id ?? t.id}] ${t.title} (${t.status})`).join(`
|
|
11576
|
+
`)}` : `No tasks found matching "${query}".`
|
|
11577
|
+
}
|
|
11578
|
+
]
|
|
11579
|
+
};
|
|
11580
|
+
}
|
|
11581
|
+
async function gatherFromTodos(options = {}) {
|
|
11582
|
+
const dbPath = join2(homedir2(), ".todos", "todos.db");
|
|
11583
|
+
const db = new Database3(dbPath, { readonly: true, create: false });
|
|
11584
|
+
try {
|
|
11585
|
+
let query = "SELECT * FROM tasks WHERE 1=1";
|
|
11586
|
+
const params = [];
|
|
11587
|
+
if (options.since) {
|
|
11588
|
+
query += " AND created_at >= ?";
|
|
11589
|
+
params.push(options.since.toISOString());
|
|
11590
|
+
}
|
|
11591
|
+
query += " ORDER BY created_at DESC";
|
|
11592
|
+
if (options.limit) {
|
|
11593
|
+
query += " LIMIT ?";
|
|
11594
|
+
params.push(options.limit * 2);
|
|
11595
|
+
}
|
|
11596
|
+
const tasks = db.query(query).all(...params);
|
|
11597
|
+
const examples = [];
|
|
11598
|
+
for (const task of tasks) {
|
|
11599
|
+
examples.push(taskToCreateExample(task));
|
|
11600
|
+
const statusEx = taskToStatusUpdateExample(task);
|
|
11601
|
+
if (statusEx)
|
|
11602
|
+
examples.push(statusEx);
|
|
11603
|
+
}
|
|
11604
|
+
const searchTerms = ["urgent", "fix", "implement", "create", "update", "review"];
|
|
11605
|
+
for (const term of searchTerms) {
|
|
11606
|
+
examples.push(taskToSearchExample(tasks, term));
|
|
11607
|
+
}
|
|
11608
|
+
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
11609
|
+
return {
|
|
11610
|
+
source: "todos",
|
|
11611
|
+
examples: finalExamples,
|
|
11612
|
+
count: finalExamples.length
|
|
11613
|
+
};
|
|
11614
|
+
} finally {
|
|
11615
|
+
db.close();
|
|
11616
|
+
}
|
|
11617
|
+
}
|
|
11618
|
+
|
|
11619
|
+
// src/lib/gatherers/mementos.ts
|
|
11620
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
11621
|
+
import { homedir as homedir3 } from "os";
|
|
11622
|
+
import { join as join3 } from "path";
|
|
11623
|
+
var SYSTEM_PROMPT2 = "You are an AI assistant with persistent memory. You can remember and recall information across sessions to provide better, more personalized assistance.";
|
|
11624
|
+
function memoryToRecallExample(memory) {
|
|
11625
|
+
return {
|
|
11626
|
+
messages: [
|
|
11627
|
+
{ role: "system", content: SYSTEM_PROMPT2 },
|
|
11628
|
+
{ role: "user", content: `What do you remember about "${memory.key}"?` },
|
|
11629
|
+
{
|
|
11630
|
+
role: "assistant",
|
|
11631
|
+
content: memory.summary ? `${memory.value}
|
|
11632
|
+
|
|
11633
|
+
Summary: ${memory.summary}` : memory.value
|
|
11634
|
+
}
|
|
11635
|
+
]
|
|
11636
|
+
};
|
|
11637
|
+
}
|
|
11638
|
+
function memoryToSaveExample(memory) {
|
|
11639
|
+
const tags = JSON.parse(memory.tags ?? "[]");
|
|
11640
|
+
return {
|
|
11641
|
+
messages: [
|
|
11642
|
+
{ role: "system", content: SYSTEM_PROMPT2 },
|
|
11643
|
+
{
|
|
11644
|
+
role: "user",
|
|
11645
|
+
content: `Remember this for me: ${memory.key} = ${memory.value}${tags.length ? ` (tags: ${tags.join(", ")})` : ""}`
|
|
11646
|
+
},
|
|
11647
|
+
{
|
|
11648
|
+
role: "assistant",
|
|
11649
|
+
content: `Saved to memory: "${memory.key}" with ${memory.category} category, importance ${memory.importance}/10, scope: ${memory.scope}.`
|
|
11650
|
+
}
|
|
11651
|
+
]
|
|
11652
|
+
};
|
|
11653
|
+
}
|
|
11654
|
+
function memoryToSearchExample(memories, category) {
|
|
11655
|
+
const matched = memories.filter((m) => m.category === category && m.status === "active").slice(0, 5);
|
|
11656
|
+
return {
|
|
11657
|
+
messages: [
|
|
11658
|
+
{ role: "system", content: SYSTEM_PROMPT2 },
|
|
11659
|
+
{ role: "user", content: `What ${category} memories do you have?` },
|
|
11660
|
+
{
|
|
11661
|
+
role: "assistant",
|
|
11662
|
+
content: matched.length > 0 ? `Here are my ${category} memories:
|
|
11663
|
+
${matched.map((m) => `- ${m.key}: ${m.value.slice(0, 120)}${m.value.length > 120 ? "..." : ""}`).join(`
|
|
11664
|
+
`)}` : `I don't have any ${category} memories stored yet.`
|
|
11665
|
+
}
|
|
11666
|
+
]
|
|
11667
|
+
};
|
|
11668
|
+
}
|
|
11669
|
+
async function gatherFromMementos(options = {}) {
|
|
11670
|
+
const dbPath = join3(homedir3(), ".mementos", "mementos.db");
|
|
11671
|
+
const db = new Database4(dbPath, { readonly: true, create: false });
|
|
11672
|
+
try {
|
|
11673
|
+
let query = "SELECT * FROM memories WHERE status = 'active'";
|
|
11674
|
+
const params = [];
|
|
11675
|
+
if (options.since) {
|
|
11676
|
+
query += " AND created_at >= ?";
|
|
11677
|
+
params.push(options.since.toISOString());
|
|
11678
|
+
}
|
|
11679
|
+
query += " ORDER BY importance DESC, created_at DESC";
|
|
11680
|
+
if (options.limit) {
|
|
11681
|
+
query += " LIMIT ?";
|
|
11682
|
+
params.push(options.limit * 3);
|
|
11683
|
+
}
|
|
11684
|
+
const memories = db.query(query).all(...params);
|
|
11685
|
+
const examples = [];
|
|
11686
|
+
for (const memory of memories) {
|
|
11687
|
+
examples.push(memoryToRecallExample(memory));
|
|
11688
|
+
examples.push(memoryToSaveExample(memory));
|
|
11689
|
+
}
|
|
11690
|
+
const categories = [...new Set(memories.map((m) => m.category))];
|
|
11691
|
+
for (const category of categories) {
|
|
11692
|
+
examples.push(memoryToSearchExample(memories, category));
|
|
11693
|
+
}
|
|
11694
|
+
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
11695
|
+
return {
|
|
11696
|
+
source: "mementos",
|
|
11697
|
+
examples: finalExamples,
|
|
11698
|
+
count: finalExamples.length
|
|
11699
|
+
};
|
|
11700
|
+
} finally {
|
|
11701
|
+
db.close();
|
|
11702
|
+
}
|
|
11703
|
+
}
|
|
11704
|
+
|
|
11705
|
+
// src/lib/gatherers/conversations.ts
|
|
11706
|
+
import { Database as Database5 } from "bun:sqlite";
|
|
11707
|
+
import { homedir as homedir4 } from "os";
|
|
11708
|
+
import { join as join4 } from "path";
|
|
11709
|
+
var SYSTEM_PROMPT3 = "You are a helpful AI assistant participating in multi-agent conversations. You communicate clearly and collaboratively with other agents and users.";
|
|
11710
|
+
function windowToExample(window2) {
|
|
11711
|
+
if (window2.length < 2)
|
|
11712
|
+
return null;
|
|
11713
|
+
const messages = [
|
|
11714
|
+
{ role: "system", content: SYSTEM_PROMPT3 }
|
|
11715
|
+
];
|
|
11716
|
+
for (let i = 0;i < window2.length - 1; i++) {
|
|
11717
|
+
const msg = window2[i];
|
|
11718
|
+
if (!msg)
|
|
11719
|
+
continue;
|
|
11720
|
+
const role = i % 2 === 0 ? "user" : "assistant";
|
|
11721
|
+
messages.push({
|
|
11722
|
+
role,
|
|
11723
|
+
content: `[${msg.from_agent} \u2192 ${msg.to_agent ?? msg.space ?? "all"}]: ${msg.content}`
|
|
11724
|
+
});
|
|
11725
|
+
}
|
|
11726
|
+
const last = window2[window2.length - 1];
|
|
11727
|
+
if (!last)
|
|
11728
|
+
return null;
|
|
11729
|
+
messages.push({
|
|
11730
|
+
role: "assistant",
|
|
11731
|
+
content: `[${last.from_agent} \u2192 ${last.to_agent ?? last.space ?? "all"}]: ${last.content}`
|
|
11732
|
+
});
|
|
11733
|
+
return { messages };
|
|
11734
|
+
}
|
|
11735
|
+
async function gatherFromConversations(options = {}) {
|
|
11736
|
+
const dbPath = join4(homedir4(), ".conversations", "messages.db");
|
|
11737
|
+
const db = new Database5(dbPath, { readonly: true, create: false });
|
|
11738
|
+
try {
|
|
11739
|
+
let query = "SELECT * FROM messages WHERE 1=1";
|
|
11740
|
+
const params = [];
|
|
11741
|
+
if (options.since) {
|
|
11742
|
+
query += " AND created_at >= ?";
|
|
11743
|
+
params.push(options.since.toISOString());
|
|
11744
|
+
}
|
|
11745
|
+
query += " ORDER BY session_id, created_at ASC";
|
|
11746
|
+
const allMessages = db.query(query).all(...params);
|
|
11747
|
+
const sessions = new Map;
|
|
11748
|
+
for (const msg of allMessages) {
|
|
11749
|
+
const msgs = sessions.get(msg.session_id) ?? [];
|
|
11750
|
+
msgs.push(msg);
|
|
11751
|
+
sessions.set(msg.session_id, msgs);
|
|
11752
|
+
}
|
|
11753
|
+
const examples = [];
|
|
11754
|
+
const windowSize = 4;
|
|
11755
|
+
for (const [, sessionMsgs] of sessions) {
|
|
11756
|
+
if (sessionMsgs.length < 2)
|
|
11757
|
+
continue;
|
|
11758
|
+
for (let start = 0;start <= sessionMsgs.length - 2; start++) {
|
|
11759
|
+
const end = Math.min(start + windowSize, sessionMsgs.length);
|
|
11760
|
+
const window2 = sessionMsgs.slice(start, end);
|
|
11761
|
+
const example = windowToExample(window2);
|
|
11762
|
+
if (example)
|
|
11763
|
+
examples.push(example);
|
|
11764
|
+
}
|
|
11765
|
+
}
|
|
11766
|
+
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
11767
|
+
return {
|
|
11768
|
+
source: "conversations",
|
|
11769
|
+
examples: finalExamples,
|
|
11770
|
+
count: finalExamples.length
|
|
11771
|
+
};
|
|
11772
|
+
} finally {
|
|
11773
|
+
db.close();
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
|
|
11777
|
+
// src/lib/gatherers/sessions.ts
|
|
11778
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
11779
|
+
import { existsSync } from "fs";
|
|
11780
|
+
import { join as join5 } from "path";
|
|
11781
|
+
import { homedir as homedir5 } from "os";
|
|
11782
|
+
var SYSTEM_PROMPT4 = "You are Claude Code, an AI assistant built by Anthropic that helps developers with coding, architecture, debugging, and software engineering tasks.";
|
|
11783
|
+
function extractText(content) {
|
|
11784
|
+
if (typeof content === "string")
|
|
11785
|
+
return content;
|
|
11786
|
+
return content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join(`
|
|
11787
|
+
`).trim();
|
|
11788
|
+
}
|
|
11789
|
+
async function gatherFromSessions(options = {}) {
|
|
11790
|
+
const { limit: limit2 = 1000 } = options;
|
|
11791
|
+
const examples = [];
|
|
11792
|
+
const claudeDir = join5(homedir5(), ".claude", "projects");
|
|
11793
|
+
if (!existsSync(claudeDir)) {
|
|
11794
|
+
return { source: "sessions", examples: [], count: 0 };
|
|
11795
|
+
}
|
|
11796
|
+
const projectDirs = await readdir(claudeDir).catch(() => []);
|
|
11797
|
+
for (const projectDir of projectDirs) {
|
|
11798
|
+
if (examples.length >= limit2)
|
|
11799
|
+
break;
|
|
11800
|
+
const projectPath = join5(claudeDir, projectDir);
|
|
11801
|
+
const files = await readdir(projectPath).catch(() => []);
|
|
11802
|
+
for (const file of files) {
|
|
11803
|
+
if (examples.length >= limit2)
|
|
11804
|
+
break;
|
|
11805
|
+
if (!file.endsWith(".jsonl"))
|
|
11806
|
+
continue;
|
|
11807
|
+
const filePath = join5(projectPath, file);
|
|
11808
|
+
if (options.since) {
|
|
11809
|
+
const fileStat = await stat(filePath).catch(() => null);
|
|
11810
|
+
if (fileStat && fileStat.mtime < options.since)
|
|
11811
|
+
continue;
|
|
11812
|
+
}
|
|
11813
|
+
const content = await readFile(filePath, "utf-8").catch(() => "");
|
|
11814
|
+
if (!content.trim())
|
|
11815
|
+
continue;
|
|
11816
|
+
const lines = content.trim().split(`
|
|
11817
|
+
`);
|
|
11818
|
+
const turns = [];
|
|
11819
|
+
for (const line of lines) {
|
|
11820
|
+
try {
|
|
11821
|
+
const entry = JSON.parse(line);
|
|
11822
|
+
if ((entry.type === "user" || entry.type === "human") && entry.message?.content) {
|
|
11823
|
+
const text2 = extractText(entry.message.content);
|
|
11824
|
+
if (text2.trim())
|
|
11825
|
+
turns.push({ role: "user", content: text2.trim() });
|
|
11826
|
+
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
11827
|
+
const text2 = extractText(entry.message.content);
|
|
11828
|
+
if (text2.trim())
|
|
11829
|
+
turns.push({ role: "assistant", content: text2.trim() });
|
|
11830
|
+
}
|
|
11831
|
+
} catch {}
|
|
11832
|
+
}
|
|
11833
|
+
const windowSize = 6;
|
|
11834
|
+
for (let start = 0;start < turns.length - 1 && examples.length < limit2; start++) {
|
|
11835
|
+
const window2 = turns.slice(start, start + windowSize);
|
|
11836
|
+
if (!window2[0] || window2[0].role !== "user")
|
|
11837
|
+
continue;
|
|
11838
|
+
const lastAssistantIdx = window2.map((t) => t.role).lastIndexOf("assistant");
|
|
11839
|
+
if (lastAssistantIdx < 1)
|
|
11840
|
+
continue;
|
|
11841
|
+
const usedTurns = window2.slice(0, lastAssistantIdx + 1);
|
|
11842
|
+
examples.push({
|
|
11843
|
+
messages: [
|
|
11844
|
+
{ role: "system", content: SYSTEM_PROMPT4 },
|
|
11845
|
+
...usedTurns
|
|
11846
|
+
]
|
|
11847
|
+
});
|
|
11848
|
+
}
|
|
11849
|
+
}
|
|
11850
|
+
}
|
|
11851
|
+
return { source: "sessions", examples, count: examples.length };
|
|
11852
|
+
}
|
|
11853
|
+
// src/lib/gatherers/index.ts
|
|
11854
|
+
var ALL_SOURCES = ["todos", "mementos", "conversations", "sessions"];
|
|
11855
|
+
async function gatherAll(sources, options = {}) {
|
|
11856
|
+
const targets = sources.includes("all") ? [...ALL_SOURCES] : sources;
|
|
11857
|
+
const results = await Promise.allSettled(targets.map((source) => {
|
|
11858
|
+
switch (source) {
|
|
11859
|
+
case "todos":
|
|
11860
|
+
return gatherFromTodos(options);
|
|
11861
|
+
case "mementos":
|
|
11862
|
+
return gatherFromMementos(options);
|
|
11863
|
+
case "conversations":
|
|
11864
|
+
return gatherFromConversations(options);
|
|
11865
|
+
case "sessions":
|
|
11866
|
+
return gatherFromSessions(options);
|
|
11867
|
+
default:
|
|
11868
|
+
return Promise.resolve({ source, examples: [], count: 0 });
|
|
11869
|
+
}
|
|
11870
|
+
}));
|
|
11871
|
+
return results.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
11872
|
+
}
|
|
11873
|
+
|
|
11989
11874
|
// src/cli/index.ts
|
|
11990
11875
|
var program2 = new Command;
|
|
11991
11876
|
program2.name("brains").description("Fine-tuned model tracker and trainer").version("0.0.1");
|
|
11992
11877
|
var modelsCmd = program2.command("models").description("Manage tracked fine-tuned models");
|
|
11993
|
-
modelsCmd.command("list").description("List all tracked fine-tuned models").
|
|
11878
|
+
modelsCmd.command("list").description("List all tracked fine-tuned models").action(async () => {
|
|
11994
11879
|
try {
|
|
11995
11880
|
const db = getDb();
|
|
11996
11881
|
const models = await db.select().from(fineTunedModels);
|
|
11997
|
-
if (opts.json) {
|
|
11998
|
-
printJson(models);
|
|
11999
|
-
return;
|
|
12000
|
-
}
|
|
12001
11882
|
if (models.length === 0) {
|
|
12002
11883
|
printInfo("No models tracked yet. Use 'brains finetune start' to train one.");
|
|
12003
11884
|
return;
|
|
@@ -12015,22 +11896,14 @@ modelsCmd.command("list").description("List all tracked fine-tuned models").opti
|
|
|
12015
11896
|
process.exit(1);
|
|
12016
11897
|
}
|
|
12017
11898
|
});
|
|
12018
|
-
modelsCmd.command("show <id>").description("Show details of a specific model").
|
|
11899
|
+
modelsCmd.command("show <id>").description("Show details of a specific model").action(async (id) => {
|
|
12019
11900
|
try {
|
|
12020
11901
|
const db = getDb();
|
|
12021
11902
|
const [model] = await db.select().from(fineTunedModels).where(eq(fineTunedModels.id, id));
|
|
12022
11903
|
if (!model) {
|
|
12023
|
-
|
|
12024
|
-
printJson({ error: `Model not found: ${id}` });
|
|
12025
|
-
} else {
|
|
12026
|
-
printError(`Model not found: ${id}`);
|
|
12027
|
-
}
|
|
11904
|
+
printError(`Model not found: ${id}`);
|
|
12028
11905
|
process.exit(1);
|
|
12029
11906
|
}
|
|
12030
|
-
if (opts.json) {
|
|
12031
|
-
printJson(model);
|
|
12032
|
-
return;
|
|
12033
|
-
}
|
|
12034
11907
|
console.log();
|
|
12035
11908
|
const tagsList = model.tags ? JSON.parse(model.tags).join(", ") : "(none)";
|
|
12036
11909
|
console.log(` ID: ${model.id}`);
|
|
@@ -12117,89 +11990,29 @@ modelsCmd.command("collection <id> <collectionName>").description("Set the colle
|
|
|
12117
11990
|
process.exit(1);
|
|
12118
11991
|
}
|
|
12119
11992
|
});
|
|
12120
|
-
modelsCmd.command("import <job-id>").description("Import an externally created fine-tuned model into local tracking").option("--provider <provider>", "Provider (openai|thinker-labs)", "openai").option("--name <name>", "Display name for the model").action(async (jobId, opts) => {
|
|
12121
|
-
try {
|
|
12122
|
-
let result;
|
|
12123
|
-
if (opts.provider === "openai") {
|
|
12124
|
-
result = await getFineTuneStatus(jobId);
|
|
12125
|
-
} else {
|
|
12126
|
-
const tl = new ThinkerLabsProvider;
|
|
12127
|
-
result = await tl.getFineTuneStatus(jobId);
|
|
12128
|
-
}
|
|
12129
|
-
const db = getDb();
|
|
12130
|
-
const [existing] = await db.select().from(fineTunedModels).where(eq(fineTunedModels.fineTuneJobId, jobId));
|
|
12131
|
-
if (existing) {
|
|
12132
|
-
printInfo(`Model already tracked as: ${existing.id}`);
|
|
12133
|
-
return;
|
|
12134
|
-
}
|
|
12135
|
-
const modelId = randomUUID();
|
|
12136
|
-
const now = Date.now();
|
|
12137
|
-
const name = opts.name ?? result.fineTunedModel ?? `imported-${jobId}`;
|
|
12138
|
-
await db.insert(fineTunedModels).values({
|
|
12139
|
-
id: modelId,
|
|
12140
|
-
name,
|
|
12141
|
-
provider: opts.provider,
|
|
12142
|
-
baseModel: result.baseModel ?? "unknown",
|
|
12143
|
-
status: result.status,
|
|
12144
|
-
fineTuneJobId: jobId,
|
|
12145
|
-
createdAt: now,
|
|
12146
|
-
updatedAt: now
|
|
12147
|
-
});
|
|
12148
|
-
await db.insert(trainingJobs).values({
|
|
12149
|
-
id: randomUUID(),
|
|
12150
|
-
modelId,
|
|
12151
|
-
provider: opts.provider,
|
|
12152
|
-
status: result.status,
|
|
12153
|
-
startedAt: now
|
|
12154
|
-
});
|
|
12155
|
-
printSuccess(`Model imported successfully.`);
|
|
12156
|
-
console.log();
|
|
12157
|
-
console.log(` Local ID: ${modelId}`);
|
|
12158
|
-
console.log(` Job ID: ${jobId}`);
|
|
12159
|
-
console.log(` Name: ${name}`);
|
|
12160
|
-
console.log(` Status: ${printStatus(result.status)}`);
|
|
12161
|
-
if (result.fineTunedModel)
|
|
12162
|
-
console.log(` Model: ${result.fineTunedModel}`);
|
|
12163
|
-
console.log();
|
|
12164
|
-
} catch (err) {
|
|
12165
|
-
printError(err instanceof Error ? err.message : String(err));
|
|
12166
|
-
process.exit(1);
|
|
12167
|
-
}
|
|
12168
|
-
});
|
|
12169
11993
|
var finetuneCmd = program2.command("finetune").description("Manage fine-tuning jobs");
|
|
12170
|
-
finetuneCmd.command("start").description("Start a fine-tuning job").requiredOption("--provider <provider>", "Provider to use (openai|thinker-labs)").requiredOption("--base-model <model>", "Base model to fine-tune (e.g. gpt-4o-mini-2024-07-18)").
|
|
11994
|
+
finetuneCmd.command("start").description("Start a fine-tuning job").requiredOption("--provider <provider>", "Provider to use (openai|thinker-labs)").requiredOption("--base-model <model>", "Base model to fine-tune (e.g. gpt-4o-mini-2024-07-18)").requiredOption("--dataset <path>", "Path to the JSONL training dataset").requiredOption("--name <name>", "Human-readable name for this fine-tuned model").action(async (opts) => {
|
|
12171
11995
|
try {
|
|
12172
11996
|
if (opts.provider !== "openai" && opts.provider !== "thinker-labs") {
|
|
12173
11997
|
printError(`Unknown provider: ${opts.provider}. Use 'openai' or 'thinker-labs'.`);
|
|
12174
11998
|
process.exit(1);
|
|
12175
11999
|
}
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
const db2 = getDb();
|
|
12179
|
-
const [latest] = await db2.select().from(trainingDatasets).orderBy(desc(trainingDatasets.createdAt)).limit(1);
|
|
12180
|
-
if (!latest?.filePath) {
|
|
12181
|
-
printError("No datasets found. Run 'brains data gather' first.");
|
|
12182
|
-
process.exit(1);
|
|
12183
|
-
}
|
|
12184
|
-
datasetPath = latest.filePath;
|
|
12185
|
-
printInfo(`Using latest dataset: ${datasetPath} (${latest.exampleCount} examples)`);
|
|
12186
|
-
}
|
|
12187
|
-
if (!existsSync3(datasetPath)) {
|
|
12188
|
-
printError(`Dataset file not found: ${datasetPath}`);
|
|
12000
|
+
if (!existsSync2(opts.dataset)) {
|
|
12001
|
+
printError(`Dataset file not found: ${opts.dataset}`);
|
|
12189
12002
|
process.exit(1);
|
|
12190
12003
|
}
|
|
12191
|
-
printInfo(`Uploading training file: ${
|
|
12004
|
+
printInfo(`Uploading training file: ${opts.dataset} \u2026`);
|
|
12192
12005
|
let fileId;
|
|
12193
12006
|
let jobId;
|
|
12194
12007
|
let jobStatus;
|
|
12195
12008
|
if (opts.provider === "openai") {
|
|
12196
|
-
({ fileId } = await uploadTrainingFile(
|
|
12009
|
+
({ fileId } = await uploadTrainingFile(opts.dataset));
|
|
12197
12010
|
printSuccess(`File uploaded. fileId = ${fileId}`);
|
|
12198
12011
|
printInfo(`Creating fine-tune job on OpenAI \u2026`);
|
|
12199
12012
|
({ jobId, status: jobStatus } = await createFineTuneJob(fileId, opts.baseModel, opts.name));
|
|
12200
12013
|
} else {
|
|
12201
12014
|
const tl = new ThinkerLabsProvider;
|
|
12202
|
-
({ fileId } = await tl.uploadTrainingFile(
|
|
12015
|
+
({ fileId } = await tl.uploadTrainingFile(opts.dataset));
|
|
12203
12016
|
printSuccess(`File uploaded. fileId = ${fileId}`);
|
|
12204
12017
|
printInfo(`Creating fine-tune job on Thinker Labs \u2026`);
|
|
12205
12018
|
({ jobId, status: jobStatus } = await tl.createFineTuneJob(fileId, opts.baseModel, opts.name));
|
|
@@ -12237,7 +12050,7 @@ finetuneCmd.command("start").description("Start a fine-tuning job").requiredOpti
|
|
|
12237
12050
|
process.exit(1);
|
|
12238
12051
|
}
|
|
12239
12052
|
});
|
|
12240
|
-
finetuneCmd.command("status <job-id>").description("Get the status of a fine-tuning job").option("--provider <provider>", "Provider (openai|thinker-labs)", "openai").
|
|
12053
|
+
finetuneCmd.command("status <job-id>").description("Get the status of a fine-tuning job").option("--provider <provider>", "Provider (openai|thinker-labs)", "openai").action(async (jobId, opts) => {
|
|
12241
12054
|
try {
|
|
12242
12055
|
let result;
|
|
12243
12056
|
if (opts.provider === "openai") {
|
|
@@ -12246,20 +12059,16 @@ finetuneCmd.command("status <job-id>").description("Get the status of a fine-tun
|
|
|
12246
12059
|
const tl = new ThinkerLabsProvider;
|
|
12247
12060
|
result = await tl.getFineTuneStatus(jobId);
|
|
12248
12061
|
}
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
}
|
|
12252
|
-
|
|
12253
|
-
console.log(`
|
|
12254
|
-
console.log(` Status: ${printStatus(result.status)}`);
|
|
12255
|
-
if (result.fineTunedModel) {
|
|
12256
|
-
console.log(` Fine-tuned model: ${result.fineTunedModel}`);
|
|
12257
|
-
}
|
|
12258
|
-
if (result.error) {
|
|
12259
|
-
console.log(` Error: ${result.error}`);
|
|
12260
|
-
}
|
|
12261
|
-
console.log();
|
|
12062
|
+
console.log();
|
|
12063
|
+
console.log(` Job ID: ${result.jobId}`);
|
|
12064
|
+
console.log(` Status: ${printStatus(result.status)}`);
|
|
12065
|
+
if (result.fineTunedModel) {
|
|
12066
|
+
console.log(` Fine-tuned model: ${result.fineTunedModel}`);
|
|
12262
12067
|
}
|
|
12068
|
+
if (result.error) {
|
|
12069
|
+
console.log(` Error: ${result.error}`);
|
|
12070
|
+
}
|
|
12071
|
+
console.log();
|
|
12263
12072
|
const db = getDb();
|
|
12264
12073
|
const [model] = await db.select().from(fineTunedModels).where(eq(fineTunedModels.fineTuneJobId, jobId));
|
|
12265
12074
|
if (model) {
|
|
@@ -12271,64 +12080,7 @@ finetuneCmd.command("status <job-id>").description("Get the status of a fine-tun
|
|
|
12271
12080
|
process.exit(1);
|
|
12272
12081
|
}
|
|
12273
12082
|
});
|
|
12274
|
-
finetuneCmd.command("
|
|
12275
|
-
const intervalMs = Math.max(5, parseInt(opts.interval, 10) || 30) * 1000;
|
|
12276
|
-
const terminalStates = new Set(["succeeded", "failed", "cancelled"]);
|
|
12277
|
-
printInfo(`Watching job ${jobId} (polling every ${intervalMs / 1000}s) \u2026`);
|
|
12278
|
-
console.log();
|
|
12279
|
-
const poll = async () => {
|
|
12280
|
-
try {
|
|
12281
|
-
let result;
|
|
12282
|
-
if (opts.provider === "openai") {
|
|
12283
|
-
result = await getFineTuneStatus(jobId);
|
|
12284
|
-
} else {
|
|
12285
|
-
const tl = new ThinkerLabsProvider;
|
|
12286
|
-
result = await tl.getFineTuneStatus(jobId);
|
|
12287
|
-
}
|
|
12288
|
-
const ts = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
12289
|
-
process.stdout.write(` [${ts}] ${printStatus(result.status)}`);
|
|
12290
|
-
if (result.fineTunedModel)
|
|
12291
|
-
process.stdout.write(` model: ${result.fineTunedModel}`);
|
|
12292
|
-
process.stdout.write(`
|
|
12293
|
-
`);
|
|
12294
|
-
const db = getDb();
|
|
12295
|
-
const [model] = await db.select().from(fineTunedModels).where(eq(fineTunedModels.fineTuneJobId, jobId));
|
|
12296
|
-
if (model) {
|
|
12297
|
-
await db.update(fineTunedModels).set({ status: result.status, updatedAt: Date.now() }).where(eq(fineTunedModels.fineTuneJobId, jobId));
|
|
12298
|
-
}
|
|
12299
|
-
if (terminalStates.has(result.status)) {
|
|
12300
|
-
console.log();
|
|
12301
|
-
if (result.status === "succeeded") {
|
|
12302
|
-
printSuccess(`Job completed successfully.`);
|
|
12303
|
-
if (result.fineTunedModel)
|
|
12304
|
-
printSuccess(`Fine-tuned model: ${result.fineTunedModel}`);
|
|
12305
|
-
} else if (result.status === "failed") {
|
|
12306
|
-
printError(`Job failed.${result.error ? " Error: " + result.error : ""}`);
|
|
12307
|
-
} else {
|
|
12308
|
-
printInfo(`Job ${result.status}.`);
|
|
12309
|
-
}
|
|
12310
|
-
return true;
|
|
12311
|
-
}
|
|
12312
|
-
return false;
|
|
12313
|
-
} catch (err) {
|
|
12314
|
-
printError(err instanceof Error ? err.message : String(err));
|
|
12315
|
-
return false;
|
|
12316
|
-
}
|
|
12317
|
-
};
|
|
12318
|
-
const done = await poll();
|
|
12319
|
-
if (!done) {
|
|
12320
|
-
await new Promise((resolve2) => {
|
|
12321
|
-
const timer = setInterval(async () => {
|
|
12322
|
-
const finished = await poll();
|
|
12323
|
-
if (finished) {
|
|
12324
|
-
clearInterval(timer);
|
|
12325
|
-
resolve2();
|
|
12326
|
-
}
|
|
12327
|
-
}, intervalMs);
|
|
12328
|
-
});
|
|
12329
|
-
}
|
|
12330
|
-
});
|
|
12331
|
-
finetuneCmd.command("list").description("List all fine-tuning jobs").option("--provider <provider>", "Provider to query (openai|thinker-labs)", "openai").option("--json", "Output as JSON").action(async (opts) => {
|
|
12083
|
+
finetuneCmd.command("list").description("List all fine-tuning jobs").option("--provider <provider>", "Provider to query (openai|thinker-labs)", "openai").action(async (opts) => {
|
|
12332
12084
|
try {
|
|
12333
12085
|
let jobs;
|
|
12334
12086
|
if (opts.provider === "openai") {
|
|
@@ -12337,10 +12089,6 @@ finetuneCmd.command("list").description("List all fine-tuning jobs").option("--p
|
|
|
12337
12089
|
const tl = new ThinkerLabsProvider;
|
|
12338
12090
|
jobs = await tl.listFineTunedModels();
|
|
12339
12091
|
}
|
|
12340
|
-
if (opts.json) {
|
|
12341
|
-
printJson(jobs);
|
|
12342
|
-
return;
|
|
12343
|
-
}
|
|
12344
12092
|
if (jobs.length === 0) {
|
|
12345
12093
|
printInfo("No fine-tuning jobs found.");
|
|
12346
12094
|
return;
|
|
@@ -12356,7 +12104,7 @@ finetuneCmd.command("list").description("List all fine-tuning jobs").option("--p
|
|
|
12356
12104
|
process.exit(1);
|
|
12357
12105
|
}
|
|
12358
12106
|
});
|
|
12359
|
-
var DEFAULT_DATASETS_DIR =
|
|
12107
|
+
var DEFAULT_DATASETS_DIR = join6(homedir6(), ".brains", "datasets");
|
|
12360
12108
|
var dataCmd = program2.command("data").description("Manage training datasets");
|
|
12361
12109
|
dataCmd.command("gather").description("Gather training data from agent memory sources").option("--source <source>", "Data source: todos|mementos|conversations|sessions|all", "all").option("--output <dir>", "Output directory", DEFAULT_DATASETS_DIR).option("--limit <n>", "Maximum number of examples to gather", "500").action(async (opts) => {
|
|
12362
12110
|
const validSources = ["todos", "mementos", "conversations", "sessions", "all"];
|
|
@@ -12370,59 +12118,44 @@ dataCmd.command("gather").description("Gather training data from agent memory so
|
|
|
12370
12118
|
process.exit(1);
|
|
12371
12119
|
}
|
|
12372
12120
|
try {
|
|
12373
|
-
|
|
12121
|
+
mkdirSync2(opts.output, { recursive: true });
|
|
12374
12122
|
const sources = opts.source === "all" ? ["todos", "mementos", "conversations", "sessions"] : [opts.source];
|
|
12375
12123
|
const now = Date.now();
|
|
12376
12124
|
const db = getDb();
|
|
12377
|
-
const
|
|
12378
|
-
todos: (o) => Promise.resolve().then(() => (init_todos(), exports_todos)).then((m) => m.gatherFromTodos(o)),
|
|
12379
|
-
mementos: (o) => Promise.resolve().then(() => (init_mementos(), exports_mementos)).then((m) => m.gatherFromMementos(o)),
|
|
12380
|
-
conversations: (o) => Promise.resolve().then(() => (init_conversations(), exports_conversations)).then((m) => m.gatherFromConversations(o)),
|
|
12381
|
-
sessions: (o) => Promise.resolve().then(() => (init_sessions(), exports_sessions2)).then((m) => m.gatherFromSessions(o))
|
|
12382
|
-
};
|
|
12125
|
+
const results = await gatherAll(sources, { limit: limit2 });
|
|
12383
12126
|
let totalExamples = 0;
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
printInfo(`
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
printInfo(` No examples found in ${source}.`);
|
|
12396
|
-
continue;
|
|
12397
|
-
}
|
|
12398
|
-
const fileName = `${source}-${now}.jsonl`;
|
|
12399
|
-
const filePath = join7(opts.output, fileName);
|
|
12400
|
-
writeFileSync2(filePath, examples.map((e) => JSON.stringify(e)).join(`
|
|
12127
|
+
for (const result of results) {
|
|
12128
|
+
const { source, examples, count } = result;
|
|
12129
|
+
printInfo(`Gathered from ${source} \u2026`);
|
|
12130
|
+
if (count === 0) {
|
|
12131
|
+
printInfo(` No examples gathered from ${source}.`);
|
|
12132
|
+
continue;
|
|
12133
|
+
}
|
|
12134
|
+
totalExamples += count;
|
|
12135
|
+
const fileName = `${source}-${now}.jsonl`;
|
|
12136
|
+
const filePath = join6(opts.output, fileName);
|
|
12137
|
+
writeFileSync(filePath, examples.map((e) => JSON.stringify(e)).join(`
|
|
12401
12138
|
`) + `
|
|
12402
12139
|
`, "utf8");
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
successfulSources++;
|
|
12413
|
-
} catch (sourceErr) {
|
|
12414
|
-
printError(` \u2717 ${source}: ${sourceErr instanceof Error ? sourceErr.message : String(sourceErr)}`);
|
|
12415
|
-
}
|
|
12140
|
+
const datasetId = randomUUID();
|
|
12141
|
+
await db.insert(trainingDatasets).values({
|
|
12142
|
+
id: datasetId,
|
|
12143
|
+
source,
|
|
12144
|
+
filePath,
|
|
12145
|
+
exampleCount: count,
|
|
12146
|
+
createdAt: now
|
|
12147
|
+
});
|
|
12148
|
+
printSuccess(` ${count} examples \u2192 ${filePath}`);
|
|
12416
12149
|
}
|
|
12417
12150
|
console.log();
|
|
12418
|
-
printSuccess(`Total
|
|
12151
|
+
printSuccess(`Total examples gathered: ${totalExamples}`);
|
|
12419
12152
|
} catch (err) {
|
|
12420
12153
|
printError(err instanceof Error ? err.message : String(err));
|
|
12421
12154
|
process.exit(1);
|
|
12422
12155
|
}
|
|
12423
12156
|
});
|
|
12424
12157
|
dataCmd.command("preview <file>").description("Preview a JSONL training file").option("-n, --count <n>", "Number of examples to show", "5").action((file, opts) => {
|
|
12425
|
-
if (!
|
|
12158
|
+
if (!existsSync2(file)) {
|
|
12426
12159
|
printError(`File not found: ${file}`);
|
|
12427
12160
|
process.exit(1);
|
|
12428
12161
|
}
|
|
@@ -12432,7 +12165,7 @@ dataCmd.command("preview <file>").description("Preview a JSONL training file").o
|
|
|
12432
12165
|
process.exit(1);
|
|
12433
12166
|
}
|
|
12434
12167
|
try {
|
|
12435
|
-
const content =
|
|
12168
|
+
const content = readFileSync2(file, "utf8");
|
|
12436
12169
|
const lines = content.trim().split(`
|
|
12437
12170
|
`).filter(Boolean);
|
|
12438
12171
|
const total = lines.length;
|
|
@@ -12457,63 +12190,10 @@ dataCmd.command("preview <file>").description("Preview a JSONL training file").o
|
|
|
12457
12190
|
process.exit(1);
|
|
12458
12191
|
}
|
|
12459
12192
|
});
|
|
12460
|
-
dataCmd.command("
|
|
12461
|
-
try {
|
|
12462
|
-
for (const f of files) {
|
|
12463
|
-
if (!existsSync3(f)) {
|
|
12464
|
-
printError(`File not found: ${f}`);
|
|
12465
|
-
process.exit(1);
|
|
12466
|
-
}
|
|
12467
|
-
}
|
|
12468
|
-
mkdirSync3(join7(opts.output, "..").replace(/\/\.\.$/, "") || DEFAULT_DATASETS_DIR, { recursive: true });
|
|
12469
|
-
const allExamples = [];
|
|
12470
|
-
for (const f of files) {
|
|
12471
|
-
const lines = readFileSync3(f, "utf8").split(`
|
|
12472
|
-
`).map((l) => l.trim()).filter(Boolean);
|
|
12473
|
-
allExamples.push(...lines);
|
|
12474
|
-
printInfo(` Read ${lines.length} examples from ${f}`);
|
|
12475
|
-
}
|
|
12476
|
-
let finalLines = allExamples;
|
|
12477
|
-
let dupeCount = 0;
|
|
12478
|
-
if (opts.dedupe) {
|
|
12479
|
-
const seen = new Set;
|
|
12480
|
-
finalLines = allExamples.filter((line) => {
|
|
12481
|
-
if (seen.has(line)) {
|
|
12482
|
-
dupeCount++;
|
|
12483
|
-
return false;
|
|
12484
|
-
}
|
|
12485
|
-
seen.add(line);
|
|
12486
|
-
return true;
|
|
12487
|
-
});
|
|
12488
|
-
}
|
|
12489
|
-
writeFileSync2(opts.output, finalLines.join(`
|
|
12490
|
-
`) + `
|
|
12491
|
-
`, "utf8");
|
|
12492
|
-
const db = getDb();
|
|
12493
|
-
await db.insert(trainingDatasets).values({
|
|
12494
|
-
id: randomUUID(),
|
|
12495
|
-
source: "mixed",
|
|
12496
|
-
filePath: opts.output,
|
|
12497
|
-
exampleCount: finalLines.length,
|
|
12498
|
-
createdAt: Date.now()
|
|
12499
|
-
});
|
|
12500
|
-
console.log();
|
|
12501
|
-
printSuccess(`Merged ${files.length} files \u2014 ${finalLines.length} examples \u2192 ${opts.output}`);
|
|
12502
|
-
if (opts.dedupe && dupeCount > 0)
|
|
12503
|
-
printInfo(` Removed ${dupeCount} duplicate(s)`);
|
|
12504
|
-
} catch (err) {
|
|
12505
|
-
printError(err instanceof Error ? err.message : String(err));
|
|
12506
|
-
process.exit(1);
|
|
12507
|
-
}
|
|
12508
|
-
});
|
|
12509
|
-
dataCmd.command("list").description("List all gathered datasets").option("--json", "Output as JSON").action(async (opts) => {
|
|
12193
|
+
dataCmd.command("list").description("List all gathered datasets").action(async () => {
|
|
12510
12194
|
try {
|
|
12511
12195
|
const db = getDb();
|
|
12512
12196
|
const datasets = await db.select().from(trainingDatasets);
|
|
12513
|
-
if (opts.json) {
|
|
12514
|
-
printJson(datasets);
|
|
12515
|
-
return;
|
|
12516
|
-
}
|
|
12517
12197
|
if (datasets.length === 0) {
|
|
12518
12198
|
printInfo("No datasets found. Use 'brains data gather' to create one.");
|
|
12519
12199
|
return;
|
|
@@ -12531,10 +12211,10 @@ dataCmd.command("list").description("List all gathered datasets").option("--json
|
|
|
12531
12211
|
}
|
|
12532
12212
|
});
|
|
12533
12213
|
var collectionsCmd = program2.command("collections").description("Manage model collections");
|
|
12534
|
-
collectionsCmd.
|
|
12535
|
-
await listCollections(
|
|
12214
|
+
collectionsCmd.action(async () => {
|
|
12215
|
+
await listCollections();
|
|
12536
12216
|
});
|
|
12537
|
-
async function listCollections(
|
|
12217
|
+
async function listCollections() {
|
|
12538
12218
|
try {
|
|
12539
12219
|
const db = getDb();
|
|
12540
12220
|
const rows = await db.select({
|
|
@@ -12542,10 +12222,6 @@ async function listCollections(json = false) {
|
|
|
12542
12222
|
count: sql`count(*)`.as("count"),
|
|
12543
12223
|
names: sql`group_concat(coalesce(${fineTunedModels.displayName}, ${fineTunedModels.name}), ', ')`.as("names")
|
|
12544
12224
|
}).from(fineTunedModels).groupBy(fineTunedModels.collection);
|
|
12545
|
-
if (json) {
|
|
12546
|
-
printJson(rows);
|
|
12547
|
-
return;
|
|
12548
|
-
}
|
|
12549
12225
|
if (rows.length === 0) {
|
|
12550
12226
|
printInfo("No collections found. Set a collection with 'brains models set-collection'.");
|
|
12551
12227
|
return;
|
|
@@ -12560,8 +12236,8 @@ async function listCollections(json = false) {
|
|
|
12560
12236
|
process.exit(1);
|
|
12561
12237
|
}
|
|
12562
12238
|
}
|
|
12563
|
-
collectionsCmd.command("list").description("List all collections with model counts").
|
|
12564
|
-
await listCollections(
|
|
12239
|
+
collectionsCmd.command("list").description("List all collections with model counts").action(async () => {
|
|
12240
|
+
await listCollections();
|
|
12565
12241
|
});
|
|
12566
12242
|
collectionsCmd.command("show <name>").description("List all models in a collection").action(async (name) => {
|
|
12567
12243
|
try {
|
|
@@ -12620,43 +12296,4 @@ program2.command("remove <id>").alias("rm").alias("uninstall").description("Remo
|
|
|
12620
12296
|
process.exit(1);
|
|
12621
12297
|
}
|
|
12622
12298
|
});
|
|
12623
|
-
var configCmd = program2.command("config").description("Manage API keys and settings");
|
|
12624
|
-
configCmd.command("list").description("Show all config keys and their sources").action(() => {
|
|
12625
|
-
const entries = listConfig();
|
|
12626
|
-
console.log();
|
|
12627
|
-
for (const { key, value, source } of entries) {
|
|
12628
|
-
const display = source === "unset" ? "(unset)" : value.length > 8 ? value.slice(0, 4) + "****" + value.slice(-4) : "****";
|
|
12629
|
-
const src = source === "env" ? " [env]" : source === "file" ? " [file]" : "";
|
|
12630
|
-
console.log(` ${key.padEnd(28)} ${display}${src}`);
|
|
12631
|
-
}
|
|
12632
|
-
console.log();
|
|
12633
|
-
});
|
|
12634
|
-
configCmd.command("get <key>").description("Get a config value").action((key) => {
|
|
12635
|
-
if (!CONFIG_KEYS.includes(key)) {
|
|
12636
|
-
printError(`Unknown key: ${key}. Valid keys: ${CONFIG_KEYS.join(", ")}`);
|
|
12637
|
-
process.exit(1);
|
|
12638
|
-
}
|
|
12639
|
-
const value = getConfigValue(key);
|
|
12640
|
-
if (!value) {
|
|
12641
|
-
printInfo(`${key} is not set.`);
|
|
12642
|
-
} else {
|
|
12643
|
-
console.log(value);
|
|
12644
|
-
}
|
|
12645
|
-
});
|
|
12646
|
-
configCmd.command("set <key> <value>").description("Set a config value (stored in ~/.brains/config.json)").action((key, value) => {
|
|
12647
|
-
if (!CONFIG_KEYS.includes(key)) {
|
|
12648
|
-
printError(`Unknown key: ${key}. Valid keys: ${CONFIG_KEYS.join(", ")}`);
|
|
12649
|
-
process.exit(1);
|
|
12650
|
-
}
|
|
12651
|
-
setConfigValue(key, value);
|
|
12652
|
-
printSuccess(`${key} saved to ~/.brains/config.json`);
|
|
12653
|
-
});
|
|
12654
|
-
configCmd.command("unset <key>").description("Remove a config value from the config file").action((key) => {
|
|
12655
|
-
if (!CONFIG_KEYS.includes(key)) {
|
|
12656
|
-
printError(`Unknown key: ${key}. Valid keys: ${CONFIG_KEYS.join(", ")}`);
|
|
12657
|
-
process.exit(1);
|
|
12658
|
-
}
|
|
12659
|
-
deleteConfigValue(key);
|
|
12660
|
-
printSuccess(`${key} removed from config.`);
|
|
12661
|
-
});
|
|
12662
12299
|
program2.parse();
|