yaab-core 0.1.0__tar.gz
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.
- yaab_core-0.1.0/Cargo.lock +315 -0
- yaab_core-0.1.0/Cargo.toml +27 -0
- yaab_core-0.1.0/LICENSE +21 -0
- yaab_core-0.1.0/PKG-INFO +32 -0
- yaab_core-0.1.0/README.md +18 -0
- yaab_core-0.1.0/pyproject.toml +21 -0
- yaab_core-0.1.0/src/actors.rs +41 -0
- yaab_core-0.1.0/src/channels.rs +94 -0
- yaab_core-0.1.0/src/checkpoint.rs +33 -0
- yaab_core-0.1.0/src/lib.rs +102 -0
- yaab_core-0.1.0/src/scheduler.rs +77 -0
- yaab_core-0.1.0/src/vector.rs +39 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.5.1"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "block-buffer"
|
|
13
|
+
version = "0.10.4"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
|
16
|
+
dependencies = [
|
|
17
|
+
"generic-array",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[[package]]
|
|
21
|
+
name = "cfg-if"
|
|
22
|
+
version = "1.0.4"
|
|
23
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
24
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "cpufeatures"
|
|
28
|
+
version = "0.2.17"
|
|
29
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
30
|
+
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
|
31
|
+
dependencies = [
|
|
32
|
+
"libc",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "crypto-common"
|
|
37
|
+
version = "0.1.7"
|
|
38
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
39
|
+
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
|
40
|
+
dependencies = [
|
|
41
|
+
"generic-array",
|
|
42
|
+
"typenum",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[[package]]
|
|
46
|
+
name = "digest"
|
|
47
|
+
version = "0.10.7"
|
|
48
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
49
|
+
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|
50
|
+
dependencies = [
|
|
51
|
+
"block-buffer",
|
|
52
|
+
"crypto-common",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[[package]]
|
|
56
|
+
name = "generic-array"
|
|
57
|
+
version = "0.14.7"
|
|
58
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
59
|
+
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|
60
|
+
dependencies = [
|
|
61
|
+
"typenum",
|
|
62
|
+
"version_check",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[[package]]
|
|
66
|
+
name = "heck"
|
|
67
|
+
version = "0.5.0"
|
|
68
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
69
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
70
|
+
|
|
71
|
+
[[package]]
|
|
72
|
+
name = "indoc"
|
|
73
|
+
version = "2.0.7"
|
|
74
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
+
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
76
|
+
dependencies = [
|
|
77
|
+
"rustversion",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
[[package]]
|
|
81
|
+
name = "itoa"
|
|
82
|
+
version = "1.0.18"
|
|
83
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
84
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
85
|
+
|
|
86
|
+
[[package]]
|
|
87
|
+
name = "libc"
|
|
88
|
+
version = "0.2.186"
|
|
89
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
90
|
+
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
|
91
|
+
|
|
92
|
+
[[package]]
|
|
93
|
+
name = "memchr"
|
|
94
|
+
version = "2.8.1"
|
|
95
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
96
|
+
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
|
|
97
|
+
|
|
98
|
+
[[package]]
|
|
99
|
+
name = "memoffset"
|
|
100
|
+
version = "0.9.1"
|
|
101
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
102
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
103
|
+
dependencies = [
|
|
104
|
+
"autocfg",
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
[[package]]
|
|
108
|
+
name = "once_cell"
|
|
109
|
+
version = "1.21.4"
|
|
110
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
111
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
112
|
+
|
|
113
|
+
[[package]]
|
|
114
|
+
name = "portable-atomic"
|
|
115
|
+
version = "1.13.1"
|
|
116
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
117
|
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
|
118
|
+
|
|
119
|
+
[[package]]
|
|
120
|
+
name = "proc-macro2"
|
|
121
|
+
version = "1.0.106"
|
|
122
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
123
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
124
|
+
dependencies = [
|
|
125
|
+
"unicode-ident",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
[[package]]
|
|
129
|
+
name = "pyo3"
|
|
130
|
+
version = "0.23.5"
|
|
131
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
132
|
+
checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
|
|
133
|
+
dependencies = [
|
|
134
|
+
"cfg-if",
|
|
135
|
+
"indoc",
|
|
136
|
+
"libc",
|
|
137
|
+
"memoffset",
|
|
138
|
+
"once_cell",
|
|
139
|
+
"portable-atomic",
|
|
140
|
+
"pyo3-build-config",
|
|
141
|
+
"pyo3-ffi",
|
|
142
|
+
"pyo3-macros",
|
|
143
|
+
"unindent",
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
[[package]]
|
|
147
|
+
name = "pyo3-build-config"
|
|
148
|
+
version = "0.23.5"
|
|
149
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
150
|
+
checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
|
|
151
|
+
dependencies = [
|
|
152
|
+
"once_cell",
|
|
153
|
+
"target-lexicon",
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
[[package]]
|
|
157
|
+
name = "pyo3-ffi"
|
|
158
|
+
version = "0.23.5"
|
|
159
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
160
|
+
checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
|
|
161
|
+
dependencies = [
|
|
162
|
+
"libc",
|
|
163
|
+
"pyo3-build-config",
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
[[package]]
|
|
167
|
+
name = "pyo3-macros"
|
|
168
|
+
version = "0.23.5"
|
|
169
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
170
|
+
checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
|
|
171
|
+
dependencies = [
|
|
172
|
+
"proc-macro2",
|
|
173
|
+
"pyo3-macros-backend",
|
|
174
|
+
"quote",
|
|
175
|
+
"syn",
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
[[package]]
|
|
179
|
+
name = "pyo3-macros-backend"
|
|
180
|
+
version = "0.23.5"
|
|
181
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
182
|
+
checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
|
|
183
|
+
dependencies = [
|
|
184
|
+
"heck",
|
|
185
|
+
"proc-macro2",
|
|
186
|
+
"pyo3-build-config",
|
|
187
|
+
"quote",
|
|
188
|
+
"syn",
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
[[package]]
|
|
192
|
+
name = "quote"
|
|
193
|
+
version = "1.0.45"
|
|
194
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
195
|
+
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
|
196
|
+
dependencies = [
|
|
197
|
+
"proc-macro2",
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
[[package]]
|
|
201
|
+
name = "rustversion"
|
|
202
|
+
version = "1.0.22"
|
|
203
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
204
|
+
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
205
|
+
|
|
206
|
+
[[package]]
|
|
207
|
+
name = "serde"
|
|
208
|
+
version = "1.0.228"
|
|
209
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
210
|
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
211
|
+
dependencies = [
|
|
212
|
+
"serde_core",
|
|
213
|
+
"serde_derive",
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
[[package]]
|
|
217
|
+
name = "serde_core"
|
|
218
|
+
version = "1.0.228"
|
|
219
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
220
|
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
221
|
+
dependencies = [
|
|
222
|
+
"serde_derive",
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
[[package]]
|
|
226
|
+
name = "serde_derive"
|
|
227
|
+
version = "1.0.228"
|
|
228
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
229
|
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
230
|
+
dependencies = [
|
|
231
|
+
"proc-macro2",
|
|
232
|
+
"quote",
|
|
233
|
+
"syn",
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
[[package]]
|
|
237
|
+
name = "serde_json"
|
|
238
|
+
version = "1.0.150"
|
|
239
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
240
|
+
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
|
241
|
+
dependencies = [
|
|
242
|
+
"itoa",
|
|
243
|
+
"memchr",
|
|
244
|
+
"serde",
|
|
245
|
+
"serde_core",
|
|
246
|
+
"zmij",
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
[[package]]
|
|
250
|
+
name = "sha2"
|
|
251
|
+
version = "0.10.9"
|
|
252
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
253
|
+
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
|
254
|
+
dependencies = [
|
|
255
|
+
"cfg-if",
|
|
256
|
+
"cpufeatures",
|
|
257
|
+
"digest",
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
[[package]]
|
|
261
|
+
name = "syn"
|
|
262
|
+
version = "2.0.117"
|
|
263
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
264
|
+
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
|
265
|
+
dependencies = [
|
|
266
|
+
"proc-macro2",
|
|
267
|
+
"quote",
|
|
268
|
+
"unicode-ident",
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
[[package]]
|
|
272
|
+
name = "target-lexicon"
|
|
273
|
+
version = "0.12.16"
|
|
274
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
275
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
276
|
+
|
|
277
|
+
[[package]]
|
|
278
|
+
name = "typenum"
|
|
279
|
+
version = "1.20.1"
|
|
280
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
281
|
+
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
|
282
|
+
|
|
283
|
+
[[package]]
|
|
284
|
+
name = "unicode-ident"
|
|
285
|
+
version = "1.0.24"
|
|
286
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
287
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
288
|
+
|
|
289
|
+
[[package]]
|
|
290
|
+
name = "unindent"
|
|
291
|
+
version = "0.2.4"
|
|
292
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
293
|
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
294
|
+
|
|
295
|
+
[[package]]
|
|
296
|
+
name = "version_check"
|
|
297
|
+
version = "0.9.5"
|
|
298
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
299
|
+
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
300
|
+
|
|
301
|
+
[[package]]
|
|
302
|
+
name = "yaab-core"
|
|
303
|
+
version = "0.1.0"
|
|
304
|
+
dependencies = [
|
|
305
|
+
"pyo3",
|
|
306
|
+
"serde",
|
|
307
|
+
"serde_json",
|
|
308
|
+
"sha2",
|
|
309
|
+
]
|
|
310
|
+
|
|
311
|
+
[[package]]
|
|
312
|
+
name = "zmij"
|
|
313
|
+
version = "1.0.21"
|
|
314
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
315
|
+
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "yaab-core"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Rust performance core for the YAAB agent SDK (orchestration, checkpoint serialization, vector ops)."
|
|
6
|
+
license = "MIT"
|
|
7
|
+
repository = "https://github.com/sthitaprajnas/yaab"
|
|
8
|
+
homepage = "https://github.com/sthitaprajnas/yaab"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
|
|
11
|
+
[lib]
|
|
12
|
+
name = "yaab_core"
|
|
13
|
+
# cdylib for the Python extension module; rlib so the engine can be reused by other bindings.
|
|
14
|
+
crate-type = ["cdylib", "rlib"]
|
|
15
|
+
|
|
16
|
+
[dependencies]
|
|
17
|
+
# abi3-py311: build a single stable-ABI wheel that works on CPython 3.11+ —
|
|
18
|
+
# including future versions (3.13, 3.14, …) — so users never fall back to the
|
|
19
|
+
# pure-Python core just because their interpreter is newer than our wheels.
|
|
20
|
+
pyo3 = { version = "0.23", features = ["extension-module", "abi3-py311"] }
|
|
21
|
+
serde = { version = "1", features = ["derive"] }
|
|
22
|
+
serde_json = "1"
|
|
23
|
+
sha2 = "0.10"
|
|
24
|
+
|
|
25
|
+
[profile.release]
|
|
26
|
+
opt-level = 3
|
|
27
|
+
lto = true
|
yaab_core-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YAAB contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
yaab_core-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yaab-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
5
|
+
Classifier: Programming Language :: Rust
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Summary: Rust performance core for the YAAB agent SDK.
|
|
9
|
+
Home-Page: https://github.com/sthitaprajnas/yaab
|
|
10
|
+
Author: YAAB contributors
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
13
|
+
|
|
14
|
+
# yaab-core
|
|
15
|
+
|
|
16
|
+
The Rust performance core for [YAAB](../README.md), exposed to Python via PyO3.
|
|
17
|
+
|
|
18
|
+
It provides the hot-path primitives the Python layer calls into:
|
|
19
|
+
|
|
20
|
+
- **vector** — cosine similarity + top-k for memory retrieval
|
|
21
|
+
- **checkpoint** — framed (de)serialization of graph state
|
|
22
|
+
- **channels** — state-channel reducers (last-value / append / add)
|
|
23
|
+
- **scheduler** — BSP superstep planning for the orchestration engine
|
|
24
|
+
- **actors** — tamper-evident hash-chaining + cost aggregation for the audit log
|
|
25
|
+
|
|
26
|
+
Every function has a pure-Python fallback in `yaab._core`, so the SDK installs
|
|
27
|
+
and runs without this extension. Build it for the accelerated path:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
maturin develop -m yaab-core/Cargo.toml --release
|
|
31
|
+
```
|
|
32
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# yaab-core
|
|
2
|
+
|
|
3
|
+
The Rust performance core for [YAAB](../README.md), exposed to Python via PyO3.
|
|
4
|
+
|
|
5
|
+
It provides the hot-path primitives the Python layer calls into:
|
|
6
|
+
|
|
7
|
+
- **vector** — cosine similarity + top-k for memory retrieval
|
|
8
|
+
- **checkpoint** — framed (de)serialization of graph state
|
|
9
|
+
- **channels** — state-channel reducers (last-value / append / add)
|
|
10
|
+
- **scheduler** — BSP superstep planning for the orchestration engine
|
|
11
|
+
- **actors** — tamper-evident hash-chaining + cost aggregation for the audit log
|
|
12
|
+
|
|
13
|
+
Every function has a pure-Python fallback in `yaab._core`, so the SDK installs
|
|
14
|
+
and runs without this extension. Build it for the accelerated path:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
maturin develop -m yaab-core/Cargo.toml --release
|
|
18
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.5,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "yaab-core"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Rust performance core for the YAAB agent SDK."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [{ name = "YAAB contributors" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Rust",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.maturin]
|
|
20
|
+
# Pure Rust extension module: produces the importable `yaab_core` module.
|
|
21
|
+
module-name = "yaab_core"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//! Tamper-evident audit hashing and cost aggregation.
|
|
2
|
+
//!
|
|
3
|
+
//! The audit log is hash-chained: each entry's hash folds in the previous
|
|
4
|
+
//! entry's hash plus the current payload, so any retroactive edit breaks the
|
|
5
|
+
//! chain. Hashing the chain in Rust keeps the audit hot-path cheap.
|
|
6
|
+
|
|
7
|
+
use sha2::{Digest, Sha256};
|
|
8
|
+
|
|
9
|
+
/// Compute the chained hash for an audit entry: `sha256(prev_hash || payload)`.
|
|
10
|
+
pub fn hash_event(prev_hash: &str, payload: &str) -> String {
|
|
11
|
+
let mut hasher = Sha256::new();
|
|
12
|
+
hasher.update(prev_hash.as_bytes());
|
|
13
|
+
hasher.update(b"\x1f"); // unit separator, avoids boundary ambiguity
|
|
14
|
+
hasher.update(payload.as_bytes());
|
|
15
|
+
let digest = hasher.finalize();
|
|
16
|
+
let mut out = String::with_capacity(64);
|
|
17
|
+
for byte in digest {
|
|
18
|
+
out.push_str(&format!("{:02x}", byte));
|
|
19
|
+
}
|
|
20
|
+
out
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Verify a chain of `(payload, recorded_hash)` entries against a genesis hash.
|
|
24
|
+
/// Returns the index of the first broken link, or `None` if the chain is intact.
|
|
25
|
+
pub fn verify_chain(genesis: &str, entries: &[(String, String)]) -> Option<usize> {
|
|
26
|
+
let mut prev = genesis.to_string();
|
|
27
|
+
for (i, (payload, recorded)) in entries.iter().enumerate() {
|
|
28
|
+
let expected = hash_event(&prev, payload);
|
|
29
|
+
if &expected != recorded {
|
|
30
|
+
return Some(i);
|
|
31
|
+
}
|
|
32
|
+
prev = expected;
|
|
33
|
+
}
|
|
34
|
+
None
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Sum a set of per-call costs into a single total. Trivial today, but a
|
|
38
|
+
/// stable seam for moving cost aggregation fully into Rust later.
|
|
39
|
+
pub fn aggregate_cost(values: &[f64]) -> f64 {
|
|
40
|
+
values.iter().sum()
|
|
41
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
//! State channel reducers and whole-superstep state advancement.
|
|
2
|
+
//!
|
|
3
|
+
//! Channels define how a node's write to a key is merged into graph state.
|
|
4
|
+
//! The semantics mirror LangGraph channels: last-value (overwrite),
|
|
5
|
+
//! append (accumulate into a list), and add (numeric aggregation).
|
|
6
|
+
//!
|
|
7
|
+
//! `advance_superstep` applies an entire superstep's node updates against the
|
|
8
|
+
//! current state in a single call — the compute-bound part of graph
|
|
9
|
+
//! orchestration the Python engine can offload to Rust when the developer
|
|
10
|
+
//! opts into the native engine.
|
|
11
|
+
|
|
12
|
+
use serde_json::{Map, Value};
|
|
13
|
+
use std::collections::HashMap;
|
|
14
|
+
|
|
15
|
+
/// Reduce a single `current`/`update` pair at the `Value` level.
|
|
16
|
+
fn reduce_value(reducer: &str, current: &Value, update: Value) -> Value {
|
|
17
|
+
match reducer {
|
|
18
|
+
"append" => {
|
|
19
|
+
let mut list = match current {
|
|
20
|
+
Value::Array(items) => items.clone(),
|
|
21
|
+
Value::Null => Vec::new(),
|
|
22
|
+
other => vec![other.clone()],
|
|
23
|
+
};
|
|
24
|
+
match update {
|
|
25
|
+
Value::Array(items) => list.extend(items),
|
|
26
|
+
other => list.push(other),
|
|
27
|
+
}
|
|
28
|
+
Value::Array(list)
|
|
29
|
+
}
|
|
30
|
+
"add" => {
|
|
31
|
+
let a = current.as_f64().unwrap_or(0.0);
|
|
32
|
+
let b = update.as_f64().unwrap_or(0.0);
|
|
33
|
+
let sum = a + b;
|
|
34
|
+
if sum.fract() == 0.0 {
|
|
35
|
+
Value::from(sum as i64)
|
|
36
|
+
} else {
|
|
37
|
+
Value::from(sum)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// "last_value" and any unknown reducer overwrite.
|
|
41
|
+
_ => update,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Reduce `current` with `update` according to `reducer` (JSON-string API).
|
|
46
|
+
pub fn reduce(reducer: &str, current_json: &str, update_json: &str) -> Result<String, String> {
|
|
47
|
+
let update: Value = serde_json::from_str(update_json).map_err(|e| e.to_string())?;
|
|
48
|
+
let current: Value = serde_json::from_str(current_json).map_err(|e| e.to_string())?;
|
|
49
|
+
let result = reduce_value(reducer, ¤t, update);
|
|
50
|
+
serde_json::to_string(&result).map_err(|e| e.to_string())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Apply a whole superstep's node updates to `state` in one pass.
|
|
54
|
+
///
|
|
55
|
+
/// * `state_json` — the current graph state object (JSON object).
|
|
56
|
+
/// * `reducers_json` — a JSON object mapping channel key -> reducer name.
|
|
57
|
+
/// Keys absent from this map use last-value semantics.
|
|
58
|
+
/// * `updates_json` — a JSON array of update objects, one per node that ran in
|
|
59
|
+
/// the superstep, applied in order.
|
|
60
|
+
///
|
|
61
|
+
/// Returns the new state as a JSON object string. Doing the full fold in Rust
|
|
62
|
+
/// avoids one Python<->Rust round-trip per (key, node) pair.
|
|
63
|
+
pub fn advance_superstep(
|
|
64
|
+
state_json: &str,
|
|
65
|
+
reducers_json: &str,
|
|
66
|
+
updates_json: &str,
|
|
67
|
+
) -> Result<String, String> {
|
|
68
|
+
let mut state: Map<String, Value> =
|
|
69
|
+
match serde_json::from_str(state_json).map_err(|e| e.to_string())? {
|
|
70
|
+
Value::Object(m) => m,
|
|
71
|
+
_ => return Err("state must be a JSON object".to_string()),
|
|
72
|
+
};
|
|
73
|
+
let reducers: HashMap<String, String> =
|
|
74
|
+
serde_json::from_str(reducers_json).map_err(|e| e.to_string())?;
|
|
75
|
+
let updates: Vec<Value> = serde_json::from_str(updates_json).map_err(|e| e.to_string())?;
|
|
76
|
+
|
|
77
|
+
for update in updates {
|
|
78
|
+
let obj = match update {
|
|
79
|
+
Value::Object(m) => m,
|
|
80
|
+
Value::Null => continue, // a node may return no update
|
|
81
|
+
_ => return Err("each node update must be a JSON object or null".to_string()),
|
|
82
|
+
};
|
|
83
|
+
for (key, value) in obj {
|
|
84
|
+
let reducer = reducers
|
|
85
|
+
.get(&key)
|
|
86
|
+
.map(|s| s.as_str())
|
|
87
|
+
.unwrap_or("last_value");
|
|
88
|
+
let current = state.get(&key).cloned().unwrap_or(Value::Null);
|
|
89
|
+
state.insert(key, reduce_value(reducer, ¤t, value));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
serde_json::to_string(&Value::Object(state)).map_err(|e| e.to_string())
|
|
94
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//! Checkpoint (de)serialization for durable graph execution.
|
|
2
|
+
//!
|
|
3
|
+
//! State snapshots are framed with a small magic header + version byte so the
|
|
4
|
+
//! format can evolve without ambiguity. The payload is compact JSON; the Rust
|
|
5
|
+
//! path validates structure on the way in and out.
|
|
6
|
+
|
|
7
|
+
use serde_json::Value;
|
|
8
|
+
|
|
9
|
+
const MAGIC: &[u8] = b"YAAB";
|
|
10
|
+
const VERSION: u8 = 1;
|
|
11
|
+
|
|
12
|
+
/// Encode a JSON document (passed as a string) into a framed checkpoint blob.
|
|
13
|
+
pub fn encode(json_str: &str) -> Result<Vec<u8>, String> {
|
|
14
|
+
let value: Value = serde_json::from_str(json_str).map_err(|e| e.to_string())?;
|
|
15
|
+
let payload = serde_json::to_vec(&value).map_err(|e| e.to_string())?;
|
|
16
|
+
let mut out = Vec::with_capacity(payload.len() + 5);
|
|
17
|
+
out.extend_from_slice(MAGIC);
|
|
18
|
+
out.push(VERSION);
|
|
19
|
+
out.extend_from_slice(&payload);
|
|
20
|
+
Ok(out)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Decode a framed checkpoint blob back into a compact JSON string.
|
|
24
|
+
pub fn decode(blob: &[u8]) -> Result<String, String> {
|
|
25
|
+
if blob.len() < 5 || &blob[0..4] != MAGIC {
|
|
26
|
+
return Err("invalid checkpoint: bad magic header".to_string());
|
|
27
|
+
}
|
|
28
|
+
if blob[4] != VERSION {
|
|
29
|
+
return Err(format!("unsupported checkpoint version: {}", blob[4]));
|
|
30
|
+
}
|
|
31
|
+
let value: Value = serde_json::from_slice(&blob[5..]).map_err(|e| e.to_string())?;
|
|
32
|
+
serde_json::to_string(&value).map_err(|e| e.to_string())
|
|
33
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//! `yaab_core` — the Rust performance core for the YAAB agent SDK.
|
|
2
|
+
//!
|
|
3
|
+
//! This crate exposes a small, stable surface of hot-path primitives to
|
|
4
|
+
//! Python via PyO3: vector similarity for memory retrieval, framed checkpoint
|
|
5
|
+
//! (de)serialization for durable graph execution, channel reducers for graph
|
|
6
|
+
//! state, BSP superstep planning for the orchestration engine, and
|
|
7
|
+
//! tamper-evident hash-chaining for the audit log.
|
|
8
|
+
//!
|
|
9
|
+
//! Every function here has a pure-Python fallback in `yaab._core`, so the SDK
|
|
10
|
+
//! installs and runs even when this extension is unavailable. The Rust path is
|
|
11
|
+
//! an accelerator, never a hard dependency.
|
|
12
|
+
|
|
13
|
+
use pyo3::prelude::*;
|
|
14
|
+
|
|
15
|
+
mod actors;
|
|
16
|
+
mod channels;
|
|
17
|
+
mod checkpoint;
|
|
18
|
+
mod scheduler;
|
|
19
|
+
mod vector;
|
|
20
|
+
|
|
21
|
+
/// Cosine similarity between two equal-length vectors.
|
|
22
|
+
#[pyfunction]
|
|
23
|
+
fn cosine_similarity(a: Vec<f32>, b: Vec<f32>) -> f32 {
|
|
24
|
+
vector::cosine(&a, &b)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Indices and scores of the `k` rows most similar to `query`.
|
|
28
|
+
#[pyfunction]
|
|
29
|
+
fn top_k(query: Vec<f32>, matrix: Vec<Vec<f32>>, k: usize) -> Vec<(usize, f32)> {
|
|
30
|
+
vector::top_k(&query, &matrix, k)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Encode a JSON document string into a framed checkpoint blob.
|
|
34
|
+
#[pyfunction]
|
|
35
|
+
fn encode_checkpoint(json_str: &str) -> PyResult<Vec<u8>> {
|
|
36
|
+
checkpoint::encode(json_str).map_err(pyo3::exceptions::PyValueError::new_err)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Decode a framed checkpoint blob back into a JSON string.
|
|
40
|
+
#[pyfunction]
|
|
41
|
+
fn decode_checkpoint(blob: Vec<u8>) -> PyResult<String> {
|
|
42
|
+
checkpoint::decode(&blob).map_err(pyo3::exceptions::PyValueError::new_err)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Reduce `current` with `update` under the named channel reducer.
|
|
46
|
+
#[pyfunction]
|
|
47
|
+
fn reduce_channel(reducer: &str, current_json: &str, update_json: &str) -> PyResult<String> {
|
|
48
|
+
channels::reduce(reducer, current_json, update_json)
|
|
49
|
+
.map_err(pyo3::exceptions::PyValueError::new_err)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Apply a whole superstep's node updates to graph state in one call.
|
|
53
|
+
#[pyfunction]
|
|
54
|
+
fn advance_superstep(
|
|
55
|
+
state_json: &str,
|
|
56
|
+
reducers_json: &str,
|
|
57
|
+
updates_json: &str,
|
|
58
|
+
) -> PyResult<String> {
|
|
59
|
+
channels::advance_superstep(state_json, reducers_json, updates_json)
|
|
60
|
+
.map_err(pyo3::exceptions::PyValueError::new_err)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Plan BSP supersteps for the given nodes and edges.
|
|
64
|
+
#[pyfunction]
|
|
65
|
+
fn plan_supersteps(nodes: Vec<String>, edges: Vec<(String, String)>) -> Vec<Vec<String>> {
|
|
66
|
+
scheduler::plan(&nodes, &edges)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// Chained hash for an audit entry: `sha256(prev_hash || payload)`.
|
|
70
|
+
#[pyfunction]
|
|
71
|
+
fn hash_event(prev_hash: &str, payload: &str) -> String {
|
|
72
|
+
actors::hash_event(prev_hash, payload)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Verify a hash chain; returns the index of the first broken link or `None`.
|
|
76
|
+
#[pyfunction]
|
|
77
|
+
fn verify_chain(genesis: &str, entries: Vec<(String, String)>) -> Option<usize> {
|
|
78
|
+
actors::verify_chain(genesis, &entries)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// Sum per-call costs into a single total.
|
|
82
|
+
#[pyfunction]
|
|
83
|
+
fn aggregate_cost(values: Vec<f64>) -> f64 {
|
|
84
|
+
actors::aggregate_cost(&values)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#[pymodule]
|
|
88
|
+
fn yaab_core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
89
|
+
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
|
|
90
|
+
m.add("RUST", true)?;
|
|
91
|
+
m.add_function(wrap_pyfunction!(cosine_similarity, m)?)?;
|
|
92
|
+
m.add_function(wrap_pyfunction!(top_k, m)?)?;
|
|
93
|
+
m.add_function(wrap_pyfunction!(encode_checkpoint, m)?)?;
|
|
94
|
+
m.add_function(wrap_pyfunction!(decode_checkpoint, m)?)?;
|
|
95
|
+
m.add_function(wrap_pyfunction!(reduce_channel, m)?)?;
|
|
96
|
+
m.add_function(wrap_pyfunction!(advance_superstep, m)?)?;
|
|
97
|
+
m.add_function(wrap_pyfunction!(plan_supersteps, m)?)?;
|
|
98
|
+
m.add_function(wrap_pyfunction!(hash_event, m)?)?;
|
|
99
|
+
m.add_function(wrap_pyfunction!(verify_chain, m)?)?;
|
|
100
|
+
m.add_function(wrap_pyfunction!(aggregate_cost, m)?)?;
|
|
101
|
+
Ok(())
|
|
102
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//! Superstep scheduler (Bulk Synchronous Parallel).
|
|
2
|
+
//!
|
|
3
|
+
//! Given a node set and directed edges, group nodes into supersteps: each
|
|
4
|
+
//! superstep is a set of nodes with no unresolved upstream dependency, so all
|
|
5
|
+
//! nodes in a superstep can execute in parallel. This mirrors the Pregel /
|
|
6
|
+
//! Apache Beam execution model LangGraph is built on.
|
|
7
|
+
//!
|
|
8
|
+
//! Cycles are expected (graphs support loops), so any nodes that remain after
|
|
9
|
+
//! the acyclic layering are emitted as a final superstep rather than treated
|
|
10
|
+
//! as an error — the runtime drives cycles via conditional edges at run time.
|
|
11
|
+
|
|
12
|
+
use std::collections::{HashMap, HashSet, VecDeque};
|
|
13
|
+
|
|
14
|
+
/// Plan the supersteps for a graph.
|
|
15
|
+
pub fn plan(nodes: &[String], edges: &[(String, String)]) -> Vec<Vec<String>> {
|
|
16
|
+
let node_set: HashSet<&String> = nodes.iter().collect();
|
|
17
|
+
|
|
18
|
+
let mut indegree: HashMap<&String, usize> = nodes.iter().map(|n| (n, 0usize)).collect();
|
|
19
|
+
let mut adj: HashMap<&String, Vec<&String>> = HashMap::new();
|
|
20
|
+
for (src, dst) in edges {
|
|
21
|
+
if !node_set.contains(src) || !node_set.contains(dst) || src == dst {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
adj.entry(src).or_default().push(dst);
|
|
25
|
+
*indegree.entry(dst).or_insert(0) += 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let mut layers: Vec<Vec<String>> = Vec::new();
|
|
29
|
+
let mut ready: VecDeque<&String> = indegree
|
|
30
|
+
.iter()
|
|
31
|
+
.filter(|(_, &d)| d == 0)
|
|
32
|
+
.map(|(&n, _)| n)
|
|
33
|
+
.collect();
|
|
34
|
+
let mut visited: HashSet<&String> = HashSet::new();
|
|
35
|
+
|
|
36
|
+
while !ready.is_empty() {
|
|
37
|
+
let mut layer: Vec<String> = Vec::new();
|
|
38
|
+
let current: Vec<&String> = ready.drain(..).collect();
|
|
39
|
+
for node in ¤t {
|
|
40
|
+
if visited.contains(node) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
visited.insert(node);
|
|
44
|
+
layer.push((*node).clone());
|
|
45
|
+
}
|
|
46
|
+
layer.sort();
|
|
47
|
+
let mut next: Vec<&String> = Vec::new();
|
|
48
|
+
for node in ¤t {
|
|
49
|
+
if let Some(children) = adj.get(node) {
|
|
50
|
+
for child in children {
|
|
51
|
+
if let Some(d) = indegree.get_mut(*child) {
|
|
52
|
+
*d = d.saturating_sub(1);
|
|
53
|
+
if *d == 0 && !visited.contains(*child) {
|
|
54
|
+
next.push(child);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if !layer.is_empty() {
|
|
61
|
+
layers.push(layer);
|
|
62
|
+
}
|
|
63
|
+
ready.extend(next);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Any nodes left participate in a cycle; emit them as a trailing superstep.
|
|
67
|
+
let mut remaining: Vec<String> = nodes
|
|
68
|
+
.iter()
|
|
69
|
+
.filter(|n| !visited.contains(n))
|
|
70
|
+
.cloned()
|
|
71
|
+
.collect();
|
|
72
|
+
if !remaining.is_empty() {
|
|
73
|
+
remaining.sort();
|
|
74
|
+
layers.push(remaining);
|
|
75
|
+
}
|
|
76
|
+
layers
|
|
77
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//! Vector similarity operations used by `MemoryService` retrieval.
|
|
2
|
+
//!
|
|
3
|
+
//! These run outside the GIL on the Python side and operate on plain
|
|
4
|
+
//! `f32` slices, which keeps memory-retrieval cheap even for large stores.
|
|
5
|
+
|
|
6
|
+
/// Cosine similarity between two equal-length vectors.
|
|
7
|
+
///
|
|
8
|
+
/// Returns 0.0 for mismatched lengths or zero-magnitude vectors so callers
|
|
9
|
+
/// never have to special-case degenerate input.
|
|
10
|
+
pub fn cosine(a: &[f32], b: &[f32]) -> f32 {
|
|
11
|
+
if a.len() != b.len() || a.is_empty() {
|
|
12
|
+
return 0.0;
|
|
13
|
+
}
|
|
14
|
+
let mut dot = 0.0f32;
|
|
15
|
+
let mut na = 0.0f32;
|
|
16
|
+
let mut nb = 0.0f32;
|
|
17
|
+
for i in 0..a.len() {
|
|
18
|
+
dot += a[i] * b[i];
|
|
19
|
+
na += a[i] * a[i];
|
|
20
|
+
nb += b[i] * b[i];
|
|
21
|
+
}
|
|
22
|
+
if na == 0.0 || nb == 0.0 {
|
|
23
|
+
return 0.0;
|
|
24
|
+
}
|
|
25
|
+
dot / (na.sqrt() * nb.sqrt())
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Return the indices and scores of the `k` rows most similar to `query`,
|
|
29
|
+
/// sorted by descending cosine similarity.
|
|
30
|
+
pub fn top_k(query: &[f32], matrix: &[Vec<f32>], k: usize) -> Vec<(usize, f32)> {
|
|
31
|
+
let mut scored: Vec<(usize, f32)> = matrix
|
|
32
|
+
.iter()
|
|
33
|
+
.enumerate()
|
|
34
|
+
.map(|(i, row)| (i, cosine(query, row)))
|
|
35
|
+
.collect();
|
|
36
|
+
scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
|
|
37
|
+
scored.truncate(k);
|
|
38
|
+
scored
|
|
39
|
+
}
|