simurg 1.0.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.
Files changed (43) hide show
  1. simurg-1.0.0/LICENSE +190 -0
  2. simurg-1.0.0/PKG-INFO +498 -0
  3. simurg-1.0.0/README.md +479 -0
  4. simurg-1.0.0/pyproject.toml +35 -0
  5. simurg-1.0.0/setup.cfg +4 -0
  6. simurg-1.0.0/src/simurg/__init__.py +51 -0
  7. simurg-1.0.0/src/simurg/core.py +83 -0
  8. simurg-1.0.0/src/simurg/data/__init__.py +6 -0
  9. simurg-1.0.0/src/simurg/data/dataset.py +92 -0
  10. simurg-1.0.0/src/simurg/data/evaluate.py +159 -0
  11. simurg-1.0.0/src/simurg/data/generate_dataset.py +167 -0
  12. simurg-1.0.0/src/simurg/data/synth.py +101 -0
  13. simurg-1.0.0/src/simurg/detection/__init__.py +6 -0
  14. simurg-1.0.0/src/simurg/detection/detectors.py +114 -0
  15. simurg-1.0.0/src/simurg/detection/fusion.py +103 -0
  16. simurg-1.0.0/src/simurg/detection/rules.py +68 -0
  17. simurg-1.0.0/src/simurg/detection/sentinel.py +157 -0
  18. simurg-1.0.0/src/simurg/features.py +194 -0
  19. simurg-1.0.0/src/simurg/guard_dashboard.py +331 -0
  20. simurg-1.0.0/src/simurg/guard_ui/index.html +433 -0
  21. simurg-1.0.0/src/simurg/integrations/__init__.py +6 -0
  22. simurg-1.0.0/src/simurg/integrations/openai_guard.py +153 -0
  23. simurg-1.0.0/src/simurg/learning/__init__.py +6 -0
  24. simurg-1.0.0/src/simurg/learning/custom.py +203 -0
  25. simurg-1.0.0/src/simurg/learning/model.py +69 -0
  26. simurg-1.0.0/src/simurg/signals/__init__.py +6 -0
  27. simurg-1.0.0/src/simurg/signals/calibrate.py +50 -0
  28. simurg-1.0.0/src/simurg/signals/changepoint.py +38 -0
  29. simurg-1.0.0/src/simurg/signals/ngram_lm.py +71 -0
  30. simurg-1.0.0/src/simurg/signals/simhash.py +62 -0
  31. simurg-1.0.0/src/simurg/signals/sketch.py +70 -0
  32. simurg-1.0.0/src/simurg/training/__init__.py +6 -0
  33. simurg-1.0.0/src/simurg/training/train_live.py +198 -0
  34. simurg-1.0.0/src/simurg/training/train_ui/index.html +196 -0
  35. simurg-1.0.0/src/simurg/weights/simurg_calib.json +1 -0
  36. simurg-1.0.0/src/simurg/weights/simurg_model.json +1 -0
  37. simurg-1.0.0/src/simurg.egg-info/PKG-INFO +498 -0
  38. simurg-1.0.0/src/simurg.egg-info/SOURCES.txt +41 -0
  39. simurg-1.0.0/src/simurg.egg-info/dependency_links.txt +1 -0
  40. simurg-1.0.0/src/simurg.egg-info/requires.txt +7 -0
  41. simurg-1.0.0/src/simurg.egg-info/top_level.txt +1 -0
  42. simurg-1.0.0/tests/test_guard_dashboard.py +143 -0
  43. simurg-1.0.0/tests/test_sentinel.py +56 -0
