stabbur 0.6.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 (153) hide show
  1. stabbur-0.6.0/LICENSE +24 -0
  2. stabbur-0.6.0/PKG-INFO +232 -0
  3. stabbur-0.6.0/README.md +184 -0
  4. stabbur-0.6.0/pyproject.toml +258 -0
  5. stabbur-0.6.0/pyproject.toml.orig +295 -0
  6. stabbur-0.6.0/src/stabbur/__init__.py +37 -0
  7. stabbur-0.6.0/src/stabbur/agent.py +332 -0
  8. stabbur-0.6.0/src/stabbur/app.py +333 -0
  9. stabbur-0.6.0/src/stabbur/arch.py +49 -0
  10. stabbur-0.6.0/src/stabbur/attach.py +71 -0
  11. stabbur-0.6.0/src/stabbur/benchmark/__init__.py +70 -0
  12. stabbur-0.6.0/src/stabbur/benchmark/__main__.py +6 -0
  13. stabbur-0.6.0/src/stabbur/benchmark/app.py +58 -0
  14. stabbur-0.6.0/src/stabbur/benchmark/core.py +622 -0
  15. stabbur-0.6.0/src/stabbur/benchmark/dhis2_state.py +168 -0
  16. stabbur-0.6.0/src/stabbur/benchmark/plugin.py +269 -0
  17. stabbur-0.6.0/src/stabbur/benchmark/suites/python.toml +166 -0
  18. stabbur-0.6.0/src/stabbur/benchmark/suites/rust.toml +178 -0
  19. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-datetime.toml +42 -0
  20. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-dhis2-write.toml +104 -0
  21. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-dhis2.toml +123 -0
  22. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-exec.toml +22 -0
  23. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-memory.toml +24 -0
  24. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-search.toml +23 -0
  25. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-utils.toml +50 -0
  26. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-weather-yr.toml +24 -0
  27. stabbur-0.6.0/src/stabbur/benchmark/suites/tools-web.toml +25 -0
  28. stabbur-0.6.0/src/stabbur/capabilities.py +283 -0
  29. stabbur-0.6.0/src/stabbur/cards.py +95 -0
  30. stabbur-0.6.0/src/stabbur/catalog.py +88 -0
  31. stabbur-0.6.0/src/stabbur/chat_tui/__init__.py +12 -0
  32. stabbur-0.6.0/src/stabbur/chat_tui/_util.py +77 -0
  33. stabbur-0.6.0/src/stabbur/chat_tui/_widgets.py +254 -0
  34. stabbur-0.6.0/src/stabbur/chat_tui/app.py +1158 -0
  35. stabbur-0.6.0/src/stabbur/chatui.py +32 -0
  36. stabbur-0.6.0/src/stabbur/cli/__init__.py +38 -0
  37. stabbur-0.6.0/src/stabbur/cli/_app.py +216 -0
  38. stabbur-0.6.0/src/stabbur/cli/_common.py +312 -0
  39. stabbur-0.6.0/src/stabbur/cli/chat.py +726 -0
  40. stabbur-0.6.0/src/stabbur/cli/config.py +90 -0
  41. stabbur-0.6.0/src/stabbur/cli/ext_dev.py +108 -0
  42. stabbur-0.6.0/src/stabbur/cli/health.py +214 -0
  43. stabbur-0.6.0/src/stabbur/cli/library.py +840 -0
  44. stabbur-0.6.0/src/stabbur/cli/mcp.py +219 -0
  45. stabbur-0.6.0/src/stabbur/cli/project.py +440 -0
  46. stabbur-0.6.0/src/stabbur/cli/serve.py +211 -0
  47. stabbur-0.6.0/src/stabbur/cli/voice.py +275 -0
  48. stabbur-0.6.0/src/stabbur/config.py +314 -0
  49. stabbur-0.6.0/src/stabbur/consumers.py +264 -0
  50. stabbur-0.6.0/src/stabbur/doctor.py +452 -0
  51. stabbur-0.6.0/src/stabbur/fsatomic.py +35 -0
  52. stabbur-0.6.0/src/stabbur/hfcache.py +51 -0
  53. stabbur-0.6.0/src/stabbur/host.py +108 -0
  54. stabbur-0.6.0/src/stabbur/library/__init__.py +39 -0
  55. stabbur-0.6.0/src/stabbur/library/_manage.py +251 -0
  56. stabbur-0.6.0/src/stabbur/library/_model.py +185 -0
  57. stabbur-0.6.0/src/stabbur/library/_roots.py +92 -0
  58. stabbur-0.6.0/src/stabbur/library/_scan.py +252 -0
  59. stabbur-0.6.0/src/stabbur/locking.py +55 -0
  60. stabbur-0.6.0/src/stabbur/mcp_catalog.py +337 -0
  61. stabbur-0.6.0/src/stabbur/mcp_servers/__init__.py +6 -0
  62. stabbur-0.6.0/src/stabbur/mcp_servers/datetime/__init__.py +28 -0
  63. stabbur-0.6.0/src/stabbur/mcp_servers/datetime/__main__.py +6 -0
  64. stabbur-0.6.0/src/stabbur/mcp_servers/datetime/app.py +329 -0
  65. stabbur-0.6.0/src/stabbur/mcp_servers/datetime/plugin.py +30 -0
  66. stabbur-0.6.0/src/stabbur/mcp_servers/exec/__init__.py +27 -0
  67. stabbur-0.6.0/src/stabbur/mcp_servers/exec/__main__.py +6 -0
  68. stabbur-0.6.0/src/stabbur/mcp_servers/exec/app.py +43 -0
  69. stabbur-0.6.0/src/stabbur/mcp_servers/exec/plugin.py +29 -0
  70. stabbur-0.6.0/src/stabbur/mcp_servers/files/__init__.py +27 -0
  71. stabbur-0.6.0/src/stabbur/mcp_servers/files/__main__.py +6 -0
  72. stabbur-0.6.0/src/stabbur/mcp_servers/files/app.py +60 -0
  73. stabbur-0.6.0/src/stabbur/mcp_servers/files/core.py +153 -0
  74. stabbur-0.6.0/src/stabbur/mcp_servers/files/plugin.py +52 -0
  75. stabbur-0.6.0/src/stabbur/mcp_servers/git/__init__.py +23 -0
  76. stabbur-0.6.0/src/stabbur/mcp_servers/git/__main__.py +6 -0
  77. stabbur-0.6.0/src/stabbur/mcp_servers/git/app.py +250 -0
  78. stabbur-0.6.0/src/stabbur/mcp_servers/git/plugin.py +43 -0
  79. stabbur-0.6.0/src/stabbur/mcp_servers/http/__init__.py +23 -0
  80. stabbur-0.6.0/src/stabbur/mcp_servers/http/__main__.py +6 -0
  81. stabbur-0.6.0/src/stabbur/mcp_servers/http/app.py +282 -0
  82. stabbur-0.6.0/src/stabbur/mcp_servers/http/plugin.py +50 -0
  83. stabbur-0.6.0/src/stabbur/mcp_servers/memory/__init__.py +28 -0
  84. stabbur-0.6.0/src/stabbur/mcp_servers/memory/__main__.py +6 -0
  85. stabbur-0.6.0/src/stabbur/mcp_servers/memory/app.py +78 -0
  86. stabbur-0.6.0/src/stabbur/mcp_servers/memory/core.py +119 -0
  87. stabbur-0.6.0/src/stabbur/mcp_servers/memory/plugin.py +50 -0
  88. stabbur-0.6.0/src/stabbur/mcp_servers/network/__init__.py +23 -0
  89. stabbur-0.6.0/src/stabbur/mcp_servers/network/__main__.py +6 -0
  90. stabbur-0.6.0/src/stabbur/mcp_servers/network/app.py +156 -0
  91. stabbur-0.6.0/src/stabbur/mcp_servers/network/plugin.py +29 -0
  92. stabbur-0.6.0/src/stabbur/mcp_servers/search/__init__.py +28 -0
  93. stabbur-0.6.0/src/stabbur/mcp_servers/search/__main__.py +6 -0
  94. stabbur-0.6.0/src/stabbur/mcp_servers/search/app.py +195 -0
  95. stabbur-0.6.0/src/stabbur/mcp_servers/search/plugin.py +44 -0
  96. stabbur-0.6.0/src/stabbur/mcp_servers/shell/__init__.py +23 -0
  97. stabbur-0.6.0/src/stabbur/mcp_servers/shell/__main__.py +6 -0
  98. stabbur-0.6.0/src/stabbur/mcp_servers/shell/app.py +230 -0
  99. stabbur-0.6.0/src/stabbur/mcp_servers/shell/plugin.py +29 -0
  100. stabbur-0.6.0/src/stabbur/mcp_servers/utils/__init__.py +27 -0
  101. stabbur-0.6.0/src/stabbur/mcp_servers/utils/__main__.py +6 -0
  102. stabbur-0.6.0/src/stabbur/mcp_servers/utils/app.py +306 -0
  103. stabbur-0.6.0/src/stabbur/mcp_servers/utils/plugin.py +29 -0
  104. stabbur-0.6.0/src/stabbur/mcp_servers/weather_yr/__init__.py +28 -0
  105. stabbur-0.6.0/src/stabbur/mcp_servers/weather_yr/__main__.py +6 -0
  106. stabbur-0.6.0/src/stabbur/mcp_servers/weather_yr/app.py +55 -0
  107. stabbur-0.6.0/src/stabbur/mcp_servers/weather_yr/core.py +183 -0
  108. stabbur-0.6.0/src/stabbur/mcp_servers/weather_yr/plugin.py +30 -0
  109. stabbur-0.6.0/src/stabbur/mcp_servers/web/__init__.py +28 -0
  110. stabbur-0.6.0/src/stabbur/mcp_servers/web/__main__.py +6 -0
  111. stabbur-0.6.0/src/stabbur/mcp_servers/web/app.py +357 -0
  112. stabbur-0.6.0/src/stabbur/mcp_servers/web/plugin.py +48 -0
  113. stabbur-0.6.0/src/stabbur/mcpservers.py +177 -0
  114. stabbur-0.6.0/src/stabbur/models.py +234 -0
  115. stabbur-0.6.0/src/stabbur/plugins.py +168 -0
  116. stabbur-0.6.0/src/stabbur/project/__init__.py +630 -0
  117. stabbur-0.6.0/src/stabbur/project/scaffold.py +203 -0
  118. stabbur-0.6.0/src/stabbur/project/templates.py +674 -0
  119. stabbur-0.6.0/src/stabbur/routers/__init__.py +1 -0
  120. stabbur-0.6.0/src/stabbur/routers/catalog.py +37 -0
  121. stabbur-0.6.0/src/stabbur/routers/health.py +11 -0
  122. stabbur-0.6.0/src/stabbur/routers/serving/__init__.py +29 -0
  123. stabbur-0.6.0/src/stabbur/routers/serving/_base.py +88 -0
  124. stabbur-0.6.0/src/stabbur/routers/serving/assistant.py +611 -0
  125. stabbur-0.6.0/src/stabbur/routers/serving/chat.py +369 -0
  126. stabbur-0.6.0/src/stabbur/routers/serving/core.py +434 -0
  127. stabbur-0.6.0/src/stabbur/routers/serving/mcp.py +211 -0
  128. stabbur-0.6.0/src/stabbur/routers/serving/proxy.py +69 -0
  129. stabbur-0.6.0/src/stabbur/routers/serving/voice.py +286 -0
  130. stabbur-0.6.0/src/stabbur/runtime/__init__.py +317 -0
  131. stabbur-0.6.0/src/stabbur/runtime/sampling.py +99 -0
  132. stabbur-0.6.0/src/stabbur/runtime/serve_registry.py +122 -0
  133. stabbur-0.6.0/src/stabbur/runtime/supervisor.py +325 -0
  134. stabbur-0.6.0/src/stabbur/server.py +425 -0
  135. stabbur-0.6.0/src/stabbur/sources/__init__.py +5 -0
  136. stabbur-0.6.0/src/stabbur/sources/base.py +132 -0
  137. stabbur-0.6.0/src/stabbur/sources/huggingface.py +252 -0
  138. stabbur-0.6.0/src/stabbur/sources/lmstudio.py +128 -0
  139. stabbur-0.6.0/src/stabbur/sources/ollama.py +415 -0
  140. stabbur-0.6.0/src/stabbur/tags.py +163 -0
  141. stabbur-0.6.0/src/stabbur/targets.py +180 -0
  142. stabbur-0.6.0/src/stabbur/tools.py +657 -0
  143. stabbur-0.6.0/src/stabbur/transcript.py +45 -0
  144. stabbur-0.6.0/src/stabbur/userconfig.py +111 -0
  145. stabbur-0.6.0/src/stabbur/voice/__init__.py +25 -0
  146. stabbur-0.6.0/src/stabbur/voice/audio.py +64 -0
  147. stabbur-0.6.0/src/stabbur/voice/catalog.py +111 -0
  148. stabbur-0.6.0/src/stabbur/voice/importer.py +170 -0
  149. stabbur-0.6.0/src/stabbur/voice/kokoro.py +228 -0
  150. stabbur-0.6.0/src/stabbur/voice/registry.py +171 -0
  151. stabbur-0.6.0/src/stabbur/voice/runtime.py +133 -0
  152. stabbur-0.6.0/src/stabbur/voice/tts.py +48 -0
  153. stabbur-0.6.0/src/stabbur/wantlist.py +208 -0
