wontopos 2.2.39 → 2.2.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/wontopos.d.cts +37 -30
- package/dist/cjs/wontopos.js +44 -14
- package/dist/wontopos.d.ts +37 -30
- package/dist/wontopos.js +44 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,7 +209,7 @@ data to fix it.
|
|
|
209
209
|
## Changelog
|
|
210
210
|
|
|
211
211
|
The three clients release in lockstep — same version, same surface, same day. Patch
|
|
212
|
-
releases are additive.
|
|
212
|
+
releases are additive. Six inside 2.2 were not, deliberately and each with its
|
|
213
213
|
reason; the changelog lists them.
|
|
214
214
|
|
|
215
215
|
See [CHANGELOG.md](https://github.com/wontopos/wontopos-node/blob/main/CHANGELOG.md).
|
package/dist/cjs/wontopos.d.cts
CHANGED
|
@@ -74,10 +74,11 @@ export declare class PaymentRequiredError extends WosError {
|
|
|
74
74
|
/** 403 — the key/model isn't allowed to do this. */
|
|
75
75
|
export declare class PermissionDeniedError extends WosError {
|
|
76
76
|
}
|
|
77
|
-
/** 404 — the store or resource doesn't exist
|
|
77
|
+
/** 404 — the store or resource doesn't exist. */
|
|
78
78
|
export declare class NotFoundError extends WosError {
|
|
79
79
|
}
|
|
80
|
-
/** 409 —
|
|
80
|
+
/** 409 — another write to this store was in flight (nothing was stored; retry), or the
|
|
81
|
+
* store id collides with an existing store's (permanent). */
|
|
81
82
|
export declare class ConflictError extends WosError {
|
|
82
83
|
}
|
|
83
84
|
/** 429 — too many requests. Back off and retry (the client already retries these). */
|
|
@@ -159,6 +160,8 @@ export interface StatusResult {
|
|
|
159
160
|
/**
|
|
160
161
|
* An image attached to a memory, passed to `add`/`store` via `opts.image`.
|
|
161
162
|
*
|
|
163
|
+
* Both edges must be 700px or more; a smaller image is refused (400).
|
|
164
|
+
*
|
|
162
165
|
* What the service keeps is NOT your original. An image whose long edge is over
|
|
163
166
|
* 1568px is downscaled to 1568 before anything else happens, and that smaller
|
|
164
167
|
* picture is what gets stored, and handed back by `getImage`. Nothing on our side
|
|
@@ -291,8 +294,8 @@ export interface LineageResult {
|
|
|
291
294
|
export interface SpeakerPage {
|
|
292
295
|
speaker?: string;
|
|
293
296
|
memories: Memory[];
|
|
294
|
-
/** How many underlying records a delete would actually remove, beyond what was returned. */
|
|
295
297
|
chunks?: number;
|
|
298
|
+
/** The count to show before anyone confirms a delete of this speaker's memories. */
|
|
296
299
|
points_to_delete?: number;
|
|
297
300
|
returned?: number;
|
|
298
301
|
has_more?: boolean;
|
|
@@ -307,20 +310,17 @@ export interface UpdateResult {
|
|
|
307
310
|
status?: string;
|
|
308
311
|
[key: string]: unknown;
|
|
309
312
|
}
|
|
310
|
-
/** `searchFull` → everything one search answered with
|
|
311
|
-
*
|
|
312
|
-
* `
|
|
313
|
-
* almost every call. Two options make it the wrong one: `max_images` asks for
|
|
314
|
-
* photos, which arrive in their own field, and `verify` is reported on by
|
|
315
|
-
* `verify_used`. Merging away both means paying for a
|
|
316
|
-
* search you shaped and never seeing what came of it. */
|
|
313
|
+
/** `searchFull` → everything one search answered with. `search` returns every memory
|
|
314
|
+
* as one array; this keeps the fields apart and adds `verify_used`, the report on
|
|
315
|
+
* the `verify` option. */
|
|
317
316
|
export interface SearchResult {
|
|
318
317
|
/** What others said, and general memories. */
|
|
319
318
|
memories: Memory[];
|
|
320
319
|
/** The assistant's own words (speaker "me"); `[]` on a model that does not keep
|
|
321
320
|
* them apart. */
|
|
322
321
|
self_memories: Memory[];
|
|
323
|
-
/** Image memories
|
|
322
|
+
/** Image memories the answer carried (one by default on an image-capable model,
|
|
323
|
+
* up to `max_images`); `[]` when there were none. */
|
|
324
324
|
images: Memory[];
|
|
325
325
|
/** Re-ask passes actually performed. Present only when `verify` was sent; lower
|
|
326
326
|
* than you asked for means the store had nothing further to add. */
|
|
@@ -434,18 +434,21 @@ export interface EngramCatalog {
|
|
|
434
434
|
}
|
|
435
435
|
/** `createStore` / `deleteStore` → `{ user_id, status }`. */
|
|
436
436
|
export interface StoreOpResult {
|
|
437
|
-
/** The store id
|
|
438
|
-
* Ids are normalized (see the `note`), so compare this against your input. */
|
|
437
|
+
/** The store id. `createStore` answers with the id as you sent it. */
|
|
439
438
|
user_id?: string;
|
|
439
|
+
/** The normalized form the store is filed under, when it differs from `user_id`. */
|
|
440
|
+
canonical_id?: string;
|
|
440
441
|
status?: string;
|
|
441
|
-
/** Present when the API
|
|
442
|
-
* end users can land in one store if their ids differ only by punctuation. */
|
|
442
|
+
/** Present when the API filed your id under a normalized form, explaining how. */
|
|
443
443
|
note?: string;
|
|
444
444
|
[key: string]: unknown;
|
|
445
445
|
}
|
|
446
446
|
export interface StoreInfo {
|
|
447
|
+
/** The id the store was created with — pass it back as `userId`. */
|
|
447
448
|
user_id: string;
|
|
448
449
|
created_at: string;
|
|
450
|
+
/** The normalized form the store is filed under, when it differs from `user_id`. */
|
|
451
|
+
canonical_id?: string;
|
|
449
452
|
[key: string]: unknown;
|
|
450
453
|
}
|
|
451
454
|
/** `addSpeaker` / `removeSpeaker` → `{ user_id, speaker, status }`. */
|
|
@@ -535,11 +538,15 @@ export interface SearchOptions {
|
|
|
535
538
|
* than answering with no images.
|
|
536
539
|
*/
|
|
537
540
|
max_images?: number;
|
|
541
|
+
/** Delivery form, "memoir" or "archive" (Scroll 1.2+): how each memory's time is
|
|
542
|
+
* rendered. The service refuses an unknown one (400). */
|
|
543
|
+
form?: string;
|
|
544
|
+
/** Your UTC offset in hours, for rendering times in `form`. */
|
|
545
|
+
tz?: number;
|
|
538
546
|
/**
|
|
539
547
|
* Fields the service accepts that this version does not know about, merged into the
|
|
540
|
-
* request as written. Every other key is refused, so a typo cannot reach the wire
|
|
541
|
-
*
|
|
542
|
-
* a caller billed for one pass believing they had bought three.
|
|
548
|
+
* request as written. Every other key is refused, so a typo cannot reach the wire.
|
|
549
|
+
* The store, the query and the count always win over a copy in here.
|
|
543
550
|
*/
|
|
544
551
|
extra?: Record<string, unknown>;
|
|
545
552
|
}
|
|
@@ -708,18 +715,18 @@ export declare class Client {
|
|
|
708
715
|
* a call that passes no count is unaffected. Before 2.2.35 the count was sent on
|
|
709
716
|
* unchecked.
|
|
710
717
|
*
|
|
711
|
-
* `limit` bounds `memories`, not the returned array.
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
* apart. */
|
|
718
|
+
* `limit` bounds `memories`, not the returned array. The assistant's own words
|
|
719
|
+
* (Scroll 1.2+) and image memories (Tablet 2+, one unless `max_images` says
|
|
720
|
+
* otherwise) come back in it as well, so it can hold more than `limit`. They are
|
|
721
|
+
* billed either way. Size a prompt window on the array you get back, not on
|
|
722
|
+
* `limit`. `searchFull()` hands the fields back apart. */
|
|
717
723
|
search(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<Memory[]>;
|
|
718
724
|
/** Search a self-memory model (Scroll 1.2+): both fields from ONE call. Returns
|
|
719
725
|
* `{ memories, self_memories }` — `memories` is what others said and general
|
|
720
726
|
* memories, `self_memories` is the assistant's OWN words (stored with speaker
|
|
721
727
|
* "me"), kept apart so whoever reads them never confuses who said what. On a model
|
|
722
|
-
* that does not keep them apart, `self_memories` is `[]`.
|
|
728
|
+
* that does not keep them apart, `self_memories` is `[]`. Image memories are not
|
|
729
|
+
* included; `search` and `searchFull` carry them. `userId` may be omitted. */
|
|
723
730
|
searchSelf(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<SelfSearchResult>;
|
|
724
731
|
/**
|
|
725
732
|
* Search, and keep every field the answer came with.
|
|
@@ -729,7 +736,7 @@ export declare class Client {
|
|
|
729
736
|
*
|
|
730
737
|
* const r = await mem.searchFull("the day we moved", "alice", 10,
|
|
731
738
|
* { max_images: 3, verify: 2 });
|
|
732
|
-
* r.images.length; // the photos,
|
|
739
|
+
* r.images.length; // the photos, apart from the text memories
|
|
733
740
|
* r.verify_used; // how many re-ask passes actually ran (you are billed per pass)
|
|
734
741
|
*
|
|
735
742
|
* Both options need Tablet 2 or newer and are refused (403) on an older engine
|
|
@@ -943,9 +950,8 @@ export declare class Client {
|
|
|
943
950
|
* assistant's own words, otherwise a person's name. Same cursor paging as
|
|
944
951
|
* `listImages`.
|
|
945
952
|
*
|
|
946
|
-
* `
|
|
947
|
-
*
|
|
948
|
-
* about to confirm one.
|
|
953
|
+
* `points_to_delete` is the count to show before anyone confirms a delete of this
|
|
954
|
+
* speaker's memories.
|
|
949
955
|
*/
|
|
950
956
|
bySpeaker(speaker: string, userId?: string, opts?: {
|
|
951
957
|
limit?: number;
|
|
@@ -975,7 +981,8 @@ export declare class Client {
|
|
|
975
981
|
* return 404. Idempotent. Every account starts with a `default` store.
|
|
976
982
|
* Returns `{ user_id, status }` (`status` is `"created"` or `"exists"`). */
|
|
977
983
|
createStore(userId?: string): Promise<StoreOpResult>;
|
|
978
|
-
/** List your stores: `[{ user_id, created_at }, ...]` (`default`
|
|
984
|
+
/** List your stores: `[{ user_id, created_at, canonical_id? }, ...]` (`default`
|
|
985
|
+
* first). Each `user_id` is the id the store was created with. */
|
|
979
986
|
listStores(): Promise<StoreInfo[]>;
|
|
980
987
|
/** Delete a store and ALL its memories. Returns `{ user_id, status }`. */
|
|
981
988
|
deleteStore(userId: string): Promise<StoreOpResult>;
|
package/dist/cjs/wontopos.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.WME = exports.Client = exports.ServerError = exports.RateLimitError = exports.ConflictError = exports.NotFoundError = exports.PermissionDeniedError = exports.PaymentRequiredError = exports.AuthenticationError = exports.BadRequestError = exports.APIConnectionError = exports.WosError = exports.CONTEXT_LIMIT_MAX = exports.CONTEXT_LIMIT_MIN = exports.SEARCH_LIMIT_MAX = exports.SEARCH_LIMIT_MIN = void 0;
|
|
4
4
|
exports._resetStoreIdWarnings = _resetStoreIdWarnings;
|
|
5
5
|
exports._resetFilterWarnings = _resetFilterWarnings;
|
|
6
|
-
const VERSION = "2.2.
|
|
6
|
+
const VERSION = "2.2.40";
|
|
7
7
|
const RUNTIME = (() => {
|
|
8
8
|
const p = globalThis?.process;
|
|
9
9
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -40,8 +40,9 @@ const warnedStoreIds = new Set();
|
|
|
40
40
|
function warnIfStoreIdCollapses(id) {
|
|
41
41
|
if (!id || warnedStoreIds.has(id))
|
|
42
42
|
return;
|
|
43
|
+
const valid = STORE_ID_FORMAT.test(id);
|
|
43
44
|
const normalized = normalizeStoreId(id);
|
|
44
|
-
if (normalized === id)
|
|
45
|
+
if (valid && normalized === id)
|
|
45
46
|
return;
|
|
46
47
|
warnedStoreIds.add(id);
|
|
47
48
|
while (warnedStoreIds.size > WARNED_STORE_IDS_MAX) {
|
|
@@ -50,10 +51,27 @@ function warnIfStoreIdCollapses(id) {
|
|
|
50
51
|
break;
|
|
51
52
|
warnedStoreIds.delete(oldest);
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
-
`
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
if (!valid) {
|
|
55
|
+
console.warn(`wontopos: store id ${JSON.stringify(id)} is not a valid store id: use 1-64 ASCII letters, ` +
|
|
56
|
+
`digits, ".", "_" and "-", starting with a letter or digit. Creating it is refused (400).`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.warn(`wontopos: store id ${JSON.stringify(id)} normalizes to ${JSON.stringify(normalized)}. ` +
|
|
60
|
+
`Ids that differ only by case name this same store; one that differs only by punctuation ` +
|
|
61
|
+
`cannot be created beside it (409) and is not found when used (404). If these ids come from ` +
|
|
62
|
+
`your end users, normalize them yourself first so two people never compete for one name.`);
|
|
63
|
+
}
|
|
64
|
+
const STORE_ID_FORMAT = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
65
|
+
const NOT_METADATA = new Set(["userid", "storeid", "idempotencykey"]);
|
|
66
|
+
function checkMetadataKeys(metadata) {
|
|
67
|
+
if (metadata == null || typeof metadata !== "object")
|
|
68
|
+
return;
|
|
69
|
+
for (const k of Object.keys(metadata)) {
|
|
70
|
+
if (NOT_METADATA.has(k.toLowerCase().replace(/[\s_.-]/g, ""))) {
|
|
71
|
+
throw new Error(`${JSON.stringify(k)} is not a metadata field: pass the store as userId and an ` +
|
|
72
|
+
`idempotency key as opts.idempotencyKey. Nothing was sent.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
57
75
|
}
|
|
58
76
|
function _resetStoreIdWarnings() {
|
|
59
77
|
warnedStoreIds.clear();
|
|
@@ -141,13 +159,24 @@ function parseRateLimit(h) {
|
|
|
141
159
|
const asList = (x) => (Array.isArray(x) ? x : []);
|
|
142
160
|
const asObj = (x) => (x != null && typeof x === "object" && !Array.isArray(x) ? x : {});
|
|
143
161
|
const asRecords = (x) => asList(x).filter((m) => m != null && typeof m === "object" && !Array.isArray(m));
|
|
162
|
+
const memoryFromGet = (r) => {
|
|
163
|
+
const o = asObj(r);
|
|
164
|
+
if (o.memory != null && typeof o.memory === "object" && !Array.isArray(o.memory))
|
|
165
|
+
return o.memory;
|
|
166
|
+
return typeof o.id === "string" ? o : {};
|
|
167
|
+
};
|
|
144
168
|
function mergeResults(r) {
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
169
|
+
const out = asRecords(r?.memories);
|
|
170
|
+
const seen = new Set(out.map((m) => m.id).filter((id) => typeof id === "string"));
|
|
171
|
+
for (const m of [...asRecords(r?.self_memories), ...asRecords(r?.images)]) {
|
|
172
|
+
if (typeof m.id === "string") {
|
|
173
|
+
if (seen.has(m.id))
|
|
174
|
+
continue;
|
|
175
|
+
seen.add(m.id);
|
|
176
|
+
}
|
|
177
|
+
out.push(m);
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
151
180
|
}
|
|
152
181
|
function withForm(body, form, tz) {
|
|
153
182
|
if (form !== undefined)
|
|
@@ -219,7 +248,7 @@ const KNOWN_FILTER_KEYS = new Set([
|
|
|
219
248
|
]);
|
|
220
249
|
const warnedFilterKeys = new Set();
|
|
221
250
|
const KNOWN_SEARCH_KEYS = new Set([
|
|
222
|
-
"cache_control", "speaker", "filters", "verify", "max_images", "extra",
|
|
251
|
+
"cache_control", "speaker", "filters", "verify", "max_images", "form", "tz", "extra",
|
|
223
252
|
]);
|
|
224
253
|
const RESERVED_SEARCH_KEYS = new Set(["user_id", "query", "max_results"]);
|
|
225
254
|
const KNOWN_RECALL_KEYS = new Set(["form", "tz", "limit", "context_limit"]);
|
|
@@ -408,6 +437,7 @@ class Client {
|
|
|
408
437
|
return this.clone({ maxRetries });
|
|
409
438
|
}
|
|
410
439
|
async add(content, userId, metadata = {}, opts = {}) {
|
|
440
|
+
checkMetadataKeys(metadata);
|
|
411
441
|
const body = { user_id: this.uid(userId), content, metadata };
|
|
412
442
|
if (opts.image !== undefined)
|
|
413
443
|
body.image = normalizeImage(opts.image);
|
|
@@ -491,7 +521,7 @@ class Client {
|
|
|
491
521
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
492
522
|
throw new Error("memoryId is required — the id that add/store or listMemories returned.");
|
|
493
523
|
}
|
|
494
|
-
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId.trim() }).then(
|
|
524
|
+
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId.trim() }).then(memoryFromGet);
|
|
495
525
|
}
|
|
496
526
|
async listMemories(userId, opts = {}) {
|
|
497
527
|
const body = { user_id: this.uid(userId), limit: opts.limit ?? 100 };
|
package/dist/wontopos.d.ts
CHANGED
|
@@ -74,10 +74,11 @@ export declare class PaymentRequiredError extends WosError {
|
|
|
74
74
|
/** 403 — the key/model isn't allowed to do this. */
|
|
75
75
|
export declare class PermissionDeniedError extends WosError {
|
|
76
76
|
}
|
|
77
|
-
/** 404 — the store or resource doesn't exist
|
|
77
|
+
/** 404 — the store or resource doesn't exist. */
|
|
78
78
|
export declare class NotFoundError extends WosError {
|
|
79
79
|
}
|
|
80
|
-
/** 409 —
|
|
80
|
+
/** 409 — another write to this store was in flight (nothing was stored; retry), or the
|
|
81
|
+
* store id collides with an existing store's (permanent). */
|
|
81
82
|
export declare class ConflictError extends WosError {
|
|
82
83
|
}
|
|
83
84
|
/** 429 — too many requests. Back off and retry (the client already retries these). */
|
|
@@ -159,6 +160,8 @@ export interface StatusResult {
|
|
|
159
160
|
/**
|
|
160
161
|
* An image attached to a memory, passed to `add`/`store` via `opts.image`.
|
|
161
162
|
*
|
|
163
|
+
* Both edges must be 700px or more; a smaller image is refused (400).
|
|
164
|
+
*
|
|
162
165
|
* What the service keeps is NOT your original. An image whose long edge is over
|
|
163
166
|
* 1568px is downscaled to 1568 before anything else happens, and that smaller
|
|
164
167
|
* picture is what gets stored, and handed back by `getImage`. Nothing on our side
|
|
@@ -291,8 +294,8 @@ export interface LineageResult {
|
|
|
291
294
|
export interface SpeakerPage {
|
|
292
295
|
speaker?: string;
|
|
293
296
|
memories: Memory[];
|
|
294
|
-
/** How many underlying records a delete would actually remove, beyond what was returned. */
|
|
295
297
|
chunks?: number;
|
|
298
|
+
/** The count to show before anyone confirms a delete of this speaker's memories. */
|
|
296
299
|
points_to_delete?: number;
|
|
297
300
|
returned?: number;
|
|
298
301
|
has_more?: boolean;
|
|
@@ -307,20 +310,17 @@ export interface UpdateResult {
|
|
|
307
310
|
status?: string;
|
|
308
311
|
[key: string]: unknown;
|
|
309
312
|
}
|
|
310
|
-
/** `searchFull` → everything one search answered with
|
|
311
|
-
*
|
|
312
|
-
* `
|
|
313
|
-
* almost every call. Two options make it the wrong one: `max_images` asks for
|
|
314
|
-
* photos, which arrive in their own field, and `verify` is reported on by
|
|
315
|
-
* `verify_used`. Merging away both means paying for a
|
|
316
|
-
* search you shaped and never seeing what came of it. */
|
|
313
|
+
/** `searchFull` → everything one search answered with. `search` returns every memory
|
|
314
|
+
* as one array; this keeps the fields apart and adds `verify_used`, the report on
|
|
315
|
+
* the `verify` option. */
|
|
317
316
|
export interface SearchResult {
|
|
318
317
|
/** What others said, and general memories. */
|
|
319
318
|
memories: Memory[];
|
|
320
319
|
/** The assistant's own words (speaker "me"); `[]` on a model that does not keep
|
|
321
320
|
* them apart. */
|
|
322
321
|
self_memories: Memory[];
|
|
323
|
-
/** Image memories
|
|
322
|
+
/** Image memories the answer carried (one by default on an image-capable model,
|
|
323
|
+
* up to `max_images`); `[]` when there were none. */
|
|
324
324
|
images: Memory[];
|
|
325
325
|
/** Re-ask passes actually performed. Present only when `verify` was sent; lower
|
|
326
326
|
* than you asked for means the store had nothing further to add. */
|
|
@@ -434,18 +434,21 @@ export interface EngramCatalog {
|
|
|
434
434
|
}
|
|
435
435
|
/** `createStore` / `deleteStore` → `{ user_id, status }`. */
|
|
436
436
|
export interface StoreOpResult {
|
|
437
|
-
/** The store id
|
|
438
|
-
* Ids are normalized (see the `note`), so compare this against your input. */
|
|
437
|
+
/** The store id. `createStore` answers with the id as you sent it. */
|
|
439
438
|
user_id?: string;
|
|
439
|
+
/** The normalized form the store is filed under, when it differs from `user_id`. */
|
|
440
|
+
canonical_id?: string;
|
|
440
441
|
status?: string;
|
|
441
|
-
/** Present when the API
|
|
442
|
-
* end users can land in one store if their ids differ only by punctuation. */
|
|
442
|
+
/** Present when the API filed your id under a normalized form, explaining how. */
|
|
443
443
|
note?: string;
|
|
444
444
|
[key: string]: unknown;
|
|
445
445
|
}
|
|
446
446
|
export interface StoreInfo {
|
|
447
|
+
/** The id the store was created with — pass it back as `userId`. */
|
|
447
448
|
user_id: string;
|
|
448
449
|
created_at: string;
|
|
450
|
+
/** The normalized form the store is filed under, when it differs from `user_id`. */
|
|
451
|
+
canonical_id?: string;
|
|
449
452
|
[key: string]: unknown;
|
|
450
453
|
}
|
|
451
454
|
/** `addSpeaker` / `removeSpeaker` → `{ user_id, speaker, status }`. */
|
|
@@ -535,11 +538,15 @@ export interface SearchOptions {
|
|
|
535
538
|
* than answering with no images.
|
|
536
539
|
*/
|
|
537
540
|
max_images?: number;
|
|
541
|
+
/** Delivery form, "memoir" or "archive" (Scroll 1.2+): how each memory's time is
|
|
542
|
+
* rendered. The service refuses an unknown one (400). */
|
|
543
|
+
form?: string;
|
|
544
|
+
/** Your UTC offset in hours, for rendering times in `form`. */
|
|
545
|
+
tz?: number;
|
|
538
546
|
/**
|
|
539
547
|
* Fields the service accepts that this version does not know about, merged into the
|
|
540
|
-
* request as written. Every other key is refused, so a typo cannot reach the wire
|
|
541
|
-
*
|
|
542
|
-
* a caller billed for one pass believing they had bought three.
|
|
548
|
+
* request as written. Every other key is refused, so a typo cannot reach the wire.
|
|
549
|
+
* The store, the query and the count always win over a copy in here.
|
|
543
550
|
*/
|
|
544
551
|
extra?: Record<string, unknown>;
|
|
545
552
|
}
|
|
@@ -708,18 +715,18 @@ export declare class Client {
|
|
|
708
715
|
* a call that passes no count is unaffected. Before 2.2.35 the count was sent on
|
|
709
716
|
* unchecked.
|
|
710
717
|
*
|
|
711
|
-
* `limit` bounds `memories`, not the returned array.
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
* apart. */
|
|
718
|
+
* `limit` bounds `memories`, not the returned array. The assistant's own words
|
|
719
|
+
* (Scroll 1.2+) and image memories (Tablet 2+, one unless `max_images` says
|
|
720
|
+
* otherwise) come back in it as well, so it can hold more than `limit`. They are
|
|
721
|
+
* billed either way. Size a prompt window on the array you get back, not on
|
|
722
|
+
* `limit`. `searchFull()` hands the fields back apart. */
|
|
717
723
|
search(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<Memory[]>;
|
|
718
724
|
/** Search a self-memory model (Scroll 1.2+): both fields from ONE call. Returns
|
|
719
725
|
* `{ memories, self_memories }` — `memories` is what others said and general
|
|
720
726
|
* memories, `self_memories` is the assistant's OWN words (stored with speaker
|
|
721
727
|
* "me"), kept apart so whoever reads them never confuses who said what. On a model
|
|
722
|
-
* that does not keep them apart, `self_memories` is `[]`.
|
|
728
|
+
* that does not keep them apart, `self_memories` is `[]`. Image memories are not
|
|
729
|
+
* included; `search` and `searchFull` carry them. `userId` may be omitted. */
|
|
723
730
|
searchSelf(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<SelfSearchResult>;
|
|
724
731
|
/**
|
|
725
732
|
* Search, and keep every field the answer came with.
|
|
@@ -729,7 +736,7 @@ export declare class Client {
|
|
|
729
736
|
*
|
|
730
737
|
* const r = await mem.searchFull("the day we moved", "alice", 10,
|
|
731
738
|
* { max_images: 3, verify: 2 });
|
|
732
|
-
* r.images.length; // the photos,
|
|
739
|
+
* r.images.length; // the photos, apart from the text memories
|
|
733
740
|
* r.verify_used; // how many re-ask passes actually ran (you are billed per pass)
|
|
734
741
|
*
|
|
735
742
|
* Both options need Tablet 2 or newer and are refused (403) on an older engine
|
|
@@ -943,9 +950,8 @@ export declare class Client {
|
|
|
943
950
|
* assistant's own words, otherwise a person's name. Same cursor paging as
|
|
944
951
|
* `listImages`.
|
|
945
952
|
*
|
|
946
|
-
* `
|
|
947
|
-
*
|
|
948
|
-
* about to confirm one.
|
|
953
|
+
* `points_to_delete` is the count to show before anyone confirms a delete of this
|
|
954
|
+
* speaker's memories.
|
|
949
955
|
*/
|
|
950
956
|
bySpeaker(speaker: string, userId?: string, opts?: {
|
|
951
957
|
limit?: number;
|
|
@@ -975,7 +981,8 @@ export declare class Client {
|
|
|
975
981
|
* return 404. Idempotent. Every account starts with a `default` store.
|
|
976
982
|
* Returns `{ user_id, status }` (`status` is `"created"` or `"exists"`). */
|
|
977
983
|
createStore(userId?: string): Promise<StoreOpResult>;
|
|
978
|
-
/** List your stores: `[{ user_id, created_at }, ...]` (`default`
|
|
984
|
+
/** List your stores: `[{ user_id, created_at, canonical_id? }, ...]` (`default`
|
|
985
|
+
* first). Each `user_id` is the id the store was created with. */
|
|
979
986
|
listStores(): Promise<StoreInfo[]>;
|
|
980
987
|
/** Delete a store and ALL its memories. Returns `{ user_id, status }`. */
|
|
981
988
|
deleteStore(userId: string): Promise<StoreOpResult>;
|
package/dist/wontopos.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const VERSION = "2.2.
|
|
1
|
+
const VERSION = "2.2.40";
|
|
2
2
|
const RUNTIME = (() => {
|
|
3
3
|
const p = globalThis?.process;
|
|
4
4
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -35,8 +35,9 @@ const warnedStoreIds = new Set();
|
|
|
35
35
|
function warnIfStoreIdCollapses(id) {
|
|
36
36
|
if (!id || warnedStoreIds.has(id))
|
|
37
37
|
return;
|
|
38
|
+
const valid = STORE_ID_FORMAT.test(id);
|
|
38
39
|
const normalized = normalizeStoreId(id);
|
|
39
|
-
if (normalized === id)
|
|
40
|
+
if (valid && normalized === id)
|
|
40
41
|
return;
|
|
41
42
|
warnedStoreIds.add(id);
|
|
42
43
|
while (warnedStoreIds.size > WARNED_STORE_IDS_MAX) {
|
|
@@ -45,10 +46,27 @@ function warnIfStoreIdCollapses(id) {
|
|
|
45
46
|
break;
|
|
46
47
|
warnedStoreIds.delete(oldest);
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if (!valid) {
|
|
50
|
+
console.warn(`wontopos: store id ${JSON.stringify(id)} is not a valid store id: use 1-64 ASCII letters, ` +
|
|
51
|
+
`digits, ".", "_" and "-", starting with a letter or digit. Creating it is refused (400).`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.warn(`wontopos: store id ${JSON.stringify(id)} normalizes to ${JSON.stringify(normalized)}. ` +
|
|
55
|
+
`Ids that differ only by case name this same store; one that differs only by punctuation ` +
|
|
56
|
+
`cannot be created beside it (409) and is not found when used (404). If these ids come from ` +
|
|
57
|
+
`your end users, normalize them yourself first so two people never compete for one name.`);
|
|
58
|
+
}
|
|
59
|
+
const STORE_ID_FORMAT = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
60
|
+
const NOT_METADATA = new Set(["userid", "storeid", "idempotencykey"]);
|
|
61
|
+
function checkMetadataKeys(metadata) {
|
|
62
|
+
if (metadata == null || typeof metadata !== "object")
|
|
63
|
+
return;
|
|
64
|
+
for (const k of Object.keys(metadata)) {
|
|
65
|
+
if (NOT_METADATA.has(k.toLowerCase().replace(/[\s_.-]/g, ""))) {
|
|
66
|
+
throw new Error(`${JSON.stringify(k)} is not a metadata field: pass the store as userId and an ` +
|
|
67
|
+
`idempotency key as opts.idempotencyKey. Nothing was sent.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
52
70
|
}
|
|
53
71
|
export function _resetStoreIdWarnings() {
|
|
54
72
|
warnedStoreIds.clear();
|
|
@@ -136,13 +154,24 @@ function parseRateLimit(h) {
|
|
|
136
154
|
const asList = (x) => (Array.isArray(x) ? x : []);
|
|
137
155
|
const asObj = (x) => (x != null && typeof x === "object" && !Array.isArray(x) ? x : {});
|
|
138
156
|
const asRecords = (x) => asList(x).filter((m) => m != null && typeof m === "object" && !Array.isArray(m));
|
|
157
|
+
const memoryFromGet = (r) => {
|
|
158
|
+
const o = asObj(r);
|
|
159
|
+
if (o.memory != null && typeof o.memory === "object" && !Array.isArray(o.memory))
|
|
160
|
+
return o.memory;
|
|
161
|
+
return typeof o.id === "string" ? o : {};
|
|
162
|
+
};
|
|
139
163
|
function mergeResults(r) {
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
164
|
+
const out = asRecords(r?.memories);
|
|
165
|
+
const seen = new Set(out.map((m) => m.id).filter((id) => typeof id === "string"));
|
|
166
|
+
for (const m of [...asRecords(r?.self_memories), ...asRecords(r?.images)]) {
|
|
167
|
+
if (typeof m.id === "string") {
|
|
168
|
+
if (seen.has(m.id))
|
|
169
|
+
continue;
|
|
170
|
+
seen.add(m.id);
|
|
171
|
+
}
|
|
172
|
+
out.push(m);
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
146
175
|
}
|
|
147
176
|
function withForm(body, form, tz) {
|
|
148
177
|
if (form !== undefined)
|
|
@@ -204,7 +233,7 @@ const KNOWN_FILTER_KEYS = new Set([
|
|
|
204
233
|
]);
|
|
205
234
|
const warnedFilterKeys = new Set();
|
|
206
235
|
const KNOWN_SEARCH_KEYS = new Set([
|
|
207
|
-
"cache_control", "speaker", "filters", "verify", "max_images", "extra",
|
|
236
|
+
"cache_control", "speaker", "filters", "verify", "max_images", "form", "tz", "extra",
|
|
208
237
|
]);
|
|
209
238
|
const RESERVED_SEARCH_KEYS = new Set(["user_id", "query", "max_results"]);
|
|
210
239
|
const KNOWN_RECALL_KEYS = new Set(["form", "tz", "limit", "context_limit"]);
|
|
@@ -393,6 +422,7 @@ export class Client {
|
|
|
393
422
|
return this.clone({ maxRetries });
|
|
394
423
|
}
|
|
395
424
|
async add(content, userId, metadata = {}, opts = {}) {
|
|
425
|
+
checkMetadataKeys(metadata);
|
|
396
426
|
const body = { user_id: this.uid(userId), content, metadata };
|
|
397
427
|
if (opts.image !== undefined)
|
|
398
428
|
body.image = normalizeImage(opts.image);
|
|
@@ -476,7 +506,7 @@ export class Client {
|
|
|
476
506
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
477
507
|
throw new Error("memoryId is required — the id that add/store or listMemories returned.");
|
|
478
508
|
}
|
|
479
|
-
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId.trim() }).then(
|
|
509
|
+
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId.trim() }).then(memoryFromGet);
|
|
480
510
|
}
|
|
481
511
|
async listMemories(userId, opts = {}) {
|
|
482
512
|
const body = { user_id: this.uid(userId), limit: opts.limit ?? 100 };
|