simurg-1.0.0/LICENSE ADDED
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2026 HAL-X AI / Farid Aghayev (doofZ)
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.
simurg-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,498 @@
1
+ Metadata-Version: 2.4
2
+ Name: simurg
3
+ Version: 1.0.0
4
+ Summary: Zero-leak online detection of LLM decoding corruption in production streams
5
+ Author-email: "Farid Aghayev (doofZ)" <farid.a@hal-x.ai>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://hal-x.ai
8
+ Project-URL: Repository, https://github.com/doofzoff/SIMURG
9
+ Keywords: llm,llm-safety,guardrails,streaming,hallucination,anomaly-detection,inference,vllm,openai-api,online-learning
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: numpy>=1.24
14
+ Provides-Extra: figures
15
+ Requires-Dist: matplotlib>=3.7; extra == "figures"
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest>=7; extra == "test"
18
+ Dynamic: license-file
19
+
20
+ <h1 align="center">SIMURG</h1>
21
+ <p align="center"><b>Streaming Integrity Monitor &amp; Universal Regeneration Guard</b></p>
22
+ <p align="center">
23
+ Catch LLM decoding corruption <b>while the answer is still being generated</b> and cut the
24
+ stream <b>mid-flight</b>: corruption that starts in the hold window never reaches the
25
+ user, and mid-stream corruption is aborted within a few hundred characters of onset,
26
+ so the host regenerates the answer.
27
+ </p>
28
+ <p align="center">
29
+ <img alt="license" src="https://img.shields.io/badge/license-Apache--2.0-blue">
30
+ <img alt="python" src="https://img.shields.io/badge/python-%E2%89%A53.10-blue">
31
+ <img alt="dependencies" src="https://img.shields.io/badge/dependencies-numpy%20only-green">
32
+ <img alt="cpu" src="https://img.shields.io/badge/runtime-CPU%2C%20no%20GPU-lightgrey">
33
+ </p>
34
+
35
+ | throughput | detection latency | false-alarm budget | footprint | setup |
36
+ |:---:|:---:|:---:|:---:|:---:|
37
+ | **197,632 chars/sec** on a laptop CPU | **~590 chars** past corruption onset | configurable, conformal-calibrated | numpy only, no model, no GPU | **3 lines**, zero training |
38
+
39
+ The guard runs hundreds of times faster than a typical LLM produces text, so it is
40
+ never the bottleneck: a model streaming at 50 tokens/sec writes ~250 chars/sec, and
41
+ SIMURG reads 197,000.
42
+
43
+ <details>
44
+ <summary><b>Table of contents</b></summary>
45
+
46
+ 1. [The problem](#the-problem)
47
+ 2. [How SIMURG differs](#how-simurg-differs)
48
+ 3. [How it works](#how-it-works)
49
+ 4. [Zero-leak in action](#zero-leak-in-action)
50
+ 5. [Benchmark](#benchmark)
51
+ 6. [Install](#install)
52
+ 7. [Quick start](#quick-start)
53
+ 8. [Teach it your domain and your failure modes](#teach-it-your-domain-and-your-failure-modes)
54
+ 9. [Live guard dashboard](#live-guard-dashboard)
55
+ 10. [What SIMURG is NOT](#what-simurg-is-not)
56
+ 11. [Repository layout](#repository-layout)
57
+ 12. [Roadmap](#roadmap)
58
+ 13. [FAQ](#faq)
59
+ 14. [Citation](#citation)
60
+ 15. [License](#license)
61
+
62
+ </details>
63
+
64
+ ---
65
+
66
+ ## The problem
67
+
68
+ When you run an LLM in production, especially a **quantized, small, or self-hosted**
69
+ model, it sometimes **derails mid-generation**. The decoded stream stops doing the
70
+ task and collapses into one of a handful of pathologies:
71
+
72
+ | failure mode | what it looks like |
73
+ |---|---|
74
+ | **repetition collapse** | the same phrase, list, or token repeated until the token budget runs out |
75
+ | **cross-lingual drift** | an English answer that quietly slides into Chinese, Arabic, or Cyrillic |
76
+ | **regurgitation** | the model dumps a README, boilerplate, or training text |
77
+ | **structural breakdown** | `#REF! -0.00 -0.00 ... 0.00`: number and symbol garbage |
78
+ | **template leakage** | `<|im_start|>`, `</s>`, `[INST]`, "As an AI language model..." spilling into the answer |
79
+
80
+ This is **not** factual hallucination. A fluent-but-wrong sentence (see
81
+ [What SIMURG is NOT](#what-simurg-is-not)) has no statistical scar. What is shown
82
+ above is **decoding corruption**, and it leaves a *statistical signature in the
83
+ token stream*: repetition rate, lexical variety, script distribution,
84
+ compressibility, and predictive surprise all move in measurable ways.
85
+
86
+ SIMURG watches that signature character by character, decides in real time whether
87
+ the stream has gone bad, tells you **where** it started, and lets you **abort and
88
+ retry** before the user ever sees the corruption.
89
+
90
+ ---
91
+
92
+ ## How SIMURG differs
93
+
94
+ | | **SIMURG** | post-hoc linter | LLM-as-judge | perplexity threshold |
95
+ |---|---|---|---|---|
96
+ | when it fires | **mid-generation**, ~590 chars past onset | after the full answer | after the full answer | post-hoc, or needs logprob access |
97
+ | what the user sees | **zero bad tokens when onset is in the hold window**; otherwise the clean prefix plus a bad tail of at most ~900 chars, replaced by the retry | the whole corrupt answer | the whole corrupt answer | varies |
98
+ | why it fired | a named, human-readable reason on every alarm | a pattern list | the judge's opinion, if any | one number |
99
+ | model-agnostic | any OpenAI-compatible endpoint, or any stream you feed | any | any | needs a logprob-capable backend |
100
+ | overhead | numpy-only, ~197k chars/sec on one CPU core | trivial | one extra LLM call per answer | per-token logprobs |
101
+
102
+ The zero-leak property is the point: post-hoc checks can only tell you that the
103
+ answer was bad *after the user read it*. SIMURG holds the opening of every stream
104
+ in a buffer, releases it only once it is verified clean, keeps re-checking, and
105
+ cuts the stream the moment it crosses the calibrated threshold.
106
+
107
+ ---
108
+
109
+ ## How it works
110
+
111
+ SIMURG makes **one O(1)-per-character pass** over the stream, maintaining a set of
112
+ incremental features (digit fraction, foreign-script fraction, repetition rate,
113
+ compressibility, type-token ratio, script-switch rate, structural-artifact density,
114
+ ...), and feeds a pluggable detector ensemble on top of them:
115
+
116
+ ```mermaid
117
+ flowchart TD
118
+ A["token stream"] --> B["stream features<br/>one O(1) per character incremental pass"]
119
+ B --> C1["char n-gram surprise<br/>self-calibrating, no reference corpus"]
120
+ B --> C2["Count-Min repetition sketch<br/>constant memory, 8k counters"]
121
+ B --> C3["rolling SimHash drift<br/>topic collapse detection"]
122
+ B --> C4["robust-z self-calibration<br/>baselines frozen on the clean prefix"]
123
+ B --> C5["rule tier<br/>interpretable thresholds, zero training"]
124
+ C1 --> D["conformal fusion<br/>finite-sample false-alarm budget"]
125
+ C2 --> D
126
+ C3 --> D
127
+ C4 --> D
128
+ C5 --> D
129
+ L["learned tier<br/>15-weight online logistic model"] --> D
130
+ D --> E["CLEAN / SUSPECT / CORRUPT<br/>plus Page-Hinkley onset localization"]
131
+ E --> F["zero-leak protocol<br/>HOLD first 350 chars, RELEASE if clean,<br/>re-check every 400, ABORT on corrupt"]
132
+ F --> G["bad tokens never reach the UI"]
133
+ ```
134
+
135
+ ### The five detectors
136
+
137
+ | detector | what it measures | why it catches corruption |
138
+ |---|---|---|
139
+ | **char n-gram surprise** | predictive surprise of each char against an in-stream 3-gram model | loops and garbage drive surprise toward zero |
140
+ | **Count-Min repetition** | n-gram repetition rate in a constant-memory sketch | repetition collapse is the most common production failure |
141
+ | **rolling SimHash drift** | distance of a 48-token fingerprint from the clean-prefix baseline | topic collapse and regurgitation move the fingerprint |
142
+ | **robust-z self-calibration** | every feature z-scored against its own frozen clean-prefix baseline | no hand-tuned magic numbers, adapts to any domain |
143
+ | **rules** | interpretable thresholds (digit fraction, script switch, template markers, ...) | day-one coverage, every alarm is a sentence a human can read |
144
+
145
+ ### Two tiers cooperate
146
+
147
+ - **Rule tier.** Interpretable thresholds on the stream features. Works on day one
148
+ with **zero training**, and every alarm is explainable: `"repetition loop
149
+ rate=0.71"`, `"digit fraction 0.57"`, `"script switch en to zh"`.
150
+ - **Learned tier.** A small **online logistic regression** (15 weights, a few KB)
151
+ that adds robustness and **keeps learning in production** via `partial_fit`.
152
+
153
+ ### Conformal calibration: a budget, not a hope
154
+
155
+ The fusion layer sets its thresholds from the score distribution on *clean*
156
+ streams, which gives a **finite-sample guarantee on the false-alarm rate**. "Flag
157
+ at most 2% of clean outputs" is a knob you set and the calibration enforces, not a
158
+ threshold you hope holds.
159
+
160
+ ### The zero-leak protocol
161
+
162
+ 1. **HOLD** the first 350 characters. A stream that is corrupt from the start is
163
+ killed before a single character reaches the UI.
164
+ 2. **RELEASE** the prefix if it scores clean, and freeze the self-calibrated
165
+ baselines on it.
166
+ 3. **Re-check** every 400 characters for the rest of the stream.
167
+ 4. **ABORT** on a calibrated threshold crossing (with a 2-hit or hard-rule
168
+ hysteresis so a single noisy checkpoint does not kill a good answer).
169
+
170
+ ---
171
+
172
+ ## Zero-leak in action
173
+
174
+ A synthetic stream that is clean prose and then collapses into a repetition loop
175
+ at character 339. SIMURG holds the opening, verifies the clean prefix, scores
176
+ the stream at every 400-char checkpoint, and aborts 821 characters after the
177
+ loop starts. Corrupt streams that are already bad at the 350-char checkpoint
178
+ are blocked fully (12 of 21 in the benchmark, see below); for this mid-stream
179
+ onset the user sees the clean prefix plus a short bad tail, and the guard's
180
+ contract with the host is a **retry**: `GuardedLLM` regenerates the answer and
181
+ the host replaces the shown text, so the bad tail never becomes the final
182
+ output:
183
+
184
+ ![zero-leak demo: corruption score stays flat, crosses the calibrated threshold, abort and retry](figures/zero_leak_demo.png)
185
+
186
+ Every alarm carries the reasons that fired it. For the stream above:
187
+
188
+ ```
189
+ repetition loop rate=0.66 zlib=0.10
190
+ vocabulary collapse ttr=0.09
191
+ surprise collapse low_frac=1.00
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Benchmark
197
+
198
+ Reproducible end-to-end benchmark: builds the **CorruptBench** synthetic set
199
+ (243 streams, 4 failure classes), trains the learned tier, calibrates the
200
+ conformal thresholds, and reports the full table:
201
+
202
+ ```bash
203
+ pip install -e .
204
+ python3 -m simurg.data.evaluate # seed 7, deterministic dataset
205
+ ```
206
+
207
+ Test split (81 streams), seed 7:
208
+
209
+ | metric | value |
210
+ |---|---|
211
+ | stream-level TPR | **78/80 = 0.975** |
212
+ | recall, repetition collapse | 16/18 = 0.89 |
213
+ | recall, cross-lingual drift | 25/25 = 1.00 |
214
+ | recall, regurgitation | 19/19 = 1.00 |
215
+ | recall, structural breakdown | 18/18 = 1.00 |
216
+ | detection latency past onset | **median 590**, p90 868 chars |
217
+ | onset localization error | median 532 chars |
218
+ | zero-leak (onset inside hold window) | 12/21 blocked fully |
219
+ | throughput | **197,632 chars/sec** |
220
+ | stream-level AUROC (final score) | 0.55, dragged down by ties at p=1.0 and a 1-stream clean test split; TPR/FPR at the calibrated threshold is the operating metric |
221
+
222
+ In addition, the shipped detector **flagged 0 false alarms on 121 real production
223
+ texts** from a self-hosted reasoning-model deployment.
224
+
225
+ ![detection recall per failure class, and the latency distribution past onset](figures/recall_latency.png)
226
+
227
+ ![learned feature weights: the 15-weight online logistic model after calibration](figures/feature_weights.png)
228
+
229
+ **Those numbers describe the bundled domain.** The detector is only as good as the
230
+ clean corpus it calibrates against, so retrain on your own traffic before you
231
+ trust it in production. It takes seconds, see
232
+ [below](#teach-it-your-domain-and-your-failure-modes).
233
+
234
+ ---
235
+
236
+ ## Install
237
+
238
+ ```bash
239
+ pip install simurg # numpy only
240
+ pip install simurg[figures] # + matplotlib, for the paper plots
241
+ pip install simurg[test] # + pytest
242
+ ```
243
+
244
+ From source:
245
+
246
+ ```bash
247
+ git clone https://github.com/doofzoff/SIMURG.git
248
+ cd SIMURG
249
+ pip install -e .
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Quick start
255
+
256
+ ### 1. Guard any OpenAI-compatible endpoint (3 lines)
257
+
258
+ Works with **vLLM, llama.cpp server, TGI, Ollama, OpenAI, OpenRouter**: anything
259
+ that speaks `/v1/chat/completions`. Batteries included: the zero-leak protocol
260
+ plus an **abort, retry, fallback-model** ladder.
261
+
262
+ ```python
263
+ from simurg import GuardedLLM
264
+
265
+ llm = GuardedLLM(
266
+ "http://localhost:8000/v1", model="my-model",
267
+ retries=1,
268
+ fallback=GuardedLLM("https://openrouter.ai/api/v1",
269
+ model="qwen/qwen3", api_key="sk-..."), # optional
270
+ )
271
+
272
+ result = llm.chat(
273
+ [{"role": "user", "content": "Explain how oil prices affect a small economy."}],
274
+ on_token=lambda t: print(t, end="", flush=True), # only CLEAN text is ever forwarded
275
+ )
276
+
277
+ print(result.ok) # True if a clean answer was produced
278
+ print(result.verdict) # "clean" | "suspect" | "corrupt"
279
+ print(result.attempts) # the full ladder: what each attempt did and why
280
+ ```
281
+
282
+ If an attempt corrupts, **nothing from it reaches `on_token`**. A corrupt attempt
283
+ is retried; if all retries fail, the fallback model is tried.
284
+
285
+ ### 2. Guard a stream from any source (5 lines)
286
+
287
+ Not on an OpenAI-style API? Wrap your own token loop:
288
+
289
+ ```python
290
+ from simurg import Simurg
291
+
292
+ s = Simurg() # rule tier works with zero setup
293
+ for token in my_llm_stream():
294
+ v = s.feed(token)
295
+ if v.state == "corrupt":
296
+ abort_and_retry(reason=v.reasons, onset=v.onset_char)
297
+ break
298
+ ui.write(v.released) # text cleared for display (may lag while holding)
299
+ final = s.finish()
300
+ ui.write(final.released)
301
+ ```
302
+
303
+ ### 3. Post-hoc check of a finished text
304
+
305
+ ```python
306
+ from simurg import Simurg
307
+
308
+ s = Simurg()
309
+ s.feed(whole_text)
310
+ print(s.finish().state) # "clean" / "suspect" / "corrupt"
311
+ ```
312
+
313
+ ---
314
+
315
+ ## Teach it your domain and your failure modes
316
+
317
+ ### Retrain on your traffic
318
+
319
+ Feed the calibration step **your** good outputs so the thresholds fit your domain:
320
+
321
+ ```bash
322
+ # bring your own clean corpus (.jsonl with a "text" field per line)
323
+ SIMURG_CORPUS_JSONL=/path/to/my_clean_outputs.jsonl python3 -m simurg.data.evaluate --save
324
+ ```
325
+
326
+ Full guide, including the quick path, the live dashboard, and the production
327
+ flywheel: **[docs/TRAINING.md](docs/TRAINING.md)**.
328
+
329
+ ### Teach it a NEW failure mode from examples, with an honesty gate
330
+
331
+ Give SIMURG examples of *your* model's bad outputs. It tells you **whether that
332
+ failure is even catchable** in stream statistics, and hands you a fitted detector
333
+ if it is:
334
+
335
+ ```python
336
+ from simurg import fit_custom_detector
337
+
338
+ report, detector = fit_custom_detector(
339
+ "template_leak",
340
+ clean_texts = my_good_outputs, # 50+
341
+ corrupt_texts = my_bad_outputs, # 20+
342
+ )
343
+ print(report)
344
+ # verdict: DETECTABLE held-out AUROC: 0.98 -> auto-registered into every Simurg()
345
+ ```
346
+
347
+ The gate is the point: fluent factual lies come back **`NOT DETECTABLE`** instead
348
+ of a false promise. Details, plus the zero-training `LexiconDetector` for known
349
+ bad markers like `<|im_start|>`: **[docs/CUSTOM.md](docs/CUSTOM.md)**.
350
+
351
+ ### Watch it train, live
352
+
353
+ ```bash
354
+ python3 -m simurg.training.train_live # writes metrics for the bundled dashboard
355
+ ```
356
+
357
+ A real-time web dashboard: log-loss, accuracy, AUROC, **all 15 weights animating
358
+ per epoch**, memory, and the final held-out TPR/FPR verdict.
359
+
360
+ ---
361
+
362
+ ## Live guard dashboard
363
+
364
+ A second web page for *runtime*: connect it to any OpenAI-compatible endpoint,
365
+ send a prompt, and watch the answer get guarded while it is generated. The
366
+ dashboard renders in real time:
367
+
368
+ - the **released stream text** (what the user would actually see),
369
+ - the **fused corruption score** with the calibrated SUSPECT/ABORT thresholds
370
+ and the 350-char hold zone,
371
+ - the **corruption onset marker** and the human-readable **reasons**,
372
+ - **all 15 stream features** as sparklines, sampled at every checkpoint.
373
+
374
+ Every run is recorded as a **session** (timestamped frames with score, state,
375
+ released text, features and reasons). The sessions panel lists them, deletes
376
+ them, and **replays any session at up to 128x** for postmortem analysis, so a
377
+ corrupt answer from Tuesday can be re-watched the way a crash log is read.
378
+
379
+ ```bash
380
+ python3 -m simurg.guard_dashboard --port 8321
381
+ # open http://127.0.0.1:8321, point it at your endpoint, guard a stream
382
+ ```
383
+
384
+ Pasted texts can also be analyzed at full speed in the same UI. Same
385
+ self-contained dark style as the training dashboard, zero new dependencies:
386
+ the server is stdlib-only and acts as a CORS-free proxy to your endpoint.
387
+
388
+ ---
389
+
390
+ ## What SIMURG is NOT
391
+
392
+ SIMURG detects **corrupt or degenerate decoding**, not **factual wrongness**. A
393
+ fluent, well-formed sentence that is simply *false* ("the capital of Australia is
394
+ Sydney") has no stream-statistical signature: it looks exactly like a true
395
+ sentence. For that you need **grounding** (constrain the model to retrieved facts
396
+ and make it quote them), retrieval verification, or a factuality checker.
397
+
398
+ SIMURG guards the *delivery*; grounding guards the *content*. Use both.
399
+ `fit_custom_detector` will explicitly refuse to pretend it can catch this class.
400
+
401
+ ---
402
+
403
+ ## Repository layout
404
+
405
+ ```
406
+ src/simurg/
407
+ ├── core.py taxonomy, detector protocol, registry
408
+ ├── features.py the single O(1)/char stream-feature pass
409
+ ├── signals/ the raw estimators: n-gram surprise, Count-Min sketch,
410
+ │ rolling SimHash, robust-z calibration, Page-Hinkley
411
+ ├── detection/ rules, detectors, conformal fusion, sentinel (protocol)
412
+ ├── learning/ online logistic model, custom-failure-mode training (BYOC)
413
+ ├── integrations/ GuardedLLM, the OpenAI-compatible drop-in guard
414
+ ├── data/ CorruptBench synth, dataset builder, benchmark, generator
415
+ ├── training/ live-training run + real-time web dashboard
416
+ ├── guard_dashboard.py live guard dashboard server (stdlib-only, SSE, sessions)
417
+ ├── guard_ui/ live guard dashboard front-end + recorded sessions
418
+ └── weights/ shipped model + conformal thresholds (use as a pair)
419
+ docs/ TRAINING.md, CUSTOM.md
420
+ examples/ runnable quickstart
421
+ tests/ sentinel regressions + end-to-end dashboard tests
422
+ figures/ benchmark figures referenced by this README
423
+ ```
424
+
425
+ ---
426
+
427
+ ## Roadmap
428
+
429
+ Ideas under active consideration, in rough priority order:
430
+
431
+ 1. **Engine-level abort.** Ship integrations that stop generation *inside* the
432
+ inference engine (a vLLM streaming hook and a generic SSE middleware proxy),
433
+ so an abort frees GPU time instead of just saving the UI. The guard already
434
+ exposes everything a host needs; what is missing is the wiring.
435
+ 2. **Fleet telemetry.** Export `p(corrupt)`, verdict transitions, and onset
436
+ positions as Prometheus metrics or OpenTelemetry spans, so a Grafana panel can
437
+ show a *corruption rate per model and endpoint* and alert when a quantization
438
+ or a prompt change starts producing bad streams.
439
+ 3. **Zero-dependency runtime.** Export the guard core (features, sketches,
440
+ fusion) to ONNX or a small C library that runs inside the inference server
441
+ with no Python, for hosts that cannot take a numpy dependency on the hot path.
442
+ 4. **CI regression suite.** A golden corpus of labeled clean and corrupt streams
443
+ with fixed expected verdicts, plus latency and throughput budgets, run as a
444
+ GitHub Action on every pull request: the build fails when a threshold tweak
445
+ quietly degrades detection.
446
+ 5. **Multi-stream fleet mode.** Guard N parallel live streams in one process,
447
+ with per-stream sessions and a single dashboard that compares corruption
448
+ rates across endpoints, so a bad quantization shows up as one lane going red
449
+ while the others stay green.
450
+
451
+ ---
452
+
453
+ ## FAQ
454
+
455
+ **Will it catch factual hallucinations?**
456
+ No, and it will tell you so. Factual errors have no stream-statistical signature.
457
+ Use grounding or a factuality checker for content, SIMURG for delivery.
458
+
459
+ **What is the overhead?**
460
+ One O(1) pass per character, ~197k chars/sec on a laptop CPU. A 50 tok/s model
461
+ writes ~250 chars/sec, so the guard is hundreds of times faster than the model it
462
+ guards. Memory is bounded per stream: 8,192 sketch counters, a 48-token SimHash
463
+ window, and an n-gram table capped at 60k contexts.
464
+
465
+ **Does it only work with English?**
466
+ No. Script features are language-agnostic (per-script fractions, switch rates),
467
+ and you can declare your expected scripts at construction time
468
+ (`Simurg(expected_scripts=("cyrillic",))`). Retrain on your traffic for best
469
+ results.
470
+
471
+ **What is the SUSPECT state for?**
472
+ It is a non-blocking warning tier between CLEAN and CORRUPT. Your host can use it
473
+ to slow the UI down, show a subtle indicator, or pre-stage a retry, without
474
+ discarding a stream that may still turn out clean.
475
+
476
+ **How do I retrain on my own domain?**
477
+ `SIMURG_CORPUS_JSONL=... python3 -m simurg.data.evaluate --save` over your clean
478
+ outputs. It rebuilds the weights and the conformal thresholds in seconds. Full
479
+ guide: [docs/TRAINING.md](docs/TRAINING.md).
480
+
481
+ ---
482
+
483
+ ## Citation
484
+
485
+ ```bibtex
486
+ @techreport{aghayev2026simurg,
487
+ title = {SIMURG: Zero-Leak Online Detection of LLM Decoding Corruption in Production Streams},
488
+ author = {Aghayev, Farid},
489
+ institution = {HAL-X AI},
490
+ year = {2026},
491
+ url = {https://github.com/doofzoff/SIMURG}
492
+ }
493
+ ```
494
+
495
+ ## License
496
+
497
+ **Apache-2.0**. See [LICENSE](LICENSE). Developed by **doofZ (Farid Aghayev)**,
498
+ HAL-X AI.