local-embed 0.2.1__tar.gz → 0.2.2__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.
Potentially problematic release.
This version of local-embed might be problematic. Click here for more details.
- {local_embed-0.2.1 → local_embed-0.2.2}/PKG-INFO +24 -12
- {local_embed-0.2.1 → local_embed-0.2.2}/README.md +23 -11
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/cli.py +20 -12
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/cmd_set.py +16 -5
- {local_embed-0.2.1 → local_embed-0.2.2}/pyproject.toml +1 -1
- {local_embed-0.2.1 → local_embed-0.2.2}/tests/test_set.py +86 -7
- {local_embed-0.2.1 → local_embed-0.2.2}/.gitignore +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/__init__.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/__main__.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/config.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/engine.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/logging.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/server.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/server_utils.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/local_embed/threads.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/tests/test_client.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/tests/test_config.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/tests/test_engine.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/tests/test_logging.py +0 -0
- {local_embed-0.2.1 → local_embed-0.2.2}/tests/test_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: local-embed
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Standalone local embedding server — GGUF / transformer models as an OpenAI-compatible /v1/embeddings service
|
|
5
5
|
Project-URL: Homepage, https://github.com/juzcn/slife
|
|
6
6
|
Project-URL: Repository, https://github.com/juzcn/slife
|
|
@@ -60,11 +60,22 @@ OpenAI-compatible client works.
|
|
|
60
60
|
|
|
61
61
|
## Install
|
|
62
62
|
|
|
63
|
-
Requires [uv](https://docs.astral.sh/uv)
|
|
64
|
-
|
|
63
|
+
Requires [uv](https://docs.astral.sh/uv). All installs below pin
|
|
64
|
+
**Python 3.13** (`requires-python` is `>=3.13`, but 3.13 is what CI tests,
|
|
65
|
+
and uv would otherwise pick the newest 3.14 it finds). Re-running any of
|
|
66
|
+
them over an **existing** install requires `--reinstall` (`uv tool install`
|
|
67
|
+
is a no-op on an already-installed tool otherwise), e.g. to upgrade to a new
|
|
68
|
+
release or pick up the `json5` dependency fix:
|
|
65
69
|
|
|
66
70
|
```bash
|
|
67
|
-
uv tool install 'local-embed[transformer]'
|
|
71
|
+
uv tool install --python 3.13 --reinstall 'local-embed[gguf,transformer]' \
|
|
72
|
+
--extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The `transformer` backend installs the same everywhere:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv tool install --python 3.13 'local-embed[transformer]' # sentence-transformers
|
|
68
79
|
```
|
|
69
80
|
|
|
70
81
|
`gguf` (llama-cpp-python) is the platform-sensitive one: PyPI ships
|
|
@@ -75,10 +86,10 @@ for your platform:
|
|
|
75
86
|
|
|
76
87
|
| Platform | Command |
|
|
77
88
|
|---|---|
|
|
78
|
-
| Linux / macOS, CPU | `uv tool install 'local-embed[gguf]'` (PyPI wheel) |
|
|
79
|
-
| **Windows, CPU** | `uv tool install 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu` |
|
|
80
|
-
| macOS arm64 (Metal) | `uv tool install 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/metal` |
|
|
81
|
-
| CUDA (Linux or Windows) | `uv tool install 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu122` — pick `cu118` / `cu121` / `cu122` / `cu123` / `cu124` / `cu125` / `cu130` / `cu132` to match your CUDA version |
|
|
89
|
+
| Linux / macOS, CPU | `uv tool install --python 3.13 'local-embed[gguf]'` (PyPI wheel) |
|
|
90
|
+
| **Windows, CPU** | `uv tool install --python 3.13 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu` |
|
|
91
|
+
| macOS arm64 (Metal) | `uv tool install --python 3.13 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/metal` |
|
|
92
|
+
| CUDA (Linux or Windows) | `uv tool install --python 3.13 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu122` — pick `cu118` / `cu121` / `cu122` / `cu123` / `cu124` / `cu125` / `cu130` / `cu132` to match your CUDA version |
|
|
82
93
|
|
|
83
94
|
When the index is an *extra*, uv uses its wheel only when PyPI has no
|
|
84
95
|
compatible wheel for the platform (the Windows / Metal cases); on Linux
|
|
@@ -94,9 +105,9 @@ environment with the second, so they would not coexist:
|
|
|
94
105
|
|
|
95
106
|
```bash
|
|
96
107
|
# both backends, Windows CPU:
|
|
97
|
-
uv tool install 'local-embed[gguf,transformer]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
|
|
108
|
+
uv tool install --python 3.13 'local-embed[gguf,transformer]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
|
|
98
109
|
# both backends, Linux / macOS CPU:
|
|
99
|
-
uv tool install 'local-embed[gguf,transformer]'
|
|
110
|
+
uv tool install --python 3.13 'local-embed[gguf,transformer]'
|
|
100
111
|
```
|
|
101
112
|
|
|
102
113
|
Core deps are `fastmcp` + `starlette`; the model backends are optional
|
|
@@ -194,14 +205,15 @@ source of truth.
|
|
|
194
205
|
|
|
195
206
|
`local-embed set` (transformer) and `local-embed set-gguf` (gguf) upsert a
|
|
196
207
|
model in the config, make it **active**, and pin port (and, for `set`, the
|
|
197
|
-
HF cache). Both are **idempotent** — re-running yields the
|
|
198
|
-
and leave other models untouched.
|
|
208
|
+
HF cache + offline flag). Both are **idempotent** — re-running yields the
|
|
209
|
+
same config — and leave other models untouched.
|
|
199
210
|
|
|
200
211
|
| | `set` (transformer) | `set-gguf` |
|
|
201
212
|
|---|---|---|
|
|
202
213
|
| `<model_name>` | required — HF repo id (key + repo loaded) | required — config key |
|
|
203
214
|
| weight ref | `--HF_HUB_CACHE <dir>` — cache must contain the repo | `--path <PATH>` required — existing `.gguf` file |
|
|
204
215
|
| cache fallback | env `HF_HUB_CACHE`, else error | — |
|
|
216
|
+
| env written | `HF_HUB_CACHE` + `HF_HUB_OFFLINE: "1"` (offline server) | — |
|
|
205
217
|
| `--port <n>` | default `8000` | default `8000` |
|
|
206
218
|
|
|
207
219
|
```bash
|
|
@@ -34,11 +34,22 @@ OpenAI-compatible client works.
|
|
|
34
34
|
|
|
35
35
|
## Install
|
|
36
36
|
|
|
37
|
-
Requires [uv](https://docs.astral.sh/uv)
|
|
38
|
-
|
|
37
|
+
Requires [uv](https://docs.astral.sh/uv). All installs below pin
|
|
38
|
+
**Python 3.13** (`requires-python` is `>=3.13`, but 3.13 is what CI tests,
|
|
39
|
+
and uv would otherwise pick the newest 3.14 it finds). Re-running any of
|
|
40
|
+
them over an **existing** install requires `--reinstall` (`uv tool install`
|
|
41
|
+
is a no-op on an already-installed tool otherwise), e.g. to upgrade to a new
|
|
42
|
+
release or pick up the `json5` dependency fix:
|
|
39
43
|
|
|
40
44
|
```bash
|
|
41
|
-
uv tool install 'local-embed[transformer]'
|
|
45
|
+
uv tool install --python 3.13 --reinstall 'local-embed[gguf,transformer]' \
|
|
46
|
+
--extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The `transformer` backend installs the same everywhere:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv tool install --python 3.13 'local-embed[transformer]' # sentence-transformers
|
|
42
53
|
```
|
|
43
54
|
|
|
44
55
|
`gguf` (llama-cpp-python) is the platform-sensitive one: PyPI ships
|
|
@@ -49,10 +60,10 @@ for your platform:
|
|
|
49
60
|
|
|
50
61
|
| Platform | Command |
|
|
51
62
|
|---|---|
|
|
52
|
-
| Linux / macOS, CPU | `uv tool install 'local-embed[gguf]'` (PyPI wheel) |
|
|
53
|
-
| **Windows, CPU** | `uv tool install 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu` |
|
|
54
|
-
| macOS arm64 (Metal) | `uv tool install 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/metal` |
|
|
55
|
-
| CUDA (Linux or Windows) | `uv tool install 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu122` — pick `cu118` / `cu121` / `cu122` / `cu123` / `cu124` / `cu125` / `cu130` / `cu132` to match your CUDA version |
|
|
63
|
+
| Linux / macOS, CPU | `uv tool install --python 3.13 'local-embed[gguf]'` (PyPI wheel) |
|
|
64
|
+
| **Windows, CPU** | `uv tool install --python 3.13 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu` |
|
|
65
|
+
| macOS arm64 (Metal) | `uv tool install --python 3.13 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/metal` |
|
|
66
|
+
| CUDA (Linux or Windows) | `uv tool install --python 3.13 'local-embed[gguf]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu122` — pick `cu118` / `cu121` / `cu122` / `cu123` / `cu124` / `cu125` / `cu130` / `cu132` to match your CUDA version |
|
|
56
67
|
|
|
57
68
|
When the index is an *extra*, uv uses its wheel only when PyPI has no
|
|
58
69
|
compatible wheel for the platform (the Windows / Metal cases); on Linux
|
|
@@ -68,9 +79,9 @@ environment with the second, so they would not coexist:
|
|
|
68
79
|
|
|
69
80
|
```bash
|
|
70
81
|
# both backends, Windows CPU:
|
|
71
|
-
uv tool install 'local-embed[gguf,transformer]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
|
|
82
|
+
uv tool install --python 3.13 'local-embed[gguf,transformer]' --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
|
|
72
83
|
# both backends, Linux / macOS CPU:
|
|
73
|
-
uv tool install 'local-embed[gguf,transformer]'
|
|
84
|
+
uv tool install --python 3.13 'local-embed[gguf,transformer]'
|
|
74
85
|
```
|
|
75
86
|
|
|
76
87
|
Core deps are `fastmcp` + `starlette`; the model backends are optional
|
|
@@ -168,14 +179,15 @@ source of truth.
|
|
|
168
179
|
|
|
169
180
|
`local-embed set` (transformer) and `local-embed set-gguf` (gguf) upsert a
|
|
170
181
|
model in the config, make it **active**, and pin port (and, for `set`, the
|
|
171
|
-
HF cache). Both are **idempotent** — re-running yields the
|
|
172
|
-
and leave other models untouched.
|
|
182
|
+
HF cache + offline flag). Both are **idempotent** — re-running yields the
|
|
183
|
+
same config — and leave other models untouched.
|
|
173
184
|
|
|
174
185
|
| | `set` (transformer) | `set-gguf` |
|
|
175
186
|
|---|---|---|
|
|
176
187
|
| `<model_name>` | required — HF repo id (key + repo loaded) | required — config key |
|
|
177
188
|
| weight ref | `--HF_HUB_CACHE <dir>` — cache must contain the repo | `--path <PATH>` required — existing `.gguf` file |
|
|
178
189
|
| cache fallback | env `HF_HUB_CACHE`, else error | — |
|
|
190
|
+
| env written | `HF_HUB_CACHE` + `HF_HUB_OFFLINE: "1"` (offline server) | — |
|
|
179
191
|
| `--port <n>` | default `8000` | default `8000` |
|
|
180
192
|
|
|
181
193
|
```bash
|
|
@@ -123,23 +123,31 @@ def main(argv: "list[str] | None" = None) -> int:
|
|
|
123
123
|
|
|
124
124
|
engine = Engine(specs=settings["specs"], active=settings["active"])
|
|
125
125
|
|
|
126
|
-
# Validate the
|
|
127
|
-
# `check_backend_runtime`, which never imports) so the answer
|
|
128
|
-
# reality. Only the ACTIVE model
|
|
129
|
-
#
|
|
130
|
-
#
|
|
126
|
+
# Validate the active model can actually run. `resolve_backend_runtime`
|
|
127
|
+
# (not `check_backend_runtime`, which never imports) so the answer
|
|
128
|
+
# reflects reality. Only the ACTIVE model is import-checked at startup:
|
|
129
|
+
# a non-active model's backend import cost (torch!) is only worth paying
|
|
130
|
+
# when the model is actually requested — it fails at load time then,
|
|
131
|
+
# and this CLI must not stall on it (or hang an impatient Ctrl-C during
|
|
132
|
+
# the import). gguf_path is still checked for every model.
|
|
131
133
|
active_name = engine.active_model
|
|
132
134
|
for spec in settings["specs"]:
|
|
133
135
|
problems: list[str] = []
|
|
134
136
|
if spec.backend == "gguf" and not spec.gguf_path:
|
|
135
137
|
problems.append("no gguf_path (set gguf_path in local_embed.json5)")
|
|
136
|
-
if
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
if spec.name == active_name:
|
|
139
|
+
try:
|
|
140
|
+
installed = resolve_backend_runtime(spec.backend)
|
|
141
|
+
except KeyboardInterrupt:
|
|
142
|
+
print("Interrupted — local-embed not started.", file=sys.stderr)
|
|
143
|
+
return 130
|
|
144
|
+
if not installed:
|
|
145
|
+
from local_embed.cmd_set import backend_install_hint
|
|
146
|
+
|
|
147
|
+
problems.append(
|
|
148
|
+
f"{spec.backend} backend not installed "
|
|
149
|
+
f"({backend_install_hint(spec.backend)})"
|
|
150
|
+
)
|
|
143
151
|
if spec.name == active_name and problems:
|
|
144
152
|
print(
|
|
145
153
|
f"Error: active model '{spec.name}' cannot start: {'; '.join(problems)}",
|
|
@@ -24,6 +24,7 @@ from pathlib import Path
|
|
|
24
24
|
from local_embed.config import load_config, write_config
|
|
25
25
|
|
|
26
26
|
ENV_CACHE_KEY = "HF_HUB_CACHE"
|
|
27
|
+
ENV_OFFLINE_KEY = "HF_HUB_OFFLINE"
|
|
27
28
|
|
|
28
29
|
# Official prebuilt-wheel indexes for llama-cpp-python (the upstream llama.cpp
|
|
29
30
|
# project). PyPI carries no Windows wheels for llama-cpp-python, so on
|
|
@@ -42,16 +43,19 @@ def backend_install_hint(backend: str, platform: "str | None" = None) -> str:
|
|
|
42
43
|
must be added or uv falls back to a source build (MSVC + CMake). On
|
|
43
44
|
Linux/macOS the plain PyPI wheel is fine. (CUDA / Metal need their own
|
|
44
45
|
indexes — see the README install matrix.)
|
|
46
|
+
|
|
47
|
+
The command always pins ``--python 3.13``: it is the version CI tests,
|
|
48
|
+
and without it uv would silently pick the newest Python it finds.
|
|
45
49
|
"""
|
|
46
50
|
platform = platform or sys.platform
|
|
47
51
|
if backend == "gguf":
|
|
48
52
|
if platform == "win32":
|
|
49
53
|
return (
|
|
50
|
-
"uv tool install 'local-embed[gguf]' "
|
|
54
|
+
"uv tool install --python 3.13 'local-embed[gguf]' "
|
|
51
55
|
f"--extra-index-url {LLAMA_CPP_INDEXES['cpu']}"
|
|
52
56
|
)
|
|
53
|
-
return "uv tool install 'local-embed[gguf]'"
|
|
54
|
-
return f"uv tool install 'local-embed[{backend}]'"
|
|
57
|
+
return "uv tool install --python 3.13 'local-embed[gguf]'"
|
|
58
|
+
return f"uv tool install --python 3.13 'local-embed[{backend}]'"
|
|
55
59
|
|
|
56
60
|
|
|
57
61
|
def resolve_cache(raw: "str | None", environ: "dict[str, str] | None" = None) -> str:
|
|
@@ -92,11 +96,18 @@ def _upsert_model(cfg: dict, model: str, entry: dict, port: int) -> dict:
|
|
|
92
96
|
|
|
93
97
|
|
|
94
98
|
def set_transformer_model(cfg: dict, model: str, cache: str, port: int) -> dict:
|
|
95
|
-
"""Upsert ``model`` as a transformer model, pin the cache
|
|
99
|
+
"""Upsert ``model`` as a transformer model, pin the pre-downloaded cache.
|
|
100
|
+
|
|
101
|
+
The server is intended to run **offline** — the model is pre-downloaded
|
|
102
|
+
into ``cache`` and ``set`` requires it to already be there — so
|
|
103
|
+
``HF_HUB_OFFLINE=1`` is written alongside the cache (the server resolves
|
|
104
|
+
the repo against this cache and never hits the network).
|
|
105
|
+
"""
|
|
96
106
|
env = dict(cfg.get("env") or {})
|
|
97
107
|
env[ENV_CACHE_KEY] = cache
|
|
108
|
+
env[ENV_OFFLINE_KEY] = "1"
|
|
98
109
|
out = _upsert_model(cfg, model, {"backend": "transformer", "model": model}, port)
|
|
99
|
-
out["env"] = env # existing env keys preserved;
|
|
110
|
+
out["env"] = env # existing env keys preserved; cache + offline (re)written
|
|
100
111
|
return out
|
|
101
112
|
|
|
102
113
|
|
|
@@ -10,15 +10,18 @@ import pytest
|
|
|
10
10
|
pytestmark = pytest.mark.unit
|
|
11
11
|
|
|
12
12
|
from local_embed.cli import main
|
|
13
|
+
|
|
14
|
+
import local_embed.cli as cli
|
|
13
15
|
from local_embed.cmd_set import (
|
|
14
16
|
ENV_CACHE_KEY,
|
|
17
|
+
ENV_OFFLINE_KEY,
|
|
15
18
|
backend_install_hint,
|
|
16
19
|
model_in_cache,
|
|
17
20
|
resolve_cache,
|
|
18
21
|
set_gguf_model,
|
|
19
22
|
set_transformer_model,
|
|
20
23
|
)
|
|
21
|
-
from local_embed.config import load_config
|
|
24
|
+
from local_embed.config import load_config, write_config
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class TestResolveCache:
|
|
@@ -41,9 +44,15 @@ class TestSetTransformerModel:
|
|
|
41
44
|
"models": {"BAAI/bge-m3": {"backend": "transformer", "model": "BAAI/bge-m3"}},
|
|
42
45
|
"active_model": "BAAI/bge-m3",
|
|
43
46
|
"port": 8000,
|
|
44
|
-
"env": {ENV_CACHE_KEY: "C:\\hub"},
|
|
47
|
+
"env": {ENV_CACHE_KEY: "C:\\hub", ENV_OFFLINE_KEY: "1"},
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
def test_offline_flag_defaults_on(self):
|
|
51
|
+
# the server should never silently refetch a missing repo — pin
|
|
52
|
+
# HF_HUB_OFFLINE even when the user only supplied a cache dir
|
|
53
|
+
out = set_transformer_model({}, "BAAI/bge-m3", "C:\\hub", 8000)
|
|
54
|
+
assert out["env"][ENV_OFFLINE_KEY] == "1"
|
|
55
|
+
|
|
47
56
|
def test_preserves_existing_models_and_env(self):
|
|
48
57
|
cfg = {
|
|
49
58
|
"models": {"old": {"backend": "gguf", "gguf_path": "/a.gguf"}},
|
|
@@ -111,18 +120,22 @@ def config_path(tmp_path, monkeypatch):
|
|
|
111
120
|
|
|
112
121
|
class TestBackendInstallHint:
|
|
113
122
|
def test_gguf_linux_macos_plain(self):
|
|
114
|
-
assert backend_install_hint("gguf", "linux") ==
|
|
115
|
-
|
|
123
|
+
assert backend_install_hint("gguf", "linux") == (
|
|
124
|
+
"uv tool install --python 3.13 'local-embed[gguf]'"
|
|
125
|
+
)
|
|
126
|
+
assert backend_install_hint("gguf", "darwin") == (
|
|
127
|
+
"uv tool install --python 3.13 'local-embed[gguf]'"
|
|
128
|
+
)
|
|
116
129
|
|
|
117
130
|
def test_gguf_windows_adds_cpu_index(self):
|
|
118
131
|
assert backend_install_hint("gguf", "win32") == (
|
|
119
|
-
"uv tool install 'local-embed[gguf]' "
|
|
132
|
+
"uv tool install --python 3.13 'local-embed[gguf]' "
|
|
120
133
|
"--extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu"
|
|
121
134
|
)
|
|
122
135
|
|
|
123
136
|
def test_transformer_plain(self):
|
|
124
137
|
assert backend_install_hint("transformer", "win32") == (
|
|
125
|
-
"uv tool install 'local-embed[transformer]'"
|
|
138
|
+
"uv tool install --python 3.13 'local-embed[transformer]'"
|
|
126
139
|
)
|
|
127
140
|
|
|
128
141
|
|
|
@@ -206,4 +219,70 @@ class TestRunSetGguf:
|
|
|
206
219
|
assert main(["set-gguf", "bge-m3", "--path", str(gguf)]) == 0
|
|
207
220
|
first = config_path.read_text(encoding="utf-8")
|
|
208
221
|
assert main(["set-gguf", "bge-m3", "--path", str(gguf)]) == 0
|
|
209
|
-
assert config_path.read_text(encoding="utf-8") == first
|
|
222
|
+
assert config_path.read_text(encoding="utf-8") == first
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class TestCliCtrlC:
|
|
226
|
+
"""Ctrl-C during the active backend check must exit 130, never traceback.
|
|
227
|
+
|
|
228
|
+
Regression: the startup validation calls ``resolve_backend_runtime``
|
|
229
|
+
(a REAL import — torch takes seconds), and a Ctrl-C landing mid-import
|
|
230
|
+
used to propagate as a raw KeyboardInterrupt traceback.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
def _write_active_transformer(self, config_path):
|
|
234
|
+
write_config(
|
|
235
|
+
{
|
|
236
|
+
"active_model": "bge-m3-transformer",
|
|
237
|
+
"models": {
|
|
238
|
+
"bge-m3-transformer": {
|
|
239
|
+
"backend": "transformer",
|
|
240
|
+
"model": "BAAI/bge-m3",
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
config_path,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
def test_interrupt_during_active_check_exits_130(self, monkeypatch, config_path, capsys):
|
|
248
|
+
self._write_active_transformer(config_path)
|
|
249
|
+
|
|
250
|
+
def interrupt(_backend):
|
|
251
|
+
raise KeyboardInterrupt()
|
|
252
|
+
|
|
253
|
+
monkeypatch.setattr(cli, "resolve_backend_runtime", interrupt)
|
|
254
|
+
code = cli.main([])
|
|
255
|
+
assert code == 130
|
|
256
|
+
assert "Interrupted" in capsys.readouterr().err
|
|
257
|
+
|
|
258
|
+
def test_non_active_backend_not_resolved(self, monkeypatch, config_path):
|
|
259
|
+
# active = gguf; the non-active transformer must NOT trigger the
|
|
260
|
+
# torch import at startup (it is only checked when actually loaded).
|
|
261
|
+
write_config(
|
|
262
|
+
{
|
|
263
|
+
"active_model": "bge-m3",
|
|
264
|
+
"models": {
|
|
265
|
+
"bge-m3": {"backend": "gguf", "gguf_path": "/x.gguf"},
|
|
266
|
+
"bge-m3-transformer": {
|
|
267
|
+
"backend": "transformer",
|
|
268
|
+
"model": "BAAI/bge-m3",
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
config_path,
|
|
273
|
+
)
|
|
274
|
+
resolved = []
|
|
275
|
+
|
|
276
|
+
def tracking(backend):
|
|
277
|
+
resolved.append(backend)
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
monkeypatch.setattr(cli, "resolve_backend_runtime", tracking)
|
|
281
|
+
# stop after the validation loop instead of serving (serve_standalone
|
|
282
|
+
# is imported lazily inside main, so patch its source module)
|
|
283
|
+
monkeypatch.setattr(
|
|
284
|
+
"local_embed.server.serve_standalone", lambda *a, **k: 0
|
|
285
|
+
)
|
|
286
|
+
code = cli.main([])
|
|
287
|
+
assert code == 0
|
|
288
|
+
assert resolved == ["gguf"] # only the active backend was imported
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|