@tensor-cad/mcp 0.1.2 → 0.1.3
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/artifacts.d.ts +46 -0
- package/bridge/server.d.ts +21 -0
- package/index.d.ts +14 -1
- package/index.js +140 -108
- package/package.json +2 -2
- package/serve.js +137 -108
- package/server.d.ts +9 -0
- package/server.json +2 -2
- package/stdio.js +137 -108
- package/store/disk-artifacts.d.ts +16 -0
- package/store/file-store.d.ts +10 -10
- package/store/types.d.ts +32 -14
- package/tools.d.ts +2 -1
package/stdio.js
CHANGED
|
@@ -312,7 +312,12 @@ Content-Length: 0\r
|
|
|
312
312
|
setTimeout(() => socket.destroy(), 50).unref();
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
315
|
-
this.wss.handleUpgrade(req, socket, head, (ws) =>
|
|
315
|
+
this.wss.handleUpgrade(req, socket, head, (ws) => {
|
|
316
|
+
this.attach(ws).catch((e) => {
|
|
317
|
+
this.log(`tensorcad bridge: could not greet a client: ${e.message}`);
|
|
318
|
+
this.send(ws, { type: "error", message: e.message });
|
|
319
|
+
});
|
|
320
|
+
});
|
|
316
321
|
});
|
|
317
322
|
}
|
|
318
323
|
get port() {
|
|
@@ -386,14 +391,14 @@ Content-Length: 0\r
|
|
|
386
391
|
}
|
|
387
392
|
return;
|
|
388
393
|
}
|
|
389
|
-
attach(ws) {
|
|
394
|
+
async attach(ws) {
|
|
390
395
|
this.send(ws, {
|
|
391
396
|
type: "hello",
|
|
392
397
|
protocol: BRIDGE_PROTOCOL,
|
|
393
398
|
server: this.options.name,
|
|
394
399
|
version: this.options.version,
|
|
395
400
|
root: this.options.root,
|
|
396
|
-
designs: this.options.store.list()
|
|
401
|
+
designs: await this.options.store.list()
|
|
397
402
|
});
|
|
398
403
|
ws.on("message", (raw) => {
|
|
399
404
|
let message;
|
|
@@ -403,23 +408,21 @@ Content-Length: 0\r
|
|
|
403
408
|
this.send(ws, { type: "error", message: `not JSON: ${e.message}` });
|
|
404
409
|
return;
|
|
405
410
|
}
|
|
406
|
-
|
|
407
|
-
this.handle(ws, message);
|
|
408
|
-
} catch (e) {
|
|
411
|
+
this.handle(ws, message).catch((e) => {
|
|
409
412
|
this.send(ws, { type: "error", message: e.message, about: message?.type });
|
|
410
|
-
}
|
|
413
|
+
});
|
|
411
414
|
});
|
|
412
415
|
}
|
|
413
|
-
handle(ws, message) {
|
|
416
|
+
async handle(ws, message) {
|
|
414
417
|
switch (message?.type) {
|
|
415
418
|
case "publish": {
|
|
416
419
|
const doc = asDocument(message.doc);
|
|
417
|
-
const record = this.during(ws, () => this.options.store.adopt(doc));
|
|
420
|
+
const record = await this.during(ws, () => this.options.store.adopt(doc));
|
|
418
421
|
this.send(ws, designMessage(record, "published"));
|
|
419
422
|
return;
|
|
420
423
|
}
|
|
421
424
|
case "attach": {
|
|
422
|
-
const record = this.options.store.get(message.design_id);
|
|
425
|
+
const record = await this.options.store.get(message.design_id);
|
|
423
426
|
this.send(ws, designMessage(record, "requested"));
|
|
424
427
|
return;
|
|
425
428
|
}
|
|
@@ -427,12 +430,12 @@ Content-Length: 0\r
|
|
|
427
430
|
case "replace": {
|
|
428
431
|
const write = message.type === "ops" ? () => this.options.store.apply(message.design_id, parseOps(message.ops), message.revision) : () => this.options.store.replace(message.design_id, asDocument(message.doc), message.revision);
|
|
429
432
|
try {
|
|
430
|
-
const { record } = this.during(ws, write);
|
|
433
|
+
const { record } = await this.during(ws, write);
|
|
431
434
|
this.send(ws, designMessage(record, message.type === "ops" ? "applied" : "replaced"));
|
|
432
435
|
} catch (e) {
|
|
433
436
|
if (e instanceof RevisionConflictError) {
|
|
434
437
|
this.send(ws, { type: "error", message: e.message, about: message.type });
|
|
435
|
-
this.send(ws, designMessage(this.options.store.get(message.design_id), "requested"));
|
|
438
|
+
this.send(ws, designMessage(await this.options.store.get(message.design_id), "requested"));
|
|
436
439
|
return;
|
|
437
440
|
}
|
|
438
441
|
throw e;
|
|
@@ -462,13 +465,11 @@ Content-Length: 0\r
|
|
|
462
465
|
}
|
|
463
466
|
}
|
|
464
467
|
}
|
|
465
|
-
during(ws, work) {
|
|
468
|
+
async during(ws, work) {
|
|
466
469
|
this.acting = ws;
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
this.acting = undefined;
|
|
471
|
-
}
|
|
470
|
+
const running = work();
|
|
471
|
+
this.acting = undefined;
|
|
472
|
+
return await running;
|
|
472
473
|
}
|
|
473
474
|
send(ws, message) {
|
|
474
475
|
if (ws.readyState !== ws.OPEN)
|
|
@@ -551,7 +552,7 @@ import { McpServer as McpServer2 } from "@modelcontextprotocol/server";
|
|
|
551
552
|
// packages/mcp/package.json
|
|
552
553
|
var package_default = {
|
|
553
554
|
name: "@tensor-cad/mcp",
|
|
554
|
-
version: "0.1.
|
|
555
|
+
version: "0.1.3",
|
|
555
556
|
description: "Model Context Protocol server for TensorCAD: design, validate, analyze and generate LLM architectures from an agent",
|
|
556
557
|
mcpName: "io.github.filip-pajalic/tensorcad",
|
|
557
558
|
type: "module",
|
|
@@ -826,7 +827,7 @@ class FileStore {
|
|
|
826
827
|
}
|
|
827
828
|
}
|
|
828
829
|
}
|
|
829
|
-
list() {
|
|
830
|
+
async list() {
|
|
830
831
|
return [...this.entries.values()].map((e) => summaryOf(e.record)).sort((a, b) => b.updated_at.localeCompare(a.updated_at));
|
|
831
832
|
}
|
|
832
833
|
async listFiles() {
|
|
@@ -852,13 +853,13 @@ class FileStore {
|
|
|
852
853
|
await walk(this.root, this.depth);
|
|
853
854
|
return out.sort();
|
|
854
855
|
}
|
|
855
|
-
get(id) {
|
|
856
|
+
async get(id) {
|
|
856
857
|
const entry = this.entries.get(id);
|
|
857
858
|
if (!entry)
|
|
858
859
|
throw new UnknownDesignError(id, [...this.entries.keys()]);
|
|
859
860
|
return entry.record;
|
|
860
861
|
}
|
|
861
|
-
create(options) {
|
|
862
|
+
async create(options) {
|
|
862
863
|
let doc;
|
|
863
864
|
let source;
|
|
864
865
|
if (options.preset) {
|
|
@@ -875,7 +876,7 @@ class FileStore {
|
|
|
875
876
|
doc.meta.name = options.name;
|
|
876
877
|
return this.register(doc, source, undefined, true);
|
|
877
878
|
}
|
|
878
|
-
adopt(doc) {
|
|
879
|
+
async adopt(doc) {
|
|
879
880
|
return this.register(doc, "derived", undefined, true);
|
|
880
881
|
}
|
|
881
882
|
async open(path) {
|
|
@@ -917,7 +918,7 @@ class FileStore {
|
|
|
917
918
|
this.emit({ kind: "registered", record });
|
|
918
919
|
return record;
|
|
919
920
|
}
|
|
920
|
-
apply(id, ops, expectedRevision) {
|
|
921
|
+
async apply(id, ops, expectedRevision) {
|
|
921
922
|
const entry = this.entry(id);
|
|
922
923
|
const { record } = entry;
|
|
923
924
|
if (expectedRevision !== undefined && expectedRevision !== record.revision) {
|
|
@@ -935,7 +936,7 @@ class FileStore {
|
|
|
935
936
|
this.emit({ kind: "applied", record, ops });
|
|
936
937
|
return { record, applied, previousRevision };
|
|
937
938
|
}
|
|
938
|
-
replace(id, doc, expectedRevision) {
|
|
939
|
+
async replace(id, doc, expectedRevision) {
|
|
939
940
|
const entry = this.entry(id);
|
|
940
941
|
const { record } = entry;
|
|
941
942
|
if (expectedRevision !== undefined && expectedRevision !== record.revision) {
|
|
@@ -952,7 +953,7 @@ class FileStore {
|
|
|
952
953
|
return { record, applied: ["replaced the document"], previousRevision };
|
|
953
954
|
}
|
|
954
955
|
async save(id, path) {
|
|
955
|
-
const record = this.get(id);
|
|
956
|
+
const record = await this.get(id);
|
|
956
957
|
const target = path ? this.resolvePath(path) : record.path ?? join2(this.root, `${slug(record.name)}.tensorcad.json`);
|
|
957
958
|
const text = `${JSON.stringify(record.doc, null, 2)}
|
|
958
959
|
`;
|
|
@@ -964,7 +965,7 @@ class FileStore {
|
|
|
964
965
|
this.emit({ kind: "saved", record });
|
|
965
966
|
return { record, path: target, bytes: Buffer.byteLength(text, "utf8") };
|
|
966
967
|
}
|
|
967
|
-
checkpoint(id, label) {
|
|
968
|
+
async checkpoint(id, label) {
|
|
968
969
|
const entry = this.entry(id);
|
|
969
970
|
const info = {
|
|
970
971
|
checkpoint_id: `ckpt_${this.nextCheckpoint++}`,
|
|
@@ -975,11 +976,11 @@ class FileStore {
|
|
|
975
976
|
entry.checkpoints.set(info.checkpoint_id, { ...info, doc: structuredClone(entry.record.doc) });
|
|
976
977
|
return info;
|
|
977
978
|
}
|
|
978
|
-
checkpoints(id) {
|
|
979
|
+
async checkpoints(id) {
|
|
979
980
|
const entry = this.entry(id);
|
|
980
981
|
return [...entry.checkpoints.values()].map(({ doc: _doc, ...info }) => info);
|
|
981
982
|
}
|
|
982
|
-
restore(id, checkpointId) {
|
|
983
|
+
async restore(id, checkpointId) {
|
|
983
984
|
const entry = this.entry(id);
|
|
984
985
|
const { record } = entry;
|
|
985
986
|
let doc;
|
|
@@ -1036,6 +1037,25 @@ function slug(name) {
|
|
|
1036
1037
|
return name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "design";
|
|
1037
1038
|
}
|
|
1038
1039
|
|
|
1040
|
+
// packages/mcp/src/store/disk-artifacts.ts
|
|
1041
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
1042
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "node:path";
|
|
1043
|
+
|
|
1044
|
+
class DiskSink {
|
|
1045
|
+
root;
|
|
1046
|
+
label = "disk";
|
|
1047
|
+
constructor(root) {
|
|
1048
|
+
this.root = root;
|
|
1049
|
+
}
|
|
1050
|
+
async write(prefix, path, contents) {
|
|
1051
|
+
const base = isAbsolute2(prefix) ? prefix : resolve2(this.root, prefix);
|
|
1052
|
+
const target = join3(base, path);
|
|
1053
|
+
await mkdir3(dirname3(target), { recursive: true });
|
|
1054
|
+
await writeFile3(target, contents, "utf8");
|
|
1055
|
+
return { location: target };
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1039
1059
|
// packages/mcp/src/prompts.ts
|
|
1040
1060
|
import * as z2 from "zod";
|
|
1041
1061
|
import { HARDWARE, PRESET_NAMES as PRESET_NAMES2 } from "@tensor-cad/engine/node";
|
|
@@ -1698,8 +1718,8 @@ function registerResources(server, store) {
|
|
|
1698
1718
|
title: "Design rule report",
|
|
1699
1719
|
description: "Every finding for a design: shape errors, memory fit, kernel constraints, Chinchilla sanity.",
|
|
1700
1720
|
mimeType: JSON_MIME
|
|
1701
|
-
}, (uri, { id }) => {
|
|
1702
|
-
const record = store.get(String(id));
|
|
1721
|
+
}, async (uri, { id }) => {
|
|
1722
|
+
const record = await store.get(String(id));
|
|
1703
1723
|
const report = validate(record.doc);
|
|
1704
1724
|
return json(uri, {
|
|
1705
1725
|
design_id: record.design_id,
|
|
@@ -1714,14 +1734,14 @@ function registerResources(server, store) {
|
|
|
1714
1734
|
title: "Design analysis",
|
|
1715
1735
|
description: "Parameters, FLOPs, KV cache, memory, throughput and cost at the document's own defaults.",
|
|
1716
1736
|
mimeType: JSON_MIME
|
|
1717
|
-
}, (uri, { id }) => {
|
|
1718
|
-
const record = store.get(String(id));
|
|
1737
|
+
}, async (uri, { id }) => {
|
|
1738
|
+
const record = await store.get(String(id));
|
|
1719
1739
|
const result = analyze(record.doc);
|
|
1720
1740
|
return json(uri, { design_id: record.design_id, revision: record.revision, ...analysisJson(result) });
|
|
1721
1741
|
});
|
|
1722
1742
|
server.registerResource("design", new ResourceTemplate("tensorcad://designs/{id}", {
|
|
1723
|
-
list: () => ({
|
|
1724
|
-
resources: store.list().map((d) => ({
|
|
1743
|
+
list: async () => ({
|
|
1744
|
+
resources: (await store.list()).map((d) => ({
|
|
1725
1745
|
uri: `tensorcad://designs/${d.design_id}`,
|
|
1726
1746
|
name: d.name,
|
|
1727
1747
|
title: `${d.name} (revision ${d.revision})`,
|
|
@@ -1734,8 +1754,8 @@ function registerResources(server, store) {
|
|
|
1734
1754
|
title: "Design document",
|
|
1735
1755
|
description: "The literal .tensorcad.json document, with a compact outline beside it.",
|
|
1736
1756
|
mimeType: JSON_MIME
|
|
1737
|
-
}, (uri, { id }) => {
|
|
1738
|
-
const record = store.get(String(id));
|
|
1757
|
+
}, async (uri, { id }) => {
|
|
1758
|
+
const record = await store.get(String(id));
|
|
1739
1759
|
return json(uri, {
|
|
1740
1760
|
design_id: record.design_id,
|
|
1741
1761
|
revision: record.revision,
|
|
@@ -1749,7 +1769,7 @@ function registerResources(server, store) {
|
|
|
1749
1769
|
title: "Block catalog",
|
|
1750
1770
|
description: "Every block type with its parameter schema, port patterns and documentation. " + "Primitives carry the formulas; composites expand into primitives; repeat is the only container.",
|
|
1751
1771
|
mimeType: JSON_MIME
|
|
1752
|
-
}, (uri) => {
|
|
1772
|
+
}, async (uri) => {
|
|
1753
1773
|
const blocks = allCatalogEntries();
|
|
1754
1774
|
return json(uri, {
|
|
1755
1775
|
count: blocks.length,
|
|
@@ -1758,7 +1778,7 @@ function registerResources(server, store) {
|
|
|
1758
1778
|
});
|
|
1759
1779
|
});
|
|
1760
1780
|
server.registerResource("catalog-block", new ResourceTemplate("tensorcad://catalog/{type}", {
|
|
1761
|
-
list: () => ({
|
|
1781
|
+
list: async () => ({
|
|
1762
1782
|
resources: Object.keys(CATALOG).sort().map((type) => ({
|
|
1763
1783
|
uri: `tensorcad://catalog/${type}`,
|
|
1764
1784
|
name: type,
|
|
@@ -1793,12 +1813,10 @@ function registerResources(server, store) {
|
|
|
1793
1813
|
}));
|
|
1794
1814
|
}
|
|
1795
1815
|
function completeDesignId(store) {
|
|
1796
|
-
return (value) => store.list().map((d) => d.design_id).filter((id) => id.startsWith(value));
|
|
1816
|
+
return async (value) => (await store.list()).map((d) => d.design_id).filter((id) => id.startsWith(value));
|
|
1797
1817
|
}
|
|
1798
1818
|
|
|
1799
1819
|
// packages/mcp/src/tools.ts
|
|
1800
|
-
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
1801
|
-
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "node:path";
|
|
1802
1820
|
import * as z3 from "zod";
|
|
1803
1821
|
import { formatBytes as formatBytes2, formatCount as formatCount2 } from "@tensor-cad/engine";
|
|
1804
1822
|
import {
|
|
@@ -1875,7 +1893,7 @@ function toAnalysisOptions(input) {
|
|
|
1875
1893
|
var READ = { readOnlyHint: true, idempotentHint: true, openWorldHint: false };
|
|
1876
1894
|
var WRITE = { readOnlyHint: false, idempotentHint: false, openWorldHint: false };
|
|
1877
1895
|
var DESTRUCTIVE = { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: false };
|
|
1878
|
-
function registerTools(server, store) {
|
|
1896
|
+
function registerTools(server, store, artifacts) {
|
|
1879
1897
|
server.registerTool("tensorcad_list_designs", {
|
|
1880
1898
|
title: "List designs",
|
|
1881
1899
|
description: "List the designs this server has open, the built-in reference architectures you can start from, " + "and the .tensorcad.json files it can see on disk. Start here when you do not already hold a design_id.",
|
|
@@ -1894,7 +1912,7 @@ function registerTools(server, store) {
|
|
|
1894
1912
|
}),
|
|
1895
1913
|
annotations: { ...READ, title: "List designs" }
|
|
1896
1914
|
}, async ({ include_files }) => guard(async () => {
|
|
1897
|
-
const designs = store.list();
|
|
1915
|
+
const designs = await store.list();
|
|
1898
1916
|
const presets = PRESET_NAMES3.map((name) => {
|
|
1899
1917
|
const doc = getPreset2(name);
|
|
1900
1918
|
const p = { name };
|
|
@@ -1935,8 +1953,8 @@ ${designs.map((d) => ` ${d.design_id} ${d.name} rev ${d.revision}${d.dirty ?
|
|
|
1935
1953
|
outline: Outline
|
|
1936
1954
|
}),
|
|
1937
1955
|
annotations: { ...WRITE, title: "New design" }
|
|
1938
|
-
}, async (args) => guard(() => {
|
|
1939
|
-
const record = store.create(args);
|
|
1956
|
+
}, async (args) => guard(async () => {
|
|
1957
|
+
const record = await store.create(args);
|
|
1940
1958
|
const outline = outlineOf(record.doc);
|
|
1941
1959
|
return ok(`${record.design_id} (revision ${record.revision})
|
|
1942
1960
|
|
|
@@ -2021,8 +2039,8 @@ ${formatCount2(report.analysis.params.total)} parameters, ` + `${report.counts.e
|
|
|
2021
2039
|
document: z3.record(z3.string(), z3.unknown()).optional().describe("The literal design document.")
|
|
2022
2040
|
}),
|
|
2023
2041
|
annotations: { ...READ, title: "Get design" }
|
|
2024
|
-
}, async ({ design_id, format }) => guard(() => {
|
|
2025
|
-
const record = store.get(design_id);
|
|
2042
|
+
}, async ({ design_id, format }) => guard(async () => {
|
|
2043
|
+
const record = await store.get(design_id);
|
|
2026
2044
|
const outline = outlineOf(record.doc);
|
|
2027
2045
|
const mode = format ?? "outline";
|
|
2028
2046
|
const base = {
|
|
@@ -2076,8 +2094,8 @@ ${outlineText(outline)}`, {
|
|
|
2076
2094
|
children: z3.array(z3.string())
|
|
2077
2095
|
}),
|
|
2078
2096
|
annotations: { ...READ, title: "Get block" }
|
|
2079
|
-
}, async ({ design_id, path }) => guard(() => {
|
|
2080
|
-
const record = store.get(design_id);
|
|
2097
|
+
}, async ({ design_id, path }) => guard(async () => {
|
|
2098
|
+
const record = await store.get(design_id);
|
|
2081
2099
|
const detail = blockDetail(record.doc, path);
|
|
2082
2100
|
return ok(blockText(detail), {
|
|
2083
2101
|
design_id: record.design_id,
|
|
@@ -2117,7 +2135,7 @@ ${outlineText(outline)}`, {
|
|
|
2117
2135
|
}))
|
|
2118
2136
|
}),
|
|
2119
2137
|
annotations: { ...READ, title: "Search catalog" }
|
|
2120
|
-
}, async ({ query, category, kind, limit }) => guard(() => {
|
|
2138
|
+
}, async ({ query, category, kind, limit }) => guard(async () => {
|
|
2121
2139
|
const all = allCatalogEntries();
|
|
2122
2140
|
const q = query?.toLowerCase();
|
|
2123
2141
|
const matched = all.filter((e) => {
|
|
@@ -2161,10 +2179,10 @@ ${catalogText(blocks)}`, {
|
|
|
2161
2179
|
validation: ValidationSummary
|
|
2162
2180
|
}),
|
|
2163
2181
|
annotations: { ...WRITE, title: "Apply edits" }
|
|
2164
|
-
}, async ({ design_id, expected_revision, ops }) => guard(() => {
|
|
2165
|
-
const before = store.get(design_id);
|
|
2182
|
+
}, async ({ design_id, expected_revision, ops }) => guard(async () => {
|
|
2183
|
+
const before = await store.get(design_id);
|
|
2166
2184
|
const paramsBefore = outlineOf(before.doc).params_total;
|
|
2167
|
-
const outcome = store.apply(design_id, ops, expected_revision);
|
|
2185
|
+
const outcome = await store.apply(design_id, ops, expected_revision);
|
|
2168
2186
|
const report = validate2(outcome.record.doc);
|
|
2169
2187
|
const total = report.analysis.params.total;
|
|
2170
2188
|
const delta = total - paramsBefore;
|
|
@@ -2207,8 +2225,8 @@ ${catalogText(blocks)}`, {
|
|
|
2207
2225
|
params_total: z3.number()
|
|
2208
2226
|
}),
|
|
2209
2227
|
annotations: { ...READ, title: "Validate design" }
|
|
2210
|
-
}, async ({ design_id, severity, ...rest }) => guard(() => {
|
|
2211
|
-
const record = store.get(design_id);
|
|
2228
|
+
}, async ({ design_id, severity, ...rest }) => guard(async () => {
|
|
2229
|
+
const record = await store.get(design_id);
|
|
2212
2230
|
const report = validate2(record.doc, toAnalysisOptions(rest));
|
|
2213
2231
|
const rank = { error: 0, warning: 1, info: 2 };
|
|
2214
2232
|
const findings = findingsJson(report).filter((f) => severity === undefined || rank[f.severity] <= rank[severity]);
|
|
@@ -2235,8 +2253,8 @@ ${catalogText(blocks)}`, {
|
|
|
2235
2253
|
inputSchema: z3.object({ design_id: DESIGN_ID, ...analysisOptionsShape }),
|
|
2236
2254
|
outputSchema: AnalysisOutput,
|
|
2237
2255
|
annotations: { ...READ, title: "Analyze design" }
|
|
2238
|
-
}, async ({ design_id, ...rest }) => guard(() => {
|
|
2239
|
-
const record = store.get(design_id);
|
|
2256
|
+
}, async ({ design_id, ...rest }) => guard(async () => {
|
|
2257
|
+
const record = await store.get(design_id);
|
|
2240
2258
|
const result = analyze2(record.doc, toAnalysisOptions(rest));
|
|
2241
2259
|
return ok(analysisText(result), {
|
|
2242
2260
|
design_id: record.design_id,
|
|
@@ -2269,30 +2287,36 @@ ${catalogText(blocks)}`, {
|
|
|
2269
2287
|
}),
|
|
2270
2288
|
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false, title: "Generate code" }
|
|
2271
2289
|
}, async ({ design_id, class_name, include_smoke_test, out_dir }) => guard(async () => {
|
|
2272
|
-
const record = store.get(design_id);
|
|
2290
|
+
const record = await store.get(design_id);
|
|
2273
2291
|
const generated = generateTorch(record.doc, {
|
|
2274
2292
|
...class_name ? { className: class_name } : {},
|
|
2275
2293
|
includeSmokeTest: include_smoke_test ?? false
|
|
2276
2294
|
});
|
|
2277
|
-
const root = out_dir ? isAbsolute2(out_dir) ? out_dir : resolve2(process.cwd(), out_dir) : undefined;
|
|
2278
2295
|
const files = [];
|
|
2279
2296
|
for (const file of generated.files) {
|
|
2280
|
-
const bytes =
|
|
2297
|
+
const bytes = new TextEncoder().encode(file.contents).length;
|
|
2281
2298
|
const lines = file.contents.split(`
|
|
2282
2299
|
`).length;
|
|
2283
|
-
if (
|
|
2284
|
-
const target = join3(root, file.path);
|
|
2285
|
-
await mkdir3(dirname3(target), { recursive: true });
|
|
2286
|
-
await writeFile3(target, file.contents, "utf8");
|
|
2287
|
-
files.push({ path: file.path, bytes, lines, written_to: target });
|
|
2288
|
-
} else {
|
|
2300
|
+
if (out_dir === undefined) {
|
|
2289
2301
|
files.push({ path: file.path, bytes, lines, contents: file.contents });
|
|
2302
|
+
continue;
|
|
2290
2303
|
}
|
|
2304
|
+
const written = await artifacts.write(out_dir, file.path, file.contents);
|
|
2305
|
+
files.push({
|
|
2306
|
+
path: file.path,
|
|
2307
|
+
bytes,
|
|
2308
|
+
lines,
|
|
2309
|
+
written_to: written.location,
|
|
2310
|
+
...written.url ? { url: written.url } : {}
|
|
2311
|
+
});
|
|
2291
2312
|
}
|
|
2292
|
-
const text =
|
|
2293
|
-
`) : generated.files.map((f) => `# ${f.path}
|
|
2313
|
+
const text = out_dir === undefined ? generated.files.map((f) => `# ${f.path}
|
|
2294
2314
|
${f.contents}`).join(`
|
|
2295
2315
|
|
|
2316
|
+
`) : [
|
|
2317
|
+
`wrote ${files.length} file(s) to ${artifacts.label}`,
|
|
2318
|
+
...files.map((f) => ` ${f.path} ${f.bytes} bytes ${f.url ?? f.written_to}`)
|
|
2319
|
+
].join(`
|
|
2296
2320
|
`);
|
|
2297
2321
|
return ok(generated.warnings.length > 0 ? `${text}
|
|
2298
2322
|
|
|
@@ -2301,8 +2325,8 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2301
2325
|
`)}` : text, {
|
|
2302
2326
|
design_id: record.design_id,
|
|
2303
2327
|
revision: record.revision,
|
|
2304
|
-
wrote:
|
|
2305
|
-
...
|
|
2328
|
+
wrote: out_dir !== undefined,
|
|
2329
|
+
...out_dir !== undefined ? { out_dir } : {},
|
|
2306
2330
|
files,
|
|
2307
2331
|
warnings: generated.warnings
|
|
2308
2332
|
});
|
|
@@ -2328,12 +2352,12 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2328
2352
|
}))
|
|
2329
2353
|
}),
|
|
2330
2354
|
annotations: { ...WRITE, title: "Checkpoint design" }
|
|
2331
|
-
}, async ({ design_id, label }) => guard(() => {
|
|
2332
|
-
const info = store.checkpoint(design_id, label);
|
|
2355
|
+
}, async ({ design_id, label }) => guard(async () => {
|
|
2356
|
+
const info = await store.checkpoint(design_id, label);
|
|
2333
2357
|
return ok(`${info.checkpoint_id} at revision ${info.revision}: ${info.label}`, {
|
|
2334
2358
|
design_id,
|
|
2335
2359
|
...info,
|
|
2336
|
-
checkpoints: store.checkpoints(design_id)
|
|
2360
|
+
checkpoints: await store.checkpoints(design_id)
|
|
2337
2361
|
});
|
|
2338
2362
|
}));
|
|
2339
2363
|
server.registerTool("tensorcad_restore", {
|
|
@@ -2352,8 +2376,8 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2352
2376
|
validation: ValidationSummary
|
|
2353
2377
|
}),
|
|
2354
2378
|
annotations: { ...DESTRUCTIVE, title: "Restore design" }
|
|
2355
|
-
}, async ({ design_id, checkpoint_id }) => guard(() => {
|
|
2356
|
-
const { record, restoredFrom } = store.restore(design_id, checkpoint_id);
|
|
2379
|
+
}, async ({ design_id, checkpoint_id }) => guard(async () => {
|
|
2380
|
+
const { record, restoredFrom } = await store.restore(design_id, checkpoint_id);
|
|
2357
2381
|
const report = validate2(record.doc);
|
|
2358
2382
|
return ok(`${record.design_id} restored from ${restoredFrom}; now revision ${record.revision}, ` + `${formatCount2(report.analysis.params.total)} parameters`, {
|
|
2359
2383
|
design_id: record.design_id,
|
|
@@ -2396,8 +2420,8 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2396
2420
|
})
|
|
2397
2421
|
}),
|
|
2398
2422
|
annotations: { ...READ, title: "Explain a block" }
|
|
2399
|
-
}, async ({ design_id, path, ...rest }) => guard(() => {
|
|
2400
|
-
const record = store.get(design_id);
|
|
2423
|
+
}, async ({ design_id, path, ...rest }) => guard(async () => {
|
|
2424
|
+
const record = await store.get(design_id);
|
|
2401
2425
|
const e = explain(record.doc, path, toAnalysisOptions(rest));
|
|
2402
2426
|
const lines = [
|
|
2403
2427
|
`${path} ${e.type} (${e.kind})`,
|
|
@@ -2458,8 +2482,8 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2458
2482
|
notes: z3.array(z3.string())
|
|
2459
2483
|
}),
|
|
2460
2484
|
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false, title: "Scale a design" }
|
|
2461
|
-
}, async ({ design_id, target_params, target_basis, vocab, tie_head, keep_depth }) => guard(() => {
|
|
2462
|
-
const record = store.get(design_id);
|
|
2485
|
+
}, async ({ design_id, target_params, target_basis, vocab, tie_head, keep_depth }) => guard(async () => {
|
|
2486
|
+
const record = await store.get(design_id);
|
|
2463
2487
|
const result = scaleDesign(record.doc, {
|
|
2464
2488
|
targetParams: target_params,
|
|
2465
2489
|
...target_basis ? { targetBasis: target_basis } : {},
|
|
@@ -2467,7 +2491,7 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2467
2491
|
...tie_head !== undefined ? { tieHead: tie_head } : {},
|
|
2468
2492
|
...keep_depth !== undefined ? { keepDepth: keep_depth } : {}
|
|
2469
2493
|
});
|
|
2470
|
-
const saved = store.adopt(result.doc);
|
|
2494
|
+
const saved = await store.adopt(result.doc);
|
|
2471
2495
|
const changes = Object.entries(result.changes).map(([symbol, c]) => ({
|
|
2472
2496
|
symbol,
|
|
2473
2497
|
from: c.from,
|
|
@@ -2520,28 +2544,32 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2520
2544
|
notes: z3.array(z3.string())
|
|
2521
2545
|
}),
|
|
2522
2546
|
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false, title: "Build a width ladder" }
|
|
2523
|
-
}, async ({ design_id, widths, base_width }) => guard(() => {
|
|
2524
|
-
const record = store.get(design_id);
|
|
2547
|
+
}, async ({ design_id, widths, base_width }) => guard(async () => {
|
|
2548
|
+
const record = await store.get(design_id);
|
|
2525
2549
|
const ladder = mupLadder(record.doc, {
|
|
2526
2550
|
...widths ? { widths } : {},
|
|
2527
2551
|
...base_width !== undefined ? { baseWidth: base_width } : {}
|
|
2528
2552
|
});
|
|
2529
|
-
const rungs =
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2553
|
+
const rungs = [];
|
|
2554
|
+
for (const rung of ladder.rungs) {
|
|
2555
|
+
const adopted = await store.adopt(rung.doc);
|
|
2556
|
+
rungs.push({
|
|
2557
|
+
design_id: adopted.design_id,
|
|
2558
|
+
width: rung.width,
|
|
2559
|
+
multiplier: rung.multiplier,
|
|
2560
|
+
heads: rung.heads,
|
|
2561
|
+
params: rung.params,
|
|
2562
|
+
base: rung.base,
|
|
2563
|
+
scaling: rung.scaling.map((s) => ({
|
|
2564
|
+
class: s.class,
|
|
2565
|
+
init_std: s.initStd,
|
|
2566
|
+
adam_lr: s.adamLr,
|
|
2567
|
+
paths: s.paths,
|
|
2568
|
+
why: s.why
|
|
2569
|
+
})),
|
|
2570
|
+
notes: rung.notes
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2545
2573
|
const text = [
|
|
2546
2574
|
`${record.doc.meta.name} laddered by ${ladder.widthSymbol}, tuned at ${ladder.baseWidth}, ` + `heads of ${ladder.headDim} throughout`,
|
|
2547
2575
|
...rungs.map((r) => ` ${r.base ? "base " : " "}${r.width} wide, ${r.heads} heads, ${formatCount2(r.params)}` + ` ${r.scaling.filter((s) => s.class !== "input").map((s) => `${s.class} init x${s.init_std.toPrecision(4)} rate x${s.adam_lr.toPrecision(4)}`).join(", ")}`),
|
|
@@ -2590,8 +2618,8 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2590
2618
|
notes: z3.array(z3.string())
|
|
2591
2619
|
}),
|
|
2592
2620
|
annotations: { ...READ, title: "Plan a cluster" }
|
|
2593
|
-
}, async ({ design_id, gpus, gpus_per_node, headroom, limit, ...rest }) => guard(() => {
|
|
2594
|
-
const record = store.get(design_id);
|
|
2621
|
+
}, async ({ design_id, gpus, gpus_per_node, headroom, limit, ...rest }) => guard(async () => {
|
|
2622
|
+
const record = await store.get(design_id);
|
|
2595
2623
|
const result = planCluster(record.doc, toAnalysisOptions(rest), {
|
|
2596
2624
|
gpus,
|
|
2597
2625
|
...gpus_per_node !== undefined ? { gpusPerNode: gpus_per_node } : {},
|
|
@@ -2667,9 +2695,9 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2667
2695
|
}))
|
|
2668
2696
|
}),
|
|
2669
2697
|
annotations: { ...READ, title: "Compare two designs" }
|
|
2670
|
-
}, async ({ a, b, ...rest }) => guard(() => {
|
|
2671
|
-
const left = store.get(a);
|
|
2672
|
-
const right = store.get(b);
|
|
2698
|
+
}, async ({ a, b, ...rest }) => guard(async () => {
|
|
2699
|
+
const left = await store.get(a);
|
|
2700
|
+
const right = await store.get(b);
|
|
2673
2701
|
const d = diffDesigns(left.doc, right.doc, toAnalysisOptions(rest));
|
|
2674
2702
|
const brief = (v) => {
|
|
2675
2703
|
if (v === undefined || v === null)
|
|
@@ -2735,9 +2763,9 @@ ${generated.warnings.map((w) => ` ${w}`).join(`
|
|
|
2735
2763
|
warnings: z3.array(z3.string())
|
|
2736
2764
|
}),
|
|
2737
2765
|
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false, title: "Import a config" }
|
|
2738
|
-
}, async ({ config, name }) => guard(() => {
|
|
2766
|
+
}, async ({ config, name }) => guard(async () => {
|
|
2739
2767
|
const result = importHfConfig(config, name);
|
|
2740
|
-
const record = store.adopt(result.doc);
|
|
2768
|
+
const record = await store.adopt(result.doc);
|
|
2741
2769
|
const total = analyze2(result.doc).params.total;
|
|
2742
2770
|
const text = [
|
|
2743
2771
|
`${result.doc.meta.name}: ${formatCount2(total)} parameters`,
|
|
@@ -2800,13 +2828,14 @@ var INSTRUCTIONS = [
|
|
|
2800
2828
|
].join(`
|
|
2801
2829
|
`);
|
|
2802
2830
|
function createServer2(options = {}) {
|
|
2803
|
-
const { store: given, bridge, ...storeOptions } = options;
|
|
2831
|
+
const { store: given, bridge, artifacts: givenArtifacts, ...storeOptions } = options;
|
|
2804
2832
|
const store = given ?? new FileStore(storeOptions);
|
|
2833
|
+
const artifacts = givenArtifacts ?? new DiskSink(storeOptions.root ?? process.cwd());
|
|
2805
2834
|
const server = new McpServer2({ name: SERVER_NAME, version: SERVER_VERSION }, {
|
|
2806
2835
|
capabilities: { tools: {}, resources: {}, prompts: {}, completions: {} },
|
|
2807
2836
|
instructions: INSTRUCTIONS
|
|
2808
2837
|
});
|
|
2809
|
-
registerTools(server, store);
|
|
2838
|
+
registerTools(server, store, artifacts);
|
|
2810
2839
|
registerResources(server, store);
|
|
2811
2840
|
registerPrompts(server);
|
|
2812
2841
|
if (bridge)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated files, written to a directory.
|
|
3
|
+
*
|
|
4
|
+
* What the tool did inline until there was a second kind of destination. It is
|
|
5
|
+
* here rather than in `tools.ts` because it is the only part of that tool that
|
|
6
|
+
* cannot run in a Worker — `mkdir` and `writeFile` do not exist there — and
|
|
7
|
+
* keeping it in one small module is what lets the hosted build substitute
|
|
8
|
+
* something else rather than fork the tool.
|
|
9
|
+
*/
|
|
10
|
+
import type { ArtifactSink, WrittenArtifact } from "../artifacts.js";
|
|
11
|
+
export declare class DiskSink implements ArtifactSink {
|
|
12
|
+
private readonly root;
|
|
13
|
+
readonly label = "disk";
|
|
14
|
+
constructor(root: string);
|
|
15
|
+
write(prefix: string, path: string, contents: string): Promise<WrittenArtifact>;
|
|
16
|
+
}
|
package/store/file-store.d.ts
CHANGED
|
@@ -31,26 +31,26 @@ export declare class FileStore implements DocumentStore {
|
|
|
31
31
|
* and it is reported where a server's diagnostics go.
|
|
32
32
|
*/
|
|
33
33
|
private emit;
|
|
34
|
-
list(): DesignSummary[]
|
|
34
|
+
list(): Promise<DesignSummary[]>;
|
|
35
35
|
listFiles(): Promise<string[]>;
|
|
36
|
-
get(id: string): DesignRecord
|
|
37
|
-
create(options: NewDesignOptions): DesignRecord
|
|
38
|
-
adopt(doc: Doc): DesignRecord
|
|
36
|
+
get(id: string): Promise<DesignRecord>;
|
|
37
|
+
create(options: NewDesignOptions): Promise<DesignRecord>;
|
|
38
|
+
adopt(doc: Doc): Promise<DesignRecord>;
|
|
39
39
|
open(path: string): Promise<DesignRecord>;
|
|
40
40
|
private register;
|
|
41
|
-
apply(id: string, ops: Op[], expectedRevision?: number): ApplyOutcome
|
|
42
|
-
replace(id: string, doc: Doc, expectedRevision?: number): ApplyOutcome
|
|
41
|
+
apply(id: string, ops: Op[], expectedRevision?: number): Promise<ApplyOutcome>;
|
|
42
|
+
replace(id: string, doc: Doc, expectedRevision?: number): Promise<ApplyOutcome>;
|
|
43
43
|
save(id: string, path?: string): Promise<{
|
|
44
44
|
record: DesignRecord;
|
|
45
45
|
path: string;
|
|
46
46
|
bytes: number;
|
|
47
47
|
}>;
|
|
48
|
-
checkpoint(id: string, label?: string): CheckpointInfo
|
|
49
|
-
checkpoints(id: string): CheckpointInfo[]
|
|
50
|
-
restore(id: string, checkpointId?: string): {
|
|
48
|
+
checkpoint(id: string, label?: string): Promise<CheckpointInfo>;
|
|
49
|
+
checkpoints(id: string): Promise<CheckpointInfo[]>;
|
|
50
|
+
restore(id: string, checkpointId?: string): Promise<{
|
|
51
51
|
record: DesignRecord;
|
|
52
52
|
restoredFrom: string;
|
|
53
|
-
}
|
|
53
|
+
}>;
|
|
54
54
|
private entry;
|
|
55
55
|
private resolvePath;
|
|
56
56
|
/** Whether a path exists, used by tools that want a friendlier message. */
|