simthinkd 0.2.0__tar.gz → 0.2.1__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 (36) hide show
  1. {simthinkd-0.2.0/src/simthinkd.egg-info → simthinkd-0.2.1}/PKG-INFO +27 -17
  2. {simthinkd-0.2.0 → simthinkd-0.2.1}/README.md +26 -16
  3. {simthinkd-0.2.0 → simthinkd-0.2.1}/pyproject.toml +1 -1
  4. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/__init__.py +1 -1
  5. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/server.py +16 -1
  6. {simthinkd-0.2.0 → simthinkd-0.2.1/src/simthinkd.egg-info}/PKG-INFO +27 -17
  7. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd.egg-info/SOURCES.txt +1 -0
  8. simthinkd-0.2.1/tests/test_server_latency.py +30 -0
  9. {simthinkd-0.2.0 → simthinkd-0.2.1}/LICENSE +0 -0
  10. {simthinkd-0.2.0 → simthinkd-0.2.1}/NOTICE +0 -0
  11. {simthinkd-0.2.0 → simthinkd-0.2.1}/setup.cfg +0 -0
  12. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/bench.py +0 -0
  13. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/cli.py +0 -0
  14. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/core.py +0 -0
  15. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/data/doom_defend_states.json +0 -0
  16. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/integrations/__init__.py +0 -0
  17. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/integrations/langchain_tool.py +0 -0
  18. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/integrations/mcp_server.py +0 -0
  19. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/policy.py +0 -0
  20. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/score.py +0 -0
  21. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/toy.py +0 -0
  22. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/train.py +0 -0
  23. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/weights/SHA256SUMS +0 -0
  24. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/weights/doom-corridor.npz +0 -0
  25. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd/weights/doom-defend.npz +0 -0
  26. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd.egg-info/dependency_links.txt +0 -0
  27. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd.egg-info/entry_points.txt +0 -0
  28. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd.egg-info/requires.txt +0 -0
  29. {simthinkd-0.2.0 → simthinkd-0.2.1}/src/simthinkd.egg-info/top_level.txt +0 -0
  30. {simthinkd-0.2.0 → simthinkd-0.2.1}/tests/test_factory_twin.py +0 -0
  31. {simthinkd-0.2.0 → simthinkd-0.2.1}/tests/test_notebook.py +0 -0
  32. {simthinkd-0.2.0 → simthinkd-0.2.1}/tests/test_package.py +0 -0
  33. {simthinkd-0.2.0 → simthinkd-0.2.1}/tests/test_score.py +0 -0
  34. {simthinkd-0.2.0 → simthinkd-0.2.1}/tests/test_space.py +0 -0
  35. {simthinkd-0.2.0 → simthinkd-0.2.1}/tests/test_web_page.py +0 -0
  36. {simthinkd-0.2.0 → simthinkd-0.2.1}/tests/test_web_parity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simthinkd
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: A 265k-parameter decision model that picks one action in about 2 ms on one CPU core, inside real-time loops.
5
5
  Author: Myeongseongsimjae AX Institute
6
6
  License: Apache-2.0
@@ -28,7 +28,7 @@ Requires-Dist: onnx>=1.15; extra == "onnx"
28
28
  Requires-Dist: onnxruntime>=1.17; extra == "onnx"
29
29
  Dynamic: license-file
30
30
 
31
- <p align="center"><img src="assets/banner.png" alt="SimThink D: a tiny decision model that runs on one CPU core" width="100%"></p>
31
+ <p align="center"><img src="assets/banner_v2.png" alt="SimThink D: a local backup for cloud decisions" width="100%"></p>
32
32
 
33
33
  <p align="center">
34
34
  <a href="https://pypi.org/project/simthinkd/"><img src="https://img.shields.io/pypi/v/simthinkd" alt="PyPI"></a>
@@ -39,27 +39,23 @@ Dynamic: license-file
39
39
  <a href="https://doi.org/10.5281/zenodo.23111615"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23111615.svg" alt="DOI"></a>
40
40
  </p>
41
41
 
