@opendatalabs/personal-server-ts-server 1.13.1 → 1.15.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question-store.d.ts","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,
|
|
1
|
+
{"version":3,"file":"question-store.d.ts","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,mDAAmD,CAAC;AAkG3D,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,aAAa,CAwGf"}
|
|
@@ -11,16 +11,44 @@ CREATE TABLE IF NOT EXISTS derivative_questions (
|
|
|
11
11
|
source_scopes TEXT NOT NULL,
|
|
12
12
|
question TEXT NOT NULL,
|
|
13
13
|
model TEXT,
|
|
14
|
+
answer_shape TEXT,
|
|
14
15
|
recompute TEXT NOT NULL DEFAULT 'on-change',
|
|
15
16
|
registered_by TEXT NOT NULL,
|
|
16
17
|
status TEXT NOT NULL,
|
|
17
18
|
error TEXT,
|
|
19
|
+
error_code TEXT,
|
|
18
20
|
created_at TEXT NOT NULL,
|
|
19
21
|
updated_at TEXT NOT NULL,
|
|
20
22
|
last_computed_at TEXT,
|
|
21
23
|
derived_version INTEGER,
|
|
22
24
|
derived_collected_at TEXT
|
|
23
25
|
)`;
|
|
26
|
+
/**
|
|
27
|
+
* Databases created before the column existed migrate in place. ALTER is
|
|
28
|
+
* guarded by a pragma check because SQLite has no ADD COLUMN IF NOT EXISTS;
|
|
29
|
+
* existing rows read back as null (pre-classification failures).
|
|
30
|
+
*/
|
|
31
|
+
function ensureErrorCodeColumn(db) {
|
|
32
|
+
const columns = db
|
|
33
|
+
.prepare("PRAGMA table_info(derivative_questions)")
|
|
34
|
+
.all();
|
|
35
|
+
if (columns.some((column) => column.name === "error_code"))
|
|
36
|
+
return;
|
|
37
|
+
db.exec("ALTER TABLE derivative_questions ADD COLUMN error_code TEXT");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Same in-place migration for the declared answer shape (stored as its
|
|
41
|
+
* JSON): rows written before it existed read back as null, which is the
|
|
42
|
+
* free-text answer the question was registered with.
|
|
43
|
+
*/
|
|
44
|
+
function ensureAnswerShapeColumn(db) {
|
|
45
|
+
const columns = db
|
|
46
|
+
.prepare("PRAGMA table_info(derivative_questions)")
|
|
47
|
+
.all();
|
|
48
|
+
if (columns.some((column) => column.name === "answer_shape"))
|
|
49
|
+
return;
|
|
50
|
+
db.exec("ALTER TABLE derivative_questions ADD COLUMN answer_shape TEXT");
|
|
51
|
+
}
|
|
24
52
|
const CREATE_INDEX_SQL = "CREATE INDEX IF NOT EXISTS idx_derivative_questions_derived_scope ON derivative_questions (derived_scope)";
|
|
25
53
|
// Databases created before the recompute policy existed migrate in place;
|
|
26
54
|
// their registrations keep the old follow-every-change behavior.
|
|
@@ -32,10 +60,14 @@ function toRegistration(row) {
|
|
|
32
60
|
sourceScopes: JSON.parse(row.source_scopes),
|
|
33
61
|
question: row.question,
|
|
34
62
|
model: row.model,
|
|
63
|
+
answerShape: row.answer_shape
|
|
64
|
+
? JSON.parse(row.answer_shape)
|
|
65
|
+
: null,
|
|
35
66
|
recompute: row.recompute,
|
|
36
67
|
registeredBy: JSON.parse(row.registered_by),
|
|
37
68
|
status: row.status,
|
|
38
69
|
error: row.error,
|
|
70
|
+
errorCode: row.error_code ?? null,
|
|
39
71
|
createdAt: row.created_at,
|
|
40
72
|
updatedAt: row.updated_at,
|
|
41
73
|
lastComputedAt: row.last_computed_at,
|
|
@@ -45,6 +77,8 @@ function toRegistration(row) {
|
|
|
45
77
|
}
|
|
46
78
|
export function createSqliteQuestionStore(db) {
|
|
47
79
|
db.exec(CREATE_TABLE_SQL);
|
|
80
|
+
ensureErrorCodeColumn(db);
|
|
81
|
+
ensureAnswerShapeColumn(db);
|
|
48
82
|
db.exec(CREATE_INDEX_SQL);
|
|
49
83
|
const columns = db
|
|
50
84
|
.prepare("PRAGMA table_info(derivative_questions)")
|
|
@@ -53,21 +87,26 @@ export function createSqliteQuestionStore(db) {
|
|
|
53
87
|
db.exec(ADD_RECOMPUTE_COLUMN_SQL);
|
|
54
88
|
}
|
|
55
89
|
const listAll = db.prepare("SELECT * FROM derivative_questions ORDER BY created_at ASC, question_id ASC");
|
|
90
|
+
// The status route polls list({ derivedScope }) on every reader request;
|
|
91
|
+
// this is the query idx_derivative_questions_derived_scope exists for.
|
|
92
|
+
const listByDerivedScope = db.prepare("SELECT * FROM derivative_questions WHERE derived_scope = ? ORDER BY created_at ASC, question_id ASC");
|
|
56
93
|
const getOne = db.prepare("SELECT * FROM derivative_questions WHERE question_id = ?");
|
|
57
94
|
const insertOne = db.prepare(`
|
|
58
95
|
INSERT INTO derivative_questions (
|
|
59
|
-
question_id, derived_scope, source_scopes, question, model,
|
|
60
|
-
registered_by, status, error, created_at, updated_at,
|
|
96
|
+
question_id, derived_scope, source_scopes, question, model, answer_shape,
|
|
97
|
+
recompute, registered_by, status, error, error_code, created_at, updated_at,
|
|
61
98
|
last_computed_at, derived_version, derived_collected_at
|
|
62
99
|
) VALUES (
|
|
63
100
|
@question_id, @derived_scope, @source_scopes, @question, @model,
|
|
64
|
-
@recompute, @registered_by, @status, @error, @
|
|
101
|
+
@answer_shape, @recompute, @registered_by, @status, @error, @error_code,
|
|
102
|
+
@created_at, @updated_at,
|
|
65
103
|
@last_computed_at, @derived_version, @derived_collected_at
|
|
66
104
|
)`);
|
|
67
105
|
const updateOne = db.prepare(`
|
|
68
106
|
UPDATE derivative_questions SET
|
|
69
107
|
status = @status,
|
|
70
108
|
error = @error,
|
|
109
|
+
error_code = @error_code,
|
|
71
110
|
updated_at = @updated_at,
|
|
72
111
|
last_computed_at = @last_computed_at,
|
|
73
112
|
derived_version = @derived_version,
|
|
@@ -76,7 +115,10 @@ export function createSqliteQuestionStore(db) {
|
|
|
76
115
|
const deleteOne = db.prepare("DELETE FROM derivative_questions WHERE question_id = ?");
|
|
77
116
|
return {
|
|
78
117
|
async list(filter) {
|
|
79
|
-
|
|
118
|
+
const rows = filter?.derivedScope !== undefined
|
|
119
|
+
? listByDerivedScope.all(filter.derivedScope)
|
|
120
|
+
: listAll.all();
|
|
121
|
+
return rows
|
|
80
122
|
.map(toRegistration)
|
|
81
123
|
.filter((registration) => matchesQuestionFilter(registration, filter));
|
|
82
124
|
},
|
|
@@ -91,10 +133,14 @@ export function createSqliteQuestionStore(db) {
|
|
|
91
133
|
source_scopes: JSON.stringify(registration.sourceScopes),
|
|
92
134
|
question: registration.question,
|
|
93
135
|
model: registration.model,
|
|
136
|
+
answer_shape: registration.answerShape
|
|
137
|
+
? JSON.stringify(registration.answerShape)
|
|
138
|
+
: null,
|
|
94
139
|
recompute: registration.recompute,
|
|
95
140
|
registered_by: JSON.stringify(registration.registeredBy),
|
|
96
141
|
status: registration.status,
|
|
97
142
|
error: registration.error,
|
|
143
|
+
error_code: registration.errorCode,
|
|
98
144
|
created_at: registration.createdAt,
|
|
99
145
|
updated_at: registration.updatedAt,
|
|
100
146
|
last_computed_at: registration.lastComputedAt,
|
|
@@ -111,6 +157,7 @@ export function createSqliteQuestionStore(db) {
|
|
|
111
157
|
question_id: questionId,
|
|
112
158
|
status: merged.status,
|
|
113
159
|
error: merged.error,
|
|
160
|
+
error_code: merged.errorCode ?? null,
|
|
114
161
|
updated_at: merged.updatedAt,
|
|
115
162
|
last_computed_at: merged.lastComputedAt,
|
|
116
163
|
derived_version: merged.derivedVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question-store.js","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,qBAAqB,
|
|
1
|
+
{"version":3,"file":"question-store.js","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/ps-lite-debug.js
CHANGED
|
@@ -61644,8 +61644,8 @@ var require_multipleOf = __commonJS({
|
|
|
61644
61644
|
const { gen: gen2, data, schemaCode, it: it3 } = cxt;
|
|
61645
61645
|
const prec = it3.opts.multipleOfPrecision;
|
|
61646
61646
|
const res = gen2.let("res");
|
|
61647
|
-
const
|
|
61648
|
-
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${
|
|
61647
|
+
const invalid2 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
|
|
61648
|
+
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid2}))`);
|
|
61649
61649
|
}
|
|
61650
61650
|
};
|
|
61651
61651
|
exports.default = def;
|
|
@@ -67542,8 +67542,8 @@ var require_multipleOf2 = __commonJS({
|
|
|
67542
67542
|
const { gen: gen2, data, schemaCode, it: it3 } = cxt;
|
|
67543
67543
|
const prec = it3.opts.multipleOfPrecision;
|
|
67544
67544
|
const res = gen2.let("res");
|
|
67545
|
-
const
|
|
67546
|
-
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${
|
|
67545
|
+
const invalid2 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
|
|
67546
|
+
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid2}))`);
|
|
67547
67547
|
}
|
|
67548
67548
|
};
|
|
67549
67549
|
exports.default = def;
|
|
@@ -114031,6 +114031,258 @@ async function loadOrCreatePsLiteServerIdentity(params) {
|
|
|
114031
114031
|
return { persisted, account };
|
|
114032
114032
|
}
|
|
114033
114033
|
|
|
114034
|
+
// ../core/dist/derivatives/answer-shape.js
|
|
114035
|
+
var MAX_ANSWER_SHAPE_FIELDS = 16;
|
|
114036
|
+
var MAX_ANSWER_FIELD_NAME_CHARS = 64;
|
|
114037
|
+
var MAX_ANSWER_STRING_CHARS = 4e3;
|
|
114038
|
+
var MAX_ANSWER_ENUM_VALUES = 32;
|
|
114039
|
+
var MAX_ANSWER_ENUM_VALUE_CHARS = 64;
|
|
114040
|
+
var FIELD_NAME = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
114041
|
+
var FIELD_TYPES = [
|
|
114042
|
+
"string",
|
|
114043
|
+
"number",
|
|
114044
|
+
"integer",
|
|
114045
|
+
"boolean",
|
|
114046
|
+
"enum"
|
|
114047
|
+
];
|
|
114048
|
+
var KEYS_BY_TYPE = {
|
|
114049
|
+
string: ["name", "type", "required", "maxLength"],
|
|
114050
|
+
number: ["name", "type", "required", "min", "max"],
|
|
114051
|
+
integer: ["name", "type", "required", "min", "max"],
|
|
114052
|
+
boolean: ["name", "type", "required"],
|
|
114053
|
+
enum: ["name", "type", "required", "values"]
|
|
114054
|
+
};
|
|
114055
|
+
function isRecord4(value) {
|
|
114056
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
114057
|
+
}
|
|
114058
|
+
function invalid(message, details) {
|
|
114059
|
+
throw new DerivativeQuestionInvalidError(message, {
|
|
114060
|
+
field: "answerShape",
|
|
114061
|
+
...details
|
|
114062
|
+
});
|
|
114063
|
+
}
|
|
114064
|
+
function parseFieldName(value, index) {
|
|
114065
|
+
if (typeof value !== "string" || value === "") {
|
|
114066
|
+
invalid(`answerShape.fields[${index}].name must be a non-empty string`, {
|
|
114067
|
+
index
|
|
114068
|
+
});
|
|
114069
|
+
}
|
|
114070
|
+
if (value.length > MAX_ANSWER_FIELD_NAME_CHARS) {
|
|
114071
|
+
invalid(`answerShape.fields[${index}].name is ${value.length} characters; the maximum is ${MAX_ANSWER_FIELD_NAME_CHARS}`, { index, max: MAX_ANSWER_FIELD_NAME_CHARS });
|
|
114072
|
+
}
|
|
114073
|
+
if (!FIELD_NAME.test(value)) {
|
|
114074
|
+
invalid(`answerShape.fields[${index}].name must start with a letter and hold only letters, digits and underscores`, { index });
|
|
114075
|
+
}
|
|
114076
|
+
return value;
|
|
114077
|
+
}
|
|
114078
|
+
function parseBound(value, index, key, integer2) {
|
|
114079
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
114080
|
+
invalid(`answerShape.fields[${index}].${key} must be a finite number`, {
|
|
114081
|
+
index,
|
|
114082
|
+
key
|
|
114083
|
+
});
|
|
114084
|
+
}
|
|
114085
|
+
if (integer2 && !Number.isSafeInteger(value)) {
|
|
114086
|
+
invalid(`answerShape.fields[${index}].${key} must be a safe integer for an integer field`, { index, key });
|
|
114087
|
+
}
|
|
114088
|
+
return value;
|
|
114089
|
+
}
|
|
114090
|
+
function parseEnumValues(value, index) {
|
|
114091
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
114092
|
+
invalid(`answerShape.fields[${index}].values must be a non-empty array of strings`, { index });
|
|
114093
|
+
}
|
|
114094
|
+
if (value.length > MAX_ANSWER_ENUM_VALUES) {
|
|
114095
|
+
invalid(`answerShape.fields[${index}].values lists ${value.length} values; the maximum is ${MAX_ANSWER_ENUM_VALUES}`, { index, max: MAX_ANSWER_ENUM_VALUES });
|
|
114096
|
+
}
|
|
114097
|
+
const values = [];
|
|
114098
|
+
for (const entry of value) {
|
|
114099
|
+
if (typeof entry !== "string" || entry === "") {
|
|
114100
|
+
invalid(`answerShape.fields[${index}].values must hold non-empty strings`, { index });
|
|
114101
|
+
}
|
|
114102
|
+
if (entry.length > MAX_ANSWER_ENUM_VALUE_CHARS) {
|
|
114103
|
+
invalid(`answerShape.fields[${index}].values holds a ${entry.length} character value; the maximum is ${MAX_ANSWER_ENUM_VALUE_CHARS}`, { index, max: MAX_ANSWER_ENUM_VALUE_CHARS });
|
|
114104
|
+
}
|
|
114105
|
+
if (/[\n\r\t]/.test(entry)) {
|
|
114106
|
+
invalid(`answerShape.fields[${index}].values must not hold line breaks or tabs`, { index });
|
|
114107
|
+
}
|
|
114108
|
+
if (values.includes(entry)) {
|
|
114109
|
+
invalid(`answerShape.fields[${index}].values lists the same value twice`, { index });
|
|
114110
|
+
}
|
|
114111
|
+
values.push(entry);
|
|
114112
|
+
}
|
|
114113
|
+
return values;
|
|
114114
|
+
}
|
|
114115
|
+
function parseField(value, index) {
|
|
114116
|
+
if (!isRecord4(value)) {
|
|
114117
|
+
invalid(`answerShape.fields[${index}] must be an object`, { index });
|
|
114118
|
+
}
|
|
114119
|
+
const name = parseFieldName(value.name, index);
|
|
114120
|
+
if (typeof value.type !== "string" || !FIELD_TYPES.includes(value.type)) {
|
|
114121
|
+
invalid(`answerShape.fields[${index}].type must be one of ${FIELD_TYPES.join(", ")}`, { index });
|
|
114122
|
+
}
|
|
114123
|
+
const type = value.type;
|
|
114124
|
+
const allowed = KEYS_BY_TYPE[type];
|
|
114125
|
+
for (const key of Object.keys(value)) {
|
|
114126
|
+
if (!allowed.includes(key)) {
|
|
114127
|
+
invalid(`answerShape.fields[${index}] does not accept "${key}" on a ${type} field`, { index, key: key.slice(0, MAX_ANSWER_FIELD_NAME_CHARS) });
|
|
114128
|
+
}
|
|
114129
|
+
}
|
|
114130
|
+
let required2 = true;
|
|
114131
|
+
if (value.required !== void 0 && value.required !== null) {
|
|
114132
|
+
if (typeof value.required !== "boolean") {
|
|
114133
|
+
invalid(`answerShape.fields[${index}].required must be a boolean`, {
|
|
114134
|
+
index
|
|
114135
|
+
});
|
|
114136
|
+
}
|
|
114137
|
+
required2 = value.required;
|
|
114138
|
+
}
|
|
114139
|
+
const field = { name, type, required: required2 };
|
|
114140
|
+
if (type === "string") {
|
|
114141
|
+
let maxLength = MAX_ANSWER_STRING_CHARS;
|
|
114142
|
+
if (value.maxLength !== void 0 && value.maxLength !== null) {
|
|
114143
|
+
if (typeof value.maxLength !== "number" || !Number.isSafeInteger(value.maxLength) || value.maxLength < 1 || value.maxLength > MAX_ANSWER_STRING_CHARS) {
|
|
114144
|
+
invalid(`answerShape.fields[${index}].maxLength must be an integer between 1 and ${MAX_ANSWER_STRING_CHARS}`, { index, max: MAX_ANSWER_STRING_CHARS });
|
|
114145
|
+
}
|
|
114146
|
+
maxLength = value.maxLength;
|
|
114147
|
+
}
|
|
114148
|
+
field.maxLength = maxLength;
|
|
114149
|
+
}
|
|
114150
|
+
if (type === "number" || type === "integer") {
|
|
114151
|
+
const integer2 = type === "integer";
|
|
114152
|
+
if (value.min !== void 0 && value.min !== null) {
|
|
114153
|
+
field.min = parseBound(value.min, index, "min", integer2);
|
|
114154
|
+
}
|
|
114155
|
+
if (value.max !== void 0 && value.max !== null) {
|
|
114156
|
+
field.max = parseBound(value.max, index, "max", integer2);
|
|
114157
|
+
}
|
|
114158
|
+
if (field.min !== void 0 && field.max !== void 0 && field.min > field.max) {
|
|
114159
|
+
invalid(`answerShape.fields[${index}].min is greater than its max`, {
|
|
114160
|
+
index
|
|
114161
|
+
});
|
|
114162
|
+
}
|
|
114163
|
+
}
|
|
114164
|
+
if (type === "enum") {
|
|
114165
|
+
field.values = parseEnumValues(value.values, index);
|
|
114166
|
+
}
|
|
114167
|
+
return field;
|
|
114168
|
+
}
|
|
114169
|
+
function parseAnswerShapeInput(value) {
|
|
114170
|
+
if (value === void 0 || value === null)
|
|
114171
|
+
return null;
|
|
114172
|
+
if (!isRecord4(value)) {
|
|
114173
|
+
invalid("answerShape must be an object with a fields array", {});
|
|
114174
|
+
}
|
|
114175
|
+
for (const key of Object.keys(value)) {
|
|
114176
|
+
if (key !== "fields") {
|
|
114177
|
+
invalid(`answerShape does not accept "${key}"`, {
|
|
114178
|
+
key: key.slice(0, MAX_ANSWER_FIELD_NAME_CHARS)
|
|
114179
|
+
});
|
|
114180
|
+
}
|
|
114181
|
+
}
|
|
114182
|
+
if (!Array.isArray(value.fields) || value.fields.length === 0) {
|
|
114183
|
+
invalid("answerShape.fields must be a non-empty array", {});
|
|
114184
|
+
}
|
|
114185
|
+
if (value.fields.length > MAX_ANSWER_SHAPE_FIELDS) {
|
|
114186
|
+
invalid(`answerShape.fields lists ${value.fields.length} fields; the maximum is ${MAX_ANSWER_SHAPE_FIELDS}`, { max: MAX_ANSWER_SHAPE_FIELDS });
|
|
114187
|
+
}
|
|
114188
|
+
const fields = [];
|
|
114189
|
+
const names = /* @__PURE__ */ new Set();
|
|
114190
|
+
value.fields.forEach((entry, index) => {
|
|
114191
|
+
const field = parseField(entry, index);
|
|
114192
|
+
if (names.has(field.name)) {
|
|
114193
|
+
invalid(`answerShape.fields names "${field.name}" twice`, { index });
|
|
114194
|
+
}
|
|
114195
|
+
names.add(field.name);
|
|
114196
|
+
fields.push(field);
|
|
114197
|
+
});
|
|
114198
|
+
return { fields };
|
|
114199
|
+
}
|
|
114200
|
+
function compileAnswerShape(shape) {
|
|
114201
|
+
const entries = {};
|
|
114202
|
+
for (const field of shape.fields) {
|
|
114203
|
+
let schema;
|
|
114204
|
+
switch (field.type) {
|
|
114205
|
+
case "string":
|
|
114206
|
+
schema = external_exports.string().max(field.maxLength ?? MAX_ANSWER_STRING_CHARS);
|
|
114207
|
+
break;
|
|
114208
|
+
case "number":
|
|
114209
|
+
case "integer": {
|
|
114210
|
+
let numeric = field.type === "integer" ? external_exports.int() : external_exports.number();
|
|
114211
|
+
if (field.min !== void 0)
|
|
114212
|
+
numeric = numeric.min(field.min);
|
|
114213
|
+
if (field.max !== void 0)
|
|
114214
|
+
numeric = numeric.max(field.max);
|
|
114215
|
+
schema = numeric;
|
|
114216
|
+
break;
|
|
114217
|
+
}
|
|
114218
|
+
case "boolean":
|
|
114219
|
+
schema = external_exports.boolean();
|
|
114220
|
+
break;
|
|
114221
|
+
case "enum":
|
|
114222
|
+
schema = external_exports.enum(field.values ?? []);
|
|
114223
|
+
break;
|
|
114224
|
+
}
|
|
114225
|
+
entries[field.name] = field.required ? schema : schema.nullish();
|
|
114226
|
+
}
|
|
114227
|
+
return external_exports.object(entries);
|
|
114228
|
+
}
|
|
114229
|
+
function validateAnswerShape(shape, compiled, candidate) {
|
|
114230
|
+
const parsed = compiled.safeParse(candidate);
|
|
114231
|
+
if (!parsed.success) {
|
|
114232
|
+
return {
|
|
114233
|
+
ok: false,
|
|
114234
|
+
// zod's messages state the constraint ("expected int", "<=5") and
|
|
114235
|
+
// never echo the received value, so they are safe to hand back to
|
|
114236
|
+
// the model in the corrective turn.
|
|
114237
|
+
issues: parsed.error.issues.map((issue2) => {
|
|
114238
|
+
const path = issue2.path.join(".");
|
|
114239
|
+
return path ? `${path}: ${issue2.message}` : issue2.message;
|
|
114240
|
+
})
|
|
114241
|
+
};
|
|
114242
|
+
}
|
|
114243
|
+
const value = {};
|
|
114244
|
+
for (const field of shape.fields) {
|
|
114245
|
+
const entry = parsed.data[field.name];
|
|
114246
|
+
if (entry === void 0 || entry === null)
|
|
114247
|
+
continue;
|
|
114248
|
+
value[field.name] = entry;
|
|
114249
|
+
}
|
|
114250
|
+
return { ok: true, value };
|
|
114251
|
+
}
|
|
114252
|
+
function describeField(field) {
|
|
114253
|
+
const requirement = field.required ? "required" : "optional";
|
|
114254
|
+
switch (field.type) {
|
|
114255
|
+
case "string":
|
|
114256
|
+
return `"${field.name}": string, at most ${field.maxLength ?? MAX_ANSWER_STRING_CHARS} characters (${requirement})`;
|
|
114257
|
+
case "number":
|
|
114258
|
+
case "integer": {
|
|
114259
|
+
const kind = field.type === "integer" ? "integer" : "number";
|
|
114260
|
+
const range = field.min !== void 0 && field.max !== void 0 ? ` between ${field.min} and ${field.max}` : field.min !== void 0 ? ` of at least ${field.min}` : field.max !== void 0 ? ` of at most ${field.max}` : "";
|
|
114261
|
+
return `"${field.name}": ${kind}${range} (${requirement})`;
|
|
114262
|
+
}
|
|
114263
|
+
case "boolean":
|
|
114264
|
+
return `"${field.name}": true or false (${requirement})`;
|
|
114265
|
+
case "enum":
|
|
114266
|
+
return `"${field.name}": exactly one of ${(field.values ?? []).map((entry) => JSON.stringify(entry)).join(", ")} (${requirement})`;
|
|
114267
|
+
}
|
|
114268
|
+
}
|
|
114269
|
+
function describeAnswerShape(shape) {
|
|
114270
|
+
return shape.fields.map((field) => ` ${describeField(field)}`);
|
|
114271
|
+
}
|
|
114272
|
+
function renderShapedAnswer(shape, value) {
|
|
114273
|
+
return shape.fields.filter((field) => value[field.name] !== void 0).map((field) => `${field.name}: ${String(value[field.name])}`).join("\n");
|
|
114274
|
+
}
|
|
114275
|
+
function cloneAnswerShape(shape) {
|
|
114276
|
+
if (!shape)
|
|
114277
|
+
return null;
|
|
114278
|
+
return {
|
|
114279
|
+
fields: shape.fields.map((field) => ({
|
|
114280
|
+
...field,
|
|
114281
|
+
...field.values ? { values: [...field.values] } : {}
|
|
114282
|
+
}))
|
|
114283
|
+
};
|
|
114284
|
+
}
|
|
114285
|
+
|
|
114034
114286
|
// ../core/dist/derivatives/types.js
|
|
114035
114287
|
function questionRegistrationView(registration) {
|
|
114036
114288
|
return {
|
|
@@ -114039,10 +114291,12 @@ function questionRegistrationView(registration) {
|
|
|
114039
114291
|
sourceScopes: [...registration.sourceScopes],
|
|
114040
114292
|
question: registration.question,
|
|
114041
114293
|
model: registration.model,
|
|
114294
|
+
answerShape: cloneAnswerShape(registration.answerShape),
|
|
114042
114295
|
recompute: registration.recompute,
|
|
114043
114296
|
registeredBy: registration.registeredBy,
|
|
114044
114297
|
status: registration.status,
|
|
114045
114298
|
error: registration.error,
|
|
114299
|
+
errorCode: registration.errorCode,
|
|
114046
114300
|
createdAt: registration.createdAt,
|
|
114047
114301
|
updatedAt: registration.updatedAt,
|
|
114048
114302
|
lastComputedAt: registration.lastComputedAt,
|
|
@@ -114055,6 +114309,9 @@ function questionRegistrationView(registration) {
|
|
|
114055
114309
|
function clone2(registration) {
|
|
114056
114310
|
return {
|
|
114057
114311
|
...registration,
|
|
114312
|
+
// Registrations persisted before the field existed load as null.
|
|
114313
|
+
errorCode: registration.errorCode ?? null,
|
|
114314
|
+
answerShape: cloneAnswerShape(registration.answerShape ?? null),
|
|
114058
114315
|
sourceScopes: [...registration.sourceScopes],
|
|
114059
114316
|
registeredBy: { ...registration.registeredBy }
|
|
114060
114317
|
};
|
|
@@ -114125,7 +114382,7 @@ var MAX_QUESTION_CHARS = 8e3;
|
|
|
114125
114382
|
var MAX_MODEL_CHARS = 128;
|
|
114126
114383
|
var MAX_ECHOED_SCOPE_CHARS = 128;
|
|
114127
114384
|
var MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
|
|
114128
|
-
function
|
|
114385
|
+
function isRecord5(value) {
|
|
114129
114386
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
114130
114387
|
}
|
|
114131
114388
|
function parseScope(value, field) {
|
|
@@ -114141,7 +114398,7 @@ function parseScope(value, field) {
|
|
|
114141
114398
|
return parsed.scope;
|
|
114142
114399
|
}
|
|
114143
114400
|
function parseQuestionInput(body) {
|
|
114144
|
-
if (!
|
|
114401
|
+
if (!isRecord5(body)) {
|
|
114145
114402
|
throw new DerivativeQuestionInvalidError("Body must be a JSON object");
|
|
114146
114403
|
}
|
|
114147
114404
|
const derivedScope = parseScope(body.derivedScope, "derivedScope");
|
|
@@ -114175,6 +114432,7 @@ function parseQuestionInput(body) {
|
|
|
114175
114432
|
}
|
|
114176
114433
|
model = body.model;
|
|
114177
114434
|
}
|
|
114435
|
+
const answerShape = parseAnswerShapeInput(body.answerShape);
|
|
114178
114436
|
let recompute = "on-change";
|
|
114179
114437
|
if (body.recompute !== void 0 && body.recompute !== null) {
|
|
114180
114438
|
if (body.recompute !== "snapshot" && body.recompute !== "on-change") {
|
|
@@ -114188,6 +114446,7 @@ function parseQuestionInput(body) {
|
|
|
114188
114446
|
sourceScopes,
|
|
114189
114447
|
question: body.question,
|
|
114190
114448
|
model,
|
|
114449
|
+
answerShape,
|
|
114191
114450
|
recompute
|
|
114192
114451
|
};
|
|
114193
114452
|
}
|
|
@@ -114237,10 +114496,12 @@ async function createQuestionRegistration(input) {
|
|
|
114237
114496
|
sourceScopes: parsed.sourceScopes,
|
|
114238
114497
|
question: parsed.question,
|
|
114239
114498
|
model: parsed.model,
|
|
114499
|
+
answerShape: parsed.answerShape,
|
|
114240
114500
|
recompute: parsed.recompute,
|
|
114241
114501
|
registeredBy: input.registeredBy,
|
|
114242
114502
|
status: "pending",
|
|
114243
114503
|
error: null,
|
|
114504
|
+
errorCode: null,
|
|
114244
114505
|
createdAt: at3,
|
|
114245
114506
|
updatedAt: at3,
|
|
114246
114507
|
lastComputedAt: null,
|
|
@@ -114273,11 +114534,11 @@ var TIMESTAMP_KEYS = [
|
|
|
114273
114534
|
"published_at"
|
|
114274
114535
|
];
|
|
114275
114536
|
var RESERVED_KEYS = /* @__PURE__ */ new Set(["$lineage", "$writtenBy", "$binary"]);
|
|
114276
|
-
function
|
|
114537
|
+
function isRecord6(value) {
|
|
114277
114538
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
114278
114539
|
}
|
|
114279
114540
|
function timestampOf(item) {
|
|
114280
|
-
if (!
|
|
114541
|
+
if (!isRecord6(item))
|
|
114281
114542
|
return null;
|
|
114282
114543
|
for (const key of TIMESTAMP_KEYS) {
|
|
114283
114544
|
const value = item[key];
|
|
@@ -114318,7 +114579,7 @@ function trimSourceData(data, options = {}) {
|
|
|
114318
114579
|
total = 0;
|
|
114319
114580
|
if (Array.isArray(data))
|
|
114320
114581
|
return trimArray(data, limit2);
|
|
114321
|
-
if (
|
|
114582
|
+
if (isRecord6(data)) {
|
|
114322
114583
|
const stamped = "$lineage" in data;
|
|
114323
114584
|
const out = {};
|
|
114324
114585
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -114350,13 +114611,28 @@ function trimSourceData(data, options = {}) {
|
|
|
114350
114611
|
}
|
|
114351
114612
|
return { data: result, kept, total, truncated: false };
|
|
114352
114613
|
}
|
|
114353
|
-
var
|
|
114614
|
+
var GROUNDING = [
|
|
114354
114615
|
"You answer a question about a person using ONLY the user data provided in the message.",
|
|
114355
|
-
"Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer."
|
|
114616
|
+
"Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer."
|
|
114617
|
+
];
|
|
114618
|
+
var SYSTEM_PROMPT = [
|
|
114619
|
+
...GROUNDING,
|
|
114356
114620
|
"Respond with a single JSON object and nothing else, with exactly these fields:",
|
|
114357
114621
|
' "answer": string, the answer to the question, written for the person the data belongs to;',
|
|
114358
114622
|
' "evidence": string, a short summary of which parts of the data support the answer.'
|
|
114359
114623
|
].join("\n");
|
|
114624
|
+
function shapedSystemPrompt(shape) {
|
|
114625
|
+
return [
|
|
114626
|
+
...GROUNDING,
|
|
114627
|
+
"Respond with a single JSON object and nothing else, with exactly these fields:",
|
|
114628
|
+
' "answer": object, holding exactly the fields listed below and no others;',
|
|
114629
|
+
' "evidence": string, a short summary of which parts of the data support the answer.',
|
|
114630
|
+
'The "answer" object holds:',
|
|
114631
|
+
...describeAnswerShape(shape),
|
|
114632
|
+
"Use the declared type for every field: a number field is a JSON number, not a string.",
|
|
114633
|
+
"Do not add fields, do not rename them and do not put prose outside them."
|
|
114634
|
+
].join("\n");
|
|
114635
|
+
}
|
|
114360
114636
|
function buildQuestionMessages(input) {
|
|
114361
114637
|
const sections = input.sources.map((source) => {
|
|
114362
114638
|
const note = source.total > source.kept ? ` (newest ${source.kept} of ${source.total} items)` : "";
|
|
@@ -114374,14 +114650,34 @@ function buildQuestionMessages(input) {
|
|
|
114374
114650
|
"## User data",
|
|
114375
114651
|
...sections,
|
|
114376
114652
|
"",
|
|
114377
|
-
"Answer the question as a JSON object with the fields answer and evidence."
|
|
114653
|
+
input.answerShape ? "Answer the question as a JSON object with the fields answer and evidence, where answer holds exactly the declared fields." : "Answer the question as a JSON object with the fields answer and evidence."
|
|
114378
114654
|
].join("\n");
|
|
114379
114655
|
return [
|
|
114380
|
-
{
|
|
114656
|
+
{
|
|
114657
|
+
role: "system",
|
|
114658
|
+
content: input.answerShape ? shapedSystemPrompt(input.answerShape) : SYSTEM_PROMPT
|
|
114659
|
+
},
|
|
114381
114660
|
{ role: "user", content: user }
|
|
114382
114661
|
];
|
|
114383
114662
|
}
|
|
114384
|
-
function
|
|
114663
|
+
function buildShapeRetryMessages(input) {
|
|
114664
|
+
return [
|
|
114665
|
+
...input.messages,
|
|
114666
|
+
{ role: "assistant", content: input.reply },
|
|
114667
|
+
{
|
|
114668
|
+
role: "user",
|
|
114669
|
+
content: [
|
|
114670
|
+
"That reply did not match the required answer shape:",
|
|
114671
|
+
...input.issues.map((issue2) => `- ${issue2}`),
|
|
114672
|
+
"",
|
|
114673
|
+
'Answer again as a single JSON object with the fields answer and evidence, where "answer" holds:',
|
|
114674
|
+
...describeAnswerShape(input.answerShape),
|
|
114675
|
+
"Return the JSON object and nothing else."
|
|
114676
|
+
].join("\n")
|
|
114677
|
+
}
|
|
114678
|
+
];
|
|
114679
|
+
}
|
|
114680
|
+
function jsonFramings(content) {
|
|
114385
114681
|
const candidates = [content.trim()];
|
|
114386
114682
|
const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(content);
|
|
114387
114683
|
if (fenced?.[1])
|
|
@@ -114391,10 +114687,13 @@ function parseAnswer(content) {
|
|
|
114391
114687
|
if (first !== -1 && last2 > first) {
|
|
114392
114688
|
candidates.push(content.slice(first, last2 + 1));
|
|
114393
114689
|
}
|
|
114394
|
-
|
|
114690
|
+
return [...new Set(candidates)];
|
|
114691
|
+
}
|
|
114692
|
+
function parseAnswer(content) {
|
|
114693
|
+
for (const candidate of jsonFramings(content)) {
|
|
114395
114694
|
try {
|
|
114396
114695
|
const parsed = JSON.parse(candidate);
|
|
114397
|
-
if (
|
|
114696
|
+
if (isRecord6(parsed) && typeof parsed.answer === "string") {
|
|
114398
114697
|
return {
|
|
114399
114698
|
answer: parsed.answer,
|
|
114400
114699
|
evidence: typeof parsed.evidence === "string" ? parsed.evidence : null
|
|
@@ -114405,6 +114704,25 @@ function parseAnswer(content) {
|
|
|
114405
114704
|
}
|
|
114406
114705
|
return { answer: content.trim(), evidence: null };
|
|
114407
114706
|
}
|
|
114707
|
+
function parseShapedAnswer(content) {
|
|
114708
|
+
const found = [];
|
|
114709
|
+
for (const candidate of jsonFramings(content)) {
|
|
114710
|
+
let parsed;
|
|
114711
|
+
try {
|
|
114712
|
+
parsed = JSON.parse(candidate);
|
|
114713
|
+
} catch {
|
|
114714
|
+
continue;
|
|
114715
|
+
}
|
|
114716
|
+
if (!isRecord6(parsed))
|
|
114717
|
+
continue;
|
|
114718
|
+
const evidence = typeof parsed.evidence === "string" ? parsed.evidence : null;
|
|
114719
|
+
if (isRecord6(parsed.answer)) {
|
|
114720
|
+
found.push({ answer: parsed.answer, evidence });
|
|
114721
|
+
}
|
|
114722
|
+
found.push({ answer: parsed, evidence });
|
|
114723
|
+
}
|
|
114724
|
+
return found;
|
|
114725
|
+
}
|
|
114408
114726
|
|
|
114409
114727
|
// ../core/dist/derivatives/inference.js
|
|
114410
114728
|
var DEFAULT_INFERENCE_BASE_URL = "https://inference.phala.com/v1";
|
|
@@ -114431,11 +114749,11 @@ var InferenceRequestError = class extends Error {
|
|
|
114431
114749
|
this.retryable = options.retryable;
|
|
114432
114750
|
}
|
|
114433
114751
|
};
|
|
114434
|
-
function
|
|
114752
|
+
function isRecord7(value) {
|
|
114435
114753
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
114436
114754
|
}
|
|
114437
114755
|
function readUsage(value) {
|
|
114438
|
-
if (!
|
|
114756
|
+
if (!isRecord7(value))
|
|
114439
114757
|
return void 0;
|
|
114440
114758
|
const num2 = (v10) => typeof v10 === "number" ? v10 : void 0;
|
|
114441
114759
|
const usage = {
|
|
@@ -114446,10 +114764,10 @@ function readUsage(value) {
|
|
|
114446
114764
|
return usage;
|
|
114447
114765
|
}
|
|
114448
114766
|
function readContent(body) {
|
|
114449
|
-
if (!
|
|
114767
|
+
if (!isRecord7(body) || !Array.isArray(body.choices))
|
|
114450
114768
|
return null;
|
|
114451
114769
|
const first = body.choices[0];
|
|
114452
|
-
if (!
|
|
114770
|
+
if (!isRecord7(first) || !isRecord7(first.message))
|
|
114453
114771
|
return null;
|
|
114454
114772
|
const index = typeof first.index === "number" ? first.index : 0;
|
|
114455
114773
|
const field = `choices.${index}.message.content`;
|
|
@@ -114458,7 +114776,7 @@ function readContent(body) {
|
|
|
114458
114776
|
if (typeof content === "string")
|
|
114459
114777
|
return { content, field, id: id2 };
|
|
114460
114778
|
if (Array.isArray(content)) {
|
|
114461
|
-
const text = content.map((part) =>
|
|
114779
|
+
const text = content.map((part) => isRecord7(part) && typeof part.text === "string" ? part.text : "").join("");
|
|
114462
114780
|
return { content: text, field, id: id2 };
|
|
114463
114781
|
}
|
|
114464
114782
|
return null;
|
|
@@ -114466,7 +114784,7 @@ function readContent(body) {
|
|
|
114466
114784
|
async function readErrorType(response) {
|
|
114467
114785
|
try {
|
|
114468
114786
|
const body = await response.json();
|
|
114469
|
-
if (
|
|
114787
|
+
if (isRecord7(body) && isRecord7(body.error)) {
|
|
114470
114788
|
return typeof body.error.type === "string" ? body.error.type : null;
|
|
114471
114789
|
}
|
|
114472
114790
|
} catch {
|
|
@@ -114574,7 +114892,7 @@ function createOpenAiCompatibleInferenceProvider(options = {}) {
|
|
|
114574
114892
|
ok: true,
|
|
114575
114893
|
result: {
|
|
114576
114894
|
content,
|
|
114577
|
-
usage: readUsage(
|
|
114895
|
+
usage: readUsage(isRecord7(parsed) ? parsed.usage : void 0),
|
|
114578
114896
|
...receiptId ? { receiptId } : {},
|
|
114579
114897
|
...aciIdentity ? { aciIdentity } : {}
|
|
114580
114898
|
}
|
|
@@ -114619,11 +114937,25 @@ async function withRetries(deps, attempt, retryable) {
|
|
|
114619
114937
|
}
|
|
114620
114938
|
}
|
|
114621
114939
|
var ComputeFailure = class extends Error {
|
|
114622
|
-
|
|
114940
|
+
code;
|
|
114941
|
+
constructor(message, code) {
|
|
114623
114942
|
super(message);
|
|
114624
114943
|
this.name = "ComputeFailure";
|
|
114944
|
+
this.code = code;
|
|
114625
114945
|
}
|
|
114626
114946
|
};
|
|
114947
|
+
function classifyComputeError(err2) {
|
|
114948
|
+
if (err2 instanceof ComputeFailure)
|
|
114949
|
+
return err2.code ?? "internal";
|
|
114950
|
+
if (err2 instanceof InferenceRequestError) {
|
|
114951
|
+
return isRetryableInferenceError(err2) ? "inference_unavailable" : "internal";
|
|
114952
|
+
}
|
|
114953
|
+
if (err2 instanceof ProtocolError) {
|
|
114954
|
+
const grantShaped = err2.errorCode === "DERIVATIVE_SOURCE_NOT_GRANTED" || err2.errorCode === "SCOPE_MISMATCH" || err2.errorCode === "UNREGISTERED_BUILDER" || err2.errorCode.startsWith("GRANT_");
|
|
114955
|
+
return grantShaped ? "grant_invalid" : "internal";
|
|
114956
|
+
}
|
|
114957
|
+
return "internal";
|
|
114958
|
+
}
|
|
114627
114959
|
function shortError(err2) {
|
|
114628
114960
|
if (err2 instanceof ComputeFailure)
|
|
114629
114961
|
return err2.message;
|
|
@@ -114633,6 +114965,53 @@ function shortError(err2) {
|
|
|
114633
114965
|
return `${err2.errorCode}: ${err2.message}`;
|
|
114634
114966
|
return `compute failed (${err2 instanceof Error ? err2.name : "Error"})`;
|
|
114635
114967
|
}
|
|
114968
|
+
async function produceAnswer(deps, registration, model, messages) {
|
|
114969
|
+
const ask = (turns2) => withRetries(deps, () => deps.provider.chat({
|
|
114970
|
+
model,
|
|
114971
|
+
messages: turns2,
|
|
114972
|
+
maxTokens: deps.maxTokens
|
|
114973
|
+
}), isRetryableInferenceError);
|
|
114974
|
+
const shape = registration.answerShape;
|
|
114975
|
+
if (!shape) {
|
|
114976
|
+
const reply = await ask(messages);
|
|
114977
|
+
const parsed = parseAnswer(reply.content);
|
|
114978
|
+
return {
|
|
114979
|
+
reply,
|
|
114980
|
+
answer: parsed.answer,
|
|
114981
|
+
evidence: parsed.evidence,
|
|
114982
|
+
answerData: null
|
|
114983
|
+
};
|
|
114984
|
+
}
|
|
114985
|
+
const compiled = compileAnswerShape(shape);
|
|
114986
|
+
let turns = messages;
|
|
114987
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
114988
|
+
const reply = await ask(turns);
|
|
114989
|
+
const candidates = parseShapedAnswer(reply.content);
|
|
114990
|
+
let issues = ["the reply was not a JSON object"];
|
|
114991
|
+
for (const [index, candidate] of candidates.entries()) {
|
|
114992
|
+
const checked = validateAnswerShape(shape, compiled, candidate.answer);
|
|
114993
|
+
if (checked.ok) {
|
|
114994
|
+
return {
|
|
114995
|
+
reply,
|
|
114996
|
+
answer: renderShapedAnswer(shape, checked.value),
|
|
114997
|
+
evidence: candidate.evidence,
|
|
114998
|
+
answerData: checked.value
|
|
114999
|
+
};
|
|
115000
|
+
}
|
|
115001
|
+
if (index === 0)
|
|
115002
|
+
issues = checked.issues;
|
|
115003
|
+
}
|
|
115004
|
+
if (attempt >= 1)
|
|
115005
|
+
break;
|
|
115006
|
+
turns = buildShapeRetryMessages({
|
|
115007
|
+
messages,
|
|
115008
|
+
reply: reply.content,
|
|
115009
|
+
issues,
|
|
115010
|
+
answerShape: shape
|
|
115011
|
+
});
|
|
115012
|
+
}
|
|
115013
|
+
throw new ComputeFailure(`answer did not match the declared shape (fields: ${shape.fields.map((field) => field.name).join(", ")})`, "internal");
|
|
115014
|
+
}
|
|
114636
115015
|
function collectedAtStamp(now, isTaken) {
|
|
114637
115016
|
const base = now();
|
|
114638
115017
|
base.setUTCMilliseconds(0);
|
|
@@ -114708,10 +115087,10 @@ async function loadSource(deps, scope) {
|
|
|
114708
115087
|
const entry = deps.storage.findEntry({ scope });
|
|
114709
115088
|
const deletion = await resolveReadDeletion({ scopeDeletions: deps.scopeDeletions, serverOwner: deps.serverOwner }, scope, entry);
|
|
114710
115089
|
if (deletion) {
|
|
114711
|
-
throw new ComputeFailure(`source scope ${scope} is deleted
|
|
115090
|
+
throw new ComputeFailure(`source scope ${scope} is deleted`, "source_missing");
|
|
114712
115091
|
}
|
|
114713
115092
|
if (!entry) {
|
|
114714
|
-
throw new ComputeFailure(`source scope ${scope} has no local data
|
|
115093
|
+
throw new ComputeFailure(`source scope ${scope} has no local data`, "source_missing");
|
|
114715
115094
|
}
|
|
114716
115095
|
let envelope;
|
|
114717
115096
|
try {
|
|
@@ -114793,18 +115172,20 @@ async function computeQuestion(questionId, deps) {
|
|
|
114793
115172
|
await assertNoLineageCycle(deps, registration, serverOwner, sourceLineage);
|
|
114794
115173
|
const messages = buildQuestionMessages({
|
|
114795
115174
|
question: registration.question,
|
|
114796
|
-
sources
|
|
115175
|
+
sources,
|
|
115176
|
+
answerShape: registration.answerShape
|
|
114797
115177
|
});
|
|
114798
115178
|
const model = registration.model ?? deps.provider.defaultModel;
|
|
114799
|
-
const
|
|
114800
|
-
const
|
|
115179
|
+
const produced = await produceAnswer(deps, registration, model, messages);
|
|
115180
|
+
const reply = produced.reply;
|
|
114801
115181
|
const computedAt = now().toISOString();
|
|
114802
115182
|
const lineageIds = registration.sourceScopes.map((scope) => computeDataPointId(serverOwner, scope));
|
|
114803
115183
|
const record2 = {
|
|
114804
115184
|
questionId: registration.questionId,
|
|
114805
115185
|
question: registration.question,
|
|
114806
|
-
answer:
|
|
114807
|
-
|
|
115186
|
+
answer: produced.answer,
|
|
115187
|
+
...produced.answerData ? { answerData: produced.answerData } : {},
|
|
115188
|
+
evidence: produced.evidence,
|
|
114808
115189
|
model,
|
|
114809
115190
|
computedAt,
|
|
114810
115191
|
sources: sources.map((source) => ({
|
|
@@ -114847,6 +115228,7 @@ async function computeQuestion(questionId, deps) {
|
|
|
114847
115228
|
const updated = await deps.store.update(questionId, {
|
|
114848
115229
|
status: "ready",
|
|
114849
115230
|
error: null,
|
|
115231
|
+
errorCode: null,
|
|
114850
115232
|
updatedAt: computedAt,
|
|
114851
115233
|
lastComputedAt: computedAt,
|
|
114852
115234
|
derivedVersion: entry?.version ?? null,
|
|
@@ -114884,28 +115266,49 @@ async function computeQuestion(questionId, deps) {
|
|
|
114884
115266
|
};
|
|
114885
115267
|
} catch (err2) {
|
|
114886
115268
|
const error51 = shortError(err2);
|
|
115269
|
+
const errorCode = classifyComputeError(err2);
|
|
114887
115270
|
const at3 = now().toISOString();
|
|
114888
115271
|
const updated = await deps.store.update(questionId, {
|
|
114889
115272
|
status: "failed",
|
|
114890
115273
|
error: error51,
|
|
115274
|
+
errorCode,
|
|
114891
115275
|
updatedAt: at3
|
|
114892
115276
|
});
|
|
114893
115277
|
deps.logger?.warn?.({ questionId, derivedScope: registration.derivedScope, error: error51 }, "Derivative question compute failed");
|
|
114894
115278
|
return {
|
|
114895
115279
|
status: "failed",
|
|
114896
|
-
registration: updated ?? {
|
|
115280
|
+
registration: updated ?? {
|
|
115281
|
+
...registration,
|
|
115282
|
+
status: "failed",
|
|
115283
|
+
error: error51,
|
|
115284
|
+
errorCode
|
|
115285
|
+
},
|
|
114897
115286
|
error: error51
|
|
114898
115287
|
};
|
|
114899
115288
|
}
|
|
114900
115289
|
}
|
|
114901
115290
|
|
|
114902
115291
|
// ../core/dist/derivatives/scheduler.js
|
|
115292
|
+
var DEFAULT_RETRY_DELAYS_MS2 = [6e4, 3e5, 18e5];
|
|
115293
|
+
function isRuntimeSkipOutcome(outcome) {
|
|
115294
|
+
if (typeof outcome !== "object" || outcome === null)
|
|
115295
|
+
return false;
|
|
115296
|
+
const shaped = outcome;
|
|
115297
|
+
return shaped.status === "skipped" && shaped.reason === "runtime-unavailable";
|
|
115298
|
+
}
|
|
115299
|
+
function isTransientFailureOutcome(outcome) {
|
|
115300
|
+
if (typeof outcome !== "object" || outcome === null)
|
|
115301
|
+
return false;
|
|
115302
|
+
const shaped = outcome;
|
|
115303
|
+
return shaped.status === "failed" && shaped.registration?.errorCode === "inference_unavailable";
|
|
115304
|
+
}
|
|
114903
115305
|
var defaultTimers = {
|
|
114904
115306
|
setTimeout: (callback, ms3) => setTimeout(callback, ms3),
|
|
114905
115307
|
clearTimeout: (handle) => clearTimeout(handle)
|
|
114906
115308
|
};
|
|
114907
115309
|
function createRecomputeScheduler(options) {
|
|
114908
115310
|
const debounceMs = options.debounceMs ?? 5e3;
|
|
115311
|
+
const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS2;
|
|
114909
115312
|
const timers = options.timers ?? defaultTimers;
|
|
114910
115313
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
114911
115314
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -114924,7 +115327,15 @@ function createRecomputeScheduler(options) {
|
|
|
114924
115327
|
function stateFor(questionId) {
|
|
114925
115328
|
let state = states.get(questionId);
|
|
114926
115329
|
if (!state) {
|
|
114927
|
-
state = {
|
|
115330
|
+
state = {
|
|
115331
|
+
timer: null,
|
|
115332
|
+
running: null,
|
|
115333
|
+
rerun: false,
|
|
115334
|
+
retryAttempts: 0,
|
|
115335
|
+
retryAtMs: null,
|
|
115336
|
+
retryScheduled: false,
|
|
115337
|
+
retryRunning: false
|
|
115338
|
+
};
|
|
114928
115339
|
states.set(questionId, state);
|
|
114929
115340
|
}
|
|
114930
115341
|
return state;
|
|
@@ -114937,17 +115348,42 @@ function createRecomputeScheduler(options) {
|
|
|
114937
115348
|
state.rerun = true;
|
|
114938
115349
|
return;
|
|
114939
115350
|
}
|
|
114940
|
-
state.running = track(Promise.resolve().then(() => options.compute(questionId)).then(() =>
|
|
114941
|
-
|
|
114942
|
-
|
|
114943
|
-
|
|
115351
|
+
state.running = track(Promise.resolve().then(() => options.compute(questionId)).then((outcome) => outcome, (err2) => {
|
|
115352
|
+
warn({
|
|
115353
|
+
questionId,
|
|
115354
|
+
error: err2 instanceof Error ? err2.name : String(err2)
|
|
115355
|
+
}, "Derivative compute threw");
|
|
115356
|
+
return void 0;
|
|
115357
|
+
}).then(async (outcome) => {
|
|
114944
115358
|
state.running = null;
|
|
115359
|
+
state.retryAtMs = null;
|
|
115360
|
+
state.retryRunning = false;
|
|
115361
|
+
const retryable = isTransientFailureOutcome(outcome) || // A runtime-unavailable skip DURING a chain consumes an attempt
|
|
115362
|
+
// instead of ending the chain; on a fresh compute it keeps the
|
|
115363
|
+
// pre-retry behavior (pending/stale, reswept by start()).
|
|
115364
|
+
isRuntimeSkipOutcome(outcome) && state.retryAttempts > 0;
|
|
114945
115365
|
if (state.rerun) {
|
|
114946
115366
|
state.rerun = false;
|
|
115367
|
+
state.retryAttempts = 0;
|
|
114947
115368
|
await markStale(questionId);
|
|
114948
115369
|
schedule(questionId, 0);
|
|
114949
|
-
} else if (
|
|
114950
|
-
|
|
115370
|
+
} else if (retryable && // A pending timer means a source change or explicit recompute
|
|
115371
|
+
// arrived during the run and already scheduled a fresher run;
|
|
115372
|
+
// replacing its 5s debounce with a 60s retry would delay the
|
|
115373
|
+
// recompute of data this compute never saw.
|
|
115374
|
+
state.timer === null && // After stop() nothing may be scheduled; without this a compute
|
|
115375
|
+
// that settles late would record a retryAtMs no timer backs.
|
|
115376
|
+
!stopped && state.retryAttempts < retryDelaysMs.length) {
|
|
115377
|
+
const delayMs = retryDelaysMs[state.retryAttempts];
|
|
115378
|
+
state.retryAttempts += 1;
|
|
115379
|
+
state.retryAtMs = now().getTime() + delayMs;
|
|
115380
|
+
schedule(questionId, delayMs);
|
|
115381
|
+
state.retryScheduled = true;
|
|
115382
|
+
} else {
|
|
115383
|
+
state.retryAttempts = 0;
|
|
115384
|
+
if (!states.get(questionId)?.timer) {
|
|
115385
|
+
states.delete(questionId);
|
|
115386
|
+
}
|
|
114951
115387
|
}
|
|
114952
115388
|
}));
|
|
114953
115389
|
}
|
|
@@ -114959,6 +115395,9 @@ function createRecomputeScheduler(options) {
|
|
|
114959
115395
|
timers.clearTimeout(state.timer);
|
|
114960
115396
|
state.timer = timers.setTimeout(() => {
|
|
114961
115397
|
state.timer = null;
|
|
115398
|
+
state.retryAtMs = null;
|
|
115399
|
+
state.retryRunning = state.retryScheduled;
|
|
115400
|
+
state.retryScheduled = false;
|
|
114962
115401
|
run(questionId);
|
|
114963
115402
|
}, delayMs);
|
|
114964
115403
|
}
|
|
@@ -114969,11 +115408,29 @@ function createRecomputeScheduler(options) {
|
|
|
114969
115408
|
if (registration.status === "ready" || registration.status === "failed") {
|
|
114970
115409
|
await options.store.update(questionId, {
|
|
114971
115410
|
status: "stale",
|
|
115411
|
+
// The reader contract says errorCode is null unless failed; a stale
|
|
115412
|
+
// question is a recompute in progress, not a terminal failure.
|
|
115413
|
+
errorCode: null,
|
|
114972
115414
|
updatedAt: now().toISOString()
|
|
114973
115415
|
});
|
|
114974
115416
|
}
|
|
114975
115417
|
}
|
|
115418
|
+
function resetRetry(questionId) {
|
|
115419
|
+
const state = states.get(questionId);
|
|
115420
|
+
if (!state)
|
|
115421
|
+
return;
|
|
115422
|
+
state.retryAttempts = 0;
|
|
115423
|
+
state.retryAtMs = null;
|
|
115424
|
+
state.retryScheduled = false;
|
|
115425
|
+
}
|
|
114976
115426
|
return {
|
|
115427
|
+
nextRetryAt(questionId) {
|
|
115428
|
+
const retryAtMs = states.get(questionId)?.retryAtMs;
|
|
115429
|
+
return retryAtMs == null ? null : new Date(retryAtMs).toISOString();
|
|
115430
|
+
},
|
|
115431
|
+
retryInFlight(questionId) {
|
|
115432
|
+
return states.get(questionId)?.retryRunning ?? false;
|
|
115433
|
+
},
|
|
114977
115434
|
markSourceChanged(scope, opts) {
|
|
114978
115435
|
if (stopped)
|
|
114979
115436
|
return;
|
|
@@ -114988,6 +115445,7 @@ function createRecomputeScheduler(options) {
|
|
|
114988
115445
|
continue;
|
|
114989
115446
|
}
|
|
114990
115447
|
await markStale(registration.questionId);
|
|
115448
|
+
resetRetry(registration.questionId);
|
|
114991
115449
|
schedule(registration.questionId, debounceMs);
|
|
114992
115450
|
}
|
|
114993
115451
|
})().catch((err2) => warn({ scope, error: err2 instanceof Error ? err2.name : String(err2) }, "Could not mark derivative questions stale")));
|
|
@@ -114998,7 +115456,10 @@ function createRecomputeScheduler(options) {
|
|
|
114998
115456
|
void track(markStale(questionId).catch((err2) => warn({
|
|
114999
115457
|
questionId,
|
|
115000
115458
|
error: err2 instanceof Error ? err2.name : String(err2)
|
|
115001
|
-
}, "Could not mark derivative question stale")).then(() =>
|
|
115459
|
+
}, "Could not mark derivative question stale")).then(() => {
|
|
115460
|
+
resetRetry(questionId);
|
|
115461
|
+
schedule(questionId, opts?.immediate ? 0 : debounceMs);
|
|
115462
|
+
}));
|
|
115002
115463
|
},
|
|
115003
115464
|
async whenIdle() {
|
|
115004
115465
|
const waitForTimers = !options.timers;
|
|
@@ -115018,6 +115479,9 @@ function createRecomputeScheduler(options) {
|
|
|
115018
115479
|
if (state.timer !== null)
|
|
115019
115480
|
timers.clearTimeout(state.timer);
|
|
115020
115481
|
state.timer = null;
|
|
115482
|
+
state.retryAttempts = 0;
|
|
115483
|
+
state.retryAtMs = null;
|
|
115484
|
+
state.retryScheduled = false;
|
|
115021
115485
|
}
|
|
115022
115486
|
},
|
|
115023
115487
|
start() {
|
|
@@ -115038,6 +115502,7 @@ function createRecomputeScheduler(options) {
|
|
|
115038
115502
|
|
|
115039
115503
|
// ../core/dist/derivatives/api.js
|
|
115040
115504
|
var MAX_QUESTION_BODY_BYTES = 16 * 1024;
|
|
115505
|
+
var RETRY_IN_FLIGHT_POLL_SECONDS = 5;
|
|
115041
115506
|
function jsonResponse3(body, init) {
|
|
115042
115507
|
const headers = new Headers(init?.headers);
|
|
115043
115508
|
headers.set("Content-Type", "application/json");
|
|
@@ -115090,12 +115555,63 @@ async function loadForCaller(deps, request2, store, questionId) {
|
|
|
115090
115555
|
}
|
|
115091
115556
|
return { registration, writer };
|
|
115092
115557
|
}
|
|
115558
|
+
async function handleStatusRoute(request2, url2, deps, store, scheduler, now) {
|
|
115559
|
+
if (request2.method !== "GET") {
|
|
115560
|
+
return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
115561
|
+
}
|
|
115562
|
+
const derivedScopeParam = url2.searchParams.get("derivedScope");
|
|
115563
|
+
if (!derivedScopeParam) {
|
|
115564
|
+
throw new DerivativeDerivedScopeRequiredError();
|
|
115565
|
+
}
|
|
115566
|
+
const scopeResult = parseDataScopeContract(derivedScopeParam);
|
|
115567
|
+
if (!scopeResult.ok) {
|
|
115568
|
+
return errorResponse2(scopeResult.status, scopeResult.body.error, scopeResult.body.message);
|
|
115569
|
+
}
|
|
115570
|
+
const derivedScope = scopeResult.scope;
|
|
115571
|
+
const auth = await deps.auth.authorizeBuilderRead({
|
|
115572
|
+
request: request2,
|
|
115573
|
+
scope: derivedScope,
|
|
115574
|
+
grantId: selectedGrantId(request2, url2)
|
|
115575
|
+
}) ?? void 0;
|
|
115576
|
+
const registrations = await store.list({ derivedScope });
|
|
115577
|
+
if (registrations.length === 0) {
|
|
115578
|
+
throw new DerivativeQuestionNotFoundError({ derivedScope });
|
|
115579
|
+
}
|
|
115580
|
+
const STATUS_PRECEDENCE = {
|
|
115581
|
+
ready: 0,
|
|
115582
|
+
stale: 1,
|
|
115583
|
+
pending: 2,
|
|
115584
|
+
failed: 3
|
|
115585
|
+
};
|
|
115586
|
+
const registration = registrations.reduce((best, candidate) => {
|
|
115587
|
+
const byStatus = STATUS_PRECEDENCE[candidate.status] - STATUS_PRECEDENCE[best.status];
|
|
115588
|
+
if (byStatus !== 0)
|
|
115589
|
+
return byStatus < 0 ? candidate : best;
|
|
115590
|
+
return candidate.updatedAt >= best.updatedAt ? candidate : best;
|
|
115591
|
+
});
|
|
115592
|
+
const nextRetryAt = scheduler.nextRetryAt?.(registration.questionId) ?? null;
|
|
115593
|
+
const retryAfterSeconds = nextRetryAt !== null ? Math.max(1, Math.ceil((Date.parse(nextRetryAt) - now().getTime()) / 1e3)) : scheduler.retryInFlight?.(registration.questionId) ? RETRY_IN_FLIGHT_POLL_SECONDS : null;
|
|
115594
|
+
const registrarView = auth === void 0 || auth.grantId === "owner" || auth.grantId === "policy-bypass" || registration.registeredBy.kind === "builder" && typeof auth.builder === "string" && sameBuilder(registration.registeredBy.builder, auth.builder);
|
|
115595
|
+
const disclosedErrorCode = registration.errorCode === "grant_invalid" && !registrarView ? "internal" : registration.errorCode;
|
|
115596
|
+
return jsonResponse3({
|
|
115597
|
+
derivedScope: registration.derivedScope,
|
|
115598
|
+
status: registration.status,
|
|
115599
|
+
lastComputedAt: registration.lastComputedAt,
|
|
115600
|
+
derivedVersion: registration.derivedVersion,
|
|
115601
|
+
derivedCollectedAt: registration.derivedCollectedAt,
|
|
115602
|
+
// Null unless failed, whatever an old store row holds: a stale question
|
|
115603
|
+
// is a recompute in progress, not a terminal failure.
|
|
115604
|
+
errorCode: registration.status === "failed" ? disclosedErrorCode : null,
|
|
115605
|
+
retryAfterSeconds
|
|
115606
|
+
});
|
|
115607
|
+
}
|
|
115093
115608
|
async function handlePersonalServerDerivativesRequest(request2, deps, options = {}) {
|
|
115094
115609
|
try {
|
|
115095
115610
|
const url2 = new URL(request2.url);
|
|
115096
115611
|
const pathname = stripBasePath2(url2.pathname, options.basePath);
|
|
115097
115612
|
const parts = pathname.split("/").filter(Boolean);
|
|
115098
|
-
|
|
115613
|
+
const isStatusRoute = parts[0] === "status" && parts.length === 1;
|
|
115614
|
+
if (!isStatusRoute && (parts[0] !== "questions" || parts.length > 3)) {
|
|
115099
115615
|
return errorResponse2(404, "NOT_FOUND", "Not found");
|
|
115100
115616
|
}
|
|
115101
115617
|
const compute = deps.compute;
|
|
@@ -115103,6 +115619,9 @@ async function handlePersonalServerDerivativesRequest(request2, deps, options =
|
|
|
115103
115619
|
throw new DerivativeComputeUnavailableError();
|
|
115104
115620
|
const { store, scheduler } = compute;
|
|
115105
115621
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
115622
|
+
if (isStatusRoute) {
|
|
115623
|
+
return await handleStatusRoute(request2, url2, deps, store, scheduler, now);
|
|
115624
|
+
}
|
|
115106
115625
|
if (parts.length === 1) {
|
|
115107
115626
|
if (request2.method === "GET") {
|
|
115108
115627
|
const derivedScope = url2.searchParams.get("derivedScope") ?? void 0;
|
|
@@ -115427,7 +115946,7 @@ var E2eeAttestationError = class extends Error {
|
|
|
115427
115946
|
this.name = "E2eeAttestationError";
|
|
115428
115947
|
}
|
|
115429
115948
|
};
|
|
115430
|
-
function
|
|
115949
|
+
function isRecord8(value) {
|
|
115431
115950
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
115432
115951
|
}
|
|
115433
115952
|
async function sha256Hex2(bytes2) {
|
|
@@ -115445,7 +115964,7 @@ async function reportDataFor(keysetDigest, nonce) {
|
|
|
115445
115964
|
return sha256Hex2(new TextEncoder().encode(statement));
|
|
115446
115965
|
}
|
|
115447
115966
|
function parseReport(body) {
|
|
115448
|
-
if (!
|
|
115967
|
+
if (!isRecord8(body) || typeof body.api_version !== "string" || typeof body.workload_keyset_digest !== "string" || !isRecord8(body.attestation) || typeof body.attestation.tee_type !== "string" || typeof body.attestation.report_data !== "string" || !isRecord8(body.attestation.workload_keyset)) {
|
|
115449
115968
|
throw new E2eeAttestationError("malformed_report", "attestation report is missing required fields");
|
|
115450
115969
|
}
|
|
115451
115970
|
const keyset = body.attestation.workload_keyset;
|
|
@@ -115492,7 +116011,7 @@ async function verifyAciReportBinding(report, nonce, nowSeconds) {
|
|
|
115492
116011
|
}
|
|
115493
116012
|
function selectX25519Key(keyset, expectedKeyId) {
|
|
115494
116013
|
for (const entry of keyset.e2ee_public_keys) {
|
|
115495
|
-
if (!
|
|
116014
|
+
if (!isRecord8(entry) || entry.algo !== E2EE_ALGO_X25519)
|
|
115496
116015
|
continue;
|
|
115497
116016
|
if (typeof entry.public_key !== "string")
|
|
115498
116017
|
continue;
|
|
@@ -133090,7 +133609,8 @@ async function createPsLiteQuestionStore(stateStore) {
|
|
|
133090
133609
|
const saved = await stateStore.get(QUESTIONS_KEY);
|
|
133091
133610
|
const initial = (saved?.version === 1 ? saved.questions : []).map((question) => ({
|
|
133092
133611
|
...question,
|
|
133093
|
-
recompute: question.recompute ?? "on-change"
|
|
133612
|
+
recompute: question.recompute ?? "on-change",
|
|
133613
|
+
answerShape: question.answerShape ?? null
|
|
133094
133614
|
}));
|
|
133095
133615
|
return createInMemoryQuestionStore({
|
|
133096
133616
|
initial,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opendatalabs/personal-server-ts-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Hono HTTP server for the Vana Personal Server — routes, middleware, composition root",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@hono/node-server": "^1.19.13",
|
|
47
|
-
"@opendatalabs/personal-server-ts-core": "1.
|
|
48
|
-
"@opendatalabs/personal-server-ts-lite": "1.
|
|
47
|
+
"@opendatalabs/personal-server-ts-core": "1.15.0",
|
|
48
|
+
"@opendatalabs/personal-server-ts-lite": "1.15.0",
|
|
49
49
|
"@opendatalabs/vana-sdk": "3.14.0",
|
|
50
50
|
"better-sqlite3": "^12.11.1",
|
|
51
51
|
"hono": "^4.12.27",
|