@lumetra/engram 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -1
- package/dist/index.cjs +31 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -3
- package/dist/index.d.ts +67 -3
- package/dist/index.js +31 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,12 +57,47 @@ new EngramClient({
|
|
|
57
57
|
## API surface
|
|
58
58
|
|
|
59
59
|
### Memories
|
|
60
|
-
- `storeMemory(content, bucket?)` — store a single fact. `bucket` defaults to `"default"`.
|
|
60
|
+
- `storeMemory(content, bucket?, { dedup? })` — store a single fact. `bucket` defaults to `"default"`. `dedup` is `"off" | "loose" | "strict"`; omit to use the server's default policy. See [Dedup](#dedup) below.
|
|
61
61
|
- `storeMemories(contents[], bucket?)` — batched store. `bucket` defaults to `"default"`.
|
|
62
62
|
- `listMemories(bucket?, { limit?, offset? })` — paginated list (`limit` defaults to 20, `offset` to 0).
|
|
63
63
|
- `deleteMemory(memoryId, bucket?)` — delete one memory. `bucket` defaults to `"default"`.
|
|
64
64
|
- `clearMemories(bucket)` — delete every memory in a bucket. **No default — explicit bucket required** (prevents accidental wipes).
|
|
65
65
|
|
|
66
|
+
### Query knobs
|
|
67
|
+
|
|
68
|
+
`query` and `queryStream` accept these tuning options (all optional):
|
|
69
|
+
|
|
70
|
+
| Field | Type | What it does |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `maxTokens` | `number` | Cap synthesis output. Lower for agent loops / cost control. |
|
|
73
|
+
| `minSimilarityThreshold` | `number` | Drop retrieved chunks below this raw cosine similarity. Citations-grade precision. |
|
|
74
|
+
| `topKPerBucket` | `number \| Record<string,number>` | Per-bucket retrieval depth. `{ edgar_AAPL: 20, prices_AAPL: 4 }` lets you express "deep here, shallow there." |
|
|
75
|
+
| `returnFormat` | `'prose' \| 'json'` | When `'json'`, server returns JSON; result includes parsed `answer_json`. |
|
|
76
|
+
| `responseSchema` | `Record<string, unknown>` (JSON Schema) | Hint the model with a target shape. Best-effort; validate client-side for strict. |
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const r = await engram.query("Apple's active legal proceedings", {
|
|
82
|
+
buckets: ['edgar_AAPL', 'patents_AAPL'],
|
|
83
|
+
topKPerBucket: { edgar_AAPL: 20, patents_AAPL: 5 },
|
|
84
|
+
maxTokens: 400,
|
|
85
|
+
returnFormat: 'json',
|
|
86
|
+
responseSchema: {
|
|
87
|
+
type: 'array',
|
|
88
|
+
items: {
|
|
89
|
+
properties: {
|
|
90
|
+
case_name: { type: 'string' },
|
|
91
|
+
jurisdiction: { type: 'string' },
|
|
92
|
+
status: { type: 'string' },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
const cases = r.answer_json as Array<{case_name: string; jurisdiction: string; status: string}> | undefined;
|
|
98
|
+
for (const c of cases ?? []) console.log(c);
|
|
99
|
+
```
|
|
100
|
+
|
|
66
101
|
### Query
|
|
67
102
|
- `query(question, { buckets?, topK?, skipSynthesis?, returnExplanation? })`
|
|
68
103
|
- `buckets` fuses across multiple buckets in one call. Defaults to `["default"]`.
|
|
@@ -72,6 +107,33 @@ new EngramClient({
|
|
|
72
107
|
- response shape: `{ answer, explanation: { retrieved_memories, profile, graph_facts }, usage }`
|
|
73
108
|
- `queryStream(question, options?)` — same args, returns an `AsyncIterable<QueryStreamEvent>` that streams the answer
|
|
74
109
|
|
|
110
|
+
## Dedup
|
|
111
|
+
|
|
112
|
+
The server runs a similarity check before storing. By default (`"loose"`, similarity ≥ 0.95) it collapses near-duplicate writes into the existing memory so re-ingesting the same source doesn't bloat the bucket. For most narrative content this is what you want.
|
|
113
|
+
|
|
114
|
+
For templated time-series content (financial filings, daily metrics, log rows) where rows are structurally similar but each carries unique values, the default collapses real data. Use `dedup: 'off'` to disable.
|
|
115
|
+
|
|
116
|
+
Every response now includes a `status` field. When `status === 'merged'`, the write was absorbed into an existing memory and three extra fields are present:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const r = await engram.storeMemory('Acme Q1 revenue: $245M', 'finance');
|
|
120
|
+
if (r.status === 'merged') {
|
|
121
|
+
console.log(`merged into ${r.deduped_into} (${r.merge_reason}, sim=${r.similarity_score?.toFixed(3)})`);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`merge_reason` is one of `content_hash`, `embedding_similarity`, `conflict_keep_existing`, `concurrent_insert_race`.
|
|
126
|
+
|
|
127
|
+
Opt out for time-series ingest:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
for (const row of monthlyPrices) {
|
|
131
|
+
await engram.storeMemory(row, 'prices_AAPL', { dedup: 'off' });
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`'strict'` is a middle ground — only collapses near-identical content (≥ 0.99).
|
|
136
|
+
|
|
75
137
|
## Streaming
|
|
76
138
|
|
|
77
139
|
For broad questions, synthesis can take 10–25 seconds. `queryStream` yields the answer incrementally so you can render it as it's produced instead of waiting for the full response:
|
package/dist/index.cjs
CHANGED
|
@@ -18,7 +18,7 @@ var DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
18
18
|
var DEFAULT_STREAM_TIMEOUT_MS = 3e5;
|
|
19
19
|
var DEFAULT_MAX_RETRIES_ON_429 = 3;
|
|
20
20
|
var RETRY_AFTER_CAP_MS = 3e4;
|
|
21
|
-
var SDK_VERSION = "0.
|
|
21
|
+
var SDK_VERSION = "0.5.0";
|
|
22
22
|
var USER_AGENT = `engram-js/${SDK_VERSION}`;
|
|
23
23
|
function parseRetryAfterMs(header, defaultBackoffMs) {
|
|
24
24
|
if (header) {
|
|
@@ -118,10 +118,12 @@ var EngramClient = class {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
// ---------- Memories ----------
|
|
121
|
-
async storeMemory(content, bucket = "default") {
|
|
121
|
+
async storeMemory(content, bucket = "default", options = {}) {
|
|
122
|
+
const body = { content };
|
|
123
|
+
if (options.dedup !== void 0) body.dedup = options.dedup;
|
|
122
124
|
return this.request(
|
|
123
125
|
`/v1/buckets/${encodeURIComponent(bucket)}/memories`,
|
|
124
|
-
{ method: "POST", body
|
|
126
|
+
{ method: "POST", body }
|
|
125
127
|
);
|
|
126
128
|
}
|
|
127
129
|
async storeMemories(contents, bucket = "default") {
|
|
@@ -156,16 +158,24 @@ var EngramClient = class {
|
|
|
156
158
|
// ---------- Query ----------
|
|
157
159
|
async query(question, options = {}) {
|
|
158
160
|
const buckets = options.buckets ?? ["default"];
|
|
161
|
+
const opts = {
|
|
162
|
+
top_k: options.topK ?? 8,
|
|
163
|
+
return_explanation: options.returnExplanation ?? true,
|
|
164
|
+
skip_synthesis: options.skipSynthesis ?? false
|
|
165
|
+
};
|
|
166
|
+
if (options.maxTokens !== void 0) opts.max_tokens = options.maxTokens;
|
|
167
|
+
if (options.minSimilarityThreshold !== void 0) {
|
|
168
|
+
opts.min_similarity_threshold = options.minSimilarityThreshold;
|
|
169
|
+
}
|
|
170
|
+
if (options.topKPerBucket !== void 0) opts.top_k_per_bucket = options.topKPerBucket;
|
|
171
|
+
if (options.returnFormat !== void 0) opts.return_format = options.returnFormat;
|
|
172
|
+
if (options.responseSchema !== void 0) opts.response_schema = options.responseSchema;
|
|
159
173
|
return this.request("/v1/query", {
|
|
160
174
|
method: "POST",
|
|
161
175
|
body: {
|
|
162
176
|
query: question,
|
|
163
177
|
buckets,
|
|
164
|
-
options:
|
|
165
|
-
top_k: options.topK ?? 8,
|
|
166
|
-
return_explanation: options.returnExplanation ?? true,
|
|
167
|
-
skip_synthesis: options.skipSynthesis ?? false
|
|
168
|
-
}
|
|
178
|
+
options: opts
|
|
169
179
|
}
|
|
170
180
|
});
|
|
171
181
|
}
|
|
@@ -183,15 +193,23 @@ var EngramClient = class {
|
|
|
183
193
|
*/
|
|
184
194
|
queryStream(question, options = {}) {
|
|
185
195
|
const buckets = options.buckets ?? ["default"];
|
|
196
|
+
const opts = {
|
|
197
|
+
top_k: options.topK ?? 8,
|
|
198
|
+
return_explanation: options.returnExplanation ?? true,
|
|
199
|
+
skip_synthesis: options.skipSynthesis ?? false
|
|
200
|
+
};
|
|
201
|
+
if (options.maxTokens !== void 0) opts.max_tokens = options.maxTokens;
|
|
202
|
+
if (options.minSimilarityThreshold !== void 0) {
|
|
203
|
+
opts.min_similarity_threshold = options.minSimilarityThreshold;
|
|
204
|
+
}
|
|
205
|
+
if (options.topKPerBucket !== void 0) opts.top_k_per_bucket = options.topKPerBucket;
|
|
206
|
+
if (options.returnFormat !== void 0) opts.return_format = options.returnFormat;
|
|
207
|
+
if (options.responseSchema !== void 0) opts.response_schema = options.responseSchema;
|
|
186
208
|
const body = {
|
|
187
209
|
query: question,
|
|
188
210
|
buckets,
|
|
189
211
|
stream: true,
|
|
190
|
-
options:
|
|
191
|
-
top_k: options.topK ?? 8,
|
|
192
|
-
return_explanation: options.returnExplanation ?? true,
|
|
193
|
-
skip_synthesis: options.skipSynthesis ?? false
|
|
194
|
-
}
|
|
212
|
+
options: opts
|
|
195
213
|
};
|
|
196
214
|
const url = `${this.baseUrl}/v1/query`;
|
|
197
215
|
const apiKey = this.apiKey;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export interface EngramClientOptions {\n /**\n * Engram API key. Looks like `eng_live_...`. Defaults to `process.env.ENGRAM_API_KEY`.\n */\n apiKey?: string;\n /**\n * API base URL. Defaults to `process.env.ENGRAM_BASE_URL` or `https://api.lumetra.io`.\n */\n baseUrl?: string;\n /**\n * Custom fetch implementation. Defaults to the global `fetch`.\n * Useful for proxying, retry middleware, or non-Node runtimes.\n */\n fetch?: typeof fetch;\n /**\n * Request timeout in milliseconds for buffered (non-streaming) calls.\n * Defaults to 30000 (30s).\n */\n timeoutMs?: number;\n /**\n * Timeout in milliseconds for `queryStream` calls. Streaming responses\n * can sit in the prep phase (retrieval + extractor pass) for 5–15s\n * before the first synthesis token arrives, so the buffered 30s\n * default would leave no headroom for the streamed body. Defaults to\n * 300000 (5 min). Use a higher value for very large synthesis bodies.\n */\n streamTimeoutMs?: number;\n /**\n * How many times to retry on a 429 (per-tenant concurrent-request cap).\n * Honors the server's `Retry-After` header, capped at 30s per sleep.\n * Defaults to 3. Set to 0 to disable retry and surface 429 as `EngramError`\n * on the first attempt.\n */\n maxRetriesOn429?: number;\n}\n\nexport interface Bucket {\n id: string;\n name: string;\n /**\n * Mirror of `name`. The server emits both so callers iterating both\n * `listBuckets` and `storeMemory` responses can use one field name.\n * Prefer `name` in new code.\n */\n bucket_name?: string;\n description?: string | null;\n created_at: string;\n memory_count?: number;\n}\n\nexport interface Memory {\n id: string;\n content: string;\n bucket_name?: string;\n created_at?: string;\n token_count?: number;\n}\n\nexport interface StoreMemoryResult {\n id: string;\n /**\n * Alias for `id` — older API docs / older SDKs referenced this name.\n * Always present; prefer `id` in new code.\n */\n memory_id?: string;\n bucket_name: string;\n token_count: number;\n}\n\nexport interface ClearMemoriesResult {\n success: boolean;\n /** Number of memories actually deleted (server-reported). */\n cleared_count: number;\n}\n\nexport interface RetrievedMemory {\n id?: string;\n content: string;\n score?: number;\n bucket?: string;\n}\n\nexport interface QueryExplanation {\n retrieved_memories?: RetrievedMemory[];\n profile?: string | null;\n graph_facts?: string[];\n}\n\nexport interface QueryUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n}\n\nexport interface QueryResult {\n answer: string;\n /**\n * Top-level count of retrieved memories. Equivalent to\n * `result.explanation?.retrieved_memories.length` but present even\n * when `returnExplanation` is false.\n */\n memories_found?: number;\n explanation?: QueryExplanation;\n usage?: QueryUsage;\n}\n\n/**\n * One frame yielded by {@link EngramClient.queryStream}.\n *\n * The shape is discriminated by `type`:\n * - `delta` frames carry an incremental piece of the answer in `content`.\n * - `done` carries the final usage + (optional) explanation. Emitted\n * exactly once at the end of the stream.\n */\nexport type QueryStreamEvent =\n | { type: 'delta'; content: string }\n | {\n type: 'done';\n usage?: QueryUsage;\n synthesis_usage?: unknown;\n explanation?: QueryExplanation;\n };\n\nexport interface QueryOptions {\n /**\n * Buckets to fuse across. Defaults to `['default']`.\n */\n buckets?: string[];\n /**\n * Maximum number of memories to retrieve. Defaults to 8.\n */\n topK?: number;\n /**\n * If true, server skips the synthesis LLM call and returns retrieval-only.\n * `answer` will be an empty string in that case. Defaults to false.\n */\n skipSynthesis?: boolean;\n /**\n * Whether to populate the `explanation` field. Defaults to true.\n */\n returnExplanation?: boolean;\n}\n\nexport interface ListMemoriesOptions {\n limit?: number;\n offset?: number;\n}\n\nexport interface ListMemoriesResult {\n memories: Memory[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport class EngramError extends Error {\n override readonly name = 'EngramError';\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n","import {\n EngramError,\n type Bucket,\n type ClearMemoriesResult,\n type EngramClientOptions,\n type ListMemoriesOptions,\n type ListMemoriesResult,\n type QueryOptions,\n type QueryResult,\n type QueryStreamEvent,\n type StoreMemoryResult,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.lumetra.io';\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_STREAM_TIMEOUT_MS = 300_000;\nconst DEFAULT_MAX_RETRIES_ON_429 = 3;\n// Cap on per-attempt backoff so a misconfigured server can't force\n// callers to sleep for minutes.\nconst RETRY_AFTER_CAP_MS = 30_000;\nconst SDK_VERSION = '0.3.2';\nconst USER_AGENT = `engram-js/${SDK_VERSION}`;\n\nfunction parseRetryAfterMs(header: string | null, defaultBackoffMs: number): number {\n if (header) {\n const value = Number(header.trim());\n if (Number.isFinite(value) && value >= 0) {\n return Math.min(value * 1000, RETRY_AFTER_CAP_MS);\n }\n }\n return Math.min(defaultBackoffMs, RETRY_AFTER_CAP_MS);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class EngramClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly streamTimeoutMs: number;\n private readonly maxRetriesOn429: number;\n\n constructor(options: EngramClientOptions = {}) {\n const apiKey =\n options.apiKey ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_API_KEY : undefined) ??\n '';\n if (!apiKey) {\n throw new Error(\n 'EngramClient: apiKey is required. Pass it explicitly or set ENGRAM_API_KEY in your environment.',\n );\n }\n const baseUrl =\n options.baseUrl ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_BASE_URL : undefined) ??\n DEFAULT_BASE_URL;\n\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.streamTimeoutMs = options.streamTimeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS;\n this.maxRetriesOn429 = Math.max(0, options.maxRetriesOn429 ?? DEFAULT_MAX_RETRIES_ON_429);\n\n if (typeof this.fetchImpl !== 'function') {\n throw new Error(\n 'EngramClient: no fetch implementation available. Use Node 18+, or pass options.fetch.',\n );\n }\n }\n\n private async request<T>(\n path: string,\n init: { method: string; body?: unknown; query?: Record<string, string | number | undefined> } = {\n method: 'GET',\n },\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (init.query) {\n for (const [k, v] of Object.entries(init.query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n // 429-aware retry. The Engram API enforces a per-tenant concurrent-\n // request cap and sets Retry-After on 429s; without retry handling,\n // bursty clients fail immediately under load. The body is JSON-\n // serialized once outside the loop so each attempt sends an\n // identical request.\n const requestBody = init.body !== undefined ? JSON.stringify(init.body) : undefined;\n let attemptsRemaining = this.maxRetriesOn429;\n let backoffMs = 1000;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let res: Response;\n try {\n res = await this.fetchImpl(url.toString(), {\n method: init.method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': USER_AGENT,\n },\n body: requestBody,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (res.status === 429 && attemptsRemaining > 0) {\n // Drain the body so the connection can return to the pool.\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n\n const text = await res.text();\n let parsed: unknown = undefined;\n if (text) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n\n if (!res.ok) {\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n\n return parsed as T;\n }\n }\n\n // ---------- Memories ----------\n\n async storeMemory(content: string, bucket: string = 'default'): Promise<StoreMemoryResult> {\n return this.request<StoreMemoryResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body: { content } },\n );\n }\n\n async storeMemories(\n contents: string[],\n bucket: string = 'default',\n ): Promise<{ memories: StoreMemoryResult[] }> {\n // Defensively unwrap: depending on server version the batch endpoint\n // returns either { memories: [...] } or a bare array. Normalize to the\n // wrapped shape so callers don't have to switch on it.\n const result = await this.request<{ memories: StoreMemoryResult[] } | StoreMemoryResult[]>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body: { memories: contents.map((content) => ({ content })) } },\n );\n return Array.isArray(result) ? { memories: result } : result;\n }\n\n async listMemories(\n bucket: string = 'default',\n options: ListMemoriesOptions = {},\n ): Promise<ListMemoriesResult> {\n return this.request<ListMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n {\n method: 'GET',\n query: { limit: options.limit ?? 20, offset: options.offset ?? 0 },\n },\n );\n }\n\n async deleteMemory(memoryId: string, bucket: string = 'default'): Promise<void> {\n await this.request<unknown>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories/${encodeURIComponent(memoryId)}`,\n { method: 'DELETE' },\n );\n }\n\n async clearMemories(bucket: string): Promise<ClearMemoriesResult> {\n const res = await this.request<ClearMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'DELETE' },\n );\n // Defensive default: older servers / proxies may strip the body. The\n // contract surface is {success, cleared_count}; if the server stayed\n // silent we still return the same shape so callers can rely on it.\n return res ?? { success: true, cleared_count: 0 };\n }\n\n // ---------- Query ----------\n\n async query(question: string, options: QueryOptions = {}): Promise<QueryResult> {\n const buckets = options.buckets ?? ['default'];\n return this.request<QueryResult>('/v1/query', {\n method: 'POST',\n body: {\n query: question,\n buckets,\n options: {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n },\n },\n });\n }\n\n /**\n * Streaming variant of {@link query}. Returns an async-iterable that\n * yields {@link QueryStreamEvent} frames as the server produces them:\n *\n * for await (const ev of engram.queryStream('...')) {\n * if (ev.type === 'delta') process.stdout.write(ev.content);\n * else if (ev.type === 'done') console.log(ev.usage);\n * }\n *\n * Break out of the loop to abort the request (the underlying\n * AbortController is wired up to the fetch call).\n */\n queryStream(question: string, options: QueryOptions = {}): AsyncIterable<QueryStreamEvent> {\n const buckets = options.buckets ?? ['default'];\n const body = {\n query: question,\n buckets,\n stream: true,\n options: {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n },\n };\n const url = `${this.baseUrl}/v1/query`;\n const apiKey = this.apiKey;\n const fetchImpl = this.fetchImpl;\n const timeoutMs = this.streamTimeoutMs;\n const maxRetriesOn429 = this.maxRetriesOn429;\n const bodyJson = JSON.stringify(body);\n\n return {\n [Symbol.asyncIterator]: () => {\n const controller = new AbortController();\n // The timeout caps total wall-clock for the stream. Set generously\n // because synthesis can run 10–25s before the first byte even with\n // streaming on slow paths; clamp to the user's configured timeout.\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let started = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n let done = false;\n const queue: QueryStreamEvent[] = [];\n let pendingError: unknown = null;\n\n const ensureStarted = async (): Promise<void> => {\n if (started) return;\n started = true;\n // 429-aware retry at the connection-open stage only. Once\n // the response body starts flowing we can't resume mid-\n // stream safely.\n let attemptsRemaining = maxRetriesOn429;\n let backoffMs = 1000;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n 'User-Agent': USER_AGENT,\n },\n body: bodyJson,\n signal: controller.signal,\n });\n if (res.status === 429 && attemptsRemaining > 0) {\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n let parsed: unknown = text;\n try {\n parsed = text ? JSON.parse(text) : text;\n } catch {\n /* keep raw text */\n }\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n if (!res.body) {\n throw new EngramError('Engram API: streaming response has no body', res.status, null);\n }\n reader = res.body.getReader();\n return;\n }\n };\n\n const drainBuffer = (): void => {\n // SSE frames are separated by a blank line ('\\n\\n'). Each frame\n // is one or more 'field: value' lines. We only care about\n // 'data:' lines for this stream.\n let idx: number;\n while ((idx = buffer.indexOf('\\n\\n')) !== -1) {\n const frame = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 2);\n const dataLines: string[] = [];\n for (const rawLine of frame.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine;\n if (line.startsWith('data: ')) {\n dataLines.push(line.slice(6));\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5));\n }\n }\n if (dataLines.length === 0) continue;\n const payloadStr = dataLines.join('\\n');\n if (payloadStr === '[DONE]') {\n done = true;\n return;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(payloadStr);\n } catch {\n // Malformed frame — skip rather than corrupt the stream.\n continue;\n }\n if (payload && typeof payload === 'object') {\n const obj = payload as Record<string, unknown>;\n if (obj.error) {\n pendingError = new EngramError(String(obj.error), 0, obj);\n done = true;\n return;\n }\n // OpenAI-style delta chunk\n const choices = obj.choices as Array<{ delta?: { content?: string } }> | undefined;\n if (Array.isArray(choices) && choices.length > 0) {\n const delta = choices[0]?.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n queue.push({ type: 'delta', content: delta });\n }\n continue;\n }\n // Final usage / explanation frame (no 'choices' key).\n queue.push({ type: 'done', ...(obj as Omit<Extract<QueryStreamEvent, { type: 'done' }>, 'type'>) });\n }\n }\n };\n\n return {\n next: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n try {\n await ensureStarted();\n } catch (err) {\n clearTimeout(timer);\n throw err;\n }\n while (queue.length === 0 && !done) {\n if (!reader) {\n clearTimeout(timer);\n throw new EngramError('Engram API: stream reader missing', 0, null);\n }\n const chunk = await reader.read();\n if (chunk.done) {\n // Flush whatever's left in the buffer (some servers don't\n // terminate with a trailing blank line).\n if (buffer.length > 0) {\n buffer += '\\n\\n';\n drainBuffer();\n }\n done = true;\n break;\n }\n buffer += decoder.decode(chunk.value, { stream: true });\n drainBuffer();\n }\n if (queue.length > 0) {\n return { value: queue.shift() as QueryStreamEvent, done: false };\n }\n clearTimeout(timer);\n if (pendingError) throw pendingError;\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n return: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n // Caller broke out of the for-await loop — cancel the upstream\n // request so we're not holding the connection open.\n clearTimeout(timer);\n controller.abort();\n try {\n await reader?.cancel();\n } catch {\n /* ignored */\n }\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n };\n },\n };\n }\n\n // ---------- Buckets ----------\n\n async listBuckets(): Promise<Bucket[]> {\n const result = await this.request<{ buckets: Bucket[] } | Bucket[]>(`/v1/buckets`, {\n method: 'GET',\n });\n return Array.isArray(result) ? result : result.buckets;\n }\n\n async createBucket(name: string, description?: string): Promise<Bucket> {\n return this.request<Bucket>('/v1/buckets', {\n method: 'POST',\n body: { name, description },\n });\n }\n\n async deleteBucket(bucket: string): Promise<void> {\n await this.request<unknown>(`/v1/buckets/${encodeURIComponent(bucket)}`, {\n method: 'DELETE',\n });\n }\n\n // ---------- Profile ----------\n\n async getProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile`,\n { method: 'GET' },\n );\n }\n\n async regenerateProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile/regenerate`,\n { method: 'POST' },\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/client.ts"],"names":[],"mappings":";;;AAgOO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAA,EACnB,IAAA,GAAO,aAAA;AAAA,EAChB,MAAA;AAAA,EACA,IAAA;AAAA,EAET,WAAA,CAAY,OAAA,EAAiB,MAAA,EAAgB,IAAA,EAAe;AAC1D,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;;;AC5NA,IAAM,gBAAA,GAAmB,wBAAA;AACzB,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,yBAAA,GAA4B,GAAA;AAClC,IAAM,0BAAA,GAA6B,CAAA;AAGnC,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,WAAA,GAAc,OAAA;AACpB,IAAM,UAAA,GAAa,aAAa,WAAW,CAAA,CAAA;AAE3C,SAAS,iBAAA,CAAkB,QAAuB,gBAAA,EAAkC;AAClF,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,CAAA;AAClC,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACxC,MAAA,OAAO,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,GAAA,EAAM,kBAAkB,CAAA;AAAA,IAClD;AAAA,EACF;AACA,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,gBAAA,EAAkB,kBAAkB,CAAA;AACtD;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAEO,IAAM,eAAN,MAAmB;AAAA,EACP,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EAEjB,WAAA,CAAY,OAAA,GAA+B,EAAC,EAAG;AAC7C,IAAA,MAAM,MAAA,GACJ,QAAQ,MAAA,KACP,OAAO,YAAY,WAAA,GAAc,OAAA,CAAQ,GAAA,EAAK,cAAA,GAAiB,MAAA,CAAA,IAChE,EAAA;AACF,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,MAAM,OAAA,GACJ,QAAQ,OAAA,KACP,OAAO,YAAY,WAAA,GAAc,OAAA,CAAQ,GAAA,EAAK,eAAA,GAAkB,MAAA,CAAA,IACjE,gBAAA;AAEF,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACzC,IAAA,IAAA,CAAK,SAAA,GAAY,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AAC7C,IAAA,IAAA,CAAK,SAAA,GAAY,QAAQ,SAAA,IAAa,kBAAA;AACtC,IAAA,IAAA,CAAK,eAAA,GAAkB,QAAQ,eAAA,IAAmB,yBAAA;AAClD,IAAA,IAAA,CAAK,kBAAkB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,CAAQ,mBAAmB,0BAA0B,CAAA;AAExF,IAAA,IAAI,OAAO,IAAA,CAAK,SAAA,KAAc,UAAA,EAAY;AACxC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,OAAA,CACZ,IAAA,EACA,IAAA,GAAgG;AAAA,IAC9F,MAAA,EAAQ;AAAA,GACV,EACY;AACZ,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,CAAA,EAAG,KAAK,OAAO,CAAA,EAAG,IAAI,CAAA,CAAE,CAAA;AAC5C,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,KAAA,MAAW,CAAC,GAAG,CAAC,CAAA,IAAK,OAAO,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA,EAAG;AAC/C,QAAA,IAAI,CAAA,KAAM,QAAW,GAAA,CAAI,YAAA,CAAa,IAAI,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,MACxD;AAAA,IACF;AAOA,IAAA,MAAM,WAAA,GAAc,KAAK,IAAA,KAAS,MAAA,GAAY,KAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AAC1E,IAAA,IAAI,oBAAoB,IAAA,CAAK,eAAA;AAC7B,IAAA,IAAI,SAAA,GAAY,GAAA;AAGhB,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,SAAS,CAAA;AAEjE,MAAA,IAAI,GAAA;AACJ,MAAA,IAAI;AACF,QAAA,GAAA,GAAM,MAAM,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,UAAS,EAAG;AAAA,UACzC,QAAQ,IAAA,CAAK,MAAA;AAAA,UACb,OAAA,EAAS;AAAA,YACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,YACpC,cAAA,EAAgB,kBAAA;AAAA,YAChB,YAAA,EAAc;AAAA,WAChB;AAAA,UACA,IAAA,EAAM,WAAA;AAAA,UACN,QAAQ,UAAA,CAAW;AAAA,SACpB,CAAA;AAAA,MACH,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAEA,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,IAAO,iBAAA,GAAoB,CAAA,EAAG;AAE/C,QAAA,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACtC,QAAA,MAAM,QAAQ,iBAAA,CAAkB,GAAA,CAAI,QAAQ,GAAA,CAAI,aAAa,GAAG,SAAS,CAAA;AACzE,QAAA,MAAM,MAAM,KAAK,CAAA;AACjB,QAAA,iBAAA,IAAqB,CAAA;AACrB,QAAA,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,SAAA,GAAY,CAAA,EAAG,kBAAkB,CAAA;AACtD,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,MAAA,IAAI,MAAA,GAAkB,MAAA;AACtB,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,IAAI;AACF,UAAA,MAAA,GAAS,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,QAC1B,CAAA,CAAA,MAAQ;AACN,UAAA,MAAA,GAAS,IAAA;AAAA,QACX;AAAA,MACF;AAEA,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,QAAA,MAAM,MAAA,GACJ,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,WAAW,IAAA,IAAQ,OAAA,IAAW,MAAA,GACjE,MAAA,CAA8B,KAAA,GAC/B,MAAA;AACN,QAAA,MAAM,IAAI,WAAA;AAAA,UACR,CAAA,WAAA,EAAc,GAAA,CAAI,MAAM,CAAA,EAAA,EAAK,OAAO,MAAA,KAAW,QAAA,GAAW,MAAA,GAAS,IAAA,CAAK,SAAA,CAAU,MAAA,IAAU,EAAE,CAAC,CAAA,CAAA;AAAA,UAC/F,GAAA,CAAI,MAAA;AAAA,UACJ;AAAA,SACF;AAAA,MACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,WAAA,CACJ,OAAA,EACA,SAAiB,SAAA,EACjB,OAAA,GAAmC,EAAC,EACR;AAC5B,IAAA,MAAM,IAAA,GAAgC,EAAE,OAAA,EAAQ;AAChD,IAAA,IAAI,OAAA,CAAQ,KAAA,KAAU,MAAA,EAAW,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AACtD,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA;AAAK,KACzB;AAAA,EACF;AAAA,EAEA,MAAM,aAAA,CACJ,QAAA,EACA,MAAA,GAAiB,SAAA,EAC2B;AAI5C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA;AAAA,MACxB,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,EAAE,QAAA,EAAU,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,MAAa,EAAE,OAAA,EAAQ,CAAE,GAAE;AAAE,KACjF;AACA,IAAA,OAAO,MAAM,OAAA,CAAQ,MAAM,IAAI,EAAE,QAAA,EAAU,QAAO,GAAI,MAAA;AAAA,EACxD;AAAA,EAEA,MAAM,YAAA,CACJ,MAAA,GAAiB,SAAA,EACjB,OAAA,GAA+B,EAAC,EACH;AAC7B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,KAAA,EAAO,EAAE,KAAA,EAAO,OAAA,CAAQ,SAAS,EAAA,EAAI,MAAA,EAAQ,OAAA,CAAQ,MAAA,IAAU,CAAA;AAAE;AACnE,KACF;AAAA,EACF;AAAA,EAEA,MAAM,YAAA,CAAa,QAAA,EAAkB,MAAA,GAAiB,SAAA,EAA0B;AAC9E,IAAA,MAAM,IAAA,CAAK,OAAA;AAAA,MACT,eAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,UAAA,EAAa,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAA;AAAA,MAClF,EAAE,QAAQ,QAAA;AAAS,KACrB;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,MAAA,EAA8C;AAChE,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA;AAAA,MACrB,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC,EAAE,QAAQ,QAAA;AAAS,KACrB;AAIA,IAAA,OAAO,GAAA,IAAO,EAAE,OAAA,EAAS,IAAA,EAAM,eAAe,CAAA,EAAE;AAAA,EAClD;AAAA;AAAA,EAIA,MAAM,KAAA,CAAM,QAAA,EAAkB,OAAA,GAAwB,EAAC,EAAyB;AAC9E,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,CAAC,SAAS,CAAA;AAC7C,IAAA,MAAM,IAAA,GAAgC;AAAA,MACpC,KAAA,EAAO,QAAQ,IAAA,IAAQ,CAAA;AAAA,MACvB,kBAAA,EAAoB,QAAQ,iBAAA,IAAqB,IAAA;AAAA,MACjD,cAAA,EAAgB,QAAQ,aAAA,IAAiB;AAAA,KAC3C;AACA,IAAA,IAAI,OAAA,CAAQ,SAAA,KAAc,MAAA,EAAW,IAAA,CAAK,aAAa,OAAA,CAAQ,SAAA;AAC/D,IAAA,IAAI,OAAA,CAAQ,2BAA2B,MAAA,EAAW;AAChD,MAAA,IAAA,CAAK,2BAA2B,OAAA,CAAQ,sBAAA;AAAA,IAC1C;AACA,IAAA,IAAI,OAAA,CAAQ,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,mBAAmB,OAAA,CAAQ,aAAA;AACzE,IAAA,IAAI,OAAA,CAAQ,YAAA,KAAiB,MAAA,EAAW,IAAA,CAAK,gBAAgB,OAAA,CAAQ,YAAA;AACrE,IAAA,IAAI,OAAA,CAAQ,cAAA,KAAmB,MAAA,EAAW,IAAA,CAAK,kBAAkB,OAAA,CAAQ,cAAA;AACzE,IAAA,OAAO,IAAA,CAAK,QAAqB,WAAA,EAAa;AAAA,MAC5C,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,KAAA,EAAO,QAAA;AAAA,QACP,OAAA;AAAA,QACA,OAAA,EAAS;AAAA;AACX,KACD,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,WAAA,CAAY,QAAA,EAAkB,OAAA,GAAwB,EAAC,EAAoC;AACzF,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,CAAC,SAAS,CAAA;AAC7C,IAAA,MAAM,IAAA,GAAgC;AAAA,MACpC,KAAA,EAAO,QAAQ,IAAA,IAAQ,CAAA;AAAA,MACvB,kBAAA,EAAoB,QAAQ,iBAAA,IAAqB,IAAA;AAAA,MACjD,cAAA,EAAgB,QAAQ,aAAA,IAAiB;AAAA,KAC3C;AACA,IAAA,IAAI,OAAA,CAAQ,SAAA,KAAc,MAAA,EAAW,IAAA,CAAK,aAAa,OAAA,CAAQ,SAAA;AAC/D,IAAA,IAAI,OAAA,CAAQ,2BAA2B,MAAA,EAAW;AAChD,MAAA,IAAA,CAAK,2BAA2B,OAAA,CAAQ,sBAAA;AAAA,IAC1C;AACA,IAAA,IAAI,OAAA,CAAQ,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,mBAAmB,OAAA,CAAQ,aAAA;AACzE,IAAA,IAAI,OAAA,CAAQ,YAAA,KAAiB,MAAA,EAAW,IAAA,CAAK,gBAAgB,OAAA,CAAQ,YAAA;AACrE,IAAA,IAAI,OAAA,CAAQ,cAAA,KAAmB,MAAA,EAAW,IAAA,CAAK,kBAAkB,OAAA,CAAQ,cAAA;AACzE,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,KAAA,EAAO,QAAA;AAAA,MACP,OAAA;AAAA,MACA,MAAA,EAAQ,IAAA;AAAA,MACR,OAAA,EAAS;AAAA,KACX;AACA,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,SAAA,CAAA;AAC3B,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,IAAA,MAAM,YAAY,IAAA,CAAK,SAAA;AACvB,IAAA,MAAM,YAAY,IAAA,CAAK,eAAA;AACvB,IAAA,MAAM,kBAAkB,IAAA,CAAK,eAAA;AAC7B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAEpC,IAAA,OAAO;AAAA,MACL,CAAC,MAAA,CAAO,aAAa,GAAG,MAAM;AAC5B,QAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AAIvC,QAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,SAAS,CAAA;AAE5D,QAAA,IAAI,OAAA,GAAU,KAAA;AACd,QAAA,IAAI,MAAA,GAAyD,IAAA;AAC7D,QAAA,MAAM,OAAA,GAAU,IAAI,WAAA,CAAY,OAAO,CAAA;AACvC,QAAA,IAAI,MAAA,GAAS,EAAA;AACb,QAAA,IAAI,IAAA,GAAO,KAAA;AACX,QAAA,MAAM,QAA4B,EAAC;AACnC,QAAA,IAAI,YAAA,GAAwB,IAAA;AAE5B,QAAA,MAAM,gBAAgB,YAA2B;AAC/C,UAAA,IAAI,OAAA,EAAS;AACb,UAAA,OAAA,GAAU,IAAA;AAIV,UAAA,IAAI,iBAAA,GAAoB,eAAA;AACxB,UAAA,IAAI,SAAA,GAAY,GAAA;AAEhB,UAAA,OAAO,IAAA,EAAM;AACX,YAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,GAAA,EAAK;AAAA,cAC/B,MAAA,EAAQ,MAAA;AAAA,cACR,OAAA,EAAS;AAAA,gBACP,aAAA,EAAe,UAAU,MAAM,CAAA,CAAA;AAAA,gBAC/B,cAAA,EAAgB,kBAAA;AAAA,gBAChB,MAAA,EAAQ,mBAAA;AAAA,gBACR,YAAA,EAAc;AAAA,eAChB;AAAA,cACA,IAAA,EAAM,QAAA;AAAA,cACN,QAAQ,UAAA,CAAW;AAAA,aACpB,CAAA;AACD,YAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,IAAO,iBAAA,GAAoB,CAAA,EAAG;AAC/C,cAAA,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACtC,cAAA,MAAM,QAAQ,iBAAA,CAAkB,GAAA,CAAI,QAAQ,GAAA,CAAI,aAAa,GAAG,SAAS,CAAA;AACzE,cAAA,MAAM,MAAM,KAAK,CAAA;AACjB,cAAA,iBAAA,IAAqB,CAAA;AACrB,cAAA,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,SAAA,GAAY,CAAA,EAAG,kBAAkB,CAAA;AACtD,cAAA;AAAA,YACF;AACA,YAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,cAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAC5C,cAAA,IAAI,MAAA,GAAkB,IAAA;AACtB,cAAA,IAAI;AACF,gBAAA,MAAA,GAAS,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,IAAA;AAAA,cACrC,CAAA,CAAA,MAAQ;AAAA,cAER;AACA,cAAA,MAAM,MAAA,GACJ,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,WAAW,IAAA,IAAQ,OAAA,IAAW,MAAA,GACjE,MAAA,CAA8B,KAAA,GAC/B,MAAA;AACN,cAAA,MAAM,IAAI,WAAA;AAAA,gBACR,CAAA,WAAA,EAAc,GAAA,CAAI,MAAM,CAAA,EAAA,EAAK,OAAO,MAAA,KAAW,QAAA,GAAW,MAAA,GAAS,IAAA,CAAK,SAAA,CAAU,MAAA,IAAU,EAAE,CAAC,CAAA,CAAA;AAAA,gBAC/F,GAAA,CAAI,MAAA;AAAA,gBACJ;AAAA,eACF;AAAA,YACF;AACA,YAAA,IAAI,CAAC,IAAI,IAAA,EAAM;AACb,cAAA,MAAM,IAAI,WAAA,CAAY,4CAAA,EAA8C,GAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,YACtF;AACA,YAAA,MAAA,GAAS,GAAA,CAAI,KAAK,SAAA,EAAU;AAC5B,YAAA;AAAA,UACF;AAAA,QACF,CAAA;AAEA,QAAA,MAAM,cAAc,MAAY;AAI9B,UAAA,IAAI,GAAA;AACJ,UAAA,OAAA,CAAQ,GAAA,GAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,OAAO,EAAA,EAAI;AAC5C,YAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AACjC,YAAA,MAAA,GAAS,MAAA,CAAO,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA;AAC7B,YAAA,MAAM,YAAsB,EAAC;AAC7B,YAAA,KAAA,MAAW,OAAA,IAAW,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA,EAAG;AACvC,cAAA,MAAM,IAAA,GAAO,QAAQ,QAAA,CAAS,IAAI,IAAI,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,OAAA;AAC7D,cAAA,IAAI,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC7B,gBAAA,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,cAC9B,CAAA,MAAA,IAAW,IAAA,CAAK,UAAA,CAAW,OAAO,CAAA,EAAG;AACnC,gBAAA,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,cAC9B;AAAA,YACF;AACA,YAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC5B,YAAA,MAAM,UAAA,GAAa,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AACtC,YAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,cAAA,IAAA,GAAO,IAAA;AACP,cAAA;AAAA,YACF;AACA,YAAA,IAAI,OAAA;AACJ,YAAA,IAAI;AACF,cAAA,OAAA,GAAU,IAAA,CAAK,MAAM,UAAU,CAAA;AAAA,YACjC,CAAA,CAAA,MAAQ;AAEN,cAAA;AAAA,YACF;AACA,YAAA,IAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC1C,cAAA,MAAM,GAAA,GAAM,OAAA;AACZ,cAAA,IAAI,IAAI,KAAA,EAAO;AACb,gBAAA,YAAA,GAAe,IAAI,WAAA,CAAY,MAAA,CAAO,IAAI,KAAK,CAAA,EAAG,GAAG,GAAG,CAAA;AACxD,gBAAA,IAAA,GAAO,IAAA;AACP,gBAAA;AAAA,cACF;AAEA,cAAA,MAAM,UAAU,GAAA,CAAI,OAAA;AACpB,cAAA,IAAI,MAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,OAAA,CAAQ,SAAS,CAAA,EAAG;AAChD,gBAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,CAAC,CAAA,EAAG,KAAA,EAAO,OAAA;AACjC,gBAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,SAAS,CAAA,EAAG;AACjD,kBAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,OAAO,CAAA;AAAA,gBAC9C;AACA,gBAAA;AAAA,cACF;AAEA,cAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,MAAA,EAAQ,GAAI,KAAmE,CAAA;AAAA,YACpG;AAAA,UACF;AAAA,QACF,CAAA;AAEA,QAAA,OAAO;AAAA,UACL,MAAM,YAAuD;AAC3D,YAAA,IAAI;AACF,cAAA,MAAM,aAAA,EAAc;AAAA,YACtB,SAAS,GAAA,EAAK;AACZ,cAAA,YAAA,CAAa,KAAK,CAAA;AAClB,cAAA,MAAM,GAAA;AAAA,YACR;AACA,YAAA,OAAO,KAAA,CAAM,MAAA,KAAW,CAAA,IAAK,CAAC,IAAA,EAAM;AAClC,cAAA,IAAI,CAAC,MAAA,EAAQ;AACX,gBAAA,YAAA,CAAa,KAAK,CAAA;AAClB,gBAAA,MAAM,IAAI,WAAA,CAAY,mCAAA,EAAqC,CAAA,EAAG,IAAI,CAAA;AAAA,cACpE;AACA,cAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,IAAA,EAAK;AAChC,cAAA,IAAI,MAAM,IAAA,EAAM;AAGd,gBAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,kBAAA,MAAA,IAAU,MAAA;AACV,kBAAA,WAAA,EAAY;AAAA,gBACd;AACA,gBAAA,IAAA,GAAO,IAAA;AACP,gBAAA;AAAA,cACF;AACA,cAAA,MAAA,IAAU,QAAQ,MAAA,CAAO,KAAA,CAAM,OAAO,EAAE,MAAA,EAAQ,MAAM,CAAA;AACtD,cAAA,WAAA,EAAY;AAAA,YACd;AACA,YAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,cAAA,OAAO,EAAE,KAAA,EAAO,KAAA,CAAM,KAAA,EAAM,EAAuB,MAAM,KAAA,EAAM;AAAA,YACjE;AACA,YAAA,YAAA,CAAa,KAAK,CAAA;AAClB,YAAA,IAAI,cAAc,MAAM,YAAA;AACxB,YAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAA0C,IAAA,EAAM,IAAA,EAAK;AAAA,UACvE,CAAA;AAAA,UACA,QAAQ,YAAuD;AAG7D,YAAA,YAAA,CAAa,KAAK,CAAA;AAClB,YAAA,UAAA,CAAW,KAAA,EAAM;AACjB,YAAA,IAAI;AACF,cAAA,MAAM,QAAQ,MAAA,EAAO;AAAA,YACvB,CAAA,CAAA,MAAQ;AAAA,YAER;AACA,YAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAA0C,IAAA,EAAM,IAAA,EAAK;AAAA,UACvE;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,WAAA,GAAiC;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,CAA0C,CAAA,WAAA,CAAA,EAAe;AAAA,MACjF,MAAA,EAAQ;AAAA,KACT,CAAA;AACD,IAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,GAAI,SAAS,MAAA,CAAO,OAAA;AAAA,EACjD;AAAA,EAEA,MAAM,YAAA,CAAa,IAAA,EAAc,WAAA,EAAuC;AACtE,IAAA,OAAO,IAAA,CAAK,QAAgB,aAAA,EAAe;AAAA,MACzC,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,EAAE,IAAA,EAAM,WAAA;AAAY,KAC3B,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,MAAA,EAA+B;AAChD,IAAA,MAAM,KAAK,OAAA,CAAiB,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,CAAA,EAAI;AAAA,MACvE,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH;AAAA;AAAA,EAIA,MAAM,UAAA,CAAW,MAAA,GAAiB,SAAA,EAAgD;AAChF,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,QAAA,CAAA;AAAA,MACzC,EAAE,QAAQ,KAAA;AAAM,KAClB;AAAA,EACF;AAAA,EAEA,MAAM,iBAAA,CAAkB,MAAA,GAAiB,SAAA,EAAgD;AACvF,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,mBAAA,CAAA;AAAA,MACzC,EAAE,QAAQ,MAAA;AAAO,KACnB;AAAA,EACF;AACF","file":"index.cjs","sourcesContent":["export interface EngramClientOptions {\n /**\n * Engram API key. Looks like `eng_live_...`. Defaults to `process.env.ENGRAM_API_KEY`.\n */\n apiKey?: string;\n /**\n * API base URL. Defaults to `process.env.ENGRAM_BASE_URL` or `https://api.lumetra.io`.\n */\n baseUrl?: string;\n /**\n * Custom fetch implementation. Defaults to the global `fetch`.\n * Useful for proxying, retry middleware, or non-Node runtimes.\n */\n fetch?: typeof fetch;\n /**\n * Request timeout in milliseconds for buffered (non-streaming) calls.\n * Defaults to 30000 (30s).\n */\n timeoutMs?: number;\n /**\n * Timeout in milliseconds for `queryStream` calls. Streaming responses\n * can sit in the prep phase (retrieval + extractor pass) for 5–15s\n * before the first synthesis token arrives, so the buffered 30s\n * default would leave no headroom for the streamed body. Defaults to\n * 300000 (5 min). Use a higher value for very large synthesis bodies.\n */\n streamTimeoutMs?: number;\n /**\n * How many times to retry on a 429 (per-tenant concurrent-request cap).\n * Honors the server's `Retry-After` header, capped at 30s per sleep.\n * Defaults to 3. Set to 0 to disable retry and surface 429 as `EngramError`\n * on the first attempt.\n */\n maxRetriesOn429?: number;\n}\n\nexport interface Bucket {\n id: string;\n name: string;\n /**\n * Mirror of `name`. The server emits both so callers iterating both\n * `listBuckets` and `storeMemory` responses can use one field name.\n * Prefer `name` in new code.\n */\n bucket_name?: string;\n description?: string | null;\n created_at: string;\n memory_count?: number;\n}\n\nexport interface Memory {\n id: string;\n content: string;\n bucket_name?: string;\n created_at?: string;\n token_count?: number;\n}\n\nexport type StoreStatus = 'stored' | 'merged';\n\nexport type MergeReason =\n | 'content_hash'\n | 'embedding_similarity'\n | 'conflict_keep_existing'\n | 'concurrent_insert_race';\n\nexport interface StoreMemoryResult {\n id: string;\n /**\n * Alias for `id` — older API docs / older SDKs referenced this name.\n * Always present; prefer `id` in new code.\n */\n memory_id?: string;\n bucket_name: string;\n token_count: number;\n /**\n * `\"stored\"` for fresh writes, `\"merged\"` when the server collapsed\n * this write into a pre-existing memory via dedup. Always present.\n */\n status?: StoreStatus;\n /** Present only when `status === \"merged\"`. ID of the canonical memory the write was absorbed into. */\n deduped_into?: string;\n /** Present only when `status === \"merged\"`. Similarity score in [0.0, 1.0] (1.0 for content-hash matches). */\n similarity_score?: number;\n /** Present only when `status === \"merged\"`. Reason for the merge. */\n merge_reason?: MergeReason;\n}\n\n/**\n * Dedup policy passed to `storeMemory`. The server's default (currently\n * `\"loose\"`) applies when omitted.\n *\n * - `\"off\"` — store every write as a new memory; useful for templated\n * time-series ingest where structurally similar rows carry unique\n * values and would otherwise collapse.\n * - `\"loose\"` — merge writes at similarity ≥ 0.95 (default).\n * - `\"strict\"` — only merge near-identical content (≥ 0.99).\n */\nexport type DedupPolicy = 'off' | 'loose' | 'strict';\n\nexport interface ClearMemoriesResult {\n success: boolean;\n /** Number of memories actually deleted (server-reported). */\n cleared_count: number;\n}\n\nexport interface RetrievedMemory {\n id?: string;\n content: string;\n score?: number;\n bucket?: string;\n}\n\nexport interface QueryExplanation {\n retrieved_memories?: RetrievedMemory[];\n profile?: string | null;\n graph_facts?: string[];\n}\n\nexport interface QueryUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n}\n\nexport interface QueryResult {\n answer: string;\n /**\n * Parsed JSON when the request set `returnFormat: 'json'`. Present\n * only on JSON queries — the parsed value (object / array / scalar)\n * on success, or `null` when the model returned malformed JSON.\n */\n answer_json?: unknown;\n /**\n * Top-level count of retrieved memories. Equivalent to\n * `result.explanation?.retrieved_memories.length` but present even\n * when `returnExplanation` is false.\n */\n memories_found?: number;\n explanation?: QueryExplanation;\n usage?: QueryUsage;\n}\n\n/**\n * One frame yielded by {@link EngramClient.queryStream}.\n *\n * The shape is discriminated by `type`:\n * - `delta` frames carry an incremental piece of the answer in `content`.\n * - `done` carries the final usage + (optional) explanation. Emitted\n * exactly once at the end of the stream.\n */\nexport type QueryStreamEvent =\n | { type: 'delta'; content: string }\n | {\n type: 'done';\n usage?: QueryUsage;\n synthesis_usage?: unknown;\n explanation?: QueryExplanation;\n };\n\nexport interface QueryOptions {\n /**\n * Buckets to fuse across. Defaults to `['default']`.\n */\n buckets?: string[];\n /**\n * Maximum number of memories to retrieve per bucket. Defaults to 8.\n * Used as the fallback K when `topKPerBucket` doesn't cover a bucket.\n */\n topK?: number;\n /**\n * If true, server skips the synthesis LLM call and returns retrieval-only.\n * `answer` will be an empty string in that case. Defaults to false.\n */\n skipSynthesis?: boolean;\n /**\n * Whether to populate the `explanation` field. Defaults to true.\n */\n returnExplanation?: boolean;\n /**\n * Cap synthesis output tokens. Default is the server's (currently\n * 8192). Lower for agent loops or cost control.\n */\n maxTokens?: number;\n /**\n * Floor for retrieval scores. When set, drops retrieved chunks\n * below this raw cosine similarity — useful for citations-grade\n * output where every chunk should actually match.\n */\n minSimilarityThreshold?: number;\n /**\n * Per-bucket retrieval depth. `number` for a uniform value across\n * all buckets; an object for explicit per-bucket K (e.g.\n * `{ edgar_AAPL: 20, prices_AAPL: 4 }`). Missing buckets fall back\n * to `topK`. Lets callers express \"deep retrieval on this one,\n * shallow on the others.\"\n */\n topKPerBucket?: number | Record<string, number>;\n /**\n * `'prose'` (default) or `'json'`. When `'json'`, the server asks\n * the synthesizer for JSON output and returns the parsed value\n * under `result.answer_json` alongside the raw `result.answer`.\n */\n returnFormat?: 'prose' | 'json';\n /**\n * Optional JSON Schema describing the desired output shape. Included\n * in the prompt to guide the model. Best-effort — validate\n * client-side if you need strict enforcement.\n */\n responseSchema?: Record<string, unknown>;\n}\n\nexport interface ListMemoriesOptions {\n limit?: number;\n offset?: number;\n}\n\nexport interface ListMemoriesResult {\n memories: Memory[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport class EngramError extends Error {\n override readonly name = 'EngramError';\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n","import {\n EngramError,\n type Bucket,\n type ClearMemoriesResult,\n type DedupPolicy,\n type EngramClientOptions,\n type ListMemoriesOptions,\n type ListMemoriesResult,\n type QueryOptions,\n type QueryResult,\n type QueryStreamEvent,\n type StoreMemoryResult,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.lumetra.io';\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_STREAM_TIMEOUT_MS = 300_000;\nconst DEFAULT_MAX_RETRIES_ON_429 = 3;\n// Cap on per-attempt backoff so a misconfigured server can't force\n// callers to sleep for minutes.\nconst RETRY_AFTER_CAP_MS = 30_000;\nconst SDK_VERSION = '0.5.0';\nconst USER_AGENT = `engram-js/${SDK_VERSION}`;\n\nfunction parseRetryAfterMs(header: string | null, defaultBackoffMs: number): number {\n if (header) {\n const value = Number(header.trim());\n if (Number.isFinite(value) && value >= 0) {\n return Math.min(value * 1000, RETRY_AFTER_CAP_MS);\n }\n }\n return Math.min(defaultBackoffMs, RETRY_AFTER_CAP_MS);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class EngramClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly streamTimeoutMs: number;\n private readonly maxRetriesOn429: number;\n\n constructor(options: EngramClientOptions = {}) {\n const apiKey =\n options.apiKey ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_API_KEY : undefined) ??\n '';\n if (!apiKey) {\n throw new Error(\n 'EngramClient: apiKey is required. Pass it explicitly or set ENGRAM_API_KEY in your environment.',\n );\n }\n const baseUrl =\n options.baseUrl ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_BASE_URL : undefined) ??\n DEFAULT_BASE_URL;\n\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.streamTimeoutMs = options.streamTimeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS;\n this.maxRetriesOn429 = Math.max(0, options.maxRetriesOn429 ?? DEFAULT_MAX_RETRIES_ON_429);\n\n if (typeof this.fetchImpl !== 'function') {\n throw new Error(\n 'EngramClient: no fetch implementation available. Use Node 18+, or pass options.fetch.',\n );\n }\n }\n\n private async request<T>(\n path: string,\n init: { method: string; body?: unknown; query?: Record<string, string | number | undefined> } = {\n method: 'GET',\n },\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (init.query) {\n for (const [k, v] of Object.entries(init.query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n // 429-aware retry. The Engram API enforces a per-tenant concurrent-\n // request cap and sets Retry-After on 429s; without retry handling,\n // bursty clients fail immediately under load. The body is JSON-\n // serialized once outside the loop so each attempt sends an\n // identical request.\n const requestBody = init.body !== undefined ? JSON.stringify(init.body) : undefined;\n let attemptsRemaining = this.maxRetriesOn429;\n let backoffMs = 1000;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let res: Response;\n try {\n res = await this.fetchImpl(url.toString(), {\n method: init.method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': USER_AGENT,\n },\n body: requestBody,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (res.status === 429 && attemptsRemaining > 0) {\n // Drain the body so the connection can return to the pool.\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n\n const text = await res.text();\n let parsed: unknown = undefined;\n if (text) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n\n if (!res.ok) {\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n\n return parsed as T;\n }\n }\n\n // ---------- Memories ----------\n\n async storeMemory(\n content: string,\n bucket: string = 'default',\n options: { dedup?: DedupPolicy } = {},\n ): Promise<StoreMemoryResult> {\n const body: Record<string, unknown> = { content };\n if (options.dedup !== undefined) body.dedup = options.dedup;\n return this.request<StoreMemoryResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body },\n );\n }\n\n async storeMemories(\n contents: string[],\n bucket: string = 'default',\n ): Promise<{ memories: StoreMemoryResult[] }> {\n // Defensively unwrap: depending on server version the batch endpoint\n // returns either { memories: [...] } or a bare array. Normalize to the\n // wrapped shape so callers don't have to switch on it.\n const result = await this.request<{ memories: StoreMemoryResult[] } | StoreMemoryResult[]>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body: { memories: contents.map((content) => ({ content })) } },\n );\n return Array.isArray(result) ? { memories: result } : result;\n }\n\n async listMemories(\n bucket: string = 'default',\n options: ListMemoriesOptions = {},\n ): Promise<ListMemoriesResult> {\n return this.request<ListMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n {\n method: 'GET',\n query: { limit: options.limit ?? 20, offset: options.offset ?? 0 },\n },\n );\n }\n\n async deleteMemory(memoryId: string, bucket: string = 'default'): Promise<void> {\n await this.request<unknown>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories/${encodeURIComponent(memoryId)}`,\n { method: 'DELETE' },\n );\n }\n\n async clearMemories(bucket: string): Promise<ClearMemoriesResult> {\n const res = await this.request<ClearMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'DELETE' },\n );\n // Defensive default: older servers / proxies may strip the body. The\n // contract surface is {success, cleared_count}; if the server stayed\n // silent we still return the same shape so callers can rely on it.\n return res ?? { success: true, cleared_count: 0 };\n }\n\n // ---------- Query ----------\n\n async query(question: string, options: QueryOptions = {}): Promise<QueryResult> {\n const buckets = options.buckets ?? ['default'];\n const opts: Record<string, unknown> = {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n };\n if (options.maxTokens !== undefined) opts.max_tokens = options.maxTokens;\n if (options.minSimilarityThreshold !== undefined) {\n opts.min_similarity_threshold = options.minSimilarityThreshold;\n }\n if (options.topKPerBucket !== undefined) opts.top_k_per_bucket = options.topKPerBucket;\n if (options.returnFormat !== undefined) opts.return_format = options.returnFormat;\n if (options.responseSchema !== undefined) opts.response_schema = options.responseSchema;\n return this.request<QueryResult>('/v1/query', {\n method: 'POST',\n body: {\n query: question,\n buckets,\n options: opts,\n },\n });\n }\n\n /**\n * Streaming variant of {@link query}. Returns an async-iterable that\n * yields {@link QueryStreamEvent} frames as the server produces them:\n *\n * for await (const ev of engram.queryStream('...')) {\n * if (ev.type === 'delta') process.stdout.write(ev.content);\n * else if (ev.type === 'done') console.log(ev.usage);\n * }\n *\n * Break out of the loop to abort the request (the underlying\n * AbortController is wired up to the fetch call).\n */\n queryStream(question: string, options: QueryOptions = {}): AsyncIterable<QueryStreamEvent> {\n const buckets = options.buckets ?? ['default'];\n const opts: Record<string, unknown> = {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n };\n if (options.maxTokens !== undefined) opts.max_tokens = options.maxTokens;\n if (options.minSimilarityThreshold !== undefined) {\n opts.min_similarity_threshold = options.minSimilarityThreshold;\n }\n if (options.topKPerBucket !== undefined) opts.top_k_per_bucket = options.topKPerBucket;\n if (options.returnFormat !== undefined) opts.return_format = options.returnFormat;\n if (options.responseSchema !== undefined) opts.response_schema = options.responseSchema;\n const body = {\n query: question,\n buckets,\n stream: true,\n options: opts,\n };\n const url = `${this.baseUrl}/v1/query`;\n const apiKey = this.apiKey;\n const fetchImpl = this.fetchImpl;\n const timeoutMs = this.streamTimeoutMs;\n const maxRetriesOn429 = this.maxRetriesOn429;\n const bodyJson = JSON.stringify(body);\n\n return {\n [Symbol.asyncIterator]: () => {\n const controller = new AbortController();\n // The timeout caps total wall-clock for the stream. Set generously\n // because synthesis can run 10–25s before the first byte even with\n // streaming on slow paths; clamp to the user's configured timeout.\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let started = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n let done = false;\n const queue: QueryStreamEvent[] = [];\n let pendingError: unknown = null;\n\n const ensureStarted = async (): Promise<void> => {\n if (started) return;\n started = true;\n // 429-aware retry at the connection-open stage only. Once\n // the response body starts flowing we can't resume mid-\n // stream safely.\n let attemptsRemaining = maxRetriesOn429;\n let backoffMs = 1000;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n 'User-Agent': USER_AGENT,\n },\n body: bodyJson,\n signal: controller.signal,\n });\n if (res.status === 429 && attemptsRemaining > 0) {\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n let parsed: unknown = text;\n try {\n parsed = text ? JSON.parse(text) : text;\n } catch {\n /* keep raw text */\n }\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n if (!res.body) {\n throw new EngramError('Engram API: streaming response has no body', res.status, null);\n }\n reader = res.body.getReader();\n return;\n }\n };\n\n const drainBuffer = (): void => {\n // SSE frames are separated by a blank line ('\\n\\n'). Each frame\n // is one or more 'field: value' lines. We only care about\n // 'data:' lines for this stream.\n let idx: number;\n while ((idx = buffer.indexOf('\\n\\n')) !== -1) {\n const frame = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 2);\n const dataLines: string[] = [];\n for (const rawLine of frame.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine;\n if (line.startsWith('data: ')) {\n dataLines.push(line.slice(6));\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5));\n }\n }\n if (dataLines.length === 0) continue;\n const payloadStr = dataLines.join('\\n');\n if (payloadStr === '[DONE]') {\n done = true;\n return;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(payloadStr);\n } catch {\n // Malformed frame — skip rather than corrupt the stream.\n continue;\n }\n if (payload && typeof payload === 'object') {\n const obj = payload as Record<string, unknown>;\n if (obj.error) {\n pendingError = new EngramError(String(obj.error), 0, obj);\n done = true;\n return;\n }\n // OpenAI-style delta chunk\n const choices = obj.choices as Array<{ delta?: { content?: string } }> | undefined;\n if (Array.isArray(choices) && choices.length > 0) {\n const delta = choices[0]?.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n queue.push({ type: 'delta', content: delta });\n }\n continue;\n }\n // Final usage / explanation frame (no 'choices' key).\n queue.push({ type: 'done', ...(obj as Omit<Extract<QueryStreamEvent, { type: 'done' }>, 'type'>) });\n }\n }\n };\n\n return {\n next: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n try {\n await ensureStarted();\n } catch (err) {\n clearTimeout(timer);\n throw err;\n }\n while (queue.length === 0 && !done) {\n if (!reader) {\n clearTimeout(timer);\n throw new EngramError('Engram API: stream reader missing', 0, null);\n }\n const chunk = await reader.read();\n if (chunk.done) {\n // Flush whatever's left in the buffer (some servers don't\n // terminate with a trailing blank line).\n if (buffer.length > 0) {\n buffer += '\\n\\n';\n drainBuffer();\n }\n done = true;\n break;\n }\n buffer += decoder.decode(chunk.value, { stream: true });\n drainBuffer();\n }\n if (queue.length > 0) {\n return { value: queue.shift() as QueryStreamEvent, done: false };\n }\n clearTimeout(timer);\n if (pendingError) throw pendingError;\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n return: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n // Caller broke out of the for-await loop — cancel the upstream\n // request so we're not holding the connection open.\n clearTimeout(timer);\n controller.abort();\n try {\n await reader?.cancel();\n } catch {\n /* ignored */\n }\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n };\n },\n };\n }\n\n // ---------- Buckets ----------\n\n async listBuckets(): Promise<Bucket[]> {\n const result = await this.request<{ buckets: Bucket[] } | Bucket[]>(`/v1/buckets`, {\n method: 'GET',\n });\n return Array.isArray(result) ? result : result.buckets;\n }\n\n async createBucket(name: string, description?: string): Promise<Bucket> {\n return this.request<Bucket>('/v1/buckets', {\n method: 'POST',\n body: { name, description },\n });\n }\n\n async deleteBucket(bucket: string): Promise<void> {\n await this.request<unknown>(`/v1/buckets/${encodeURIComponent(bucket)}`, {\n method: 'DELETE',\n });\n }\n\n // ---------- Profile ----------\n\n async getProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile`,\n { method: 'GET' },\n );\n }\n\n async regenerateProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile/regenerate`,\n { method: 'POST' },\n );\n }\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -53,6 +53,8 @@ interface Memory {
|
|
|
53
53
|
created_at?: string;
|
|
54
54
|
token_count?: number;
|
|
55
55
|
}
|
|
56
|
+
type StoreStatus = 'stored' | 'merged';
|
|
57
|
+
type MergeReason = 'content_hash' | 'embedding_similarity' | 'conflict_keep_existing' | 'concurrent_insert_race';
|
|
56
58
|
interface StoreMemoryResult {
|
|
57
59
|
id: string;
|
|
58
60
|
/**
|
|
@@ -62,7 +64,29 @@ interface StoreMemoryResult {
|
|
|
62
64
|
memory_id?: string;
|
|
63
65
|
bucket_name: string;
|
|
64
66
|
token_count: number;
|
|
67
|
+
/**
|
|
68
|
+
* `"stored"` for fresh writes, `"merged"` when the server collapsed
|
|
69
|
+
* this write into a pre-existing memory via dedup. Always present.
|
|
70
|
+
*/
|
|
71
|
+
status?: StoreStatus;
|
|
72
|
+
/** Present only when `status === "merged"`. ID of the canonical memory the write was absorbed into. */
|
|
73
|
+
deduped_into?: string;
|
|
74
|
+
/** Present only when `status === "merged"`. Similarity score in [0.0, 1.0] (1.0 for content-hash matches). */
|
|
75
|
+
similarity_score?: number;
|
|
76
|
+
/** Present only when `status === "merged"`. Reason for the merge. */
|
|
77
|
+
merge_reason?: MergeReason;
|
|
65
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Dedup policy passed to `storeMemory`. The server's default (currently
|
|
81
|
+
* `"loose"`) applies when omitted.
|
|
82
|
+
*
|
|
83
|
+
* - `"off"` — store every write as a new memory; useful for templated
|
|
84
|
+
* time-series ingest where structurally similar rows carry unique
|
|
85
|
+
* values and would otherwise collapse.
|
|
86
|
+
* - `"loose"` — merge writes at similarity ≥ 0.95 (default).
|
|
87
|
+
* - `"strict"` — only merge near-identical content (≥ 0.99).
|
|
88
|
+
*/
|
|
89
|
+
type DedupPolicy = 'off' | 'loose' | 'strict';
|
|
66
90
|
interface ClearMemoriesResult {
|
|
67
91
|
success: boolean;
|
|
68
92
|
/** Number of memories actually deleted (server-reported). */
|
|
@@ -86,6 +110,12 @@ interface QueryUsage {
|
|
|
86
110
|
}
|
|
87
111
|
interface QueryResult {
|
|
88
112
|
answer: string;
|
|
113
|
+
/**
|
|
114
|
+
* Parsed JSON when the request set `returnFormat: 'json'`. Present
|
|
115
|
+
* only on JSON queries — the parsed value (object / array / scalar)
|
|
116
|
+
* on success, or `null` when the model returned malformed JSON.
|
|
117
|
+
*/
|
|
118
|
+
answer_json?: unknown;
|
|
89
119
|
/**
|
|
90
120
|
* Top-level count of retrieved memories. Equivalent to
|
|
91
121
|
* `result.explanation?.retrieved_memories.length` but present even
|
|
@@ -118,7 +148,8 @@ interface QueryOptions {
|
|
|
118
148
|
*/
|
|
119
149
|
buckets?: string[];
|
|
120
150
|
/**
|
|
121
|
-
* Maximum number of memories to retrieve. Defaults to 8.
|
|
151
|
+
* Maximum number of memories to retrieve per bucket. Defaults to 8.
|
|
152
|
+
* Used as the fallback K when `topKPerBucket` doesn't cover a bucket.
|
|
122
153
|
*/
|
|
123
154
|
topK?: number;
|
|
124
155
|
/**
|
|
@@ -130,6 +161,37 @@ interface QueryOptions {
|
|
|
130
161
|
* Whether to populate the `explanation` field. Defaults to true.
|
|
131
162
|
*/
|
|
132
163
|
returnExplanation?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Cap synthesis output tokens. Default is the server's (currently
|
|
166
|
+
* 8192). Lower for agent loops or cost control.
|
|
167
|
+
*/
|
|
168
|
+
maxTokens?: number;
|
|
169
|
+
/**
|
|
170
|
+
* Floor for retrieval scores. When set, drops retrieved chunks
|
|
171
|
+
* below this raw cosine similarity — useful for citations-grade
|
|
172
|
+
* output where every chunk should actually match.
|
|
173
|
+
*/
|
|
174
|
+
minSimilarityThreshold?: number;
|
|
175
|
+
/**
|
|
176
|
+
* Per-bucket retrieval depth. `number` for a uniform value across
|
|
177
|
+
* all buckets; an object for explicit per-bucket K (e.g.
|
|
178
|
+
* `{ edgar_AAPL: 20, prices_AAPL: 4 }`). Missing buckets fall back
|
|
179
|
+
* to `topK`. Lets callers express "deep retrieval on this one,
|
|
180
|
+
* shallow on the others."
|
|
181
|
+
*/
|
|
182
|
+
topKPerBucket?: number | Record<string, number>;
|
|
183
|
+
/**
|
|
184
|
+
* `'prose'` (default) or `'json'`. When `'json'`, the server asks
|
|
185
|
+
* the synthesizer for JSON output and returns the parsed value
|
|
186
|
+
* under `result.answer_json` alongside the raw `result.answer`.
|
|
187
|
+
*/
|
|
188
|
+
returnFormat?: 'prose' | 'json';
|
|
189
|
+
/**
|
|
190
|
+
* Optional JSON Schema describing the desired output shape. Included
|
|
191
|
+
* in the prompt to guide the model. Best-effort — validate
|
|
192
|
+
* client-side if you need strict enforcement.
|
|
193
|
+
*/
|
|
194
|
+
responseSchema?: Record<string, unknown>;
|
|
133
195
|
}
|
|
134
196
|
interface ListMemoriesOptions {
|
|
135
197
|
limit?: number;
|
|
@@ -157,7 +219,9 @@ declare class EngramClient {
|
|
|
157
219
|
private readonly maxRetriesOn429;
|
|
158
220
|
constructor(options?: EngramClientOptions);
|
|
159
221
|
private request;
|
|
160
|
-
storeMemory(content: string, bucket?: string
|
|
222
|
+
storeMemory(content: string, bucket?: string, options?: {
|
|
223
|
+
dedup?: DedupPolicy;
|
|
224
|
+
}): Promise<StoreMemoryResult>;
|
|
161
225
|
storeMemories(contents: string[], bucket?: string): Promise<{
|
|
162
226
|
memories: StoreMemoryResult[];
|
|
163
227
|
}>;
|
|
@@ -189,4 +253,4 @@ declare class EngramClient {
|
|
|
189
253
|
}>;
|
|
190
254
|
}
|
|
191
255
|
|
|
192
|
-
export { type Bucket, type ClearMemoriesResult, EngramClient, type EngramClientOptions, EngramError, type ListMemoriesOptions, type ListMemoriesResult, type Memory, type QueryExplanation, type QueryOptions, type QueryResult, type QueryStreamEvent, type QueryUsage, type RetrievedMemory, type StoreMemoryResult };
|
|
256
|
+
export { type Bucket, type ClearMemoriesResult, type DedupPolicy, EngramClient, type EngramClientOptions, EngramError, type ListMemoriesOptions, type ListMemoriesResult, type Memory, type MergeReason, type QueryExplanation, type QueryOptions, type QueryResult, type QueryStreamEvent, type QueryUsage, type RetrievedMemory, type StoreMemoryResult, type StoreStatus };
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,8 @@ interface Memory {
|
|
|
53
53
|
created_at?: string;
|
|
54
54
|
token_count?: number;
|
|
55
55
|
}
|
|
56
|
+
type StoreStatus = 'stored' | 'merged';
|
|
57
|
+
type MergeReason = 'content_hash' | 'embedding_similarity' | 'conflict_keep_existing' | 'concurrent_insert_race';
|
|
56
58
|
interface StoreMemoryResult {
|
|
57
59
|
id: string;
|
|
58
60
|
/**
|
|
@@ -62,7 +64,29 @@ interface StoreMemoryResult {
|
|
|
62
64
|
memory_id?: string;
|
|
63
65
|
bucket_name: string;
|
|
64
66
|
token_count: number;
|
|
67
|
+
/**
|
|
68
|
+
* `"stored"` for fresh writes, `"merged"` when the server collapsed
|
|
69
|
+
* this write into a pre-existing memory via dedup. Always present.
|
|
70
|
+
*/
|
|
71
|
+
status?: StoreStatus;
|
|
72
|
+
/** Present only when `status === "merged"`. ID of the canonical memory the write was absorbed into. */
|
|
73
|
+
deduped_into?: string;
|
|
74
|
+
/** Present only when `status === "merged"`. Similarity score in [0.0, 1.0] (1.0 for content-hash matches). */
|
|
75
|
+
similarity_score?: number;
|
|
76
|
+
/** Present only when `status === "merged"`. Reason for the merge. */
|
|
77
|
+
merge_reason?: MergeReason;
|
|
65
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Dedup policy passed to `storeMemory`. The server's default (currently
|
|
81
|
+
* `"loose"`) applies when omitted.
|
|
82
|
+
*
|
|
83
|
+
* - `"off"` — store every write as a new memory; useful for templated
|
|
84
|
+
* time-series ingest where structurally similar rows carry unique
|
|
85
|
+
* values and would otherwise collapse.
|
|
86
|
+
* - `"loose"` — merge writes at similarity ≥ 0.95 (default).
|
|
87
|
+
* - `"strict"` — only merge near-identical content (≥ 0.99).
|
|
88
|
+
*/
|
|
89
|
+
type DedupPolicy = 'off' | 'loose' | 'strict';
|
|
66
90
|
interface ClearMemoriesResult {
|
|
67
91
|
success: boolean;
|
|
68
92
|
/** Number of memories actually deleted (server-reported). */
|
|
@@ -86,6 +110,12 @@ interface QueryUsage {
|
|
|
86
110
|
}
|
|
87
111
|
interface QueryResult {
|
|
88
112
|
answer: string;
|
|
113
|
+
/**
|
|
114
|
+
* Parsed JSON when the request set `returnFormat: 'json'`. Present
|
|
115
|
+
* only on JSON queries — the parsed value (object / array / scalar)
|
|
116
|
+
* on success, or `null` when the model returned malformed JSON.
|
|
117
|
+
*/
|
|
118
|
+
answer_json?: unknown;
|
|
89
119
|
/**
|
|
90
120
|
* Top-level count of retrieved memories. Equivalent to
|
|
91
121
|
* `result.explanation?.retrieved_memories.length` but present even
|
|
@@ -118,7 +148,8 @@ interface QueryOptions {
|
|
|
118
148
|
*/
|
|
119
149
|
buckets?: string[];
|
|
120
150
|
/**
|
|
121
|
-
* Maximum number of memories to retrieve. Defaults to 8.
|
|
151
|
+
* Maximum number of memories to retrieve per bucket. Defaults to 8.
|
|
152
|
+
* Used as the fallback K when `topKPerBucket` doesn't cover a bucket.
|
|
122
153
|
*/
|
|
123
154
|
topK?: number;
|
|
124
155
|
/**
|
|
@@ -130,6 +161,37 @@ interface QueryOptions {
|
|
|
130
161
|
* Whether to populate the `explanation` field. Defaults to true.
|
|
131
162
|
*/
|
|
132
163
|
returnExplanation?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Cap synthesis output tokens. Default is the server's (currently
|
|
166
|
+
* 8192). Lower for agent loops or cost control.
|
|
167
|
+
*/
|
|
168
|
+
maxTokens?: number;
|
|
169
|
+
/**
|
|
170
|
+
* Floor for retrieval scores. When set, drops retrieved chunks
|
|
171
|
+
* below this raw cosine similarity — useful for citations-grade
|
|
172
|
+
* output where every chunk should actually match.
|
|
173
|
+
*/
|
|
174
|
+
minSimilarityThreshold?: number;
|
|
175
|
+
/**
|
|
176
|
+
* Per-bucket retrieval depth. `number` for a uniform value across
|
|
177
|
+
* all buckets; an object for explicit per-bucket K (e.g.
|
|
178
|
+
* `{ edgar_AAPL: 20, prices_AAPL: 4 }`). Missing buckets fall back
|
|
179
|
+
* to `topK`. Lets callers express "deep retrieval on this one,
|
|
180
|
+
* shallow on the others."
|
|
181
|
+
*/
|
|
182
|
+
topKPerBucket?: number | Record<string, number>;
|
|
183
|
+
/**
|
|
184
|
+
* `'prose'` (default) or `'json'`. When `'json'`, the server asks
|
|
185
|
+
* the synthesizer for JSON output and returns the parsed value
|
|
186
|
+
* under `result.answer_json` alongside the raw `result.answer`.
|
|
187
|
+
*/
|
|
188
|
+
returnFormat?: 'prose' | 'json';
|
|
189
|
+
/**
|
|
190
|
+
* Optional JSON Schema describing the desired output shape. Included
|
|
191
|
+
* in the prompt to guide the model. Best-effort — validate
|
|
192
|
+
* client-side if you need strict enforcement.
|
|
193
|
+
*/
|
|
194
|
+
responseSchema?: Record<string, unknown>;
|
|
133
195
|
}
|
|
134
196
|
interface ListMemoriesOptions {
|
|
135
197
|
limit?: number;
|
|
@@ -157,7 +219,9 @@ declare class EngramClient {
|
|
|
157
219
|
private readonly maxRetriesOn429;
|
|
158
220
|
constructor(options?: EngramClientOptions);
|
|
159
221
|
private request;
|
|
160
|
-
storeMemory(content: string, bucket?: string
|
|
222
|
+
storeMemory(content: string, bucket?: string, options?: {
|
|
223
|
+
dedup?: DedupPolicy;
|
|
224
|
+
}): Promise<StoreMemoryResult>;
|
|
161
225
|
storeMemories(contents: string[], bucket?: string): Promise<{
|
|
162
226
|
memories: StoreMemoryResult[];
|
|
163
227
|
}>;
|
|
@@ -189,4 +253,4 @@ declare class EngramClient {
|
|
|
189
253
|
}>;
|
|
190
254
|
}
|
|
191
255
|
|
|
192
|
-
export { type Bucket, type ClearMemoriesResult, EngramClient, type EngramClientOptions, EngramError, type ListMemoriesOptions, type ListMemoriesResult, type Memory, type QueryExplanation, type QueryOptions, type QueryResult, type QueryStreamEvent, type QueryUsage, type RetrievedMemory, type StoreMemoryResult };
|
|
256
|
+
export { type Bucket, type ClearMemoriesResult, type DedupPolicy, EngramClient, type EngramClientOptions, EngramError, type ListMemoriesOptions, type ListMemoriesResult, type Memory, type MergeReason, type QueryExplanation, type QueryOptions, type QueryResult, type QueryStreamEvent, type QueryUsage, type RetrievedMemory, type StoreMemoryResult, type StoreStatus };
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ var DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
16
16
|
var DEFAULT_STREAM_TIMEOUT_MS = 3e5;
|
|
17
17
|
var DEFAULT_MAX_RETRIES_ON_429 = 3;
|
|
18
18
|
var RETRY_AFTER_CAP_MS = 3e4;
|
|
19
|
-
var SDK_VERSION = "0.
|
|
19
|
+
var SDK_VERSION = "0.5.0";
|
|
20
20
|
var USER_AGENT = `engram-js/${SDK_VERSION}`;
|
|
21
21
|
function parseRetryAfterMs(header, defaultBackoffMs) {
|
|
22
22
|
if (header) {
|
|
@@ -116,10 +116,12 @@ var EngramClient = class {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
// ---------- Memories ----------
|
|
119
|
-
async storeMemory(content, bucket = "default") {
|
|
119
|
+
async storeMemory(content, bucket = "default", options = {}) {
|
|
120
|
+
const body = { content };
|
|
121
|
+
if (options.dedup !== void 0) body.dedup = options.dedup;
|
|
120
122
|
return this.request(
|
|
121
123
|
`/v1/buckets/${encodeURIComponent(bucket)}/memories`,
|
|
122
|
-
{ method: "POST", body
|
|
124
|
+
{ method: "POST", body }
|
|
123
125
|
);
|
|
124
126
|
}
|
|
125
127
|
async storeMemories(contents, bucket = "default") {
|
|
@@ -154,16 +156,24 @@ var EngramClient = class {
|
|
|
154
156
|
// ---------- Query ----------
|
|
155
157
|
async query(question, options = {}) {
|
|
156
158
|
const buckets = options.buckets ?? ["default"];
|
|
159
|
+
const opts = {
|
|
160
|
+
top_k: options.topK ?? 8,
|
|
161
|
+
return_explanation: options.returnExplanation ?? true,
|
|
162
|
+
skip_synthesis: options.skipSynthesis ?? false
|
|
163
|
+
};
|
|
164
|
+
if (options.maxTokens !== void 0) opts.max_tokens = options.maxTokens;
|
|
165
|
+
if (options.minSimilarityThreshold !== void 0) {
|
|
166
|
+
opts.min_similarity_threshold = options.minSimilarityThreshold;
|
|
167
|
+
}
|
|
168
|
+
if (options.topKPerBucket !== void 0) opts.top_k_per_bucket = options.topKPerBucket;
|
|
169
|
+
if (options.returnFormat !== void 0) opts.return_format = options.returnFormat;
|
|
170
|
+
if (options.responseSchema !== void 0) opts.response_schema = options.responseSchema;
|
|
157
171
|
return this.request("/v1/query", {
|
|
158
172
|
method: "POST",
|
|
159
173
|
body: {
|
|
160
174
|
query: question,
|
|
161
175
|
buckets,
|
|
162
|
-
options:
|
|
163
|
-
top_k: options.topK ?? 8,
|
|
164
|
-
return_explanation: options.returnExplanation ?? true,
|
|
165
|
-
skip_synthesis: options.skipSynthesis ?? false
|
|
166
|
-
}
|
|
176
|
+
options: opts
|
|
167
177
|
}
|
|
168
178
|
});
|
|
169
179
|
}
|
|
@@ -181,15 +191,23 @@ var EngramClient = class {
|
|
|
181
191
|
*/
|
|
182
192
|
queryStream(question, options = {}) {
|
|
183
193
|
const buckets = options.buckets ?? ["default"];
|
|
194
|
+
const opts = {
|
|
195
|
+
top_k: options.topK ?? 8,
|
|
196
|
+
return_explanation: options.returnExplanation ?? true,
|
|
197
|
+
skip_synthesis: options.skipSynthesis ?? false
|
|
198
|
+
};
|
|
199
|
+
if (options.maxTokens !== void 0) opts.max_tokens = options.maxTokens;
|
|
200
|
+
if (options.minSimilarityThreshold !== void 0) {
|
|
201
|
+
opts.min_similarity_threshold = options.minSimilarityThreshold;
|
|
202
|
+
}
|
|
203
|
+
if (options.topKPerBucket !== void 0) opts.top_k_per_bucket = options.topKPerBucket;
|
|
204
|
+
if (options.returnFormat !== void 0) opts.return_format = options.returnFormat;
|
|
205
|
+
if (options.responseSchema !== void 0) opts.response_schema = options.responseSchema;
|
|
184
206
|
const body = {
|
|
185
207
|
query: question,
|
|
186
208
|
buckets,
|
|
187
209
|
stream: true,
|
|
188
|
-
options:
|
|
189
|
-
top_k: options.topK ?? 8,
|
|
190
|
-
return_explanation: options.returnExplanation ?? true,
|
|
191
|
-
skip_synthesis: options.skipSynthesis ?? false
|
|
192
|
-
}
|
|
210
|
+
options: opts
|
|
193
211
|
};
|
|
194
212
|
const url = `${this.baseUrl}/v1/query`;
|
|
195
213
|
const apiKey = this.apiKey;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export interface EngramClientOptions {\n /**\n * Engram API key. Looks like `eng_live_...`. Defaults to `process.env.ENGRAM_API_KEY`.\n */\n apiKey?: string;\n /**\n * API base URL. Defaults to `process.env.ENGRAM_BASE_URL` or `https://api.lumetra.io`.\n */\n baseUrl?: string;\n /**\n * Custom fetch implementation. Defaults to the global `fetch`.\n * Useful for proxying, retry middleware, or non-Node runtimes.\n */\n fetch?: typeof fetch;\n /**\n * Request timeout in milliseconds for buffered (non-streaming) calls.\n * Defaults to 30000 (30s).\n */\n timeoutMs?: number;\n /**\n * Timeout in milliseconds for `queryStream` calls. Streaming responses\n * can sit in the prep phase (retrieval + extractor pass) for 5–15s\n * before the first synthesis token arrives, so the buffered 30s\n * default would leave no headroom for the streamed body. Defaults to\n * 300000 (5 min). Use a higher value for very large synthesis bodies.\n */\n streamTimeoutMs?: number;\n /**\n * How many times to retry on a 429 (per-tenant concurrent-request cap).\n * Honors the server's `Retry-After` header, capped at 30s per sleep.\n * Defaults to 3. Set to 0 to disable retry and surface 429 as `EngramError`\n * on the first attempt.\n */\n maxRetriesOn429?: number;\n}\n\nexport interface Bucket {\n id: string;\n name: string;\n /**\n * Mirror of `name`. The server emits both so callers iterating both\n * `listBuckets` and `storeMemory` responses can use one field name.\n * Prefer `name` in new code.\n */\n bucket_name?: string;\n description?: string | null;\n created_at: string;\n memory_count?: number;\n}\n\nexport interface Memory {\n id: string;\n content: string;\n bucket_name?: string;\n created_at?: string;\n token_count?: number;\n}\n\nexport interface StoreMemoryResult {\n id: string;\n /**\n * Alias for `id` — older API docs / older SDKs referenced this name.\n * Always present; prefer `id` in new code.\n */\n memory_id?: string;\n bucket_name: string;\n token_count: number;\n}\n\nexport interface ClearMemoriesResult {\n success: boolean;\n /** Number of memories actually deleted (server-reported). */\n cleared_count: number;\n}\n\nexport interface RetrievedMemory {\n id?: string;\n content: string;\n score?: number;\n bucket?: string;\n}\n\nexport interface QueryExplanation {\n retrieved_memories?: RetrievedMemory[];\n profile?: string | null;\n graph_facts?: string[];\n}\n\nexport interface QueryUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n}\n\nexport interface QueryResult {\n answer: string;\n /**\n * Top-level count of retrieved memories. Equivalent to\n * `result.explanation?.retrieved_memories.length` but present even\n * when `returnExplanation` is false.\n */\n memories_found?: number;\n explanation?: QueryExplanation;\n usage?: QueryUsage;\n}\n\n/**\n * One frame yielded by {@link EngramClient.queryStream}.\n *\n * The shape is discriminated by `type`:\n * - `delta` frames carry an incremental piece of the answer in `content`.\n * - `done` carries the final usage + (optional) explanation. Emitted\n * exactly once at the end of the stream.\n */\nexport type QueryStreamEvent =\n | { type: 'delta'; content: string }\n | {\n type: 'done';\n usage?: QueryUsage;\n synthesis_usage?: unknown;\n explanation?: QueryExplanation;\n };\n\nexport interface QueryOptions {\n /**\n * Buckets to fuse across. Defaults to `['default']`.\n */\n buckets?: string[];\n /**\n * Maximum number of memories to retrieve. Defaults to 8.\n */\n topK?: number;\n /**\n * If true, server skips the synthesis LLM call and returns retrieval-only.\n * `answer` will be an empty string in that case. Defaults to false.\n */\n skipSynthesis?: boolean;\n /**\n * Whether to populate the `explanation` field. Defaults to true.\n */\n returnExplanation?: boolean;\n}\n\nexport interface ListMemoriesOptions {\n limit?: number;\n offset?: number;\n}\n\nexport interface ListMemoriesResult {\n memories: Memory[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport class EngramError extends Error {\n override readonly name = 'EngramError';\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n","import {\n EngramError,\n type Bucket,\n type ClearMemoriesResult,\n type EngramClientOptions,\n type ListMemoriesOptions,\n type ListMemoriesResult,\n type QueryOptions,\n type QueryResult,\n type QueryStreamEvent,\n type StoreMemoryResult,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.lumetra.io';\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_STREAM_TIMEOUT_MS = 300_000;\nconst DEFAULT_MAX_RETRIES_ON_429 = 3;\n// Cap on per-attempt backoff so a misconfigured server can't force\n// callers to sleep for minutes.\nconst RETRY_AFTER_CAP_MS = 30_000;\nconst SDK_VERSION = '0.3.2';\nconst USER_AGENT = `engram-js/${SDK_VERSION}`;\n\nfunction parseRetryAfterMs(header: string | null, defaultBackoffMs: number): number {\n if (header) {\n const value = Number(header.trim());\n if (Number.isFinite(value) && value >= 0) {\n return Math.min(value * 1000, RETRY_AFTER_CAP_MS);\n }\n }\n return Math.min(defaultBackoffMs, RETRY_AFTER_CAP_MS);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class EngramClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly streamTimeoutMs: number;\n private readonly maxRetriesOn429: number;\n\n constructor(options: EngramClientOptions = {}) {\n const apiKey =\n options.apiKey ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_API_KEY : undefined) ??\n '';\n if (!apiKey) {\n throw new Error(\n 'EngramClient: apiKey is required. Pass it explicitly or set ENGRAM_API_KEY in your environment.',\n );\n }\n const baseUrl =\n options.baseUrl ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_BASE_URL : undefined) ??\n DEFAULT_BASE_URL;\n\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.streamTimeoutMs = options.streamTimeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS;\n this.maxRetriesOn429 = Math.max(0, options.maxRetriesOn429 ?? DEFAULT_MAX_RETRIES_ON_429);\n\n if (typeof this.fetchImpl !== 'function') {\n throw new Error(\n 'EngramClient: no fetch implementation available. Use Node 18+, or pass options.fetch.',\n );\n }\n }\n\n private async request<T>(\n path: string,\n init: { method: string; body?: unknown; query?: Record<string, string | number | undefined> } = {\n method: 'GET',\n },\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (init.query) {\n for (const [k, v] of Object.entries(init.query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n // 429-aware retry. The Engram API enforces a per-tenant concurrent-\n // request cap and sets Retry-After on 429s; without retry handling,\n // bursty clients fail immediately under load. The body is JSON-\n // serialized once outside the loop so each attempt sends an\n // identical request.\n const requestBody = init.body !== undefined ? JSON.stringify(init.body) : undefined;\n let attemptsRemaining = this.maxRetriesOn429;\n let backoffMs = 1000;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let res: Response;\n try {\n res = await this.fetchImpl(url.toString(), {\n method: init.method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': USER_AGENT,\n },\n body: requestBody,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (res.status === 429 && attemptsRemaining > 0) {\n // Drain the body so the connection can return to the pool.\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n\n const text = await res.text();\n let parsed: unknown = undefined;\n if (text) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n\n if (!res.ok) {\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n\n return parsed as T;\n }\n }\n\n // ---------- Memories ----------\n\n async storeMemory(content: string, bucket: string = 'default'): Promise<StoreMemoryResult> {\n return this.request<StoreMemoryResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body: { content } },\n );\n }\n\n async storeMemories(\n contents: string[],\n bucket: string = 'default',\n ): Promise<{ memories: StoreMemoryResult[] }> {\n // Defensively unwrap: depending on server version the batch endpoint\n // returns either { memories: [...] } or a bare array. Normalize to the\n // wrapped shape so callers don't have to switch on it.\n const result = await this.request<{ memories: StoreMemoryResult[] } | StoreMemoryResult[]>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body: { memories: contents.map((content) => ({ content })) } },\n );\n return Array.isArray(result) ? { memories: result } : result;\n }\n\n async listMemories(\n bucket: string = 'default',\n options: ListMemoriesOptions = {},\n ): Promise<ListMemoriesResult> {\n return this.request<ListMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n {\n method: 'GET',\n query: { limit: options.limit ?? 20, offset: options.offset ?? 0 },\n },\n );\n }\n\n async deleteMemory(memoryId: string, bucket: string = 'default'): Promise<void> {\n await this.request<unknown>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories/${encodeURIComponent(memoryId)}`,\n { method: 'DELETE' },\n );\n }\n\n async clearMemories(bucket: string): Promise<ClearMemoriesResult> {\n const res = await this.request<ClearMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'DELETE' },\n );\n // Defensive default: older servers / proxies may strip the body. The\n // contract surface is {success, cleared_count}; if the server stayed\n // silent we still return the same shape so callers can rely on it.\n return res ?? { success: true, cleared_count: 0 };\n }\n\n // ---------- Query ----------\n\n async query(question: string, options: QueryOptions = {}): Promise<QueryResult> {\n const buckets = options.buckets ?? ['default'];\n return this.request<QueryResult>('/v1/query', {\n method: 'POST',\n body: {\n query: question,\n buckets,\n options: {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n },\n },\n });\n }\n\n /**\n * Streaming variant of {@link query}. Returns an async-iterable that\n * yields {@link QueryStreamEvent} frames as the server produces them:\n *\n * for await (const ev of engram.queryStream('...')) {\n * if (ev.type === 'delta') process.stdout.write(ev.content);\n * else if (ev.type === 'done') console.log(ev.usage);\n * }\n *\n * Break out of the loop to abort the request (the underlying\n * AbortController is wired up to the fetch call).\n */\n queryStream(question: string, options: QueryOptions = {}): AsyncIterable<QueryStreamEvent> {\n const buckets = options.buckets ?? ['default'];\n const body = {\n query: question,\n buckets,\n stream: true,\n options: {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n },\n };\n const url = `${this.baseUrl}/v1/query`;\n const apiKey = this.apiKey;\n const fetchImpl = this.fetchImpl;\n const timeoutMs = this.streamTimeoutMs;\n const maxRetriesOn429 = this.maxRetriesOn429;\n const bodyJson = JSON.stringify(body);\n\n return {\n [Symbol.asyncIterator]: () => {\n const controller = new AbortController();\n // The timeout caps total wall-clock for the stream. Set generously\n // because synthesis can run 10–25s before the first byte even with\n // streaming on slow paths; clamp to the user's configured timeout.\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let started = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n let done = false;\n const queue: QueryStreamEvent[] = [];\n let pendingError: unknown = null;\n\n const ensureStarted = async (): Promise<void> => {\n if (started) return;\n started = true;\n // 429-aware retry at the connection-open stage only. Once\n // the response body starts flowing we can't resume mid-\n // stream safely.\n let attemptsRemaining = maxRetriesOn429;\n let backoffMs = 1000;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n 'User-Agent': USER_AGENT,\n },\n body: bodyJson,\n signal: controller.signal,\n });\n if (res.status === 429 && attemptsRemaining > 0) {\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n let parsed: unknown = text;\n try {\n parsed = text ? JSON.parse(text) : text;\n } catch {\n /* keep raw text */\n }\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n if (!res.body) {\n throw new EngramError('Engram API: streaming response has no body', res.status, null);\n }\n reader = res.body.getReader();\n return;\n }\n };\n\n const drainBuffer = (): void => {\n // SSE frames are separated by a blank line ('\\n\\n'). Each frame\n // is one or more 'field: value' lines. We only care about\n // 'data:' lines for this stream.\n let idx: number;\n while ((idx = buffer.indexOf('\\n\\n')) !== -1) {\n const frame = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 2);\n const dataLines: string[] = [];\n for (const rawLine of frame.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine;\n if (line.startsWith('data: ')) {\n dataLines.push(line.slice(6));\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5));\n }\n }\n if (dataLines.length === 0) continue;\n const payloadStr = dataLines.join('\\n');\n if (payloadStr === '[DONE]') {\n done = true;\n return;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(payloadStr);\n } catch {\n // Malformed frame — skip rather than corrupt the stream.\n continue;\n }\n if (payload && typeof payload === 'object') {\n const obj = payload as Record<string, unknown>;\n if (obj.error) {\n pendingError = new EngramError(String(obj.error), 0, obj);\n done = true;\n return;\n }\n // OpenAI-style delta chunk\n const choices = obj.choices as Array<{ delta?: { content?: string } }> | undefined;\n if (Array.isArray(choices) && choices.length > 0) {\n const delta = choices[0]?.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n queue.push({ type: 'delta', content: delta });\n }\n continue;\n }\n // Final usage / explanation frame (no 'choices' key).\n queue.push({ type: 'done', ...(obj as Omit<Extract<QueryStreamEvent, { type: 'done' }>, 'type'>) });\n }\n }\n };\n\n return {\n next: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n try {\n await ensureStarted();\n } catch (err) {\n clearTimeout(timer);\n throw err;\n }\n while (queue.length === 0 && !done) {\n if (!reader) {\n clearTimeout(timer);\n throw new EngramError('Engram API: stream reader missing', 0, null);\n }\n const chunk = await reader.read();\n if (chunk.done) {\n // Flush whatever's left in the buffer (some servers don't\n // terminate with a trailing blank line).\n if (buffer.length > 0) {\n buffer += '\\n\\n';\n drainBuffer();\n }\n done = true;\n break;\n }\n buffer += decoder.decode(chunk.value, { stream: true });\n drainBuffer();\n }\n if (queue.length > 0) {\n return { value: queue.shift() as QueryStreamEvent, done: false };\n }\n clearTimeout(timer);\n if (pendingError) throw pendingError;\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n return: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n // Caller broke out of the for-await loop — cancel the upstream\n // request so we're not holding the connection open.\n clearTimeout(timer);\n controller.abort();\n try {\n await reader?.cancel();\n } catch {\n /* ignored */\n }\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n };\n },\n };\n }\n\n // ---------- Buckets ----------\n\n async listBuckets(): Promise<Bucket[]> {\n const result = await this.request<{ buckets: Bucket[] } | Bucket[]>(`/v1/buckets`, {\n method: 'GET',\n });\n return Array.isArray(result) ? result : result.buckets;\n }\n\n async createBucket(name: string, description?: string): Promise<Bucket> {\n return this.request<Bucket>('/v1/buckets', {\n method: 'POST',\n body: { name, description },\n });\n }\n\n async deleteBucket(bucket: string): Promise<void> {\n await this.request<unknown>(`/v1/buckets/${encodeURIComponent(bucket)}`, {\n method: 'DELETE',\n });\n }\n\n // ---------- Profile ----------\n\n async getProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile`,\n { method: 'GET' },\n );\n }\n\n async regenerateProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile/regenerate`,\n { method: 'POST' },\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/client.ts"],"names":[],"mappings":";AAgOO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAA,EACnB,IAAA,GAAO,aAAA;AAAA,EAChB,MAAA;AAAA,EACA,IAAA;AAAA,EAET,WAAA,CAAY,OAAA,EAAiB,MAAA,EAAgB,IAAA,EAAe;AAC1D,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;;;AC5NA,IAAM,gBAAA,GAAmB,wBAAA;AACzB,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,yBAAA,GAA4B,GAAA;AAClC,IAAM,0BAAA,GAA6B,CAAA;AAGnC,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,WAAA,GAAc,OAAA;AACpB,IAAM,UAAA,GAAa,aAAa,WAAW,CAAA,CAAA;AAE3C,SAAS,iBAAA,CAAkB,QAAuB,gBAAA,EAAkC;AAClF,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,CAAA;AAClC,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACxC,MAAA,OAAO,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,GAAA,EAAM,kBAAkB,CAAA;AAAA,IAClD;AAAA,EACF;AACA,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,gBAAA,EAAkB,kBAAkB,CAAA;AACtD;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAEO,IAAM,eAAN,MAAmB;AAAA,EACP,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EAEjB,WAAA,CAAY,OAAA,GAA+B,EAAC,EAAG;AAC7C,IAAA,MAAM,MAAA,GACJ,QAAQ,MAAA,KACP,OAAO,YAAY,WAAA,GAAc,OAAA,CAAQ,GAAA,EAAK,cAAA,GAAiB,MAAA,CAAA,IAChE,EAAA;AACF,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,MAAM,OAAA,GACJ,QAAQ,OAAA,KACP,OAAO,YAAY,WAAA,GAAc,OAAA,CAAQ,GAAA,EAAK,eAAA,GAAkB,MAAA,CAAA,IACjE,gBAAA;AAEF,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACzC,IAAA,IAAA,CAAK,SAAA,GAAY,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AAC7C,IAAA,IAAA,CAAK,SAAA,GAAY,QAAQ,SAAA,IAAa,kBAAA;AACtC,IAAA,IAAA,CAAK,eAAA,GAAkB,QAAQ,eAAA,IAAmB,yBAAA;AAClD,IAAA,IAAA,CAAK,kBAAkB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,CAAQ,mBAAmB,0BAA0B,CAAA;AAExF,IAAA,IAAI,OAAO,IAAA,CAAK,SAAA,KAAc,UAAA,EAAY;AACxC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,OAAA,CACZ,IAAA,EACA,IAAA,GAAgG;AAAA,IAC9F,MAAA,EAAQ;AAAA,GACV,EACY;AACZ,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,CAAA,EAAG,KAAK,OAAO,CAAA,EAAG,IAAI,CAAA,CAAE,CAAA;AAC5C,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,KAAA,MAAW,CAAC,GAAG,CAAC,CAAA,IAAK,OAAO,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA,EAAG;AAC/C,QAAA,IAAI,CAAA,KAAM,QAAW,GAAA,CAAI,YAAA,CAAa,IAAI,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,MACxD;AAAA,IACF;AAOA,IAAA,MAAM,WAAA,GAAc,KAAK,IAAA,KAAS,MAAA,GAAY,KAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AAC1E,IAAA,IAAI,oBAAoB,IAAA,CAAK,eAAA;AAC7B,IAAA,IAAI,SAAA,GAAY,GAAA;AAGhB,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,SAAS,CAAA;AAEjE,MAAA,IAAI,GAAA;AACJ,MAAA,IAAI;AACF,QAAA,GAAA,GAAM,MAAM,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,UAAS,EAAG;AAAA,UACzC,QAAQ,IAAA,CAAK,MAAA;AAAA,UACb,OAAA,EAAS;AAAA,YACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,YACpC,cAAA,EAAgB,kBAAA;AAAA,YAChB,YAAA,EAAc;AAAA,WAChB;AAAA,UACA,IAAA,EAAM,WAAA;AAAA,UACN,QAAQ,UAAA,CAAW;AAAA,SACpB,CAAA;AAAA,MACH,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAEA,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,IAAO,iBAAA,GAAoB,CAAA,EAAG;AAE/C,QAAA,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACtC,QAAA,MAAM,QAAQ,iBAAA,CAAkB,GAAA,CAAI,QAAQ,GAAA,CAAI,aAAa,GAAG,SAAS,CAAA;AACzE,QAAA,MAAM,MAAM,KAAK,CAAA;AACjB,QAAA,iBAAA,IAAqB,CAAA;AACrB,QAAA,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,SAAA,GAAY,CAAA,EAAG,kBAAkB,CAAA;AACtD,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,MAAA,IAAI,MAAA,GAAkB,MAAA;AACtB,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,IAAI;AACF,UAAA,MAAA,GAAS,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,QAC1B,CAAA,CAAA,MAAQ;AACN,UAAA,MAAA,GAAS,IAAA;AAAA,QACX;AAAA,MACF;AAEA,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,QAAA,MAAM,MAAA,GACJ,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,WAAW,IAAA,IAAQ,OAAA,IAAW,MAAA,GACjE,MAAA,CAA8B,KAAA,GAC/B,MAAA;AACN,QAAA,MAAM,IAAI,WAAA;AAAA,UACR,CAAA,WAAA,EAAc,GAAA,CAAI,MAAM,CAAA,EAAA,EAAK,OAAO,MAAA,KAAW,QAAA,GAAW,MAAA,GAAS,IAAA,CAAK,SAAA,CAAU,MAAA,IAAU,EAAE,CAAC,CAAA,CAAA;AAAA,UAC/F,GAAA,CAAI,MAAA;AAAA,UACJ;AAAA,SACF;AAAA,MACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,WAAA,CACJ,OAAA,EACA,SAAiB,SAAA,EACjB,OAAA,GAAmC,EAAC,EACR;AAC5B,IAAA,MAAM,IAAA,GAAgC,EAAE,OAAA,EAAQ;AAChD,IAAA,IAAI,OAAA,CAAQ,KAAA,KAAU,MAAA,EAAW,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AACtD,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA;AAAK,KACzB;AAAA,EACF;AAAA,EAEA,MAAM,aAAA,CACJ,QAAA,EACA,MAAA,GAAiB,SAAA,EAC2B;AAI5C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA;AAAA,MACxB,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,EAAE,QAAA,EAAU,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,MAAa,EAAE,OAAA,EAAQ,CAAE,GAAE;AAAE,KACjF;AACA,IAAA,OAAO,MAAM,OAAA,CAAQ,MAAM,IAAI,EAAE,QAAA,EAAU,QAAO,GAAI,MAAA;AAAA,EACxD;AAAA,EAEA,MAAM,YAAA,CACJ,MAAA,GAAiB,SAAA,EACjB,OAAA,GAA+B,EAAC,EACH;AAC7B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,KAAA,EAAO,EAAE,KAAA,EAAO,OAAA,CAAQ,SAAS,EAAA,EAAI,MAAA,EAAQ,OAAA,CAAQ,MAAA,IAAU,CAAA;AAAE;AACnE,KACF;AAAA,EACF;AAAA,EAEA,MAAM,YAAA,CAAa,QAAA,EAAkB,MAAA,GAAiB,SAAA,EAA0B;AAC9E,IAAA,MAAM,IAAA,CAAK,OAAA;AAAA,MACT,eAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,UAAA,EAAa,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAA;AAAA,MAClF,EAAE,QAAQ,QAAA;AAAS,KACrB;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,MAAA,EAA8C;AAChE,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA;AAAA,MACrB,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,SAAA,CAAA;AAAA,MACzC,EAAE,QAAQ,QAAA;AAAS,KACrB;AAIA,IAAA,OAAO,GAAA,IAAO,EAAE,OAAA,EAAS,IAAA,EAAM,eAAe,CAAA,EAAE;AAAA,EAClD;AAAA;AAAA,EAIA,MAAM,KAAA,CAAM,QAAA,EAAkB,OAAA,GAAwB,EAAC,EAAyB;AAC9E,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,CAAC,SAAS,CAAA;AAC7C,IAAA,MAAM,IAAA,GAAgC;AAAA,MACpC,KAAA,EAAO,QAAQ,IAAA,IAAQ,CAAA;AAAA,MACvB,kBAAA,EAAoB,QAAQ,iBAAA,IAAqB,IAAA;AAAA,MACjD,cAAA,EAAgB,QAAQ,aAAA,IAAiB;AAAA,KAC3C;AACA,IAAA,IAAI,OAAA,CAAQ,SAAA,KAAc,MAAA,EAAW,IAAA,CAAK,aAAa,OAAA,CAAQ,SAAA;AAC/D,IAAA,IAAI,OAAA,CAAQ,2BAA2B,MAAA,EAAW;AAChD,MAAA,IAAA,CAAK,2BAA2B,OAAA,CAAQ,sBAAA;AAAA,IAC1C;AACA,IAAA,IAAI,OAAA,CAAQ,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,mBAAmB,OAAA,CAAQ,aAAA;AACzE,IAAA,IAAI,OAAA,CAAQ,YAAA,KAAiB,MAAA,EAAW,IAAA,CAAK,gBAAgB,OAAA,CAAQ,YAAA;AACrE,IAAA,IAAI,OAAA,CAAQ,cAAA,KAAmB,MAAA,EAAW,IAAA,CAAK,kBAAkB,OAAA,CAAQ,cAAA;AACzE,IAAA,OAAO,IAAA,CAAK,QAAqB,WAAA,EAAa;AAAA,MAC5C,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,KAAA,EAAO,QAAA;AAAA,QACP,OAAA;AAAA,QACA,OAAA,EAAS;AAAA;AACX,KACD,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,WAAA,CAAY,QAAA,EAAkB,OAAA,GAAwB,EAAC,EAAoC;AACzF,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,CAAC,SAAS,CAAA;AAC7C,IAAA,MAAM,IAAA,GAAgC;AAAA,MACpC,KAAA,EAAO,QAAQ,IAAA,IAAQ,CAAA;AAAA,MACvB,kBAAA,EAAoB,QAAQ,iBAAA,IAAqB,IAAA;AAAA,MACjD,cAAA,EAAgB,QAAQ,aAAA,IAAiB;AAAA,KAC3C;AACA,IAAA,IAAI,OAAA,CAAQ,SAAA,KAAc,MAAA,EAAW,IAAA,CAAK,aAAa,OAAA,CAAQ,SAAA;AAC/D,IAAA,IAAI,OAAA,CAAQ,2BAA2B,MAAA,EAAW;AAChD,MAAA,IAAA,CAAK,2BAA2B,OAAA,CAAQ,sBAAA;AAAA,IAC1C;AACA,IAAA,IAAI,OAAA,CAAQ,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,mBAAmB,OAAA,CAAQ,aAAA;AACzE,IAAA,IAAI,OAAA,CAAQ,YAAA,KAAiB,MAAA,EAAW,IAAA,CAAK,gBAAgB,OAAA,CAAQ,YAAA;AACrE,IAAA,IAAI,OAAA,CAAQ,cAAA,KAAmB,MAAA,EAAW,IAAA,CAAK,kBAAkB,OAAA,CAAQ,cAAA;AACzE,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,KAAA,EAAO,QAAA;AAAA,MACP,OAAA;AAAA,MACA,MAAA,EAAQ,IAAA;AAAA,MACR,OAAA,EAAS;AAAA,KACX;AACA,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,SAAA,CAAA;AAC3B,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,IAAA,MAAM,YAAY,IAAA,CAAK,SAAA;AACvB,IAAA,MAAM,YAAY,IAAA,CAAK,eAAA;AACvB,IAAA,MAAM,kBAAkB,IAAA,CAAK,eAAA;AAC7B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAEpC,IAAA,OAAO;AAAA,MACL,CAAC,MAAA,CAAO,aAAa,GAAG,MAAM;AAC5B,QAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AAIvC,QAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,SAAS,CAAA;AAE5D,QAAA,IAAI,OAAA,GAAU,KAAA;AACd,QAAA,IAAI,MAAA,GAAyD,IAAA;AAC7D,QAAA,MAAM,OAAA,GAAU,IAAI,WAAA,CAAY,OAAO,CAAA;AACvC,QAAA,IAAI,MAAA,GAAS,EAAA;AACb,QAAA,IAAI,IAAA,GAAO,KAAA;AACX,QAAA,MAAM,QAA4B,EAAC;AACnC,QAAA,IAAI,YAAA,GAAwB,IAAA;AAE5B,QAAA,MAAM,gBAAgB,YAA2B;AAC/C,UAAA,IAAI,OAAA,EAAS;AACb,UAAA,OAAA,GAAU,IAAA;AAIV,UAAA,IAAI,iBAAA,GAAoB,eAAA;AACxB,UAAA,IAAI,SAAA,GAAY,GAAA;AAEhB,UAAA,OAAO,IAAA,EAAM;AACX,YAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,GAAA,EAAK;AAAA,cAC/B,MAAA,EAAQ,MAAA;AAAA,cACR,OAAA,EAAS;AAAA,gBACP,aAAA,EAAe,UAAU,MAAM,CAAA,CAAA;AAAA,gBAC/B,cAAA,EAAgB,kBAAA;AAAA,gBAChB,MAAA,EAAQ,mBAAA;AAAA,gBACR,YAAA,EAAc;AAAA,eAChB;AAAA,cACA,IAAA,EAAM,QAAA;AAAA,cACN,QAAQ,UAAA,CAAW;AAAA,aACpB,CAAA;AACD,YAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,IAAO,iBAAA,GAAoB,CAAA,EAAG;AAC/C,cAAA,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACtC,cAAA,MAAM,QAAQ,iBAAA,CAAkB,GAAA,CAAI,QAAQ,GAAA,CAAI,aAAa,GAAG,SAAS,CAAA;AACzE,cAAA,MAAM,MAAM,KAAK,CAAA;AACjB,cAAA,iBAAA,IAAqB,CAAA;AACrB,cAAA,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,SAAA,GAAY,CAAA,EAAG,kBAAkB,CAAA;AACtD,cAAA;AAAA,YACF;AACA,YAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,cAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAC5C,cAAA,IAAI,MAAA,GAAkB,IAAA;AACtB,cAAA,IAAI;AACF,gBAAA,MAAA,GAAS,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,IAAA;AAAA,cACrC,CAAA,CAAA,MAAQ;AAAA,cAER;AACA,cAAA,MAAM,MAAA,GACJ,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,WAAW,IAAA,IAAQ,OAAA,IAAW,MAAA,GACjE,MAAA,CAA8B,KAAA,GAC/B,MAAA;AACN,cAAA,MAAM,IAAI,WAAA;AAAA,gBACR,CAAA,WAAA,EAAc,GAAA,CAAI,MAAM,CAAA,EAAA,EAAK,OAAO,MAAA,KAAW,QAAA,GAAW,MAAA,GAAS,IAAA,CAAK,SAAA,CAAU,MAAA,IAAU,EAAE,CAAC,CAAA,CAAA;AAAA,gBAC/F,GAAA,CAAI,MAAA;AAAA,gBACJ;AAAA,eACF;AAAA,YACF;AACA,YAAA,IAAI,CAAC,IAAI,IAAA,EAAM;AACb,cAAA,MAAM,IAAI,WAAA,CAAY,4CAAA,EAA8C,GAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,YACtF;AACA,YAAA,MAAA,GAAS,GAAA,CAAI,KAAK,SAAA,EAAU;AAC5B,YAAA;AAAA,UACF;AAAA,QACF,CAAA;AAEA,QAAA,MAAM,cAAc,MAAY;AAI9B,UAAA,IAAI,GAAA;AACJ,UAAA,OAAA,CAAQ,GAAA,GAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,OAAO,EAAA,EAAI;AAC5C,YAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AACjC,YAAA,MAAA,GAAS,MAAA,CAAO,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA;AAC7B,YAAA,MAAM,YAAsB,EAAC;AAC7B,YAAA,KAAA,MAAW,OAAA,IAAW,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA,EAAG;AACvC,cAAA,MAAM,IAAA,GAAO,QAAQ,QAAA,CAAS,IAAI,IAAI,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,OAAA;AAC7D,cAAA,IAAI,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC7B,gBAAA,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,cAC9B,CAAA,MAAA,IAAW,IAAA,CAAK,UAAA,CAAW,OAAO,CAAA,EAAG;AACnC,gBAAA,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,cAC9B;AAAA,YACF;AACA,YAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC5B,YAAA,MAAM,UAAA,GAAa,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AACtC,YAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,cAAA,IAAA,GAAO,IAAA;AACP,cAAA;AAAA,YACF;AACA,YAAA,IAAI,OAAA;AACJ,YAAA,IAAI;AACF,cAAA,OAAA,GAAU,IAAA,CAAK,MAAM,UAAU,CAAA;AAAA,YACjC,CAAA,CAAA,MAAQ;AAEN,cAAA;AAAA,YACF;AACA,YAAA,IAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC1C,cAAA,MAAM,GAAA,GAAM,OAAA;AACZ,cAAA,IAAI,IAAI,KAAA,EAAO;AACb,gBAAA,YAAA,GAAe,IAAI,WAAA,CAAY,MAAA,CAAO,IAAI,KAAK,CAAA,EAAG,GAAG,GAAG,CAAA;AACxD,gBAAA,IAAA,GAAO,IAAA;AACP,gBAAA;AAAA,cACF;AAEA,cAAA,MAAM,UAAU,GAAA,CAAI,OAAA;AACpB,cAAA,IAAI,MAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,OAAA,CAAQ,SAAS,CAAA,EAAG;AAChD,gBAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,CAAC,CAAA,EAAG,KAAA,EAAO,OAAA;AACjC,gBAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,SAAS,CAAA,EAAG;AACjD,kBAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,OAAO,CAAA;AAAA,gBAC9C;AACA,gBAAA;AAAA,cACF;AAEA,cAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,MAAA,EAAQ,GAAI,KAAmE,CAAA;AAAA,YACpG;AAAA,UACF;AAAA,QACF,CAAA;AAEA,QAAA,OAAO;AAAA,UACL,MAAM,YAAuD;AAC3D,YAAA,IAAI;AACF,cAAA,MAAM,aAAA,EAAc;AAAA,YACtB,SAAS,GAAA,EAAK;AACZ,cAAA,YAAA,CAAa,KAAK,CAAA;AAClB,cAAA,MAAM,GAAA;AAAA,YACR;AACA,YAAA,OAAO,KAAA,CAAM,MAAA,KAAW,CAAA,IAAK,CAAC,IAAA,EAAM;AAClC,cAAA,IAAI,CAAC,MAAA,EAAQ;AACX,gBAAA,YAAA,CAAa,KAAK,CAAA;AAClB,gBAAA,MAAM,IAAI,WAAA,CAAY,mCAAA,EAAqC,CAAA,EAAG,IAAI,CAAA;AAAA,cACpE;AACA,cAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,IAAA,EAAK;AAChC,cAAA,IAAI,MAAM,IAAA,EAAM;AAGd,gBAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,kBAAA,MAAA,IAAU,MAAA;AACV,kBAAA,WAAA,EAAY;AAAA,gBACd;AACA,gBAAA,IAAA,GAAO,IAAA;AACP,gBAAA;AAAA,cACF;AACA,cAAA,MAAA,IAAU,QAAQ,MAAA,CAAO,KAAA,CAAM,OAAO,EAAE,MAAA,EAAQ,MAAM,CAAA;AACtD,cAAA,WAAA,EAAY;AAAA,YACd;AACA,YAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,cAAA,OAAO,EAAE,KAAA,EAAO,KAAA,CAAM,KAAA,EAAM,EAAuB,MAAM,KAAA,EAAM;AAAA,YACjE;AACA,YAAA,YAAA,CAAa,KAAK,CAAA;AAClB,YAAA,IAAI,cAAc,MAAM,YAAA;AACxB,YAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAA0C,IAAA,EAAM,IAAA,EAAK;AAAA,UACvE,CAAA;AAAA,UACA,QAAQ,YAAuD;AAG7D,YAAA,YAAA,CAAa,KAAK,CAAA;AAClB,YAAA,UAAA,CAAW,KAAA,EAAM;AACjB,YAAA,IAAI;AACF,cAAA,MAAM,QAAQ,MAAA,EAAO;AAAA,YACvB,CAAA,CAAA,MAAQ;AAAA,YAER;AACA,YAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAA0C,IAAA,EAAM,IAAA,EAAK;AAAA,UACvE;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,WAAA,GAAiC;AACrC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,CAA0C,CAAA,WAAA,CAAA,EAAe;AAAA,MACjF,MAAA,EAAQ;AAAA,KACT,CAAA;AACD,IAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,GAAI,SAAS,MAAA,CAAO,OAAA;AAAA,EACjD;AAAA,EAEA,MAAM,YAAA,CAAa,IAAA,EAAc,WAAA,EAAuC;AACtE,IAAA,OAAO,IAAA,CAAK,QAAgB,aAAA,EAAe;AAAA,MACzC,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,EAAE,IAAA,EAAM,WAAA;AAAY,KAC3B,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,MAAA,EAA+B;AAChD,IAAA,MAAM,KAAK,OAAA,CAAiB,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,CAAA,EAAI;AAAA,MACvE,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH;AAAA;AAAA,EAIA,MAAM,UAAA,CAAW,MAAA,GAAiB,SAAA,EAAgD;AAChF,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,QAAA,CAAA;AAAA,MACzC,EAAE,QAAQ,KAAA;AAAM,KAClB;AAAA,EACF;AAAA,EAEA,MAAM,iBAAA,CAAkB,MAAA,GAAiB,SAAA,EAAgD;AACvF,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,CAAA,YAAA,EAAe,kBAAA,CAAmB,MAAM,CAAC,CAAA,mBAAA,CAAA;AAAA,MACzC,EAAE,QAAQ,MAAA;AAAO,KACnB;AAAA,EACF;AACF","file":"index.js","sourcesContent":["export interface EngramClientOptions {\n /**\n * Engram API key. Looks like `eng_live_...`. Defaults to `process.env.ENGRAM_API_KEY`.\n */\n apiKey?: string;\n /**\n * API base URL. Defaults to `process.env.ENGRAM_BASE_URL` or `https://api.lumetra.io`.\n */\n baseUrl?: string;\n /**\n * Custom fetch implementation. Defaults to the global `fetch`.\n * Useful for proxying, retry middleware, or non-Node runtimes.\n */\n fetch?: typeof fetch;\n /**\n * Request timeout in milliseconds for buffered (non-streaming) calls.\n * Defaults to 30000 (30s).\n */\n timeoutMs?: number;\n /**\n * Timeout in milliseconds for `queryStream` calls. Streaming responses\n * can sit in the prep phase (retrieval + extractor pass) for 5–15s\n * before the first synthesis token arrives, so the buffered 30s\n * default would leave no headroom for the streamed body. Defaults to\n * 300000 (5 min). Use a higher value for very large synthesis bodies.\n */\n streamTimeoutMs?: number;\n /**\n * How many times to retry on a 429 (per-tenant concurrent-request cap).\n * Honors the server's `Retry-After` header, capped at 30s per sleep.\n * Defaults to 3. Set to 0 to disable retry and surface 429 as `EngramError`\n * on the first attempt.\n */\n maxRetriesOn429?: number;\n}\n\nexport interface Bucket {\n id: string;\n name: string;\n /**\n * Mirror of `name`. The server emits both so callers iterating both\n * `listBuckets` and `storeMemory` responses can use one field name.\n * Prefer `name` in new code.\n */\n bucket_name?: string;\n description?: string | null;\n created_at: string;\n memory_count?: number;\n}\n\nexport interface Memory {\n id: string;\n content: string;\n bucket_name?: string;\n created_at?: string;\n token_count?: number;\n}\n\nexport type StoreStatus = 'stored' | 'merged';\n\nexport type MergeReason =\n | 'content_hash'\n | 'embedding_similarity'\n | 'conflict_keep_existing'\n | 'concurrent_insert_race';\n\nexport interface StoreMemoryResult {\n id: string;\n /**\n * Alias for `id` — older API docs / older SDKs referenced this name.\n * Always present; prefer `id` in new code.\n */\n memory_id?: string;\n bucket_name: string;\n token_count: number;\n /**\n * `\"stored\"` for fresh writes, `\"merged\"` when the server collapsed\n * this write into a pre-existing memory via dedup. Always present.\n */\n status?: StoreStatus;\n /** Present only when `status === \"merged\"`. ID of the canonical memory the write was absorbed into. */\n deduped_into?: string;\n /** Present only when `status === \"merged\"`. Similarity score in [0.0, 1.0] (1.0 for content-hash matches). */\n similarity_score?: number;\n /** Present only when `status === \"merged\"`. Reason for the merge. */\n merge_reason?: MergeReason;\n}\n\n/**\n * Dedup policy passed to `storeMemory`. The server's default (currently\n * `\"loose\"`) applies when omitted.\n *\n * - `\"off\"` — store every write as a new memory; useful for templated\n * time-series ingest where structurally similar rows carry unique\n * values and would otherwise collapse.\n * - `\"loose\"` — merge writes at similarity ≥ 0.95 (default).\n * - `\"strict\"` — only merge near-identical content (≥ 0.99).\n */\nexport type DedupPolicy = 'off' | 'loose' | 'strict';\n\nexport interface ClearMemoriesResult {\n success: boolean;\n /** Number of memories actually deleted (server-reported). */\n cleared_count: number;\n}\n\nexport interface RetrievedMemory {\n id?: string;\n content: string;\n score?: number;\n bucket?: string;\n}\n\nexport interface QueryExplanation {\n retrieved_memories?: RetrievedMemory[];\n profile?: string | null;\n graph_facts?: string[];\n}\n\nexport interface QueryUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n}\n\nexport interface QueryResult {\n answer: string;\n /**\n * Parsed JSON when the request set `returnFormat: 'json'`. Present\n * only on JSON queries — the parsed value (object / array / scalar)\n * on success, or `null` when the model returned malformed JSON.\n */\n answer_json?: unknown;\n /**\n * Top-level count of retrieved memories. Equivalent to\n * `result.explanation?.retrieved_memories.length` but present even\n * when `returnExplanation` is false.\n */\n memories_found?: number;\n explanation?: QueryExplanation;\n usage?: QueryUsage;\n}\n\n/**\n * One frame yielded by {@link EngramClient.queryStream}.\n *\n * The shape is discriminated by `type`:\n * - `delta` frames carry an incremental piece of the answer in `content`.\n * - `done` carries the final usage + (optional) explanation. Emitted\n * exactly once at the end of the stream.\n */\nexport type QueryStreamEvent =\n | { type: 'delta'; content: string }\n | {\n type: 'done';\n usage?: QueryUsage;\n synthesis_usage?: unknown;\n explanation?: QueryExplanation;\n };\n\nexport interface QueryOptions {\n /**\n * Buckets to fuse across. Defaults to `['default']`.\n */\n buckets?: string[];\n /**\n * Maximum number of memories to retrieve per bucket. Defaults to 8.\n * Used as the fallback K when `topKPerBucket` doesn't cover a bucket.\n */\n topK?: number;\n /**\n * If true, server skips the synthesis LLM call and returns retrieval-only.\n * `answer` will be an empty string in that case. Defaults to false.\n */\n skipSynthesis?: boolean;\n /**\n * Whether to populate the `explanation` field. Defaults to true.\n */\n returnExplanation?: boolean;\n /**\n * Cap synthesis output tokens. Default is the server's (currently\n * 8192). Lower for agent loops or cost control.\n */\n maxTokens?: number;\n /**\n * Floor for retrieval scores. When set, drops retrieved chunks\n * below this raw cosine similarity — useful for citations-grade\n * output where every chunk should actually match.\n */\n minSimilarityThreshold?: number;\n /**\n * Per-bucket retrieval depth. `number` for a uniform value across\n * all buckets; an object for explicit per-bucket K (e.g.\n * `{ edgar_AAPL: 20, prices_AAPL: 4 }`). Missing buckets fall back\n * to `topK`. Lets callers express \"deep retrieval on this one,\n * shallow on the others.\"\n */\n topKPerBucket?: number | Record<string, number>;\n /**\n * `'prose'` (default) or `'json'`. When `'json'`, the server asks\n * the synthesizer for JSON output and returns the parsed value\n * under `result.answer_json` alongside the raw `result.answer`.\n */\n returnFormat?: 'prose' | 'json';\n /**\n * Optional JSON Schema describing the desired output shape. Included\n * in the prompt to guide the model. Best-effort — validate\n * client-side if you need strict enforcement.\n */\n responseSchema?: Record<string, unknown>;\n}\n\nexport interface ListMemoriesOptions {\n limit?: number;\n offset?: number;\n}\n\nexport interface ListMemoriesResult {\n memories: Memory[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport class EngramError extends Error {\n override readonly name = 'EngramError';\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n","import {\n EngramError,\n type Bucket,\n type ClearMemoriesResult,\n type DedupPolicy,\n type EngramClientOptions,\n type ListMemoriesOptions,\n type ListMemoriesResult,\n type QueryOptions,\n type QueryResult,\n type QueryStreamEvent,\n type StoreMemoryResult,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.lumetra.io';\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_STREAM_TIMEOUT_MS = 300_000;\nconst DEFAULT_MAX_RETRIES_ON_429 = 3;\n// Cap on per-attempt backoff so a misconfigured server can't force\n// callers to sleep for minutes.\nconst RETRY_AFTER_CAP_MS = 30_000;\nconst SDK_VERSION = '0.5.0';\nconst USER_AGENT = `engram-js/${SDK_VERSION}`;\n\nfunction parseRetryAfterMs(header: string | null, defaultBackoffMs: number): number {\n if (header) {\n const value = Number(header.trim());\n if (Number.isFinite(value) && value >= 0) {\n return Math.min(value * 1000, RETRY_AFTER_CAP_MS);\n }\n }\n return Math.min(defaultBackoffMs, RETRY_AFTER_CAP_MS);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class EngramClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly streamTimeoutMs: number;\n private readonly maxRetriesOn429: number;\n\n constructor(options: EngramClientOptions = {}) {\n const apiKey =\n options.apiKey ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_API_KEY : undefined) ??\n '';\n if (!apiKey) {\n throw new Error(\n 'EngramClient: apiKey is required. Pass it explicitly or set ENGRAM_API_KEY in your environment.',\n );\n }\n const baseUrl =\n options.baseUrl ??\n (typeof process !== 'undefined' ? process.env?.ENGRAM_BASE_URL : undefined) ??\n DEFAULT_BASE_URL;\n\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.streamTimeoutMs = options.streamTimeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS;\n this.maxRetriesOn429 = Math.max(0, options.maxRetriesOn429 ?? DEFAULT_MAX_RETRIES_ON_429);\n\n if (typeof this.fetchImpl !== 'function') {\n throw new Error(\n 'EngramClient: no fetch implementation available. Use Node 18+, or pass options.fetch.',\n );\n }\n }\n\n private async request<T>(\n path: string,\n init: { method: string; body?: unknown; query?: Record<string, string | number | undefined> } = {\n method: 'GET',\n },\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (init.query) {\n for (const [k, v] of Object.entries(init.query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n // 429-aware retry. The Engram API enforces a per-tenant concurrent-\n // request cap and sets Retry-After on 429s; without retry handling,\n // bursty clients fail immediately under load. The body is JSON-\n // serialized once outside the loop so each attempt sends an\n // identical request.\n const requestBody = init.body !== undefined ? JSON.stringify(init.body) : undefined;\n let attemptsRemaining = this.maxRetriesOn429;\n let backoffMs = 1000;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let res: Response;\n try {\n res = await this.fetchImpl(url.toString(), {\n method: init.method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': USER_AGENT,\n },\n body: requestBody,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (res.status === 429 && attemptsRemaining > 0) {\n // Drain the body so the connection can return to the pool.\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n\n const text = await res.text();\n let parsed: unknown = undefined;\n if (text) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n\n if (!res.ok) {\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n\n return parsed as T;\n }\n }\n\n // ---------- Memories ----------\n\n async storeMemory(\n content: string,\n bucket: string = 'default',\n options: { dedup?: DedupPolicy } = {},\n ): Promise<StoreMemoryResult> {\n const body: Record<string, unknown> = { content };\n if (options.dedup !== undefined) body.dedup = options.dedup;\n return this.request<StoreMemoryResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body },\n );\n }\n\n async storeMemories(\n contents: string[],\n bucket: string = 'default',\n ): Promise<{ memories: StoreMemoryResult[] }> {\n // Defensively unwrap: depending on server version the batch endpoint\n // returns either { memories: [...] } or a bare array. Normalize to the\n // wrapped shape so callers don't have to switch on it.\n const result = await this.request<{ memories: StoreMemoryResult[] } | StoreMemoryResult[]>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'POST', body: { memories: contents.map((content) => ({ content })) } },\n );\n return Array.isArray(result) ? { memories: result } : result;\n }\n\n async listMemories(\n bucket: string = 'default',\n options: ListMemoriesOptions = {},\n ): Promise<ListMemoriesResult> {\n return this.request<ListMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n {\n method: 'GET',\n query: { limit: options.limit ?? 20, offset: options.offset ?? 0 },\n },\n );\n }\n\n async deleteMemory(memoryId: string, bucket: string = 'default'): Promise<void> {\n await this.request<unknown>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories/${encodeURIComponent(memoryId)}`,\n { method: 'DELETE' },\n );\n }\n\n async clearMemories(bucket: string): Promise<ClearMemoriesResult> {\n const res = await this.request<ClearMemoriesResult>(\n `/v1/buckets/${encodeURIComponent(bucket)}/memories`,\n { method: 'DELETE' },\n );\n // Defensive default: older servers / proxies may strip the body. The\n // contract surface is {success, cleared_count}; if the server stayed\n // silent we still return the same shape so callers can rely on it.\n return res ?? { success: true, cleared_count: 0 };\n }\n\n // ---------- Query ----------\n\n async query(question: string, options: QueryOptions = {}): Promise<QueryResult> {\n const buckets = options.buckets ?? ['default'];\n const opts: Record<string, unknown> = {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n };\n if (options.maxTokens !== undefined) opts.max_tokens = options.maxTokens;\n if (options.minSimilarityThreshold !== undefined) {\n opts.min_similarity_threshold = options.minSimilarityThreshold;\n }\n if (options.topKPerBucket !== undefined) opts.top_k_per_bucket = options.topKPerBucket;\n if (options.returnFormat !== undefined) opts.return_format = options.returnFormat;\n if (options.responseSchema !== undefined) opts.response_schema = options.responseSchema;\n return this.request<QueryResult>('/v1/query', {\n method: 'POST',\n body: {\n query: question,\n buckets,\n options: opts,\n },\n });\n }\n\n /**\n * Streaming variant of {@link query}. Returns an async-iterable that\n * yields {@link QueryStreamEvent} frames as the server produces them:\n *\n * for await (const ev of engram.queryStream('...')) {\n * if (ev.type === 'delta') process.stdout.write(ev.content);\n * else if (ev.type === 'done') console.log(ev.usage);\n * }\n *\n * Break out of the loop to abort the request (the underlying\n * AbortController is wired up to the fetch call).\n */\n queryStream(question: string, options: QueryOptions = {}): AsyncIterable<QueryStreamEvent> {\n const buckets = options.buckets ?? ['default'];\n const opts: Record<string, unknown> = {\n top_k: options.topK ?? 8,\n return_explanation: options.returnExplanation ?? true,\n skip_synthesis: options.skipSynthesis ?? false,\n };\n if (options.maxTokens !== undefined) opts.max_tokens = options.maxTokens;\n if (options.minSimilarityThreshold !== undefined) {\n opts.min_similarity_threshold = options.minSimilarityThreshold;\n }\n if (options.topKPerBucket !== undefined) opts.top_k_per_bucket = options.topKPerBucket;\n if (options.returnFormat !== undefined) opts.return_format = options.returnFormat;\n if (options.responseSchema !== undefined) opts.response_schema = options.responseSchema;\n const body = {\n query: question,\n buckets,\n stream: true,\n options: opts,\n };\n const url = `${this.baseUrl}/v1/query`;\n const apiKey = this.apiKey;\n const fetchImpl = this.fetchImpl;\n const timeoutMs = this.streamTimeoutMs;\n const maxRetriesOn429 = this.maxRetriesOn429;\n const bodyJson = JSON.stringify(body);\n\n return {\n [Symbol.asyncIterator]: () => {\n const controller = new AbortController();\n // The timeout caps total wall-clock for the stream. Set generously\n // because synthesis can run 10–25s before the first byte even with\n // streaming on slow paths; clamp to the user's configured timeout.\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let started = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n let done = false;\n const queue: QueryStreamEvent[] = [];\n let pendingError: unknown = null;\n\n const ensureStarted = async (): Promise<void> => {\n if (started) return;\n started = true;\n // 429-aware retry at the connection-open stage only. Once\n // the response body starts flowing we can't resume mid-\n // stream safely.\n let attemptsRemaining = maxRetriesOn429;\n let backoffMs = 1000;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n 'User-Agent': USER_AGENT,\n },\n body: bodyJson,\n signal: controller.signal,\n });\n if (res.status === 429 && attemptsRemaining > 0) {\n await res.text().catch(() => undefined);\n const delay = parseRetryAfterMs(res.headers.get('Retry-After'), backoffMs);\n await sleep(delay);\n attemptsRemaining -= 1;\n backoffMs = Math.min(backoffMs * 2, RETRY_AFTER_CAP_MS);\n continue;\n }\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n let parsed: unknown = text;\n try {\n parsed = text ? JSON.parse(text) : text;\n } catch {\n /* keep raw text */\n }\n const detail =\n parsed && typeof parsed === 'object' && parsed !== null && 'error' in parsed\n ? (parsed as { error: unknown }).error\n : parsed;\n throw new EngramError(\n `Engram API ${res.status}: ${typeof detail === 'string' ? detail : JSON.stringify(detail ?? '')}`,\n res.status,\n parsed,\n );\n }\n if (!res.body) {\n throw new EngramError('Engram API: streaming response has no body', res.status, null);\n }\n reader = res.body.getReader();\n return;\n }\n };\n\n const drainBuffer = (): void => {\n // SSE frames are separated by a blank line ('\\n\\n'). Each frame\n // is one or more 'field: value' lines. We only care about\n // 'data:' lines for this stream.\n let idx: number;\n while ((idx = buffer.indexOf('\\n\\n')) !== -1) {\n const frame = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 2);\n const dataLines: string[] = [];\n for (const rawLine of frame.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine;\n if (line.startsWith('data: ')) {\n dataLines.push(line.slice(6));\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5));\n }\n }\n if (dataLines.length === 0) continue;\n const payloadStr = dataLines.join('\\n');\n if (payloadStr === '[DONE]') {\n done = true;\n return;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(payloadStr);\n } catch {\n // Malformed frame — skip rather than corrupt the stream.\n continue;\n }\n if (payload && typeof payload === 'object') {\n const obj = payload as Record<string, unknown>;\n if (obj.error) {\n pendingError = new EngramError(String(obj.error), 0, obj);\n done = true;\n return;\n }\n // OpenAI-style delta chunk\n const choices = obj.choices as Array<{ delta?: { content?: string } }> | undefined;\n if (Array.isArray(choices) && choices.length > 0) {\n const delta = choices[0]?.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n queue.push({ type: 'delta', content: delta });\n }\n continue;\n }\n // Final usage / explanation frame (no 'choices' key).\n queue.push({ type: 'done', ...(obj as Omit<Extract<QueryStreamEvent, { type: 'done' }>, 'type'>) });\n }\n }\n };\n\n return {\n next: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n try {\n await ensureStarted();\n } catch (err) {\n clearTimeout(timer);\n throw err;\n }\n while (queue.length === 0 && !done) {\n if (!reader) {\n clearTimeout(timer);\n throw new EngramError('Engram API: stream reader missing', 0, null);\n }\n const chunk = await reader.read();\n if (chunk.done) {\n // Flush whatever's left in the buffer (some servers don't\n // terminate with a trailing blank line).\n if (buffer.length > 0) {\n buffer += '\\n\\n';\n drainBuffer();\n }\n done = true;\n break;\n }\n buffer += decoder.decode(chunk.value, { stream: true });\n drainBuffer();\n }\n if (queue.length > 0) {\n return { value: queue.shift() as QueryStreamEvent, done: false };\n }\n clearTimeout(timer);\n if (pendingError) throw pendingError;\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n return: async (): Promise<IteratorResult<QueryStreamEvent>> => {\n // Caller broke out of the for-await loop — cancel the upstream\n // request so we're not holding the connection open.\n clearTimeout(timer);\n controller.abort();\n try {\n await reader?.cancel();\n } catch {\n /* ignored */\n }\n return { value: undefined as unknown as QueryStreamEvent, done: true };\n },\n };\n },\n };\n }\n\n // ---------- Buckets ----------\n\n async listBuckets(): Promise<Bucket[]> {\n const result = await this.request<{ buckets: Bucket[] } | Bucket[]>(`/v1/buckets`, {\n method: 'GET',\n });\n return Array.isArray(result) ? result : result.buckets;\n }\n\n async createBucket(name: string, description?: string): Promise<Bucket> {\n return this.request<Bucket>('/v1/buckets', {\n method: 'POST',\n body: { name, description },\n });\n }\n\n async deleteBucket(bucket: string): Promise<void> {\n await this.request<unknown>(`/v1/buckets/${encodeURIComponent(bucket)}`, {\n method: 'DELETE',\n });\n }\n\n // ---------- Profile ----------\n\n async getProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile`,\n { method: 'GET' },\n );\n }\n\n async regenerateProfile(bucket: string = 'default'): Promise<{ profile: string | null }> {\n return this.request<{ profile: string | null }>(\n `/v1/buckets/${encodeURIComponent(bucket)}/profile/regenerate`,\n { method: 'POST' },\n );\n }\n}\n"]}
|