42
- <p align="center"><a href="https://mssj-ai-org.github.io/simthinkd/"><b>Project page, live demo and video →</b></a></p>
42
+ <p align="center"><b>Network down. Decisions stay local.</b></p>
43
43
 
44
- **A tiny decision model that runs on one CPU core.** It has 265,665 parameters. It picks one action in about 2 ms. That is fast enough to decide inside every tick of a game or a control loop. No GPU is needed, not even for training.
44
+ SimThink D is a small CPU model for offline backup decisions.
45
+ It has 265,665 parameters and takes about 2 ms per decision on one CPU core.
45
46
 
46
- <p align="center">
47
- <img src="assets/side_by_side.gif" alt="Same Doom game, same seed, same CPU. Left: SimThink D answers every tick. Right: a 421M-parameter general decision model, used as published, misses most ticks while it thinks." width="100%" />
48
- </p>
49
-
50
- <p align="center"><sub>Same game, same seed, same 6-core CPU. Left: SimThink D, 1.9 ms per decision, 1 of 420 ticks missed. Right: Laya, a 421M-parameter open decision model, used as published without training on this game. It takes about 360 ms per decision and misses 390 of 420 ticks. A dark frame means the game moved on before the decider answered.</sub></p>
47
+ In the factory simulation, the internet was cut for 15 seconds.
48
+ The local backup got 59 of 63 parts right, with no late decisions.
51
49
 
52
- <div align="center">
50
+ <p align="center"><a href="assets/factory_fallback.mp4"><img src="assets/factory_fallback_poster.jpg" alt="Watch the factory simulation: local backup during a network outage" width="100%"></a></p>
53
51
 
