wontopos 2.2.44 → 2.2.46
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 +29 -23
- package/dist/cjs/wontopos.d.cts +68 -55
- package/dist/cjs/wontopos.js +28 -13
- package/dist/wontopos.d.ts +68 -55
- package/dist/wontopos.js +28 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ npm install wontopos
|
|
|
5
5
|
```
|
|
6
6
|
|
|
7
7
|
Get an API key in the [console](https://wontopos.com). Keys look like `wos-live-...`;
|
|
8
|
-
|
|
8
|
+
`Client.fromEnv()` reads `WONTOPOS_API_KEY` from the environment instead.
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
11
|
import { Client } from "wontopos";
|
|
@@ -24,8 +24,8 @@ const ctx = await mem.recall("what does alice drink?", "alice");
|
|
|
24
24
|
## Why
|
|
25
25
|
|
|
26
26
|
- **The same in every language** — identical recall whichever language a memory was written in (Korean · Japanese · Chinese · English).
|
|
27
|
-
- **No LLM
|
|
28
|
-
- **Bounded retrieval** — `recall()` returns
|
|
27
|
+
- **No LLM reads your memories** — no language model reads your stored memories, on any model. You pay retrieval, not generation.
|
|
28
|
+
- **Bounded retrieval** — `recall()` returns the recent turns, at most `limit` long-term memories (default 10, up to 20) and at most `context_limit` rows of the surrounding conversation (default 10, up to 20), however much you've stored.
|
|
29
29
|
|
|
30
30
|
## Methods
|
|
31
31
|
|
|
@@ -35,10 +35,10 @@ Where the store id goes depends on whether the method takes a payload. A method
|
|
|
35
35
|
writes or searches puts the payload first and the store second — `mem.add("...", "alice")`,
|
|
36
36
|
`mem.search("...", "alice")`. A method that only addresses a store takes it first —
|
|
37
37
|
`mem.listMemories("alice")`, `mem.stats("alice")`, `mem.delete("alice", memoryId)`.
|
|
38
|
-
Most of them let you leave it out for the client's default store.
|
|
39
|
-
positionally and cannot — `get`, `delete`, `lineage` and `forgetImage` want
|
|
40
|
-
`undefined` there, because a second required argument follows it. Python
|
|
41
|
-
all
|
|
38
|
+
Most of them let you leave it out for the client's default store. Five take it
|
|
39
|
+
positionally and cannot — `get`, `getImage`, `delete`, `lineage` and `forgetImage` want
|
|
40
|
+
an explicit `undefined` there, because a second required argument follows it. Python
|
|
41
|
+
omits it in all five, and Rust takes it positionally (`None` for the default store, where there is one).
|
|
42
42
|
Let your editor confirm the order: every signature is typed. A store is one isolated memory space per end-user, agent, or topic. WHO said each memory inside a store is the `speaker` tag below — storing the assistant's own words never needs a separate id.
|
|
43
43
|
|
|
44
44
|
## Who said it (speakers)
|
|
@@ -56,19 +56,22 @@ await mem.search("what did Bob say about deadlines?", "alice", 10, { speaker: "B
|
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
`listSpeakers()` shows the registered people with per-person memory counts;
|
|
59
|
-
`removeSpeaker()` unregisters (memories stay
|
|
59
|
+
`removeSpeaker()` unregisters (memories stay; the tag goes, except on `tablet-2`, which keeps it on memories already stored). A store registers
|
|
60
60
|
up to 50 people to start (a limit we plan to raise); `"me"` never needs
|
|
61
61
|
registration and never counts against it.
|
|
62
62
|
|
|
63
63
|
## Recall caching
|
|
64
64
|
|
|
65
|
-
Opt in per search and repeated
|
|
66
|
-
|
|
65
|
+
Opt in per search and a repeated query reuses the previous result at 10% of the
|
|
66
|
+
normal rate; an extended one reuses part of it (Tablet and Scroll models).
|
|
67
67
|
|
|
68
68
|
It is not free to turn on: the FIRST call writes the cache and bills the query
|
|
69
|
-
tokens at 2x for a `5m` TTL, 3x for `1h`.
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
tokens at 2x for a `5m` TTL, 3x for `1h`. The same query again inside the TTL bills
|
|
70
|
+
its query and its results at 0.1x. A query that extends the cached one (its text plus
|
|
71
|
+
more) bills the cached part at 0.1x, the new part at the write rate and its results
|
|
72
|
+
at the normal rate. A store keeps one cached query per model and set of options, so a
|
|
73
|
+
different query replaces it. So it pays for a query you repeat or extend, and costs
|
|
74
|
+
more for one you issue once — do not switch it on globally. Any write to the store invalidates its cache
|
|
72
75
|
at once, so a hit can never predate a new memory.
|
|
73
76
|
|
|
74
77
|
```ts
|
|
@@ -82,11 +85,11 @@ Built in, no configuration needed:
|
|
|
82
85
|
|
|
83
86
|
- **Automatic retries** — 429 and a 409 for a write already in flight always, and 408 /
|
|
84
87
|
502 / 503 / 504 or a connection error only when a retry cannot double-process a write.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
`
|
|
89
|
-
|
|
88
|
+
A write may already have landed when any of those four comes back, so a POST write gets
|
|
89
|
+
429, that 409 and connect-level failures only. The calls that are safe to send again
|
|
90
|
+
retry all four: the reads sent as POST (`search`, `recall`, `get`, `listMemories`,
|
|
91
|
+
`stats`, ...) and the GET and DELETE calls (`ping`, `listStores`, `listSpeakers`,
|
|
92
|
+
`listModels`, `listEngrams`, `usage`, `deleteStore`, `removeSpeaker`, `forgetImage`). Twice, with exponential backoff + jitter, honoring the server's
|
|
90
93
|
`Retry-After` up to 30s. Tune with `new Client({ maxRetries })`; `0` disables.
|
|
91
94
|
- **Redirects refused** — the API key never follows a 3xx to another host.
|
|
92
95
|
- **Timeouts** — 30s per attempt by default (`timeoutMs`), and a total budget for
|
|
@@ -112,6 +115,7 @@ This one needs `npm i undici` — Node's global `fetch` is built on undici, but
|
|
|
112
115
|
package itself is not installed for you.
|
|
113
116
|
|
|
114
117
|
```ts
|
|
118
|
+
import { Client } from "wontopos";
|
|
115
119
|
import { ProxyAgent } from "undici";
|
|
116
120
|
|
|
117
121
|
const agent = new ProxyAgent(process.env.HTTPS_PROXY!);
|
|
@@ -146,8 +150,9 @@ Built in, none of it configurable off:
|
|
|
146
150
|
Every method returns a documented shape — `StoreResult`, `UpdateResult`,
|
|
147
151
|
`RecallResult`, `EngramResult`, `StatsResult`, `SpeakersList`, `Memory[]`, ... —
|
|
148
152
|
each with an index signature, so new server fields flow through without an SDK
|
|
149
|
-
update. `
|
|
150
|
-
`
|
|
153
|
+
update. `searchSelf`'s result and `getImage`'s bytes are built by the client and
|
|
154
|
+
carry only their own fields. `search` options are typed too (`SearchOptions`: `cache_control`,
|
|
155
|
+
`speaker`, plus `extra` for pass-through).
|
|
151
156
|
|
|
152
157
|
```ts
|
|
153
158
|
import { type Memory } from "wontopos";
|
|
@@ -156,7 +161,8 @@ const hits: Memory[] = await mem.search("what did Bob say?", "alice", 10, { spea
|
|
|
156
161
|
console.log(hits[0].speaker, hits[0].time_bucket); // typed: string | undefined
|
|
157
162
|
```
|
|
158
163
|
|
|
159
|
-
`hits`
|
|
164
|
+
`hits` holds memories best-first, then the assistant's own words and image memories in
|
|
165
|
+
their own order. Take it in the order given. `similarity` is a raw
|
|
160
166
|
closeness score, not the ranking key: what produces the order is internal and is
|
|
161
167
|
not returned, so sorting by `similarity` overrides the ranking and makes results
|
|
162
168
|
worse. There is no `score` field.
|
|
@@ -206,14 +212,14 @@ Found something wrong, or something that looks unsafe? Tell us — every report
|
|
|
206
212
|
- Security: <https://wontopos.com/contact?topic=security> (also published at
|
|
207
213
|
[`/.well-known/security.txt`](https://wontopos.com/.well-known/security.txt))
|
|
208
214
|
|
|
209
|
-
Include the SDK version (
|
|
215
|
+
Include the SDK version (the one in package.json) and the language. If it involves a store id or a
|
|
210
216
|
memory, describe the shape rather than pasting the contents — we do not need your
|
|
211
217
|
data to fix it.
|
|
212
218
|
|
|
213
219
|
## Changelog
|
|
214
220
|
|
|
215
221
|
The three clients release in lockstep — same version, same surface, same day. Patch
|
|
216
|
-
releases are additive.
|
|
222
|
+
releases are additive. Nine inside 2.2 were not, deliberately and each with its
|
|
217
223
|
reason; the changelog lists them.
|
|
218
224
|
|
|
219
225
|
See [CHANGELOG.md](https://github.com/wontopos/wontopos-node/blob/main/CHANGELOG.md).
|
package/dist/cjs/wontopos.d.cts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* (`createStore`) or calls return 404. Every account starts with a `default` store,
|
|
15
15
|
* so with no `userId` anywhere the zero-setup path just works.
|
|
16
16
|
*
|
|
17
|
-
* The API key picks *which memory* (your
|
|
17
|
+
* The API key picks *which memory* (your workspace); `model` picks *which engine*
|
|
18
18
|
* reads it. Models on the shared pool read the same memory, so you can store with
|
|
19
19
|
* one and recall with another; `listModels()` reports each model's `memory` as
|
|
20
20
|
* `"shared"` or `"isolated"`, and an isolated one starts empty. Set a default in
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
* `mem.withModel("tablet-1").recall(...)`.
|
|
23
23
|
*
|
|
24
24
|
* Recall quality does not depend on which language a memory was written in: a
|
|
25
|
-
* memory stored in one language is found by a question asked in another.
|
|
26
|
-
*
|
|
25
|
+
* memory stored in one language is found by a question asked in another. No language
|
|
26
|
+
* model reads your stored memories.
|
|
27
27
|
*
|
|
28
28
|
* Reliability: every call retries transient failures with exponential backoff +
|
|
29
29
|
* jitter, honoring `Retry-After` up to 30s — 429 and a 409 that says another write
|
|
30
30
|
* to the store was in flight, always; 408/502/503/504 and connection errors only when
|
|
31
|
-
* a retry can never double-process a write (
|
|
32
|
-
* time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
|
|
31
|
+
* a retry can never double-process a write (reads, the DELETE calls, or a failure at
|
|
32
|
+
* connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
|
|
33
33
|
* the `withTimeout()` / `withRetries()` clones.
|
|
34
34
|
*
|
|
35
35
|
* Debugging: set `WONTOPOS_LOG=debug` to log method/path/status/timing/retries
|
|
@@ -89,7 +89,9 @@ export declare class BadRequestError extends WosError {
|
|
|
89
89
|
/** 401 — the API key is missing, wrong, or revoked. */
|
|
90
90
|
export declare class AuthenticationError extends WosError {
|
|
91
91
|
}
|
|
92
|
-
/** 402 — no card on file
|
|
92
|
+
/** 402 — no card on file, the balance is depleted, or the month's spend cap for the
|
|
93
|
+
* account's tier is reached (`details.reason` is `tier_cap_reached`). Top up to
|
|
94
|
+
* continue; a top-up also raises the tier. */
|
|
93
95
|
export declare class PaymentRequiredError extends WosError {
|
|
94
96
|
}
|
|
95
97
|
/** 403 — the key/model isn't allowed to do this. */
|
|
@@ -194,8 +196,9 @@ export interface StatusResult {
|
|
|
194
196
|
/**
|
|
195
197
|
* An image attached to a memory, passed to `add`/`store` via `opts.image`.
|
|
196
198
|
*
|
|
197
|
-
* Both edges must be 700px or more
|
|
198
|
-
*
|
|
199
|
+
* Both edges must be 700px or more and the long edge at most 2500px, and the image
|
|
200
|
+
* file at most 7MB; anything else is refused (400) with the reason. A request body is
|
|
201
|
+
* capped at 10MB.
|
|
199
202
|
*
|
|
200
203
|
* What the service keeps is NOT your original. An image whose long edge is over
|
|
201
204
|
* 1568px is downscaled to 1568 before anything else happens, and that smaller
|
|
@@ -241,8 +244,9 @@ export interface ImageBytes {
|
|
|
241
244
|
* The image as stored. Write it to a file, or wrap it in a Blob to show it.
|
|
242
245
|
*
|
|
243
246
|
* This is the service's copy, not necessarily your upload: anything over 1568px
|
|
244
|
-
* on its long edge was downscaled on the way in, and re-encoded to
|
|
245
|
-
* was a JPEG (see `ImageInput`). Take the file extension from
|
|
247
|
+
* on its long edge (2500 at most) was downscaled on the way in, and re-encoded to
|
|
248
|
+
* WebP unless it was a JPEG (see `ImageInput`). Take the file extension from
|
|
249
|
+
* `contentType`.
|
|
246
250
|
*/
|
|
247
251
|
bytes: Uint8Array;
|
|
248
252
|
/** Sniffed from the BYTES, not from whatever the upload was named — e.g. `image/jpeg`. */
|
|
@@ -317,7 +321,8 @@ export interface LineageStep {
|
|
|
317
321
|
is_current?: boolean;
|
|
318
322
|
[key: string]: unknown;
|
|
319
323
|
}
|
|
320
|
-
/** `lineage` → the
|
|
324
|
+
/** `lineage` → the edits that followed one memory, from the id passed forward to the
|
|
325
|
+
* version in force, oldest first. */
|
|
321
326
|
export interface LineageResult {
|
|
322
327
|
memory_id?: string;
|
|
323
328
|
chain: LineageStep[];
|
|
@@ -360,8 +365,8 @@ export interface SearchResult {
|
|
|
360
365
|
/** Image memories the answer carried (one by default on an image-capable model,
|
|
361
366
|
* up to `max_images`); `[]` when there were none. */
|
|
362
367
|
images: Memory[];
|
|
363
|
-
/** Re-ask passes
|
|
364
|
-
* than you asked for means
|
|
368
|
+
/** Re-ask passes that added memories. Present only when `verify` above 0 was sent;
|
|
369
|
+
* lower than you asked for means a pass found nothing new, or could not run. */
|
|
365
370
|
verify_used?: number;
|
|
366
371
|
[key: string]: unknown;
|
|
367
372
|
}
|
|
@@ -414,7 +419,7 @@ export interface UsageResult {
|
|
|
414
419
|
output_tokens?: number;
|
|
415
420
|
since?: string;
|
|
416
421
|
};
|
|
417
|
-
/** The workspace this key belongs to, over the window. */
|
|
422
|
+
/** The workspace this key belongs to, over the window: every key in it, not only this one. */
|
|
418
423
|
workspace?: {
|
|
419
424
|
workspace_id?: string | null;
|
|
420
425
|
requests?: number;
|
|
@@ -542,9 +547,8 @@ export interface SpeakersList {
|
|
|
542
547
|
[key: string]: unknown;
|
|
543
548
|
}
|
|
544
549
|
/**
|
|
545
|
-
* Narrow a search to part of a store.
|
|
546
|
-
*
|
|
547
|
-
* many matches sit inside it.
|
|
550
|
+
* Narrow a search to part of a store. A narrow filter can return fewer than `limit`
|
|
551
|
+
* even when the store holds more inside it.
|
|
548
552
|
*
|
|
549
553
|
* Retrieval behaves the same in every language, so these behave
|
|
550
554
|
* identically in every language. Unlisted keys are dropped by the API rather than
|
|
@@ -565,7 +569,8 @@ export interface SearchFilters {
|
|
|
565
569
|
time_from?: string;
|
|
566
570
|
time_to?: string;
|
|
567
571
|
/** WHEN THE CONTENT HAPPENED (`metadata.event_date`), not when it was written —
|
|
568
|
-
* this is the one you usually want.
|
|
572
|
+
* this is the one you usually want. A memory stored without an `event_date` is
|
|
573
|
+
* matched by when it was stored. */
|
|
569
574
|
event_from?: string;
|
|
570
575
|
event_to?: string;
|
|
571
576
|
/** Drop matches the engine scored below this importance (0–1). */
|
|
@@ -578,9 +583,11 @@ export declare function _resetFilterWarnings(): void;
|
|
|
578
583
|
* could not be told from an option that worked. Put a genuinely new one under
|
|
579
584
|
* `extra`. */
|
|
580
585
|
export interface SearchOptions {
|
|
581
|
-
/** Recall caching.
|
|
582
|
-
*
|
|
583
|
-
*
|
|
586
|
+
/** Recall caching. The same query again inside the TTL bills at 0.1×; one that
|
|
587
|
+
* extends the cached query bills only its cached part at 0.1×, the new part at the
|
|
588
|
+
* write rate and its results at the normal rate. The FIRST call writes the cache
|
|
589
|
+
* and bills the query tokens at 2× (`5m`) or 3× (`1h`), so this only pays for a
|
|
590
|
+
* query you repeat or extend. Do not enable it globally. */
|
|
584
591
|
cache_control?: {
|
|
585
592
|
ttl: "5m" | "1h";
|
|
586
593
|
};
|
|
@@ -591,9 +598,9 @@ export interface SearchOptions {
|
|
|
591
598
|
/**
|
|
592
599
|
* Re-ask passes, 0–3. After the first retrieval the service asks the engine again up
|
|
593
600
|
* to this many times, each time telling it what was already returned so it skips
|
|
594
|
-
* those and reaches further back.
|
|
601
|
+
* those and reaches further back. Raising it adds no language model call at any
|
|
595
602
|
* value. Stops early when a pass finds nothing new; `verify_used` in the response
|
|
596
|
-
* says how many
|
|
603
|
+
* says how many passes added memories.
|
|
597
604
|
*
|
|
598
605
|
* Each pass is another engine call and can add up to `limit` more memories, so it
|
|
599
606
|
* costs more — you are billed for what is delivered. Default 0. Worth it on questions
|
|
@@ -652,9 +659,10 @@ export interface SearchOptions {
|
|
|
652
659
|
export interface WriteOptions {
|
|
653
660
|
idempotencyKey?: string;
|
|
654
661
|
/**
|
|
655
|
-
* An image to store alongside the text
|
|
656
|
-
* `
|
|
657
|
-
*
|
|
662
|
+
* An image to store alongside the text, read by `add` (and `store`) only: `addTurn`,
|
|
663
|
+
* `addBulk` and `update` warn about it and do not send it. Needs a model that lists
|
|
664
|
+
* `images` in its `listModels()` capabilities; any other refuses the write rather than
|
|
665
|
+
* store the caption and quietly drop the image.
|
|
658
666
|
*
|
|
659
667
|
* A caption is required: the service refuses empty `content` (400) even with an
|
|
660
668
|
* image attached.
|
|
@@ -858,15 +866,16 @@ export declare class Client {
|
|
|
858
866
|
addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
859
867
|
/** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
|
|
860
868
|
update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
|
|
861
|
-
/** Search a store's memories.
|
|
869
|
+
/** Search a store's memories. They come most relevant first; the assistant's own
|
|
870
|
+
* words and image memories, when there are any, follow them in their own order.
|
|
862
871
|
*
|
|
863
872
|
* `limit` is 5-20, and out of range is refused rather than clamped: asking for 50
|
|
864
873
|
* and silently receiving 20 reads as "that is all there is". The default is 10.
|
|
865
874
|
*
|
|
866
|
-
* `limit` bounds `memories`, not the returned array
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
* it as well, so it can hold more than `limit`. They are billed either way. Size a
|
|
875
|
+
* `limit` bounds `memories`, not the returned array, and each `verify` pass can add
|
|
876
|
+
* up to `limit` more. The assistant's own words (on a model that lists
|
|
877
|
+
* `self_memories` in its capabilities) and image memories (one unless `max_images`
|
|
878
|
+
* says otherwise, on a model that lists `images`) come back after them in it as well, so it can hold more than `limit`. They are billed either way. Size a
|
|
870
879
|
* prompt window on the array you get back, not on `limit`. `searchFull()` hands the
|
|
871
880
|
* fields back apart.
|
|
872
881
|
*
|
|
@@ -890,7 +899,7 @@ export declare class Client {
|
|
|
890
899
|
* const r = await mem.searchFull("the day we moved", "alice", 10,
|
|
891
900
|
* { max_images: 3, verify: 2 });
|
|
892
901
|
* r.images.length; // the photos, apart from the text memories
|
|
893
|
-
* r.verify_used; //
|
|
902
|
+
* r.verify_used; // re-ask passes that added memories (each billed for what it added)
|
|
894
903
|
*
|
|
895
904
|
* Each option needs a model that lists it in its `listModels()` capabilities
|
|
896
905
|
* (`images`, `re_ask`) and is refused (403) on any other rather than accepted and
|
|
@@ -927,7 +936,8 @@ export declare class Client {
|
|
|
927
936
|
}): Promise<EngramResult>;
|
|
928
937
|
/** Recent conversation turns (short-term memory). */
|
|
929
938
|
history(userId?: string): Promise<HistoryTurn[]>;
|
|
930
|
-
/** Memory counts for a store: { total_memories, short_term_turns }.
|
|
939
|
+
/** Memory counts for a store: { total_memories, short_term_turns }. `total_memories`
|
|
940
|
+
* can be larger than the number `listMemories` lists. */
|
|
931
941
|
stats(userId?: string): Promise<StatsResult>;
|
|
932
942
|
/**
|
|
933
943
|
* What this key has spent, and what is left — the numbers behind "can I keep going?".
|
|
@@ -935,18 +945,17 @@ export declare class Client {
|
|
|
935
945
|
* Free: it carries no charge and skips the balance gate, because an account at zero
|
|
936
946
|
* still has to be able to find out why. Rate-limited instead.
|
|
937
947
|
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
* because that is what gates the next call whichever key makes it.
|
|
948
|
+
* `key` is THIS key's own lifetime spend. `workspace` and `stores` cover the window
|
|
949
|
+
* for the workspace this key belongs to, every key in it included. `balance_cents` is
|
|
950
|
+
* account-wide, because that is what gates the next call whichever key makes it.
|
|
941
951
|
*
|
|
942
952
|
* const u = await mem.usage(7);
|
|
943
953
|
* if (u.balance_cents! < 100) stop();
|
|
944
954
|
*/
|
|
945
955
|
usage(days?: number): Promise<UsageResult>;
|
|
946
956
|
/** Fetch ONE memory by id — the text you stored, and its metadata.
|
|
947
|
-
* The id is what `add`/`store` or `listMemories` returned.
|
|
948
|
-
*
|
|
949
|
-
* or an invalidated memory rejects with `NotFoundError`. Pass `undefined` as
|
|
957
|
+
* The id is what `add`/`store` or `listMemories` returned. An unknown id or one
|
|
958
|
+
* from another store rejects with `NotFoundError`. Pass `undefined` as
|
|
950
959
|
* `userId` for the default store — it is positional here. */
|
|
951
960
|
get(userId: string | undefined, memoryId: string): Promise<Memory>;
|
|
952
961
|
/** List a store's stored memories — the text you stored, plus its metadata.
|
|
@@ -992,9 +1001,10 @@ export declare class Client {
|
|
|
992
1001
|
* Fetch the bytes of an image memory — the picture the SERVICE holds.
|
|
993
1002
|
*
|
|
994
1003
|
* Not necessarily your upload, in size OR in format: an image whose long edge was
|
|
995
|
-
* over 1568px was downscaled to 1568 on the way in and re-encoded —
|
|
996
|
-
* formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG
|
|
997
|
-
*
|
|
1004
|
+
* over 1568px (2500 at most) was downscaled to 1568 on the way in and re-encoded —
|
|
1005
|
+
* lossless formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG;
|
|
1006
|
+
* kept as sent when the re-encode came out bigger — and that smaller picture is what
|
|
1007
|
+
* is stored and comes back here. Nothing on our side ever
|
|
998
1008
|
* uses more than 1568, so the extra pixels would be bytes nobody reads.
|
|
999
1009
|
* Keep your own copy for the full-resolution file.
|
|
1000
1010
|
*
|
|
@@ -1099,11 +1109,13 @@ export declare class Client {
|
|
|
1099
1109
|
skipIds?: string[];
|
|
1100
1110
|
}): Promise<RevisionsResult>;
|
|
1101
1111
|
/**
|
|
1102
|
-
* The
|
|
1112
|
+
* The edits that followed one memory: from the id you pass forward to the version in
|
|
1113
|
+
* force, oldest first.
|
|
1103
1114
|
*
|
|
1104
|
-
* `is_current` marks the version in force.
|
|
1105
|
-
* say, and when did it change"
|
|
1106
|
-
*
|
|
1115
|
+
* `is_current` marks the version in force. The chain runs forward only, so to answer
|
|
1116
|
+
* "what did this used to say, and when did it change" pass the first version's id;
|
|
1117
|
+
* the current version's id (what `search` returns) comes back alone. `revisions()`
|
|
1118
|
+
* tells you HOW MUCH a store moved, this tells you what happened to one fact.
|
|
1107
1119
|
*/
|
|
1108
1120
|
lineage(userId: string | undefined, memoryId: string): Promise<LineageResult>;
|
|
1109
1121
|
/**
|
|
@@ -1125,8 +1137,7 @@ export declare class Client {
|
|
|
1125
1137
|
* `AuthenticationError` (bad key), `PaymentRequiredError` (valid key but no card /
|
|
1126
1138
|
* depleted balance), or `APIConnectionError` (unreachable). Makes one metered request. */
|
|
1127
1139
|
ping(): Promise<boolean>;
|
|
1128
|
-
/** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`.
|
|
1129
|
-
* Needs no API key. */
|
|
1140
|
+
/** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`. */
|
|
1130
1141
|
listModels(): Promise<ModelInfo[]>;
|
|
1131
1142
|
/**
|
|
1132
1143
|
* The engrams (and delivery forms) the selected model can actually run.
|
|
@@ -1159,7 +1170,9 @@ export declare class Client {
|
|
|
1159
1170
|
addSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
|
|
1160
1171
|
/** The store's registered people, each with its memory count. */
|
|
1161
1172
|
listSpeakers(userId?: string): Promise<SpeakersList>;
|
|
1162
|
-
/** Unregister a person. Their memories stay
|
|
1173
|
+
/** Unregister a person. Their memories stay, and the name can no longer tag a new
|
|
1174
|
+
* memory or filter a search. The tag goes from stored memories too, except on
|
|
1175
|
+
* `tablet-2`, which keeps it on memories already stored with it. Retried like
|
|
1163
1176
|
* `deleteStore`, with the same note on a 404 after an ambiguous failure. */
|
|
1164
1177
|
removeSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
|
|
1165
1178
|
/** Delete a single memory by id. Pass `undefined` as `userId` for the default
|
|
@@ -1209,12 +1222,12 @@ export declare class Client {
|
|
|
1209
1222
|
*
|
|
1210
1223
|
* Retried: 429, and a 409 whose error carries `retry_after_ms` (another write to the
|
|
1211
1224
|
* store was in flight; nothing was stored), on every method; 408/502/503/504 and
|
|
1212
|
-
* dropped connections on
|
|
1213
|
-
*
|
|
1214
|
-
*
|
|
1215
|
-
*
|
|
1216
|
-
* only reads that the deadline cuts short. Any other retry cut
|
|
1217
|
-
* deadline error: a write may have been applied.
|
|
1225
|
+
* dropped connections on a call that only reads (an idempotent method, or a POST
|
|
1226
|
+
* that only reads); connect failures on every method. A retry whose wait is over the
|
|
1227
|
+
* cap, or does not fit the deadline, is not made: the call fails with the error of
|
|
1228
|
+
* the response it has. So does a retry the deadline leaves no time to send, and the
|
|
1229
|
+
* retry of a call that only reads that the deadline cuts short. Any other retry cut
|
|
1230
|
+
* short stays a status-0 deadline error: a write may have been applied.
|
|
1218
1231
|
*/
|
|
1219
1232
|
private send;
|
|
1220
1233
|
/**
|
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.GoneError = 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.46";
|
|
7
7
|
const RUNTIME = (() => {
|
|
8
8
|
const p = globalThis?.process;
|
|
9
9
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -508,7 +508,7 @@ const KNOWN_ENGRAM_KEYS = new Set(["form", "tz"]);
|
|
|
508
508
|
function checkSearchOpts(opts) {
|
|
509
509
|
checkOpts(opts, KNOWN_SEARCH_KEYS, "search");
|
|
510
510
|
const o = opts;
|
|
511
|
-
optRange(o.max_images
|
|
511
|
+
optRange(o.max_images != null ? o.max_images : o.extra?.max_images, "max_images", 0, MAX_IMAGES_MAX);
|
|
512
512
|
}
|
|
513
513
|
function checkOpts(opts, known, what) {
|
|
514
514
|
for (const k of Object.keys(opts)) {
|
|
@@ -558,6 +558,17 @@ function warnOnceUnknownKeys(obj, known, warned, message) {
|
|
|
558
558
|
console.warn(message(k));
|
|
559
559
|
}
|
|
560
560
|
}
|
|
561
|
+
function givenOpts(known) {
|
|
562
|
+
return Object.fromEntries(Object.entries(known).filter(([, v]) => v !== null));
|
|
563
|
+
}
|
|
564
|
+
const ADD_KEYS = new Set(["idempotencyKey", "image"]);
|
|
565
|
+
const WRITE_KEYS = new Set(["idempotencyKey"]);
|
|
566
|
+
const warnedWriteKeys = new Set();
|
|
567
|
+
function writeKey(opts, takesImage = false) {
|
|
568
|
+
warnOnceUnknownKeys(opts, takesImage ? ADD_KEYS : WRITE_KEYS, warnedWriteKeys, (k) => `wontopos: write option ${JSON.stringify(k)} is not sent. ` +
|
|
569
|
+
(takesImage ? "add takes idempotencyKey and image." : "This call takes idempotencyKey only."));
|
|
570
|
+
return opts.idempotencyKey;
|
|
571
|
+
}
|
|
561
572
|
function warnOnUnknownFilters(filters) {
|
|
562
573
|
warnOnceUnknownKeys(filters, KNOWN_FILTER_KEYS, warnedFilterKeys, (k) => `wontopos: unknown search filter ${JSON.stringify(k)} — the API drops keys it does not know, ` +
|
|
563
574
|
`so this filter has NO effect and the search is wider than you think. ` +
|
|
@@ -718,22 +729,22 @@ class Client {
|
|
|
718
729
|
if (opts.image !== undefined)
|
|
719
730
|
body.image = normalizeImage(opts.image);
|
|
720
731
|
warnOnUnknownMetadata(metadata);
|
|
721
|
-
return this.post("/api/v1/memory/store", body, opts
|
|
732
|
+
return this.post("/api/v1/memory/store", body, writeKey(opts, true));
|
|
722
733
|
}
|
|
723
734
|
async store(content, userId, metadata = {}, opts = {}) {
|
|
724
735
|
return this.add(content, userId, metadata, opts);
|
|
725
736
|
}
|
|
726
737
|
async addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
727
|
-
return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, opts
|
|
738
|
+
return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, writeKey(opts));
|
|
728
739
|
}
|
|
729
740
|
async addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
730
|
-
const body = { user_id: this.uid(userId), content, category };
|
|
741
|
+
const body = { user_id: this.uid(userId), content, category: category ?? "general" };
|
|
731
742
|
if (timestamp)
|
|
732
743
|
body.timestamp = timestamp;
|
|
733
|
-
return this.post("/api/v1/memory/bulk-store", body, opts
|
|
744
|
+
return this.post("/api/v1/memory/bulk-store", body, writeKey(opts));
|
|
734
745
|
}
|
|
735
746
|
async update(oldMemoryId, newContent, userId, opts = {}) {
|
|
736
|
-
return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts
|
|
747
|
+
return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, writeKey(opts));
|
|
737
748
|
}
|
|
738
749
|
async search(query, userId, limit = 10, opts = {}) {
|
|
739
750
|
checkSearchOpts(opts);
|
|
@@ -741,7 +752,7 @@ class Client {
|
|
|
741
752
|
limit ??= 10;
|
|
742
753
|
checkCount(limit, "limit");
|
|
743
754
|
const { extra, ...known } = opts;
|
|
744
|
-
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
755
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
|
|
745
756
|
return mergeResults(r);
|
|
746
757
|
}
|
|
747
758
|
async searchSelf(query, userId, limit = 10, opts = {}) {
|
|
@@ -750,7 +761,7 @@ class Client {
|
|
|
750
761
|
limit ??= 10;
|
|
751
762
|
checkCount(limit, "limit");
|
|
752
763
|
const { extra, ...known } = opts;
|
|
753
|
-
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
764
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
|
|
754
765
|
return { memories: asRecords(r.memories), self_memories: asRecords(r.self_memories) };
|
|
755
766
|
}
|
|
756
767
|
async searchFull(query, userId, limit = 10, opts = {}) {
|
|
@@ -759,7 +770,7 @@ class Client {
|
|
|
759
770
|
limit ??= 10;
|
|
760
771
|
checkCount(limit, "limit");
|
|
761
772
|
const { extra, ...known } = opts;
|
|
762
|
-
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
773
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
|
|
763
774
|
return {
|
|
764
775
|
...r,
|
|
765
776
|
memories: asRecords(r.memories),
|
|
@@ -894,9 +905,13 @@ class Client {
|
|
|
894
905
|
throw truncatedWalk(`stopped after ${MAX_PAGES} pages — the store did not end.`);
|
|
895
906
|
}
|
|
896
907
|
async revisions(userId, opts = {}) {
|
|
908
|
+
const inc = opts.include;
|
|
909
|
+
if (inc !== undefined && inc !== null && inc !== "revised" && inc !== "unrevised") {
|
|
910
|
+
throw new Error(`include must be "revised" or "unrevised", got ${JSON.stringify(inc)}.`);
|
|
911
|
+
}
|
|
897
912
|
const limit = optRange(opts.limit, "limit", PAGE_LIMIT_MIN, PAGE_LIMIT_MAX);
|
|
898
913
|
const body = {};
|
|
899
|
-
if (opts.include
|
|
914
|
+
if (opts.include != null)
|
|
900
915
|
body.include = opts.include;
|
|
901
916
|
if (limit !== undefined)
|
|
902
917
|
body.limit = limit;
|
|
@@ -1098,8 +1113,8 @@ class Client {
|
|
|
1098
1113
|
}
|
|
1099
1114
|
const attempts = this.maxRetries + 1;
|
|
1100
1115
|
const upper = method.toUpperCase();
|
|
1101
|
-
const idempotent = IDEMPOTENT_METHODS.has(upper);
|
|
1102
1116
|
const logPath = path.split("?")[0];
|
|
1117
|
+
const idempotent = IDEMPOTENT_METHODS.has(upper) || reads || (upper === "POST" && READ_POSTS.has(logPath));
|
|
1103
1118
|
const deadlineAt = this.deadlineAt();
|
|
1104
1119
|
const headers = extraHeaders ? { ...this.headers, ...extraHeaders } : this.headers;
|
|
1105
1120
|
let ambiguous = false;
|
|
@@ -1120,7 +1135,7 @@ class Client {
|
|
|
1120
1135
|
};
|
|
1121
1136
|
const cutShort = () => {
|
|
1122
1137
|
const why = att.why();
|
|
1123
|
-
if (why === "deadline" &&
|
|
1138
|
+
if (why === "deadline" && idempotent && previous) {
|
|
1124
1139
|
return previous;
|
|
1125
1140
|
}
|
|
1126
1141
|
return new APIConnectionError(0, this.abortMessage(why));
|
package/dist/wontopos.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* (`createStore`) or calls return 404. Every account starts with a `default` store,
|
|
15
15
|
* so with no `userId` anywhere the zero-setup path just works.
|
|
16
16
|
*
|
|
17
|
-
* The API key picks *which memory* (your
|
|
17
|
+
* The API key picks *which memory* (your workspace); `model` picks *which engine*
|
|
18
18
|
* reads it. Models on the shared pool read the same memory, so you can store with
|
|
19
19
|
* one and recall with another; `listModels()` reports each model's `memory` as
|
|
20
20
|
* `"shared"` or `"isolated"`, and an isolated one starts empty. Set a default in
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
* `mem.withModel("tablet-1").recall(...)`.
|
|
23
23
|
*
|
|
24
24
|
* Recall quality does not depend on which language a memory was written in: a
|
|
25
|
-
* memory stored in one language is found by a question asked in another.
|
|
26
|
-
*
|
|
25
|
+
* memory stored in one language is found by a question asked in another. No language
|
|
26
|
+
* model reads your stored memories.
|
|
27
27
|
*
|
|
28
28
|
* Reliability: every call retries transient failures with exponential backoff +
|
|
29
29
|
* jitter, honoring `Retry-After` up to 30s — 429 and a 409 that says another write
|
|
30
30
|
* to the store was in flight, always; 408/502/503/504 and connection errors only when
|
|
31
|
-
* a retry can never double-process a write (
|
|
32
|
-
* time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
|
|
31
|
+
* a retry can never double-process a write (reads, the DELETE calls, or a failure at
|
|
32
|
+
* connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
|
|
33
33
|
* the `withTimeout()` / `withRetries()` clones.
|
|
34
34
|
*
|
|
35
35
|
* Debugging: set `WONTOPOS_LOG=debug` to log method/path/status/timing/retries
|
|
@@ -89,7 +89,9 @@ export declare class BadRequestError extends WosError {
|
|
|
89
89
|
/** 401 — the API key is missing, wrong, or revoked. */
|
|
90
90
|
export declare class AuthenticationError extends WosError {
|
|
91
91
|
}
|
|
92
|
-
/** 402 — no card on file
|
|
92
|
+
/** 402 — no card on file, the balance is depleted, or the month's spend cap for the
|
|
93
|
+
* account's tier is reached (`details.reason` is `tier_cap_reached`). Top up to
|
|
94
|
+
* continue; a top-up also raises the tier. */
|
|
93
95
|
export declare class PaymentRequiredError extends WosError {
|
|
94
96
|
}
|
|
95
97
|
/** 403 — the key/model isn't allowed to do this. */
|
|
@@ -194,8 +196,9 @@ export interface StatusResult {
|
|
|
194
196
|
/**
|
|
195
197
|
* An image attached to a memory, passed to `add`/`store` via `opts.image`.
|
|
196
198
|
*
|
|
197
|
-
* Both edges must be 700px or more
|
|
198
|
-
*
|
|
199
|
+
* Both edges must be 700px or more and the long edge at most 2500px, and the image
|
|
200
|
+
* file at most 7MB; anything else is refused (400) with the reason. A request body is
|
|
201
|
+
* capped at 10MB.
|
|
199
202
|
*
|
|
200
203
|
* What the service keeps is NOT your original. An image whose long edge is over
|
|
201
204
|
* 1568px is downscaled to 1568 before anything else happens, and that smaller
|
|
@@ -241,8 +244,9 @@ export interface ImageBytes {
|
|
|
241
244
|
* The image as stored. Write it to a file, or wrap it in a Blob to show it.
|
|
242
245
|
*
|
|
243
246
|
* This is the service's copy, not necessarily your upload: anything over 1568px
|
|
244
|
-
* on its long edge was downscaled on the way in, and re-encoded to
|
|
245
|
-
* was a JPEG (see `ImageInput`). Take the file extension from
|
|
247
|
+
* on its long edge (2500 at most) was downscaled on the way in, and re-encoded to
|
|
248
|
+
* WebP unless it was a JPEG (see `ImageInput`). Take the file extension from
|
|
249
|
+
* `contentType`.
|
|
246
250
|
*/
|
|
247
251
|
bytes: Uint8Array;
|
|
248
252
|
/** Sniffed from the BYTES, not from whatever the upload was named — e.g. `image/jpeg`. */
|
|
@@ -317,7 +321,8 @@ export interface LineageStep {
|
|
|
317
321
|
is_current?: boolean;
|
|
318
322
|
[key: string]: unknown;
|
|
319
323
|
}
|
|
320
|
-
/** `lineage` → the
|
|
324
|
+
/** `lineage` → the edits that followed one memory, from the id passed forward to the
|
|
325
|
+
* version in force, oldest first. */
|
|
321
326
|
export interface LineageResult {
|
|
322
327
|
memory_id?: string;
|
|
323
328
|
chain: LineageStep[];
|
|
@@ -360,8 +365,8 @@ export interface SearchResult {
|
|
|
360
365
|
/** Image memories the answer carried (one by default on an image-capable model,
|
|
361
366
|
* up to `max_images`); `[]` when there were none. */
|
|
362
367
|
images: Memory[];
|
|
363
|
-
/** Re-ask passes
|
|
364
|
-
* than you asked for means
|
|
368
|
+
/** Re-ask passes that added memories. Present only when `verify` above 0 was sent;
|
|
369
|
+
* lower than you asked for means a pass found nothing new, or could not run. */
|
|
365
370
|
verify_used?: number;
|
|
366
371
|
[key: string]: unknown;
|
|
367
372
|
}
|
|
@@ -414,7 +419,7 @@ export interface UsageResult {
|
|
|
414
419
|
output_tokens?: number;
|
|
415
420
|
since?: string;
|
|
416
421
|
};
|
|
417
|
-
/** The workspace this key belongs to, over the window. */
|
|
422
|
+
/** The workspace this key belongs to, over the window: every key in it, not only this one. */
|
|
418
423
|
workspace?: {
|
|
419
424
|
workspace_id?: string | null;
|
|
420
425
|
requests?: number;
|
|
@@ -542,9 +547,8 @@ export interface SpeakersList {
|
|
|
542
547
|
[key: string]: unknown;
|
|
543
548
|
}
|
|
544
549
|
/**
|
|
545
|
-
* Narrow a search to part of a store.
|
|
546
|
-
*
|
|
547
|
-
* many matches sit inside it.
|
|
550
|
+
* Narrow a search to part of a store. A narrow filter can return fewer than `limit`
|
|
551
|
+
* even when the store holds more inside it.
|
|
548
552
|
*
|
|
549
553
|
* Retrieval behaves the same in every language, so these behave
|
|
550
554
|
* identically in every language. Unlisted keys are dropped by the API rather than
|
|
@@ -565,7 +569,8 @@ export interface SearchFilters {
|
|
|
565
569
|
time_from?: string;
|
|
566
570
|
time_to?: string;
|
|
567
571
|
/** WHEN THE CONTENT HAPPENED (`metadata.event_date`), not when it was written —
|
|
568
|
-
* this is the one you usually want.
|
|
572
|
+
* this is the one you usually want. A memory stored without an `event_date` is
|
|
573
|
+
* matched by when it was stored. */
|
|
569
574
|
event_from?: string;
|
|
570
575
|
event_to?: string;
|
|
571
576
|
/** Drop matches the engine scored below this importance (0–1). */
|
|
@@ -578,9 +583,11 @@ export declare function _resetFilterWarnings(): void;
|
|
|
578
583
|
* could not be told from an option that worked. Put a genuinely new one under
|
|
579
584
|
* `extra`. */
|
|
580
585
|
export interface SearchOptions {
|
|
581
|
-
/** Recall caching.
|
|
582
|
-
*
|
|
583
|
-
*
|
|
586
|
+
/** Recall caching. The same query again inside the TTL bills at 0.1×; one that
|
|
587
|
+
* extends the cached query bills only its cached part at 0.1×, the new part at the
|
|
588
|
+
* write rate and its results at the normal rate. The FIRST call writes the cache
|
|
589
|
+
* and bills the query tokens at 2× (`5m`) or 3× (`1h`), so this only pays for a
|
|
590
|
+
* query you repeat or extend. Do not enable it globally. */
|
|
584
591
|
cache_control?: {
|
|
585
592
|
ttl: "5m" | "1h";
|
|
586
593
|
};
|
|
@@ -591,9 +598,9 @@ export interface SearchOptions {
|
|
|
591
598
|
/**
|
|
592
599
|
* Re-ask passes, 0–3. After the first retrieval the service asks the engine again up
|
|
593
600
|
* to this many times, each time telling it what was already returned so it skips
|
|
594
|
-
* those and reaches further back.
|
|
601
|
+
* those and reaches further back. Raising it adds no language model call at any
|
|
595
602
|
* value. Stops early when a pass finds nothing new; `verify_used` in the response
|
|
596
|
-
* says how many
|
|
603
|
+
* says how many passes added memories.
|
|
597
604
|
*
|
|
598
605
|
* Each pass is another engine call and can add up to `limit` more memories, so it
|
|
599
606
|
* costs more — you are billed for what is delivered. Default 0. Worth it on questions
|
|
@@ -652,9 +659,10 @@ export interface SearchOptions {
|
|
|
652
659
|
export interface WriteOptions {
|
|
653
660
|
idempotencyKey?: string;
|
|
654
661
|
/**
|
|
655
|
-
* An image to store alongside the text
|
|
656
|
-
* `
|
|
657
|
-
*
|
|
662
|
+
* An image to store alongside the text, read by `add` (and `store`) only: `addTurn`,
|
|
663
|
+
* `addBulk` and `update` warn about it and do not send it. Needs a model that lists
|
|
664
|
+
* `images` in its `listModels()` capabilities; any other refuses the write rather than
|
|
665
|
+
* store the caption and quietly drop the image.
|
|
658
666
|
*
|
|
659
667
|
* A caption is required: the service refuses empty `content` (400) even with an
|
|
660
668
|
* image attached.
|
|
@@ -858,15 +866,16 @@ export declare class Client {
|
|
|
858
866
|
addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
859
867
|
/** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
|
|
860
868
|
update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
|
|
861
|
-
/** Search a store's memories.
|
|
869
|
+
/** Search a store's memories. They come most relevant first; the assistant's own
|
|
870
|
+
* words and image memories, when there are any, follow them in their own order.
|
|
862
871
|
*
|
|
863
872
|
* `limit` is 5-20, and out of range is refused rather than clamped: asking for 50
|
|
864
873
|
* and silently receiving 20 reads as "that is all there is". The default is 10.
|
|
865
874
|
*
|
|
866
|
-
* `limit` bounds `memories`, not the returned array
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
* it as well, so it can hold more than `limit`. They are billed either way. Size a
|
|
875
|
+
* `limit` bounds `memories`, not the returned array, and each `verify` pass can add
|
|
876
|
+
* up to `limit` more. The assistant's own words (on a model that lists
|
|
877
|
+
* `self_memories` in its capabilities) and image memories (one unless `max_images`
|
|
878
|
+
* says otherwise, on a model that lists `images`) come back after them in it as well, so it can hold more than `limit`. They are billed either way. Size a
|
|
870
879
|
* prompt window on the array you get back, not on `limit`. `searchFull()` hands the
|
|
871
880
|
* fields back apart.
|
|
872
881
|
*
|
|
@@ -890,7 +899,7 @@ export declare class Client {
|
|
|
890
899
|
* const r = await mem.searchFull("the day we moved", "alice", 10,
|
|
891
900
|
* { max_images: 3, verify: 2 });
|
|
892
901
|
* r.images.length; // the photos, apart from the text memories
|
|
893
|
-
* r.verify_used; //
|
|
902
|
+
* r.verify_used; // re-ask passes that added memories (each billed for what it added)
|
|
894
903
|
*
|
|
895
904
|
* Each option needs a model that lists it in its `listModels()` capabilities
|
|
896
905
|
* (`images`, `re_ask`) and is refused (403) on any other rather than accepted and
|
|
@@ -927,7 +936,8 @@ export declare class Client {
|
|
|
927
936
|
}): Promise<EngramResult>;
|
|
928
937
|
/** Recent conversation turns (short-term memory). */
|
|
929
938
|
history(userId?: string): Promise<HistoryTurn[]>;
|
|
930
|
-
/** Memory counts for a store: { total_memories, short_term_turns }.
|
|
939
|
+
/** Memory counts for a store: { total_memories, short_term_turns }. `total_memories`
|
|
940
|
+
* can be larger than the number `listMemories` lists. */
|
|
931
941
|
stats(userId?: string): Promise<StatsResult>;
|
|
932
942
|
/**
|
|
933
943
|
* What this key has spent, and what is left — the numbers behind "can I keep going?".
|
|
@@ -935,18 +945,17 @@ export declare class Client {
|
|
|
935
945
|
* Free: it carries no charge and skips the balance gate, because an account at zero
|
|
936
946
|
* still has to be able to find out why. Rate-limited instead.
|
|
937
947
|
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
* because that is what gates the next call whichever key makes it.
|
|
948
|
+
* `key` is THIS key's own lifetime spend. `workspace` and `stores` cover the window
|
|
949
|
+
* for the workspace this key belongs to, every key in it included. `balance_cents` is
|
|
950
|
+
* account-wide, because that is what gates the next call whichever key makes it.
|
|
941
951
|
*
|
|
942
952
|
* const u = await mem.usage(7);
|
|
943
953
|
* if (u.balance_cents! < 100) stop();
|
|
944
954
|
*/
|
|
945
955
|
usage(days?: number): Promise<UsageResult>;
|
|
946
956
|
/** Fetch ONE memory by id — the text you stored, and its metadata.
|
|
947
|
-
* The id is what `add`/`store` or `listMemories` returned.
|
|
948
|
-
*
|
|
949
|
-
* or an invalidated memory rejects with `NotFoundError`. Pass `undefined` as
|
|
957
|
+
* The id is what `add`/`store` or `listMemories` returned. An unknown id or one
|
|
958
|
+
* from another store rejects with `NotFoundError`. Pass `undefined` as
|
|
950
959
|
* `userId` for the default store — it is positional here. */
|
|
951
960
|
get(userId: string | undefined, memoryId: string): Promise<Memory>;
|
|
952
961
|
/** List a store's stored memories — the text you stored, plus its metadata.
|
|
@@ -992,9 +1001,10 @@ export declare class Client {
|
|
|
992
1001
|
* Fetch the bytes of an image memory — the picture the SERVICE holds.
|
|
993
1002
|
*
|
|
994
1003
|
* Not necessarily your upload, in size OR in format: an image whose long edge was
|
|
995
|
-
* over 1568px was downscaled to 1568 on the way in and re-encoded —
|
|
996
|
-
* formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG
|
|
997
|
-
*
|
|
1004
|
+
* over 1568px (2500 at most) was downscaled to 1568 on the way in and re-encoded —
|
|
1005
|
+
* lossless formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG;
|
|
1006
|
+
* kept as sent when the re-encode came out bigger — and that smaller picture is what
|
|
1007
|
+
* is stored and comes back here. Nothing on our side ever
|
|
998
1008
|
* uses more than 1568, so the extra pixels would be bytes nobody reads.
|
|
999
1009
|
* Keep your own copy for the full-resolution file.
|
|
1000
1010
|
*
|
|
@@ -1099,11 +1109,13 @@ export declare class Client {
|
|
|
1099
1109
|
skipIds?: string[];
|
|
1100
1110
|
}): Promise<RevisionsResult>;
|
|
1101
1111
|
/**
|
|
1102
|
-
* The
|
|
1112
|
+
* The edits that followed one memory: from the id you pass forward to the version in
|
|
1113
|
+
* force, oldest first.
|
|
1103
1114
|
*
|
|
1104
|
-
* `is_current` marks the version in force.
|
|
1105
|
-
* say, and when did it change"
|
|
1106
|
-
*
|
|
1115
|
+
* `is_current` marks the version in force. The chain runs forward only, so to answer
|
|
1116
|
+
* "what did this used to say, and when did it change" pass the first version's id;
|
|
1117
|
+
* the current version's id (what `search` returns) comes back alone. `revisions()`
|
|
1118
|
+
* tells you HOW MUCH a store moved, this tells you what happened to one fact.
|
|
1107
1119
|
*/
|
|
1108
1120
|
lineage(userId: string | undefined, memoryId: string): Promise<LineageResult>;
|
|
1109
1121
|
/**
|
|
@@ -1125,8 +1137,7 @@ export declare class Client {
|
|
|
1125
1137
|
* `AuthenticationError` (bad key), `PaymentRequiredError` (valid key but no card /
|
|
1126
1138
|
* depleted balance), or `APIConnectionError` (unreachable). Makes one metered request. */
|
|
1127
1139
|
ping(): Promise<boolean>;
|
|
1128
|
-
/** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`.
|
|
1129
|
-
* Needs no API key. */
|
|
1140
|
+
/** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`. */
|
|
1130
1141
|
listModels(): Promise<ModelInfo[]>;
|
|
1131
1142
|
/**
|
|
1132
1143
|
* The engrams (and delivery forms) the selected model can actually run.
|
|
@@ -1159,7 +1170,9 @@ export declare class Client {
|
|
|
1159
1170
|
addSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
|
|
1160
1171
|
/** The store's registered people, each with its memory count. */
|
|
1161
1172
|
listSpeakers(userId?: string): Promise<SpeakersList>;
|
|
1162
|
-
/** Unregister a person. Their memories stay
|
|
1173
|
+
/** Unregister a person. Their memories stay, and the name can no longer tag a new
|
|
1174
|
+
* memory or filter a search. The tag goes from stored memories too, except on
|
|
1175
|
+
* `tablet-2`, which keeps it on memories already stored with it. Retried like
|
|
1163
1176
|
* `deleteStore`, with the same note on a 404 after an ambiguous failure. */
|
|
1164
1177
|
removeSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
|
|
1165
1178
|
/** Delete a single memory by id. Pass `undefined` as `userId` for the default
|
|
@@ -1209,12 +1222,12 @@ export declare class Client {
|
|
|
1209
1222
|
*
|
|
1210
1223
|
* Retried: 429, and a 409 whose error carries `retry_after_ms` (another write to the
|
|
1211
1224
|
* store was in flight; nothing was stored), on every method; 408/502/503/504 and
|
|
1212
|
-
* dropped connections on
|
|
1213
|
-
*
|
|
1214
|
-
*
|
|
1215
|
-
*
|
|
1216
|
-
* only reads that the deadline cuts short. Any other retry cut
|
|
1217
|
-
* deadline error: a write may have been applied.
|
|
1225
|
+
* dropped connections on a call that only reads (an idempotent method, or a POST
|
|
1226
|
+
* that only reads); connect failures on every method. A retry whose wait is over the
|
|
1227
|
+
* cap, or does not fit the deadline, is not made: the call fails with the error of
|
|
1228
|
+
* the response it has. So does a retry the deadline leaves no time to send, and the
|
|
1229
|
+
* retry of a call that only reads that the deadline cuts short. Any other retry cut
|
|
1230
|
+
* short stays a status-0 deadline error: a write may have been applied.
|
|
1218
1231
|
*/
|
|
1219
1232
|
private send;
|
|
1220
1233
|
/**
|
package/dist/wontopos.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const VERSION = "2.2.
|
|
1
|
+
const VERSION = "2.2.46";
|
|
2
2
|
const RUNTIME = (() => {
|
|
3
3
|
const p = globalThis?.process;
|
|
4
4
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -492,7 +492,7 @@ const KNOWN_ENGRAM_KEYS = new Set(["form", "tz"]);
|
|
|
492
492
|
function checkSearchOpts(opts) {
|
|
493
493
|
checkOpts(opts, KNOWN_SEARCH_KEYS, "search");
|
|
494
494
|
const o = opts;
|
|
495
|
-
optRange(o.max_images
|
|
495
|
+
optRange(o.max_images != null ? o.max_images : o.extra?.max_images, "max_images", 0, MAX_IMAGES_MAX);
|
|
496
496
|
}
|
|
497
497
|
function checkOpts(opts, known, what) {
|
|
498
498
|
for (const k of Object.keys(opts)) {
|
|
@@ -542,6 +542,17 @@ function warnOnceUnknownKeys(obj, known, warned, message) {
|
|
|
542
542
|
console.warn(message(k));
|
|
543
543
|
}
|
|
544
544
|
}
|
|
545
|
+
function givenOpts(known) {
|
|
546
|
+
return Object.fromEntries(Object.entries(known).filter(([, v]) => v !== null));
|
|
547
|
+
}
|
|
548
|
+
const ADD_KEYS = new Set(["idempotencyKey", "image"]);
|
|
549
|
+
const WRITE_KEYS = new Set(["idempotencyKey"]);
|
|
550
|
+
const warnedWriteKeys = new Set();
|
|
551
|
+
function writeKey(opts, takesImage = false) {
|
|
552
|
+
warnOnceUnknownKeys(opts, takesImage ? ADD_KEYS : WRITE_KEYS, warnedWriteKeys, (k) => `wontopos: write option ${JSON.stringify(k)} is not sent. ` +
|
|
553
|
+
(takesImage ? "add takes idempotencyKey and image." : "This call takes idempotencyKey only."));
|
|
554
|
+
return opts.idempotencyKey;
|
|
555
|
+
}
|
|
545
556
|
function warnOnUnknownFilters(filters) {
|
|
546
557
|
warnOnceUnknownKeys(filters, KNOWN_FILTER_KEYS, warnedFilterKeys, (k) => `wontopos: unknown search filter ${JSON.stringify(k)} — the API drops keys it does not know, ` +
|
|
547
558
|
`so this filter has NO effect and the search is wider than you think. ` +
|
|
@@ -702,22 +713,22 @@ export class Client {
|
|
|
702
713
|
if (opts.image !== undefined)
|
|
703
714
|
body.image = normalizeImage(opts.image);
|
|
704
715
|
warnOnUnknownMetadata(metadata);
|
|
705
|
-
return this.post("/api/v1/memory/store", body, opts
|
|
716
|
+
return this.post("/api/v1/memory/store", body, writeKey(opts, true));
|
|
706
717
|
}
|
|
707
718
|
async store(content, userId, metadata = {}, opts = {}) {
|
|
708
719
|
return this.add(content, userId, metadata, opts);
|
|
709
720
|
}
|
|
710
721
|
async addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
711
|
-
return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, opts
|
|
722
|
+
return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, writeKey(opts));
|
|
712
723
|
}
|
|
713
724
|
async addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
714
|
-
const body = { user_id: this.uid(userId), content, category };
|
|
725
|
+
const body = { user_id: this.uid(userId), content, category: category ?? "general" };
|
|
715
726
|
if (timestamp)
|
|
716
727
|
body.timestamp = timestamp;
|
|
717
|
-
return this.post("/api/v1/memory/bulk-store", body, opts
|
|
728
|
+
return this.post("/api/v1/memory/bulk-store", body, writeKey(opts));
|
|
718
729
|
}
|
|
719
730
|
async update(oldMemoryId, newContent, userId, opts = {}) {
|
|
720
|
-
return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts
|
|
731
|
+
return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, writeKey(opts));
|
|
721
732
|
}
|
|
722
733
|
async search(query, userId, limit = 10, opts = {}) {
|
|
723
734
|
checkSearchOpts(opts);
|
|
@@ -725,7 +736,7 @@ export class Client {
|
|
|
725
736
|
limit ??= 10;
|
|
726
737
|
checkCount(limit, "limit");
|
|
727
738
|
const { extra, ...known } = opts;
|
|
728
|
-
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
739
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
|
|
729
740
|
return mergeResults(r);
|
|
730
741
|
}
|
|
731
742
|
async searchSelf(query, userId, limit = 10, opts = {}) {
|
|
@@ -734,7 +745,7 @@ export class Client {
|
|
|
734
745
|
limit ??= 10;
|
|
735
746
|
checkCount(limit, "limit");
|
|
736
747
|
const { extra, ...known } = opts;
|
|
737
|
-
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
748
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
|
|
738
749
|
return { memories: asRecords(r.memories), self_memories: asRecords(r.self_memories) };
|
|
739
750
|
}
|
|
740
751
|
async searchFull(query, userId, limit = 10, opts = {}) {
|
|
@@ -743,7 +754,7 @@ export class Client {
|
|
|
743
754
|
limit ??= 10;
|
|
744
755
|
checkCount(limit, "limit");
|
|
745
756
|
const { extra, ...known } = opts;
|
|
746
|
-
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
757
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
|
|
747
758
|
return {
|
|
748
759
|
...r,
|
|
749
760
|
memories: asRecords(r.memories),
|
|
@@ -878,9 +889,13 @@ export class Client {
|
|
|
878
889
|
throw truncatedWalk(`stopped after ${MAX_PAGES} pages — the store did not end.`);
|
|
879
890
|
}
|
|
880
891
|
async revisions(userId, opts = {}) {
|
|
892
|
+
const inc = opts.include;
|
|
893
|
+
if (inc !== undefined && inc !== null && inc !== "revised" && inc !== "unrevised") {
|
|
894
|
+
throw new Error(`include must be "revised" or "unrevised", got ${JSON.stringify(inc)}.`);
|
|
895
|
+
}
|
|
881
896
|
const limit = optRange(opts.limit, "limit", PAGE_LIMIT_MIN, PAGE_LIMIT_MAX);
|
|
882
897
|
const body = {};
|
|
883
|
-
if (opts.include
|
|
898
|
+
if (opts.include != null)
|
|
884
899
|
body.include = opts.include;
|
|
885
900
|
if (limit !== undefined)
|
|
886
901
|
body.limit = limit;
|
|
@@ -1082,8 +1097,8 @@ export class Client {
|
|
|
1082
1097
|
}
|
|
1083
1098
|
const attempts = this.maxRetries + 1;
|
|
1084
1099
|
const upper = method.toUpperCase();
|
|
1085
|
-
const idempotent = IDEMPOTENT_METHODS.has(upper);
|
|
1086
1100
|
const logPath = path.split("?")[0];
|
|
1101
|
+
const idempotent = IDEMPOTENT_METHODS.has(upper) || reads || (upper === "POST" && READ_POSTS.has(logPath));
|
|
1087
1102
|
const deadlineAt = this.deadlineAt();
|
|
1088
1103
|
const headers = extraHeaders ? { ...this.headers, ...extraHeaders } : this.headers;
|
|
1089
1104
|
let ambiguous = false;
|
|
@@ -1104,7 +1119,7 @@ export class Client {
|
|
|
1104
1119
|
};
|
|
1105
1120
|
const cutShort = () => {
|
|
1106
1121
|
const why = att.why();
|
|
1107
|
-
if (why === "deadline" &&
|
|
1122
|
+
if (why === "deadline" && idempotent && previous) {
|
|
1108
1123
|
return previous;
|
|
1109
1124
|
}
|
|
1110
1125
|
return new APIConnectionError(0, this.abortMessage(why));
|