memorio 4.9.10 → 4.9.31
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/AGENTS.md +126 -126
- package/README.md +241 -100
- package/examples/basic.ts +1 -1
- package/examples/cross-platform-guards.ts +57 -57
- package/examples/multi-tenant-context.ts +44 -44
- package/examples/react-observer.tsx +63 -63
- package/examples/semantic-memory.ts +60 -60
- package/examples/session-advanced.ts +1 -1
- package/examples/sqlite-batched-writes.ts +57 -57
- package/examples/store-advanced.ts +1 -1
- package/index.cjs +74 -44
- package/index.js +74 -44
- package/llms.txt +13 -5
- package/modules/redux.cjs +2677 -0
- package/modules/redux.cjs.map +1 -0
- package/modules/redux.d.ts +119 -0
- package/modules/redux.js +2669 -0
- package/modules/redux.js.map +1 -0
- package/package.json +9 -3
- package/types/memorio.d.ts +1 -1
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cross-platform-guards.ts
|
|
3
|
-
*
|
|
4
|
-
* Scenario: code that might run in a browser, Node.js, Deno, or an edge
|
|
5
|
-
* runtime, and needs to behave correctly (not just "not crash") in each.
|
|
6
|
-
*
|
|
7
|
-
* Prefer `getCapabilities()` over branching on `isBrowser()`/`isNode()`
|
|
8
|
-
* alone
|
|
9
|
-
* (some edge runtimes expose `localStorage`, some don't).
|
|
10
|
-
*/
|
|
11
|
-
import { memorio, idb, store, session } from 'memorio'
|
|
12
|
-
|
|
13
|
-
export async function savePreferences(prefs: Record<string, unknown>) {
|
|
14
|
-
const caps = memorio.getCapabilities()
|
|
15
|
-
|
|
16
|
-
// store/session silently fall back to a non-durable in-memory Map outside
|
|
17
|
-
// the browser
|
|
18
|
-
// to a caller. Surface the distinction instead of hiding it.
|
|
19
|
-
store.set('preferences', prefs)
|
|
20
|
-
|
|
21
|
-
if (!store.isPersistent) {
|
|
22
|
-
console.warn(
|
|
23
|
-
'[preferences] store is not durable in this environment (%s)
|
|
24
|
-
|
|
25
|
-
caps.platform
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return store.isPersistent
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function saveStructuredRecord(dbName: string, table: string, record: unknown) {
|
|
33
|
-
// idb is disabled outright in Node.js/Deno (no-op + warning). Guard
|
|
34
|
-
// explicitly instead of relying on that warning reaching anyone.
|
|
35
|
-
if (!idb.db.support()) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
`[saveStructuredRecord] IndexedDB is not available in this environment ` +
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
await idb.db.create(dbName)
|
|
44
|
-
await idb.table.create(dbName, table)
|
|
45
|
-
await idb.data.set(dbName, table, record)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function describeEnvironment() {
|
|
49
|
-
const caps = memorio.getCapabilities()
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
platform: caps.platform,
|
|
53
|
-
canPersistAcrossTabs: caps.hasLocalStorage,
|
|
54
|
-
canPersistThisTabOnly: session.isPersistent,
|
|
55
|
-
canUseStructuredStorage: caps.hasIndexedDB,
|
|
56
|
-
}
|
|
57
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* cross-platform-guards.ts
|
|
3
|
+
*
|
|
4
|
+
* Scenario: code that might run in a browser, Node.js, Deno, or an edge
|
|
5
|
+
* runtime, and needs to behave correctly (not just "not crash") in each.
|
|
6
|
+
*
|
|
7
|
+
* Prefer `getCapabilities()` over branching on `isBrowser()`/`isNode()`
|
|
8
|
+
* alone - capability support can differ *within* a platform category
|
|
9
|
+
* (some edge runtimes expose `localStorage`, some don't).
|
|
10
|
+
*/
|
|
11
|
+
import { memorio, idb, store, session } from 'memorio'
|
|
12
|
+
|
|
13
|
+
export async function savePreferences(prefs: Record<string, unknown>) {
|
|
14
|
+
const caps = memorio.getCapabilities()
|
|
15
|
+
|
|
16
|
+
// store/session silently fall back to a non-durable in-memory Map outside
|
|
17
|
+
// the browser - that's not a crash, but it's also not what "save" implies
|
|
18
|
+
// to a caller. Surface the distinction instead of hiding it.
|
|
19
|
+
store.set('preferences', prefs)
|
|
20
|
+
|
|
21
|
+
if (!store.isPersistent) {
|
|
22
|
+
console.warn(
|
|
23
|
+
'[preferences] store is not durable in this environment (%s) - ' +
|
|
24
|
+
'data will not survive a process restart',
|
|
25
|
+
caps.platform
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return store.isPersistent
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function saveStructuredRecord(dbName: string, table: string, record: unknown) {
|
|
33
|
+
// idb is disabled outright in Node.js/Deno (no-op + warning). Guard
|
|
34
|
+
// explicitly instead of relying on that warning reaching anyone.
|
|
35
|
+
if (!idb.db.support()) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`[saveStructuredRecord] IndexedDB is not available in this environment ` +
|
|
38
|
+
`(platform: ${memorio.getCapabilities().platform}). ` +
|
|
39
|
+
`Use sqlite or store instead when running outside a browser.`
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await idb.db.create(dbName)
|
|
44
|
+
await idb.table.create(dbName, table)
|
|
45
|
+
await idb.data.set(dbName, table, record)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function describeEnvironment() {
|
|
49
|
+
const caps = memorio.getCapabilities()
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
platform: caps.platform,
|
|
53
|
+
canPersistAcrossTabs: caps.hasLocalStorage,
|
|
54
|
+
canPersistThisTabOnly: session.isPersistent,
|
|
55
|
+
canUseStructuredStorage: caps.hasIndexedDB,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* multi-tenant-context.ts
|
|
3
|
-
*
|
|
4
|
-
* Scenario: a server-side handler (API route, edge function) that may
|
|
5
|
-
* process requests from different users/tenants in the same process.
|
|
6
|
-
*
|
|
7
|
-
* `state` is a shared global namespace by default
|
|
8
|
-
* in a multi-request handler leaks data between requests. Use an explicit
|
|
9
|
-
* context per request instead.
|
|
10
|
-
*/
|
|
11
|
-
import { memorio } from 'memorio'
|
|
12
|
-
|
|
13
|
-
interface RequestContext {
|
|
14
|
-
userId: string
|
|
15
|
-
tenantId: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function handleRequest(req: RequestContext, payload: { language: string }) {
|
|
19
|
-
// Derive the context id from trusted, server-verified data
|
|
20
|
-
// raw client-supplied header or query param, since contexts are a
|
|
21
|
-
// key-prefix convention, not a hard isolation boundary.
|
|
22
|
-
const contextId = `tenant:${req.tenantId}:user:${req.userId}`
|
|
23
|
-
const ctx = memorio.createContext(contextId)
|
|
24
|
-
|
|
25
|
-
// All reads/writes go through ctx.state, not the bare global `state`
|
|
26
|
-
// this is what actually keeps this request's data from leaking into the
|
|
27
|
-
// next one handled by the same process.
|
|
28
|
-
ctx.state.language = payload.language
|
|
29
|
-
|
|
30
|
-
return { language: ctx.state.language }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Cleanup for long-lived processes that create many short-lived contexts
|
|
34
|
-
// (e.g. one per request in a busy server): drop the context when the
|
|
35
|
-
// request is done, or it accumulates for the life of the process.
|
|
36
|
-
export function teardownRequest(req: RequestContext) {
|
|
37
|
-
const contextId = `tenant:${req.tenantId}:user:${req.userId}`
|
|
38
|
-
memorio.deleteContext(contextId)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// NOTE: context isolation here is organizational (key-prefixing under the
|
|
42
|
-
// same storage), not a security boundary. Don't rely on it as the only
|
|
43
|
-
// thing preventing one tenant from reaching another tenant's data
|
|
44
|
-
// that at the auth/backend layer as well.
|
|
1
|
+
/**
|
|
2
|
+
* multi-tenant-context.ts
|
|
3
|
+
*
|
|
4
|
+
* Scenario: a server-side handler (API route, edge function) that may
|
|
5
|
+
* process requests from different users/tenants in the same process.
|
|
6
|
+
*
|
|
7
|
+
* `state` is a shared global namespace by default - writing to it directly
|
|
8
|
+
* in a multi-request handler leaks data between requests. Use an explicit
|
|
9
|
+
* context per request instead.
|
|
10
|
+
*/
|
|
11
|
+
import { memorio } from 'memorio'
|
|
12
|
+
|
|
13
|
+
interface RequestContext {
|
|
14
|
+
userId: string
|
|
15
|
+
tenantId: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function handleRequest(req: RequestContext, payload: { language: string }) {
|
|
19
|
+
// Derive the context id from trusted, server-verified data - never from a
|
|
20
|
+
// raw client-supplied header or query param, since contexts are a
|
|
21
|
+
// key-prefix convention, not a hard isolation boundary.
|
|
22
|
+
const contextId = `tenant:${req.tenantId}:user:${req.userId}`
|
|
23
|
+
const ctx = memorio.createContext(contextId)
|
|
24
|
+
|
|
25
|
+
// All reads/writes go through ctx.state, not the bare global `state` -
|
|
26
|
+
// this is what actually keeps this request's data from leaking into the
|
|
27
|
+
// next one handled by the same process.
|
|
28
|
+
ctx.state.language = payload.language
|
|
29
|
+
|
|
30
|
+
return { language: ctx.state.language }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Cleanup for long-lived processes that create many short-lived contexts
|
|
34
|
+
// (e.g. one per request in a busy server): drop the context when the
|
|
35
|
+
// request is done, or it accumulates for the life of the process.
|
|
36
|
+
export function teardownRequest(req: RequestContext) {
|
|
37
|
+
const contextId = `tenant:${req.tenantId}:user:${req.userId}`
|
|
38
|
+
memorio.deleteContext(contextId)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// NOTE: context isolation here is organizational (key-prefixing under the
|
|
42
|
+
// same storage), not a security boundary. Don't rely on it as the only
|
|
43
|
+
// thing preventing one tenant from reaching another tenant's data - enforce
|
|
44
|
+
// that at the auth/backend layer as well.
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* react-observer.tsx
|
|
3
|
-
*
|
|
4
|
-
* Scenario: a React component reading reactive `state`. Shows both
|
|
5
|
-
* `useObserver` modes and when to prefer each, plus `typed<T>()` +
|
|
6
|
-
* `registerSchema()` for the paths you don't want to fail silently on a
|
|
7
|
-
* typo or a later rename.
|
|
8
|
-
*/
|
|
9
|
-
import 'memorio'
|
|
10
|
-
import { useReducer } from 'react'
|
|
11
|
-
|
|
12
|
-
interface AppState {
|
|
13
|
-
counter: number
|
|
14
|
-
user: { name: string; email: string }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Compile-time safety: same Proxy as the global `state`, no duplicated store.
|
|
18
|
-
const app = memorio.typed<AppState>()
|
|
19
|
-
|
|
20
|
-
// Runtime safety: reject writes that don't match the shape, independent of
|
|
21
|
-
// whatever calls it (including code you didn't write).
|
|
22
|
-
memorio.registerSchema('user', {
|
|
23
|
-
type: 'object',
|
|
24
|
-
required: ['name', 'email'],
|
|
25
|
-
properties: {
|
|
26
|
-
name: { type: 'string', min: 1 },
|
|
27
|
-
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
export function Counter() {
|
|
32
|
-
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
33
|
-
|
|
34
|
-
// Auto-discovery mode: re-runs when any state path *touched inside the
|
|
35
|
-
// callback* changes. Convenient, but re-runs on anything you read there
|
|
36
|
-
// don't reach for it in a callback that reads many unrelated paths.
|
|
37
|
-
useObserver(() => {
|
|
38
|
-
forceUpdate()
|
|
39
|
-
}, state.counter)
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div>
|
|
43
|
-
<span>Count: {app.counter}</span>
|
|
44
|
-
<button onClick={() => { app.counter += 1 }}>+1</button>
|
|
45
|
-
</div>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function UserBadge() {
|
|
50
|
-
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
51
|
-
|
|
52
|
-
// Explicit deps mode: only re-runs when the listed path(s) change
|
|
53
|
-
// this when you want precise control instead of auto-discovery.
|
|
54
|
-
useObserver(() => {
|
|
55
|
-
forceUpdate()
|
|
56
|
-
}, [state.user])
|
|
57
|
-
|
|
58
|
-
return <span>{app.user?.name ?? 'Guest'}</span>
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Rejected at write time by the schema registered above:
|
|
62
|
-
// app.user = { name: 'Sara' } // ❌ missing "email"
|
|
63
|
-
// app.user = { name: 'Sara', email: 'x@y.com' } // ✅
|
|
1
|
+
/**
|
|
2
|
+
* react-observer.tsx
|
|
3
|
+
*
|
|
4
|
+
* Scenario: a React component reading reactive `state`. Shows both
|
|
5
|
+
* `useObserver` modes and when to prefer each, plus `typed<T>()` +
|
|
6
|
+
* `registerSchema()` for the paths you don't want to fail silently on a
|
|
7
|
+
* typo or a later rename.
|
|
8
|
+
*/
|
|
9
|
+
import 'memorio'
|
|
10
|
+
import { useReducer } from 'react'
|
|
11
|
+
|
|
12
|
+
interface AppState {
|
|
13
|
+
counter: number
|
|
14
|
+
user: { name: string; email: string }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Compile-time safety: same Proxy as the global `state`, no duplicated store.
|
|
18
|
+
const app = memorio.typed<AppState>()
|
|
19
|
+
|
|
20
|
+
// Runtime safety: reject writes that don't match the shape, independent of
|
|
21
|
+
// whatever calls it (including code you didn't write).
|
|
22
|
+
memorio.registerSchema('user', {
|
|
23
|
+
type: 'object',
|
|
24
|
+
required: ['name', 'email'],
|
|
25
|
+
properties: {
|
|
26
|
+
name: { type: 'string', min: 1 },
|
|
27
|
+
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export function Counter() {
|
|
32
|
+
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
33
|
+
|
|
34
|
+
// Auto-discovery mode: re-runs when any state path *touched inside the
|
|
35
|
+
// callback* changes. Convenient, but re-runs on anything you read there -
|
|
36
|
+
// don't reach for it in a callback that reads many unrelated paths.
|
|
37
|
+
useObserver(() => {
|
|
38
|
+
forceUpdate()
|
|
39
|
+
}, state.counter)
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div>
|
|
43
|
+
<span>Count: {app.counter}</span>
|
|
44
|
+
<button onClick={() => { app.counter += 1 }}>+1</button>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function UserBadge() {
|
|
50
|
+
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
51
|
+
|
|
52
|
+
// Explicit deps mode: only re-runs when the listed path(s) change - use
|
|
53
|
+
// this when you want precise control instead of auto-discovery.
|
|
54
|
+
useObserver(() => {
|
|
55
|
+
forceUpdate()
|
|
56
|
+
}, [state.user])
|
|
57
|
+
|
|
58
|
+
return <span>{app.user?.name ?? 'Guest'}</span>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Rejected at write time by the schema registered above:
|
|
62
|
+
// app.user = { name: 'Sara' } // ❌ missing "email"
|
|
63
|
+
// app.user = { name: 'Sara', email: 'x@y.com' } // ✅
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* semantic-memory.ts
|
|
3
|
-
*
|
|
4
|
-
* Scenario: an LLM-backed app that needs to remember facts about a user
|
|
5
|
-
* with confidence, source, and expiry
|
|
6
|
-
* relevant to the current task.
|
|
7
|
-
*
|
|
8
|
-
* Important: `memory.context()` ranks by tags/type/confidence/recency.
|
|
9
|
-
* It is NOT embedding-based semantic search
|
|
10
|
-
* assumes free-text meaning-based retrieval on top of this alone.
|
|
11
|
-
*/
|
|
12
|
-
import { memorio } from 'memorio'
|
|
13
|
-
|
|
14
|
-
export async function rememberPreference(
|
|
15
|
-
key: string,
|
|
16
|
-
value: unknown,
|
|
17
|
-
opts: { confidence: number; source: string; tags: string[] }
|
|
18
|
-
) {
|
|
19
|
-
await memorio.memory.remember(key, value, {
|
|
20
|
-
type: 'preference',
|
|
21
|
-
scope: 'local',
|
|
22
|
-
...opts,
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function correctPreference(key: string, newValue: unknown, confidence: number) {
|
|
27
|
-
// update() supersedes, it doesn't overwrite
|
|
28
|
-
// status: 'superseded' and stays queryable for history/audit purposes.
|
|
29
|
-
await memorio.memory.update(key, newValue, { confidence })
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function getRelevantContext(tags: string[]) {
|
|
33
|
-
// This is rule-based retrieval, not semantic similarity search. It's the
|
|
34
|
-
// right tool for "give me what's tagged/confident/recent enough," not for
|
|
35
|
-
// "find the memory that means the same thing as this new sentence."
|
|
36
|
-
return memorio.memory.context({
|
|
37
|
-
tags,
|
|
38
|
-
types: ['preference', 'decision'],
|
|
39
|
-
minConfidence: 0.7,
|
|
40
|
-
maxEntries: 10,
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// If the task genuinely needs meaning-based retrieval over free text (e.g.
|
|
45
|
-
// "find prior notes about a similar problem" rather than "find notes tagged
|
|
46
|
-
// X with confidence > Y"), pair this with a dedicated embedding store for
|
|
47
|
-
// the similarity search, and keep using memorio.memory for the lifecycle
|
|
48
|
-
// metadata (confidence, TTL, supersession) on top of whatever it finds
|
|
49
|
-
// don't simulate similarity search with context() alone.
|
|
50
|
-
|
|
51
|
-
// Usage:
|
|
52
|
-
// await rememberPreference('user.language', 'Italian', {
|
|
53
|
-
// confidence: 0.92,
|
|
54
|
-
// source: 'conversation',
|
|
55
|
-
// tags: ['user', 'ui'],
|
|
56
|
-
// })
|
|
57
|
-
//
|
|
58
|
-
// await correctPreference('user.language', 'English', 0.95)
|
|
59
|
-
//
|
|
60
|
-
// const ctx = await getRelevantContext(['user'])
|
|
1
|
+
/**
|
|
2
|
+
* semantic-memory.ts
|
|
3
|
+
*
|
|
4
|
+
* Scenario: an LLM-backed app that needs to remember facts about a user
|
|
5
|
+
* with confidence, source, and expiry - and later retrieve only what's
|
|
6
|
+
* relevant to the current task.
|
|
7
|
+
*
|
|
8
|
+
* Important: `memory.context()` ranks by tags/type/confidence/recency.
|
|
9
|
+
* It is NOT embedding-based semantic search - don't build a feature that
|
|
10
|
+
* assumes free-text meaning-based retrieval on top of this alone.
|
|
11
|
+
*/
|
|
12
|
+
import { memorio } from 'memorio'
|
|
13
|
+
|
|
14
|
+
export async function rememberPreference(
|
|
15
|
+
key: string,
|
|
16
|
+
value: unknown,
|
|
17
|
+
opts: { confidence: number; source: string; tags: string[] }
|
|
18
|
+
) {
|
|
19
|
+
await memorio.memory.remember(key, value, {
|
|
20
|
+
type: 'preference',
|
|
21
|
+
scope: 'local',
|
|
22
|
+
...opts,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function correctPreference(key: string, newValue: unknown, confidence: number) {
|
|
27
|
+
// update() supersedes, it doesn't overwrite - the old entry becomes
|
|
28
|
+
// status: 'superseded' and stays queryable for history/audit purposes.
|
|
29
|
+
await memorio.memory.update(key, newValue, { confidence })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getRelevantContext(tags: string[]) {
|
|
33
|
+
// This is rule-based retrieval, not semantic similarity search. It's the
|
|
34
|
+
// right tool for "give me what's tagged/confident/recent enough," not for
|
|
35
|
+
// "find the memory that means the same thing as this new sentence."
|
|
36
|
+
return memorio.memory.context({
|
|
37
|
+
tags,
|
|
38
|
+
types: ['preference', 'decision'],
|
|
39
|
+
minConfidence: 0.7,
|
|
40
|
+
maxEntries: 10,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// If the task genuinely needs meaning-based retrieval over free text (e.g.
|
|
45
|
+
// "find prior notes about a similar problem" rather than "find notes tagged
|
|
46
|
+
// X with confidence > Y"), pair this with a dedicated embedding store for
|
|
47
|
+
// the similarity search, and keep using memorio.memory for the lifecycle
|
|
48
|
+
// metadata (confidence, TTL, supersession) on top of whatever it finds -
|
|
49
|
+
// don't simulate similarity search with context() alone.
|
|
50
|
+
|
|
51
|
+
// Usage:
|
|
52
|
+
// await rememberPreference('user.language', 'Italian', {
|
|
53
|
+
// confidence: 0.92,
|
|
54
|
+
// source: 'conversation',
|
|
55
|
+
// tags: ['user', 'ui'],
|
|
56
|
+
// })
|
|
57
|
+
//
|
|
58
|
+
// await correctPreference('user.language', 'English', 0.95)
|
|
59
|
+
//
|
|
60
|
+
// const ctx = await getRelevantContext(['user'])
|
|
@@ -73,7 +73,7 @@ console.debug('Cart total:', total)
|
|
|
73
73
|
// ============================================
|
|
74
74
|
|
|
75
75
|
// Get session storage size
|
|
76
|
-
console.debug('Session size:', session.size(), '
|
|
76
|
+
console.debug('Session size:', session.size(), 'kilobytes')
|
|
77
77
|
|
|
78
78
|
// ============================================
|
|
79
79
|
// CLEANUP
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sqlite-batched-writes.ts
|
|
3
|
-
*
|
|
4
|
-
* Scenario: importing or writing many rows into memorio's sqlite layer.
|
|
5
|
-
*
|
|
6
|
-
* Persistence there serializes the ENTIRE database on every flush
|
|
7
|
-
* incremental. Calling a persisting write inside a per-row loop is the
|
|
8
|
-
* single most common way to accidentally make this layer slow.
|
|
9
|
-
*/
|
|
10
|
-
import { memorio } from 'memorio'
|
|
11
|
-
|
|
12
|
-
interface UserRow {
|
|
13
|
-
id: number
|
|
14
|
-
name: string
|
|
15
|
-
role: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function importUsers(users: UserRow[]) {
|
|
19
|
-
await memorio.sqlite.ready
|
|
20
|
-
await memorio.sqlite.db.create('app', { persistence: true })
|
|
21
|
-
|
|
22
|
-
await memorio.sqlite.query.run(
|
|
23
|
-
'app',
|
|
24
|
-
`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, role TEXT)`
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
// Bad: this would serialize the whole DB after every single insert.
|
|
28
|
-
//
|
|
29
|
-
// for (const user of users) {
|
|
30
|
-
// await memorio.sqlite.query.run('app', 'INSERT INTO users ...', [...])
|
|
31
|
-
// await memorio.sqlite.db.flush('app') // <- don't do this per row
|
|
32
|
-
// }
|
|
33
|
-
|
|
34
|
-
// Good: run every insert first, persist once after the batch.
|
|
35
|
-
for (const user of users) {
|
|
36
|
-
await memorio.sqlite.query.run(
|
|
37
|
-
'app',
|
|
38
|
-
`INSERT INTO users (id, name, role) VALUES (?, ?, ?)`,
|
|
39
|
-
[user.id, user.name, user.role]
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Persist explicitly, once, after the whole batch
|
|
44
|
-
// version's API for the exact flush/persist call name if it differs from
|
|
45
|
-
// automatic persistence-on-close.
|
|
46
|
-
const admins = await memorio.sqlite.query.select(
|
|
47
|
-
'app',
|
|
48
|
-
`SELECT * FROM users WHERE role = ?`,
|
|
49
|
-
['admin']
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
return admins
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// For read-heavy or scratch-space use where durability doesn't matter,
|
|
56
|
-
// skip { persistence: true } entirely
|
|
57
|
-
// which avoids the serialize cost altogether.
|
|
1
|
+
/**
|
|
2
|
+
* sqlite-batched-writes.ts
|
|
3
|
+
*
|
|
4
|
+
* Scenario: importing or writing many rows into memorio's sqlite layer.
|
|
5
|
+
*
|
|
6
|
+
* Persistence there serializes the ENTIRE database on every flush - not
|
|
7
|
+
* incremental. Calling a persisting write inside a per-row loop is the
|
|
8
|
+
* single most common way to accidentally make this layer slow.
|
|
9
|
+
*/
|
|
10
|
+
import { memorio } from 'memorio'
|
|
11
|
+
|
|
12
|
+
interface UserRow {
|
|
13
|
+
id: number
|
|
14
|
+
name: string
|
|
15
|
+
role: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function importUsers(users: UserRow[]) {
|
|
19
|
+
await memorio.sqlite.ready
|
|
20
|
+
await memorio.sqlite.db.create('app', { persistence: true })
|
|
21
|
+
|
|
22
|
+
await memorio.sqlite.query.run(
|
|
23
|
+
'app',
|
|
24
|
+
`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, role TEXT)`
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Bad: this would serialize the whole DB after every single insert.
|
|
28
|
+
//
|
|
29
|
+
// for (const user of users) {
|
|
30
|
+
// await memorio.sqlite.query.run('app', 'INSERT INTO users ...', [...])
|
|
31
|
+
// await memorio.sqlite.db.flush('app') // <- don't do this per row
|
|
32
|
+
// }
|
|
33
|
+
|
|
34
|
+
// Good: run every insert first, persist once after the batch.
|
|
35
|
+
for (const user of users) {
|
|
36
|
+
await memorio.sqlite.query.run(
|
|
37
|
+
'app',
|
|
38
|
+
`INSERT INTO users (id, name, role) VALUES (?, ?, ?)`,
|
|
39
|
+
[user.id, user.name, user.role]
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Persist explicitly, once, after the whole batch - check the installed
|
|
44
|
+
// version's API for the exact flush/persist call name if it differs from
|
|
45
|
+
// automatic persistence-on-close.
|
|
46
|
+
const admins = await memorio.sqlite.query.select(
|
|
47
|
+
'app',
|
|
48
|
+
`SELECT * FROM users WHERE role = ?`,
|
|
49
|
+
['admin']
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return admins
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// For read-heavy or scratch-space use where durability doesn't matter,
|
|
56
|
+
// skip { persistence: true } entirely - sqlite runs in memory by default,
|
|
57
|
+
// which avoids the serialize cost altogether.
|
|
@@ -51,7 +51,7 @@ if (savedPrefs) {
|
|
|
51
51
|
|
|
52
52
|
// Get storage size
|
|
53
53
|
const currentSize = store.size()
|
|
54
|
-
console.debug('Current storage size:', currentSize, '
|
|
54
|
+
console.debug('Current storage size:', currentSize, 'kilobytes')
|
|
55
55
|
|
|
56
56
|
// ============================================
|
|
57
57
|
// ALIAS METHODS
|