@stackbone/sdk 0.1.0-alpha.1 → 0.1.0-alpha.2
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/CHANGELOG.md +43 -0
- package/README.md +13 -12
- package/index.cjs +167 -231
- package/index.cjs.map +1 -1
- package/index.d.cts +64 -7
- package/index.d.ts +64 -7
- package/index.js +167 -231
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/stackbone-sdk-0.1.0-alpha.2.tgz +0 -0
- package/stackbone-sdk-0.1.0-alpha.1.tgz +0 -0
package/index.cjs
CHANGED
|
@@ -1793,9 +1793,9 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
|
|
|
1793
1793
|
var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
|
|
1794
1794
|
var extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;
|
|
1795
1795
|
var guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
|
|
1796
|
-
var uuid = /* @__PURE__ */ __name((
|
|
1797
|
-
if (!
|
|
1798
|
-
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${
|
|
1796
|
+
var uuid = /* @__PURE__ */ __name((version3) => {
|
|
1797
|
+
if (!version3) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
|
|
1798
|
+
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version3}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
|
|
1799
1799
|
}, "uuid");
|
|
1800
1800
|
var uuid4 = /* @__PURE__ */ uuid(4);
|
|
1801
1801
|
var uuid6 = /* @__PURE__ */ uuid(6);
|
|
@@ -14979,10 +14979,10 @@ function fromJSONSchema(schema, params) {
|
|
|
14979
14979
|
if (typeof schema === "boolean") {
|
|
14980
14980
|
return schema ? z.any() : z.never();
|
|
14981
14981
|
}
|
|
14982
|
-
const
|
|
14982
|
+
const version3 = detectVersion(schema, params?.defaultTarget);
|
|
14983
14983
|
const defs = schema.$defs || schema.definitions || {};
|
|
14984
14984
|
const ctx = {
|
|
14985
|
-
version:
|
|
14985
|
+
version: version3,
|
|
14986
14986
|
defs,
|
|
14987
14987
|
refs: /* @__PURE__ */ new Map(),
|
|
14988
14988
|
processing: /* @__PURE__ */ new Set(),
|
|
@@ -16651,6 +16651,34 @@ function pickDefinedAsStrings(source) {
|
|
|
16651
16651
|
}
|
|
16652
16652
|
__name(pickDefinedAsStrings, "pickDefinedAsStrings");
|
|
16653
16653
|
|
|
16654
|
+
// src/lib/facade-helpers.ts
|
|
16655
|
+
function remapHttpNotFound(code, domainCode, message, meta3) {
|
|
16656
|
+
if (code !== "http_not_found") return null;
|
|
16657
|
+
const error48 = {
|
|
16658
|
+
code: domainCode,
|
|
16659
|
+
message
|
|
16660
|
+
};
|
|
16661
|
+
if (meta3) error48.meta = meta3;
|
|
16662
|
+
return err(error48);
|
|
16663
|
+
}
|
|
16664
|
+
__name(remapHttpNotFound, "remapHttpNotFound");
|
|
16665
|
+
function validateStringArray(values, field, errorCode) {
|
|
16666
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
16667
|
+
return err({
|
|
16668
|
+
code: errorCode,
|
|
16669
|
+
message: `\`${field}\` must be a non-empty array.`
|
|
16670
|
+
});
|
|
16671
|
+
}
|
|
16672
|
+
if (values.some((v) => typeof v !== "string" || !v.trim())) {
|
|
16673
|
+
return err({
|
|
16674
|
+
code: errorCode,
|
|
16675
|
+
message: `\`${field}[*]\` must be non-empty strings.`
|
|
16676
|
+
});
|
|
16677
|
+
}
|
|
16678
|
+
return null;
|
|
16679
|
+
}
|
|
16680
|
+
__name(validateStringArray, "validateStringArray");
|
|
16681
|
+
|
|
16654
16682
|
// src/facades/config/config.ts
|
|
16655
16683
|
var ConfigFacade = class {
|
|
16656
16684
|
static {
|
|
@@ -16672,7 +16700,11 @@ var ConfigFacade = class {
|
|
|
16672
16700
|
const gateResult = await this._gate();
|
|
16673
16701
|
if (gateResult.error) return err(gateResult.error);
|
|
16674
16702
|
const result = await this._http.get(`/api/config/${encodeURIComponent(key)}`);
|
|
16675
|
-
if (result.error)
|
|
16703
|
+
if (result.error) {
|
|
16704
|
+
return remapHttpNotFound(result.error.code, "config_not_found", `Config key \`${key}\` is not set for this agent.`, {
|
|
16705
|
+
key
|
|
16706
|
+
}) ?? result;
|
|
16707
|
+
}
|
|
16676
16708
|
return ok(result.data.value);
|
|
16677
16709
|
}
|
|
16678
16710
|
/**
|
|
@@ -16682,18 +16714,8 @@ var ConfigFacade = class {
|
|
|
16682
16714
|
* is `Partial<T>`).
|
|
16683
16715
|
*/
|
|
16684
16716
|
async getMany(keys) {
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
code: "config_invalid_request",
|
|
16688
|
-
message: "`keys` must be a non-empty array."
|
|
16689
|
-
});
|
|
16690
|
-
}
|
|
16691
|
-
if (keys.some((k) => !k.trim())) {
|
|
16692
|
-
return err({
|
|
16693
|
-
code: "config_invalid_request",
|
|
16694
|
-
message: "`keys[*]` must be non-empty strings."
|
|
16695
|
-
});
|
|
16696
|
-
}
|
|
16717
|
+
const invalid = validateStringArray(keys, "keys", "config_invalid_request");
|
|
16718
|
+
if (invalid) return invalid;
|
|
16697
16719
|
const gateResult = await this._gate();
|
|
16698
16720
|
if (gateResult.error) return err(gateResult.error);
|
|
16699
16721
|
const result = await this._http.get("/api/config", {
|
|
@@ -16712,17 +16734,6 @@ var ConfigFacade = class {
|
|
|
16712
16734
|
return ok(values);
|
|
16713
16735
|
}
|
|
16714
16736
|
};
|
|
16715
|
-
function remapNotFound(code, key) {
|
|
16716
|
-
if (code !== "http_not_found") return null;
|
|
16717
|
-
return err({
|
|
16718
|
-
code: "config_not_found",
|
|
16719
|
-
message: `Config key \`${key}\` is not set for this agent.`,
|
|
16720
|
-
meta: {
|
|
16721
|
-
key
|
|
16722
|
-
}
|
|
16723
|
-
});
|
|
16724
|
-
}
|
|
16725
|
-
__name(remapNotFound, "remapNotFound");
|
|
16726
16737
|
|
|
16727
16738
|
// src/facades/connections/connections.ts
|
|
16728
16739
|
var ConnectionsFacade = class {
|
|
@@ -16808,7 +16819,11 @@ var SecretsFacade = class {
|
|
|
16808
16819
|
const gateResult = await this._gate();
|
|
16809
16820
|
if (gateResult.error) return err(gateResult.error);
|
|
16810
16821
|
const result = await this._http.get(`/api/secrets/${encodeURIComponent(name)}`);
|
|
16811
|
-
if (result.error)
|
|
16822
|
+
if (result.error) {
|
|
16823
|
+
return remapHttpNotFound(result.error.code, "secrets_not_found", `Secret \`${name}\` is not registered for this organization.`, {
|
|
16824
|
+
name
|
|
16825
|
+
}) ?? result;
|
|
16826
|
+
}
|
|
16812
16827
|
if (typeof result.data.value !== "string") {
|
|
16813
16828
|
return err({
|
|
16814
16829
|
code: "secrets_invalid_response",
|
|
@@ -16821,23 +16836,13 @@ var SecretsFacade = class {
|
|
|
16821
16836
|
return ok(result.data.value);
|
|
16822
16837
|
}
|
|
16823
16838
|
/**
|
|
16824
|
-
* Names absent from the
|
|
16839
|
+
* Names absent from the organization come back as omissions in the response —
|
|
16825
16840
|
* the returned map only contains entries the control plane actually has.
|
|
16826
16841
|
* Callers that need "all-or-nothing" semantics should diff the keys.
|
|
16827
16842
|
*/
|
|
16828
16843
|
async getMany(names) {
|
|
16829
|
-
|
|
16830
|
-
|
|
16831
|
-
code: "secrets_invalid_request",
|
|
16832
|
-
message: "`names` must be a non-empty array."
|
|
16833
|
-
});
|
|
16834
|
-
}
|
|
16835
|
-
if (names.some((n) => !n.trim())) {
|
|
16836
|
-
return err({
|
|
16837
|
-
code: "secrets_invalid_request",
|
|
16838
|
-
message: "`names[*]` must be non-empty strings."
|
|
16839
|
-
});
|
|
16840
|
-
}
|
|
16844
|
+
const invalid = validateStringArray(names, "names", "secrets_invalid_request");
|
|
16845
|
+
if (invalid) return invalid;
|
|
16841
16846
|
const gateResult = await this._gate();
|
|
16842
16847
|
if (gateResult.error) return err(gateResult.error);
|
|
16843
16848
|
const result = await this._http.get("/api/secrets", {
|
|
@@ -16856,17 +16861,6 @@ var SecretsFacade = class {
|
|
|
16856
16861
|
return ok(values);
|
|
16857
16862
|
}
|
|
16858
16863
|
};
|
|
16859
|
-
function remapNotFound2(code, name) {
|
|
16860
|
-
if (code !== "http_not_found") return null;
|
|
16861
|
-
return err({
|
|
16862
|
-
code: "secrets_not_found",
|
|
16863
|
-
message: `Secret \`${name}\` is not registered for this workspace.`,
|
|
16864
|
-
meta: {
|
|
16865
|
-
name
|
|
16866
|
-
}
|
|
16867
|
-
});
|
|
16868
|
-
}
|
|
16869
|
-
__name(remapNotFound2, "remapNotFound");
|
|
16870
16864
|
|
|
16871
16865
|
// src/lib/http-client.ts
|
|
16872
16866
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([
|
|
@@ -17749,6 +17743,11 @@ async function aggregateRunCost(runId, options) {
|
|
|
17749
17743
|
};
|
|
17750
17744
|
}
|
|
17751
17745
|
__name(aggregateRunCost, "aggregateRunCost");
|
|
17746
|
+
|
|
17747
|
+
// package.json
|
|
17748
|
+
var version2 = "0.1.0-alpha.2";
|
|
17749
|
+
|
|
17750
|
+
// src/observability/logger.ts
|
|
17752
17751
|
var LOG_LEVELS = {
|
|
17753
17752
|
trace: 10,
|
|
17754
17753
|
debug: 20,
|
|
@@ -17962,7 +17961,7 @@ var OtlpHttpDestination = class OtlpHttpDestination2 {
|
|
|
17962
17961
|
};
|
|
17963
17962
|
var SDK_SCOPE = {
|
|
17964
17963
|
name: "@stackbone/sdk",
|
|
17965
|
-
version:
|
|
17964
|
+
version: version2
|
|
17966
17965
|
};
|
|
17967
17966
|
function buildOtlpLogsBody(records, resourceAttributes) {
|
|
17968
17967
|
return {
|
|
@@ -17995,11 +17994,12 @@ function toOtlpLogRecord(record2) {
|
|
|
17995
17994
|
};
|
|
17996
17995
|
}
|
|
17997
17996
|
__name(toOtlpLogRecord, "toOtlpLogRecord");
|
|
17997
|
+
var SEVERITY_TEXT_BY_LEVEL = Object.fromEntries(Object.entries(LOG_LEVELS).map(([name, value]) => [
|
|
17998
|
+
value,
|
|
17999
|
+
name.toUpperCase()
|
|
18000
|
+
]));
|
|
17998
18001
|
function severityText(level) {
|
|
17999
|
-
|
|
18000
|
-
if (value === level) return name.toUpperCase();
|
|
18001
|
-
}
|
|
18002
|
-
return "INFO";
|
|
18002
|
+
return SEVERITY_TEXT_BY_LEVEL[level] ?? "INFO";
|
|
18003
18003
|
}
|
|
18004
18004
|
__name(severityText, "severityText");
|
|
18005
18005
|
function toOtlpAttributes(attrs) {
|
|
@@ -18167,11 +18167,11 @@ var RunStepsSpanProcessor = class {
|
|
|
18167
18167
|
}
|
|
18168
18168
|
}
|
|
18169
18169
|
buildRow(span) {
|
|
18170
|
-
const runId = readString(span.attributes[RUN_ID_ATTRIBUTE]);
|
|
18171
|
-
if (!runId) return null;
|
|
18172
18170
|
const spanId = span.spanContext().spanId;
|
|
18173
18171
|
const stepId = this.stepIdBySpanId.get(spanId) ?? this.newId();
|
|
18174
18172
|
this.stepIdBySpanId.delete(spanId);
|
|
18173
|
+
const runId = readString(span.attributes[RUN_ID_ATTRIBUTE]);
|
|
18174
|
+
if (!runId) return null;
|
|
18175
18175
|
const parentSpanId = span.parentSpanContext?.spanId ?? span.parentSpanId ?? null;
|
|
18176
18176
|
const parentStepId = parentSpanId ? this.stepIdBySpanId.get(parentSpanId) ?? null : null;
|
|
18177
18177
|
const startedAt = formatHrTime(span.startTime);
|
|
@@ -18621,6 +18621,41 @@ function isUnsupportedModelError(error48) {
|
|
|
18621
18621
|
}
|
|
18622
18622
|
__name(isUnsupportedModelError, "isUnsupportedModelError");
|
|
18623
18623
|
|
|
18624
|
+
// src/modules/rag/errors.ts
|
|
18625
|
+
var PG_UNDEFINED_TABLE = "42P01";
|
|
18626
|
+
var RAG_SCHEMA_MISSING_HINT = "run `stackbone db migrate add-rag` and `stackbone db migrate up`";
|
|
18627
|
+
function isTaggedError(cause) {
|
|
18628
|
+
return cause instanceof Error;
|
|
18629
|
+
}
|
|
18630
|
+
__name(isTaggedError, "isTaggedError");
|
|
18631
|
+
function toRagError(cause, message, fallbackCode = "rag_error") {
|
|
18632
|
+
if (isTaggedError(cause) && cause.code === PG_UNDEFINED_TABLE) {
|
|
18633
|
+
return {
|
|
18634
|
+
code: "rag_schema_missing",
|
|
18635
|
+
message: `RAG tables are not installed \u2014 ${RAG_SCHEMA_MISSING_HINT}.`,
|
|
18636
|
+
cause,
|
|
18637
|
+
meta: {
|
|
18638
|
+
hint: RAG_SCHEMA_MISSING_HINT
|
|
18639
|
+
}
|
|
18640
|
+
};
|
|
18641
|
+
}
|
|
18642
|
+
if (isTaggedError(cause)) {
|
|
18643
|
+
const error48 = {
|
|
18644
|
+
code: cause.code ?? fallbackCode,
|
|
18645
|
+
message: `${message}: ${cause.message}`,
|
|
18646
|
+
cause
|
|
18647
|
+
};
|
|
18648
|
+
if (cause.meta) error48.meta = cause.meta;
|
|
18649
|
+
return error48;
|
|
18650
|
+
}
|
|
18651
|
+
return {
|
|
18652
|
+
code: fallbackCode,
|
|
18653
|
+
message,
|
|
18654
|
+
cause
|
|
18655
|
+
};
|
|
18656
|
+
}
|
|
18657
|
+
__name(toRagError, "toRagError");
|
|
18658
|
+
|
|
18624
18659
|
// src/modules/rag/jobs.ts
|
|
18625
18660
|
function createSqlJobWriter(sql) {
|
|
18626
18661
|
return {
|
|
@@ -18646,7 +18681,7 @@ function createSqlJobWriter(sql) {
|
|
|
18646
18681
|
jobId: row.id
|
|
18647
18682
|
});
|
|
18648
18683
|
} catch (cause) {
|
|
18649
|
-
return err(
|
|
18684
|
+
return err(toRagError(cause, "Job start failed", "rag_jobs_error"));
|
|
18650
18685
|
}
|
|
18651
18686
|
},
|
|
18652
18687
|
async progress({ jobId, processedChunks, totalChunks, currentDocument }) {
|
|
@@ -18685,7 +18720,7 @@ function createSqlJobWriter(sql) {
|
|
|
18685
18720
|
},
|
|
18686
18721
|
async isCancelled({ jobId }) {
|
|
18687
18722
|
const rows = await sql`
|
|
18688
|
-
SELECT
|
|
18723
|
+
SELECT status FROM stackbone_rag_jobs WHERE id = ${jobId} LIMIT 1
|
|
18689
18724
|
`;
|
|
18690
18725
|
return rows[0]?.status === "cancelled";
|
|
18691
18726
|
}
|
|
@@ -18693,10 +18728,6 @@ function createSqlJobWriter(sql) {
|
|
|
18693
18728
|
}
|
|
18694
18729
|
__name(createSqlJobWriter, "createSqlJobWriter");
|
|
18695
18730
|
async function ensureCollection(sql, name) {
|
|
18696
|
-
const existing = await sql`
|
|
18697
|
-
SELECT id FROM stackbone_rag_collections WHERE name = ${name} LIMIT 1
|
|
18698
|
-
`;
|
|
18699
|
-
if (existing[0]) return existing[0].id;
|
|
18700
18731
|
const inserted = await sql`
|
|
18701
18732
|
INSERT INTO stackbone_rag_collections (name) VALUES (${name})
|
|
18702
18733
|
ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
|
|
@@ -18704,37 +18735,11 @@ async function ensureCollection(sql, name) {
|
|
|
18704
18735
|
`;
|
|
18705
18736
|
const row = inserted[0];
|
|
18706
18737
|
if (!row) {
|
|
18707
|
-
throw new Error(`stackbone_rag_collections
|
|
18738
|
+
throw new Error(`stackbone_rag_collections upsert returned no row for ${name}`);
|
|
18708
18739
|
}
|
|
18709
18740
|
return row.id;
|
|
18710
18741
|
}
|
|
18711
18742
|
__name(ensureCollection, "ensureCollection");
|
|
18712
|
-
function toJobsError(cause, message) {
|
|
18713
|
-
if (cause instanceof Error) {
|
|
18714
|
-
const tagged = cause;
|
|
18715
|
-
if (tagged.code === "42P01") {
|
|
18716
|
-
return {
|
|
18717
|
-
code: "rag_schema_missing",
|
|
18718
|
-
message: "RAG tables are not installed \u2014 run `stackbone db migrate add-rag` and `stackbone db migrate up`.",
|
|
18719
|
-
cause,
|
|
18720
|
-
meta: {
|
|
18721
|
-
hint: "run `stackbone db migrate add-rag` and `stackbone db migrate up`"
|
|
18722
|
-
}
|
|
18723
|
-
};
|
|
18724
|
-
}
|
|
18725
|
-
return {
|
|
18726
|
-
code: tagged.code ?? "rag_jobs_error",
|
|
18727
|
-
message: `${message}: ${cause.message}`,
|
|
18728
|
-
cause
|
|
18729
|
-
};
|
|
18730
|
-
}
|
|
18731
|
-
return {
|
|
18732
|
-
code: "rag_jobs_error",
|
|
18733
|
-
message,
|
|
18734
|
-
cause
|
|
18735
|
-
};
|
|
18736
|
-
}
|
|
18737
|
-
__name(toJobsError, "toJobsError");
|
|
18738
18743
|
var PDF_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
18739
18744
|
"application/pdf",
|
|
18740
18745
|
"application/x-pdf"
|
|
@@ -18794,10 +18799,12 @@ __name(looksLikePdf, "looksLikePdf");
|
|
|
18794
18799
|
var DEFAULT_NAMESPACE = "default";
|
|
18795
18800
|
var META_KEY_DIMENSIONS = "dimensions";
|
|
18796
18801
|
var META_KEY_DISTANCE = "distance";
|
|
18802
|
+
var schemaVerifiedDimensions = /* @__PURE__ */ new WeakMap();
|
|
18797
18803
|
async function ensureSchema(sql, dimensions) {
|
|
18798
18804
|
if (!Number.isInteger(dimensions) || dimensions <= 0 || dimensions > 8192) {
|
|
18799
18805
|
throw new Error(`rag.ensureSchema: invalid dimensions ${dimensions}. Must be a positive integer <= 8192.`);
|
|
18800
18806
|
}
|
|
18807
|
+
if (schemaVerifiedDimensions.get(sql) === dimensions) return;
|
|
18801
18808
|
const existing = await sql`
|
|
18802
18809
|
SELECT value::int AS value FROM _rag_meta WHERE key = ${META_KEY_DIMENSIONS}
|
|
18803
18810
|
`.catch(() => []);
|
|
@@ -18812,6 +18819,7 @@ async function ensureSchema(sql, dimensions) {
|
|
|
18812
18819
|
};
|
|
18813
18820
|
throw error48;
|
|
18814
18821
|
}
|
|
18822
|
+
schemaVerifiedDimensions.set(sql, dimensions);
|
|
18815
18823
|
return;
|
|
18816
18824
|
}
|
|
18817
18825
|
await sql.begin(async (tx) => {
|
|
@@ -18831,6 +18839,7 @@ async function ensureSchema(sql, dimensions) {
|
|
|
18831
18839
|
ON CONFLICT (key) DO NOTHING
|
|
18832
18840
|
`;
|
|
18833
18841
|
});
|
|
18842
|
+
schemaVerifiedDimensions.set(sql, dimensions);
|
|
18834
18843
|
}
|
|
18835
18844
|
__name(ensureSchema, "ensureSchema");
|
|
18836
18845
|
async function reset(sql) {
|
|
@@ -18838,29 +18847,31 @@ async function reset(sql) {
|
|
|
18838
18847
|
await tx`DROP TABLE IF EXISTS rag_chunks`;
|
|
18839
18848
|
await tx`DROP TABLE IF EXISTS _rag_meta`;
|
|
18840
18849
|
});
|
|
18850
|
+
schemaVerifiedDimensions.delete(sql);
|
|
18841
18851
|
}
|
|
18842
18852
|
__name(reset, "reset");
|
|
18843
|
-
async function
|
|
18853
|
+
async function clearDocument(sql, docId, namespace) {
|
|
18854
|
+
await sql`
|
|
18855
|
+
DELETE FROM rag_chunks
|
|
18856
|
+
WHERE namespace = ${namespace} AND doc_id = ${docId}
|
|
18857
|
+
`;
|
|
18858
|
+
}
|
|
18859
|
+
__name(clearDocument, "clearDocument");
|
|
18860
|
+
async function ingestDocument(sql, rows) {
|
|
18844
18861
|
if (rows.length === 0) return 0;
|
|
18845
|
-
|
|
18846
|
-
|
|
18847
|
-
|
|
18848
|
-
|
|
18849
|
-
|
|
18850
|
-
|
|
18851
|
-
|
|
18852
|
-
|
|
18853
|
-
|
|
18854
|
-
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
row.content,
|
|
18859
|
-
formatVector(row.embedding),
|
|
18860
|
-
JSON.stringify(row.metadata)
|
|
18861
|
-
]);
|
|
18862
|
-
await tx.unsafe(`INSERT INTO rag_chunks (doc_id, chunk_idx, namespace, content, embedding, metadata) VALUES ${placeholders}`, params);
|
|
18863
|
-
});
|
|
18862
|
+
const placeholders = rows.map((_, i) => {
|
|
18863
|
+
const base = i * 6;
|
|
18864
|
+
return `($${base + 1}, $${base + 2}, $${base + 3}, $${base + 4}, $${base + 5}::vector, $${base + 6}::jsonb)`;
|
|
18865
|
+
}).join(",");
|
|
18866
|
+
const params = rows.flatMap((row) => [
|
|
18867
|
+
row.docId,
|
|
18868
|
+
row.chunkIdx,
|
|
18869
|
+
row.namespace,
|
|
18870
|
+
row.content,
|
|
18871
|
+
formatVector(row.embedding),
|
|
18872
|
+
JSON.stringify(row.metadata)
|
|
18873
|
+
]);
|
|
18874
|
+
await sql.unsafe(`INSERT INTO rag_chunks (doc_id, chunk_idx, namespace, content, embedding, metadata) VALUES ${placeholders}`, params);
|
|
18864
18875
|
return rows.length;
|
|
18865
18876
|
}
|
|
18866
18877
|
__name(ingestDocument, "ingestDocument");
|
|
@@ -18930,8 +18941,6 @@ var CHUNK_INDEXES_DDL = `
|
|
|
18930
18941
|
|
|
18931
18942
|
// src/modules/rag/pipeline.ts
|
|
18932
18943
|
var DEFAULT_TOP_K = 5;
|
|
18933
|
-
var RAG_SCHEMA_MISSING_HINT = "run `stackbone db migrate add-rag` and `stackbone db migrate up`";
|
|
18934
|
-
var PG_UNDEFINED_TABLE = "42P01";
|
|
18935
18944
|
var RagPipeline = class {
|
|
18936
18945
|
static {
|
|
18937
18946
|
__name(this, "RagPipeline");
|
|
@@ -18996,19 +19005,16 @@ var RagPipeline = class {
|
|
|
18996
19005
|
if (started.error) return err(started.error);
|
|
18997
19006
|
jobId = started.data.jobId;
|
|
18998
19007
|
}
|
|
18999
|
-
|
|
19000
|
-
if (onProgress) await onProgress(event);
|
|
19001
|
-
}, "emit");
|
|
19002
|
-
const startEvent = {
|
|
19008
|
+
await onProgress?.({
|
|
19003
19009
|
type: "started",
|
|
19004
19010
|
jobId: jobId ?? request.id,
|
|
19005
19011
|
totalChunks
|
|
19006
|
-
};
|
|
19007
|
-
await emit(startEvent);
|
|
19012
|
+
});
|
|
19008
19013
|
try {
|
|
19009
19014
|
await ensureSchema(sql.data, dimensions);
|
|
19010
19015
|
const needsBatching = Boolean(writer || onProgress);
|
|
19011
19016
|
const batchSize = this._progressBatchSize ?? (needsBatching ? 1 : rows.length || 1);
|
|
19017
|
+
await clearDocument(sql.data, request.id, namespace);
|
|
19012
19018
|
let processed = 0;
|
|
19013
19019
|
for (let offset = 0; offset < rows.length; offset += batchSize) {
|
|
19014
19020
|
if (writer && jobId) {
|
|
@@ -19028,37 +19034,33 @@ var RagPipeline = class {
|
|
|
19028
19034
|
}
|
|
19029
19035
|
}
|
|
19030
19036
|
const slice = rows.slice(offset, offset + batchSize);
|
|
19031
|
-
await ingestDocument(sql.data,
|
|
19037
|
+
await ingestDocument(sql.data, slice);
|
|
19032
19038
|
processed += slice.length;
|
|
19033
|
-
const
|
|
19039
|
+
const currentDocument = {
|
|
19040
|
+
source: request.id,
|
|
19041
|
+
ordinal: processed - 1
|
|
19042
|
+
};
|
|
19043
|
+
await onProgress?.({
|
|
19034
19044
|
type: "progress",
|
|
19035
19045
|
jobId: jobId ?? request.id,
|
|
19036
19046
|
processedChunks: processed,
|
|
19037
19047
|
totalChunks,
|
|
19038
|
-
currentDocument
|
|
19039
|
-
|
|
19040
|
-
ordinal: processed - 1
|
|
19041
|
-
}
|
|
19042
|
-
};
|
|
19043
|
-
await emit(progressEvent);
|
|
19048
|
+
currentDocument
|
|
19049
|
+
});
|
|
19044
19050
|
if (writer && jobId) {
|
|
19045
19051
|
await writer.progress({
|
|
19046
19052
|
jobId,
|
|
19047
19053
|
processedChunks: processed,
|
|
19048
19054
|
totalChunks,
|
|
19049
|
-
currentDocument
|
|
19050
|
-
source: request.id,
|
|
19051
|
-
ordinal: processed - 1
|
|
19052
|
-
}
|
|
19055
|
+
currentDocument
|
|
19053
19056
|
});
|
|
19054
19057
|
}
|
|
19055
19058
|
}
|
|
19056
|
-
|
|
19059
|
+
await onProgress?.({
|
|
19057
19060
|
type: "completed",
|
|
19058
19061
|
jobId: jobId ?? request.id,
|
|
19059
19062
|
totalChunks
|
|
19060
|
-
};
|
|
19061
|
-
await emit(completedEvent);
|
|
19063
|
+
});
|
|
19062
19064
|
if (writer && jobId) await writer.complete({
|
|
19063
19065
|
jobId,
|
|
19064
19066
|
totalChunks
|
|
@@ -19069,12 +19071,11 @@ var RagPipeline = class {
|
|
|
19069
19071
|
});
|
|
19070
19072
|
} catch (cause) {
|
|
19071
19073
|
const mapped = toRagError(cause, "Ingest failed");
|
|
19072
|
-
|
|
19074
|
+
await onProgress?.({
|
|
19073
19075
|
type: "failed",
|
|
19074
19076
|
jobId: jobId ?? request.id,
|
|
19075
19077
|
error: mapped.message
|
|
19076
|
-
};
|
|
19077
|
-
await emit(failedEvent);
|
|
19078
|
+
});
|
|
19078
19079
|
if (writer && jobId) await writer.fail({
|
|
19079
19080
|
jobId,
|
|
19080
19081
|
error: mapped.message
|
|
@@ -19234,37 +19235,6 @@ function validateIngest(request, hasEmbedder) {
|
|
|
19234
19235
|
return ok(true);
|
|
19235
19236
|
}
|
|
19236
19237
|
__name(validateIngest, "validateIngest");
|
|
19237
|
-
function isTaggedError(cause) {
|
|
19238
|
-
return cause instanceof Error;
|
|
19239
|
-
}
|
|
19240
|
-
__name(isTaggedError, "isTaggedError");
|
|
19241
|
-
function toRagError(cause, message) {
|
|
19242
|
-
if (isTaggedError(cause) && cause.code === PG_UNDEFINED_TABLE) {
|
|
19243
|
-
return {
|
|
19244
|
-
code: "rag_schema_missing",
|
|
19245
|
-
message: `RAG tables are not installed \u2014 ${RAG_SCHEMA_MISSING_HINT}.`,
|
|
19246
|
-
cause,
|
|
19247
|
-
meta: {
|
|
19248
|
-
hint: RAG_SCHEMA_MISSING_HINT
|
|
19249
|
-
}
|
|
19250
|
-
};
|
|
19251
|
-
}
|
|
19252
|
-
if (isTaggedError(cause)) {
|
|
19253
|
-
const error48 = {
|
|
19254
|
-
code: cause.code ?? "rag_error",
|
|
19255
|
-
message: `${message}: ${cause.message}`,
|
|
19256
|
-
cause
|
|
19257
|
-
};
|
|
19258
|
-
if (cause.meta) error48.meta = cause.meta;
|
|
19259
|
-
return error48;
|
|
19260
|
-
}
|
|
19261
|
-
return {
|
|
19262
|
-
code: "rag_error",
|
|
19263
|
-
message,
|
|
19264
|
-
cause
|
|
19265
|
-
};
|
|
19266
|
-
}
|
|
19267
|
-
__name(toRagError, "toRagError");
|
|
19268
19238
|
|
|
19269
19239
|
// src/modules/rag/rag.ts
|
|
19270
19240
|
var DEFAULT_BATCH_SIZE4 = 128;
|
|
@@ -19276,10 +19246,14 @@ var RagModule = class {
|
|
|
19276
19246
|
_resolved;
|
|
19277
19247
|
_getAi;
|
|
19278
19248
|
_gate;
|
|
19279
|
-
|
|
19249
|
+
_testSqlOverride;
|
|
19250
|
+
_testJobWriterFactory;
|
|
19251
|
+
constructor(_resolved, _getAi, gate, testDeps) {
|
|
19280
19252
|
this._resolved = _resolved;
|
|
19281
19253
|
this._getAi = _getAi;
|
|
19282
19254
|
this._gate = gate ?? createModuleGate("rag", _resolved);
|
|
19255
|
+
if (testDeps?.sqlProvider) this._testSqlOverride = testDeps.sqlProvider;
|
|
19256
|
+
if (testDeps?.jobWriterFactory) this._testJobWriterFactory = testDeps.jobWriterFactory;
|
|
19283
19257
|
}
|
|
19284
19258
|
async ingest(request) {
|
|
19285
19259
|
const gateResult = await this._gate();
|
|
@@ -19312,7 +19286,8 @@ var RagModule = class {
|
|
|
19312
19286
|
if (gateResult.error) return err(gateResult.error);
|
|
19313
19287
|
const sql = this._testSqlOverride ? this._testSqlOverride() : this.sql();
|
|
19314
19288
|
if (sql.error) return err(sql.error);
|
|
19315
|
-
const
|
|
19289
|
+
const writerFactory = this._testJobWriterFactory ?? createSqlJobWriter;
|
|
19290
|
+
const writer = writerFactory(sql.data);
|
|
19316
19291
|
const channel = createProgressChannel();
|
|
19317
19292
|
const resolved = resolveIngestModel(request, this._resolved);
|
|
19318
19293
|
const requestWithHook = {
|
|
@@ -19343,7 +19318,7 @@ var RagModule = class {
|
|
|
19343
19318
|
return r;
|
|
19344
19319
|
}).catch((cause) => {
|
|
19345
19320
|
channel.close();
|
|
19346
|
-
return err(
|
|
19321
|
+
return err(toRagError(cause, "Ingest failed"));
|
|
19347
19322
|
});
|
|
19348
19323
|
return ok({
|
|
19349
19324
|
jobId,
|
|
@@ -19351,10 +19326,6 @@ var RagModule = class {
|
|
|
19351
19326
|
result
|
|
19352
19327
|
});
|
|
19353
19328
|
}
|
|
19354
|
-
/** Test-only seam — assigned by `ingest-async.spec.ts`. */
|
|
19355
|
-
_testJobWriter;
|
|
19356
|
-
/** Test-only seam — assigned by `ingest-async.spec.ts`. */
|
|
19357
|
-
_testSqlOverride;
|
|
19358
19329
|
async delete(ids, options) {
|
|
19359
19330
|
const gateResult = await this._gate();
|
|
19360
19331
|
if (gateResult.error) return err(gateResult.error);
|
|
@@ -19386,7 +19357,7 @@ var RagModule = class {
|
|
|
19386
19357
|
await reset(sql.data);
|
|
19387
19358
|
return ok(void 0);
|
|
19388
19359
|
} catch (cause) {
|
|
19389
|
-
return err(
|
|
19360
|
+
return err(toRagError(cause, "Reset failed"));
|
|
19390
19361
|
}
|
|
19391
19362
|
}
|
|
19392
19363
|
/**
|
|
@@ -19414,22 +19385,18 @@ var RagModule = class {
|
|
|
19414
19385
|
* `Result.err` with the same `database_not_configured` shape `client.database`
|
|
19415
19386
|
* raises, instead of throwing through the public surface.
|
|
19416
19387
|
*/
|
|
19417
|
-
// `_resolved` is reserved for future per-config overrides (e.g. routing
|
|
19418
|
-
// RAG queries through a logical replica). The URL itself is owned by the
|
|
19419
|
-
// shared handle.
|
|
19420
19388
|
sql() {
|
|
19421
|
-
void this._resolved;
|
|
19422
19389
|
try {
|
|
19423
19390
|
return ok(getDatabaseHandle().$client);
|
|
19424
19391
|
} catch (cause) {
|
|
19425
|
-
if (
|
|
19392
|
+
if (isTaggedError(cause) && cause.code === "database_not_configured") {
|
|
19426
19393
|
return err({
|
|
19427
19394
|
code: "database_not_configured",
|
|
19428
19395
|
message: cause.message,
|
|
19429
19396
|
cause
|
|
19430
19397
|
});
|
|
19431
19398
|
}
|
|
19432
|
-
return err(
|
|
19399
|
+
return err(toRagError(cause, "Database handle unavailable"));
|
|
19433
19400
|
}
|
|
19434
19401
|
}
|
|
19435
19402
|
async withPipeline(embedder, run) {
|
|
@@ -19550,27 +19517,6 @@ function resolveRetrieveModel(request, resolved) {
|
|
|
19550
19517
|
};
|
|
19551
19518
|
}
|
|
19552
19519
|
__name(resolveRetrieveModel, "resolveRetrieveModel");
|
|
19553
|
-
function isTaggedError2(cause) {
|
|
19554
|
-
return cause instanceof Error;
|
|
19555
|
-
}
|
|
19556
|
-
__name(isTaggedError2, "isTaggedError");
|
|
19557
|
-
function toRagError2(cause, message) {
|
|
19558
|
-
if (isTaggedError2(cause)) {
|
|
19559
|
-
const error48 = {
|
|
19560
|
-
code: cause.code ?? "rag_error",
|
|
19561
|
-
message: `${message}: ${cause.message}`,
|
|
19562
|
-
cause
|
|
19563
|
-
};
|
|
19564
|
-
if (cause.meta) error48.meta = cause.meta;
|
|
19565
|
-
return error48;
|
|
19566
|
-
}
|
|
19567
|
-
return {
|
|
19568
|
-
code: "rag_error",
|
|
19569
|
-
message,
|
|
19570
|
-
cause
|
|
19571
|
-
};
|
|
19572
|
-
}
|
|
19573
|
-
__name(toRagError2, "toRagError");
|
|
19574
19520
|
var DEFAULT_SIGNED_URL_TTL_SECONDS = 3600;
|
|
19575
19521
|
var StorageModule = class {
|
|
19576
19522
|
static {
|
|
@@ -19772,29 +19718,19 @@ var StorageBucket = class {
|
|
|
19772
19718
|
return ok(`${base}/${bucket}/${encodeS3Key(fullKey)}`);
|
|
19773
19719
|
}
|
|
19774
19720
|
async getSignedUploadUrl(key, options) {
|
|
19775
|
-
|
|
19776
|
-
|
|
19777
|
-
|
|
19778
|
-
|
|
19779
|
-
|
|
19780
|
-
const expiresIn = options?.expiresIn ?? DEFAULT_SIGNED_URL_TTL_SECONDS;
|
|
19781
|
-
try {
|
|
19782
|
-
const url2 = await s3RequestPresigner.getSignedUrl(client, new clientS3.PutObjectCommand({
|
|
19783
|
-
Bucket: bucket,
|
|
19784
|
-
Key: fullKey,
|
|
19785
|
-
ContentType: options?.contentType
|
|
19786
|
-
}), {
|
|
19787
|
-
expiresIn
|
|
19788
|
-
});
|
|
19789
|
-
return ok({
|
|
19790
|
-
url: url2,
|
|
19791
|
-
expiresAt: new Date(Date.now() + expiresIn * 1e3)
|
|
19792
|
-
});
|
|
19793
|
-
} catch (cause) {
|
|
19794
|
-
return err(toStorageError("Signing upload URL failed", cause));
|
|
19795
|
-
}
|
|
19721
|
+
return this.signUrl(key, options, "Signing upload URL failed", ({ bucket, fullKey }) => new clientS3.PutObjectCommand({
|
|
19722
|
+
Bucket: bucket,
|
|
19723
|
+
Key: fullKey,
|
|
19724
|
+
ContentType: options?.contentType
|
|
19725
|
+
}));
|
|
19796
19726
|
}
|
|
19797
19727
|
async getSignedDownloadUrl(key, options) {
|
|
19728
|
+
return this.signUrl(key, options, "Signing download URL failed", ({ bucket, fullKey }) => new clientS3.GetObjectCommand({
|
|
19729
|
+
Bucket: bucket,
|
|
19730
|
+
Key: fullKey
|
|
19731
|
+
}));
|
|
19732
|
+
}
|
|
19733
|
+
async signUrl(key, options, failureMessage, buildCommand) {
|
|
19798
19734
|
const gateResult = await this.gate();
|
|
19799
19735
|
if (gateResult.error) return err(gateResult.error);
|
|
19800
19736
|
const resolved = this.resolve(key);
|
|
@@ -19802,9 +19738,9 @@ var StorageBucket = class {
|
|
|
19802
19738
|
const { client, bucket, fullKey } = resolved.data;
|
|
19803
19739
|
const expiresIn = options?.expiresIn ?? DEFAULT_SIGNED_URL_TTL_SECONDS;
|
|
19804
19740
|
try {
|
|
19805
|
-
const url2 = await s3RequestPresigner.getSignedUrl(client,
|
|
19806
|
-
|
|
19807
|
-
|
|
19741
|
+
const url2 = await s3RequestPresigner.getSignedUrl(client, buildCommand({
|
|
19742
|
+
bucket,
|
|
19743
|
+
fullKey
|
|
19808
19744
|
}), {
|
|
19809
19745
|
expiresIn
|
|
19810
19746
|
});
|
|
@@ -19813,7 +19749,7 @@ var StorageBucket = class {
|
|
|
19813
19749
|
expiresAt: new Date(Date.now() + expiresIn * 1e3)
|
|
19814
19750
|
});
|
|
19815
19751
|
} catch (cause) {
|
|
19816
|
-
return err(toStorageError(
|
|
19752
|
+
return err(toStorageError(failureMessage, cause));
|
|
19817
19753
|
}
|
|
19818
19754
|
}
|
|
19819
19755
|
/**
|