@madewithremy/admin 0.1.30 → 0.1.31
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 +351 -13
- package/dist/index.js +92 -3
- package/dist/index.js.map +1 -1
- package/dist/prod.js +566 -77
- package/package.json +1 -1
package/dist/prod.js
CHANGED
|
@@ -36,10 +36,10 @@ function fatal(message) {
|
|
|
36
36
|
throw new CliError(message);
|
|
37
37
|
}
|
|
38
38
|
var AdminApiError = class extends Error {
|
|
39
|
-
constructor(method,
|
|
40
|
-
super(`API ${method} ${
|
|
39
|
+
constructor(method, path8, status2, body) {
|
|
40
|
+
super(`API ${method} ${path8} returned ${status2}: ${JSON.stringify(body)}`);
|
|
41
41
|
this.method = method;
|
|
42
|
-
this.path =
|
|
42
|
+
this.path = path8;
|
|
43
43
|
this.status = status2;
|
|
44
44
|
this.body = body;
|
|
45
45
|
this.name = "AdminApiError";
|
|
@@ -1036,6 +1036,13 @@ function summarizeRelease(release) {
|
|
|
1036
1036
|
publishedAt: release.publishedAt ?? null,
|
|
1037
1037
|
...release.previewUrl ? { previewUrl: release.previewUrl } : {}
|
|
1038
1038
|
};
|
|
1039
|
+
const readers = (release.dataSources ?? []).filter((d) => d.reader);
|
|
1040
|
+
if (readers.length > 0 && release.status === "live") {
|
|
1041
|
+
summary2.readers = readers.map((d) => ({
|
|
1042
|
+
slug: d.slug,
|
|
1043
|
+
unread: d.readerUnread ?? 0
|
|
1044
|
+
}));
|
|
1045
|
+
}
|
|
1039
1046
|
if (release.status !== "failed") {
|
|
1040
1047
|
return summary2;
|
|
1041
1048
|
}
|
|
@@ -3092,8 +3099,8 @@ Notes:
|
|
|
3092
3099
|
- Public image URLs accept transform params, e.g. ?w=400&fit=cover.`;
|
|
3093
3100
|
|
|
3094
3101
|
// src/commands/dataSources.ts
|
|
3095
|
-
import
|
|
3096
|
-
import
|
|
3102
|
+
import fs6 from "fs";
|
|
3103
|
+
import path6 from "path";
|
|
3097
3104
|
|
|
3098
3105
|
// src/ops/dataSources.ts
|
|
3099
3106
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -3111,7 +3118,7 @@ var summarize = (d) => ({
|
|
|
3111
3118
|
...d.errorMessage ? { error: d.errorMessage } : {}
|
|
3112
3119
|
});
|
|
3113
3120
|
async function addDocument(ctx, params) {
|
|
3114
|
-
const { slug, filename, content, metadata, onProgress } = params;
|
|
3121
|
+
const { slug, filename, content, metadata, entities: entities2, onProgress } = params;
|
|
3115
3122
|
const contentHash = createHash2("sha256").update(content).digest("hex");
|
|
3116
3123
|
const token = await call(
|
|
3117
3124
|
ctx,
|
|
@@ -3121,7 +3128,8 @@ async function addDocument(ctx, params) {
|
|
|
3121
3128
|
slug,
|
|
3122
3129
|
filename,
|
|
3123
3130
|
contentHash,
|
|
3124
|
-
...metadata ? { metadata } : {}
|
|
3131
|
+
...metadata ? { metadata } : {},
|
|
3132
|
+
...entities2 ? { entities: entities2 } : {}
|
|
3125
3133
|
}
|
|
3126
3134
|
);
|
|
3127
3135
|
if (token.alreadyCurrent) {
|
|
@@ -3147,7 +3155,8 @@ async function addDocument(ctx, params) {
|
|
|
3147
3155
|
contentHash,
|
|
3148
3156
|
contentType: token.contentType,
|
|
3149
3157
|
size: content.length,
|
|
3150
|
-
...metadata ? { metadata } : {}
|
|
3158
|
+
...metadata ? { metadata } : {},
|
|
3159
|
+
...entities2 ? { entities: entities2 } : {}
|
|
3151
3160
|
}
|
|
3152
3161
|
);
|
|
3153
3162
|
return {
|
|
@@ -3324,6 +3333,20 @@ function count(ctx, params) {
|
|
|
3324
3333
|
...candidate ? { candidate: true } : {}
|
|
3325
3334
|
});
|
|
3326
3335
|
}
|
|
3336
|
+
function entities(ctx, params) {
|
|
3337
|
+
const { slug, filter, limit, candidate } = params;
|
|
3338
|
+
return call(
|
|
3339
|
+
ctx,
|
|
3340
|
+
"POST",
|
|
3341
|
+
`${base(ctx.appId)}/entities`,
|
|
3342
|
+
{
|
|
3343
|
+
slug,
|
|
3344
|
+
...filter ? { filter } : {},
|
|
3345
|
+
...limit !== void 0 ? { limit } : {},
|
|
3346
|
+
...candidate ? { candidate: true } : {}
|
|
3347
|
+
}
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3327
3350
|
function configGet(ctx, slug) {
|
|
3328
3351
|
return call(
|
|
3329
3352
|
ctx,
|
|
@@ -3485,6 +3508,67 @@ function deleteSource(ctx, slug) {
|
|
|
3485
3508
|
{ slug }
|
|
3486
3509
|
);
|
|
3487
3510
|
}
|
|
3511
|
+
var DICTIONARY_PAGE_ENTRIES = 1e4;
|
|
3512
|
+
var DICTIONARY_PAGE_BYTES = 4e6;
|
|
3513
|
+
async function pushDictionary(ctx, params) {
|
|
3514
|
+
const { slug, entries } = params;
|
|
3515
|
+
const stage = await call(
|
|
3516
|
+
ctx,
|
|
3517
|
+
"POST",
|
|
3518
|
+
`${base(ctx.appId)}/dictionary/stage`,
|
|
3519
|
+
{
|
|
3520
|
+
slug,
|
|
3521
|
+
...params.linkerVersion !== void 0 ? { linkerVersion: params.linkerVersion } : {}
|
|
3522
|
+
}
|
|
3523
|
+
);
|
|
3524
|
+
const perPage = Math.min(
|
|
3525
|
+
DICTIONARY_PAGE_ENTRIES,
|
|
3526
|
+
stage.pageMaxEntries || DICTIONARY_PAGE_ENTRIES
|
|
3527
|
+
);
|
|
3528
|
+
let pages2 = 0;
|
|
3529
|
+
let start2 = 0;
|
|
3530
|
+
while (start2 < entries.length) {
|
|
3531
|
+
let end = start2;
|
|
3532
|
+
let bytes = 0;
|
|
3533
|
+
while (end < entries.length && end - start2 < perPage) {
|
|
3534
|
+
const size = JSON.stringify(entries[end]).length;
|
|
3535
|
+
if (end > start2 && bytes + size > DICTIONARY_PAGE_BYTES) {
|
|
3536
|
+
break;
|
|
3537
|
+
}
|
|
3538
|
+
bytes += size;
|
|
3539
|
+
end += 1;
|
|
3540
|
+
}
|
|
3541
|
+
await call(ctx, "POST", `${base(ctx.appId)}/dictionary/page`, {
|
|
3542
|
+
slug,
|
|
3543
|
+
stageId: stage.stageId,
|
|
3544
|
+
page: pages2,
|
|
3545
|
+
entries: entries.slice(start2, end)
|
|
3546
|
+
});
|
|
3547
|
+
pages2 += 1;
|
|
3548
|
+
start2 = end;
|
|
3549
|
+
params.onProgress?.(start2, entries.length);
|
|
3550
|
+
}
|
|
3551
|
+
return call(
|
|
3552
|
+
ctx,
|
|
3553
|
+
"POST",
|
|
3554
|
+
`${base(ctx.appId)}/dictionary/commit`,
|
|
3555
|
+
{ slug, stageId: stage.stageId, pages: pages2 }
|
|
3556
|
+
);
|
|
3557
|
+
}
|
|
3558
|
+
function dictionary(ctx, params) {
|
|
3559
|
+
return call(
|
|
3560
|
+
ctx,
|
|
3561
|
+
"POST",
|
|
3562
|
+
`${base(ctx.appId)}/dictionary`,
|
|
3563
|
+
{ slug: params.slug }
|
|
3564
|
+
);
|
|
3565
|
+
}
|
|
3566
|
+
function link(ctx, params) {
|
|
3567
|
+
return call(ctx, "POST", `${base(ctx.appId)}/link`, {
|
|
3568
|
+
slug: params.slug,
|
|
3569
|
+
text: params.text
|
|
3570
|
+
});
|
|
3571
|
+
}
|
|
3488
3572
|
|
|
3489
3573
|
// src/ops/dataSourceJobs.ts
|
|
3490
3574
|
var DEFAULT_WAIT_TIMEOUT_MS2 = 60 * 60 * 1e3;
|
|
@@ -3643,6 +3727,39 @@ function describeProgress(job, start2) {
|
|
|
3643
3727
|
// src/commands/_shared/dataSources.ts
|
|
3644
3728
|
var DEFAULT_SOURCE = "default";
|
|
3645
3729
|
var sourceOf = (a) => a.str("source") || DEFAULT_SOURCE;
|
|
3730
|
+
function parseKeyValuePairs(raw, flag) {
|
|
3731
|
+
if (raw === void 0) {
|
|
3732
|
+
return void 0;
|
|
3733
|
+
}
|
|
3734
|
+
const pairs2 = {};
|
|
3735
|
+
for (const part of raw.split(",")) {
|
|
3736
|
+
const trimmed = part.trim();
|
|
3737
|
+
if (!trimmed) {
|
|
3738
|
+
continue;
|
|
3739
|
+
}
|
|
3740
|
+
const eq = trimmed.indexOf("=");
|
|
3741
|
+
if (eq <= 0) {
|
|
3742
|
+
fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
|
|
3743
|
+
}
|
|
3744
|
+
const key = trimmed.slice(0, eq).trim();
|
|
3745
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
3746
|
+
pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
|
|
3747
|
+
}
|
|
3748
|
+
if (Object.keys(pairs2).length === 0) {
|
|
3749
|
+
fatal(`--${flag} needs at least one key=value pair.`);
|
|
3750
|
+
}
|
|
3751
|
+
return pairs2;
|
|
3752
|
+
}
|
|
3753
|
+
function documentSelectorOf(raw) {
|
|
3754
|
+
if (raw.trimStart().startsWith("{")) {
|
|
3755
|
+
try {
|
|
3756
|
+
return JSON.parse(raw);
|
|
3757
|
+
} catch (err) {
|
|
3758
|
+
fatal(`--filter is not valid JSON: ${err.message}`);
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
return { metadata: parseKeyValuePairs(raw, "filter") };
|
|
3762
|
+
}
|
|
3646
3763
|
var timeoutOf = (a) => {
|
|
3647
3764
|
const seconds = a.str("timeout");
|
|
3648
3765
|
return seconds ? { timeoutMs: Number(seconds) * 1e3 } : {};
|
|
@@ -4658,9 +4775,116 @@ Retrieval evals:
|
|
|
4658
4775
|
with --candidate, compare, promote or drop.
|
|
4659
4776
|
`;
|
|
4660
4777
|
|
|
4661
|
-
// src/commands/
|
|
4778
|
+
// src/commands/dataSourceDictionary.ts
|
|
4662
4779
|
import fs4 from "fs";
|
|
4663
4780
|
import path4 from "path";
|
|
4781
|
+
var dataSourceDictionarySpecs = {
|
|
4782
|
+
"datasources dictionary push": {
|
|
4783
|
+
usage: "Usage: remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>",
|
|
4784
|
+
positionals: [{ name: "file", required: true }],
|
|
4785
|
+
flags: {
|
|
4786
|
+
source: { type: "string" },
|
|
4787
|
+
"linker-version": { type: "number", min: 1 }
|
|
4788
|
+
}
|
|
4789
|
+
},
|
|
4790
|
+
"datasources dictionary status": {
|
|
4791
|
+
usage: "Usage: remy-admin datasources dictionary status [--source <slug>]",
|
|
4792
|
+
flags: {
|
|
4793
|
+
source: { type: "string" }
|
|
4794
|
+
}
|
|
4795
|
+
},
|
|
4796
|
+
"datasources link": {
|
|
4797
|
+
usage: "Usage: remy-admin datasources link [--source <slug>] <text>",
|
|
4798
|
+
positionals: [{ name: "text", required: true }],
|
|
4799
|
+
flags: {
|
|
4800
|
+
source: { type: "string" }
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
};
|
|
4804
|
+
function readEntries(file) {
|
|
4805
|
+
const abs = path4.isAbsolute(file) ? file : path4.join(WORKSPACE_DIR, file);
|
|
4806
|
+
let content;
|
|
4807
|
+
try {
|
|
4808
|
+
content = fs4.readFileSync(abs, "utf8");
|
|
4809
|
+
} catch (err) {
|
|
4810
|
+
fatal(`Could not read "${file}": ${err.message}`);
|
|
4811
|
+
}
|
|
4812
|
+
const entries = [];
|
|
4813
|
+
content.split("\n").forEach((line, i) => {
|
|
4814
|
+
const trimmed = line.trim();
|
|
4815
|
+
if (!trimmed) {
|
|
4816
|
+
return;
|
|
4817
|
+
}
|
|
4818
|
+
let parsed;
|
|
4819
|
+
try {
|
|
4820
|
+
parsed = JSON.parse(trimmed);
|
|
4821
|
+
} catch {
|
|
4822
|
+
fatal(`${file}:${i + 1} is not valid JSON.`);
|
|
4823
|
+
}
|
|
4824
|
+
if (typeof parsed?.id !== "string" || !Array.isArray(parsed?.names) || !parsed.names.every((name) => typeof name === "string")) {
|
|
4825
|
+
fatal(`${file}:${i + 1} needs { "id": "...", "names": ["...", ...] }.`);
|
|
4826
|
+
}
|
|
4827
|
+
entries.push({ id: parsed.id, names: parsed.names });
|
|
4828
|
+
});
|
|
4829
|
+
if (entries.length === 0) {
|
|
4830
|
+
fatal(`"${file}" has no entries.`);
|
|
4831
|
+
}
|
|
4832
|
+
return entries;
|
|
4833
|
+
}
|
|
4834
|
+
async function dictionaryPush(ctx, a) {
|
|
4835
|
+
const entries = readEntries(a.req("file"));
|
|
4836
|
+
const linkerVersion = a.num("linker-version");
|
|
4837
|
+
out(
|
|
4838
|
+
await pushDictionary(ctx, {
|
|
4839
|
+
slug: sourceOf(a),
|
|
4840
|
+
entries,
|
|
4841
|
+
...linkerVersion !== void 0 ? { linkerVersion } : {},
|
|
4842
|
+
onProgress: (sent, total) => progress(`pushing ${sent.toLocaleString()}/${total.toLocaleString()}\u2026`)
|
|
4843
|
+
})
|
|
4844
|
+
);
|
|
4845
|
+
}
|
|
4846
|
+
async function dictionaryStatus(ctx, a) {
|
|
4847
|
+
out(await dictionary(ctx, { slug: sourceOf(a) }));
|
|
4848
|
+
}
|
|
4849
|
+
async function link2(ctx, a) {
|
|
4850
|
+
out(await link(ctx, { slug: sourceOf(a), text: a.req("text") }));
|
|
4851
|
+
}
|
|
4852
|
+
var dataSourceDictionaryHandlers = {
|
|
4853
|
+
"datasources dictionary push": dictionaryPush,
|
|
4854
|
+
"datasources dictionary status": dictionaryStatus,
|
|
4855
|
+
"datasources link": link2
|
|
4856
|
+
};
|
|
4857
|
+
var dataSourceDictionaryHelp = `
|
|
4858
|
+
Dictionary:
|
|
4859
|
+
remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>
|
|
4860
|
+
remy-admin datasources dictionary status [--source <slug>]
|
|
4861
|
+
remy-admin datasources link [--source <slug>] <text>
|
|
4862
|
+
|
|
4863
|
+
A dictionary is your own entity ids and the names each goes by, a
|
|
4864
|
+
projection of your tables: one JSONL line per entry,
|
|
4865
|
+
{"id": "P-101A", "names": ["P-101A", "Main Feed Pump"]}. Push it whole; it
|
|
4866
|
+
replaces the source's dictionary. From then on every passage carries the
|
|
4867
|
+
ids whose names its text mentions, beside the ones its document asserts,
|
|
4868
|
+
so --entities on search and count, and \`datasources entities\`, find a tag
|
|
4869
|
+
wherever it's written. Passages indexed before are re-tagged in the
|
|
4870
|
+
background (a relink; \`dictionary status\` shows it), with nothing
|
|
4871
|
+
re-embedded. An unchanged dictionary commits nothing.
|
|
4872
|
+
|
|
4873
|
+
Matching ignores case and a tag's separators (P-101A, P101A, p 101a), takes
|
|
4874
|
+
whole words only (P-101AB is not P-101A), and prefers the longest name; a
|
|
4875
|
+
name two ids claim tags both. A name needs three letters or digits (two in
|
|
4876
|
+
Chinese, Japanese or Korean). The push reports ambiguous names, the most
|
|
4877
|
+
claimed, and names that can't match their own text as written
|
|
4878
|
+
(\`unmatchable\`). Up to 250,000 entries, 16 names each, a million names.
|
|
4879
|
+
|
|
4880
|
+
\`link\` shows what the current dictionary finds in any text: check one
|
|
4881
|
+
there before relying on it. --linker-version moves a dictionary to a newer
|
|
4882
|
+
matching rule; without it a push keeps the one its dictionary has.
|
|
4883
|
+
`;
|
|
4884
|
+
|
|
4885
|
+
// src/commands/dataSourceJobs.ts
|
|
4886
|
+
import fs5 from "fs";
|
|
4887
|
+
import path5 from "path";
|
|
4664
4888
|
var dataSourceJobsSpecs = {
|
|
4665
4889
|
"datasources jobs start": {
|
|
4666
4890
|
usage: "Usage: remy-admin datasources jobs start [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --manifest <file.jsonl>) [--limit <n>] [--budget <dollars>] [--concurrency <n>] [--priority] [--approve] [--wait] [--timeout <sec>]",
|
|
@@ -4738,10 +4962,10 @@ async function jobsStart(ctx, a) {
|
|
|
4738
4962
|
}
|
|
4739
4963
|
let source;
|
|
4740
4964
|
if (manifest) {
|
|
4741
|
-
const abs =
|
|
4965
|
+
const abs = path5.isAbsolute(manifest) ? manifest : path5.join(WORKSPACE_DIR, manifest);
|
|
4742
4966
|
let content;
|
|
4743
4967
|
try {
|
|
4744
|
-
content =
|
|
4968
|
+
content = fs5.readFileSync(abs);
|
|
4745
4969
|
} catch (err) {
|
|
4746
4970
|
fatal(`Could not read manifest "${manifest}": ${err.message}`);
|
|
4747
4971
|
}
|
|
@@ -4867,12 +5091,13 @@ Bulk ingestion (jobs):
|
|
|
4867
5091
|
A job loads a whole corpus at once. It reads either every object under a
|
|
4868
5092
|
prefix of one of the app's file stores (\`files put\` fills one; an app can
|
|
4869
5093
|
write one at runtime) or a manifest you upload: JSONL, one line per document,
|
|
4870
|
-
{"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026}
|
|
4871
|
-
|
|
5094
|
+
{"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026},
|
|
5095
|
+
"entities": ["\u2026"]}. Everything but the URL is optional; it must be public.
|
|
4872
5096
|
|
|
4873
|
-
Nothing
|
|
4874
|
-
sample of about a hundred documents, and writes a plan:
|
|
4875
|
-
cost per stage (embedding, extraction, contextual
|
|
5097
|
+
Nothing past the sample runs until you say so. The platform counts the
|
|
5098
|
+
source, reads a sample of about a hundred documents, and writes a plan:
|
|
5099
|
+
documents, chunks, cost per stage (embedding, extraction, contextual, and
|
|
5100
|
+
the mapper's own model calls, priced from the sample's) at today's rates, storage,
|
|
4876
5101
|
whether it fits where the source lives, and a duration. \`jobs status\` shows
|
|
4877
5102
|
it; \`jobs approve\` starts the run. Two gates: the corpus has to fit the
|
|
4878
5103
|
source's placement (a shared-pool source over the per-source cap answers
|
|
@@ -4982,6 +5207,7 @@ function quarantine(ctx, params) {
|
|
|
4982
5207
|
"GET",
|
|
4983
5208
|
`${base5(ctx.appId)}/jobs/${params.id}/quarantine${qs({
|
|
4984
5209
|
...params.kind ? { kind: params.kind } : {},
|
|
5210
|
+
...params.stage ? { stage: params.stage } : {},
|
|
4985
5211
|
...params.reason ? { reason: params.reason } : {},
|
|
4986
5212
|
...params.cursor ? { cursor: params.cursor } : {},
|
|
4987
5213
|
...params.limit !== void 0 ? { limit: params.limit } : {}
|
|
@@ -5050,10 +5276,11 @@ var dataSourceMappersSpecs = {
|
|
|
5050
5276
|
}
|
|
5051
5277
|
},
|
|
5052
5278
|
"datasources jobs quarantine": {
|
|
5053
|
-
usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]",
|
|
5279
|
+
usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]",
|
|
5054
5280
|
positionals: [{ name: "id", required: true }],
|
|
5055
5281
|
flags: {
|
|
5056
5282
|
kind: { type: "string" },
|
|
5283
|
+
stage: { type: "string" },
|
|
5057
5284
|
reason: { type: "string" },
|
|
5058
5285
|
cursor: { type: "string" },
|
|
5059
5286
|
limit: { type: "number", min: 1 }
|
|
@@ -5206,18 +5433,32 @@ async function quarantine2(ctx, a) {
|
|
|
5206
5433
|
const id = a.req("id");
|
|
5207
5434
|
const limit = a.num("limit");
|
|
5208
5435
|
const kind = kindOf(a);
|
|
5436
|
+
const stage = a.str("stage");
|
|
5437
|
+
if (stage !== void 0 && stage !== "map" && stage !== "read") {
|
|
5438
|
+
fatal(`--stage must be "map" or "read" (got "${stage}").`);
|
|
5439
|
+
}
|
|
5209
5440
|
const reason = a.str("reason");
|
|
5210
5441
|
const cursor = a.str("cursor");
|
|
5211
5442
|
const result = await quarantine(ctx, {
|
|
5212
5443
|
id,
|
|
5213
5444
|
...kind ? { kind } : {},
|
|
5445
|
+
...stage ? { stage } : {},
|
|
5214
5446
|
...reason ? { reason } : {},
|
|
5215
5447
|
...cursor ? { cursor } : {},
|
|
5216
5448
|
...limit !== void 0 ? { limit } : {}
|
|
5217
5449
|
});
|
|
5450
|
+
const { skipped, errors, readErrors } = result.counts;
|
|
5451
|
+
const notes = [
|
|
5452
|
+
...skipped + errors > 0 ? [
|
|
5453
|
+
`Mapping: fix the mapper, push, \`datasources map deploy\`, then \`datasources jobs replay ${id}\` to run these through it again.`
|
|
5454
|
+
] : [],
|
|
5455
|
+
...readErrors > 0 ? [
|
|
5456
|
+
`Reading: fix the reader and publish, then \`datasources reader retry --source ${result.dataSource.slug} --job ${id}\` reads these again.`
|
|
5457
|
+
] : []
|
|
5458
|
+
];
|
|
5218
5459
|
out({
|
|
5219
5460
|
...result,
|
|
5220
|
-
note:
|
|
5461
|
+
note: notes.length === 0 ? "Nothing quarantined: the mapper produced documents (or deletions) for every object, and the reader read every document." : notes.join(" ")
|
|
5221
5462
|
});
|
|
5222
5463
|
}
|
|
5223
5464
|
async function replay2(ctx, a) {
|
|
@@ -5258,7 +5499,7 @@ Mapping raw objects into documents (mappers):
|
|
|
5258
5499
|
remy-admin datasources map test [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>] [--dev] [--full]
|
|
5259
5500
|
remy-admin datasources map deploy [--source <slug>] [--release <id>]
|
|
5260
5501
|
remy-admin datasources remap [--source <slug>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
|
|
5261
|
-
remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]
|
|
5502
|
+
remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]
|
|
5262
5503
|
remy-admin datasources jobs replay <id> [--kind skip|error] [--wait] [--timeout <sec>]
|
|
5263
5504
|
|
|
5264
5505
|
A source takes every object it is given as one document. When the file is
|
|
@@ -5299,6 +5540,228 @@ Mapping raw objects into documents (mappers):
|
|
|
5299
5540
|
pauses with pauseReason 'skips' for a look at the quarantine.
|
|
5300
5541
|
`;
|
|
5301
5542
|
|
|
5543
|
+
// src/ops/dataSourceReaders.ts
|
|
5544
|
+
function base6(appId) {
|
|
5545
|
+
return `/_internal/v2/apps/${appId}/datasources`;
|
|
5546
|
+
}
|
|
5547
|
+
function readerTest(ctx, params) {
|
|
5548
|
+
return call(
|
|
5549
|
+
ctx,
|
|
5550
|
+
"POST",
|
|
5551
|
+
`${base6(ctx.appId)}/reader/test`,
|
|
5552
|
+
{
|
|
5553
|
+
slug: params.slug,
|
|
5554
|
+
...params.documents ? { documents: params.documents } : {},
|
|
5555
|
+
...params.limit !== void 0 ? { limit: params.limit } : {}
|
|
5556
|
+
}
|
|
5557
|
+
);
|
|
5558
|
+
}
|
|
5559
|
+
function readerStatus(ctx, params) {
|
|
5560
|
+
return call(
|
|
5561
|
+
ctx,
|
|
5562
|
+
"POST",
|
|
5563
|
+
`${base6(ctx.appId)}/reader/status`,
|
|
5564
|
+
{ slug: params.slug }
|
|
5565
|
+
);
|
|
5566
|
+
}
|
|
5567
|
+
function readerRetry(ctx, params) {
|
|
5568
|
+
return call(
|
|
5569
|
+
ctx,
|
|
5570
|
+
"POST",
|
|
5571
|
+
`${base6(ctx.appId)}/reader/retry`,
|
|
5572
|
+
{
|
|
5573
|
+
slug: params.slug,
|
|
5574
|
+
...params.jobId ? { jobId: params.jobId } : {}
|
|
5575
|
+
}
|
|
5576
|
+
);
|
|
5577
|
+
}
|
|
5578
|
+
function reread(ctx, params) {
|
|
5579
|
+
return call(
|
|
5580
|
+
ctx,
|
|
5581
|
+
"POST",
|
|
5582
|
+
`${base6(ctx.appId)}/reread`,
|
|
5583
|
+
{
|
|
5584
|
+
slug: params.slug,
|
|
5585
|
+
...params.unread ? { unread: true } : {},
|
|
5586
|
+
...params.filter ? { filter: params.filter } : {},
|
|
5587
|
+
...params.budgetDollars !== void 0 ? { budgetDollars: params.budgetDollars } : {},
|
|
5588
|
+
...params.limit !== void 0 ? { limit: params.limit } : {}
|
|
5589
|
+
}
|
|
5590
|
+
);
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5593
|
+
// src/commands/dataSourceReaders.ts
|
|
5594
|
+
var dataSourceReadersSpecs = {
|
|
5595
|
+
"datasources reader test": {
|
|
5596
|
+
usage: "Usage: remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2,...] [--limit <n>]",
|
|
5597
|
+
flags: {
|
|
5598
|
+
source: { type: "string" },
|
|
5599
|
+
dev: { type: "boolean" },
|
|
5600
|
+
documents: { type: "string" },
|
|
5601
|
+
limit: { type: "number", min: 1 }
|
|
5602
|
+
}
|
|
5603
|
+
},
|
|
5604
|
+
"datasources reader status": {
|
|
5605
|
+
usage: "Usage: remy-admin datasources reader status [--source <slug>]",
|
|
5606
|
+
flags: {
|
|
5607
|
+
source: { type: "string" }
|
|
5608
|
+
}
|
|
5609
|
+
},
|
|
5610
|
+
"datasources reader retry": {
|
|
5611
|
+
usage: "Usage: remy-admin datasources reader retry [--source <slug>] [--job <id>]",
|
|
5612
|
+
flags: {
|
|
5613
|
+
source: { type: "string" },
|
|
5614
|
+
job: { type: "string" }
|
|
5615
|
+
}
|
|
5616
|
+
},
|
|
5617
|
+
"datasources reread": {
|
|
5618
|
+
usage: "Usage: remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v,...|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]",
|
|
5619
|
+
flags: {
|
|
5620
|
+
source: { type: "string" },
|
|
5621
|
+
unread: { type: "boolean" },
|
|
5622
|
+
filter: { type: "string" },
|
|
5623
|
+
budget: { type: "number", min: 0.01 },
|
|
5624
|
+
limit: { type: "number", min: 1 },
|
|
5625
|
+
wait: { type: "boolean" },
|
|
5626
|
+
timeout: { type: "string" }
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
};
|
|
5630
|
+
async function readerTest2(ctx, a) {
|
|
5631
|
+
if (!a.bool("dev")) {
|
|
5632
|
+
fatal(
|
|
5633
|
+
"reader test runs the dev session's reader only: pass --dev. The live reader reads through deliveries and `datasources reread`."
|
|
5634
|
+
);
|
|
5635
|
+
}
|
|
5636
|
+
const slug = sourceOf(a);
|
|
5637
|
+
const raw = a.str("documents");
|
|
5638
|
+
const documents2 = raw?.split(",").map((id) => id.trim()).filter(Boolean);
|
|
5639
|
+
if (raw !== void 0 && documents2?.length === 0) {
|
|
5640
|
+
fatal("--documents needs at least one document id.");
|
|
5641
|
+
}
|
|
5642
|
+
const limit = a.num("limit");
|
|
5643
|
+
const result = await readerTest(ctx, {
|
|
5644
|
+
slug,
|
|
5645
|
+
...documents2 ? { documents: documents2 } : {},
|
|
5646
|
+
...limit !== void 0 ? { limit } : {}
|
|
5647
|
+
});
|
|
5648
|
+
out({
|
|
5649
|
+
dataSource: slug,
|
|
5650
|
+
...result,
|
|
5651
|
+
note: "Ran the LOCAL reader through the dev session, writing into the dev session's database. Query its tables to check what it wrote; publish to make it the live reader, then `datasources reread --unread` reads what it has not."
|
|
5652
|
+
});
|
|
5653
|
+
if (result.failed > 0) {
|
|
5654
|
+
throw new CliError(
|
|
5655
|
+
`The reader failed on ${result.failed} of ${result.documents.length} document(s).`,
|
|
5656
|
+
EXIT.buildFailed
|
|
5657
|
+
);
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
async function readerStatus2(ctx, a) {
|
|
5661
|
+
const slug = sourceOf(a);
|
|
5662
|
+
const result = await readerStatus(ctx, { slug });
|
|
5663
|
+
const { reader, deliveries, documents: documents2 } = result;
|
|
5664
|
+
out({
|
|
5665
|
+
dataSource: slug,
|
|
5666
|
+
...result,
|
|
5667
|
+
note: !reader ? `The live release declares no reader for "${slug}". Declare one in mindstudio.json's dataSources and publish.` : deliveries.parked > 0 ? `${deliveries.parked} delivery(ies) failed six times and parked. Fix the reader and publish, then \`datasources reader retry\`.` : documents2.unread > 0 ? `${documents2.unread.toLocaleString()} live document(s) not read yet. \`datasources reread --unread\` plans reading them.` : "Every live document has been read."
|
|
5668
|
+
});
|
|
5669
|
+
}
|
|
5670
|
+
async function readerRetry2(ctx, a) {
|
|
5671
|
+
const slug = sourceOf(a);
|
|
5672
|
+
const jobId = a.str("job");
|
|
5673
|
+
const result = await readerRetry(ctx, {
|
|
5674
|
+
slug,
|
|
5675
|
+
...jobId ? { jobId } : {}
|
|
5676
|
+
});
|
|
5677
|
+
out({
|
|
5678
|
+
dataSource: slug,
|
|
5679
|
+
...jobId ? { job: jobId } : {},
|
|
5680
|
+
...result,
|
|
5681
|
+
note: result.retried === 0 ? jobId ? `Job ${jobId}'s reader failed on nothing.` : "Nothing parked." : `Queued ${result.retried.toLocaleString()} delivery(ies) again; \`datasources reader status\` shows them drain.`
|
|
5682
|
+
});
|
|
5683
|
+
}
|
|
5684
|
+
async function reread2(ctx, a) {
|
|
5685
|
+
const slug = sourceOf(a);
|
|
5686
|
+
const raw = a.str("filter");
|
|
5687
|
+
const budget = a.num("budget");
|
|
5688
|
+
const limit = a.num("limit");
|
|
5689
|
+
const { job } = await reread(ctx, {
|
|
5690
|
+
slug,
|
|
5691
|
+
...a.bool("unread") ? { unread: true } : {},
|
|
5692
|
+
...raw !== void 0 ? { filter: documentSelectorOf(raw) } : {},
|
|
5693
|
+
...budget !== void 0 ? { budgetDollars: budget } : {},
|
|
5694
|
+
...limit !== void 0 ? { limit } : {}
|
|
5695
|
+
});
|
|
5696
|
+
if (!a.bool("wait")) {
|
|
5697
|
+
out({
|
|
5698
|
+
dataSource: slug,
|
|
5699
|
+
job,
|
|
5700
|
+
note: `Planning: the reread prices reading from the reader's history, or reads a first slice for real to measure it. \`datasources jobs status ${job.id}\` shows the plan; \`datasources jobs approve ${job.id}\` runs it.`
|
|
5701
|
+
});
|
|
5702
|
+
return;
|
|
5703
|
+
}
|
|
5704
|
+
await waitAndReport(ctx, a, job.id, "plan", {
|
|
5705
|
+
dataSource: slug,
|
|
5706
|
+
action: "reread"
|
|
5707
|
+
});
|
|
5708
|
+
}
|
|
5709
|
+
var dataSourceReadersHandlers = {
|
|
5710
|
+
"datasources reader test": readerTest2,
|
|
5711
|
+
"datasources reader status": readerStatus2,
|
|
5712
|
+
"datasources reader retry": readerRetry2,
|
|
5713
|
+
"datasources reread": reread2
|
|
5714
|
+
};
|
|
5715
|
+
var dataSourceReadersHelp = `
|
|
5716
|
+
Reading documents into the app (readers):
|
|
5717
|
+
remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2] [--limit <n>]
|
|
5718
|
+
remy-admin datasources reader status [--source <slug>]
|
|
5719
|
+
remy-admin datasources reader retry [--source <slug>] [--job <id>]
|
|
5720
|
+
remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
|
|
5721
|
+
|
|
5722
|
+
A READER is app code the platform runs over every document a source
|
|
5723
|
+
builds, writing what it finds into the app's own tables: the extraction an
|
|
5724
|
+
app would otherwise run from a cron over allDocuments(), with the
|
|
5725
|
+
platform's batching, retries, budgets and pauses. Declare it in
|
|
5726
|
+
mindstudio.json:
|
|
5727
|
+
"dataSources": [{ "slug": "archive", "reader": { "path": "dist/datasources/archive.reader.ts" } }]
|
|
5728
|
+
The file exports defineReader(Archive, { read, removed, cleared? }) from
|
|
5729
|
+
@mindstudio-ai/agent. read(document) gets the document (id, sourceId,
|
|
5730
|
+
filename, metadata, entities) and document.chunks(), its passages as
|
|
5731
|
+
indexed with each one's entities; removed(documentId) is called when a
|
|
5732
|
+
document leaves the source; cleared(sourceId) once when the source is
|
|
5733
|
+
deleted. Throwing fails that document, which is retried. A document can be
|
|
5734
|
+
read more than once, so read() replaces the document's rows as a set,
|
|
5735
|
+
keyed by its id, and removed() deletes them. Options on the declaration:
|
|
5736
|
+
timeoutMs (per document, default 60000, max 300000) and concurrency
|
|
5737
|
+
(frames in flight for the source, default 16, max 64).
|
|
5738
|
+
|
|
5739
|
+
The reader always runs against the LIVE release, its code and its
|
|
5740
|
+
database: nothing reads until the app publishes one. From then on every
|
|
5741
|
+
document whose build lands on the active version is read, every removed
|
|
5742
|
+
document delivered as removed, whether it came through add(), a sync or a
|
|
5743
|
+
job. A job over a reader source reads as its last stage: the job stays
|
|
5744
|
+
running until its documents are read, \`jobs status\` says "reading N of M",
|
|
5745
|
+
a document the reader fails on lands in the job's quarantine (stage read),
|
|
5746
|
+
and a reader failing on far more documents than it did at first pauses the
|
|
5747
|
+
job with pauseReason 'reader'.
|
|
5748
|
+
|
|
5749
|
+
The loop: \`reader test --dev\` runs the LOCAL reader through the dev
|
|
5750
|
+
session over real documents, writing into the dev session's database, and
|
|
5751
|
+
prints each outcome; publish, and \`releases wait\` reports each reader's
|
|
5752
|
+
unread documents; \`reread --unread\` reads them. \`reread\` is a job that
|
|
5753
|
+
plans first: it prices reading from what the live reader has done (after a
|
|
5754
|
+
hundred documents), or reads a first slice for real and measures spend,
|
|
5755
|
+
failures and the live database's growth, then waits for \`jobs approve\`.
|
|
5756
|
+
Without --unread it reads every built document again (a changed reader).
|
|
5757
|
+
\`reader status\` shows the reader, its deliveries (pending, in flight,
|
|
5758
|
+
retrying, parked, recent errors), unread documents, the measured cost per
|
|
5759
|
+
document and the live database's size. Deliveries outside jobs retry after
|
|
5760
|
+
1, 5, 15, 60 and 240 minutes, then park; \`reader retry\` sends parked ones
|
|
5761
|
+
again, and \`reader retry --job <id>\` the documents a job's reader failed
|
|
5762
|
+
on.
|
|
5763
|
+
`;
|
|
5764
|
+
|
|
5302
5765
|
// src/commands/dataSources.ts
|
|
5303
5766
|
var INGEST_FLAGS = {
|
|
5304
5767
|
chunking: { type: "string" },
|
|
@@ -5329,11 +5792,12 @@ var CONFIG_FLAGS = {
|
|
|
5329
5792
|
};
|
|
5330
5793
|
var dataSourcesSpecs = {
|
|
5331
5794
|
"datasources add": {
|
|
5332
|
-
usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>",
|
|
5795
|
+
usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>",
|
|
5333
5796
|
positionals: [{ name: "file", required: true, variadic: true }],
|
|
5334
5797
|
flags: {
|
|
5335
5798
|
source: { type: "string" },
|
|
5336
5799
|
metadata: { type: "string" },
|
|
5800
|
+
entities: { type: "string" },
|
|
5337
5801
|
wait: { type: "boolean" },
|
|
5338
5802
|
timeout: { type: "string" }
|
|
5339
5803
|
}
|
|
@@ -5362,7 +5826,7 @@ var dataSourcesSpecs = {
|
|
|
5362
5826
|
}
|
|
5363
5827
|
},
|
|
5364
5828
|
"datasources search": {
|
|
5365
|
-
usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
|
|
5829
|
+
usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--entities <id,...>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
|
|
5366
5830
|
positionals: [{ name: "query", required: true }],
|
|
5367
5831
|
flags: {
|
|
5368
5832
|
source: { type: "string" },
|
|
@@ -5372,6 +5836,7 @@ var dataSourcesSpecs = {
|
|
|
5372
5836
|
hybrid: { type: "string" },
|
|
5373
5837
|
mode: { type: "string" },
|
|
5374
5838
|
filter: { type: "string" },
|
|
5839
|
+
entities: { type: "string" },
|
|
5375
5840
|
phrase: { type: "string" },
|
|
5376
5841
|
contains: { type: "string" },
|
|
5377
5842
|
"max-per-document": { type: "number", min: 1 },
|
|
@@ -5398,12 +5863,25 @@ var dataSourcesSpecs = {
|
|
|
5398
5863
|
}
|
|
5399
5864
|
},
|
|
5400
5865
|
"datasources count": {
|
|
5401
|
-
usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]",
|
|
5866
|
+
usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]",
|
|
5867
|
+
flags: {
|
|
5868
|
+
source: { type: "string" },
|
|
5869
|
+
filter: { type: "string" },
|
|
5870
|
+
entities: { type: "string" },
|
|
5871
|
+
contains: { type: "string" },
|
|
5872
|
+
phrase: { type: "string" },
|
|
5873
|
+
candidate: { type: "boolean" }
|
|
5874
|
+
}
|
|
5875
|
+
},
|
|
5876
|
+
"datasources entities": {
|
|
5877
|
+
usage: "Usage: remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--limit <n>] [--candidate]",
|
|
5402
5878
|
flags: {
|
|
5403
5879
|
source: { type: "string" },
|
|
5404
5880
|
filter: { type: "string" },
|
|
5881
|
+
entities: { type: "string" },
|
|
5405
5882
|
contains: { type: "string" },
|
|
5406
5883
|
phrase: { type: "string" },
|
|
5884
|
+
limit: { type: "number", min: 1 },
|
|
5407
5885
|
candidate: { type: "boolean" }
|
|
5408
5886
|
}
|
|
5409
5887
|
},
|
|
@@ -5449,22 +5927,24 @@ async function dataSourcesAdd(ctx, a) {
|
|
|
5449
5927
|
const files = a.rest("file");
|
|
5450
5928
|
const slug = sourceOf(a);
|
|
5451
5929
|
const metadata = parseKeyValuePairs(a.str("metadata"), "metadata");
|
|
5930
|
+
const entities2 = parseIdList(a.str("entities"));
|
|
5452
5931
|
const results = [];
|
|
5453
5932
|
for (const file of files) {
|
|
5454
|
-
const abs =
|
|
5933
|
+
const abs = path6.isAbsolute(file) ? file : path6.join(WORKSPACE_DIR, file);
|
|
5455
5934
|
let bytes;
|
|
5456
5935
|
try {
|
|
5457
|
-
bytes =
|
|
5936
|
+
bytes = fs6.readFileSync(abs);
|
|
5458
5937
|
} catch (err) {
|
|
5459
5938
|
fatal(`Could not read file "${file}": ${err.message}`);
|
|
5460
5939
|
}
|
|
5461
|
-
const filename =
|
|
5940
|
+
const filename = path6.basename(file);
|
|
5462
5941
|
results.push(
|
|
5463
5942
|
await addDocument(ctx, {
|
|
5464
5943
|
slug,
|
|
5465
5944
|
filename,
|
|
5466
5945
|
content: bytes,
|
|
5467
5946
|
...metadata ? { metadata } : {},
|
|
5947
|
+
...entities2 ? { entities: entities2 } : {},
|
|
5468
5948
|
onProgress: progress
|
|
5469
5949
|
})
|
|
5470
5950
|
);
|
|
@@ -5535,6 +6015,7 @@ async function dataSourcesStatus(ctx, a) {
|
|
|
5535
6015
|
activeVersion: source.activeVersion,
|
|
5536
6016
|
candidate: source.candidate,
|
|
5537
6017
|
job: source.job,
|
|
6018
|
+
relink: source.relink,
|
|
5538
6019
|
failures: (failures?.documents ?? []).map(summarize),
|
|
5539
6020
|
moreFailures: !!failures?.nextCursor
|
|
5540
6021
|
});
|
|
@@ -5550,16 +6031,7 @@ async function dataSourcesRm(ctx, a) {
|
|
|
5550
6031
|
out({ deleted: documentId });
|
|
5551
6032
|
return;
|
|
5552
6033
|
}
|
|
5553
|
-
|
|
5554
|
-
if (rawFilter.trimStart().startsWith("{")) {
|
|
5555
|
-
try {
|
|
5556
|
-
filter = JSON.parse(rawFilter);
|
|
5557
|
-
} catch (err) {
|
|
5558
|
-
fatal(`--filter is not valid JSON: ${err.message}`);
|
|
5559
|
-
}
|
|
5560
|
-
} else {
|
|
5561
|
-
filter = { metadata: parseKeyValuePairs(rawFilter, "filter") };
|
|
5562
|
-
}
|
|
6034
|
+
const filter = documentSelectorOf(rawFilter);
|
|
5563
6035
|
const slug = sourceOf(a);
|
|
5564
6036
|
const result = await rmWhereAll(ctx, {
|
|
5565
6037
|
slug,
|
|
@@ -5614,6 +6086,24 @@ async function dataSourcesCount(ctx, a) {
|
|
|
5614
6086
|
})
|
|
5615
6087
|
);
|
|
5616
6088
|
}
|
|
6089
|
+
async function dataSourcesEntities(ctx, a) {
|
|
6090
|
+
const filter = filterFromFlags(a);
|
|
6091
|
+
const limit = a.num("limit");
|
|
6092
|
+
out(
|
|
6093
|
+
await entities(ctx, {
|
|
6094
|
+
slug: sourceOf(a),
|
|
6095
|
+
...filter ? { filter } : {},
|
|
6096
|
+
...limit !== void 0 ? { limit } : {},
|
|
6097
|
+
...a.bool("candidate") ? { candidate: true } : {}
|
|
6098
|
+
})
|
|
6099
|
+
);
|
|
6100
|
+
}
|
|
6101
|
+
function parseIdList(raw) {
|
|
6102
|
+
if (raw === void 0) {
|
|
6103
|
+
return void 0;
|
|
6104
|
+
}
|
|
6105
|
+
return raw.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
|
|
6106
|
+
}
|
|
5617
6107
|
function filterFromFlags(a) {
|
|
5618
6108
|
let filter = {};
|
|
5619
6109
|
const raw = a.str("filter");
|
|
@@ -5631,6 +6121,10 @@ function filterFromFlags(a) {
|
|
|
5631
6121
|
}
|
|
5632
6122
|
}
|
|
5633
6123
|
}
|
|
6124
|
+
const entities2 = parseIdList(a.str("entities"));
|
|
6125
|
+
if (entities2?.length) {
|
|
6126
|
+
filter.entities = entities2;
|
|
6127
|
+
}
|
|
5634
6128
|
const phrase = a.str("phrase");
|
|
5635
6129
|
if (phrase) {
|
|
5636
6130
|
filter.phrase = phrase;
|
|
@@ -5641,29 +6135,6 @@ function filterFromFlags(a) {
|
|
|
5641
6135
|
}
|
|
5642
6136
|
return Object.keys(filter).length ? filter : void 0;
|
|
5643
6137
|
}
|
|
5644
|
-
function parseKeyValuePairs(raw, flag) {
|
|
5645
|
-
if (raw === void 0) {
|
|
5646
|
-
return void 0;
|
|
5647
|
-
}
|
|
5648
|
-
const pairs2 = {};
|
|
5649
|
-
for (const part of raw.split(",")) {
|
|
5650
|
-
const trimmed = part.trim();
|
|
5651
|
-
if (!trimmed) {
|
|
5652
|
-
continue;
|
|
5653
|
-
}
|
|
5654
|
-
const eq = trimmed.indexOf("=");
|
|
5655
|
-
if (eq <= 0) {
|
|
5656
|
-
fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
|
|
5657
|
-
}
|
|
5658
|
-
const key = trimmed.slice(0, eq).trim();
|
|
5659
|
-
const value = trimmed.slice(eq + 1).trim();
|
|
5660
|
-
pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
|
|
5661
|
-
}
|
|
5662
|
-
if (Object.keys(pairs2).length === 0) {
|
|
5663
|
-
fatal(`--${flag} needs at least one key=value pair.`);
|
|
5664
|
-
}
|
|
5665
|
-
return pairs2;
|
|
5666
|
-
}
|
|
5667
6138
|
function triState2(a, flag, key) {
|
|
5668
6139
|
const raw = a.str(flag);
|
|
5669
6140
|
if (raw === void 0) {
|
|
@@ -5890,6 +6361,7 @@ var dataSourcesHandlers = {
|
|
|
5890
6361
|
"datasources create": dataSourcesCreate,
|
|
5891
6362
|
"datasources config": dataSourcesConfig,
|
|
5892
6363
|
"datasources count": dataSourcesCount,
|
|
6364
|
+
"datasources entities": dataSourcesEntities,
|
|
5893
6365
|
"datasources move": dataSourcesMove,
|
|
5894
6366
|
"datasources revectorize": dataSourcesRevectorize,
|
|
5895
6367
|
"datasources promote": dataSourcesPromote,
|
|
@@ -5929,12 +6401,13 @@ Subcommands:
|
|
|
5929
6401
|
delete Delete a whole data source \u2014 documents, vectors and versions
|
|
5930
6402
|
|
|
5931
6403
|
Usage:
|
|
5932
|
-
remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>
|
|
6404
|
+
remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>
|
|
5933
6405
|
remy-admin datasources list
|
|
5934
6406
|
remy-admin datasources status [--source <slug>] [--status error] [--all]
|
|
5935
6407
|
remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)
|
|
5936
6408
|
remy-admin datasources search [--source <slug>] [search options] <query>
|
|
5937
|
-
remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]
|
|
6409
|
+
remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]
|
|
6410
|
+
remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--limit <n>] [--candidate]
|
|
5938
6411
|
remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
|
|
5939
6412
|
remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]
|
|
5940
6413
|
remy-admin datasources move [--source <slug>] --to <resource-id|shared> [--chains <n>] [--restart] [--wait] [--timeout <sec>]
|
|
@@ -5955,6 +6428,15 @@ Count:
|
|
|
5955
6428
|
count (none exists), and not the set search returns. The SDK's
|
|
5956
6429
|
\`Source.count(filter)\` is the same call for an app.
|
|
5957
6430
|
|
|
6431
|
+
Entities:
|
|
6432
|
+
A document can assert what it is about as your own ids (\`add --entities\`,
|
|
6433
|
+
a mapper's \`entities\`, a manifest line's \`entities\`), and every passage
|
|
6434
|
+
carries them. \`--entities a,b\` on search and count narrows to passages
|
|
6435
|
+
about any of them. \`entities\` lists which entities the matching passages
|
|
6436
|
+
are about, most first, with how many passages carry each (approximate, and
|
|
6437
|
+
passages, not documents): filter on one entity to see what appears beside
|
|
6438
|
+
it. The SDK's \`Source.entities({ filter, limit })\` is the same call.
|
|
6439
|
+
|
|
5958
6440
|
Search options:
|
|
5959
6441
|
--top-k <n> Results to return (default 5, max 50)
|
|
5960
6442
|
--mode <m> hybrid (default) | semantic | lexical. Lexical is
|
|
@@ -5963,11 +6445,12 @@ Search options:
|
|
|
5963
6445
|
--filter <k=v,...> Match document metadata set at add time. Values
|
|
5964
6446
|
coerce: true/false and numbers become typed.
|
|
5965
6447
|
Pass a JSON object for the full grammar:
|
|
5966
|
-
metadata, filename, documentIds,
|
|
5967
|
-
phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
|
|
6448
|
+
metadata, filename, documentIds, entities, pages,
|
|
6449
|
+
contains, phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
|
|
5968
6450
|
Metadata takes numeric ranges via {gte,lte}: store
|
|
5969
6451
|
dates as integers (YYYYMMDD or epoch seconds), then
|
|
5970
6452
|
--filter '{"metadata":{"date":{"gte":20250101}}}'
|
|
6453
|
+
--entities <id,...> Chunk must be about any of these entities
|
|
5971
6454
|
--phrase <text> Chunk must contain this exact word sequence
|
|
5972
6455
|
--contains <words> Chunk must contain ALL these words, any order
|
|
5973
6456
|
--max-per-document <n> Cap hits per document; backfills from others
|
|
@@ -6056,6 +6539,8 @@ Notes:
|
|
|
6056
6539
|
Re-adding an unchanged file is free: no upload, no re-embedding. Re-adding
|
|
6057
6540
|
with a different --metadata updates the tags in place, also free.
|
|
6058
6541
|
--metadata tags documents for search-time filtering (scalars, \u226416 keys).
|
|
6542
|
+
--entities names what a document is about, as your own ids (\u2264256, no spaces
|
|
6543
|
+
or commas); an empty --entities clears them. Also free on a re-add.
|
|
6059
6544
|
rm --filter removes every matching document in pages of a thousand: k=v
|
|
6060
6545
|
pairs match metadata; a JSON object takes the full selector \u2014 metadata,
|
|
6061
6546
|
filename, documentIds, externalIdPrefix (the key a job or connector
|
|
@@ -6095,7 +6580,7 @@ Examples:
|
|
|
6095
6580
|
remy-admin datasources revectorize --source archive-sample --max-chars 900 --wait
|
|
6096
6581
|
remy-admin datasources eval run --set <setId> --candidate --label small-chunks --wait
|
|
6097
6582
|
remy-admin datasources eval compare <runA> <runB>
|
|
6098
|
-
${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceEvalsHelp}`;
|
|
6583
|
+
${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceReadersHelp}${dataSourceEvalsHelp}${dataSourceDictionaryHelp}`;
|
|
6099
6584
|
|
|
6100
6585
|
// src/ops/voice.ts
|
|
6101
6586
|
function normalizeE164(input) {
|
|
@@ -7932,21 +8417,21 @@ Examples:
|
|
|
7932
8417
|
// src/ops/infra.ts
|
|
7933
8418
|
var DEFAULT_WAIT_TIMEOUT_MS4 = 10 * 60 * 1e3;
|
|
7934
8419
|
var POLL_MS5 = 3e3;
|
|
7935
|
-
function
|
|
8420
|
+
function base7(appId) {
|
|
7936
8421
|
return `/_internal/v2/apps/${appId}/infra`;
|
|
7937
8422
|
}
|
|
7938
8423
|
function list14(ctx, params) {
|
|
7939
8424
|
return call(
|
|
7940
8425
|
ctx,
|
|
7941
8426
|
"GET",
|
|
7942
|
-
`${
|
|
8427
|
+
`${base7(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
|
|
7943
8428
|
);
|
|
7944
8429
|
}
|
|
7945
8430
|
function get12(ctx, params) {
|
|
7946
8431
|
return call(
|
|
7947
8432
|
ctx,
|
|
7948
8433
|
"GET",
|
|
7949
|
-
`${
|
|
8434
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}`
|
|
7950
8435
|
);
|
|
7951
8436
|
}
|
|
7952
8437
|
function logs(ctx, params) {
|
|
@@ -7961,11 +8446,11 @@ function logs(ctx, params) {
|
|
|
7961
8446
|
return call(
|
|
7962
8447
|
ctx,
|
|
7963
8448
|
"GET",
|
|
7964
|
-
`${
|
|
8449
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
|
|
7965
8450
|
);
|
|
7966
8451
|
}
|
|
7967
8452
|
function provision(ctx, params) {
|
|
7968
|
-
return call(ctx, "POST",
|
|
8453
|
+
return call(ctx, "POST", base7(ctx.appId), {
|
|
7969
8454
|
offeringId: params.offeringId,
|
|
7970
8455
|
...params.name ? { name: params.name } : {}
|
|
7971
8456
|
});
|
|
@@ -7974,28 +8459,28 @@ function hibernate(ctx, params) {
|
|
|
7974
8459
|
return call(
|
|
7975
8460
|
ctx,
|
|
7976
8461
|
"POST",
|
|
7977
|
-
`${
|
|
8462
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
|
|
7978
8463
|
);
|
|
7979
8464
|
}
|
|
7980
8465
|
function resume2(ctx, params) {
|
|
7981
8466
|
return call(
|
|
7982
8467
|
ctx,
|
|
7983
8468
|
"POST",
|
|
7984
|
-
`${
|
|
8469
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
|
|
7985
8470
|
);
|
|
7986
8471
|
}
|
|
7987
8472
|
function destroy(ctx, params) {
|
|
7988
8473
|
return call(
|
|
7989
8474
|
ctx,
|
|
7990
8475
|
"POST",
|
|
7991
|
-
`${
|
|
8476
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
|
|
7992
8477
|
);
|
|
7993
8478
|
}
|
|
7994
8479
|
function resize(ctx, params) {
|
|
7995
8480
|
return call(
|
|
7996
8481
|
ctx,
|
|
7997
8482
|
"POST",
|
|
7998
|
-
`${
|
|
8483
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resize`,
|
|
7999
8484
|
{ offeringId: params.offeringId }
|
|
8000
8485
|
);
|
|
8001
8486
|
}
|
|
@@ -8003,7 +8488,7 @@ function rename(ctx, params) {
|
|
|
8003
8488
|
return call(
|
|
8004
8489
|
ctx,
|
|
8005
8490
|
"POST",
|
|
8006
|
-
`${
|
|
8491
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
|
|
8007
8492
|
{ name: params.name }
|
|
8008
8493
|
);
|
|
8009
8494
|
}
|
|
@@ -8572,7 +9057,9 @@ var SPECS = {
|
|
|
8572
9057
|
...dataSourceJobsSpecs,
|
|
8573
9058
|
...dataSourceConnectorsSpecs,
|
|
8574
9059
|
...dataSourceEvalsSpecs,
|
|
9060
|
+
...dataSourceDictionarySpecs,
|
|
8575
9061
|
...dataSourceMappersSpecs,
|
|
9062
|
+
...dataSourceReadersSpecs,
|
|
8576
9063
|
...voiceSpecs,
|
|
8577
9064
|
...agentSpecs,
|
|
8578
9065
|
...tasksSpecs,
|
|
@@ -8603,7 +9090,9 @@ var HANDLERS = {
|
|
|
8603
9090
|
...dataSourceJobsHandlers,
|
|
8604
9091
|
...dataSourceConnectorsHandlers,
|
|
8605
9092
|
...dataSourceEvalsHandlers,
|
|
9093
|
+
...dataSourceDictionaryHandlers,
|
|
8606
9094
|
...dataSourceMappersHandlers,
|
|
9095
|
+
...dataSourceReadersHandlers,
|
|
8607
9096
|
...voiceHandlers,
|
|
8608
9097
|
...agentHandlers,
|
|
8609
9098
|
...tasksHandlers,
|
|
@@ -8644,8 +9133,8 @@ var GROUP_HELP = {
|
|
|
8644
9133
|
};
|
|
8645
9134
|
|
|
8646
9135
|
// src/ctx.ts
|
|
8647
|
-
import
|
|
8648
|
-
import
|
|
9136
|
+
import fs7 from "fs";
|
|
9137
|
+
import path7 from "path";
|
|
8649
9138
|
|
|
8650
9139
|
// src/parseJsonConfig.ts
|
|
8651
9140
|
import JSON5 from "json5";
|
|
@@ -8674,10 +9163,10 @@ function parseJsonConfig(raw) {
|
|
|
8674
9163
|
// src/ctx.ts
|
|
8675
9164
|
var DEFAULT_BASE_URL = "https://api.mindstudio.ai";
|
|
8676
9165
|
function loadWorkspaceAppId(workspaceDir = WORKSPACE_DIR) {
|
|
8677
|
-
const manifestPath =
|
|
9166
|
+
const manifestPath = path7.join(workspaceDir, "mindstudio.json");
|
|
8678
9167
|
let raw;
|
|
8679
9168
|
try {
|
|
8680
|
-
raw =
|
|
9169
|
+
raw = fs7.readFileSync(manifestPath, "utf-8");
|
|
8681
9170
|
} catch (err) {
|
|
8682
9171
|
if (err.code === "ENOENT") {
|
|
8683
9172
|
throw new Error(`mindstudio.json not found at ${manifestPath}`);
|