@flaxia/node 0.3.7 → 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 +43 -19
- package/dist/__tests__/consent-policy.test.d.ts +2 -0
- package/dist/__tests__/consent-policy.test.d.ts.map +1 -0
- package/dist/assets/VectorStoreEngine.js +140 -85
- package/dist/assets/ai-inference.js +173 -33
- package/dist/assets/container.js +368 -260
- package/dist/assets/dist.js +209 -0
- package/dist/assets/egress-guard.js +174 -0
- package/dist/assets/image-process.js +184 -19
- package/dist/assets/nudenet.js +2 -2
- package/dist/client/SignalingClient.d.ts +6 -0
- package/dist/client/SignalingClient.d.ts.map +1 -1
- package/dist/consent/ConsentUI.d.ts +44 -5
- package/dist/consent/ConsentUI.d.ts.map +1 -1
- package/dist/consent/notice.d.ts +29 -0
- package/dist/consent/notice.d.ts.map +1 -0
- package/dist/consent/storage.d.ts +91 -7
- package/dist/consent/storage.d.ts.map +1 -1
- package/dist/executor/WorkerPool.d.ts +35 -1
- package/dist/executor/WorkerPool.d.ts.map +1 -1
- package/dist/executor/__tests__/WorkerPool.swarm.test.d.ts +13 -0
- package/dist/executor/__tests__/WorkerPool.swarm.test.d.ts.map +1 -0
- package/dist/executor/__tests__/container-executor.test.d.ts +2 -0
- package/dist/executor/__tests__/container-executor.test.d.ts.map +1 -0
- package/dist/executor/__tests__/egress-guard.test.d.ts +2 -0
- package/dist/executor/__tests__/egress-guard.test.d.ts.map +1 -0
- package/dist/executor/__tests__/webgpuProbe.test.d.ts +2 -0
- package/dist/executor/__tests__/webgpuProbe.test.d.ts.map +1 -0
- package/dist/executor/container-executor.d.ts +49 -1
- package/dist/executor/container-executor.d.ts.map +1 -1
- package/dist/executor/egress-guard.d.ts +105 -0
- package/dist/executor/egress-guard.d.ts.map +1 -0
- package/dist/executor/memoryProbe.d.ts +25 -0
- package/dist/executor/memoryProbe.d.ts.map +1 -1
- package/dist/executor/throttle.d.ts +62 -4
- package/dist/executor/throttle.d.ts.map +1 -1
- package/dist/executor/webgpuProbe.d.ts +9 -0
- package/dist/executor/webgpuProbe.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +766 -230
- package/dist/swarm/__tests__/adapter.test.d.ts +2 -0
- package/dist/swarm/__tests__/adapter.test.d.ts.map +1 -0
- package/dist/swarm/__tests__/controller.test.d.ts +2 -0
- package/dist/swarm/__tests__/controller.test.d.ts.map +1 -0
- package/dist/swarm/__tests__/defects.test.d.ts +16 -0
- package/dist/swarm/__tests__/defects.test.d.ts.map +1 -0
- package/dist/swarm/__tests__/hardening.test.d.ts +2 -0
- package/dist/swarm/__tests__/hardening.test.d.ts.map +1 -0
- package/dist/swarm/__tests__/runtime.test.d.ts +2 -0
- package/dist/swarm/__tests__/runtime.test.d.ts.map +1 -0
- package/dist/swarm/__tests__/session.test.d.ts +2 -0
- package/dist/swarm/__tests__/session.test.d.ts.map +1 -0
- package/dist/swarm/adapter.d.ts +115 -0
- package/dist/swarm/adapter.d.ts.map +1 -0
- package/dist/swarm/controller.d.ts +87 -0
- package/dist/swarm/controller.d.ts.map +1 -0
- package/dist/swarm/messages.d.ts +41 -0
- package/dist/swarm/messages.d.ts.map +1 -0
- package/dist/swarm/runtime.d.ts +16 -0
- package/dist/swarm/runtime.d.ts.map +1 -0
- package/dist/swarm/session.d.ts +73 -0
- package/dist/swarm/session.d.ts.map +1 -0
- package/dist/swarm-engine.js +7477 -0
- package/dist/vector-store/HNSWIndex.d.ts +30 -0
- package/dist/vector-store/HNSWIndex.d.ts.map +1 -1
- package/dist/vector-store/VectorStoreEngine.d.ts +43 -0
- package/dist/vector-store/VectorStoreEngine.d.ts.map +1 -1
- package/dist/vector-store/__tests__/HNSWIndex.test.d.ts +2 -0
- package/dist/vector-store/__tests__/HNSWIndex.test.d.ts.map +1 -0
- package/dist/vector-store/__tests__/VectorStoreEngine.test.d.ts +2 -0
- package/dist/vector-store/__tests__/VectorStoreEngine.test.d.ts.map +1 -0
- package/dist/worker/__tests__/main.worker.abort.test.d.ts +2 -0
- package/dist/worker/__tests__/main.worker.abort.test.d.ts.map +1 -0
- package/dist/worker/__tests__/main.worker.control.test.d.ts +2 -0
- package/dist/worker/__tests__/main.worker.control.test.d.ts.map +1 -0
- package/dist/worker.js +643 -67
- package/dist/workloads/__tests__/nudenet.test.d.ts +2 -0
- package/dist/workloads/__tests__/nudenet.test.d.ts.map +1 -0
- package/dist/workloads/ai-inference.d.ts +2 -1
- package/dist/workloads/ai-inference.d.ts.map +1 -1
- package/dist/workloads/ai-model-registry.d.ts +72 -0
- package/dist/workloads/ai-model-registry.d.ts.map +1 -0
- package/dist/workloads/container.d.ts +1 -1
- package/dist/workloads/container.d.ts.map +1 -1
- package/dist/workloads/image-process.d.ts +27 -0
- package/dist/workloads/image-process.d.ts.map +1 -1
- package/dist/workloads/nudenet.d.ts +8 -0
- package/dist/workloads/nudenet.d.ts.map +1 -1
- package/package.json +3 -2
- package/dist/assets/memoryProbe.js +0 -28
- package/dist/client/WebRTCPeer.d.ts +0 -8
- package/dist/client/WebRTCPeer.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.test.d.ts","sourceRoot":"","sources":["../../../src/swarm/__tests__/adapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.test.d.ts","sourceRoot":"","sources":["../../../src/swarm/__tests__/controller.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for the swarm node runtime, written from the
|
|
3
|
+
* feature/swarm-inference quality review and kept after the fixes landed.
|
|
4
|
+
*
|
|
5
|
+
* Every test here encodes the behaviour we WANT. Do not weaken a test in this
|
|
6
|
+
* file to make the suite green — if one of these starts failing, the defect it
|
|
7
|
+
* documents has come back.
|
|
8
|
+
*
|
|
9
|
+
* H2 the engine / GPU device must be disposed when a session ends
|
|
10
|
+
* M1 `maxNewTokens` and the prompt are validated against the model
|
|
11
|
+
* M2 a coordinator `swarm-error` settles the local session
|
|
12
|
+
* M4 the host result conforms to `SwarmInferenceResult`
|
|
13
|
+
* M6 malformed or stray hidden-state frames are never trusted
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=defects.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defects.test.d.ts","sourceRoot":"","sources":["../../../src/swarm/__tests__/defects.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardening.test.d.ts","sourceRoot":"","sources":["../../../src/swarm/__tests__/hardening.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.test.d.ts","sourceRoot":"","sources":["../../../src/swarm/__tests__/runtime.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.test.d.ts","sourceRoot":"","sources":["../../../src/swarm/__tests__/session.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal surface of one Pooled (SwarmLLM) engine slice that a swarm session
|
|
3
|
+
* drives. It mirrors the methods `Qwen35Engine` exposes in the vendored engine
|
|
4
|
+
* (`vendor/pooled/engine/qwen35.js`) so the same adapter works for a host slice
|
|
5
|
+
* (`hasEmbed`/`hasHead`) and a middle slice.
|
|
6
|
+
*/
|
|
7
|
+
export interface SwarmEngineAdapter {
|
|
8
|
+
readonly dim: number;
|
|
9
|
+
readonly nc: number;
|
|
10
|
+
/** KV cache length of this slice; the session may not exceed it. */
|
|
11
|
+
readonly maxSeq?: number;
|
|
12
|
+
reset(): void;
|
|
13
|
+
/** Embed a single token into a hidden state (host slices only). */
|
|
14
|
+
embedRun(token: number, pos: number): Promise<Float32Array>;
|
|
15
|
+
/** Run a hidden state through this slice's layers. */
|
|
16
|
+
runHidden(hidden: Float32Array, pos: number): Promise<Float32Array>;
|
|
17
|
+
/** Final norm + LM head + logits (host slices only). */
|
|
18
|
+
headFromHidden(hidden: Float32Array): Promise<Float32Array>;
|
|
19
|
+
setHidden(hidden: Float32Array): void;
|
|
20
|
+
dispose(): void;
|
|
21
|
+
}
|
|
22
|
+
/** Config passed straight through to `Qwen35Engine.create`. */
|
|
23
|
+
export interface PooledEngineSliceConfig {
|
|
24
|
+
device: unknown;
|
|
25
|
+
meta: Record<string, unknown>;
|
|
26
|
+
layerRange: [number, number];
|
|
27
|
+
hasEmbed: boolean;
|
|
28
|
+
hasHead: boolean;
|
|
29
|
+
vocab: number;
|
|
30
|
+
maxSeq?: number;
|
|
31
|
+
batchCols?: number;
|
|
32
|
+
weights: unknown;
|
|
33
|
+
}
|
|
34
|
+
export interface RawPooledEngine {
|
|
35
|
+
dims: {
|
|
36
|
+
dim: number;
|
|
37
|
+
};
|
|
38
|
+
NC: number;
|
|
39
|
+
maxSeq?: number;
|
|
40
|
+
reset(): void;
|
|
41
|
+
embedRun(token: number, pos: number): Promise<Float32Array>;
|
|
42
|
+
runHidden(hidden: Float32Array, pos: number): Promise<Float32Array>;
|
|
43
|
+
headFromHidden(hidden: Float32Array): Promise<Float32Array>;
|
|
44
|
+
setHidden(hidden: Float32Array): void;
|
|
45
|
+
dispose?(): void;
|
|
46
|
+
}
|
|
47
|
+
export interface PooledTensorInfo {
|
|
48
|
+
shape: number[];
|
|
49
|
+
byteOffset: number;
|
|
50
|
+
byteLength: number;
|
|
51
|
+
ggmlType: number;
|
|
52
|
+
}
|
|
53
|
+
export interface PooledGguf {
|
|
54
|
+
meta: Record<string, unknown>;
|
|
55
|
+
tensors: Record<string, PooledTensorInfo>;
|
|
56
|
+
/** Set to stream Q4_0/Q8_0 matrices straight into GPU buffers. */
|
|
57
|
+
streamEntry?: (info: PooledTensorInfo) => Promise<unknown>;
|
|
58
|
+
}
|
|
59
|
+
export interface PooledTokenizer {
|
|
60
|
+
vocab: Record<string, number>;
|
|
61
|
+
encode(text: string): number[];
|
|
62
|
+
decode(ids: number[]): string;
|
|
63
|
+
}
|
|
64
|
+
export interface PooledEngineModule {
|
|
65
|
+
Qwen35Engine: {
|
|
66
|
+
create(config: PooledEngineSliceConfig): Promise<RawPooledEngine>;
|
|
67
|
+
};
|
|
68
|
+
parseGGUFHeader(buffer: ArrayBuffer, options?: {
|
|
69
|
+
skipTokenizer?: boolean;
|
|
70
|
+
}): PooledGguf;
|
|
71
|
+
qwen35Weights(gguf: PooledGguf, bytesOf: (info: PooledTensorInfo) => Promise<Uint8Array>, options: {
|
|
72
|
+
lo: number;
|
|
73
|
+
hi: number;
|
|
74
|
+
hasEmbed: boolean;
|
|
75
|
+
hasHead: boolean;
|
|
76
|
+
mtp?: boolean;
|
|
77
|
+
}, onProgress?: (bytes: number) => void, onEntry?: (entry: unknown, name: string) => void): Promise<unknown>;
|
|
78
|
+
tokenizerFromGGUF(meta: Record<string, unknown>): unknown;
|
|
79
|
+
makeTokenizer(vocab: unknown): PooledTokenizer;
|
|
80
|
+
argmax(logits: Float32Array): number;
|
|
81
|
+
f32ToF16(value: number): number;
|
|
82
|
+
f16ToF32(value: number): number;
|
|
83
|
+
/** Upload one converted entry with writeBuffer (large matrices cannot use mappedAtCreation). */
|
|
84
|
+
gpuUploadEntry(device: unknown, entry: unknown, keepCpu?: boolean): unknown;
|
|
85
|
+
/** Stream a Q4_0/Q8_0 tensor from a fetch Response straight into GPU buffers. */
|
|
86
|
+
streamEntryToGPU(device: unknown, info: PooledTensorInfo, openRange: (info: PooledTensorInfo) => Promise<Response>, options?: {
|
|
87
|
+
staging?: number;
|
|
88
|
+
}): Promise<unknown>;
|
|
89
|
+
GGML_EMBED: string;
|
|
90
|
+
}
|
|
91
|
+
export type EngineModuleImporter = (url: string) => Promise<unknown>;
|
|
92
|
+
export declare class SwarmEngineUnavailableError extends Error {
|
|
93
|
+
constructor(message: string);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Load the vendored engine module for a node's swarm slice. Fails fast with
|
|
97
|
+
* {@link SwarmEngineUnavailableError} when the context has no WebGPU adapter or
|
|
98
|
+
* the module does not look like the Pooled engine, so a node can report a clean
|
|
99
|
+
* capability failure instead of throwing deep inside an inference loop.
|
|
100
|
+
*
|
|
101
|
+
* The module URL is a runtime string (the engine ships as its own chunk), so the
|
|
102
|
+
* bundler must not try to resolve it statically; the injected importer keeps it
|
|
103
|
+
* testable without a browser.
|
|
104
|
+
*/
|
|
105
|
+
export declare function loadPooledEngineModule(moduleUrl: string, importer?: EngineModuleImporter): Promise<PooledEngineModule>;
|
|
106
|
+
/**
|
|
107
|
+
* Wrap a raw Pooled engine slice in the stable adapter surface.
|
|
108
|
+
*
|
|
109
|
+
* `dispose()` releases the slice's own buffers *and* the GPU device it was
|
|
110
|
+
* created on: the runtime requests a fresh device per session, so leaving it
|
|
111
|
+
* alive would keep the whole model resident in the GPU on a worker that is
|
|
112
|
+
* reused for the next task. Calling it twice is a no-op.
|
|
113
|
+
*/
|
|
114
|
+
export declare function wrapPooledEngine(engine: RawPooledEngine, device?: unknown): SwarmEngineAdapter;
|
|
115
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/swarm/adapter.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5D,sDAAsD;IACtD,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACpE,wDAAwD;IACxD,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5D,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IACtC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,+DAA+D;AAC/D,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5D,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACpE,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5D,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,IAAI,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC1C,kEAAkE;IAClE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE;QAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;KAAE,CAAC;IACpF,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,UAAU,CAAC;IACxF,aAAa,CACX,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,UAAU,CAAC,EACxD,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,EACvF,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAC/C,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IAC1D,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAAC;IAC/C,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,gGAAgG;IAChG,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5E,iFAAiF;IACjF,gBAAgB,CACd,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,gBAAgB,EACtB,SAAS,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,EACxD,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAErE,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAoB7B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,kBAAkB,CA4B9F"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type SwarmChainNode, type SwarmInitMessage, type SwarmMember, type SwarmSlice, type SwarmSliceMessage } from '@flaxia/sdk';
|
|
2
|
+
import type { SwarmEngineAdapter } from './adapter';
|
|
3
|
+
import { type SwarmSemantics } from './session';
|
|
4
|
+
import type { SwarmControlMessage } from './messages';
|
|
5
|
+
/**
|
|
6
|
+
* Model and tokenizer access the controller needs. `plan` runs on the host
|
|
7
|
+
* (it owns the metadata); `load` builds this node's engine slice. Both are
|
|
8
|
+
* injected so the controller is testable without a GPU or a model server.
|
|
9
|
+
*/
|
|
10
|
+
export interface SwarmRuntime {
|
|
11
|
+
plan(members: SwarmMember[], model: string): Promise<SwarmChainNode[]>;
|
|
12
|
+
load(slice: SwarmSlice, model: string): Promise<{
|
|
13
|
+
engine: SwarmEngineAdapter;
|
|
14
|
+
semantics: SwarmSemantics;
|
|
15
|
+
warm?: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export interface SwarmControllerOptions {
|
|
19
|
+
/** First control message: `swarm-init` on the host, `swarm-slice` on a worker. */
|
|
20
|
+
initial: SwarmInitMessage | SwarmSliceMessage;
|
|
21
|
+
runtime: SwarmRuntime;
|
|
22
|
+
/** Worker -> main: a JSON control message for the coordinator. */
|
|
23
|
+
sendControl: (message: unknown) => void;
|
|
24
|
+
/** Worker -> main: a binary hidden-state frame for the coordinator. */
|
|
25
|
+
sendFrame: (frame: ArrayBuffer) => void;
|
|
26
|
+
/** Worker -> main: a streamed token. */
|
|
27
|
+
emitToken: (token: string) => unknown;
|
|
28
|
+
onDone: (result: unknown) => void;
|
|
29
|
+
onError: (error: string) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Drives one node's part of a swarm session inside the Web Worker: the host
|
|
33
|
+
* plans the split then generates; a worker loads its slice and forwards frames.
|
|
34
|
+
* All I/O is through injected callbacks, so `self` never leaks in here.
|
|
35
|
+
*
|
|
36
|
+
* The controller owns one engine for the life of the session and disposes it
|
|
37
|
+
* when the session ends either way: a worker is reused for the next task, so an
|
|
38
|
+
* undisposed slice would keep its GPU buffers (and device) resident.
|
|
39
|
+
*/
|
|
40
|
+
export declare class SwarmController {
|
|
41
|
+
private readonly options;
|
|
42
|
+
private frameHandler;
|
|
43
|
+
private engine;
|
|
44
|
+
private semantics;
|
|
45
|
+
private sessionId;
|
|
46
|
+
private model;
|
|
47
|
+
private chainLength;
|
|
48
|
+
private chain;
|
|
49
|
+
private slice;
|
|
50
|
+
private hostWarm;
|
|
51
|
+
private prompt;
|
|
52
|
+
private maxNewTokens;
|
|
53
|
+
private sliceLoaded;
|
|
54
|
+
private generationStarted;
|
|
55
|
+
private settled;
|
|
56
|
+
constructor(options: SwarmControllerOptions);
|
|
57
|
+
start(): Promise<void>;
|
|
58
|
+
handleControl(message: SwarmControlMessage): Promise<void>;
|
|
59
|
+
handleFrame(frame: ArrayBuffer): void;
|
|
60
|
+
/**
|
|
61
|
+
* Stop the session because the coordinator settled the task elsewhere. Exactly
|
|
62
|
+
* like a failure this disposes the engine, so the GPU buffers and device do
|
|
63
|
+
* not stay resident on a device the scheduler has already moved on from.
|
|
64
|
+
*/
|
|
65
|
+
abort(reason: string): void;
|
|
66
|
+
private link;
|
|
67
|
+
/**
|
|
68
|
+
* A slice message decides how much of the model this device materialises, so
|
|
69
|
+
* validate it even when it arrived through a path that already validated it:
|
|
70
|
+
* a hostile host must not be able to ask for a slice like `end = 1e9` (whole
|
|
71
|
+
* model in GPU memory) or claim a role/position that is not its own.
|
|
72
|
+
*/
|
|
73
|
+
private validateSlice;
|
|
74
|
+
private loadSlice;
|
|
75
|
+
private startHost;
|
|
76
|
+
/**
|
|
77
|
+
* The chain this node planned (or, for a node that never saw the plan, its own
|
|
78
|
+
* slice) as `SwarmInferenceNodeInfo`s. Only this node can report its own
|
|
79
|
+
* warmth, so `warm` is filled in for the host entry alone.
|
|
80
|
+
*/
|
|
81
|
+
private nodeInfos;
|
|
82
|
+
/** Release the engine (and its GPU device) exactly once, at session end. */
|
|
83
|
+
private teardown;
|
|
84
|
+
private finish;
|
|
85
|
+
private fail;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/swarm/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EAEnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAsE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACpH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACvE,IAAI,CACF,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,MAAM,EAAE,kBAAkB,CAAC;QAAC,SAAS,EAAE,cAAc,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACvF;AAED,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IAC9C,OAAO,EAAE,YAAY,CAAC;IACtB,kEAAkE;IAClE,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,uEAAuE;IACvE,SAAS,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,wCAAwC;IACxC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAgBd,OAAO,CAAC,QAAQ,CAAC,OAAO;IAfpC,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,YAAY,CAAO;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,OAAO,CAAS;gBAEK,OAAO,EAAE,sBAAsB;IAEtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BtB,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhE,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAKrC;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,CAAC,IAAI;IASZ;;;;;OAKG;IACH,OAAO,CAAC,aAAa;YAuBP,SAAS;YA6BT,SAAS;IAkDvB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAcjB,4EAA4E;IAC5E,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,IAAI;CAMb"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type SwarmErrorMessage, type SwarmInitMessage, type SwarmSliceMessage, type SwarmStartMessage } from '@flaxia/sdk';
|
|
2
|
+
/** Swarm control messages the coordinator sends to a node. */
|
|
3
|
+
export type SwarmControlMessage = SwarmInitMessage | SwarmSliceMessage | SwarmStartMessage | SwarmErrorMessage;
|
|
4
|
+
/** Messages from the main thread into the swarm worker. */
|
|
5
|
+
export type SwarmWorkerInbound = {
|
|
6
|
+
id: string;
|
|
7
|
+
type: 'swarm-control';
|
|
8
|
+
message: SwarmControlMessage;
|
|
9
|
+
} | {
|
|
10
|
+
id: string;
|
|
11
|
+
type: 'swarm-frame';
|
|
12
|
+
frame: ArrayBuffer;
|
|
13
|
+
};
|
|
14
|
+
/** Messages the swarm worker sends back to the main thread. */
|
|
15
|
+
export type SwarmWorkerOutbound = {
|
|
16
|
+
id: string;
|
|
17
|
+
type: 'swarm-message';
|
|
18
|
+
message: unknown;
|
|
19
|
+
} | {
|
|
20
|
+
id: string;
|
|
21
|
+
type: 'swarm-frame';
|
|
22
|
+
frame: ArrayBuffer;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Structural validation for every control message a node accepts. The SDK
|
|
26
|
+
* validators check the fields the node relies on (slice bounds, chain position,
|
|
27
|
+
* hop keys, ids), so a malformed or hostile message is dropped before it can
|
|
28
|
+
* reach the engine or the layer loader.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isSwarmControlMessage(value: unknown): value is SwarmControlMessage;
|
|
31
|
+
export declare function isSwarmFrameMessage(value: unknown): value is {
|
|
32
|
+
id: string;
|
|
33
|
+
type: 'swarm-frame';
|
|
34
|
+
frame: ArrayBuffer;
|
|
35
|
+
};
|
|
36
|
+
export declare function isSwarmControlEnvelope(value: unknown): value is {
|
|
37
|
+
id: string;
|
|
38
|
+
type: 'swarm-control';
|
|
39
|
+
message: SwarmControlMessage;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/swarm/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAErB,8DAA8D;AAC9D,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAE/G,2DAA2D;AAC3D,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAA;CAAE,GACnE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAE5D,+DAA+D;AAC/D,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACvD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAE5D;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAOlF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAIpH;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAA;CAAE,CAI9E"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type PooledEngineModule, type PooledTokenizer } from './adapter';
|
|
2
|
+
import type { SwarmRuntime } from './controller';
|
|
3
|
+
import { type SwarmSemantics } from './session';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a task payload's `model` to the GGUF it loads.
|
|
6
|
+
*
|
|
7
|
+
* The payload is unvalidated API input: it must only ever match the registry
|
|
8
|
+
* above, never a caller-supplied URL, or every opted-in node could be made to
|
|
9
|
+
* range-fetch an arbitrary host of the submitter's choosing.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveModelUrl(model: string): string;
|
|
12
|
+
/** Range-fetch bytes, caching complete entries so a second start skips the network. */
|
|
13
|
+
export declare function rangeFetch(url: string, lo: number, hi: number): Promise<Uint8Array>;
|
|
14
|
+
export declare function buildSemantics(mod: PooledEngineModule, tok: PooledTokenizer): SwarmSemantics;
|
|
15
|
+
export declare function createSwarmRuntime(): SwarmRuntime;
|
|
16
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/swarm/runtime.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,kBAAkB,EAGvB,KAAK,eAAe,EACrB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AA4BjE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrD;AAuBD,uFAAuF;AACvF,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAMzF;AAoFD,wBAAgB,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,eAAe,GAAG,cAAc,CA0C5F;AAED,wBAAgB,kBAAkB,IAAI,YAAY,CA2FjD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { SwarmEngineAdapter } from './adapter';
|
|
2
|
+
/**
|
|
3
|
+
* One swarm node's hidden-state link to the coordinator. Frames are opaque
|
|
4
|
+
* ArrayBuffers (see `encodeSwarmFrame`); the coordinator relays them around the
|
|
5
|
+
* chain without touching the payload.
|
|
6
|
+
*/
|
|
7
|
+
export interface SwarmFrameLink {
|
|
8
|
+
send(frame: ArrayBuffer): void;
|
|
9
|
+
onFrame(handler: (frame: ArrayBuffer) => void): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The bits that depend on the model/tokenizer, injected so the session logic
|
|
13
|
+
* stays testable without a GPU: how to pack a hidden state to bytes, how to
|
|
14
|
+
* unpack it, and how to pick the next token from logits.
|
|
15
|
+
*/
|
|
16
|
+
export interface SwarmSemantics {
|
|
17
|
+
/** Pack a hidden state to the f16 payload bytes carried by a frame. */
|
|
18
|
+
packHidden(hidden: Float32Array): Uint8Array;
|
|
19
|
+
/** Unpack a frame payload back to a hidden state. */
|
|
20
|
+
unpackHidden(payload: Uint8Array): Float32Array;
|
|
21
|
+
argmax(logits: Float32Array): number;
|
|
22
|
+
/** Tokenize the prompt (host only). */
|
|
23
|
+
encodePrompt?(text: string | string[]): number[];
|
|
24
|
+
/** Detokenize generated ids (host only). */
|
|
25
|
+
decodeTokens?(ids: number[]): string;
|
|
26
|
+
/** Stop as soon as this id is sampled (host only). */
|
|
27
|
+
eosIds?: Set<number>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Ceiling on prompt + generated tokens a session may use. Enough context for
|
|
31
|
+
* chat prompts; KV memory grows with this on the attention layers the node
|
|
32
|
+
* holds, so keep it modest for crowd devices. A model with a smaller native
|
|
33
|
+
* context lowers it further when the slice is created.
|
|
34
|
+
*/
|
|
35
|
+
export declare const SESSION_MAX_SEQ = 4096;
|
|
36
|
+
export interface SwarmHostOptions {
|
|
37
|
+
/** Number of nodes in the chain, including this host. */
|
|
38
|
+
chainLength: number;
|
|
39
|
+
/** Prompt token ids; the host tokenizes before starting. */
|
|
40
|
+
promptTokens: number[];
|
|
41
|
+
/** Maximum tokens to generate. */
|
|
42
|
+
maxNewTokens: number;
|
|
43
|
+
engine: SwarmEngineAdapter;
|
|
44
|
+
link: SwarmFrameLink;
|
|
45
|
+
semantics: SwarmSemantics;
|
|
46
|
+
/** Stop as soon as this id is sampled (it is not emitted). */
|
|
47
|
+
eosIds?: Set<number>;
|
|
48
|
+
/** Called with each generated token id as it is produced. */
|
|
49
|
+
onToken?: (id: number) => unknown;
|
|
50
|
+
}
|
|
51
|
+
export interface SwarmHostResult {
|
|
52
|
+
tokens: number[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Drive a generation across the chain. The host embeds each token, ships the
|
|
56
|
+
* hidden state around the ring (each worker runs its slice and forwards), then
|
|
57
|
+
* runs the LM head and samples locally. With `chainLength === 1` the host is the
|
|
58
|
+
* whole model and no frames are sent.
|
|
59
|
+
*/
|
|
60
|
+
export declare function runSwarmHost(options: SwarmHostOptions): Promise<SwarmHostResult>;
|
|
61
|
+
export interface SwarmWorkerOptions {
|
|
62
|
+
engine: SwarmEngineAdapter;
|
|
63
|
+
link: SwarmFrameLink;
|
|
64
|
+
semantics: SwarmSemantics;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Run one middle slice until the host's stop frame arrives. Each hidden frame
|
|
68
|
+
* is unpacked, run through this node's layers and forwarded around the ring.
|
|
69
|
+
* Resolves once the stop frame has been forwarded; rejects if a frame cannot be
|
|
70
|
+
* decoded, so a corrupt stream fails the session instead of hanging it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function runSwarmWorker(options: SwarmWorkerOptions): Promise<void>;
|
|
73
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/swarm/session.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAEpD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAAC;CACtD;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAAC;IAC7C,qDAAqD;IACrD,YAAY,CAAC,OAAO,EAAE,UAAU,GAAG,YAAY,CAAC;IAChD,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;IACrC,uCAAuC;IACvC,YAAY,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACjD,4CAA4C;IAC5C,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACrC,sDAAsD;IACtD,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,OAAO,CAAC;AA2EpC,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,cAAc,CAAC;IAC1B,8DAA8D;IAC9D,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAyDtF;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCzE"}
|