wontopos 2.2.38 → 2.2.39
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 +7 -9
- package/dist/cjs/wontopos.d.cts +20 -14
- package/dist/cjs/wontopos.js +44 -27
- package/dist/wontopos.d.ts +20 -14
- package/dist/wontopos.js +44 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,12 +80,12 @@ const hits = await mem.search("...the conversation so far...", "alice", 10,
|
|
|
80
80
|
|
|
81
81
|
Built in, no configuration needed:
|
|
82
82
|
|
|
83
|
-
- **Automatic retries** — 429 always, and 502 / 503 or a connection error
|
|
84
|
-
retry cannot double-process a write. The writes and the searches are
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
- **Automatic retries** — 429 always, and 408 / 502 / 503 / 504 or a connection error
|
|
84
|
+
only when a retry cannot double-process a write. The writes and the searches are
|
|
85
|
+
POSTs, and any of those four may have been returned *after* the write already
|
|
86
|
+
landed, so those get 429 and connect-level failures only. The reads and the
|
|
87
87
|
deletes that address a whole store — `listStores`, `listSpeakers`, `deleteStore`,
|
|
88
|
-
`removeSpeaker`, `forgetImage` — are GET or DELETE and do retry
|
|
88
|
+
`removeSpeaker`, `forgetImage` — are GET or DELETE and do retry all four. Twice, with
|
|
89
89
|
exponential backoff + jitter, honoring the server's `Retry-After`. Tune with
|
|
90
90
|
`new Client({ maxRetries })`; `0` disables.
|
|
91
91
|
- **Redirects refused** — the API key never follows a 3xx to another host.
|
|
@@ -106,9 +106,7 @@ Built in, no configuration needed:
|
|
|
106
106
|
### Behind a corporate proxy
|
|
107
107
|
|
|
108
108
|
Node's global `fetch` (undici) ignores `HTTP_PROXY` / `HTTPS_PROXY`, so behind an
|
|
109
|
-
egress proxy
|
|
110
|
-
through, because `requests` and `reqwest` both read those variables. Pass a
|
|
111
|
-
proxy-aware `fetch` and the three behave the same:
|
|
109
|
+
egress proxy no request leaves at all. Pass a proxy-aware `fetch` and they go through:
|
|
112
110
|
|
|
113
111
|
This one needs `npm i undici` — Node's global `fetch` is built on undici, but the
|
|
114
112
|
package itself is not installed for you.
|
|
@@ -211,7 +209,7 @@ data to fix it.
|
|
|
211
209
|
## Changelog
|
|
212
210
|
|
|
213
211
|
The three clients release in lockstep — same version, same surface, same day. Patch
|
|
214
|
-
releases are additive.
|
|
212
|
+
releases are additive. Five inside 2.2 were not, deliberately and each with its
|
|
215
213
|
reason; the changelog lists them.
|
|
216
214
|
|
|
217
215
|
See [CHANGELOG.md](https://github.com/wontopos/wontopos-node/blob/main/CHANGELOG.md).
|
package/dist/cjs/wontopos.d.cts
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* and searching call no LLM.
|
|
27
27
|
*
|
|
28
28
|
* Reliability: every call retries transient failures with exponential backoff +
|
|
29
|
-
* jitter, honoring `Retry-After` — 429 always; 502/503 and connection errors
|
|
30
|
-
* when a retry can never double-process a write (idempotent calls, or a failure
|
|
29
|
+
* jitter, honoring `Retry-After` — 429 always; 408/502/503/504 and connection errors
|
|
30
|
+
* only when a retry can never double-process a write (idempotent calls, or a failure
|
|
31
31
|
* at connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per
|
|
32
32
|
* call site via the `withTimeout()` / `withRetries()` clones.
|
|
33
33
|
*
|
|
@@ -86,8 +86,8 @@ export declare class RateLimitError extends WosError {
|
|
|
86
86
|
/**
|
|
87
87
|
* 5xx — the service failed.
|
|
88
88
|
*
|
|
89
|
-
* `502` / `503` are transient: the client already retries them for calls
|
|
90
|
-
* retry cannot double-process a write, and retrying yourself is reasonable.
|
|
89
|
+
* `502` / `503` / `504` are transient: the client already retries them for calls
|
|
90
|
+
* where a retry cannot double-process a write, and retrying yourself is reasonable.
|
|
91
91
|
*
|
|
92
92
|
* `501` is NOT transient. It means the engine behind the model you selected does
|
|
93
93
|
* not implement that endpoint at all (the error carries `model` and `endpoint`).
|
|
@@ -428,6 +428,9 @@ export interface EngramCatalog {
|
|
|
428
428
|
engrams: EngramInfo[];
|
|
429
429
|
forms: EngramInfo[];
|
|
430
430
|
note?: string;
|
|
431
|
+
/** Anything else the reply carried. Responses widen; the runtime keeps those fields
|
|
432
|
+
* and this is what lets a typed caller read them. */
|
|
433
|
+
[key: string]: unknown;
|
|
431
434
|
}
|
|
432
435
|
/** `createStore` / `deleteStore` → `{ user_id, status }`. */
|
|
433
436
|
export interface StoreOpResult {
|
|
@@ -548,9 +551,9 @@ export interface SearchOptions {
|
|
|
548
551
|
* arrives with a different body. Use it when a retry is your own (a job that died and
|
|
549
552
|
* was re-run, a queue that redelivers). The SDK retries a write on exactly one
|
|
550
553
|
* status: 429, which the service answers before it processes anything, so nothing
|
|
551
|
-
* was stored. It never retries a write on 502 / 503 or a dropped body,
|
|
552
|
-
*
|
|
553
|
-
* whether
|
|
554
|
+
* was stored. It never retries a write on 408 / 502 / 503 / 504 or a dropped body,
|
|
555
|
+
* where the first attempt may already have landed — without a key the client cannot
|
|
556
|
+
* know whether it did.
|
|
554
557
|
*
|
|
555
558
|
* The key must be UNIQUE PER LOGICAL WRITE — derive it from the thing being stored
|
|
556
559
|
* (`` `import:${row.id}` ``), never a constant, or the second write replays the first
|
|
@@ -582,8 +585,8 @@ export interface ClientOptions {
|
|
|
582
585
|
* to the account's built-in `default` store. */
|
|
583
586
|
userId?: string;
|
|
584
587
|
/** How many times to retry transient failures before throwing — 429 always;
|
|
585
|
-
* 502/503 and connection errors only when a retry can never
|
|
586
|
-
* write. 0 disables retries (default 2). */
|
|
588
|
+
* 408/502/503/504 and connection errors only when a retry can never
|
|
589
|
+
* double-process a write. 0 disables retries (default 2). */
|
|
587
590
|
maxRetries?: number;
|
|
588
591
|
/** Alias of `maxRetries`. The Python SDK calls this option `retries`, and these
|
|
589
592
|
* SDKs are published in lockstep as "the same surface" — so code ported between
|
|
@@ -611,10 +614,8 @@ export interface ClientOptions {
|
|
|
611
614
|
*
|
|
612
615
|
* This is the seam for anything the runtime cannot express through options.
|
|
613
616
|
* The one that matters in practice is a corporate egress proxy: Node's global
|
|
614
|
-
* fetch (undici) IGNORES `HTTP_PROXY` / `HTTPS_PROXY`, so behind such a proxy
|
|
615
|
-
*
|
|
616
|
-
* through, because `requests` and `reqwest` both read those variables. Pass a
|
|
617
|
-
* proxy-aware fetch and the three behave the same again:
|
|
617
|
+
* fetch (undici) IGNORES `HTTP_PROXY` / `HTTPS_PROXY`, so behind such a proxy no
|
|
618
|
+
* request leaves at all. Pass a proxy-aware fetch and they go through:
|
|
618
619
|
*
|
|
619
620
|
* import { ProxyAgent } from "undici";
|
|
620
621
|
* const agent = new ProxyAgent(process.env.HTTPS_PROXY!);
|
|
@@ -696,7 +697,7 @@ export declare class Client {
|
|
|
696
697
|
addTurn(userMsg: string, assistantMsg: string, userId?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
697
698
|
/** Bulk-ingest a large blob of text in one call. For backfilling.
|
|
698
699
|
* The call most worth an `opts.idempotencyKey`: a backfill that dies halfway and is
|
|
699
|
-
* re-run would otherwise
|
|
700
|
+
* re-run would otherwise ingest the whole blob a second time. */
|
|
700
701
|
addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
701
702
|
/** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
|
|
702
703
|
update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
|
|
@@ -812,6 +813,11 @@ export declare class Client {
|
|
|
812
813
|
}): AsyncGenerator<Memory>;
|
|
813
814
|
/** Collect ALL of a store's memories into an array (the text you stored, and its metadata). */
|
|
814
815
|
exportMemories(userId?: string): Promise<Memory[]>;
|
|
816
|
+
/** Collect ALL of a store's image memories into an array — the image-side pair of
|
|
817
|
+
* `exportMemories`. */
|
|
818
|
+
exportImages(userId?: string, opts?: {
|
|
819
|
+
pageSize?: number;
|
|
820
|
+
}): Promise<Memory[]>;
|
|
815
821
|
/**
|
|
816
822
|
* Fetch the bytes of an image memory — the picture the SERVICE holds.
|
|
817
823
|
*
|
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.39";
|
|
7
7
|
const RUNTIME = (() => {
|
|
8
8
|
const p = globalThis?.process;
|
|
9
9
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -18,7 +18,7 @@ function logDebug(msg) {
|
|
|
18
18
|
const DEFAULT_BASE_URL = "https://api.wontopos.com";
|
|
19
19
|
const DEFAULT_MODEL = "tablet-2";
|
|
20
20
|
const RETRY_ALWAYS = new Set([429]);
|
|
21
|
-
const RETRY_IF_IDEMPOTENT = new Set([502, 503]);
|
|
21
|
+
const RETRY_IF_IDEMPOTENT = new Set([408, 502, 503, 504]);
|
|
22
22
|
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
23
23
|
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
|
|
24
24
|
const MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
@@ -223,6 +223,7 @@ const KNOWN_SEARCH_KEYS = new Set([
|
|
|
223
223
|
]);
|
|
224
224
|
const RESERVED_SEARCH_KEYS = new Set(["user_id", "query", "max_results"]);
|
|
225
225
|
const KNOWN_RECALL_KEYS = new Set(["form", "tz", "limit", "context_limit"]);
|
|
226
|
+
const KNOWN_ENGRAM_KEYS = new Set(["form", "tz"]);
|
|
226
227
|
function checkSearchOpts(opts) {
|
|
227
228
|
checkOpts(opts, KNOWN_SEARCH_KEYS, "search");
|
|
228
229
|
}
|
|
@@ -237,8 +238,7 @@ function checkOpts(opts, known, what) {
|
|
|
237
238
|
const near = [...known].find((n) => n !== "extra" && editWithin(k, n, 2));
|
|
238
239
|
throw new Error(`unknown ${what} option ${JSON.stringify(k)}` +
|
|
239
240
|
(near ? ` — did you mean ${JSON.stringify(near)}?` : "") +
|
|
240
|
-
`.
|
|
241
|
-
`have looked like it worked.` +
|
|
241
|
+
`. It would have had no effect, and the call would have looked like it worked.` +
|
|
242
242
|
(known.has("extra")
|
|
243
243
|
? ` Pass it under \`extra\` if the service accepts it and this client does not know it yet.`
|
|
244
244
|
: ""));
|
|
@@ -407,25 +407,25 @@ class Client {
|
|
|
407
407
|
withRetries(maxRetries) {
|
|
408
408
|
return this.clone({ maxRetries });
|
|
409
409
|
}
|
|
410
|
-
add(content, userId, metadata = {}, opts = {}) {
|
|
410
|
+
async add(content, userId, metadata = {}, opts = {}) {
|
|
411
411
|
const body = { user_id: this.uid(userId), content, metadata };
|
|
412
412
|
if (opts.image !== undefined)
|
|
413
413
|
body.image = normalizeImage(opts.image);
|
|
414
414
|
return this.post("/api/v1/memory/store", body, opts.idempotencyKey);
|
|
415
415
|
}
|
|
416
|
-
store(content, userId, metadata = {}, opts = {}) {
|
|
416
|
+
async store(content, userId, metadata = {}, opts = {}) {
|
|
417
417
|
return this.add(content, userId, metadata, opts);
|
|
418
418
|
}
|
|
419
|
-
addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
419
|
+
async addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
420
420
|
return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, opts.idempotencyKey);
|
|
421
421
|
}
|
|
422
|
-
addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
422
|
+
async addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
423
423
|
const body = { user_id: this.uid(userId), content, category };
|
|
424
424
|
if (timestamp)
|
|
425
425
|
body.timestamp = timestamp;
|
|
426
426
|
return this.post("/api/v1/memory/bulk-store", body, opts.idempotencyKey);
|
|
427
427
|
}
|
|
428
|
-
update(oldMemoryId, newContent, userId, opts = {}) {
|
|
428
|
+
async update(oldMemoryId, newContent, userId, opts = {}) {
|
|
429
429
|
return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts.idempotencyKey);
|
|
430
430
|
}
|
|
431
431
|
async search(query, userId, limit = 10, opts = {}) {
|
|
@@ -470,14 +470,15 @@ class Client {
|
|
|
470
470
|
}
|
|
471
471
|
return this.post("/api/v1/memory/recall", withForm(body, opts.form, opts.tz));
|
|
472
472
|
}
|
|
473
|
-
engram(name, query, userId, opts = {}) {
|
|
473
|
+
async engram(name, query, userId, opts = {}) {
|
|
474
|
+
checkOpts(opts, KNOWN_ENGRAM_KEYS, "engram");
|
|
474
475
|
return this.post("/api/v1/engram/run", withForm({ name, user_id: this.uid(userId), query }, opts.form, opts.tz));
|
|
475
476
|
}
|
|
476
477
|
async history(userId) {
|
|
477
478
|
const r = await this.post("/api/v1/memory/history", { user_id: this.uid(userId) });
|
|
478
479
|
return asRecords(r.turns);
|
|
479
480
|
}
|
|
480
|
-
stats(userId) {
|
|
481
|
+
async stats(userId) {
|
|
481
482
|
return this.post("/api/v1/memory/stats", { user_id: this.uid(userId) });
|
|
482
483
|
}
|
|
483
484
|
async usage(days = 7) {
|
|
@@ -486,13 +487,13 @@ class Client {
|
|
|
486
487
|
}
|
|
487
488
|
return this.request("GET", `/api/v1/won/usage?days=${days}`);
|
|
488
489
|
}
|
|
489
|
-
get(userId, memoryId) {
|
|
490
|
+
async get(userId, memoryId) {
|
|
490
491
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
491
492
|
throw new Error("memoryId is required — the id that add/store or listMemories returned.");
|
|
492
493
|
}
|
|
493
494
|
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId.trim() }).then((r) => asObj(r.memory));
|
|
494
495
|
}
|
|
495
|
-
listMemories(userId, opts = {}) {
|
|
496
|
+
async listMemories(userId, opts = {}) {
|
|
496
497
|
const body = { user_id: this.uid(userId), limit: opts.limit ?? 100 };
|
|
497
498
|
if (opts.cursor)
|
|
498
499
|
body.cursor = opts.cursor;
|
|
@@ -520,13 +521,19 @@ class Client {
|
|
|
520
521
|
out.push(m);
|
|
521
522
|
return out;
|
|
522
523
|
}
|
|
523
|
-
|
|
524
|
+
async exportImages(userId, opts = {}) {
|
|
525
|
+
const out = [];
|
|
526
|
+
for await (const m of this.iterImages(userId, opts))
|
|
527
|
+
out.push(m);
|
|
528
|
+
return out;
|
|
529
|
+
}
|
|
530
|
+
async getImage(userId, memoryId) {
|
|
524
531
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
525
532
|
throw new Error("memoryId is required — the id that add/store or listImages returned.");
|
|
526
533
|
}
|
|
527
534
|
return this.requestBytes("/api/v1/memory/image", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
528
535
|
}
|
|
529
|
-
forgetImage(userId, memoryId, opts = {}) {
|
|
536
|
+
async forgetImage(userId, memoryId, opts = {}) {
|
|
530
537
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
531
538
|
throw new Error("memoryId is required — the id of the memory whose image you want removed.");
|
|
532
539
|
}
|
|
@@ -535,7 +542,7 @@ class Client {
|
|
|
535
542
|
body.preview = true;
|
|
536
543
|
return this.request("DELETE", "/api/v1/memory/image", body);
|
|
537
544
|
}
|
|
538
|
-
listImages(userId, opts = {}) {
|
|
545
|
+
async listImages(userId, opts = {}) {
|
|
539
546
|
const body = { user_id: this.uid(userId) };
|
|
540
547
|
if (opts.limit !== undefined)
|
|
541
548
|
body.limit = opts.limit;
|
|
@@ -565,7 +572,7 @@ class Client {
|
|
|
565
572
|
throw new Error(`stopped after ${MAX_PAGES} pages — the store did not end. This is a truncated ` +
|
|
566
573
|
`answer, not the whole store.`);
|
|
567
574
|
}
|
|
568
|
-
revisions(userId, opts = {}) {
|
|
575
|
+
async revisions(userId, opts = {}) {
|
|
569
576
|
const body = {};
|
|
570
577
|
if (opts.include !== undefined)
|
|
571
578
|
body.include = opts.include;
|
|
@@ -578,13 +585,13 @@ class Client {
|
|
|
578
585
|
body.user_id = this.uid(userId);
|
|
579
586
|
return this.post("/api/v1/won/revisions", body);
|
|
580
587
|
}
|
|
581
|
-
lineage(userId, memoryId) {
|
|
588
|
+
async lineage(userId, memoryId) {
|
|
582
589
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
583
590
|
throw new Error("memoryId is required — the memory whose history you want.");
|
|
584
591
|
}
|
|
585
592
|
return this.post("/api/v1/memory/lineage", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
586
593
|
}
|
|
587
|
-
bySpeaker(speaker, userId, opts = {}) {
|
|
594
|
+
async bySpeaker(speaker, userId, opts = {}) {
|
|
588
595
|
if (!speaker || typeof speaker !== "string" || speaker.trim() === "") {
|
|
589
596
|
throw new Error('speaker is required — "me" for the assistant, or a person\'s name.');
|
|
590
597
|
}
|
|
@@ -608,41 +615,42 @@ class Client {
|
|
|
608
615
|
async listEngrams() {
|
|
609
616
|
const data = await this.request("GET", "/api/v1/engram");
|
|
610
617
|
return {
|
|
618
|
+
...data,
|
|
611
619
|
engrams: asRecords(data.engrams),
|
|
612
620
|
forms: asRecords(data.forms),
|
|
613
621
|
note: typeof data.note === "string" ? data.note : undefined,
|
|
614
622
|
};
|
|
615
623
|
}
|
|
616
|
-
createStore(userId) {
|
|
624
|
+
async createStore(userId) {
|
|
617
625
|
return this.post("/api/v1/memory/collection", { user_id: this.uid(userId) });
|
|
618
626
|
}
|
|
619
627
|
async listStores() {
|
|
620
628
|
const r = await this.request("GET", "/api/v1/memory/collections");
|
|
621
629
|
return asRecords(r.collections);
|
|
622
630
|
}
|
|
623
|
-
deleteStore(userId) {
|
|
631
|
+
async deleteStore(userId) {
|
|
624
632
|
if (typeof userId !== "string" || !userId.trim())
|
|
625
633
|
throw new Error("userId is required (a non-blank string) — deleteStore never falls back to the default store.");
|
|
626
634
|
warnIfStoreIdCollapses(userId);
|
|
627
635
|
return this.request("DELETE", "/api/v1/memory/collection", { user_id: userId });
|
|
628
636
|
}
|
|
629
|
-
addSpeaker(speaker, userId) {
|
|
637
|
+
async addSpeaker(speaker, userId) {
|
|
630
638
|
return this.post("/api/v1/memory/speakers", { user_id: this.uid(userId), speaker });
|
|
631
639
|
}
|
|
632
640
|
async listSpeakers(userId) {
|
|
633
641
|
const r = await this.request("GET", `/api/v1/memory/speakers?user_id=${encodeURIComponent(this.uid(userId))}`);
|
|
634
642
|
return { ...r, speakers: asRecords(r?.speakers) };
|
|
635
643
|
}
|
|
636
|
-
removeSpeaker(speaker, userId) {
|
|
644
|
+
async removeSpeaker(speaker, userId) {
|
|
637
645
|
return this.request("DELETE", "/api/v1/memory/speakers", { user_id: this.uid(userId), speaker });
|
|
638
646
|
}
|
|
639
|
-
delete(userId, memoryId) {
|
|
647
|
+
async delete(userId, memoryId) {
|
|
640
648
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
641
649
|
throw new Error("memoryId is required (non-blank). To delete every memory in a store, call deleteAll(userId) explicitly.");
|
|
642
650
|
}
|
|
643
651
|
return this.post("/api/v1/memory/forget", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
644
652
|
}
|
|
645
|
-
deleteAll(userId) {
|
|
653
|
+
async deleteAll(userId) {
|
|
646
654
|
if (typeof userId !== "string" || !userId.trim())
|
|
647
655
|
throw new Error("userId is required (a non-blank string) for deleteAll — anything else would wipe the default store.");
|
|
648
656
|
warnIfStoreIdCollapses(userId);
|
|
@@ -777,7 +785,9 @@ class Client {
|
|
|
777
785
|
if (e?.name === "AbortError" || att.signal.aborted) {
|
|
778
786
|
throw new APIConnectionError(0, this.abortMessage(att.why()));
|
|
779
787
|
}
|
|
780
|
-
|
|
788
|
+
if (e instanceof WosError)
|
|
789
|
+
throw e;
|
|
790
|
+
throw new APIConnectionError(0, `network error: ${e?.message ?? e}`);
|
|
781
791
|
}
|
|
782
792
|
if (buf.byteLength === 0) {
|
|
783
793
|
throw new WosError(res.status, "empty image body — the service returned no bytes");
|
|
@@ -910,7 +920,14 @@ class Client {
|
|
|
910
920
|
}
|
|
911
921
|
if (e instanceof WosError)
|
|
912
922
|
throw e;
|
|
913
|
-
|
|
923
|
+
if (!(IDEMPOTENT_METHODS.has(method.toUpperCase()) && attempt + 1 < attempts)) {
|
|
924
|
+
throw new APIConnectionError(0, `network error: ${e?.message ?? e}`);
|
|
925
|
+
}
|
|
926
|
+
att.clear();
|
|
927
|
+
const delay = this.backoffMs(attempt);
|
|
928
|
+
logDebug(`${method} ${logPath} — body dropped mid-stream, retrying in ${delay}ms (attempt ${attempt + 1}/${attempts})`);
|
|
929
|
+
await this.backoffSleep(delay, deadlineAt);
|
|
930
|
+
continue;
|
|
914
931
|
}
|
|
915
932
|
finally {
|
|
916
933
|
att.clear();
|
package/dist/wontopos.d.ts
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* and searching call no LLM.
|
|
27
27
|
*
|
|
28
28
|
* Reliability: every call retries transient failures with exponential backoff +
|
|
29
|
-
* jitter, honoring `Retry-After` — 429 always; 502/503 and connection errors
|
|
30
|
-
* when a retry can never double-process a write (idempotent calls, or a failure
|
|
29
|
+
* jitter, honoring `Retry-After` — 429 always; 408/502/503/504 and connection errors
|
|
30
|
+
* only when a retry can never double-process a write (idempotent calls, or a failure
|
|
31
31
|
* at connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per
|
|
32
32
|
* call site via the `withTimeout()` / `withRetries()` clones.
|
|
33
33
|
*
|
|
@@ -86,8 +86,8 @@ export declare class RateLimitError extends WosError {
|
|
|
86
86
|
/**
|
|
87
87
|
* 5xx — the service failed.
|
|
88
88
|
*
|
|
89
|
-
* `502` / `503` are transient: the client already retries them for calls
|
|
90
|
-
* retry cannot double-process a write, and retrying yourself is reasonable.
|
|
89
|
+
* `502` / `503` / `504` are transient: the client already retries them for calls
|
|
90
|
+
* where a retry cannot double-process a write, and retrying yourself is reasonable.
|
|
91
91
|
*
|
|
92
92
|
* `501` is NOT transient. It means the engine behind the model you selected does
|
|
93
93
|
* not implement that endpoint at all (the error carries `model` and `endpoint`).
|
|
@@ -428,6 +428,9 @@ export interface EngramCatalog {
|
|
|
428
428
|
engrams: EngramInfo[];
|
|
429
429
|
forms: EngramInfo[];
|
|
430
430
|
note?: string;
|
|
431
|
+
/** Anything else the reply carried. Responses widen; the runtime keeps those fields
|
|
432
|
+
* and this is what lets a typed caller read them. */
|
|
433
|
+
[key: string]: unknown;
|
|
431
434
|
}
|
|
432
435
|
/** `createStore` / `deleteStore` → `{ user_id, status }`. */
|
|
433
436
|
export interface StoreOpResult {
|
|
@@ -548,9 +551,9 @@ export interface SearchOptions {
|
|
|
548
551
|
* arrives with a different body. Use it when a retry is your own (a job that died and
|
|
549
552
|
* was re-run, a queue that redelivers). The SDK retries a write on exactly one
|
|
550
553
|
* status: 429, which the service answers before it processes anything, so nothing
|
|
551
|
-
* was stored. It never retries a write on 502 / 503 or a dropped body,
|
|
552
|
-
*
|
|
553
|
-
* whether
|
|
554
|
+
* was stored. It never retries a write on 408 / 502 / 503 / 504 or a dropped body,
|
|
555
|
+
* where the first attempt may already have landed — without a key the client cannot
|
|
556
|
+
* know whether it did.
|
|
554
557
|
*
|
|
555
558
|
* The key must be UNIQUE PER LOGICAL WRITE — derive it from the thing being stored
|
|
556
559
|
* (`` `import:${row.id}` ``), never a constant, or the second write replays the first
|
|
@@ -582,8 +585,8 @@ export interface ClientOptions {
|
|
|
582
585
|
* to the account's built-in `default` store. */
|
|
583
586
|
userId?: string;
|
|
584
587
|
/** How many times to retry transient failures before throwing — 429 always;
|
|
585
|
-
* 502/503 and connection errors only when a retry can never
|
|
586
|
-
* write. 0 disables retries (default 2). */
|
|
588
|
+
* 408/502/503/504 and connection errors only when a retry can never
|
|
589
|
+
* double-process a write. 0 disables retries (default 2). */
|
|
587
590
|
maxRetries?: number;
|
|
588
591
|
/** Alias of `maxRetries`. The Python SDK calls this option `retries`, and these
|
|
589
592
|
* SDKs are published in lockstep as "the same surface" — so code ported between
|
|
@@ -611,10 +614,8 @@ export interface ClientOptions {
|
|
|
611
614
|
*
|
|
612
615
|
* This is the seam for anything the runtime cannot express through options.
|
|
613
616
|
* The one that matters in practice is a corporate egress proxy: Node's global
|
|
614
|
-
* fetch (undici) IGNORES `HTTP_PROXY` / `HTTPS_PROXY`, so behind such a proxy
|
|
615
|
-
*
|
|
616
|
-
* through, because `requests` and `reqwest` both read those variables. Pass a
|
|
617
|
-
* proxy-aware fetch and the three behave the same again:
|
|
617
|
+
* fetch (undici) IGNORES `HTTP_PROXY` / `HTTPS_PROXY`, so behind such a proxy no
|
|
618
|
+
* request leaves at all. Pass a proxy-aware fetch and they go through:
|
|
618
619
|
*
|
|
619
620
|
* import { ProxyAgent } from "undici";
|
|
620
621
|
* const agent = new ProxyAgent(process.env.HTTPS_PROXY!);
|
|
@@ -696,7 +697,7 @@ export declare class Client {
|
|
|
696
697
|
addTurn(userMsg: string, assistantMsg: string, userId?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
697
698
|
/** Bulk-ingest a large blob of text in one call. For backfilling.
|
|
698
699
|
* The call most worth an `opts.idempotencyKey`: a backfill that dies halfway and is
|
|
699
|
-
* re-run would otherwise
|
|
700
|
+
* re-run would otherwise ingest the whole blob a second time. */
|
|
700
701
|
addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
701
702
|
/** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
|
|
702
703
|
update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
|
|
@@ -812,6 +813,11 @@ export declare class Client {
|
|
|
812
813
|
}): AsyncGenerator<Memory>;
|
|
813
814
|
/** Collect ALL of a store's memories into an array (the text you stored, and its metadata). */
|
|
814
815
|
exportMemories(userId?: string): Promise<Memory[]>;
|
|
816
|
+
/** Collect ALL of a store's image memories into an array — the image-side pair of
|
|
817
|
+
* `exportMemories`. */
|
|
818
|
+
exportImages(userId?: string, opts?: {
|
|
819
|
+
pageSize?: number;
|
|
820
|
+
}): Promise<Memory[]>;
|
|
815
821
|
/**
|
|
816
822
|
* Fetch the bytes of an image memory — the picture the SERVICE holds.
|
|
817
823
|
*
|
package/dist/wontopos.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const VERSION = "2.2.
|
|
1
|
+
const VERSION = "2.2.39";
|
|
2
2
|
const RUNTIME = (() => {
|
|
3
3
|
const p = globalThis?.process;
|
|
4
4
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -13,7 +13,7 @@ function logDebug(msg) {
|
|
|
13
13
|
const DEFAULT_BASE_URL = "https://api.wontopos.com";
|
|
14
14
|
const DEFAULT_MODEL = "tablet-2";
|
|
15
15
|
const RETRY_ALWAYS = new Set([429]);
|
|
16
|
-
const RETRY_IF_IDEMPOTENT = new Set([502, 503]);
|
|
16
|
+
const RETRY_IF_IDEMPOTENT = new Set([408, 502, 503, 504]);
|
|
17
17
|
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
18
18
|
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
|
|
19
19
|
const MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
@@ -208,6 +208,7 @@ const KNOWN_SEARCH_KEYS = new Set([
|
|
|
208
208
|
]);
|
|
209
209
|
const RESERVED_SEARCH_KEYS = new Set(["user_id", "query", "max_results"]);
|
|
210
210
|
const KNOWN_RECALL_KEYS = new Set(["form", "tz", "limit", "context_limit"]);
|
|
211
|
+
const KNOWN_ENGRAM_KEYS = new Set(["form", "tz"]);
|
|
211
212
|
function checkSearchOpts(opts) {
|
|
212
213
|
checkOpts(opts, KNOWN_SEARCH_KEYS, "search");
|
|
213
214
|
}
|
|
@@ -222,8 +223,7 @@ function checkOpts(opts, known, what) {
|
|
|
222
223
|
const near = [...known].find((n) => n !== "extra" && editWithin(k, n, 2));
|
|
223
224
|
throw new Error(`unknown ${what} option ${JSON.stringify(k)}` +
|
|
224
225
|
(near ? ` — did you mean ${JSON.stringify(near)}?` : "") +
|
|
225
|
-
`.
|
|
226
|
-
`have looked like it worked.` +
|
|
226
|
+
`. It would have had no effect, and the call would have looked like it worked.` +
|
|
227
227
|
(known.has("extra")
|
|
228
228
|
? ` Pass it under \`extra\` if the service accepts it and this client does not know it yet.`
|
|
229
229
|
: ""));
|
|
@@ -392,25 +392,25 @@ export class Client {
|
|
|
392
392
|
withRetries(maxRetries) {
|
|
393
393
|
return this.clone({ maxRetries });
|
|
394
394
|
}
|
|
395
|
-
add(content, userId, metadata = {}, opts = {}) {
|
|
395
|
+
async add(content, userId, metadata = {}, opts = {}) {
|
|
396
396
|
const body = { user_id: this.uid(userId), content, metadata };
|
|
397
397
|
if (opts.image !== undefined)
|
|
398
398
|
body.image = normalizeImage(opts.image);
|
|
399
399
|
return this.post("/api/v1/memory/store", body, opts.idempotencyKey);
|
|
400
400
|
}
|
|
401
|
-
store(content, userId, metadata = {}, opts = {}) {
|
|
401
|
+
async store(content, userId, metadata = {}, opts = {}) {
|
|
402
402
|
return this.add(content, userId, metadata, opts);
|
|
403
403
|
}
|
|
404
|
-
addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
404
|
+
async addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
405
405
|
return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, opts.idempotencyKey);
|
|
406
406
|
}
|
|
407
|
-
addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
407
|
+
async addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
408
408
|
const body = { user_id: this.uid(userId), content, category };
|
|
409
409
|
if (timestamp)
|
|
410
410
|
body.timestamp = timestamp;
|
|
411
411
|
return this.post("/api/v1/memory/bulk-store", body, opts.idempotencyKey);
|
|
412
412
|
}
|
|
413
|
-
update(oldMemoryId, newContent, userId, opts = {}) {
|
|
413
|
+
async update(oldMemoryId, newContent, userId, opts = {}) {
|
|
414
414
|
return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts.idempotencyKey);
|
|
415
415
|
}
|
|
416
416
|
async search(query, userId, limit = 10, opts = {}) {
|
|
@@ -455,14 +455,15 @@ export class Client {
|
|
|
455
455
|
}
|
|
456
456
|
return this.post("/api/v1/memory/recall", withForm(body, opts.form, opts.tz));
|
|
457
457
|
}
|
|
458
|
-
engram(name, query, userId, opts = {}) {
|
|
458
|
+
async engram(name, query, userId, opts = {}) {
|
|
459
|
+
checkOpts(opts, KNOWN_ENGRAM_KEYS, "engram");
|
|
459
460
|
return this.post("/api/v1/engram/run", withForm({ name, user_id: this.uid(userId), query }, opts.form, opts.tz));
|
|
460
461
|
}
|
|
461
462
|
async history(userId) {
|
|
462
463
|
const r = await this.post("/api/v1/memory/history", { user_id: this.uid(userId) });
|
|
463
464
|
return asRecords(r.turns);
|
|
464
465
|
}
|
|
465
|
-
stats(userId) {
|
|
466
|
+
async stats(userId) {
|
|
466
467
|
return this.post("/api/v1/memory/stats", { user_id: this.uid(userId) });
|
|
467
468
|
}
|
|
468
469
|
async usage(days = 7) {
|
|
@@ -471,13 +472,13 @@ export class Client {
|
|
|
471
472
|
}
|
|
472
473
|
return this.request("GET", `/api/v1/won/usage?days=${days}`);
|
|
473
474
|
}
|
|
474
|
-
get(userId, memoryId) {
|
|
475
|
+
async get(userId, memoryId) {
|
|
475
476
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
476
477
|
throw new Error("memoryId is required — the id that add/store or listMemories returned.");
|
|
477
478
|
}
|
|
478
479
|
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId.trim() }).then((r) => asObj(r.memory));
|
|
479
480
|
}
|
|
480
|
-
listMemories(userId, opts = {}) {
|
|
481
|
+
async listMemories(userId, opts = {}) {
|
|
481
482
|
const body = { user_id: this.uid(userId), limit: opts.limit ?? 100 };
|
|
482
483
|
if (opts.cursor)
|
|
483
484
|
body.cursor = opts.cursor;
|
|
@@ -505,13 +506,19 @@ export class Client {
|
|
|
505
506
|
out.push(m);
|
|
506
507
|
return out;
|
|
507
508
|
}
|
|
508
|
-
|
|
509
|
+
async exportImages(userId, opts = {}) {
|
|
510
|
+
const out = [];
|
|
511
|
+
for await (const m of this.iterImages(userId, opts))
|
|
512
|
+
out.push(m);
|
|
513
|
+
return out;
|
|
514
|
+
}
|
|
515
|
+
async getImage(userId, memoryId) {
|
|
509
516
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
510
517
|
throw new Error("memoryId is required — the id that add/store or listImages returned.");
|
|
511
518
|
}
|
|
512
519
|
return this.requestBytes("/api/v1/memory/image", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
513
520
|
}
|
|
514
|
-
forgetImage(userId, memoryId, opts = {}) {
|
|
521
|
+
async forgetImage(userId, memoryId, opts = {}) {
|
|
515
522
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
516
523
|
throw new Error("memoryId is required — the id of the memory whose image you want removed.");
|
|
517
524
|
}
|
|
@@ -520,7 +527,7 @@ export class Client {
|
|
|
520
527
|
body.preview = true;
|
|
521
528
|
return this.request("DELETE", "/api/v1/memory/image", body);
|
|
522
529
|
}
|
|
523
|
-
listImages(userId, opts = {}) {
|
|
530
|
+
async listImages(userId, opts = {}) {
|
|
524
531
|
const body = { user_id: this.uid(userId) };
|
|
525
532
|
if (opts.limit !== undefined)
|
|
526
533
|
body.limit = opts.limit;
|
|
@@ -550,7 +557,7 @@ export class Client {
|
|
|
550
557
|
throw new Error(`stopped after ${MAX_PAGES} pages — the store did not end. This is a truncated ` +
|
|
551
558
|
`answer, not the whole store.`);
|
|
552
559
|
}
|
|
553
|
-
revisions(userId, opts = {}) {
|
|
560
|
+
async revisions(userId, opts = {}) {
|
|
554
561
|
const body = {};
|
|
555
562
|
if (opts.include !== undefined)
|
|
556
563
|
body.include = opts.include;
|
|
@@ -563,13 +570,13 @@ export class Client {
|
|
|
563
570
|
body.user_id = this.uid(userId);
|
|
564
571
|
return this.post("/api/v1/won/revisions", body);
|
|
565
572
|
}
|
|
566
|
-
lineage(userId, memoryId) {
|
|
573
|
+
async lineage(userId, memoryId) {
|
|
567
574
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
568
575
|
throw new Error("memoryId is required — the memory whose history you want.");
|
|
569
576
|
}
|
|
570
577
|
return this.post("/api/v1/memory/lineage", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
571
578
|
}
|
|
572
|
-
bySpeaker(speaker, userId, opts = {}) {
|
|
579
|
+
async bySpeaker(speaker, userId, opts = {}) {
|
|
573
580
|
if (!speaker || typeof speaker !== "string" || speaker.trim() === "") {
|
|
574
581
|
throw new Error('speaker is required — "me" for the assistant, or a person\'s name.');
|
|
575
582
|
}
|
|
@@ -593,41 +600,42 @@ export class Client {
|
|
|
593
600
|
async listEngrams() {
|
|
594
601
|
const data = await this.request("GET", "/api/v1/engram");
|
|
595
602
|
return {
|
|
603
|
+
...data,
|
|
596
604
|
engrams: asRecords(data.engrams),
|
|
597
605
|
forms: asRecords(data.forms),
|
|
598
606
|
note: typeof data.note === "string" ? data.note : undefined,
|
|
599
607
|
};
|
|
600
608
|
}
|
|
601
|
-
createStore(userId) {
|
|
609
|
+
async createStore(userId) {
|
|
602
610
|
return this.post("/api/v1/memory/collection", { user_id: this.uid(userId) });
|
|
603
611
|
}
|
|
604
612
|
async listStores() {
|
|
605
613
|
const r = await this.request("GET", "/api/v1/memory/collections");
|
|
606
614
|
return asRecords(r.collections);
|
|
607
615
|
}
|
|
608
|
-
deleteStore(userId) {
|
|
616
|
+
async deleteStore(userId) {
|
|
609
617
|
if (typeof userId !== "string" || !userId.trim())
|
|
610
618
|
throw new Error("userId is required (a non-blank string) — deleteStore never falls back to the default store.");
|
|
611
619
|
warnIfStoreIdCollapses(userId);
|
|
612
620
|
return this.request("DELETE", "/api/v1/memory/collection", { user_id: userId });
|
|
613
621
|
}
|
|
614
|
-
addSpeaker(speaker, userId) {
|
|
622
|
+
async addSpeaker(speaker, userId) {
|
|
615
623
|
return this.post("/api/v1/memory/speakers", { user_id: this.uid(userId), speaker });
|
|
616
624
|
}
|
|
617
625
|
async listSpeakers(userId) {
|
|
618
626
|
const r = await this.request("GET", `/api/v1/memory/speakers?user_id=${encodeURIComponent(this.uid(userId))}`);
|
|
619
627
|
return { ...r, speakers: asRecords(r?.speakers) };
|
|
620
628
|
}
|
|
621
|
-
removeSpeaker(speaker, userId) {
|
|
629
|
+
async removeSpeaker(speaker, userId) {
|
|
622
630
|
return this.request("DELETE", "/api/v1/memory/speakers", { user_id: this.uid(userId), speaker });
|
|
623
631
|
}
|
|
624
|
-
delete(userId, memoryId) {
|
|
632
|
+
async delete(userId, memoryId) {
|
|
625
633
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
626
634
|
throw new Error("memoryId is required (non-blank). To delete every memory in a store, call deleteAll(userId) explicitly.");
|
|
627
635
|
}
|
|
628
636
|
return this.post("/api/v1/memory/forget", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
629
637
|
}
|
|
630
|
-
deleteAll(userId) {
|
|
638
|
+
async deleteAll(userId) {
|
|
631
639
|
if (typeof userId !== "string" || !userId.trim())
|
|
632
640
|
throw new Error("userId is required (a non-blank string) for deleteAll — anything else would wipe the default store.");
|
|
633
641
|
warnIfStoreIdCollapses(userId);
|
|
@@ -762,7 +770,9 @@ export class Client {
|
|
|
762
770
|
if (e?.name === "AbortError" || att.signal.aborted) {
|
|
763
771
|
throw new APIConnectionError(0, this.abortMessage(att.why()));
|
|
764
772
|
}
|
|
765
|
-
|
|
773
|
+
if (e instanceof WosError)
|
|
774
|
+
throw e;
|
|
775
|
+
throw new APIConnectionError(0, `network error: ${e?.message ?? e}`);
|
|
766
776
|
}
|
|
767
777
|
if (buf.byteLength === 0) {
|
|
768
778
|
throw new WosError(res.status, "empty image body — the service returned no bytes");
|
|
@@ -895,7 +905,14 @@ export class Client {
|
|
|
895
905
|
}
|
|
896
906
|
if (e instanceof WosError)
|
|
897
907
|
throw e;
|
|
898
|
-
|
|
908
|
+
if (!(IDEMPOTENT_METHODS.has(method.toUpperCase()) && attempt + 1 < attempts)) {
|
|
909
|
+
throw new APIConnectionError(0, `network error: ${e?.message ?? e}`);
|
|
910
|
+
}
|
|
911
|
+
att.clear();
|
|
912
|
+
const delay = this.backoffMs(attempt);
|
|
913
|
+
logDebug(`${method} ${logPath} — body dropped mid-stream, retrying in ${delay}ms (attempt ${attempt + 1}/${attempts})`);
|
|
914
|
+
await this.backoffSleep(delay, deadlineAt);
|
|
915
|
+
continue;
|
|
899
916
|
}
|
|
900
917
|
finally {
|
|
901
918
|
att.clear();
|