@hviana/sema 0.5.8 → 0.6.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/AGENTS.md +43 -4
- package/DATASETS.md +160 -0
- package/README.md +12 -0
- package/dist/example/train_base/cache.d.ts +35 -0
- package/dist/example/train_base/cache.js +211 -0
- package/dist/example/train_base/config.d.ts +21 -0
- package/dist/example/train_base/config.js +94 -0
- package/dist/example/train_base/corpora/aya.d.ts +19 -0
- package/dist/example/train_base/corpora/aya.js +76 -0
- package/dist/example/train_base/corpora/converted-parquet.d.ts +14 -0
- package/dist/example/train_base/corpora/converted-parquet.js +44 -0
- package/dist/example/train_base/corpora/genknow.d.ts +14 -0
- package/dist/example/train_base/corpora/genknow.js +83 -0
- package/dist/example/train_base/corpora/index.d.ts +29 -0
- package/dist/example/train_base/corpora/index.js +81 -0
- package/dist/example/train_base/corpora/massive.d.ts +7 -0
- package/dist/example/train_base/corpora/massive.js +98 -0
- package/dist/example/train_base/corpora/oasst2.d.ts +52 -0
- package/dist/example/train_base/corpora/oasst2.js +120 -0
- package/dist/example/train_base/corpora/smolsent.d.ts +23 -0
- package/dist/example/train_base/corpora/smolsent.js +156 -0
- package/dist/example/train_base/corpora/soda.d.ts +12 -0
- package/dist/example/train_base/corpora/soda.js +113 -0
- package/dist/example/train_base/corpora/taskmaster.d.ts +15 -0
- package/dist/example/train_base/corpora/taskmaster.js +144 -0
- package/dist/example/train_base/corpora/wiki2.d.ts +23 -0
- package/dist/example/train_base/corpora/wiki2.js +132 -0
- package/dist/example/train_base/corpus.d.ts +88 -0
- package/dist/example/train_base/corpus.js +65 -0
- package/dist/example/train_base/discovery.d.ts +48 -0
- package/dist/example/train_base/discovery.js +143 -0
- package/dist/example/train_base/http.d.ts +82 -0
- package/dist/example/train_base/http.js +219 -0
- package/dist/example/train_base/items.d.ts +46 -0
- package/dist/example/train_base/items.js +98 -0
- package/dist/example/train_base/main.d.ts +4 -0
- package/dist/example/train_base/main.js +207 -0
- package/dist/example/train_base/progress.d.ts +34 -0
- package/dist/example/train_base/progress.js +114 -0
- package/dist/example/train_base/readers.d.ts +125 -0
- package/dist/example/train_base/readers.js +391 -0
- package/dist/example/train_base/runtime.d.ts +115 -0
- package/dist/example/train_base/runtime.js +637 -0
- package/dist/example/train_base/stage.d.ts +3 -0
- package/dist/example/train_base/stage.js +246 -0
- package/dist/example/train_base/ui.d.ts +88 -0
- package/dist/example/train_base/ui.js +272 -0
- package/dist/src/geometry.d.ts +20 -0
- package/dist/src/geometry.js +22 -0
- package/dist/src/mind/attention.d.ts +6 -0
- package/dist/src/mind/attention.js +44 -4
- package/dist/src/mind/learning.js +134 -50
- package/dist/src/mind/mechanisms/cast.js +45 -1
- package/dist/src/mind/mind.d.ts +7 -2
- package/dist/src/mind/mind.js +15 -3
- package/dist/src/mind/reasoning.js +59 -5
- package/dist/src/mind/recognition.js +29 -3
- package/dist/src/mind/traverse.d.ts +16 -0
- package/dist/src/mind/traverse.js +18 -0
- package/dist/src/store-sqlite.d.ts +4 -0
- package/dist/src/store-sqlite.js +47 -0
- package/dist/src/store.d.ts +7 -0
- package/example/train_base/cache.ts +251 -0
- package/example/train_base/config.ts +128 -0
- package/example/train_base/corpora/aya.ts +106 -0
- package/example/train_base/corpora/converted-parquet.ts +64 -0
- package/example/train_base/corpora/genknow.ts +114 -0
- package/example/train_base/corpora/index.ts +88 -0
- package/example/train_base/corpora/massive.ts +111 -0
- package/example/train_base/corpora/oasst2.ts +163 -0
- package/example/train_base/corpora/smolsent.ts +203 -0
- package/example/train_base/corpora/soda.ts +130 -0
- package/example/train_base/corpora/taskmaster.ts +217 -0
- package/example/train_base/corpora/wiki2.ts +190 -0
- package/example/train_base/corpus.ts +150 -0
- package/example/train_base/discovery.ts +203 -0
- package/example/train_base/http.ts +284 -0
- package/example/train_base/items.ts +118 -0
- package/example/train_base/main.ts +240 -0
- package/example/train_base/progress.ts +149 -0
- package/example/train_base/readers.ts +505 -0
- package/example/train_base/runtime.ts +894 -0
- package/example/train_base/stage.ts +276 -0
- package/example/train_base/ui.ts +333 -0
- package/jsr.json +1 -1
- package/package.json +2 -4
- package/src/geometry.ts +23 -0
- package/src/mind/attention.ts +54 -1
- package/src/mind/learning.ts +137 -43
- package/src/mind/mechanisms/cast.ts +48 -1
- package/src/mind/mind.ts +13 -2
- package/src/mind/reasoning.ts +64 -5
- package/src/mind/recognition.ts +29 -3
- package/src/mind/traverse.ts +19 -0
- package/src/store-sqlite.ts +53 -0
- package/src/store.ts +28 -0
- package/test/13-conversation.test.mjs +1 -1
- package/test/29-counterfactual.test.mjs +43 -6
- package/test/77-company-saturation.test.mjs +302 -0
- package/test/78-atom-hub-recognition-cliff.test.mjs +135 -0
- package/test/84-composed-answer-honesty.test.mjs +137 -0
- package/test/85-answered-directly.test.mjs +126 -0
- package/test/86-cast-voices-committed.test.mjs +164 -0
- package/test/87-codominant-commitment.test.mjs +250 -0
- package/test/88-dependency-footprint.test.mjs +99 -0
- package/dist/example/train_base.d.ts +0 -93
- package/dist/example/train_base.js +0 -2269
- package/example/train_base.ts +0 -2735
package/AGENTS.md
CHANGED
|
@@ -768,10 +768,12 @@ memoises perceive+intern of repeated inputs and routes through the same
|
|
|
768
768
|
`store.commit()` at checkpoints; run `compactContentIndex` /
|
|
769
769
|
`repairContentIndex` post-training if eviction was heavy, and
|
|
770
770
|
`mind.buildCanonIndex()` if queries will carry a canonicalizer (2.9). See
|
|
771
|
-
`example/train_base
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
771
|
+
`example/train_base/` — a folder, entry `main.ts`: the run context lives in
|
|
772
|
+
`runtime.ts`, the one per-corpus loop in `stage.ts`, and each corpus (knobs, row
|
|
773
|
+
adapter, stage descriptor) in `corpora/<name>.ts`. Profiling note: the first
|
|
774
|
+
`resonate` after a big ingest pays the pending index flush; the dominant
|
|
775
|
+
query-side ANN cost is connector pre-resolution (bounded by recognised-site
|
|
776
|
+
count — don't add another loop over site pairs).
|
|
775
777
|
|
|
776
778
|
---
|
|
777
779
|
|
|
@@ -810,3 +812,40 @@ PolyForm Noncommercial 1.0.0 with separate commercial licensing (see
|
|
|
810
812
|
vendor code under licenses incompatible with dual distribution, and do not add
|
|
811
813
|
runtime dependencies casually — the near-zero-dependency footprint is a product
|
|
812
814
|
feature.
|
|
815
|
+
|
|
816
|
+
**The library has NO runtime dependencies at all**, and that is now pinned by
|
|
817
|
+
`test/88-dependency-footprint.test.mjs`: the built `dist/src` may import only
|
|
818
|
+
`node:` builtins and relative paths, `package.json` may declare no
|
|
819
|
+
`dependencies`, and the published entry points may not reach outside `dist/src`.
|
|
820
|
+
The rule an EXAMPLE follows is different and looser — it may use what it needs,
|
|
821
|
+
as a **dev** dependency, loaded **lazily** so it is a requirement only of the
|
|
822
|
+
code path that uses it. `example/train_base` is the reference: `hyparquet` (+
|
|
823
|
+
its Snappy codec) is the sole third-party package in this repository, it is
|
|
824
|
+
dev-only, and `readers.ts` resolves it by dynamic import the first time a
|
|
825
|
+
Parquet corpus is actually read — so a curriculum with no Parquet stage runs
|
|
826
|
+
with the package absent. It used to sit in `dependencies`, which installed a
|
|
827
|
+
Parquet reader on every consumer of Sema for the sake of one example; that is
|
|
828
|
+
the mistake the suite exists to catch.
|
|
829
|
+
|
|
830
|
+
**Training corpora are governed by the same rule, and more strictly.** Sema is
|
|
831
|
+
non-parametric: a trained store retains its training text VERBATIM (read any
|
|
832
|
+
content node back and the original sentence comes out). A store is therefore a
|
|
833
|
+
redistribution of its corpora, not a derived model, and every upstream licence
|
|
834
|
+
applies to it in full. Two consequences:
|
|
835
|
+
|
|
836
|
+
- A corpus carrying a **NonCommercial** term cannot enter a trainer — it
|
|
837
|
+
conflicts with the commercial licence tier.
|
|
838
|
+
- A corpus carrying a **ShareAlike** term cannot enter a trainer — its copyleft
|
|
839
|
+
would attach to the distributed store.
|
|
840
|
+
|
|
841
|
+
Check both against **what the corpus was built from**, not only the repository's
|
|
842
|
+
license tag: a dataset assembled out of Wikipedia prose and published under
|
|
843
|
+
Apache-2.0 still carries CC BY-SA on that prose. Where a corpus has a clean
|
|
844
|
+
layer and a contaminated one, ingest only the clean layer.
|
|
845
|
+
|
|
846
|
+
Sema's own license does **not** extend over corpus content inside a store, and
|
|
847
|
+
cannot: CC BY 4.0 §2(a)(5)(B) forbids applying terms that restrict what the
|
|
848
|
+
license permits. The engine is what PolyForm protects. Per-corpus attribution,
|
|
849
|
+
the required modification statement, and the current allow/deny list live in
|
|
850
|
+
[DATASETS.md](DATASETS.md) — update it in the same change that touches a
|
|
851
|
+
trainer's corpus set.
|
package/DATASETS.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Training corpora — provenance, licensing, and attribution
|
|
2
|
+
|
|
3
|
+
This file is the attribution notice for every corpus Sema is trained on, and the
|
|
4
|
+
licensing statement for the **trained memory files** that training produces.
|
|
5
|
+
|
|
6
|
+
It is a required companion to any distributed Sema store. If you publish or ship
|
|
7
|
+
`*.sqlite` / `*.content.vec` / `*.halo.vec`, ship this file with them.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Why a trained store is not a weight file
|
|
12
|
+
|
|
13
|
+
Sema is non-parametric. Training is deposition, not gradient descent: source
|
|
14
|
+
text is segmented and content-addressed, and the **bytes are retained**. Reading
|
|
15
|
+
a node returns the original text:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
#15709469 → "Kohei Uchimura from Japan holds the record for the most World
|
|
19
|
+
Championship medals won by a male gymnast, with a total of 21 medals."
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
A trained store is therefore a database that contains its training corpora in
|
|
23
|
+
recoverable form. Distributing one **is** distributing those corpora, and every
|
|
24
|
+
upstream licence applies in full. The "it's only model weights, the text isn't
|
|
25
|
+
really in there" argument is not available to Sema, by design.
|
|
26
|
+
|
|
27
|
+
Two consequences follow, and both are load-bearing:
|
|
28
|
+
|
|
29
|
+
1. **A corpus whose licence forbids commercial use cannot enter the store**,
|
|
30
|
+
because Sema is offered under a paid commercial licence as well as
|
|
31
|
+
[PolyForm Noncommercial](LICENSE.md).
|
|
32
|
+
2. **A corpus under a ShareAlike licence cannot enter the store**, because its
|
|
33
|
+
copyleft would attach to the distributed artifact.
|
|
34
|
+
|
|
35
|
+
Both rules are stated in [AGENTS.md](AGENTS.md) §6 and must be checked before
|
|
36
|
+
any corpus is added to a trainer.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. How a distributed store is licensed
|
|
41
|
+
|
|
42
|
+
A trained Sema store has two layers, licensed separately. Conflating them is a
|
|
43
|
+
licence violation in one direction or the other.
|
|
44
|
+
|
|
45
|
+
| Layer | What it covers | Licence |
|
|
46
|
+
| :------------------------------ | :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------ |
|
|
47
|
+
| **Sema itself** | The algorithms, geometry, deduction engine, indexes, file formats, and all code that produced the store | [PolyForm Noncommercial 1.0.0](LICENSE.md), with a separate [commercial licence](COMMERCIAL-LICENSE.md) |
|
|
48
|
+
| **Corpus content in the store** | The retained training text and anything derived from it | Each corpus's own upstream licence, listed in §3 |
|
|
49
|
+
|
|
50
|
+
**Sema's source licence is not extended over the corpus content, and cannot
|
|
51
|
+
be.** CC BY 4.0 §2(a)(5)(B) forbids applying legal terms that restrict a
|
|
52
|
+
recipient from doing what the licence permits — so the noncommercial term cannot
|
|
53
|
+
be applied to CC BY text sitting inside the store. What the noncommercial term
|
|
54
|
+
protects is the engine, which is the part that is actually ours.
|
|
55
|
+
|
|
56
|
+
**Modification statement** (required by CC BY 4.0 §3(a)(1)(B)): all corpus text
|
|
57
|
+
in a Sema store has been modified. It is segmented at content-defined
|
|
58
|
+
boundaries, re-encoded, deduplicated by content address, and interleaved with
|
|
59
|
+
text from other sources. It is not presented as a faithful reproduction of any
|
|
60
|
+
upstream dataset, and no endorsement by any upstream author is implied.
|
|
61
|
+
|
|
62
|
+
**Apache-2.0 obligations**: corpora marked Apache-2.0 below require the licence
|
|
63
|
+
text and any upstream `NOTICE` to travel with the distribution, and require
|
|
64
|
+
changes to be stated. The modification statement above satisfies the latter.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 3. Corpora
|
|
69
|
+
|
|
70
|
+
### 3.1 In use
|
|
71
|
+
|
|
72
|
+
| Corpus | Licence | Attribution | Notes |
|
|
73
|
+
| :------------------------------------------------------------------------------------------------------- | :---------------------------------------------------- | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
74
|
+
| [google/smol](https://huggingface.co/datasets/google/smol) (SmolSent) | CC BY 4.0 | Google LLC | Translation pairs |
|
|
75
|
+
| [CohereLabs/aya_dataset](https://huggingface.co/datasets/CohereLabs/aya_dataset) | Apache-2.0 | Cohere For AI | Human-written prompt/completion |
|
|
76
|
+
| [OpenAssistant/oasst2](https://huggingface.co/datasets/OpenAssistant/oasst2) | Apache-2.0 | LAION / OpenAssistant contributors | Human-authored dialogue — see §5 |
|
|
77
|
+
| [Taskmaster-1/2/3/4](https://github.com/google-research-datasets/Taskmaster) | CC BY 4.0 | Google LLC | Task-oriented dialogue. Only `utterances[].text` is ingested; the `instructions` / `scenario` / `vertical` fields are never read |
|
|
78
|
+
| [2WikiMultihopQA](https://huggingface.co/datasets/xanhho/2WikiMultihopQA) — **`evidences` triples only** | Apache-2.0 (repo); triples originate in Wikidata, CC0 | Ho et al.; Wikidata contributors | Only the `evidences` column is ingested. The `context` column (Wikipedia prose, CC BY-SA) is **never read** — see §4. The `question`/`answer` columns are also never deposited, for a capability reason rather than a licence one: they memorise instead of composing |
|
|
79
|
+
| [allenai/soda](https://huggingface.co/datasets/allenai/soda) | CC BY 4.0 | Allen Institute for AI | Social dialogue. Only the `dialogue` column is ingested; `narrative` / `literal` / `head` / `relation` / `tail` are never read. Model-generated provenance — see §5 |
|
|
80
|
+
| [AmazonScience/massive](https://huggingface.co/datasets/AmazonScience/massive) | CC BY 4.0 | Amazon Science | Short multilingual intents. Only the `utt` column is ingested; the slot-annotated `annot_utt` is never read. **Disabled by default** on capability grounds (not licence) — see `MASSIVE` in `example/train_base/corpora/massive.ts` |
|
|
81
|
+
|
|
82
|
+
### 3.2 Excluded, and why
|
|
83
|
+
|
|
84
|
+
| Corpus | Reason |
|
|
85
|
+
| :---------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| **MuskumPillerum/General-Knowledge** | **No licence at all.** The HF repo carries no licence tag and no licence in its card; an earlier header in `example/train_base.ts` (now `corpora/genknow.ts`) claimed MIT without support. Its own card states it "contains a subset of the alpaca dataset", and Alpaca is CC BY-NC 4.0 — **NonCommercial**, incompatible with Sema's commercial licence. See §6. |
|
|
87
|
+
| **PAWS** | Google's own grant is maximally permissive ("may be freely used for any purpose"), but PAWS-Wiki sentences derive from Wikipedia (CC BY-**SA**) and PAWS-QQP from Quora question pairs under Quora's terms. Because Sema retains text verbatim, the upstream terms would attach to the distributed store. Excluded despite strong measured fit. |
|
|
88
|
+
| **Schema-Guided Dialogue (SGD/dstc8)**, **HotpotQA**, **MuSiQue** | CC BY-SA 4.0 — ShareAlike conflicts with dual distribution. |
|
|
89
|
+
| **2WikiMultihopQA passages** | Wikipedia prose, CC BY-SA. The repo's Apache-2.0 tag does not relicense the text it was built from. Only the Wikidata-derived `evidences` triples are ingested. |
|
|
90
|
+
| **Alpaca** and derivatives | CC BY-NC 4.0, and generated from OpenAI model outputs. |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### 3.3 What each stage actually deposits
|
|
95
|
+
|
|
96
|
+
A corpus's licence applies to what is ingested, and every stage ingests a strict
|
|
97
|
+
subset of its source. This is the authoritative list.
|
|
98
|
+
|
|
99
|
+
| Stage | Columns/fields read | Deposit shape |
|
|
100
|
+
| :----------------------- | :---------------------------------------- | :-------------------------------------------------------- |
|
|
101
|
+
| SmolSent | `src`, `trg` | one `src → trg` (foreign → English) episode per row |
|
|
102
|
+
| Aya | `inputs`, `targets` | one question → answer episode |
|
|
103
|
+
| oasst2 | message `text` along the best-ranked path | cumulative-context walk |
|
|
104
|
+
| Taskmaster | `utterances[].text` | cumulative-context walk over speaker-merged turns |
|
|
105
|
+
| 2Wiki | `evidences` | per triple: a relation fact and a bare-subject pivot fact |
|
|
106
|
+
| SODA | `dialogue`, `speakers` | cumulative-context walk over speaker-merged turns |
|
|
107
|
+
| MASSIVE (off by default) | `utt` | one bare experience |
|
|
108
|
+
|
|
109
|
+
Everything else in those sources — Taskmaster's `instructions`/`scenario`,
|
|
110
|
+
2Wiki's `context`/`question`/`answer`, SODA's `narrative`/`literal`/`head`/
|
|
111
|
+
`relation`/`tail`, MASSIVE's `annot_utt` — is **not read** and therefore not
|
|
112
|
+
distributed in a trained store.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 4. The rule that decides these cases
|
|
117
|
+
|
|
118
|
+
**A repository's licence tag does not relicense the material the repository was
|
|
119
|
+
built from.** A dataset assembled out of Wikipedia prose and published under
|
|
120
|
+
Apache-2.0 still carries Wikipedia's ShareAlike terms on that prose. Because
|
|
121
|
+
Sema stores text verbatim, Sema inherits the _upstream_ terms, not the
|
|
122
|
+
repackager's.
|
|
123
|
+
|
|
124
|
+
So the check for any candidate corpus is two questions, not one:
|
|
125
|
+
|
|
126
|
+
1. What licence does the repository carry?
|
|
127
|
+
2. **What was it built from, and what licence does that carry?**
|
|
128
|
+
|
|
129
|
+
Where a corpus has a clean layer and a contaminated one, take the clean layer
|
|
130
|
+
only — as with 2Wiki's Wikidata triples (CC0) versus its Wikipedia passages (CC
|
|
131
|
+
BY-SA).
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 5. Disclosures
|
|
136
|
+
|
|
137
|
+
**Model-generated provenance.** `allenai/soda` is licensed CC BY 4.0 but was
|
|
138
|
+
distilled from OpenAI GPT-3.5 outputs. The licence is clean; the provenance is
|
|
139
|
+
disclosed here so downstream users can make their own assessment.
|
|
140
|
+
|
|
141
|
+
**Personal data.** `OpenAssistant/oasst2` is human-authored content contributed
|
|
142
|
+
by identifiable volunteers, and Sema retains it verbatim in a redistributable
|
|
143
|
+
artifact. Erasure requests against a content-addressed store are not
|
|
144
|
+
straightforward. Anyone distributing a Sema store trained on human-contributed
|
|
145
|
+
dialogue should account for this.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 6. Status of previously published stores
|
|
150
|
+
|
|
151
|
+
Stores published before this file was written — including those under
|
|
152
|
+
[hviana/sema-trained-v1](https://huggingface.co/buckets/hviana/sema-trained-v1)
|
|
153
|
+
— were trained with the `MuskumPillerum/General-Knowledge` stage enabled (37,623
|
|
154
|
+
rows), whose licence status is described in §3.2. Those artifacts should be
|
|
155
|
+
treated as **not redistributable** until retrained without that stage.
|
|
156
|
+
|
|
157
|
+
The stage is now **disabled by default** in
|
|
158
|
+
`example/train_base/corpora/genknow.ts` (`GENKNOW=0`). The adapter code remains
|
|
159
|
+
so the stage can be re-enabled for local experiments; a store trained with
|
|
160
|
+
`GENKNOW=1` must not be distributed.
|
package/README.md
CHANGED
|
@@ -320,6 +320,7 @@ start talking — no install, no runtime, no API key.
|
|
|
320
320
|
| 🛠️ **[AGENTS.md](AGENTS.md)** | The development manual: repo layout, build/test, internals, invariants, and recipes for extending the system. |
|
|
321
321
|
| 🎓 **[CITATION.cff](CITATION.cff)** | How to cite Sema in academic work. |
|
|
322
322
|
| ⚖️ **[LICENSE.md](LICENSE.md)** | PolyForm Noncommercial License 1.0.0. |
|
|
323
|
+
| 📚 **[DATASETS.md](DATASETS.md)** | Training corpora: provenance, per-corpus attribution, and how a trained memory file is licensed. |
|
|
323
324
|
| 💼 **[COMMERCIAL-LICENSE.md](COMMERCIAL-LICENSE.md)** | Commercial licensing terms and contact. |
|
|
324
325
|
| 🤗 **[Trained examples](https://huggingface.co/buckets/hviana/sema-trained-v1)** | Pre-trained memory files you can download and use directly. |
|
|
325
326
|
| 💿 **[Binary examples](https://huggingface.co/buckets/hviana/sema-binary-examples)** | Ready-to-run web chat apps for Windows, Mac, and Linux — one file, no install. |
|
|
@@ -350,6 +351,17 @@ start talking — no install, no runtime, no API key.
|
|
|
350
351
|
> **[TRADEMARKS.md](TRADEMARKS.md)** — the **Sema** name, logos, and brand are
|
|
351
352
|
> _not_ covered by the source license.
|
|
352
353
|
|
|
354
|
+
> [!NOTE]
|
|
355
|
+
> **Trained memory files are licensed in two layers.** Sema is non-parametric,
|
|
356
|
+
> so a trained store retains its training text verbatim — it is a database that
|
|
357
|
+
> contains its corpora, not a weight file. The **engine** (algorithms, geometry,
|
|
358
|
+
> deduction, indexes, formats, and all code) is under PolyForm Noncommercial
|
|
359
|
+
> with a separate commercial license. The **corpus content inside a store**
|
|
360
|
+
> stays under each corpus's own upstream license, and Sema's license is not
|
|
361
|
+
> extended over it. If you distribute a store, ship
|
|
362
|
+
> **[DATASETS.md](DATASETS.md)** with it — it carries the attribution and
|
|
363
|
+
> modification statement those licenses require.
|
|
364
|
+
|
|
353
365
|
<div align="center">
|
|
354
366
|
|
|
355
367
|
**Respecting these terms funds the research that makes work like this
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Delete every orphaned "<file>.part" in the cache, returning how many were
|
|
2
|
+
* removed and the bytes they held.
|
|
3
|
+
*
|
|
4
|
+
* A .part file at rest is by definition the debris of a download that never
|
|
5
|
+
* finished — the rename that promotes one is the last step of `downloadFile`,
|
|
6
|
+
* so a live .part exists only while THIS process is writing it. Sweeping at
|
|
7
|
+
* startup is therefore safe, and it is load-bearing rather than cosmetic:
|
|
8
|
+
* `cacheSize` deliberately counts .part files (an in-flight download really
|
|
9
|
+
* does occupy the disk), so debris left by a killed run consumes ceiling
|
|
10
|
+
* budget that nothing would ever free, and `ensureCacheRoom` would wait for
|
|
11
|
+
* room that cannot appear.
|
|
12
|
+
*
|
|
13
|
+
* The one assumption is that a cache directory belongs to ONE run at a time.
|
|
14
|
+
* That was already true — two trainers sharing CACHE_DIR would write the same
|
|
15
|
+
* .part path — so this adds no constraint that did not exist. */
|
|
16
|
+
export declare function sweepPartials(): {
|
|
17
|
+
files: number;
|
|
18
|
+
bytes: number;
|
|
19
|
+
};
|
|
20
|
+
/** Total bytes currently held in the cache directory — INCLUDING any .part
|
|
21
|
+
* file, because an in-flight download occupies the disk like any other file.
|
|
22
|
+
* Orphaned ones are removed by {@link sweepPartials} at startup. */
|
|
23
|
+
export declare function cacheSize(): number;
|
|
24
|
+
/** Block until there is room for a file of `fileBytes` under the ceiling.
|
|
25
|
+
* A single file larger than the whole ceiling can never "fit", so we let it
|
|
26
|
+
* through (it is deleted right after processing) rather than wait forever. */
|
|
27
|
+
export declare function ensureCacheRoom(fileBytes: number, signal: AbortSignal, warn?: (msg: string) => void, maxWaitMs?: number): Promise<void>;
|
|
28
|
+
export interface DownloadOptions {
|
|
29
|
+
signal: AbortSignal;
|
|
30
|
+
tries: number;
|
|
31
|
+
onFail?: (attempt: number, err: Error) => void;
|
|
32
|
+
onProgress?: (done: number, total: number) => void;
|
|
33
|
+
}
|
|
34
|
+
/** Stream `url` to `destPath`, atomically and with backpressure. */
|
|
35
|
+
export declare function downloadFile(url: string, destPath: string, opts: DownloadOptions): Promise<void>;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// train_base/cache.ts — the durable disk cache and the download sink.
|
|
2
|
+
//
|
|
3
|
+
// This is the ONE place the trainer needs Node rather than the web platform:
|
|
4
|
+
// every other byte in the pipeline moves through fetch, WHATWG streams,
|
|
5
|
+
// DecompressionStream, TextDecoderStream and Blob, but writing a file is the
|
|
6
|
+
// single capability the web platform does not expose. So the sink below wraps a
|
|
7
|
+
// raw fs descriptor, and nothing else here does.
|
|
8
|
+
//
|
|
9
|
+
// Two invariants the rest of the trainer relies on:
|
|
10
|
+
// • ATOMIC — a download streams to "<file>.part", is fsync'd, then renamed
|
|
11
|
+
// into place. A file at its final path is, by construction, complete, so an
|
|
12
|
+
// interrupted download can never be mistaken for a cached one.
|
|
13
|
+
// • BOUNDED — a download blocks under the MAX_CACHE_GB ceiling, and a fully
|
|
14
|
+
// processed file is deleted by its caller immediately.
|
|
15
|
+
import { CACHE_DIR, CACHE_WAIT_MS, MAX_CACHE_BYTES, PART_SUFFIX, } from "./config.js";
|
|
16
|
+
import { httpError, retry, waitMs } from "./http.js";
|
|
17
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, renameSync, statSync, unlinkSync, writeSync, } from "node:fs";
|
|
18
|
+
import { basename, join } from "node:path";
|
|
19
|
+
/** Delete every orphaned "<file>.part" in the cache, returning how many were
|
|
20
|
+
* removed and the bytes they held.
|
|
21
|
+
*
|
|
22
|
+
* A .part file at rest is by definition the debris of a download that never
|
|
23
|
+
* finished — the rename that promotes one is the last step of `downloadFile`,
|
|
24
|
+
* so a live .part exists only while THIS process is writing it. Sweeping at
|
|
25
|
+
* startup is therefore safe, and it is load-bearing rather than cosmetic:
|
|
26
|
+
* `cacheSize` deliberately counts .part files (an in-flight download really
|
|
27
|
+
* does occupy the disk), so debris left by a killed run consumes ceiling
|
|
28
|
+
* budget that nothing would ever free, and `ensureCacheRoom` would wait for
|
|
29
|
+
* room that cannot appear.
|
|
30
|
+
*
|
|
31
|
+
* The one assumption is that a cache directory belongs to ONE run at a time.
|
|
32
|
+
* That was already true — two trainers sharing CACHE_DIR would write the same
|
|
33
|
+
* .part path — so this adds no constraint that did not exist. */
|
|
34
|
+
export function sweepPartials() {
|
|
35
|
+
const out = { files: 0, bytes: 0 };
|
|
36
|
+
if (!existsSync(CACHE_DIR))
|
|
37
|
+
return out;
|
|
38
|
+
for (const name of readdirSync(CACHE_DIR)) {
|
|
39
|
+
if (!name.endsWith(PART_SUFFIX))
|
|
40
|
+
continue;
|
|
41
|
+
const p = join(CACHE_DIR, name);
|
|
42
|
+
try {
|
|
43
|
+
const size = statSync(p).size;
|
|
44
|
+
unlinkSync(p);
|
|
45
|
+
out.files++;
|
|
46
|
+
out.bytes += size;
|
|
47
|
+
}
|
|
48
|
+
catch { /* raced with another delete — nothing to reclaim */ }
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/** Total bytes currently held in the cache directory — INCLUDING any .part
|
|
53
|
+
* file, because an in-flight download occupies the disk like any other file.
|
|
54
|
+
* Orphaned ones are removed by {@link sweepPartials} at startup. */
|
|
55
|
+
export function cacheSize() {
|
|
56
|
+
if (!existsSync(CACHE_DIR))
|
|
57
|
+
return 0;
|
|
58
|
+
let total = 0;
|
|
59
|
+
for (const name of readdirSync(CACHE_DIR)) {
|
|
60
|
+
try {
|
|
61
|
+
total += statSync(join(CACHE_DIR, name)).size;
|
|
62
|
+
}
|
|
63
|
+
catch { /* raced with a delete */ }
|
|
64
|
+
}
|
|
65
|
+
return total;
|
|
66
|
+
}
|
|
67
|
+
/** Block until there is room for a file of `fileBytes` under the ceiling.
|
|
68
|
+
* A single file larger than the whole ceiling can never "fit", so we let it
|
|
69
|
+
* through (it is deleted right after processing) rather than wait forever. */
|
|
70
|
+
export async function ensureCacheRoom(fileBytes, signal, warn, maxWaitMs = CACHE_WAIT_MS) {
|
|
71
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
72
|
+
if (fileBytes >= MAX_CACHE_BYTES)
|
|
73
|
+
return;
|
|
74
|
+
let warned = false;
|
|
75
|
+
const until = Date.now() + maxWaitMs;
|
|
76
|
+
// Stop waiting the moment a shutdown is requested — the abort signal unblocks
|
|
77
|
+
// a long cache-full wait so Ctrl+C is never swallowed by the ceiling.
|
|
78
|
+
while (!signal.aborted && cacheSize() + fileBytes > MAX_CACHE_BYTES) {
|
|
79
|
+
// BOUNDED. Room appears when this run consumes and deletes a file, so a
|
|
80
|
+
// cache already over the ceiling with nothing left to consume — stale files
|
|
81
|
+
// from another run, a ceiling set below one corpus — would otherwise wait
|
|
82
|
+
// for room that cannot arrive, forever, after a single warning line.
|
|
83
|
+
if (Date.now() >= until) {
|
|
84
|
+
throw new Error(`cache still full after ${Math.round(maxWaitMs / 60_000)} min ` +
|
|
85
|
+
`(${(cacheSize() / 1e9).toFixed(1)} GB of a ` +
|
|
86
|
+
`${(MAX_CACHE_BYTES / 1e9).toFixed(0)} GB ceiling) — raise ` +
|
|
87
|
+
`MAX_CACHE_GB or clear ${CACHE_DIR}`);
|
|
88
|
+
}
|
|
89
|
+
if (!warned) {
|
|
90
|
+
warn?.(`cache at ${(MAX_CACHE_BYTES / 1e9).toFixed(0)} GB ceiling — waiting for room…`);
|
|
91
|
+
warned = true;
|
|
92
|
+
}
|
|
93
|
+
await waitMs(5_000, signal);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Stream `url` to `destPath`, atomically and with backpressure. */
|
|
97
|
+
export async function downloadFile(url, destPath, opts) {
|
|
98
|
+
const partPath = destPath + PART_SUFFIX;
|
|
99
|
+
await retry(`download ${basename(destPath)}`, async () => {
|
|
100
|
+
// Abort promptly on shutdown rather than waiting out a slow socket.
|
|
101
|
+
if (opts.signal.aborted) {
|
|
102
|
+
const e = new Error("aborted");
|
|
103
|
+
e.fatal = true;
|
|
104
|
+
throw e;
|
|
105
|
+
}
|
|
106
|
+
const res = await fetch(url, { signal: opts.signal });
|
|
107
|
+
if (!res.ok)
|
|
108
|
+
throw httpError(res);
|
|
109
|
+
if (!res.body)
|
|
110
|
+
throw new Error("empty response body");
|
|
111
|
+
// `content-length` describes the bytes ON THE WIRE. When the server
|
|
112
|
+
// applied a content-coding, fetch hands us the DECODED body, so the
|
|
113
|
+
// header no longer describes what gets written to disk and the integrity
|
|
114
|
+
// guard below must not use it. Measured: raw.githubusercontent.com sends
|
|
115
|
+
// `content-encoding: gzip` with content-length 110,928 for a file that
|
|
116
|
+
// decodes to 1,607,931 bytes — a size check against that rejects every
|
|
117
|
+
// healthy download. (The bug stayed latent because Hugging Face sends
|
|
118
|
+
// `content-encoding: br` and NO content-length, leaving total = 0, which
|
|
119
|
+
// already disables the guard.)
|
|
120
|
+
const encoding = (res.headers.get("content-encoding") ?? "").trim()
|
|
121
|
+
.toLowerCase();
|
|
122
|
+
const decoded = encoding !== "" && encoding !== "identity";
|
|
123
|
+
const total = decoded
|
|
124
|
+
? 0
|
|
125
|
+
: Number(res.headers.get("content-length")) || 0;
|
|
126
|
+
let done = 0;
|
|
127
|
+
// Stream straight to a ".part" sibling using pure WHATWG streams. A
|
|
128
|
+
// TransformStream meters progress; pipeTo into a WritableStream gives REAL
|
|
129
|
+
// backpressure natively — the sink's write() returns a promise the
|
|
130
|
+
// readable side awaits, so a fast server can never outrun the disk (no
|
|
131
|
+
// whole-file heap buffering). The sink wraps a single raw fs descriptor
|
|
132
|
+
// (the one capability the web platform lacks); writing to disk is the only
|
|
133
|
+
// Node operation in the whole pipeline. The final, valid file only ever
|
|
134
|
+
// appears via the atomic rename below, so a crash mid-transfer can never
|
|
135
|
+
// leave a truncated file at the real path.
|
|
136
|
+
const meter = new TransformStream({
|
|
137
|
+
transform(chunk, controller) {
|
|
138
|
+
done += chunk.length;
|
|
139
|
+
opts.onProgress?.(done, total);
|
|
140
|
+
controller.enqueue(chunk);
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
const fd = openSync(partPath, "w");
|
|
144
|
+
let closed = false;
|
|
145
|
+
const closeFd = () => {
|
|
146
|
+
if (closed)
|
|
147
|
+
return;
|
|
148
|
+
closed = true;
|
|
149
|
+
try {
|
|
150
|
+
closeSync(fd);
|
|
151
|
+
}
|
|
152
|
+
catch { /* already closed */ }
|
|
153
|
+
};
|
|
154
|
+
const sink = new WritableStream({
|
|
155
|
+
write(chunk) {
|
|
156
|
+
// writeSync drains the whole chunk before returning, so the readable
|
|
157
|
+
// side is paused for exactly as long as the disk needs — backpressure.
|
|
158
|
+
let off = 0;
|
|
159
|
+
while (off < chunk.length) {
|
|
160
|
+
off += writeSync(fd, chunk, off, chunk.length - off);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
close() {
|
|
164
|
+
fsyncSync(fd); // durable bytes before the rename promotes them
|
|
165
|
+
closeFd();
|
|
166
|
+
},
|
|
167
|
+
abort() {
|
|
168
|
+
closeFd();
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
try {
|
|
172
|
+
await res.body.pipeThrough(meter).pipeTo(sink, {
|
|
173
|
+
signal: opts.signal,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
// pipeTo's abort() ran the sink's abort() (closing the descriptor); if
|
|
178
|
+
// it didn't (a non-abort throw), make sure the descriptor is not leaked.
|
|
179
|
+
closeFd();
|
|
180
|
+
try {
|
|
181
|
+
unlinkSync(partPath);
|
|
182
|
+
}
|
|
183
|
+
catch { /* best effort */ }
|
|
184
|
+
throw e;
|
|
185
|
+
}
|
|
186
|
+
// Optional integrity guard: when the server advertised a size FOR THE
|
|
187
|
+
// BYTES WE WRITE (see the content-encoding note above — `total` is 0 for
|
|
188
|
+
// a decoded body, which disables this), a complete file must match it. A
|
|
189
|
+
// short read (silent truncation) is retried rather than promoted, so the
|
|
190
|
+
// parser never sees a partial file.
|
|
191
|
+
try {
|
|
192
|
+
const got = statSync(partPath).size;
|
|
193
|
+
if (total > 0 && got !== total) {
|
|
194
|
+
try {
|
|
195
|
+
unlinkSync(partPath);
|
|
196
|
+
}
|
|
197
|
+
catch { /* best effort */ }
|
|
198
|
+
throw new Error(`size mismatch: got ${got}, expected ${total}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
if (e instanceof Error && e.message.startsWith("size mismatch")) {
|
|
203
|
+
throw e;
|
|
204
|
+
}
|
|
205
|
+
// statSync failure is non-fatal here; the rename below will surface it.
|
|
206
|
+
}
|
|
207
|
+
// Atomic publish: rename is atomic within a filesystem, so the final path
|
|
208
|
+
// flips from "absent" to "complete" in one step — never an in-between.
|
|
209
|
+
renameSync(partPath, destPath);
|
|
210
|
+
}, opts.tries, { signal: opts.signal, onFail: opts.onFail });
|
|
211
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Read an environment variable, or `d` when it is unset. */
|
|
2
|
+
export declare const env: (k: string, d: string) => string;
|
|
3
|
+
export declare const DB_PATH: string;
|
|
4
|
+
export declare const D: number;
|
|
5
|
+
export declare const SEED: number;
|
|
6
|
+
export declare const CHECKPOINT_BYTES: number;
|
|
7
|
+
export declare const PARQUET_BATCH_BYTES: number;
|
|
8
|
+
export declare const LOCAL_PATH: string;
|
|
9
|
+
export declare const CACHE_DIR: string;
|
|
10
|
+
export declare const MAX_CACHE_BYTES: number;
|
|
11
|
+
export declare const PROGRESS_MS: number;
|
|
12
|
+
export declare const INDEX_MAINTENANCE: boolean;
|
|
13
|
+
export declare const DOWNLOAD_TRIES = 5;
|
|
14
|
+
export declare const PART_SUFFIX = ".part";
|
|
15
|
+
export declare const INFER_TIMEOUT_MS: number;
|
|
16
|
+
export declare const STALL_MS: number;
|
|
17
|
+
export declare const CACHE_WAIT_MS: number;
|
|
18
|
+
export declare const VECTOR_CACHE_MB: number;
|
|
19
|
+
export declare const SQLITE_CACHE_MB: number;
|
|
20
|
+
export declare const MAX_MB: number;
|
|
21
|
+
export declare const MAX_BYTES: number;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// train_base/config.ts — RUN-LEVEL configuration, all from the environment.
|
|
2
|
+
//
|
|
3
|
+
// Only knobs that describe the RUN live here: the store, the checkpoint cadence,
|
|
4
|
+
// the cache ceiling, the read budgets, the caps. A knob that describes ONE
|
|
5
|
+
// CORPUS (which pairs of SmolSent, how many SODA dialogues, how long an Aya
|
|
6
|
+
// field may be) belongs next to that corpus's adapter, together with the
|
|
7
|
+
// evidence that fixed its default — see AGENTS.md §2.16: a comment carries the
|
|
8
|
+
// constraint, and a constraint is only readable beside the code it constrains.
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
/** Read an environment variable, or `d` when it is unset. */
|
|
11
|
+
export const env = (k, d) => process.env[k] ?? d;
|
|
12
|
+
export const DB_PATH = env("DB_PATH", "sema"); // → {DB_PATH}.sqlite
|
|
13
|
+
export const D = Number(env("D", "1024"));
|
|
14
|
+
export const SEED = Number(env("SEED", "7"));
|
|
15
|
+
// Checkpoint cadence is measured in LEARNED CONTENT, not deposits: a snapshot
|
|
16
|
+
// every CHECKPOINT_MB megabytes of trained UTF-8 content (decimal MB, matching
|
|
17
|
+
// the bytes() helper). A floor of 1 MB: a zero/NaN value must not make every
|
|
18
|
+
// deposit checkpoint, nor silently disable checkpointing. The tail (a run that
|
|
19
|
+
// learns less than one interval, or the remainder past the last interval) is
|
|
20
|
+
// always saved by finish() at exit — a complete point.
|
|
21
|
+
export const CHECKPOINT_BYTES = Math.max(1_000_000, Math.floor(Number(env("CHECKPOINT_MB", "100")) * 1_000_000) || 100_000_000);
|
|
22
|
+
// Target size of ONE materialised Parquet read, in uncompressed source bytes.
|
|
23
|
+
// A row-GROUP is a layout choice made by whoever wrote the file, not a memory
|
|
24
|
+
// budget: Aya ships 203 groups of 1,000 rows (~1 MB each), while SODA ships ONE
|
|
25
|
+
// group of 1,191,582 rows (1.19 GB uncompressed) and 2Wiki ONE of 167,454
|
|
26
|
+
// (666 MB). Reading "exactly one row-group" is therefore safe for the first and
|
|
27
|
+
// fatal for the others, so reads are sized in BYTES instead — see
|
|
28
|
+
// `parquetBatchRows`. Materialised JS objects cost several times their source
|
|
29
|
+
// bytes, hence a default well under available memory.
|
|
30
|
+
export const PARQUET_BATCH_BYTES = Math.max(1_000_000, Math.floor(Number(env("PARQUET_BATCH_MB", "32")) * 1_000_000) || 32_000_000);
|
|
31
|
+
export const LOCAL_PATH = env("LOCAL_PATH", ""); // train from a local dir
|
|
32
|
+
export const CACHE_DIR = env("CACHE_DIR", join(process.cwd(), "cache"));
|
|
33
|
+
export const MAX_CACHE_BYTES = Number(env("MAX_CACHE_GB", "100")) * 1e9;
|
|
34
|
+
export const PROGRESS_MS = Number(env("PROGRESS_MS", "250")); // panel cadence
|
|
35
|
+
// Index maintenance at checkpoints: compact (remove garbage), repair (fill
|
|
36
|
+
// gaps), then refresh the canonical-form index (equivalence-class resolution —
|
|
37
|
+
// src/canon.ts). All three are idempotent batch operations (the canon build is
|
|
38
|
+
// additionally incremental via the store's `canon.upto` cursor);
|
|
39
|
+
// INDEX_MAINTENANCE=0 disables.
|
|
40
|
+
export const INDEX_MAINTENANCE = env("INDEX_MAINTENANCE", "1") !== "0";
|
|
41
|
+
export const DOWNLOAD_TRIES = 5;
|
|
42
|
+
// In-progress downloads are written to a sibling "<dest>.part" and atomically
|
|
43
|
+
// renamed into place only after the bytes are fully flushed to disk. The cache
|
|
44
|
+
// invariant is therefore absolute: a file at its final path is, by definition,
|
|
45
|
+
// complete. Partial transfers (a crash, a kill, a dropped socket) leave only a
|
|
46
|
+
// .part file, which is never fed to the parser and is swept at startup by
|
|
47
|
+
// cache.ts's sweepPartials() — without which the debris would consume cache
|
|
48
|
+
// ceiling that nothing frees.
|
|
49
|
+
export const PART_SUFFIX = ".part";
|
|
50
|
+
// The checkpoint recall is a best-effort diagnostic — it must NEVER stall
|
|
51
|
+
// training. We bound it so a slow/large store cannot freeze the deposit loop.
|
|
52
|
+
export const INFER_TIMEOUT_MS = Number(env("INFER_TIMEOUT_MS", "15000"));
|
|
53
|
+
// How long the run may make NO progress before it gives up and exits non-zero.
|
|
54
|
+
//
|
|
55
|
+
// A long training run's worst failure is not a crash — a crash resumes. It is a
|
|
56
|
+
// HANG: the uncaught-exception handler deliberately swallows dropped-connection
|
|
57
|
+
// errors so a long run survives them, and the keep-alive timer deliberately
|
|
58
|
+
// holds the process open; together, an error that escapes and leaves an await
|
|
59
|
+
// unsettled produces a live process that will never do anything again. No error,
|
|
60
|
+
// no exit, and a supervisor that sees a healthy pid. Exiting instead turns that
|
|
61
|
+
// into a resume, which costs at most the work since the last checkpoint.
|
|
62
|
+
//
|
|
63
|
+
// "Progress" is any deposit, downloaded chunk, or rate-limit wait; time inside
|
|
64
|
+
// index maintenance and the checkpoint recall does not count against it, since
|
|
65
|
+
// those legitimately deposit nothing. Generous by default — this is a
|
|
66
|
+
// last-resort backstop, not a latency budget. 0 disables it.
|
|
67
|
+
export const STALL_MS = Math.max(0, Math.floor(Number(env("STALL_MIN", "15")) * 60_000) || 900_000);
|
|
68
|
+
// How long a download may wait for room under the cache ceiling before failing
|
|
69
|
+
// the unit instead of waiting forever. The wait exists so a bounded cache can
|
|
70
|
+
// throttle a fast source; it is not meant to outlast the run. The unit stays
|
|
71
|
+
// resumable, so a genuine ceiling problem costs a retry, not the corpus.
|
|
72
|
+
export const CACHE_WAIT_MS = Math.max(60_000, Math.floor(Number(env("CACHE_WAIT_MIN", "10")) * 60_000) || 600_000);
|
|
73
|
+
// The vector indices' memory knob (MiB) — each index's SQLite page cache.
|
|
74
|
+
// The IVF index routes inserts through a RAM-resident pivot table and
|
|
75
|
+
// appends to chunk blobs, so this cache mostly serves query-time cluster
|
|
76
|
+
// scans; 256 MiB comfortably covers the probed working set of a trained
|
|
77
|
+
// store. Override with VECTOR_CACHE_MB (64 is the library default).
|
|
78
|
+
export const VECTOR_CACHE_MB = Math.max(0, Number(env("VECTOR_CACHE_MB", "256")));
|
|
79
|
+
// Page cache for the MAIN DAG database (node/kid/edge/contain tables).
|
|
80
|
+
// Training issues millions of content-addressed point probes per session
|
|
81
|
+
// against a GB-scale file; the library default (64 MiB) is sized for a
|
|
82
|
+
// small machine — a training box affords more. Override with
|
|
83
|
+
// SQLITE_CACHE_MB.
|
|
84
|
+
export const SQLITE_CACHE_MB = Math.max(0, Number(env("SQLITE_CACHE_MB", "256")));
|
|
85
|
+
// Optional ceiling on how much LEARNED CONTENT to train, in megabytes (decimal,
|
|
86
|
+
// like CHECKPOINT_MB). Default Infinity = unbounded. The cap is checked against
|
|
87
|
+
// trainedContentBytes after each deposit, so a run stops at the first item that
|
|
88
|
+
// carries the running total to/past the ceiling (that item is still counted).
|
|
89
|
+
export const MAX_MB = Number(env("MAX_MB", "Infinity"));
|
|
90
|
+
if (isNaN(MAX_MB) || MAX_MB < 0) {
|
|
91
|
+
process.stderr.write(`fatal: MAX_MB must be a non-negative number or "Infinity"\n`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
export const MAX_BYTES = MAX_MB * 1_000_000; // Infinity stays Infinity
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type TrainingItem } from "../items.js";
|
|
2
|
+
import { type Corpus } from "../corpus.js";
|
|
3
|
+
/** One normalized Aya row. */
|
|
4
|
+
export interface AyaRow {
|
|
5
|
+
inputs: string;
|
|
6
|
+
targets: string;
|
|
7
|
+
language: string;
|
|
8
|
+
}
|
|
9
|
+
/** Normalize a raw datasets-server row object into an AyaRow, or null when it
|
|
10
|
+
* lacks a usable prompt/answer or a field is implausibly large (a dump, not a
|
|
11
|
+
* cognitive example). Trims surrounding whitespace; keeps inner text verbatim
|
|
12
|
+
* (human prose, possibly multi-paragraph). */
|
|
13
|
+
export declare function toAyaRow(row: unknown, maxChars?: number): AyaRow | null;
|
|
14
|
+
/** Translate ONE Aya row into SEMA training items. A row is a single human
|
|
15
|
+
* (question → answer) exchange — exactly one FACT, the (inputs → targets) edge.
|
|
16
|
+
* No standalone-answer experience and no one-exchange "cumulative" walk: a lone
|
|
17
|
+
* Q→A is not multi-turn, and both would only replicate the same edge. */
|
|
18
|
+
export declare function ayaRowToItems(row: AyaRow): TrainingItem[];
|
|
19
|
+
export declare const aya: Corpus;
|