stabbur-0.6.0/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ stabbur — Proprietary Software License
2
+
3
+ Copyright (c) 2026 Morten Hansen. All rights reserved.
4
+
5
+ This software and its source code (the "Software") are the exclusive property of
6
+ Morten Hansen (the "Owner"). The Software is made source-available for reference
7
+ and evaluation only. It is NOT open-source and is NOT licensed for use.
8
+
9
+ No license or right is granted, whether express or implied, to any person or
10
+ entity to use, copy, run, execute, deploy, host, modify, adapt, translate,
11
+ create derivative works of, distribute, sublicense, sell, or otherwise exploit
12
+ the Software, in whole or in part, for any purpose, without the prior written
13
+ permission of the Owner.
14
+
15
+ Viewing the source in the public repository does not grant any right to use it.
16
+ Any use of the Software requires a separate written license agreement with the
17
+ Owner. To request permission, contact morten@winterop.com.
18
+
19
+ Unless required by applicable law or agreed to in writing, the Software is
20
+ provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied, including
21
+ but not limited to the warranties of merchantability, fitness for a particular
22
+ purpose, and non-infringement. In no event shall the Owner be liable for any
23
+ claim, damages, or other liability arising from, out of, or in connection with
24
+ the Software or its use.
stabbur-0.6.0/PKG-INFO ADDED
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.4
2
+ Name: stabbur
3
+ Version: 0.6.0
4
+ Summary: Stabbur — build a local LLM library, then run, chat, and serve it — fully on-box
5
+ Keywords: llm,local-llm,self-hosted,mcp,agent,dhis2,fastapi,huggingface
6
+ License-Expression: LicenseRef-Proprietary
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Operating System :: MacOS
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Dist: fastapi[standard]>=0.138.2
16
+ Requires-Dist: fastmcp>=3.4.2
17
+ Requires-Dist: hf-xet>=1.5.1
18
+ Requires-Dist: httpx>=0.28.1
19
+ Requires-Dist: huggingface-hub>=1.21.0
20
+ Requires-Dist: pydantic>=2.0
21
+ Requires-Dist: kokoro-onnx>=0.4.9
22
+ Requires-Dist: pluginkit>=0.4.7
23
+ Requires-Dist: pydantic-settings>=2.14.2
24
+ Requires-Dist: soundfile>=0.13.1
25
+ Requires-Dist: textual>=0.85
26
+ Requires-Dist: typer>=0.25.1
27
+ Requires-Dist: uvicorn[standard]>=0.49.0
28
+ Requires-Dist: mlx-lm>=0.31.3 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'mlx'
29
+ Requires-Dist: mlx-vlm>=0.6.3 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'mlx'
30
+ Requires-Dist: transformers>=5.5,<5.13 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'mlx'
31
+ Requires-Dist: mlx-audio>=0.4.4 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'voice'
32
+ Requires-Dist: misaki[en]>=0.9.4 ; extra == 'voice'
33
+ Requires-Dist: soundfile>=0.13.1 ; extra == 'voice'
34
+ Requires-Dist: soxr>=0.5 ; extra == 'voice'
35
+ Requires-Dist: transformers>=5.5,<5.13 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'voice'
36
+ Requires-Dist: playwright>=1.47 ; extra == 'web'
37
+ Requires-Dist: trafilatura>=1.12 ; extra == 'web'
38
+ Requires-Python: >=3.13
39
+ Project-URL: Homepage, https://github.com/winterop-com/stabbur
40
+ Project-URL: Repository, https://github.com/winterop-com/stabbur
41
+ Project-URL: Documentation, https://github.com/winterop-com/stabbur/tree/main/docs
42
+ Project-URL: Issues, https://github.com/winterop-com/stabbur/issues
43
+ Provides-Extra: benchmark
44
+ Provides-Extra: mlx
45
+ Provides-Extra: voice
46
+ Provides-Extra: web
47
+ Description-Content-Type: text/markdown
48
+
49
+ <!-- Absolute URLs: this README is the PyPI long description, and PyPI does not rewrite
50
+ relative paths - a repo-relative src renders as a broken image on the project page. -->
51
+ <div align="center">
52
+
53
+ <img src="https://raw.githubusercontent.com/winterop-com/stabbur/main/docs/assets/logo.png" alt="stabbur" width="200">
54
+
55
+ # stabbur
56
+
57
+ [![CI](https://github.com/winterop-com/stabbur/actions/workflows/ci.yml/badge.svg)](https://github.com/winterop-com/stabbur/actions/workflows/ci.yml)
58
+ [![PyPI](https://img.shields.io/pypi/v/stabbur)](https://pypi.org/project/stabbur/)
59
+ [![Python](https://img.shields.io/badge/python-3.13-3776ab)](https://www.python.org/)
60
+ [![Packaging](https://img.shields.io/badge/install-uv-6340ac)](https://docs.astral.sh/uv/)
61
+ [![License](https://img.shields.io/badge/license-proprietary-lightgrey)](https://github.com/winterop-com/stabbur/blob/main/LICENSE)
62
+
63
+ </div>
64
+
65
+ A tool for building and keeping a **full local library of LLM models**. It
66
+ discovers models from **Hugging Face**, **Ollama**, and **LM Studio**, pulls
67
+ them into a single library (browse via a **Typer CLI** or a **browser chat UI**),
68
+ and runs them from there. The library lives under one configurable root you point
69
+ at an external drive (e.g. a 5TB drive).
70
+
71
+ **Short form.** Every command is also available as `sb` — `sb chat`, `sb serve`, `sb library ls`.
72
+
73
+ **The name.** A *stabbur* is the Norwegian storehouse — raised on pillars, off the ground and
74
+ out of the damp, where a household kept what it had gathered and wanted to keep. That is what this
75
+ is: your models, pulled in from wherever they came from and kept somewhere of your own, on your own
76
+ box, rather than in someone else's cloud.
77
+
78
+ > **Proprietary, source-available.** stabbur is not open-source. Copyright (c) 2026 Morten
79
+ > Hansen, all rights reserved (see [`LICENSE`](LICENSE)). The source is published for
80
+ > reference and evaluation; running it requires a written license — contact
81
+ > <morten@winterop.com>. Install is from source with `uv` (there is no `pip install stabbur`).
82
+
83
+ ![stabbur web UI](https://raw.githubusercontent.com/winterop-com/stabbur/main/docs/assets/web-ui.png)
84
+
85
+ ## Layout
86
+
87
+ ```
88
+ src/stabbur/
89
+ ├── config.py # Pydantic settings (STABBUR_* env vars)
90
+ ├── models.py # Catalog / entry / result models
91
+ ├── catalog.py # Aggregates listing + pull across sources
92
+ ├── library.py # Scans the on-drive library (gguf/ mlx/ voice/ ...)
93
+ ├── capabilities.py # Detects per-model tools/vision/audio + context
94
+ ├── runtime.py # Serves a model (llama.cpp / mlx_lm / mlx-vlm)
95
+ ├── voice/ # Voice models: registry, import, TTS/STT runtime
96
+ ├── cli/ # Typer CLI package, one module per command group (entry: `stabbur` → stabbur.cli:main)
97
+ ├── app.py # FastAPI app factory
98
+ ├── routers/ # health + catalog (browse/pull) + serving (load/chat/audio) endpoints
99
+ └── sources/ # huggingface / ollama / lmstudio adapters
100
+ ```
101
+
102
+ ## Setup
103
+
104
+ ```bash
105
+ uv sync # stabbur itself (needs Python 3.13 + uv)
106
+ brew install llama.cpp # baseline runtime: GGUF chat + OuteTTS speech (build from source on Linux)
107
+ make install-mlx # optional: MLX runtimes (Apple Silicon)
108
+ uv sync --extra voice # optional: mlx-audio runtimes (Dia/Whisper, Apple Silicon)
109
+ make frontend # optional: build the web UI (needs Bun)
110
+ export STABBUR_LIBRARY_ROOT=/path/to/your/library # required: where your library lives
111
+ stabbur doctor # verify what's installed
112
+ ```
113
+
114
+ **Point stabbur at a library.** stabbur won't guess a location — set `STABBUR_LIBRARY_ROOT`
115
+ (a per-machine shell/`.env` value; an external drive is the intended home). Without
116
+ it, library commands fail with a clear message instead of silently using `./data`.
117
+
118
+ **Install globally** (run `stabbur` from any directory):
119
+
120
+ ```bash
121
+ uv tool install --editable ".[mlx,voice,web,benchmark]" # from a checkout: stabbur on your PATH, code edits live
122
+ uv tool install "git+https://github.com/winterop-com/stabbur" # or straight from git (requires repo access; core CLI)
123
+ # then put STABBUR_LIBRARY_ROOT in your shell profile (~/.zshrc) so it applies everywhere
124
+ ```
125
+
126
+ Only `uv sync` + llama.cpp are needed to run GGUF models; the rest are optional.
127
+ The `benchmark` extra adds the `stabbur benchmark` eval command; drop it if you don't need it.
128
+ See [getting started](docs/getting-started.md) for details.
129
+
130
+ stabbur is installed **from source** (`uv sync` / `uv tool install -e .`), not from PyPI. It builds
131
+ as a single self-contained wheel — the bundled first-party MCP servers are vendored into the `stabbur`
132
+ package (`src/stabbur/mcp_servers/*`) rather than published as separate packages — but stabbur is
133
+ proprietary/source-available and is not distributed on PyPI.
134
+
135
+ ## CLI
136
+
137
+ ```bash
138
+ stabbur library ls # your library (the models on your drive)
139
+ stabbur library sources # models in app caches (HF/Ollama/LM Studio) you could pull
140
+ stabbur library pull lmstudio lmstudio-community/gemma-4-12B-it-QAT-GGUF
141
+ stabbur library pull ollama gemma4:31b --move # copy to the library, then delete the local copy
142
+ stabbur doctor # pre-flight: runtimes, library, project
143
+ stabbur serve --ui # browse + chat in the browser (Chat · Voice · Library)
144
+ stabbur chat gemma-4-12B-it-QAT-GGUF -p "hi" # one-shot, scriptable
145
+ stabbur chat gemma-4-12B-it-QAT-GGUF -p "?" -i pic.jpg # image input (vision model)
146
+ stabbur voice ls # voice models (TTS/STT) in the library
147
+ stabbur voice import --all # import known voice models to the library
148
+ stabbur voice speak -v af_heart "hello there" # text-to-speech (Kokoro)
149
+ stabbur voice speak --model dia --seed 10 "hi there" # Dia (pin a seed for a stable voice)
150
+ stabbur project init # scaffold a project assistant (model + tools + prompt)
151
+ ```
152
+
153
+ **Two model families:** **Chat** (language models you talk to — text in/out; some
154
+ also read images/audio or call tools) and **Voice** (TTS speaks, STT transcribes).
155
+ stabbur detects each chat model's capabilities and runs the right runtime — GGUF via
156
+ llama.cpp (`llama-server`, `--mmproj` for vision/audio), MLX via `mlx_lm`/`mlx-vlm`.
157
+ The web UI's **Library** lists both families; the **Voice** studio does TTS/STT
158
+ (Kokoro, Dia with voice cloning, Whisper); in chat you can attach images/audio,
159
+ dictate with the mic (Whisper), and **read replies aloud** (Kokoro by default). See
160
+ the [voice guide](docs/guides/voice.md).
161
+
162
+ Full docs: **[`docs/`](docs/)** (run `make docs` to serve the site locally) —
163
+ getting started, the library, pulling, running & chatting, the web UI, the Chrome side panel,
164
+ the DHIS2 assistant, and the architecture.
165
+
166
+ ## API
167
+
168
+ `stabbur serve` exposes an OpenAI-compatible surface plus browse/voice endpoints:
169
+ `/api/status`, `/api/library`, `/api/voice`, `/api/chat` (tool-aware SSE),
170
+ `/v1/*` (proxied to the loaded model), and `/v1/audio/speech` +
171
+ `/v1/audio/transcriptions`. See the [web UI guide](docs/guides/web-ui.md) for the
172
+ full endpoint table and the single-origin proxy design.
173
+
174
+ ## Chrome side panel & the DHIS2 assistant
175
+
176
+ stabbur ships an **MV3 Chrome side panel** (`extension/`, built with WXT) — a thin client for
177
+ a local or remote `stabbur serve` that puts your own model + tools next to any page. It builds
178
+ in two flavors from one codebase: the generic **stabbur** panel and **stabbur for DHIS2**
179
+ (`STABBUR_FLAVOR=dhis2`).
180
+
181
+ Pointed at a DHIS2 project (`stabbur project new --template dhis2`), it becomes the north-star
182
+ assistant: chat grounded in the page you are viewing, a target banner (verify + tab
183
+ match/mismatch), and **"Use my login"** — mint a read-only, GET-scoped Personal Access Token
184
+ in the DHIS2 tab's own security context and hand it to stabbur once, so the tools act as *you*
185
+ (with a session-cookie fallback). Everything runs against your own local model; nothing
186
+ leaves the box.
187
+
188
+ ```bash
189
+ cd extension && bun install && bun run build # -> extension/.output/chrome-mv3(-dhis2)
190
+ # chrome://extensions -> Load unpacked -> the built dir; then `stabbur serve` and open the panel
191
+ ```
192
+
193
+ See the [Chrome side panel guide](docs/guides/extension.md) and
194
+ the [verified prompt catalog](docs/guides/extension-prompts.md).
195
+
196
+ ## Configuration
197
+
198
+ Two separate concepts:
199
+
200
+ - **The library location** — where your models live. Set **`STABBUR_LIBRARY_ROOT`** (a
201
+ per-machine value; shell profile or `.env`). stabbur **requires** it — without one,
202
+ library/chat/serve commands fail with a clear message rather than silently using a
203
+ local folder. An external drive is the intended home:
204
+
205
+ ```bash
206
+ export STABBUR_LIBRARY_ROOT=/path/to/your/library # e.g. a mounted external drive
207
+ ```
208
+
209
+ - **A project** (`stabbur.toml`, via `stabbur project init` / `stabbur project new <dir>`) — a
210
+ purpose-built **assistant**: `[project].model` + `system_prompt`, with tools in a sibling
211
+ `.mcp.json` (standard `mcpServers`). In a project, `stabbur serve` / `stabbur chat` bind to that
212
+ model (like `--model`, with its tools + prompt); **outside** a project it's free-play
213
+ (pick/switch any model). A project uses the machine library by default.
214
+
215
+ Precedence (high → low): CLI flags, `STABBUR_*` env vars, `stabbur.toml`, `.env`,
216
+ `~/.config/stabbur/config.toml` (machine defaults).
217
+
218
+ | Key / env var | Purpose |
219
+ | --------------------------------------------- | ----------------------------------- |
220
+ | `STABBUR_LIBRARY_ROOT` (or `library_root`) | the library location (**required**) |
221
+ | `STABBUR_OLLAMA_MODELS_DIR`, `STABBUR_LMSTUDIO_MODELS_DIR` | source caches to pull from |
222
+ | `STABBUR_HF_TOKEN` | HF token (uses your HF login if unset) |
223
+ | `STABBUR_DEFAULT_MAX_TOKENS` (or `default_max_tokens`) | per-turn `/api/chat` generation cap (default 4096; `0` = unbounded) |
224
+
225
+ ## Develop
226
+
227
+ ```bash
228
+ make lint # ruff format + check, mypy, pyright (mutates)
229
+ make check # same, read-only (CI gate) + tests
230
+ make test # pytest
231
+ make build # uv build (wheel + sdist)
232
+ ```
@@ -0,0 +1,184 @@
1
+ <!-- Absolute URLs: this README is the PyPI long description, and PyPI does not rewrite
2
+ relative paths - a repo-relative src renders as a broken image on the project page. -->
3
+ <div align="center">
4
+
5
+ <img src="https://raw.githubusercontent.com/winterop-com/stabbur/main/docs/assets/logo.png" alt="stabbur" width="200">
6
+
7
+ # stabbur
8
+
9
+ [![CI](https://github.com/winterop-com/stabbur/actions/workflows/ci.yml/badge.svg)](https://github.com/winterop-com/stabbur/actions/workflows/ci.yml)
10
+ [![PyPI](https://img.shields.io/pypi/v/stabbur)](https://pypi.org/project/stabbur/)
11
+ [![Python](https://img.shields.io/badge/python-3.13-3776ab)](https://www.python.org/)
12
+ [![Packaging](https://img.shields.io/badge/install-uv-6340ac)](https://docs.astral.sh/uv/)
13
+ [![License](https://img.shields.io/badge/license-proprietary-lightgrey)](https://github.com/winterop-com/stabbur/blob/main/LICENSE)
14
+
15
+ </div>
16
+
17
+ A tool for building and keeping a **full local library of LLM models**. It
18
+ discovers models from **Hugging Face**, **Ollama**, and **LM Studio**, pulls
19
+ them into a single library (browse via a **Typer CLI** or a **browser chat UI**),
20
+ and runs them from there. The library lives under one configurable root you point
21
+ at an external drive (e.g. a 5TB drive).
22
+
23
+ **Short form.** Every command is also available as `sb` — `sb chat`, `sb serve`, `sb library ls`.
24
+
25
+ **The name.** A *stabbur* is the Norwegian storehouse — raised on pillars, off the ground and
26
+ out of the damp, where a household kept what it had gathered and wanted to keep. That is what this
27
+ is: your models, pulled in from wherever they came from and kept somewhere of your own, on your own
28
+ box, rather than in someone else's cloud.
29
+
30
+ > **Proprietary, source-available.** stabbur is not open-source. Copyright (c) 2026 Morten
31
+ > Hansen, all rights reserved (see [`LICENSE`](LICENSE)). The source is published for
32
+ > reference and evaluation; running it requires a written license — contact
33
+ > <morten@winterop.com>. Install is from source with `uv` (there is no `pip install stabbur`).
34
+
35
+ ![stabbur web UI](https://raw.githubusercontent.com/winterop-com/stabbur/main/docs/assets/web-ui.png)
36
+
37
+ ## Layout
38
+
39
+ ```
40
+ src/stabbur/
41
+ ├── config.py # Pydantic settings (STABBUR_* env vars)
42
+ ├── models.py # Catalog / entry / result models
43
+ ├── catalog.py # Aggregates listing + pull across sources
44
+ ├── library.py # Scans the on-drive library (gguf/ mlx/ voice/ ...)
45
+ ├── capabilities.py # Detects per-model tools/vision/audio + context
46
+ ├── runtime.py # Serves a model (llama.cpp / mlx_lm / mlx-vlm)
47
+ ├── voice/ # Voice models: registry, import, TTS/STT runtime
48
+ ├── cli/ # Typer CLI package, one module per command group (entry: `stabbur` → stabbur.cli:main)
49
+ ├── app.py # FastAPI app factory
50
+ ├── routers/ # health + catalog (browse/pull) + serving (load/chat/audio) endpoints
51
+ └── sources/ # huggingface / ollama / lmstudio adapters
52
+ ```
53
+
54
+ ## Setup
55
+
56
+ ```bash
57
+ uv sync # stabbur itself (needs Python 3.13 + uv)
58
+ brew install llama.cpp # baseline runtime: GGUF chat + OuteTTS speech (build from source on Linux)
59
+ make install-mlx # optional: MLX runtimes (Apple Silicon)
60
+ uv sync --extra voice # optional: mlx-audio runtimes (Dia/Whisper, Apple Silicon)
61
+ make frontend # optional: build the web UI (needs Bun)
62
+ export STABBUR_LIBRARY_ROOT=/path/to/your/library # required: where your library lives
63
+ stabbur doctor # verify what's installed
64
+ ```
65
+
66
+ **Point stabbur at a library.** stabbur won't guess a location — set `STABBUR_LIBRARY_ROOT`
67
+ (a per-machine shell/`.env` value; an external drive is the intended home). Without
68
+ it, library commands fail with a clear message instead of silently using `./data`.
69
+
70
+ **Install globally** (run `stabbur` from any directory):
71
+
72
+ ```bash
73
+ uv tool install --editable ".[mlx,voice,web,benchmark]" # from a checkout: stabbur on your PATH, code edits live
74
+ uv tool install "git+https://github.com/winterop-com/stabbur" # or straight from git (requires repo access; core CLI)
75
+ # then put STABBUR_LIBRARY_ROOT in your shell profile (~/.zshrc) so it applies everywhere
76
+ ```
77
+
78
+ Only `uv sync` + llama.cpp are needed to run GGUF models; the rest are optional.
79
+ The `benchmark` extra adds the `stabbur benchmark` eval command; drop it if you don't need it.
80
+ See [getting started](docs/getting-started.md) for details.
81
+
82
+ stabbur is installed **from source** (`uv sync` / `uv tool install -e .`), not from PyPI. It builds
83
+ as a single self-contained wheel — the bundled first-party MCP servers are vendored into the `stabbur`
84
+ package (`src/stabbur/mcp_servers/*`) rather than published as separate packages — but stabbur is
85
+ proprietary/source-available and is not distributed on PyPI.
86
+
87
+ ## CLI
88
+
89
+ ```bash
90
+ stabbur library ls # your library (the models on your drive)
91
+ stabbur library sources # models in app caches (HF/Ollama/LM Studio) you could pull
92
+ stabbur library pull lmstudio lmstudio-community/gemma-4-12B-it-QAT-GGUF
93
+ stabbur library pull ollama gemma4:31b --move # copy to the library, then delete the local copy
94
+ stabbur doctor # pre-flight: runtimes, library, project
95
+ stabbur serve --ui # browse + chat in the browser (Chat · Voice · Library)
96
+ stabbur chat gemma-4-12B-it-QAT-GGUF -p "hi" # one-shot, scriptable
97
+ stabbur chat gemma-4-12B-it-QAT-GGUF -p "?" -i pic.jpg # image input (vision model)
98
+ stabbur voice ls # voice models (TTS/STT) in the library
99
+ stabbur voice import --all # import known voice models to the library
100
+ stabbur voice speak -v af_heart "hello there" # text-to-speech (Kokoro)
101
+ stabbur voice speak --model dia --seed 10 "hi there" # Dia (pin a seed for a stable voice)
102
+ stabbur project init # scaffold a project assistant (model + tools + prompt)
103
+ ```
104
+
105
+ **Two model families:** **Chat** (language models you talk to — text in/out; some
106
+ also read images/audio or call tools) and **Voice** (TTS speaks, STT transcribes).
107
+ stabbur detects each chat model's capabilities and runs the right runtime — GGUF via
108
+ llama.cpp (`llama-server`, `--mmproj` for vision/audio), MLX via `mlx_lm`/`mlx-vlm`.
109
+ The web UI's **Library** lists both families; the **Voice** studio does TTS/STT
110
+ (Kokoro, Dia with voice cloning, Whisper); in chat you can attach images/audio,
111
+ dictate with the mic (Whisper), and **read replies aloud** (Kokoro by default). See
112
+ the [voice guide](docs/guides/voice.md).
113
+
114
+ Full docs: **[`docs/`](docs/)** (run `make docs` to serve the site locally) —
115
+ getting started, the library, pulling, running & chatting, the web UI, the Chrome side panel,
116
+ the DHIS2 assistant, and the architecture.
117
+
118
+ ## API
119
+
120
+ `stabbur serve` exposes an OpenAI-compatible surface plus browse/voice endpoints:
121
+ `/api/status`, `/api/library`, `/api/voice`, `/api/chat` (tool-aware SSE),
122
+ `/v1/*` (proxied to the loaded model), and `/v1/audio/speech` +
123
+ `/v1/audio/transcriptions`. See the [web UI guide](docs/guides/web-ui.md) for the
124
+ full endpoint table and the single-origin proxy design.
125
+
126
+ ## Chrome side panel & the DHIS2 assistant
127
+
128
+ stabbur ships an **MV3 Chrome side panel** (`extension/`, built with WXT) — a thin client for
129
+ a local or remote `stabbur serve` that puts your own model + tools next to any page. It builds
130
+ in two flavors from one codebase: the generic **stabbur** panel and **stabbur for DHIS2**
131
+ (`STABBUR_FLAVOR=dhis2`).
132
+
133
+ Pointed at a DHIS2 project (`stabbur project new --template dhis2`), it becomes the north-star
134
+ assistant: chat grounded in the page you are viewing, a target banner (verify + tab
135
+ match/mismatch), and **"Use my login"** — mint a read-only, GET-scoped Personal Access Token
136
+ in the DHIS2 tab's own security context and hand it to stabbur once, so the tools act as *you*
137
+ (with a session-cookie fallback). Everything runs against your own local model; nothing
138
+ leaves the box.
139
+
140
+ ```bash
141
+ cd extension && bun install && bun run build # -> extension/.output/chrome-mv3(-dhis2)
142
+ # chrome://extensions -> Load unpacked -> the built dir; then `stabbur serve` and open the panel
143
+ ```
144
+
145
+ See the [Chrome side panel guide](docs/guides/extension.md) and
146
+ the [verified prompt catalog](docs/guides/extension-prompts.md).
147
+
148
+ ## Configuration
149
+
150
+ Two separate concepts:
151
+
152
+ - **The library location** — where your models live. Set **`STABBUR_LIBRARY_ROOT`** (a
153
+ per-machine value; shell profile or `.env`). stabbur **requires** it — without one,
154
+ library/chat/serve commands fail with a clear message rather than silently using a
155
+ local folder. An external drive is the intended home:
156
+
157
+ ```bash
158
+ export STABBUR_LIBRARY_ROOT=/path/to/your/library # e.g. a mounted external drive
159
+ ```
160
+
161
+ - **A project** (`stabbur.toml`, via `stabbur project init` / `stabbur project new <dir>`) — a
162
+ purpose-built **assistant**: `[project].model` + `system_prompt`, with tools in a sibling
163
+ `.mcp.json` (standard `mcpServers`). In a project, `stabbur serve` / `stabbur chat` bind to that
164
+ model (like `--model`, with its tools + prompt); **outside** a project it's free-play
165
+ (pick/switch any model). A project uses the machine library by default.
166
+
167
+ Precedence (high → low): CLI flags, `STABBUR_*` env vars, `stabbur.toml`, `.env`,
168
+ `~/.config/stabbur/config.toml` (machine defaults).
169
+
170
+ | Key / env var | Purpose |
171
+ | --------------------------------------------- | ----------------------------------- |
172
+ | `STABBUR_LIBRARY_ROOT` (or `library_root`) | the library location (**required**) |
173
+ | `STABBUR_OLLAMA_MODELS_DIR`, `STABBUR_LMSTUDIO_MODELS_DIR` | source caches to pull from |
174
+ | `STABBUR_HF_TOKEN` | HF token (uses your HF login if unset) |
175
+ | `STABBUR_DEFAULT_MAX_TOKENS` (or `default_max_tokens`) | per-turn `/api/chat` generation cap (default 4096; `0` = unbounded) |
176
+
177
+ ## Develop
178
+
179
+ ```bash
180
+ make lint # ruff format + check, mypy, pyright (mutates)
181
+ make check # same, read-only (CI gate) + tests
182
+ make test # pytest
183
+ make build # uv build (wheel + sdist)
184
+ ```