tinker-agent 1.4.0 → 1.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.
@@ -0,0 +1,153 @@
1
+ import { MemoryError } from "./contracts";
2
+
3
+ export function normalizeEmbedding(
4
+ values: readonly number[],
5
+ dimensions: number,
6
+ ): Float32Array {
7
+ requireDimensions(dimensions);
8
+ if (values.length !== dimensions) {
9
+ throw new MemoryError(
10
+ "memory_embedding_dimensions_invalid",
11
+ `Embedding has ${values.length} dimensions; expected ${dimensions}.`,
12
+ );
13
+ }
14
+ if (values.some((value) => !Number.isFinite(value))) {
15
+ throw new MemoryError(
16
+ "memory_embedding_values_invalid",
17
+ "Embedding contains a non-finite value.",
18
+ );
19
+ }
20
+
21
+ const rawNorm = vectorNorm(values);
22
+ if (!Number.isFinite(rawNorm) || rawNorm === 0) {
23
+ throw new MemoryError(
24
+ "memory_embedding_norm_invalid",
25
+ "Embedding norm must be finite and non-zero.",
26
+ );
27
+ }
28
+
29
+ const normalized = new Float32Array(
30
+ values.map((value) => Math.fround(value / rawNorm)),
31
+ );
32
+ const floatNorm = vectorNorm(normalized);
33
+ if (!Number.isFinite(floatNorm) || floatNorm === 0) {
34
+ throw new MemoryError(
35
+ "memory_embedding_norm_invalid",
36
+ "Embedding became zero or non-finite after Float32 conversion.",
37
+ );
38
+ }
39
+ for (let index = 0; index < normalized.length; index += 1) {
40
+ normalized[index] = Math.fround(normalized[index] / floatNorm);
41
+ }
42
+ return normalized;
43
+ }
44
+
45
+ export function encodeEmbedding(vector: Float32Array): Uint8Array {
46
+ const bytes = new Uint8Array(vector.length * Float32Array.BYTES_PER_ELEMENT);
47
+ const view = new DataView(bytes.buffer);
48
+ for (let index = 0; index < vector.length; index += 1) {
49
+ const value = vector[index];
50
+ if (!Number.isFinite(value)) {
51
+ throw new MemoryError(
52
+ "memory_embedding_values_invalid",
53
+ "Embedding contains a non-finite Float32 value.",
54
+ );
55
+ }
56
+ view.setFloat32(index * Float32Array.BYTES_PER_ELEMENT, value, true);
57
+ }
58
+ return bytes;
59
+ }
60
+
61
+ export function decodeEmbedding(
62
+ value: unknown,
63
+ expectedBlobBytes: number,
64
+ ): Float32Array {
65
+ if (!(value instanceof Uint8Array)) {
66
+ throw new MemoryError(
67
+ "memory_embedding_blob_invalid",
68
+ "Stored memory embedding is not a SQLite BLOB.",
69
+ );
70
+ }
71
+ if (value.byteLength !== expectedBlobBytes) {
72
+ throw new MemoryError(
73
+ "memory_embedding_blob_invalid",
74
+ `Stored memory embedding has ${value.byteLength} bytes; expected ${expectedBlobBytes}.`,
75
+ );
76
+ }
77
+
78
+ const view = new DataView(value.buffer, value.byteOffset, value.byteLength);
79
+ const dimensions = expectedBlobBytes / Float32Array.BYTES_PER_ELEMENT;
80
+ const vector = new Float32Array(dimensions);
81
+ for (let index = 0; index < dimensions; index += 1) {
82
+ const decoded = view.getFloat32(index * Float32Array.BYTES_PER_ELEMENT, true);
83
+ if (!Number.isFinite(decoded)) {
84
+ throw new MemoryError(
85
+ "memory_embedding_blob_invalid",
86
+ "Stored memory embedding contains a non-finite value.",
87
+ );
88
+ }
89
+ vector[index] = decoded;
90
+ }
91
+ return vector;
92
+ }
93
+
94
+ export function cosineFromNormalized(left: Float32Array, right: Float32Array): number {
95
+ if (left.length !== right.length || left.length === 0) {
96
+ throw new MemoryError(
97
+ "memory_embedding_dimensions_invalid",
98
+ "Cosine vectors must have the same non-zero dimensions.",
99
+ );
100
+ }
101
+ let score = 0;
102
+ for (let index = 0; index < left.length; index += 1) {
103
+ score += left[index] * right[index];
104
+ }
105
+ if (!Number.isFinite(score)) {
106
+ throw new MemoryError(
107
+ "memory_embedding_values_invalid",
108
+ "Cosine score is not finite.",
109
+ );
110
+ }
111
+ return score;
112
+ }
113
+
114
+ export function expectedEmbeddingBlobBytes(dimensions: number): number {
115
+ requireDimensions(dimensions);
116
+ const bytes = dimensions * Float32Array.BYTES_PER_ELEMENT;
117
+ if (!Number.isSafeInteger(bytes) || bytes < 1) {
118
+ throw new MemoryError(
119
+ "memory_embedding_dimensions_invalid",
120
+ "Embedding byte length is not a positive safe integer.",
121
+ );
122
+ }
123
+ return bytes;
124
+ }
125
+
126
+ function requireDimensions(dimensions: number): void {
127
+ if (!Number.isSafeInteger(dimensions) || dimensions < 1) {
128
+ throw new MemoryError(
129
+ "memory_embedding_dimensions_invalid",
130
+ "Embedding dimensions must be a positive safe integer.",
131
+ );
132
+ }
133
+ }
134
+
135
+ function vectorNorm(values: ArrayLike<number>): number {
136
+ let scale = 0;
137
+ let sumSquares = 1;
138
+ for (let index = 0; index < values.length; index += 1) {
139
+ const absolute = Math.abs(values[index]);
140
+ if (absolute === 0) {
141
+ continue;
142
+ }
143
+ if (scale < absolute) {
144
+ const ratio = scale / absolute;
145
+ sumSquares = 1 + sumSquares * ratio * ratio;
146
+ scale = absolute;
147
+ } else {
148
+ const ratio = absolute / scale;
149
+ sumSquares += ratio * ratio;
150
+ }
151
+ }
152
+ return scale === 0 ? 0 : scale * Math.sqrt(sumSquares);
153
+ }