@hasna/brains 0.0.5 → 0.0.6
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/dist/cli/index.js +51 -2
- package/dist/index.js +32 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/package-metadata.d.ts +2 -0
- package/dist/lib/package-metadata.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +74 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +334 -285
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -5,25 +5,43 @@ 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
47
|
var __require = import.meta.require;
|
|
@@ -12247,4 +12265,35 @@ collectionsCmd.command("rename <oldName> <newName>").description("Rename a colle
|
|
|
12247
12265
|
process.exit(1);
|
|
12248
12266
|
}
|
|
12249
12267
|
});
|
|
12268
|
+
program2.command("remove <id>").alias("rm").alias("uninstall").description("Remove a fine-tuned model or training job by ID").option("--type <type>", "Type: model | job (default: auto-detect)").action(async (id, opts) => {
|
|
12269
|
+
const db = getDb();
|
|
12270
|
+
try {
|
|
12271
|
+
const type = opts.type?.toLowerCase();
|
|
12272
|
+
if (type === "job" || !type && !type) {
|
|
12273
|
+
const job = db.select().from(trainingJobs).where(eq(trainingJobs.id, id)).get();
|
|
12274
|
+
if (job || type === "job") {
|
|
12275
|
+
if (!job) {
|
|
12276
|
+
printError(`Job not found: ${id}`);
|
|
12277
|
+
process.exit(1);
|
|
12278
|
+
}
|
|
12279
|
+
db.delete(trainingJobs).where(eq(trainingJobs.id, id)).run();
|
|
12280
|
+
printSuccess(`Training job ${id} removed`);
|
|
12281
|
+
return;
|
|
12282
|
+
}
|
|
12283
|
+
}
|
|
12284
|
+
if (type === "model" || !type) {
|
|
12285
|
+
const model = db.select().from(fineTunedModels).where(eq(fineTunedModels.id, id)).get();
|
|
12286
|
+
if (model) {
|
|
12287
|
+
db.delete(fineTunedModels).where(eq(fineTunedModels.id, id)).run();
|
|
12288
|
+
printSuccess(`Model ${id} removed`);
|
|
12289
|
+
return;
|
|
12290
|
+
}
|
|
12291
|
+
}
|
|
12292
|
+
printError(`Not found: ${id}. Use --type model|job`);
|
|
12293
|
+
process.exit(1);
|
|
12294
|
+
} catch (err) {
|
|
12295
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
12296
|
+
process.exit(1);
|
|
12297
|
+
}
|
|
12298
|
+
});
|
|
12250
12299
|
program2.parse();
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
3
7
|
var __export = (target, all) => {
|
|
4
8
|
for (var name in all)
|
|
5
9
|
__defProp(target, name, {
|
|
6
10
|
get: all[name],
|
|
7
11
|
enumerable: true,
|
|
8
12
|
configurable: true,
|
|
9
|
-
set: (
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
10
14
|
});
|
|
11
15
|
};
|
|
12
16
|
// node_modules/drizzle-orm/bun-sqlite/driver.js
|
|
@@ -9403,6 +9407,32 @@ class ThinkerLabsProvider {
|
|
|
9403
9407
|
return cancelJob(jobId);
|
|
9404
9408
|
}
|
|
9405
9409
|
}
|
|
9410
|
+
// src/lib/package-metadata.ts
|
|
9411
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
9412
|
+
import { dirname as dirname2, resolve } from "path";
|
|
9413
|
+
import { fileURLToPath } from "url";
|
|
9414
|
+
var DEFAULT_VERSION = "0.0.0";
|
|
9415
|
+
var cachedVersion;
|
|
9416
|
+
function getPackageJsonPath() {
|
|
9417
|
+
return resolve(dirname2(fileURLToPath(import.meta.url)), "../../package.json");
|
|
9418
|
+
}
|
|
9419
|
+
function getPackageVersion() {
|
|
9420
|
+
if (cachedVersion) {
|
|
9421
|
+
return cachedVersion;
|
|
9422
|
+
}
|
|
9423
|
+
const packageJsonPath = getPackageJsonPath();
|
|
9424
|
+
if (!existsSync(packageJsonPath)) {
|
|
9425
|
+
cachedVersion = DEFAULT_VERSION;
|
|
9426
|
+
return cachedVersion;
|
|
9427
|
+
}
|
|
9428
|
+
try {
|
|
9429
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
9430
|
+
cachedVersion = typeof packageJson.version === "string" ? packageJson.version : DEFAULT_VERSION;
|
|
9431
|
+
} catch {
|
|
9432
|
+
cachedVersion = DEFAULT_VERSION;
|
|
9433
|
+
}
|
|
9434
|
+
return cachedVersion;
|
|
9435
|
+
}
|
|
9406
9436
|
export {
|
|
9407
9437
|
uploadTrainingFile,
|
|
9408
9438
|
uploadTrainingData,
|
|
@@ -9412,6 +9442,7 @@ export {
|
|
|
9412
9442
|
listModels,
|
|
9413
9443
|
listFineTunedModels,
|
|
9414
9444
|
getStatus,
|
|
9445
|
+
getPackageVersion,
|
|
9415
9446
|
getFineTuneStatus,
|
|
9416
9447
|
getDb,
|
|
9417
9448
|
fineTunedModels,
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-metadata.d.ts","sourceRoot":"","sources":["../../src/lib/package-metadata.ts"],"names":[],"mappings":"AAYA,wBAAgB,iBAAiB,IAAI,MAAM,CAmB1C"}
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,3 +1,76 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
export declare const MCP_SERVER_INFO: {
|
|
5
|
+
readonly name: "brains";
|
|
6
|
+
readonly version: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function createMcpServer(): Server<{
|
|
9
|
+
method: string;
|
|
10
|
+
params?: {
|
|
11
|
+
[x: string]: unknown;
|
|
12
|
+
_meta?: {
|
|
13
|
+
[x: string]: unknown;
|
|
14
|
+
progressToken?: string | number | undefined;
|
|
15
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
16
|
+
taskId: string;
|
|
17
|
+
} | undefined;
|
|
18
|
+
} | undefined;
|
|
19
|
+
} | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
method: string;
|
|
22
|
+
params?: {
|
|
23
|
+
[x: string]: unknown;
|
|
24
|
+
_meta?: {
|
|
25
|
+
[x: string]: unknown;
|
|
26
|
+
progressToken?: string | number | undefined;
|
|
27
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
28
|
+
taskId: string;
|
|
29
|
+
} | undefined;
|
|
30
|
+
} | undefined;
|
|
31
|
+
} | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
[x: string]: unknown;
|
|
34
|
+
_meta?: {
|
|
35
|
+
[x: string]: unknown;
|
|
36
|
+
progressToken?: string | number | undefined;
|
|
37
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
38
|
+
taskId: string;
|
|
39
|
+
} | undefined;
|
|
40
|
+
} | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
export declare function startMcpServer(transport?: StdioServerTransport): Promise<Server<{
|
|
43
|
+
method: string;
|
|
44
|
+
params?: {
|
|
45
|
+
[x: string]: unknown;
|
|
46
|
+
_meta?: {
|
|
47
|
+
[x: string]: unknown;
|
|
48
|
+
progressToken?: string | number | undefined;
|
|
49
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
50
|
+
taskId: string;
|
|
51
|
+
} | undefined;
|
|
52
|
+
} | undefined;
|
|
53
|
+
} | undefined;
|
|
54
|
+
}, {
|
|
55
|
+
method: string;
|
|
56
|
+
params?: {
|
|
57
|
+
[x: string]: unknown;
|
|
58
|
+
_meta?: {
|
|
59
|
+
[x: string]: unknown;
|
|
60
|
+
progressToken?: string | number | undefined;
|
|
61
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
62
|
+
taskId: string;
|
|
63
|
+
} | undefined;
|
|
64
|
+
} | undefined;
|
|
65
|
+
} | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
[x: string]: unknown;
|
|
68
|
+
_meta?: {
|
|
69
|
+
[x: string]: unknown;
|
|
70
|
+
progressToken?: string | number | undefined;
|
|
71
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
72
|
+
taskId: string;
|
|
73
|
+
} | undefined;
|
|
74
|
+
} | undefined;
|
|
75
|
+
}>>;
|
|
3
76
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AA8BjF,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAEX,wBAAgB,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4X9B;AAED,wBAAsB,cAAc,CAAC,SAAS,uBAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAI1E"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
|
+
var __returnValue = (v) => v;
|
|
5
|
+
function __exportSetter(name, newValue) {
|
|
6
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
7
|
+
}
|
|
4
8
|
var __export = (target, all) => {
|
|
5
9
|
for (var name in all)
|
|
6
10
|
__defProp(target, name, {
|
|
7
11
|
get: all[name],
|
|
8
12
|
enumerable: true,
|
|
9
13
|
configurable: true,
|
|
10
|
-
set: (
|
|
14
|
+
set: __exportSetter.bind(all, name)
|
|
11
15
|
});
|
|
12
16
|
};
|
|
13
17
|
var __require = import.meta.require;
|
|
@@ -19,8 +23,8 @@ import {
|
|
|
19
23
|
CallToolRequestSchema,
|
|
20
24
|
ListToolsRequestSchema
|
|
21
25
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
22
|
-
import { readFileSync as
|
|
23
|
-
import { resolve } from "path";
|
|
26
|
+
import { readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
|
|
27
|
+
import { resolve as resolve2 } from "path";
|
|
24
28
|
import { homedir as homedir3 } from "os";
|
|
25
29
|
|
|
26
30
|
// node_modules/drizzle-orm/entity.js
|
|
@@ -9511,6 +9515,33 @@ async function gatherFromTodos(options = {}) {
|
|
|
9511
9515
|
}
|
|
9512
9516
|
}
|
|
9513
9517
|
|
|
9518
|
+
// src/lib/package-metadata.ts
|
|
9519
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
9520
|
+
import { dirname as dirname2, resolve } from "path";
|
|
9521
|
+
import { fileURLToPath } from "url";
|
|
9522
|
+
var DEFAULT_VERSION = "0.0.0";
|
|
9523
|
+
var cachedVersion;
|
|
9524
|
+
function getPackageJsonPath() {
|
|
9525
|
+
return resolve(dirname2(fileURLToPath(import.meta.url)), "../../package.json");
|
|
9526
|
+
}
|
|
9527
|
+
function getPackageVersion() {
|
|
9528
|
+
if (cachedVersion) {
|
|
9529
|
+
return cachedVersion;
|
|
9530
|
+
}
|
|
9531
|
+
const packageJsonPath = getPackageJsonPath();
|
|
9532
|
+
if (!existsSync(packageJsonPath)) {
|
|
9533
|
+
cachedVersion = DEFAULT_VERSION;
|
|
9534
|
+
return cachedVersion;
|
|
9535
|
+
}
|
|
9536
|
+
try {
|
|
9537
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
9538
|
+
cachedVersion = typeof packageJson.version === "string" ? packageJson.version : DEFAULT_VERSION;
|
|
9539
|
+
} catch {
|
|
9540
|
+
cachedVersion = DEFAULT_VERSION;
|
|
9541
|
+
}
|
|
9542
|
+
return cachedVersion;
|
|
9543
|
+
}
|
|
9544
|
+
|
|
9514
9545
|
// src/mcp/index.ts
|
|
9515
9546
|
function getProvider(provider) {
|
|
9516
9547
|
if (provider === "openai")
|
|
@@ -9520,312 +9551,330 @@ function getProvider(provider) {
|
|
|
9520
9551
|
throw new Error(`Unknown provider: ${provider}`);
|
|
9521
9552
|
}
|
|
9522
9553
|
function defaultOutputDir() {
|
|
9523
|
-
return
|
|
9524
|
-
}
|
|
9525
|
-
var
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
name: "start_finetune",
|
|
9550
|
-
description: "Upload a training file and start a fine-tuning job",
|
|
9551
|
-
inputSchema: {
|
|
9552
|
-
type: "object",
|
|
9553
|
-
properties: {
|
|
9554
|
-
provider: {
|
|
9555
|
-
type: "string",
|
|
9556
|
-
enum: ["openai", "thinker-labs"],
|
|
9557
|
-
description: "Provider to use for fine-tuning"
|
|
9554
|
+
return resolve2(homedir3(), ".brains", "datasets");
|
|
9555
|
+
}
|
|
9556
|
+
var MCP_SERVER_INFO = {
|
|
9557
|
+
name: "brains",
|
|
9558
|
+
version: getPackageVersion()
|
|
9559
|
+
};
|
|
9560
|
+
function createMcpServer() {
|
|
9561
|
+
const server = new Server(MCP_SERVER_INFO, { capabilities: { tools: {} } });
|
|
9562
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
9563
|
+
tools: [
|
|
9564
|
+
{
|
|
9565
|
+
name: "list_models",
|
|
9566
|
+
description: "List all fine-tuned models tracked in the local DB",
|
|
9567
|
+
inputSchema: {
|
|
9568
|
+
type: "object",
|
|
9569
|
+
properties: {},
|
|
9570
|
+
required: []
|
|
9571
|
+
}
|
|
9572
|
+
},
|
|
9573
|
+
{
|
|
9574
|
+
name: "get_model",
|
|
9575
|
+
description: "Get details for a specific fine-tuned model",
|
|
9576
|
+
inputSchema: {
|
|
9577
|
+
type: "object",
|
|
9578
|
+
properties: {
|
|
9579
|
+
model_id: { type: "string", description: "Model ID" }
|
|
9558
9580
|
},
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9581
|
+
required: ["model_id"]
|
|
9582
|
+
}
|
|
9583
|
+
},
|
|
9584
|
+
{
|
|
9585
|
+
name: "start_finetune",
|
|
9586
|
+
description: "Upload a training file and start a fine-tuning job",
|
|
9587
|
+
inputSchema: {
|
|
9588
|
+
type: "object",
|
|
9589
|
+
properties: {
|
|
9590
|
+
provider: {
|
|
9591
|
+
type: "string",
|
|
9592
|
+
enum: ["openai", "thinker-labs"],
|
|
9593
|
+
description: "Provider to use for fine-tuning"
|
|
9594
|
+
},
|
|
9595
|
+
base_model: {
|
|
9596
|
+
type: "string",
|
|
9597
|
+
description: "Base model identifier (e.g. gpt-4o-mini-2024-07-18)"
|
|
9598
|
+
},
|
|
9599
|
+
dataset_path: {
|
|
9600
|
+
type: "string",
|
|
9601
|
+
description: "Absolute path to the JSONL training file"
|
|
9602
|
+
},
|
|
9603
|
+
name: {
|
|
9604
|
+
type: "string",
|
|
9605
|
+
description: "Optional friendly name for this model"
|
|
9606
|
+
}
|
|
9562
9607
|
},
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9608
|
+
required: ["provider", "base_model", "dataset_path"]
|
|
9609
|
+
}
|
|
9610
|
+
},
|
|
9611
|
+
{
|
|
9612
|
+
name: "get_finetune_status",
|
|
9613
|
+
description: "Check the status of a fine-tuning job",
|
|
9614
|
+
inputSchema: {
|
|
9615
|
+
type: "object",
|
|
9616
|
+
properties: {
|
|
9617
|
+
job_id: { type: "string", description: "Fine-tune job ID" },
|
|
9618
|
+
provider: {
|
|
9619
|
+
type: "string",
|
|
9620
|
+
enum: ["openai", "thinker-labs"],
|
|
9621
|
+
description: "Provider that owns the job"
|
|
9622
|
+
}
|
|
9566
9623
|
},
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
}
|
|
9590
|
-
},
|
|
9591
|
-
{
|
|
9592
|
-
name: "gather_training_data",
|
|
9593
|
-
description: "Gather training data from ecosystem sources (todos, mementos, conversations, sessions)",
|
|
9594
|
-
inputSchema: {
|
|
9595
|
-
type: "object",
|
|
9596
|
-
properties: {
|
|
9597
|
-
sources: {
|
|
9598
|
-
type: "array",
|
|
9599
|
-
items: { type: "string" },
|
|
9600
|
-
description: "Sources to gather from: todos, mementos, conversations, sessions"
|
|
9624
|
+
required: ["job_id", "provider"]
|
|
9625
|
+
}
|
|
9626
|
+
},
|
|
9627
|
+
{
|
|
9628
|
+
name: "gather_training_data",
|
|
9629
|
+
description: "Gather training data from ecosystem sources (todos, mementos, conversations, sessions)",
|
|
9630
|
+
inputSchema: {
|
|
9631
|
+
type: "object",
|
|
9632
|
+
properties: {
|
|
9633
|
+
sources: {
|
|
9634
|
+
type: "array",
|
|
9635
|
+
items: { type: "string" },
|
|
9636
|
+
description: "Sources to gather from: todos, mementos, conversations, sessions"
|
|
9637
|
+
},
|
|
9638
|
+
limit: {
|
|
9639
|
+
type: "number",
|
|
9640
|
+
description: "Max examples per source (default: unlimited)"
|
|
9641
|
+
},
|
|
9642
|
+
output_dir: {
|
|
9643
|
+
type: "string",
|
|
9644
|
+
description: "Directory to write JSONL files (default: ~/.brains/datasets/)"
|
|
9645
|
+
}
|
|
9601
9646
|
},
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9647
|
+
required: ["sources"]
|
|
9648
|
+
}
|
|
9649
|
+
},
|
|
9650
|
+
{
|
|
9651
|
+
name: "preview_training_data",
|
|
9652
|
+
description: "Preview examples from a JSONL training file",
|
|
9653
|
+
inputSchema: {
|
|
9654
|
+
type: "object",
|
|
9655
|
+
properties: {
|
|
9656
|
+
file_path: {
|
|
9657
|
+
type: "string",
|
|
9658
|
+
description: "Absolute path to the JSONL file to preview"
|
|
9659
|
+
},
|
|
9660
|
+
limit: {
|
|
9661
|
+
type: "number",
|
|
9662
|
+
description: "Max number of examples to return (default: 5)"
|
|
9663
|
+
}
|
|
9605
9664
|
},
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
description: "Directory to write JSONL files (default: ~/.brains/datasets/)"
|
|
9609
|
-
}
|
|
9610
|
-
},
|
|
9611
|
-
required: ["sources"]
|
|
9665
|
+
required: ["file_path"]
|
|
9666
|
+
}
|
|
9612
9667
|
}
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9668
|
+
]
|
|
9669
|
+
}));
|
|
9670
|
+
server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
9671
|
+
const { name, arguments: args } = request2.params;
|
|
9672
|
+
try {
|
|
9673
|
+
switch (name) {
|
|
9674
|
+
case "list_models": {
|
|
9675
|
+
const db = getDb();
|
|
9676
|
+
const models = await db.select({
|
|
9677
|
+
id: fineTunedModels.id,
|
|
9678
|
+
name: fineTunedModels.name,
|
|
9679
|
+
provider: fineTunedModels.provider,
|
|
9680
|
+
status: fineTunedModels.status,
|
|
9681
|
+
base_model: fineTunedModels.baseModel,
|
|
9682
|
+
created_at: fineTunedModels.createdAt
|
|
9683
|
+
}).from(fineTunedModels).orderBy(fineTunedModels.createdAt);
|
|
9684
|
+
return {
|
|
9685
|
+
content: [
|
|
9686
|
+
{
|
|
9687
|
+
type: "text",
|
|
9688
|
+
text: JSON.stringify(models, null, 2)
|
|
9689
|
+
}
|
|
9690
|
+
]
|
|
9691
|
+
};
|
|
9692
|
+
}
|
|
9693
|
+
case "get_model": {
|
|
9694
|
+
const { model_id } = args;
|
|
9695
|
+
const db = getDb();
|
|
9696
|
+
const results = await db.select().from(fineTunedModels).where(eq(fineTunedModels.id, model_id));
|
|
9697
|
+
if (results.length === 0) {
|
|
9698
|
+
return {
|
|
9699
|
+
content: [{ type: "text", text: `Model not found: ${model_id}` }],
|
|
9700
|
+
isError: true
|
|
9701
|
+
};
|
|
9627
9702
|
}
|
|
9628
|
-
},
|
|
9629
|
-
required: ["file_path"]
|
|
9630
|
-
}
|
|
9631
|
-
}
|
|
9632
|
-
]
|
|
9633
|
-
}));
|
|
9634
|
-
server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
9635
|
-
const { name, arguments: args } = request2.params;
|
|
9636
|
-
try {
|
|
9637
|
-
switch (name) {
|
|
9638
|
-
case "list_models": {
|
|
9639
|
-
const db = getDb();
|
|
9640
|
-
const models = await db.select({
|
|
9641
|
-
id: fineTunedModels.id,
|
|
9642
|
-
name: fineTunedModels.name,
|
|
9643
|
-
provider: fineTunedModels.provider,
|
|
9644
|
-
status: fineTunedModels.status,
|
|
9645
|
-
base_model: fineTunedModels.baseModel,
|
|
9646
|
-
created_at: fineTunedModels.createdAt
|
|
9647
|
-
}).from(fineTunedModels).orderBy(fineTunedModels.createdAt);
|
|
9648
|
-
return {
|
|
9649
|
-
content: [
|
|
9650
|
-
{
|
|
9651
|
-
type: "text",
|
|
9652
|
-
text: JSON.stringify(models, null, 2)
|
|
9653
|
-
}
|
|
9654
|
-
]
|
|
9655
|
-
};
|
|
9656
|
-
}
|
|
9657
|
-
case "get_model": {
|
|
9658
|
-
const { model_id } = args;
|
|
9659
|
-
const db = getDb();
|
|
9660
|
-
const results = await db.select().from(fineTunedModels).where(eq(fineTunedModels.id, model_id));
|
|
9661
|
-
if (results.length === 0) {
|
|
9662
9703
|
return {
|
|
9663
|
-
content: [
|
|
9664
|
-
|
|
9704
|
+
content: [
|
|
9705
|
+
{ type: "text", text: JSON.stringify(results[0], null, 2) }
|
|
9706
|
+
]
|
|
9665
9707
|
};
|
|
9666
9708
|
}
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9709
|
+
case "start_finetune": {
|
|
9710
|
+
const {
|
|
9711
|
+
provider,
|
|
9712
|
+
base_model,
|
|
9713
|
+
dataset_path,
|
|
9714
|
+
name: modelName
|
|
9715
|
+
} = args;
|
|
9716
|
+
const resolvedPath = resolve2(dataset_path);
|
|
9717
|
+
if (!existsSync2(resolvedPath)) {
|
|
9718
|
+
return {
|
|
9719
|
+
content: [
|
|
9720
|
+
{
|
|
9721
|
+
type: "text",
|
|
9722
|
+
text: `Dataset file not found: ${resolvedPath}`
|
|
9723
|
+
}
|
|
9724
|
+
],
|
|
9725
|
+
isError: true
|
|
9726
|
+
};
|
|
9727
|
+
}
|
|
9728
|
+
const p = getProvider(provider);
|
|
9729
|
+
const { fileId } = await p.uploadTrainingFile(resolvedPath);
|
|
9730
|
+
const { jobId, status } = await p.createFineTuneJob(fileId, base_model, modelName);
|
|
9731
|
+
const db = getDb();
|
|
9732
|
+
const now = Date.now();
|
|
9733
|
+
const id = `${provider}-${jobId}`;
|
|
9734
|
+
await db.insert(fineTunedModels).values({
|
|
9735
|
+
id,
|
|
9736
|
+
name: modelName ?? `${base_model}-finetune-${now}`,
|
|
9737
|
+
provider,
|
|
9738
|
+
baseModel: base_model,
|
|
9739
|
+
status: "pending",
|
|
9740
|
+
fineTuneJobId: jobId,
|
|
9741
|
+
createdAt: now,
|
|
9742
|
+
updatedAt: now
|
|
9743
|
+
});
|
|
9682
9744
|
return {
|
|
9683
9745
|
content: [
|
|
9684
9746
|
{
|
|
9685
9747
|
type: "text",
|
|
9686
|
-
text:
|
|
9748
|
+
text: JSON.stringify({ job_id: jobId, status, model_db_id: id }, null, 2)
|
|
9687
9749
|
}
|
|
9688
|
-
]
|
|
9689
|
-
isError: true
|
|
9750
|
+
]
|
|
9690
9751
|
};
|
|
9691
9752
|
}
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
};
|
|
9716
|
-
}
|
|
9717
|
-
case "get_finetune_status": {
|
|
9718
|
-
const { job_id, provider } = args;
|
|
9719
|
-
const p = getProvider(provider);
|
|
9720
|
-
const result = await p.getFineTuneStatus(job_id);
|
|
9721
|
-
const db = getDb();
|
|
9722
|
-
const dbId = `${provider}-${job_id}`;
|
|
9723
|
-
const existing = await db.select().from(fineTunedModels).where(eq(fineTunedModels.id, dbId));
|
|
9724
|
-
if (existing.length > 0) {
|
|
9725
|
-
const mappedStatus = (() => {
|
|
9726
|
-
if (result.status === "succeeded")
|
|
9727
|
-
return "succeeded";
|
|
9728
|
-
if (result.status === "failed")
|
|
9729
|
-
return "failed";
|
|
9730
|
-
if (result.status === "cancelled")
|
|
9731
|
-
return "cancelled";
|
|
9732
|
-
if (result.status === "running")
|
|
9733
|
-
return "running";
|
|
9734
|
-
return "pending";
|
|
9735
|
-
})();
|
|
9736
|
-
await db.update(fineTunedModels).set({
|
|
9737
|
-
status: mappedStatus,
|
|
9738
|
-
updatedAt: Date.now()
|
|
9739
|
-
}).where(eq(fineTunedModels.id, dbId));
|
|
9740
|
-
}
|
|
9741
|
-
return {
|
|
9742
|
-
content: [
|
|
9743
|
-
{
|
|
9744
|
-
type: "text",
|
|
9745
|
-
text: JSON.stringify({
|
|
9746
|
-
job_id: result.jobId,
|
|
9747
|
-
status: result.status,
|
|
9748
|
-
model_id: result.fineTunedModel,
|
|
9749
|
-
error: result.error
|
|
9750
|
-
}, null, 2)
|
|
9751
|
-
}
|
|
9752
|
-
]
|
|
9753
|
-
};
|
|
9754
|
-
}
|
|
9755
|
-
case "gather_training_data": {
|
|
9756
|
-
const {
|
|
9757
|
-
sources,
|
|
9758
|
-
limit: limit2,
|
|
9759
|
-
output_dir
|
|
9760
|
-
} = args;
|
|
9761
|
-
const { mkdirSync: mkdirSync2, writeFileSync } = await import("fs");
|
|
9762
|
-
const { join: join3 } = await import("path");
|
|
9763
|
-
const outDir = output_dir ?? defaultOutputDir();
|
|
9764
|
-
mkdirSync2(outDir, { recursive: true });
|
|
9765
|
-
const datasets = [];
|
|
9766
|
-
let totalExamples = 0;
|
|
9767
|
-
for (const source of sources) {
|
|
9768
|
-
let examples = [];
|
|
9769
|
-
if (source === "todos") {
|
|
9770
|
-
const result = await gatherFromTodos({ limit: limit2 });
|
|
9771
|
-
examples = result.examples;
|
|
9772
|
-
} else {
|
|
9773
|
-
examples = [];
|
|
9753
|
+
case "get_finetune_status": {
|
|
9754
|
+
const { job_id, provider } = args;
|
|
9755
|
+
const p = getProvider(provider);
|
|
9756
|
+
const result = await p.getFineTuneStatus(job_id);
|
|
9757
|
+
const db = getDb();
|
|
9758
|
+
const dbId = `${provider}-${job_id}`;
|
|
9759
|
+
const existing = await db.select().from(fineTunedModels).where(eq(fineTunedModels.id, dbId));
|
|
9760
|
+
if (existing.length > 0) {
|
|
9761
|
+
const mappedStatus = (() => {
|
|
9762
|
+
if (result.status === "succeeded")
|
|
9763
|
+
return "succeeded";
|
|
9764
|
+
if (result.status === "failed")
|
|
9765
|
+
return "failed";
|
|
9766
|
+
if (result.status === "cancelled")
|
|
9767
|
+
return "cancelled";
|
|
9768
|
+
if (result.status === "running")
|
|
9769
|
+
return "running";
|
|
9770
|
+
return "pending";
|
|
9771
|
+
})();
|
|
9772
|
+
await db.update(fineTunedModels).set({
|
|
9773
|
+
status: mappedStatus,
|
|
9774
|
+
updatedAt: Date.now()
|
|
9775
|
+
}).where(eq(fineTunedModels.id, dbId));
|
|
9774
9776
|
}
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9777
|
+
return {
|
|
9778
|
+
content: [
|
|
9779
|
+
{
|
|
9780
|
+
type: "text",
|
|
9781
|
+
text: JSON.stringify({
|
|
9782
|
+
job_id: result.jobId,
|
|
9783
|
+
status: result.status,
|
|
9784
|
+
model_id: result.fineTunedModel,
|
|
9785
|
+
error: result.error
|
|
9786
|
+
}, null, 2)
|
|
9787
|
+
}
|
|
9788
|
+
]
|
|
9789
|
+
};
|
|
9781
9790
|
}
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9791
|
+
case "gather_training_data": {
|
|
9792
|
+
const {
|
|
9793
|
+
sources,
|
|
9794
|
+
limit: limit2,
|
|
9795
|
+
output_dir
|
|
9796
|
+
} = args;
|
|
9797
|
+
const { mkdirSync: mkdirSync2, writeFileSync } = await import("fs");
|
|
9798
|
+
const { join: join3 } = await import("path");
|
|
9799
|
+
const outDir = output_dir ?? defaultOutputDir();
|
|
9800
|
+
mkdirSync2(outDir, { recursive: true });
|
|
9801
|
+
const datasets = [];
|
|
9802
|
+
let totalExamples = 0;
|
|
9803
|
+
for (const source of sources) {
|
|
9804
|
+
let examples = [];
|
|
9805
|
+
if (source === "todos") {
|
|
9806
|
+
const result = await gatherFromTodos({ limit: limit2 });
|
|
9807
|
+
examples = result.examples;
|
|
9808
|
+
} else {
|
|
9809
|
+
examples = [];
|
|
9787
9810
|
}
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9811
|
+
const filePath = join3(outDir, `${source}-${Date.now()}.jsonl`);
|
|
9812
|
+
const jsonl = examples.map((ex) => JSON.stringify(ex)).join(`
|
|
9813
|
+
`);
|
|
9814
|
+
writeFileSync(filePath, jsonl, "utf-8");
|
|
9815
|
+
datasets.push({ source, count: examples.length, file_path: filePath });
|
|
9816
|
+
totalExamples += examples.length;
|
|
9817
|
+
}
|
|
9795
9818
|
return {
|
|
9796
9819
|
content: [
|
|
9797
|
-
{
|
|
9798
|
-
|
|
9799
|
-
|
|
9820
|
+
{
|
|
9821
|
+
type: "text",
|
|
9822
|
+
text: JSON.stringify({ datasets, total_examples: totalExamples }, null, 2)
|
|
9823
|
+
}
|
|
9824
|
+
]
|
|
9800
9825
|
};
|
|
9801
9826
|
}
|
|
9802
|
-
|
|
9803
|
-
|
|
9827
|
+
case "preview_training_data": {
|
|
9828
|
+
const { file_path, limit: limit2 = 5 } = args;
|
|
9829
|
+
const resolvedPath = resolve2(file_path);
|
|
9830
|
+
if (!existsSync2(resolvedPath)) {
|
|
9831
|
+
return {
|
|
9832
|
+
content: [
|
|
9833
|
+
{ type: "text", text: `File not found: ${resolvedPath}` }
|
|
9834
|
+
],
|
|
9835
|
+
isError: true
|
|
9836
|
+
};
|
|
9837
|
+
}
|
|
9838
|
+
const content = readFileSync3(resolvedPath, "utf-8");
|
|
9839
|
+
const lines = content.split(`
|
|
9804
9840
|
`).map((l) => l.trim()).filter(Boolean);
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9841
|
+
const total = lines.length;
|
|
9842
|
+
const examples = lines.slice(0, limit2).map((line) => JSON.parse(line));
|
|
9843
|
+
return {
|
|
9844
|
+
content: [
|
|
9845
|
+
{
|
|
9846
|
+
type: "text",
|
|
9847
|
+
text: JSON.stringify({ examples, total }, null, 2)
|
|
9848
|
+
}
|
|
9849
|
+
]
|
|
9850
|
+
};
|
|
9851
|
+
}
|
|
9852
|
+
default:
|
|
9853
|
+
return {
|
|
9854
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
9855
|
+
isError: true
|
|
9856
|
+
};
|
|
9815
9857
|
}
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9858
|
+
} catch (err) {
|
|
9859
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9860
|
+
return {
|
|
9861
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
9862
|
+
isError: true
|
|
9863
|
+
};
|
|
9821
9864
|
}
|
|
9822
|
-
}
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
}
|
|
9830
|
-
|
|
9831
|
-
await
|
|
9865
|
+
});
|
|
9866
|
+
return server;
|
|
9867
|
+
}
|
|
9868
|
+
async function startMcpServer(transport = new StdioServerTransport) {
|
|
9869
|
+
const server = createMcpServer();
|
|
9870
|
+
await server.connect(transport);
|
|
9871
|
+
return server;
|
|
9872
|
+
}
|
|
9873
|
+
if (import.meta.main) {
|
|
9874
|
+
await startMcpServer();
|
|
9875
|
+
}
|
|
9876
|
+
export {
|
|
9877
|
+
startMcpServer,
|
|
9878
|
+
createMcpServer,
|
|
9879
|
+
MCP_SERVER_INFO
|
|
9880
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/brains",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Fine-tuned model tracker and trainer — wraps OpenAI + Thinker Labs, gathers training data from todos/mementos/conversations/sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|