54
- [Open in Colab](https://colab.research.google.com/github/MSSJ-AI-ORG/simthinkd/blob/main/notebooks/quickstart.ipynb) · [Try it in your browser](https://mssj-ai-org.github.io/simthinkd/#demo) · [Gradio demo](space/) · [Factory twin](examples/factory_twin/) · [Paper](docs/PAPER.md) · [Reproduce the paper](docs/REPRODUCE.md) · [Figures](docs/FIGURES.md) · [Protocol](docs/PROTOCOL.md)
52
+ <p align="center"><a href="https://mssj-ai-org.github.io/simthinkd/#demo">Try in your browser</a> · <a href="assets/factory_fallback.mp4">Watch the video (72 s)</a> · <a href="examples/factory_twin/">Factory code</a> · <a href="docs/PAPER.md">Paper</a></p>
55
53
 
56
- </div>
57
-
58
- ### Video: the internet goes down, the line keeps going
59
-
60
- <a href="assets/factory_fallback.mp4"><img src="assets/factory_fallback_poster.jpg" alt="Internet down. The line kept going." width="100%"></a>
54
+ ```bash
55
+ pip install simthinkd
56
+ ```
61
57
 
62
- A cloud decision service runs a simulated inspection line, with SimThink D on the factory PC as its backup. When an answer does not come back in time, or the network is cut, SimThink D makes the decision. [Watch the video](assets/factory_fallback.mp4) (72 s) or see it [on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7510142949981057024/). The simulator is in [examples/factory_twin](examples/factory_twin/).
58
+ What makes the next decision when your network goes down?
63
59
 
64
60
  ## Words used here
65
61
 
@@ -181,6 +177,12 @@ The second line measures any server that accepts the [decision request](docs/PRO
181
177
 
182
178
  ## Same CPU, same states
183
179
 
180
+ <p align="center">
181
+ <img src="assets/side_by_side.gif" alt="Same Doom game, same seed, same CPU. Left: SimThink D answers every tick. Right: a 421M-parameter general decision model, used as published, misses most ticks while it thinks." width="100%" />
182
+ </p>
183
+
184
+ <p align="center"><sub>Same game, same seed, same 6-core CPU. Left: SimThink D, 1.9 ms per decision, 1 of 420 ticks missed. Right: Laya, a 421M-parameter open decision model, used as published without training on this game. It takes about 360 ms per decision and misses 390 of 420 ticks. A dark frame means the game moved on before the decider answered.</sub></p>
185
+
184
186
  **Read this first.** Laya is a general model and was not trained on this game. Its published speed, about 33 ms per question, is on a GPU. We only had a CPU. So this table compares time inside a real-time loop. It does not compare overall quality.
185
187
 
186
188
  We used one workstation CPU (6 threads) and 1,050 Doom states, then 10 live games on the same seeds. "Missed ticks" are ticks that passed before the decider answered.
@@ -200,6 +202,7 @@ SimThink D only knows what its teacher knows. It does not reason, read long text
200
202
  | Unity / C# | [docs/INTEGRATION_UNITY.md](docs/INTEGRATION_UNITY.md): a client loop that keeps the game running while it waits |
201
203
  | Browser | [web/](web/): the same model in plain JavaScript, no server |
202
204
  | A factory line (simulator) | [examples/factory_twin/](examples/factory_twin/): an inspection conveyor with a 400 ms deadline per part |
205
+ | Gradio | [space/](space/): a small web demo you can run locally or on Hugging Face Spaces |
203
206
  | MCP (Claude Desktop, Cursor and others) | `pip install "simthinkd[mcp]"`, then `python -m simthinkd.integrations.mcp_server` |
204
207
  | LangChain / LangGraph | `from simthinkd.integrations.langchain_tool import simthinkd_tool` |
205
208
 
@@ -218,6 +221,13 @@ SimThink D only knows what its teacher knows. It does not reason, read long text
218
221
  - Probabilities are calibrated for the decider's own task only.
219
222
  - A score model returns one number. It gives no probability or error bar with it.
220
223
 
224
+ ## More
225
+
226
+ - [Figures from the paper](docs/FIGURES.md)
227
+ - [What you can reproduce](docs/REPRODUCE.md)
228
+ - [Decision request format](docs/PROTOCOL.md)
229
+ - [The factory video on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7510142949981057024/)
230
+
221
231
  ## Citation
222
232
 
223
233
  If you use SimThink D, please cite it with [CITATION.cff](CITATION.cff). GitHub shows a "Cite this repository" button for it.
@@ -1,4 +1,4 @@
1
- <p align="center"><img src="assets/banner.png" alt="SimThink D: a tiny decision model that runs on one CPU core" width="100%"></p>
1
+ <p align="center"><img src="assets/banner_v2.png" alt="SimThink D: a local backup for cloud decisions" width="100%"></p>
2
2
 
3
3
  <p align="center">
4
4
  <a href="https://pypi.org/project/simthinkd/"><img src="https://img.shields.io/pypi/v/simthinkd" alt="PyPI"></a>
@@ -9,27 +9,23 @@
9
9
  <a href="https://doi.org/10.5281/zenodo.23111615"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23111615.svg" alt="DOI"></a>
10
10
  </p>
11
11
 
12
- <p align="center"><a href="https://mssj-ai-org.github.io/simthinkd/"><b>Project page, live demo and video →</b></a></p>
12
+ <p align="center"><b>Network down. Decisions stay local.</b></p>
13
13
 
14
- **A tiny decision model that runs on one CPU core.** It has 265,665 parameters. It picks one action in about 2 ms. That is fast enough to decide inside every tick of a game or a control loop. No GPU is needed, not even for training.
14
+ SimThink D is a small CPU model for offline backup decisions.
15
+ It has 265,665 parameters and takes about 2 ms per decision on one CPU core.
15
16
 
16
- <p align="center">
17
- <img src="assets/side_by_side.gif" alt="Same Doom game, same seed, same CPU. Left: SimThink D answers every tick. Right: a 421M-parameter general decision model, used as published, misses most ticks while it thinks." width="100%" />
18
- </p>
19
-
20
- <p align="center"><sub>Same game, same seed, same 6-core CPU. Left: SimThink D, 1.9 ms per decision, 1 of 420 ticks missed. Right: Laya, a 421M-parameter open decision model, used as published without training on this game. It takes about 360 ms per decision and misses 390 of 420 ticks. A dark frame means the game moved on before the decider answered.</sub></p>
17
+ In the factory simulation, the internet was cut for 15 seconds.
18
+ The local backup got 59 of 63 parts right, with no late decisions.
21
19
 
22
- <div align="center">
20
+ <p align="center"><a href="assets/factory_fallback.mp4"><img src="assets/factory_fallback_poster.jpg" alt="Watch the factory simulation: local backup during a network outage" width="100%"></a></p>
23
21
 
24
- [Open in Colab](https://colab.research.google.com/github/MSSJ-AI-ORG/simthinkd/blob/main/notebooks/quickstart.ipynb) · [Try it in your browser](https://mssj-ai-org.github.io/simthinkd/#demo) · [Gradio demo](space/) · [Factory twin](examples/factory_twin/) · [Paper](docs/PAPER.md) · [Reproduce the paper](docs/REPRODUCE.md) · [Figures](docs/FIGURES.md) · [Protocol](docs/PROTOCOL.md)
22
+ <p align="center"><a href="https://mssj-ai-org.github.io/simthinkd/#demo">Try in your browser</a> · <a href="assets/factory_fallback.mp4">Watch the video (72 s)</a> · <a href="examples/factory_twin/">Factory code</a> · <a href="docs/PAPER.md">Paper</a></p>
25
23
 
26
- </div>
27
-
28
- ### Video: the internet goes down, the line keeps going
29
-
30
- <a href="assets/factory_fallback.mp4"><img src="assets/factory_fallback_poster.jpg" alt="Internet down. The line kept going." width="100%"></a>
24
+ ```bash
25
+ pip install simthinkd
26
+ ```
31
27
 
32
- A cloud decision service runs a simulated inspection line, with SimThink D on the factory PC as its backup. When an answer does not come back in time, or the network is cut, SimThink D makes the decision. [Watch the video](assets/factory_fallback.mp4) (72 s) or see it [on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7510142949981057024/). The simulator is in [examples/factory_twin](examples/factory_twin/).
28
+ What makes the next decision when your network goes down?
33
29
 
34
30
  ## Words used here
35
31
 
@@ -151,6 +147,12 @@ The second line measures any server that accepts the [decision request](docs/PRO
151
147
 
152
148
  ## Same CPU, same states
153
149
 
150
+ <p align="center">
151
+ <img src="assets/side_by_side.gif" alt="Same Doom game, same seed, same CPU. Left: SimThink D answers every tick. Right: a 421M-parameter general decision model, used as published, misses most ticks while it thinks." width="100%" />
152
+ </p>
153
+
154
+ <p align="center"><sub>Same game, same seed, same 6-core CPU. Left: SimThink D, 1.9 ms per decision, 1 of 420 ticks missed. Right: Laya, a 421M-parameter open decision model, used as published without training on this game. It takes about 360 ms per decision and misses 390 of 420 ticks. A dark frame means the game moved on before the decider answered.</sub></p>
155
+
154
156
  **Read this first.** Laya is a general model and was not trained on this game. Its published speed, about 33 ms per question, is on a GPU. We only had a CPU. So this table compares time inside a real-time loop. It does not compare overall quality.
155
157
 
156
158
  We used one workstation CPU (6 threads) and 1,050 Doom states, then 10 live games on the same seeds. "Missed ticks" are ticks that passed before the decider answered.
@@ -170,6 +172,7 @@ SimThink D only knows what its teacher knows. It does not reason, read long text
170
172
  | Unity / C# | [docs/INTEGRATION_UNITY.md](docs/INTEGRATION_UNITY.md): a client loop that keeps the game running while it waits |
171
173
  | Browser | [web/](web/): the same model in plain JavaScript, no server |
172
174
  | A factory line (simulator) | [examples/factory_twin/](examples/factory_twin/): an inspection conveyor with a 400 ms deadline per part |
175
+ | Gradio | [space/](space/): a small web demo you can run locally or on Hugging Face Spaces |
173
176
  | MCP (Claude Desktop, Cursor and others) | `pip install "simthinkd[mcp]"`, then `python -m simthinkd.integrations.mcp_server` |
174
177
  | LangChain / LangGraph | `from simthinkd.integrations.langchain_tool import simthinkd_tool` |
175
178
 
@@ -188,6 +191,13 @@ SimThink D only knows what its teacher knows. It does not reason, read long text
188
191
  - Probabilities are calibrated for the decider's own task only.
189
192
  - A score model returns one number. It gives no probability or error bar with it.
190
193
 
194
+ ## More
195
+
196
+ - [Figures from the paper](docs/FIGURES.md)
197
+ - [What you can reproduce](docs/REPRODUCE.md)
198
+ - [Decision request format](docs/PROTOCOL.md)
199
+ - [The factory video on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7510142949981057024/)
200
+
191
201
  ## Citation
192
202
 
193
203
  If you use SimThink D, please cite it with [CITATION.cff](CITATION.cff). GitHub shows a "Cite this repository" button for it.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simthinkd"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "A 265k-parameter decision model that picks one action in about 2 ms on one CPU core, inside real-time loops."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -6,7 +6,7 @@
6
6
  from .core import Decider, Decision, available, build_request
7
7
  from .score import Score, Scorer
8
8
 
9
- __version__ = '0.2.0'
9
+ __version__ = '0.2.1'
10
10
  __all__ = ['Decider', 'Decision', 'Score', 'Scorer', 'available', 'build_request', 'fit', 'fit_score', '__version__']
11
11
 
12
12
 
@@ -5,6 +5,7 @@
5
5
  Binds to 127.0.0.1 by default. `--delay-ms` adds a fixed wait after inference (latency-injection experiments).
6
6
  """
7
7
  import json
8
+ import socket
8
9
  import time
9
10
  from datetime import datetime, timezone
10
11
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
@@ -52,9 +53,23 @@ def make_handler(decider, name, delay_ms):
52
53
  return Handler
53
54
 
54
55
 
56
+ class NoDelayHTTPServer(ThreadingHTTPServer):
57
+ """Turns off Nagle's algorithm on every connection.
58
+
59
+ The handler writes the headers and the body separately. On a kept-alive connection the second small write waits
60
+ for the client's delayed ACK, so every answer took about 40 ms (measured 2026-10-03: median 42 ms kept-alive vs
61
+ 1.4 ms with TCP_NODELAY). Clients that reuse connections (Java HttpURLConnection does) missed 30 ms deadlines.
62
+ """
63
+
64
+ def get_request(self):
65
+ sock, addr = super().get_request()
66
+ sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
67
+ return sock, addr
68
+
69
+
55
70
  def serve(decider='doom-defend', host='127.0.0.1', port=11890, delay_ms=0.0, name=None):
56
71
  decider = decider if isinstance(decider, Decider) else Decider(decider)
57
72
  name = name or f'simthink-d:{decider.preset["name"]}'
58
- server = ThreadingHTTPServer((host, port), make_handler(decider, name, delay_ms))
73
+ server = NoDelayHTTPServer((host, port), make_handler(decider, name, delay_ms))
59
74
  print(json.dumps({'listening': f'{host}:{port}', 'weights_sha256': decider.sha256, 'model': name}), flush=True)
60
75
  server.serve_forever()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simthinkd
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: A 265k-parameter decision model that picks one action in about 2 ms on one CPU core, inside real-time loops.
5
5
  Author: Myeongseongsimjae AX Institute
6
6
  License: Apache-2.0
@@ -28,7 +28,7 @@ Requires-Dist: onnx>=1.15; extra == "onnx"
28
28
  Requires-Dist: onnxruntime>=1.17; extra == "onnx"
29
29
  Dynamic: license-file
30
30
 
31
- <p align="center"><img src="assets/banner.png" alt="SimThink D: a tiny decision model that runs on one CPU core" width="100%"></p>
31
+ <p align="center"><img src="assets/banner_v2.png" alt="SimThink D: a local backup for cloud decisions" width="100%"></p>
32
32
 
33
33
  <p align="center">
34
34
  <a href="https://pypi.org/project/simthinkd/"><img src="https://img.shields.io/pypi/v/simthinkd" alt="PyPI"></a>
@@ -39,27 +39,23 @@ Dynamic: license-file
39
39
  <a href="https://doi.org/10.5281/zenodo.23111615"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23111615.svg" alt="DOI"></a>
40
40
  </p>
41
41
 
42
- <p align="center"><a href="https://mssj-ai-org.github.io/simthinkd/"><b>Project page, live demo and video →</b></a></p>
42
+ <p align="center"><b>Network down. Decisions stay local.</b></p>
43
43
 
44
- **A tiny decision model that runs on one CPU core.** It has 265,665 parameters. It picks one action in about 2 ms. That is fast enough to decide inside every tick of a game or a control loop. No GPU is needed, not even for training.
44
+ SimThink D is a small CPU model for offline backup decisions.
45
+ It has 265,665 parameters and takes about 2 ms per decision on one CPU core.
45
46
 
46
- <p align="center">
47
- <img src="assets/side_by_side.gif" alt="Same Doom game, same seed, same CPU. Left: SimThink D answers every tick. Right: a 421M-parameter general decision model, used as published, misses most ticks while it thinks." width="100%" />
48
- </p>
49
-
50
- <p align="center"><sub>Same game, same seed, same 6-core CPU. Left: SimThink D, 1.9 ms per decision, 1 of 420 ticks missed. Right: Laya, a 421M-parameter open decision model, used as published without training on this game. It takes about 360 ms per decision and misses 390 of 420 ticks. A dark frame means the game moved on before the decider answered.</sub></p>
47
+ In the factory simulation, the internet was cut for 15 seconds.
48
+ The local backup got 59 of 63 parts right, with no late decisions.
51
49
 
52
- <div align="center">
50
+ <p align="center"><a href="assets/factory_fallback.mp4"><img src="assets/factory_fallback_poster.jpg" alt="Watch the factory simulation: local backup during a network outage" width="100%"></a></p>
53
51
 
54
- [Open in Colab](https://colab.research.google.com/github/MSSJ-AI-ORG/simthinkd/blob/main/notebooks/quickstart.ipynb) · [Try it in your browser](https://mssj-ai-org.github.io/simthinkd/#demo) · [Gradio demo](space/) · [Factory twin](examples/factory_twin/) · [Paper](docs/PAPER.md) · [Reproduce the paper](docs/REPRODUCE.md) · [Figures](docs/FIGURES.md) · [Protocol](docs/PROTOCOL.md)
52
+ <p align="center"><a href="https://mssj-ai-org.github.io/simthinkd/#demo">Try in your browser</a> · <a href="assets/factory_fallback.mp4">Watch the video (72 s)</a> · <a href="examples/factory_twin/">Factory code</a> · <a href="docs/PAPER.md">Paper</a></p>
55
53
 
56
- </div>
57
-
58
- ### Video: the internet goes down, the line keeps going
59
-
60
- <a href="assets/factory_fallback.mp4"><img src="assets/factory_fallback_poster.jpg" alt="Internet down. The line kept going." width="100%"></a>
54
+ ```bash
55
+ pip install simthinkd
56
+ ```
61
57
 
62
- A cloud decision service runs a simulated inspection line, with SimThink D on the factory PC as its backup. When an answer does not come back in time, or the network is cut, SimThink D makes the decision. [Watch the video](assets/factory_fallback.mp4) (72 s) or see it [on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7510142949981057024/). The simulator is in [examples/factory_twin](examples/factory_twin/).
58
+ What makes the next decision when your network goes down?
63
59
 
64
60
  ## Words used here
65
61
 
@@ -181,6 +177,12 @@ The second line measures any server that accepts the [decision request](docs/PRO
181
177
 
182
178
  ## Same CPU, same states
183
179
 
180
+ <p align="center">
181
+ <img src="assets/side_by_side.gif" alt="Same Doom game, same seed, same CPU. Left: SimThink D answers every tick. Right: a 421M-parameter general decision model, used as published, misses most ticks while it thinks." width="100%" />
182
+ </p>
183
+
184
+ <p align="center"><sub>Same game, same seed, same 6-core CPU. Left: SimThink D, 1.9 ms per decision, 1 of 420 ticks missed. Right: Laya, a 421M-parameter open decision model, used as published without training on this game. It takes about 360 ms per decision and misses 390 of 420 ticks. A dark frame means the game moved on before the decider answered.</sub></p>
185
+
184
186
  **Read this first.** Laya is a general model and was not trained on this game. Its published speed, about 33 ms per question, is on a GPU. We only had a CPU. So this table compares time inside a real-time loop. It does not compare overall quality.
185
187
 
186
188
  We used one workstation CPU (6 threads) and 1,050 Doom states, then 10 live games on the same seeds. "Missed ticks" are ticks that passed before the decider answered.
@@ -200,6 +202,7 @@ SimThink D only knows what its teacher knows. It does not reason, read long text
200
202
  | Unity / C# | [docs/INTEGRATION_UNITY.md](docs/INTEGRATION_UNITY.md): a client loop that keeps the game running while it waits |
201
203
  | Browser | [web/](web/): the same model in plain JavaScript, no server |
202
204
  | A factory line (simulator) | [examples/factory_twin/](examples/factory_twin/): an inspection conveyor with a 400 ms deadline per part |
205
+ | Gradio | [space/](space/): a small web demo you can run locally or on Hugging Face Spaces |
203
206
  | MCP (Claude Desktop, Cursor and others) | `pip install "simthinkd[mcp]"`, then `python -m simthinkd.integrations.mcp_server` |
204
207
  | LangChain / LangGraph | `from simthinkd.integrations.langchain_tool import simthinkd_tool` |
205
208
 
@@ -218,6 +221,13 @@ SimThink D only knows what its teacher knows. It does not reason, read long text
218
221
  - Probabilities are calibrated for the decider's own task only.
219
222
  - A score model returns one number. It gives no probability or error bar with it.
220
223
 
224
+ ## More
225
+
226
+ - [Figures from the paper](docs/FIGURES.md)
227
+ - [What you can reproduce](docs/REPRODUCE.md)
228
+ - [Decision request format](docs/PROTOCOL.md)
229
+ - [The factory video on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7510142949981057024/)
230
+
221
231
  ## Citation
222
232
 
223
233
  If you use SimThink D, please cite it with [CITATION.cff](CITATION.cff). GitHub shows a "Cite this repository" button for it.
@@ -28,6 +28,7 @@ tests/test_factory_twin.py
28
28
  tests/test_notebook.py
29
29
  tests/test_package.py
30
30
  tests/test_score.py
31
+ tests/test_server_latency.py
31
32
  tests/test_space.py
32
33
  tests/test_web_page.py
33
34
  tests/test_web_parity.py
@@ -0,0 +1,30 @@
1
+ """The HTTP server must answer fast on a kept-alive connection (regression: ~40 ms Nagle/delayed-ACK stall in 0.2.0)."""
2
+ import http.client
3
+ import statistics
4
+ import threading
5
+ import time
6
+
7
+ from simthinkd.core import Decider
8
+ from simthinkd.server import NoDelayHTTPServer, make_handler
9
+
10
+
11
+ def test_keep_alive_answers_fast():
12
+ decider = Decider('doom-defend')
13
+ server = NoDelayHTTPServer(('127.0.0.1', 0), make_handler(decider, 'test', 0.0))
14
+ threading.Thread(target=server.serve_forever, daemon=True).start()
15
+ try:
16
+ conn = http.client.HTTPConnection('127.0.0.1', server.server_address[1], timeout=2)
17
+ times = []
18
+ for _ in range(30):
19
+ start = time.perf_counter()
20
+ conn.request('GET', '/health')
21
+ conn.getresponse().read()
22
+ times.append((time.perf_counter() - start) * 1000)
23
+ assert statistics.median(times) < 20, times
24
+ finally:
25
+ server.shutdown()
26
+
27
+
28
+ if __name__ == "__main__":
29
+ test_keep_alive_answers_fast()
30
+ print("server latency test passed")
File without changes
File without changes
File without changes
File without changes
File without changes