@deepagents/context 0.8.1 → 0.9.0
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/index.d.ts +93 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +297 -103
- package/dist/index.js.map +4 -4
- package/dist/lib/agent.d.ts +28 -0
- package/dist/lib/agent.d.ts.map +1 -0
- package/dist/lib/codec.d.ts +17 -0
- package/dist/lib/codec.d.ts.map +1 -0
- package/dist/lib/context.d.ts +6 -0
- package/dist/lib/context.d.ts.map +1 -1
- package/dist/lib/estimate.d.ts +15 -2
- package/dist/lib/estimate.d.ts.map +1 -1
- package/dist/lib/store/sqlite.store.d.ts +4 -3
- package/dist/lib/store/sqlite.store.d.ts.map +1 -1
- package/dist/lib/store/store.d.ts +14 -3
- package/dist/lib/store/store.d.ts.map +1 -1
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// packages/context/src/index.ts
|
|
2
|
+
import { generateId } from "ai";
|
|
3
|
+
|
|
1
4
|
// packages/context/src/lib/context.ts
|
|
2
5
|
function isFragment(data) {
|
|
3
6
|
return typeof data === "object" && data !== null && "name" in data && "data" in data && typeof data.name === "string";
|
|
@@ -119,7 +122,8 @@ var ModelsRegistry = class {
|
|
|
119
122
|
context: model.limit.context,
|
|
120
123
|
output: model.limit.output,
|
|
121
124
|
exceedsContext: tokens > model.limit.context
|
|
122
|
-
}
|
|
125
|
+
},
|
|
126
|
+
fragments: []
|
|
123
127
|
};
|
|
124
128
|
}
|
|
125
129
|
};
|
|
@@ -130,6 +134,43 @@ function getModelsRegistry() {
|
|
|
130
134
|
}
|
|
131
135
|
return _registry;
|
|
132
136
|
}
|
|
137
|
+
async function estimate(modelId, renderer, ...fragments) {
|
|
138
|
+
const registry = getModelsRegistry();
|
|
139
|
+
await registry.load();
|
|
140
|
+
const input = renderer.render(fragments);
|
|
141
|
+
const model = registry.get(modelId);
|
|
142
|
+
if (!model) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Model "${modelId}" not found. Call load() first or check model ID.`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const tokenizer = registry.getTokenizer(modelId);
|
|
148
|
+
const totalTokens = tokenizer.count(input);
|
|
149
|
+
const totalCost = totalTokens / 1e6 * model.cost.input;
|
|
150
|
+
const fragmentEstimates = fragments.map((fragment2) => {
|
|
151
|
+
const rendered = renderer.render([fragment2]);
|
|
152
|
+
const tokens = tokenizer.count(rendered);
|
|
153
|
+
const cost = tokens / 1e6 * model.cost.input;
|
|
154
|
+
return {
|
|
155
|
+
id: fragment2.id,
|
|
156
|
+
name: fragment2.name,
|
|
157
|
+
tokens,
|
|
158
|
+
cost
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
model: model.id,
|
|
163
|
+
provider: model.provider,
|
|
164
|
+
tokens: totalTokens,
|
|
165
|
+
cost: totalCost,
|
|
166
|
+
limits: {
|
|
167
|
+
context: model.limit.context,
|
|
168
|
+
output: model.limit.output,
|
|
169
|
+
exceedsContext: totalTokens > model.limit.context
|
|
170
|
+
},
|
|
171
|
+
fragments: fragmentEstimates
|
|
172
|
+
};
|
|
173
|
+
}
|
|
133
174
|
|
|
134
175
|
// packages/context/src/lib/renderers/abstract.renderer.ts
|
|
135
176
|
import pluralize from "pluralize";
|
|
@@ -789,12 +830,13 @@ var ContextStore = class {
|
|
|
789
830
|
import { DatabaseSync } from "node:sqlite";
|
|
790
831
|
var STORE_DDL = `
|
|
791
832
|
-- Chats table
|
|
833
|
+
-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
|
|
792
834
|
CREATE TABLE IF NOT EXISTS chats (
|
|
793
835
|
id TEXT PRIMARY KEY,
|
|
794
836
|
title TEXT,
|
|
795
837
|
metadata TEXT,
|
|
796
|
-
createdAt INTEGER NOT NULL,
|
|
797
|
-
updatedAt INTEGER NOT NULL
|
|
838
|
+
createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
839
|
+
updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
|
|
798
840
|
);
|
|
799
841
|
|
|
800
842
|
CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
|
|
@@ -807,7 +849,6 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
807
849
|
name TEXT NOT NULL,
|
|
808
850
|
type TEXT,
|
|
809
851
|
data TEXT NOT NULL,
|
|
810
|
-
persist INTEGER NOT NULL DEFAULT 1,
|
|
811
852
|
createdAt INTEGER NOT NULL,
|
|
812
853
|
FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
|
|
813
854
|
FOREIGN KEY (parentId) REFERENCES messages(id)
|
|
@@ -846,13 +887,13 @@ CREATE TABLE IF NOT EXISTS checkpoints (
|
|
|
846
887
|
CREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);
|
|
847
888
|
|
|
848
889
|
-- FTS5 virtual table for full-text search
|
|
849
|
-
--
|
|
890
|
+
-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
|
|
891
|
+
-- Only 'content' is indexed for full-text search
|
|
850
892
|
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
|
|
851
|
-
messageId,
|
|
852
|
-
chatId,
|
|
853
|
-
name,
|
|
893
|
+
messageId UNINDEXED,
|
|
894
|
+
chatId UNINDEXED,
|
|
895
|
+
name UNINDEXED,
|
|
854
896
|
content,
|
|
855
|
-
content='',
|
|
856
897
|
tokenize='porter unicode61'
|
|
857
898
|
);
|
|
858
899
|
`;
|
|
@@ -869,15 +910,32 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
869
910
|
// ==========================================================================
|
|
870
911
|
async createChat(chat) {
|
|
871
912
|
this.#db.prepare(
|
|
872
|
-
`INSERT INTO chats (id, title, metadata
|
|
873
|
-
VALUES (?, ?,
|
|
913
|
+
`INSERT INTO chats (id, title, metadata)
|
|
914
|
+
VALUES (?, ?, ?)`
|
|
874
915
|
).run(
|
|
875
916
|
chat.id,
|
|
876
917
|
chat.title ?? null,
|
|
877
|
-
chat.metadata ? JSON.stringify(chat.metadata) : null
|
|
878
|
-
|
|
879
|
-
|
|
918
|
+
chat.metadata ? JSON.stringify(chat.metadata) : null
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
async upsertChat(chat) {
|
|
922
|
+
const row = this.#db.prepare(
|
|
923
|
+
`INSERT INTO chats (id, title, metadata)
|
|
924
|
+
VALUES (?, ?, ?)
|
|
925
|
+
ON CONFLICT(id) DO UPDATE SET id = excluded.id
|
|
926
|
+
RETURNING *`
|
|
927
|
+
).get(
|
|
928
|
+
chat.id,
|
|
929
|
+
chat.title ?? null,
|
|
930
|
+
chat.metadata ? JSON.stringify(chat.metadata) : null
|
|
880
931
|
);
|
|
932
|
+
return {
|
|
933
|
+
id: row.id,
|
|
934
|
+
title: row.title ?? void 0,
|
|
935
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
936
|
+
createdAt: row.createdAt,
|
|
937
|
+
updatedAt: row.updatedAt
|
|
938
|
+
};
|
|
881
939
|
}
|
|
882
940
|
async getChat(chatId) {
|
|
883
941
|
const row = this.#db.prepare("SELECT * FROM chats WHERE id = ?").get(chatId);
|
|
@@ -893,7 +951,7 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
893
951
|
};
|
|
894
952
|
}
|
|
895
953
|
async updateChat(chatId, updates) {
|
|
896
|
-
const setClauses = [];
|
|
954
|
+
const setClauses = ["updatedAt = strftime('%s', 'now') * 1000"];
|
|
897
955
|
const params = [];
|
|
898
956
|
if (updates.title !== void 0) {
|
|
899
957
|
setClauses.push("title = ?");
|
|
@@ -903,15 +961,17 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
903
961
|
setClauses.push("metadata = ?");
|
|
904
962
|
params.push(JSON.stringify(updates.metadata));
|
|
905
963
|
}
|
|
906
|
-
if (updates.updatedAt !== void 0) {
|
|
907
|
-
setClauses.push("updatedAt = ?");
|
|
908
|
-
params.push(updates.updatedAt);
|
|
909
|
-
}
|
|
910
|
-
if (setClauses.length === 0) {
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
964
|
params.push(chatId);
|
|
914
|
-
this.#db.prepare(
|
|
965
|
+
const row = this.#db.prepare(
|
|
966
|
+
`UPDATE chats SET ${setClauses.join(", ")} WHERE id = ? RETURNING *`
|
|
967
|
+
).get(...params);
|
|
968
|
+
return {
|
|
969
|
+
id: row.id,
|
|
970
|
+
title: row.title ?? void 0,
|
|
971
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
972
|
+
createdAt: row.createdAt,
|
|
973
|
+
updatedAt: row.updatedAt
|
|
974
|
+
};
|
|
915
975
|
}
|
|
916
976
|
async listChats() {
|
|
917
977
|
const rows = this.#db.prepare(
|
|
@@ -940,25 +1000,30 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
940
1000
|
// ==========================================================================
|
|
941
1001
|
// Message Operations (Graph Nodes)
|
|
942
1002
|
// ==========================================================================
|
|
943
|
-
async addMessage(
|
|
1003
|
+
async addMessage(message2) {
|
|
944
1004
|
this.#db.prepare(
|
|
945
|
-
`INSERT INTO messages (id, chatId, parentId, name, type, data,
|
|
946
|
-
VALUES (?, ?, ?, ?, ?, ?,
|
|
1005
|
+
`INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
|
|
1006
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1007
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1008
|
+
parentId = excluded.parentId,
|
|
1009
|
+
name = excluded.name,
|
|
1010
|
+
type = excluded.type,
|
|
1011
|
+
data = excluded.data`
|
|
947
1012
|
).run(
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
JSON.stringify(
|
|
954
|
-
|
|
955
|
-
message.createdAt
|
|
1013
|
+
message2.id,
|
|
1014
|
+
message2.chatId,
|
|
1015
|
+
message2.parentId,
|
|
1016
|
+
message2.name,
|
|
1017
|
+
message2.type ?? null,
|
|
1018
|
+
JSON.stringify(message2.data),
|
|
1019
|
+
message2.createdAt
|
|
956
1020
|
);
|
|
957
|
-
const content = typeof
|
|
1021
|
+
const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
|
|
1022
|
+
this.#db.prepare(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
|
|
958
1023
|
this.#db.prepare(
|
|
959
1024
|
`INSERT INTO messages_fts(messageId, chatId, name, content)
|
|
960
1025
|
VALUES (?, ?, ?, ?)`
|
|
961
|
-
).run(
|
|
1026
|
+
).run(message2.id, message2.chatId, message2.name, content);
|
|
962
1027
|
}
|
|
963
1028
|
async getMessage(messageId) {
|
|
964
1029
|
const row = this.#db.prepare("SELECT * FROM messages WHERE id = ?").get(messageId);
|
|
@@ -972,7 +1037,6 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
972
1037
|
name: row.name,
|
|
973
1038
|
type: row.type ?? void 0,
|
|
974
1039
|
data: JSON.parse(row.data),
|
|
975
|
-
persist: row.persist === 1,
|
|
976
1040
|
createdAt: row.createdAt
|
|
977
1041
|
};
|
|
978
1042
|
}
|
|
@@ -994,7 +1058,6 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
994
1058
|
name: row.name,
|
|
995
1059
|
type: row.type ?? void 0,
|
|
996
1060
|
data: JSON.parse(row.data),
|
|
997
|
-
persist: row.persist === 1,
|
|
998
1061
|
createdAt: row.createdAt
|
|
999
1062
|
}));
|
|
1000
1063
|
}
|
|
@@ -1155,7 +1218,6 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1155
1218
|
m.name,
|
|
1156
1219
|
m.type,
|
|
1157
1220
|
m.data,
|
|
1158
|
-
m.persist,
|
|
1159
1221
|
m.createdAt,
|
|
1160
1222
|
fts.rank,
|
|
1161
1223
|
snippet(messages_fts, 3, '<mark>', '</mark>', '...', 32) as snippet
|
|
@@ -1181,7 +1243,6 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1181
1243
|
name: row.name,
|
|
1182
1244
|
type: row.type ?? void 0,
|
|
1183
1245
|
data: JSON.parse(row.data),
|
|
1184
|
-
persist: row.persist === 1,
|
|
1185
1246
|
createdAt: row.createdAt
|
|
1186
1247
|
},
|
|
1187
1248
|
rank: row.rank,
|
|
@@ -1305,7 +1366,7 @@ function visualizeGraph(data) {
|
|
|
1305
1366
|
// packages/context/src/index.ts
|
|
1306
1367
|
var ContextEngine = class {
|
|
1307
1368
|
/** Non-message fragments (role, hints, etc.) - not persisted in graph */
|
|
1308
|
-
#
|
|
1369
|
+
#fragments = [];
|
|
1309
1370
|
/** Pending message fragments to be added to graph */
|
|
1310
1371
|
#pendingMessages = [];
|
|
1311
1372
|
#store;
|
|
@@ -1329,18 +1390,7 @@ var ContextEngine = class {
|
|
|
1329
1390
|
if (this.#initialized) {
|
|
1330
1391
|
return;
|
|
1331
1392
|
}
|
|
1332
|
-
|
|
1333
|
-
if (existingChat) {
|
|
1334
|
-
this.#chatData = existingChat;
|
|
1335
|
-
} else {
|
|
1336
|
-
const now = Date.now();
|
|
1337
|
-
this.#chatData = {
|
|
1338
|
-
id: this.#chatId,
|
|
1339
|
-
createdAt: now,
|
|
1340
|
-
updatedAt: now
|
|
1341
|
-
};
|
|
1342
|
-
await this.#store.createChat(this.#chatData);
|
|
1343
|
-
}
|
|
1393
|
+
this.#chatData = await this.#store.upsertChat({ id: this.#chatId });
|
|
1344
1394
|
const existingBranch = await this.#store.getBranch(
|
|
1345
1395
|
this.#chatId,
|
|
1346
1396
|
this.#branchName
|
|
@@ -1399,7 +1449,7 @@ var ContextEngine = class {
|
|
|
1399
1449
|
if (isMessageFragment(fragment2)) {
|
|
1400
1450
|
this.#pendingMessages.push(fragment2);
|
|
1401
1451
|
} else {
|
|
1402
|
-
this.#
|
|
1452
|
+
this.#fragments.push(fragment2);
|
|
1403
1453
|
}
|
|
1404
1454
|
}
|
|
1405
1455
|
return this;
|
|
@@ -1409,7 +1459,7 @@ var ContextEngine = class {
|
|
|
1409
1459
|
* @internal Use resolve() instead for public API.
|
|
1410
1460
|
*/
|
|
1411
1461
|
render(renderer) {
|
|
1412
|
-
return renderer.render(this.#
|
|
1462
|
+
return renderer.render(this.#fragments);
|
|
1413
1463
|
}
|
|
1414
1464
|
/**
|
|
1415
1465
|
* Resolve context into AI SDK-ready format.
|
|
@@ -1428,26 +1478,21 @@ var ContextEngine = class {
|
|
|
1428
1478
|
* await generateText({ system: systemPrompt, messages });
|
|
1429
1479
|
* ```
|
|
1430
1480
|
*/
|
|
1431
|
-
async resolve(options
|
|
1481
|
+
async resolve(options) {
|
|
1432
1482
|
await this.#ensureInitialized();
|
|
1433
|
-
const
|
|
1434
|
-
const
|
|
1435
|
-
const persistedMessages = [];
|
|
1483
|
+
const systemPrompt = options.renderer.render(this.#fragments);
|
|
1484
|
+
const messages = [];
|
|
1436
1485
|
if (this.#branch?.headMessageId) {
|
|
1437
1486
|
const chain = await this.#store.getMessageChain(
|
|
1438
1487
|
this.#branch.headMessageId
|
|
1439
1488
|
);
|
|
1440
|
-
|
|
1489
|
+
for (const msg of chain) {
|
|
1490
|
+
messages.push(message(msg.data).codec?.decode());
|
|
1491
|
+
}
|
|
1441
1492
|
}
|
|
1442
|
-
const messages = persistedMessages.map((msg) => ({
|
|
1443
|
-
role: msg.name,
|
|
1444
|
-
content: String(msg.data)
|
|
1445
|
-
}));
|
|
1446
1493
|
for (const fragment2 of this.#pendingMessages) {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
content: String(fragment2.data)
|
|
1450
|
-
});
|
|
1494
|
+
const decoded = fragment2.codec.decode();
|
|
1495
|
+
messages.push(decoded);
|
|
1451
1496
|
}
|
|
1452
1497
|
return { systemPrompt, messages };
|
|
1453
1498
|
}
|
|
@@ -1479,8 +1524,7 @@ var ContextEngine = class {
|
|
|
1479
1524
|
parentId,
|
|
1480
1525
|
name: fragment2.name,
|
|
1481
1526
|
type: fragment2.type,
|
|
1482
|
-
data: fragment2.
|
|
1483
|
-
persist: fragment2.persist ?? true,
|
|
1527
|
+
data: fragment2.codec.encode(),
|
|
1484
1528
|
createdAt: now
|
|
1485
1529
|
};
|
|
1486
1530
|
await this.#store.addMessage(messageData);
|
|
@@ -1488,25 +1532,85 @@ var ContextEngine = class {
|
|
|
1488
1532
|
}
|
|
1489
1533
|
await this.#store.updateBranchHead(this.#branch.id, parentId);
|
|
1490
1534
|
this.#branch.headMessageId = parentId;
|
|
1491
|
-
await this.#store.updateChat(this.#chatId, { updatedAt: now });
|
|
1492
|
-
if (this.#chatData) {
|
|
1493
|
-
this.#chatData.updatedAt = now;
|
|
1494
|
-
}
|
|
1495
1535
|
this.#pendingMessages = [];
|
|
1496
1536
|
}
|
|
1497
1537
|
/**
|
|
1498
|
-
* Estimate token count and cost for the
|
|
1538
|
+
* Estimate token count and cost for the full context.
|
|
1539
|
+
*
|
|
1540
|
+
* Includes:
|
|
1541
|
+
* - System prompt fragments (role, hints, etc.)
|
|
1542
|
+
* - Persisted chat messages (from store)
|
|
1543
|
+
* - Pending messages (not yet saved)
|
|
1499
1544
|
*
|
|
1500
1545
|
* @param modelId - Model ID (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet")
|
|
1501
1546
|
* @param options - Optional settings
|
|
1502
|
-
* @returns Estimate result with token counts and
|
|
1547
|
+
* @returns Estimate result with token counts, costs, and per-fragment breakdown
|
|
1503
1548
|
*/
|
|
1504
1549
|
async estimate(modelId, options = {}) {
|
|
1550
|
+
await this.#ensureInitialized();
|
|
1505
1551
|
const renderer = options.renderer ?? new XmlRenderer();
|
|
1506
|
-
const renderedContext = this.render(renderer);
|
|
1507
1552
|
const registry = getModelsRegistry();
|
|
1508
1553
|
await registry.load();
|
|
1509
|
-
|
|
1554
|
+
const model = registry.get(modelId);
|
|
1555
|
+
if (!model) {
|
|
1556
|
+
throw new Error(
|
|
1557
|
+
`Model "${modelId}" not found. Call load() first or check model ID.`
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
const tokenizer = registry.getTokenizer(modelId);
|
|
1561
|
+
const fragmentEstimates = [];
|
|
1562
|
+
for (const fragment2 of this.#fragments) {
|
|
1563
|
+
const rendered = renderer.render([fragment2]);
|
|
1564
|
+
const tokens = tokenizer.count(rendered);
|
|
1565
|
+
const cost = tokens / 1e6 * model.cost.input;
|
|
1566
|
+
fragmentEstimates.push({
|
|
1567
|
+
id: fragment2.id,
|
|
1568
|
+
name: fragment2.name,
|
|
1569
|
+
tokens,
|
|
1570
|
+
cost
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
if (this.#branch?.headMessageId) {
|
|
1574
|
+
const chain = await this.#store.getMessageChain(
|
|
1575
|
+
this.#branch.headMessageId
|
|
1576
|
+
);
|
|
1577
|
+
for (const msg of chain) {
|
|
1578
|
+
const content = String(msg.data);
|
|
1579
|
+
const tokens = tokenizer.count(content);
|
|
1580
|
+
const cost = tokens / 1e6 * model.cost.input;
|
|
1581
|
+
fragmentEstimates.push({
|
|
1582
|
+
name: msg.name,
|
|
1583
|
+
id: msg.id,
|
|
1584
|
+
tokens,
|
|
1585
|
+
cost
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
for (const fragment2 of this.#pendingMessages) {
|
|
1590
|
+
const content = String(fragment2.data);
|
|
1591
|
+
const tokens = tokenizer.count(content);
|
|
1592
|
+
const cost = tokens / 1e6 * model.cost.input;
|
|
1593
|
+
fragmentEstimates.push({
|
|
1594
|
+
name: fragment2.name,
|
|
1595
|
+
id: fragment2.id,
|
|
1596
|
+
tokens,
|
|
1597
|
+
cost
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
const totalTokens = fragmentEstimates.reduce((sum, f) => sum + f.tokens, 0);
|
|
1601
|
+
const totalCost = fragmentEstimates.reduce((sum, f) => sum + f.cost, 0);
|
|
1602
|
+
return {
|
|
1603
|
+
model: model.id,
|
|
1604
|
+
provider: model.provider,
|
|
1605
|
+
tokens: totalTokens,
|
|
1606
|
+
cost: totalCost,
|
|
1607
|
+
limits: {
|
|
1608
|
+
context: model.limit.context,
|
|
1609
|
+
output: model.limit.output,
|
|
1610
|
+
exceedsContext: totalTokens > model.limit.context
|
|
1611
|
+
},
|
|
1612
|
+
fragments: fragmentEstimates
|
|
1613
|
+
};
|
|
1510
1614
|
}
|
|
1511
1615
|
/**
|
|
1512
1616
|
* Rewind to a specific message by ID.
|
|
@@ -1533,11 +1637,11 @@ var ContextEngine = class {
|
|
|
1533
1637
|
*/
|
|
1534
1638
|
async rewind(messageId) {
|
|
1535
1639
|
await this.#ensureInitialized();
|
|
1536
|
-
const
|
|
1537
|
-
if (!
|
|
1640
|
+
const message2 = await this.#store.getMessage(messageId);
|
|
1641
|
+
if (!message2) {
|
|
1538
1642
|
throw new Error(`Message "${messageId}" not found`);
|
|
1539
1643
|
}
|
|
1540
|
-
if (
|
|
1644
|
+
if (message2.chatId !== this.#chatId) {
|
|
1541
1645
|
throw new Error(`Message "${messageId}" belongs to a different chat`);
|
|
1542
1646
|
}
|
|
1543
1647
|
const branches = await this.#store.listBranches(this.#chatId);
|
|
@@ -1671,10 +1775,7 @@ var ContextEngine = class {
|
|
|
1671
1775
|
*/
|
|
1672
1776
|
async updateChat(updates) {
|
|
1673
1777
|
await this.#ensureInitialized();
|
|
1674
|
-
const
|
|
1675
|
-
const storeUpdates = {
|
|
1676
|
-
updatedAt: now
|
|
1677
|
-
};
|
|
1778
|
+
const storeUpdates = {};
|
|
1678
1779
|
if (updates.title !== void 0) {
|
|
1679
1780
|
storeUpdates.title = updates.title;
|
|
1680
1781
|
}
|
|
@@ -1684,16 +1785,7 @@ var ContextEngine = class {
|
|
|
1684
1785
|
...updates.metadata
|
|
1685
1786
|
};
|
|
1686
1787
|
}
|
|
1687
|
-
await this.#store.updateChat(this.#chatId, storeUpdates);
|
|
1688
|
-
if (this.#chatData) {
|
|
1689
|
-
if (storeUpdates.title !== void 0) {
|
|
1690
|
-
this.#chatData.title = storeUpdates.title;
|
|
1691
|
-
}
|
|
1692
|
-
if (storeUpdates.metadata !== void 0) {
|
|
1693
|
-
this.#chatData.metadata = storeUpdates.metadata;
|
|
1694
|
-
}
|
|
1695
|
-
this.#chatData.updatedAt = now;
|
|
1696
|
-
}
|
|
1788
|
+
this.#chatData = await this.#store.updateChat(this.#chatId, storeUpdates);
|
|
1697
1789
|
}
|
|
1698
1790
|
/**
|
|
1699
1791
|
* Consolidate context fragments (no-op for now).
|
|
@@ -1706,6 +1798,54 @@ var ContextEngine = class {
|
|
|
1706
1798
|
consolidate() {
|
|
1707
1799
|
return void 0;
|
|
1708
1800
|
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Inspect the full context state for debugging.
|
|
1803
|
+
* Returns a comprehensive JSON-serializable object with all context information.
|
|
1804
|
+
*
|
|
1805
|
+
* @param options - Inspection options (modelId and renderer required)
|
|
1806
|
+
* @returns Complete inspection data including estimates, rendered output, fragments, and graph
|
|
1807
|
+
*
|
|
1808
|
+
* @example
|
|
1809
|
+
* ```ts
|
|
1810
|
+
* const inspection = await context.inspect({
|
|
1811
|
+
* modelId: 'openai:gpt-4o',
|
|
1812
|
+
* renderer: new XmlRenderer(),
|
|
1813
|
+
* });
|
|
1814
|
+
* console.log(JSON.stringify(inspection, null, 2));
|
|
1815
|
+
*
|
|
1816
|
+
* // Or write to file for analysis
|
|
1817
|
+
* await fs.writeFile('context-debug.json', JSON.stringify(inspection, null, 2));
|
|
1818
|
+
* ```
|
|
1819
|
+
*/
|
|
1820
|
+
async inspect(options) {
|
|
1821
|
+
await this.#ensureInitialized();
|
|
1822
|
+
const { renderer } = options;
|
|
1823
|
+
const estimateResult = await this.estimate(options.modelId, { renderer });
|
|
1824
|
+
const rendered = renderer.render(this.#fragments);
|
|
1825
|
+
const persistedMessages = [];
|
|
1826
|
+
if (this.#branch?.headMessageId) {
|
|
1827
|
+
const chain = await this.#store.getMessageChain(
|
|
1828
|
+
this.#branch.headMessageId
|
|
1829
|
+
);
|
|
1830
|
+
persistedMessages.push(...chain);
|
|
1831
|
+
}
|
|
1832
|
+
const graph = await this.#store.getGraph(this.#chatId);
|
|
1833
|
+
return {
|
|
1834
|
+
estimate: estimateResult,
|
|
1835
|
+
rendered,
|
|
1836
|
+
fragments: {
|
|
1837
|
+
context: [...this.#fragments],
|
|
1838
|
+
pending: [...this.#pendingMessages],
|
|
1839
|
+
persisted: persistedMessages
|
|
1840
|
+
},
|
|
1841
|
+
graph,
|
|
1842
|
+
meta: {
|
|
1843
|
+
chatId: this.#chatId,
|
|
1844
|
+
branch: this.#branchName,
|
|
1845
|
+
timestamp: Date.now()
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1709
1849
|
};
|
|
1710
1850
|
function hint(text) {
|
|
1711
1851
|
return {
|
|
@@ -1725,24 +1865,75 @@ function role(content) {
|
|
|
1725
1865
|
data: content
|
|
1726
1866
|
};
|
|
1727
1867
|
}
|
|
1728
|
-
function user(content
|
|
1868
|
+
function user(content) {
|
|
1869
|
+
const message2 = typeof content === "string" ? {
|
|
1870
|
+
id: generateId(),
|
|
1871
|
+
role: "user",
|
|
1872
|
+
parts: [{ type: "text", text: content }]
|
|
1873
|
+
} : content;
|
|
1729
1874
|
return {
|
|
1730
|
-
id:
|
|
1875
|
+
id: message2.id,
|
|
1731
1876
|
name: "user",
|
|
1732
|
-
data: content,
|
|
1877
|
+
data: "content",
|
|
1733
1878
|
type: "message",
|
|
1734
|
-
persist: true
|
|
1879
|
+
persist: true,
|
|
1880
|
+
codec: {
|
|
1881
|
+
decode() {
|
|
1882
|
+
return message2;
|
|
1883
|
+
},
|
|
1884
|
+
encode() {
|
|
1885
|
+
return message2;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1735
1888
|
};
|
|
1736
1889
|
}
|
|
1737
|
-
function assistant(
|
|
1890
|
+
function assistant(message2) {
|
|
1738
1891
|
return {
|
|
1739
|
-
id:
|
|
1892
|
+
id: message2.id,
|
|
1740
1893
|
name: "assistant",
|
|
1741
|
-
data: content,
|
|
1894
|
+
data: "content",
|
|
1895
|
+
type: "message",
|
|
1896
|
+
persist: true,
|
|
1897
|
+
codec: {
|
|
1898
|
+
decode() {
|
|
1899
|
+
return message2;
|
|
1900
|
+
},
|
|
1901
|
+
encode() {
|
|
1902
|
+
return message2;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
function message(content) {
|
|
1908
|
+
const message2 = typeof content === "string" ? {
|
|
1909
|
+
id: generateId(),
|
|
1910
|
+
role: "user",
|
|
1911
|
+
parts: [{ type: "text", text: content }]
|
|
1912
|
+
} : content;
|
|
1913
|
+
return {
|
|
1914
|
+
id: message2.id,
|
|
1915
|
+
name: "message",
|
|
1916
|
+
data: "content",
|
|
1742
1917
|
type: "message",
|
|
1743
|
-
persist: true
|
|
1918
|
+
persist: true,
|
|
1919
|
+
codec: {
|
|
1920
|
+
decode() {
|
|
1921
|
+
return message2;
|
|
1922
|
+
},
|
|
1923
|
+
encode() {
|
|
1924
|
+
return message2;
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1744
1927
|
};
|
|
1745
1928
|
}
|
|
1929
|
+
function assistantText(content, options) {
|
|
1930
|
+
const id = options?.id ?? crypto.randomUUID();
|
|
1931
|
+
return assistant({
|
|
1932
|
+
id,
|
|
1933
|
+
role: "assistant",
|
|
1934
|
+
parts: [{ type: "text", text: content }]
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1746
1937
|
export {
|
|
1747
1938
|
ContextEngine,
|
|
1748
1939
|
ContextStore,
|
|
@@ -1754,11 +1945,14 @@ export {
|
|
|
1754
1945
|
ToonRenderer,
|
|
1755
1946
|
XmlRenderer,
|
|
1756
1947
|
assistant,
|
|
1948
|
+
assistantText,
|
|
1757
1949
|
defaultTokenizer,
|
|
1950
|
+
estimate,
|
|
1758
1951
|
fragment,
|
|
1759
1952
|
getModelsRegistry,
|
|
1760
1953
|
hint,
|
|
1761
1954
|
isMessageFragment,
|
|
1955
|
+
message,
|
|
1762
1956
|
role,
|
|
1763
1957
|
user,
|
|
1764
1958
|
visualizeGraph
|