@littlebigbrain/mcp 0.10.0 → 0.12.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/README.md +33 -18
- package/dist/files-tool.d.ts +43 -0
- package/dist/files-tool.js +183 -0
- package/dist/query-tools.d.ts +89 -0
- package/dist/query-tools.js +208 -0
- package/dist/question.d.ts +9 -7
- package/dist/question.js +112 -61
- package/dist/tool-contracts.js +164 -13
- package/dist/tools.js +472 -10
- package/package.json +2 -2
package/dist/question.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { lexicalRow, listLimit } from "./query-tools.js";
|
|
1
2
|
import { QUESTION_MAX_ROWS } from "./tool-contracts.js";
|
|
2
3
|
import { defaultRowLimit, normalizeDetail, queryEnvelope, queryToolResult, rowPageFrom, rowPageNext, scoped, } from "./tool-runtime.js";
|
|
3
|
-
const REPAIR_ERROR_CHARS = 200;
|
|
4
4
|
/** The progress message of a stream event, in short plain words. */
|
|
5
5
|
export function progressMessage(event) {
|
|
6
6
|
const data = (event.data ?? {});
|
|
@@ -13,41 +13,35 @@ export function progressMessage(event) {
|
|
|
13
13
|
: "";
|
|
14
14
|
return `Route: ${String(data.kind)} (${String(data.by)}${confidence})`;
|
|
15
15
|
}
|
|
16
|
-
case "
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
?
|
|
21
|
-
:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
case "rows": {
|
|
25
|
-
const count = Number(data.count);
|
|
26
|
-
return `${count} ${count === 1 ? "row" : "rows"} in ${String(data.ms)} ms`;
|
|
27
|
-
}
|
|
28
|
-
case "repair": {
|
|
29
|
-
const error = String(data.error ?? "");
|
|
30
|
-
const shown = error.length > REPAIR_ERROR_CHARS
|
|
31
|
-
? `${error.slice(0, REPAIR_ERROR_CHARS - 1)}…`
|
|
32
|
-
: error;
|
|
33
|
-
return `Correcting the query: ${shown}`;
|
|
16
|
+
case "step": {
|
|
17
|
+
// A compare step counts the entries that differ, not rows.
|
|
18
|
+
const unit = data.tool === "compare" ? "difference" : "row";
|
|
19
|
+
const rows = typeof data.rows === "number"
|
|
20
|
+
? `: ${data.rows} ${unit}${data.rows === 1 ? "" : "s"}`
|
|
21
|
+
: "";
|
|
22
|
+
const outcome = data.ok === false ? ": failed" : rows;
|
|
23
|
+
return `Step ${String(data.n)}, ${String(data.tool)} ${String(data.input ?? "")}${outcome}`;
|
|
34
24
|
}
|
|
25
|
+
case "answer":
|
|
26
|
+
return "Answered";
|
|
35
27
|
default:
|
|
36
28
|
return undefined;
|
|
37
29
|
}
|
|
38
30
|
}
|
|
39
31
|
/**
|
|
40
|
-
* The
|
|
32
|
+
* The question as a stream of progress events. Each event becomes one
|
|
41
33
|
* progress notification; the `done` event is the same response as the call
|
|
42
|
-
* without a stream. A client release without `query.
|
|
34
|
+
* without a stream. A client release without `query.askStream` answers
|
|
43
35
|
* without progress.
|
|
44
36
|
*/
|
|
45
|
-
async function
|
|
37
|
+
async function streamedAnswer(target, body, progress) {
|
|
46
38
|
const query = target.query;
|
|
47
|
-
if (typeof query.
|
|
39
|
+
if (typeof query.askStream !== "function")
|
|
48
40
|
return undefined;
|
|
41
|
+
const { question, ...options } = body;
|
|
49
42
|
let step = 0;
|
|
50
|
-
for await (const event of query.
|
|
43
|
+
for await (const event of query.askStream.call(target.query, question, {
|
|
44
|
+
...options,
|
|
51
45
|
signal: progress.signal,
|
|
52
46
|
})) {
|
|
53
47
|
if (event.event === "done")
|
|
@@ -59,30 +53,50 @@ async function streamedRewrite(target, body, progress) {
|
|
|
59
53
|
// A lost notification never fails the question.
|
|
60
54
|
await progress.report(step, message).catch(() => undefined);
|
|
61
55
|
}
|
|
62
|
-
throw new Error("the
|
|
56
|
+
throw new Error("the answer stream ended before its done event");
|
|
63
57
|
}
|
|
64
|
-
/**
|
|
65
|
-
function
|
|
66
|
-
return
|
|
58
|
+
/** The totals of a comparison: the server's, else the lengths of its lists. */
|
|
59
|
+
function historyTotals(history) {
|
|
60
|
+
return (history.totals ?? {
|
|
61
|
+
added: history.added?.length ?? 0,
|
|
62
|
+
removed: history.removed?.length ?? 0,
|
|
63
|
+
changed: history.changed?.length ?? 0,
|
|
64
|
+
});
|
|
67
65
|
}
|
|
68
66
|
/**
|
|
69
67
|
* The history the tool returns: where the question read the graph, and for
|
|
70
|
-
* a comparison the rows that differ as lexical values
|
|
71
|
-
*
|
|
72
|
-
*
|
|
68
|
+
* a comparison the rows that differ as lexical values, with the total of
|
|
69
|
+
* each list. Each list shows at most `limit` entries; `shown` says how many
|
|
70
|
+
* when a list is cut.
|
|
73
71
|
*/
|
|
74
|
-
function historyView(history) {
|
|
72
|
+
function historyView(history, limit) {
|
|
75
73
|
const view = { ...history };
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
if (!history.added && !history.removed && !history.changed)
|
|
75
|
+
return view;
|
|
76
|
+
const totals = historyTotals(history);
|
|
77
|
+
view.totals = totals;
|
|
78
|
+
const shown = {};
|
|
79
|
+
const show = (name, list, map) => {
|
|
80
|
+
if (!list)
|
|
81
|
+
return;
|
|
82
|
+
const kept = list.slice(0, limit);
|
|
83
|
+
view[name] = kept.map(map);
|
|
84
|
+
if (kept.length < (totals[name] ?? list.length))
|
|
85
|
+
shown[name] = kept.length;
|
|
86
|
+
};
|
|
87
|
+
show("added", history.added, lexicalRow);
|
|
88
|
+
show("removed", history.removed, lexicalRow);
|
|
89
|
+
show("changed", history.changed, (change) => ({
|
|
90
|
+
key: lexicalRow(change.key),
|
|
91
|
+
before: change.before.map(lexicalRow),
|
|
92
|
+
after: change.after.map(lexicalRow),
|
|
93
|
+
}));
|
|
94
|
+
if (Object.keys(shown).length)
|
|
95
|
+
view.shown = shown;
|
|
82
96
|
return view;
|
|
83
97
|
}
|
|
84
98
|
/** What the agent should know about the history of the answer. */
|
|
85
|
-
function historyNotes(history) {
|
|
99
|
+
function historyNotes(history, limit, query) {
|
|
86
100
|
if (!history)
|
|
87
101
|
return [];
|
|
88
102
|
const notes = [];
|
|
@@ -100,36 +114,55 @@ function historyNotes(history) {
|
|
|
100
114
|
else if (history.as_of_date) {
|
|
101
115
|
notes.push(`The server could not place ${history.as_of_date} (see error). Pass timeline=[{date, as_of_commit_seq}] to name the commit of each date, or run the query with lbb_query mode=sparql as_of_commit_seq=<commit>.`);
|
|
102
116
|
}
|
|
103
|
-
if (history.compare && history.added && point) {
|
|
104
|
-
|
|
117
|
+
if (history.compare && (history.added || history.changed) && point) {
|
|
118
|
+
const totals = historyTotals(history);
|
|
119
|
+
// The compared query reads the later point.
|
|
120
|
+
const later = typeof query?.as_of_commit_seq === "number"
|
|
121
|
+
? query.as_of_commit_seq
|
|
122
|
+
: undefined;
|
|
123
|
+
const keyed = history.key?.length
|
|
124
|
+
? `paired by ${history.key.map((v) => `?${v}`).join(", ")}: history.added and history.removed hold the entities at one point only, history.changed the entities whose values differ`
|
|
125
|
+
: "history.added and history.removed hold the rows that differ (a changed value is a removed row and an added row)";
|
|
126
|
+
notes.push(`A comparison from ${point} to ${later === undefined ? "the latest commit" : `commit ${later}`}; ${keyed}. Totals: added ${totals.added}, removed ${totals.removed}, changed ${totals.changed}.`);
|
|
127
|
+
const cut = ["added", "removed", "changed"].filter((name) => totals[name] > limit);
|
|
128
|
+
if (cut.length && query) {
|
|
129
|
+
const after = later === undefined ? "" : ` after={"as_of_commit_seq": ${later}}`;
|
|
130
|
+
const key = history.key?.length
|
|
131
|
+
? ` key=${JSON.stringify(history.key)}`
|
|
132
|
+
: "";
|
|
133
|
+
notes.push(`Each list shows at most ${limit} entries (${cut.map((name) => `${name}: ${totals[name]}`).join(", ")}; see history.shown). Read them all, page by page, with lbb_query mode=compare query=<the query> before={"as_of_commit_seq": ${history.as_of_commit_seq}}${after}${key}.`);
|
|
134
|
+
}
|
|
135
|
+
if (history.truncated) {
|
|
136
|
+
notes.push("The difference is not complete: a point was not read whole, or a list held more than 500 entries on the server. Use lbb_query mode=compare for the whole difference.");
|
|
137
|
+
}
|
|
105
138
|
}
|
|
106
139
|
return notes;
|
|
107
140
|
}
|
|
108
141
|
/**
|
|
109
|
-
* `lbb_query mode=question`: the server
|
|
110
|
-
* query
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
142
|
+
* `lbb_query mode=question`: the server answers the question in plain words
|
|
143
|
+
* (`POST /v1/query/ask`). Its model runs queries in a bounded loop and
|
|
144
|
+
* reads their rows. The result holds the answer, its citations and chart
|
|
145
|
+
* hint, the steps, and the rows of the query the answer stands on, bounded
|
|
146
|
+
* as `mode=sparql`
|
|
147
|
+
* bounds them; `next` continues that query with `mode=sparql`. With
|
|
148
|
+
* `progress`, the server streams its steps and each step is reported; the
|
|
149
|
+
* result is the same.
|
|
114
150
|
*/
|
|
115
151
|
export async function answerQuestion(client, args, textFormat, progress) {
|
|
116
152
|
const detail = normalizeDetail(args.detail);
|
|
117
|
-
const run = args.run ?? true;
|
|
118
153
|
const rowLimit = Math.min(args.limit ?? defaultRowLimit(detail), QUESTION_MAX_ROWS);
|
|
119
154
|
const target = scoped(client, args.graph);
|
|
120
155
|
const body = {
|
|
121
156
|
question: args.question,
|
|
122
157
|
...(args.context !== undefined ? { context: args.context } : {}),
|
|
123
158
|
...(args.route !== undefined ? { route: args.route } : {}),
|
|
124
|
-
|
|
125
|
-
...(run ? { limit: rowLimit } : {}),
|
|
159
|
+
limit: rowLimit,
|
|
126
160
|
...(args.anchor?.length ? { anchor: args.anchor } : {}),
|
|
127
161
|
...(args.timeline?.length ? { timeline: args.timeline } : {}),
|
|
128
162
|
};
|
|
129
|
-
// Each call uses model tokens,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
(await target.request("POST", "/v1/query/rewrite", {
|
|
163
|
+
// Each call uses model tokens, so a failed call is not retried.
|
|
164
|
+
const response = (progress ? await streamedAnswer(target, body, progress) : undefined) ??
|
|
165
|
+
(await target.request("POST", "/v1/query/ask", {
|
|
133
166
|
body,
|
|
134
167
|
retry: false,
|
|
135
168
|
query: { consistency: target.defaultConsistency },
|
|
@@ -137,6 +170,20 @@ export async function answerQuestion(client, args, textFormat, progress) {
|
|
|
137
170
|
const { route, query, result } = response;
|
|
138
171
|
const traceId = result?.trace_id ?? null;
|
|
139
172
|
const answer = {
|
|
173
|
+
answer: response.answer
|
|
174
|
+
? {
|
|
175
|
+
text: response.answer.text,
|
|
176
|
+
citations: response.answer.citations ?? [],
|
|
177
|
+
...(response.answer.chart ? { chart: response.answer.chart } : {}),
|
|
178
|
+
}
|
|
179
|
+
: null,
|
|
180
|
+
steps: (response.steps ?? []).map((step) => ({
|
|
181
|
+
n: step.n,
|
|
182
|
+
tool: step.tool,
|
|
183
|
+
ok: step.ok,
|
|
184
|
+
...(typeof step.rows === "number" ? { rows: step.rows } : {}),
|
|
185
|
+
...(step.error ? { error: step.error } : {}),
|
|
186
|
+
})),
|
|
140
187
|
route: { kind: route.kind, confidence: route.confidence, by: route.by },
|
|
141
188
|
rationale: response.rationale,
|
|
142
189
|
sparql: query?.sparql ?? null,
|
|
@@ -144,7 +191,9 @@ export async function answerQuestion(client, args, textFormat, progress) {
|
|
|
144
191
|
...(typeof query?.as_of_commit_seq === "number"
|
|
145
192
|
? { as_of_commit_seq: query.as_of_commit_seq }
|
|
146
193
|
: {}),
|
|
147
|
-
...(response.history
|
|
194
|
+
...(response.history
|
|
195
|
+
? { history: historyView(response.history, listLimit(detail)) }
|
|
196
|
+
: {}),
|
|
148
197
|
error: response.error ?? null,
|
|
149
198
|
trace_id: traceId,
|
|
150
199
|
attempts: response.attempts,
|
|
@@ -160,9 +209,9 @@ export async function answerQuestion(client, args, textFormat, progress) {
|
|
|
160
209
|
if (traceId) {
|
|
161
210
|
notes.push(`eval trace ${traceId}: to label rows, read their item ids with lbb_evals action=trace trace_id=${traceId}, then call lbb_evals action=label trace_id=${traceId} item=<id> valid=true|false (or items=[…]).`);
|
|
162
211
|
}
|
|
163
|
-
notes.push(...historyNotes(response.history));
|
|
164
|
-
if (response.
|
|
165
|
-
notes.push("The
|
|
212
|
+
notes.push(...historyNotes(response.history, listLimit(detail), query));
|
|
213
|
+
if (!response.answer) {
|
|
214
|
+
notes.push("The answer loop stopped before it answered (see error); the rows are the best ones it read. Continue with lbb_query mode=sparql, or ask again with more context.");
|
|
166
215
|
}
|
|
167
216
|
const label = `lbb_query.question: route ${route.kind} (${route.by}, confidence ${route.confidence.toFixed(2)})`;
|
|
168
217
|
const data = result
|
|
@@ -186,13 +235,15 @@ export async function answerQuestion(client, args, textFormat, progress) {
|
|
|
186
235
|
};
|
|
187
236
|
return queryToolResult(queryEnvelope(label, { ...answer, ...data }, detail, rowPage, rowPageNext(cursorBase, rowPage), cursorBase, { textFormat, notes }), textFormat);
|
|
188
237
|
}
|
|
189
|
-
// No rows to page:
|
|
238
|
+
// No rows to page: an ASK answer, a comparison, or no query at all. The
|
|
190
239
|
// query text is returned whole.
|
|
191
240
|
const summary = data
|
|
192
241
|
? `${label}: ran the query`
|
|
193
|
-
:
|
|
194
|
-
? `${label}:
|
|
195
|
-
:
|
|
242
|
+
: response.history?.compare
|
|
243
|
+
? `${label}: compared two points`
|
|
244
|
+
: query
|
|
245
|
+
? `${label}: no rows`
|
|
246
|
+
: `${label}: no query`;
|
|
196
247
|
return queryToolResult({
|
|
197
248
|
summary,
|
|
198
249
|
data: { ...answer, ...(data ?? {}) },
|
package/dist/tool-contracts.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
export const DEFAULT_DETAIL = "compact";
|
|
3
3
|
export const HARD_OUTPUT_CHARS = 80_000;
|
|
4
4
|
export const MAX_QUERY_ROW_LIMIT = 5_000;
|
|
5
|
-
/** The most rows `POST /v1/query/
|
|
5
|
+
/** The most rows each query of `POST /v1/query/ask` returns. */
|
|
6
6
|
export const QUESTION_MAX_ROWS = 1_000;
|
|
7
7
|
/** Entity IRIs one question may anchor, and the characters of one. */
|
|
8
8
|
export const QUESTION_MAX_ANCHORS = 10;
|
|
@@ -331,6 +331,73 @@ export const ontologyEvolveOpSchema = z.discriminatedUnion("op", [
|
|
|
331
331
|
})
|
|
332
332
|
.strict(),
|
|
333
333
|
]);
|
|
334
|
+
const namedEntityInputSchema = z
|
|
335
|
+
.object({
|
|
336
|
+
type: z.string(),
|
|
337
|
+
name: z.string(),
|
|
338
|
+
key: z
|
|
339
|
+
.string()
|
|
340
|
+
.optional()
|
|
341
|
+
.describe("The record's external key, when it has one"),
|
|
342
|
+
})
|
|
343
|
+
.strict();
|
|
344
|
+
/** Graph facts a review item proposes; accepting commits them. */
|
|
345
|
+
export const suggestedFactsSchema = z
|
|
346
|
+
.object({
|
|
347
|
+
triplets: z
|
|
348
|
+
.array(z
|
|
349
|
+
.object({
|
|
350
|
+
source: namedEntityInputSchema,
|
|
351
|
+
relation: z.string(),
|
|
352
|
+
target: namedEntityInputSchema,
|
|
353
|
+
confidence: z.number().min(0).max(1).optional(),
|
|
354
|
+
evidence: z
|
|
355
|
+
.unknown()
|
|
356
|
+
.optional()
|
|
357
|
+
.describe("Why the link holds: a string, or { text, source_id } as lbb_commit takes it"),
|
|
358
|
+
})
|
|
359
|
+
.passthrough())
|
|
360
|
+
.max(200)
|
|
361
|
+
.optional()
|
|
362
|
+
.describe("Links to commit, as lbb_commit takes triplets"),
|
|
363
|
+
entity_properties: z
|
|
364
|
+
.array(jsonObjectSchema)
|
|
365
|
+
.max(200)
|
|
366
|
+
.optional()
|
|
367
|
+
.describe("Fields to commit, each { type, name, properties: { field: value } } as lbb_commit takes them"),
|
|
368
|
+
replace_owned: z
|
|
369
|
+
.object({
|
|
370
|
+
subjects: z
|
|
371
|
+
.array(namedEntityInputSchema)
|
|
372
|
+
.max(1000)
|
|
373
|
+
.describe("The records this write is for"),
|
|
374
|
+
relations: z
|
|
375
|
+
.array(z.string())
|
|
376
|
+
.max(64)
|
|
377
|
+
.optional()
|
|
378
|
+
.describe("Owned relations: their current out-edges of the subjects that the facts do not write again are removed"),
|
|
379
|
+
properties: z
|
|
380
|
+
.array(z.string())
|
|
381
|
+
.max(64)
|
|
382
|
+
.optional()
|
|
383
|
+
.describe("Owned fields: the ones the facts do not set on a subject are removed"),
|
|
384
|
+
retract_entities: z
|
|
385
|
+
.array(namedEntityInputSchema)
|
|
386
|
+
.max(1000)
|
|
387
|
+
.optional()
|
|
388
|
+
.describe("Records the producer made and no longer makes"),
|
|
389
|
+
})
|
|
390
|
+
.strict()
|
|
391
|
+
.optional()
|
|
392
|
+
.describe("Replace the producer's owned edges and fields on its subjects in the same commit"),
|
|
393
|
+
summary: z
|
|
394
|
+
.string()
|
|
395
|
+
.max(500)
|
|
396
|
+
.optional()
|
|
397
|
+
.describe("One line about the facts for the reviewer"),
|
|
398
|
+
})
|
|
399
|
+
.strict()
|
|
400
|
+
.describe("Graph facts a person confirms: accepting commits them after the change (at most 200 triplets and property rows together). Records graphs only");
|
|
334
401
|
export const inspectInputSchema = z.discriminatedUnion("action", [
|
|
335
402
|
z.object({ action: z.literal("guide"), ...readScope }).strict(),
|
|
336
403
|
z
|
|
@@ -405,6 +472,17 @@ export const SPARQL_IRI_GUIDE = 'IRI scheme: relations are <https://littlebigbra
|
|
|
405
472
|
// Search by meaning runs inside SPARQL text as a pattern, so an agent can
|
|
406
473
|
// filter and join the hits in one query instead of a search then a lookup.
|
|
407
474
|
export const SPARQL_SEARCH_GUIDE = 'Search by meaning inside the query (the graph needs an embedding; see lbb_embeddings action=list): PREFIX search: <https://littlebigbrain.com/search#> SELECT ?x ?score WHERE { ?x search:similarTo "card payments" ; search:score ?score . ?x <https://littlebigbrain.com/r/calls> ?y } ORDER BY DESC(?score) LIMIT 5. The other patterns filter and join the hits. One search:similarTo per query, with a variable subject, in the main group (not inside OPTIONAL, UNION, MINUS or a subquery). The object is text, an entity IRI (records like that one) or a vector literal. search:top N sets the hits (1 to 1000; default the query LIMIT, then 10) and search:embedding "name" searches one embedding. FILTER(?score > 0.8) keeps close hits. The result\'s search field reports the plan (nearest, filter_first or search_first), the hits and complete. For a question in words, add search:rerank true ; search:relevance ?r and ORDER BY DESC(?r): the rerank model (Jev) reads the best hits with the text, keeps the top that answer it and binds its answer (0 to 1); search.rerank reports the status. Only a text query reranks.';
|
|
475
|
+
/** One point of `lbb_query mode=compare`: a commit, a date or a moment. */
|
|
476
|
+
const comparePointSchema = z
|
|
477
|
+
.object({
|
|
478
|
+
as_of_commit_seq: z.number().int().min(0).optional(),
|
|
479
|
+
date: z
|
|
480
|
+
.string()
|
|
481
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
482
|
+
.optional(),
|
|
483
|
+
moment: z.string().optional(),
|
|
484
|
+
})
|
|
485
|
+
.strict();
|
|
408
486
|
export const queryInputSchema = z.discriminatedUnion("mode", [
|
|
409
487
|
z
|
|
410
488
|
.object({
|
|
@@ -447,7 +525,7 @@ export const queryInputSchema = z.discriminatedUnion("mode", [
|
|
|
447
525
|
query: z
|
|
448
526
|
.string()
|
|
449
527
|
.optional()
|
|
450
|
-
.describe(`SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. ${SPARQL_IRI_GUIDE} Example: SELECT ?service ?db WHERE { ?service <https://littlebigbrain.com/r/writes_to> ?db } LIMIT 10. ${SPARQL_SEARCH_GUIDE}
|
|
528
|
+
.describe(`SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. ${SPARQL_IRI_GUIDE} Example: SELECT ?service ?db WHERE { ?service <https://littlebigbrain.com/r/writes_to> ?db } LIMIT 10. ${SPARQL_SEARCH_GUIDE} compare: the SELECT to run at both points; leave out LIMIT.`),
|
|
451
529
|
// Kept for an actionable error when an older connector sends this field.
|
|
452
530
|
as_of: z
|
|
453
531
|
.string()
|
|
@@ -474,7 +552,7 @@ export const queryInputSchema = z.discriminatedUnion("mode", [
|
|
|
474
552
|
question: z
|
|
475
553
|
.string()
|
|
476
554
|
.min(1)
|
|
477
|
-
.describe("question: the question in plain words (required), 1 to 4,000 characters. The server
|
|
555
|
+
.describe("question: the question in plain words (required), 1 to 4,000 characters. The server's model runs queries on this graph, reads their rows, and answers. describe: the question whose classes and properties to describe."),
|
|
478
556
|
context: z
|
|
479
557
|
.string()
|
|
480
558
|
.optional()
|
|
@@ -489,23 +567,19 @@ export const queryInputSchema = z.discriminatedUnion("mode", [
|
|
|
489
567
|
"unanswerable",
|
|
490
568
|
])
|
|
491
569
|
.optional()
|
|
492
|
-
.describe("question: the kind of
|
|
570
|
+
.describe("question: the kind of question, when you know it. Omit it and the router model picks it."),
|
|
493
571
|
limit: z
|
|
494
572
|
.number()
|
|
495
573
|
.int()
|
|
496
574
|
.min(1)
|
|
497
575
|
.max(QUESTION_MAX_ROWS)
|
|
498
576
|
.optional()
|
|
499
|
-
.describe("question: rows the
|
|
500
|
-
run: z
|
|
501
|
-
.boolean()
|
|
502
|
-
.optional()
|
|
503
|
-
.describe("question: run the query and return its rows. Defaults to true; false returns the query only."),
|
|
577
|
+
.describe("question: rows each query of the server's loop returns, 1 to 1,000. Defaults by detail: compact=20, standard=100, full=1000. names: candidates per name, 1 to 10 (default 5). compare: entries of each list per page, 1 to 1,000 (defaults by detail: 20, 100, 500)."),
|
|
504
578
|
anchor: z
|
|
505
579
|
.array(z.string().min(1).max(QUESTION_MAX_ANCHOR_CHARS))
|
|
506
580
|
.max(QUESTION_MAX_ANCHORS)
|
|
507
581
|
.optional()
|
|
508
|
-
.describe("question: entity IRIs the question is about, at most 10 (for example the record the user has open). The server reads each one, and
|
|
582
|
+
.describe("question: entity IRIs the question is about, at most 10 (for example the record the user has open). The server reads each one, and its queries use these IRIs directly instead of matching their names."),
|
|
509
583
|
timeline: z
|
|
510
584
|
.array(z
|
|
511
585
|
.object({
|
|
@@ -541,7 +615,7 @@ export const queryInputSchema = z.discriminatedUnion("mode", [
|
|
|
541
615
|
text: z
|
|
542
616
|
.string()
|
|
543
617
|
.optional()
|
|
544
|
-
.describe("
|
|
618
|
+
.describe("search: the query text (required); embedded with the graph's model. names: the question, or the names, to find (required)."),
|
|
545
619
|
top_k: z.number().int().positive().max(200).optional(),
|
|
546
620
|
probe: z
|
|
547
621
|
.number()
|
|
@@ -605,6 +679,82 @@ export const queryInputSchema = z.discriminatedUnion("mode", [
|
|
|
605
679
|
...readScope,
|
|
606
680
|
})
|
|
607
681
|
.strict(),
|
|
682
|
+
z
|
|
683
|
+
.object({
|
|
684
|
+
mode: z.literal("names"),
|
|
685
|
+
text: z
|
|
686
|
+
.string()
|
|
687
|
+
.min(1)
|
|
688
|
+
.max(4_000)
|
|
689
|
+
.describe("names: the question, or the names, to find (required)."),
|
|
690
|
+
limit: z.number().int().min(1).max(10).optional(),
|
|
691
|
+
...readScope,
|
|
692
|
+
})
|
|
693
|
+
.strict(),
|
|
694
|
+
z
|
|
695
|
+
.object({
|
|
696
|
+
mode: z.literal("describe"),
|
|
697
|
+
question: z.string().max(4_000).optional(),
|
|
698
|
+
classes: z
|
|
699
|
+
.array(z.string().min(1))
|
|
700
|
+
.max(20)
|
|
701
|
+
.optional()
|
|
702
|
+
.describe("describe: class IRIs to describe, at most 20."),
|
|
703
|
+
properties: z
|
|
704
|
+
.array(z.string().min(1))
|
|
705
|
+
.max(50)
|
|
706
|
+
.optional()
|
|
707
|
+
.describe("describe: property IRIs to describe, at most 50."),
|
|
708
|
+
...readScope,
|
|
709
|
+
})
|
|
710
|
+
.strict(),
|
|
711
|
+
z
|
|
712
|
+
.object({
|
|
713
|
+
mode: z.literal("commit_at"),
|
|
714
|
+
date: z
|
|
715
|
+
.string()
|
|
716
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
717
|
+
.optional()
|
|
718
|
+
.describe("commit_at: YYYY-MM-DD; the last commit written by the end of that day (UTC)."),
|
|
719
|
+
moment: z
|
|
720
|
+
.string()
|
|
721
|
+
.optional()
|
|
722
|
+
.describe("commit_at: RFC 3339, e.g. 2026-06-18T12:00:00Z; the last commit written at or before it."),
|
|
723
|
+
...readScope,
|
|
724
|
+
})
|
|
725
|
+
.strict(),
|
|
726
|
+
z
|
|
727
|
+
.object({
|
|
728
|
+
mode: z.literal("compare"),
|
|
729
|
+
query: z
|
|
730
|
+
.string()
|
|
731
|
+
.min(1)
|
|
732
|
+
.describe("compare: the SELECT to run at both points. Leave out LIMIT: the server reads up to max_rows rows a point, page by page."),
|
|
733
|
+
before: comparePointSchema.describe('compare: the earlier point (required): {"as_of_commit_seq": n}, {"date": "YYYY-MM-DD"} or {"moment": "RFC 3339"}.'),
|
|
734
|
+
after: comparePointSchema
|
|
735
|
+
.optional()
|
|
736
|
+
.describe("compare: the later point; defaults to the latest commit."),
|
|
737
|
+
key: z
|
|
738
|
+
.array(z.string().min(1))
|
|
739
|
+
.max(8)
|
|
740
|
+
.optional()
|
|
741
|
+
.describe('compare: the variables that identify a row\'s entity, e.g. ["contact"]. With a key the rows are paired: added and removed hold the entities at one point only, changed the entities whose values differ. Without one whole rows are compared.'),
|
|
742
|
+
entailment: z.enum(["none", "subclass", "rdfs", "owl"]).optional(),
|
|
743
|
+
max_rows: z
|
|
744
|
+
.number()
|
|
745
|
+
.int()
|
|
746
|
+
.min(1)
|
|
747
|
+
.max(50_000)
|
|
748
|
+
.optional()
|
|
749
|
+
.describe("compare: rows to read per point, 1 to 50,000 (default 20,000). Past it the result says truncated."),
|
|
750
|
+
limit: z.number().int().min(1).max(1_000).optional(),
|
|
751
|
+
compare_cursor: z
|
|
752
|
+
.string()
|
|
753
|
+
.optional()
|
|
754
|
+
.describe("compare: the cursor of the page before, from next; pass it with the same arguments."),
|
|
755
|
+
...readScope,
|
|
756
|
+
})
|
|
757
|
+
.strict(),
|
|
608
758
|
]);
|
|
609
759
|
export const configureInputSchema = z.discriminatedUnion("action", [
|
|
610
760
|
z
|
|
@@ -690,9 +840,10 @@ export const configureInputSchema = z.discriminatedUnion("action", [
|
|
|
690
840
|
.describe("Short imperative text, e.g. 'Add class Contractor'"),
|
|
691
841
|
change: z
|
|
692
842
|
.array(ontologyEvolveOpSchema)
|
|
693
|
-
.min(1)
|
|
694
843
|
.max(128)
|
|
695
|
-
.
|
|
844
|
+
.optional()
|
|
845
|
+
.describe("The ontology operations a person applies by accepting, in order (same shapes as evolve_ontology ops). Up to 128; may be empty or absent when facts is set"),
|
|
846
|
+
facts: suggestedFactsSchema.optional(),
|
|
696
847
|
rationale: z
|
|
697
848
|
.string()
|
|
698
849
|
.max(4000)
|