@hypabolic/crossbar 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITY-MATRIX.md +2 -2
- package/package.json +9 -2
- package/src/adapters/lmstudio.ts +31 -11
package/CAPABILITY-MATRIX.md
CHANGED
|
@@ -7,7 +7,7 @@ adapter registers under (`oai` = `openai-completions`, `ant` = `anthropic-messag
|
|
|
7
7
|
| Backend | port | pi api | listModels | introspectLoaded | switchModel | loadUnload | auth | health | perModelCaps | streaming | discovery fingerprint |
|
|
8
8
|
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
9
9
|
| **Ollama** | 11434 | oai | ✅ `/api/tags`,`/v1/models` | ✅ `/api/ps` | ✅ implicit (request id) | ✅ `keep_alive:0` | ◐ none local | ✅ `GET /` text | ✅ `/api/show` caps + ctx | ✅ | `GET /` → `Ollama is running` |
|
|
10
|
-
| **LM Studio** | 1234 | oai | ✅ `/api/
|
|
10
|
+
| **LM Studio** | 1234 | oai | ✅ `/api/v1/models` (v0 fallback) | ✅ `state` field | ✅ JIT + `/api/v1/models/load` | ✅ load/unload + `lms` | ◐ Bearer, none default | ◐ infer 200 | ✅ type+`max_context_length` | ✅ | `/api/v1/models` (v0 fallback) w/ `state`,`compatibility_type` |
|
|
11
11
|
| **llama-server** | 8080 | oai | ✅ `/v1/models` | ◐ `/props`,`/slots` (single) | ❌ (1/instance) | ❌ classic | ◐ none / `--api-key` | ✅ `/health` | ◐ ctx via `/props`,`meta` | ✅ | `/props` w/ `default_generation_settings`+`build_info` |
|
|
12
12
|
| **llama-swap** | 8080 | oai/ant | ✅ `/v1/models` (all config) | ✅ `/running` | ✅ via `model` → restart upstream | ✅ `/api/models/unload`, ttl | ◐ optional multi-scheme | ✅ `/health`→OK | ◐ via upstream | ✅ | `/` → `/ui/`; `/running`,`/upstream/{model}` |
|
|
13
13
|
| **vLLM** | 8000 | oai | ✅ `/v1/models` | ◐ `/is_sleeping` (dev) | ❌ base · ◐ LoRA | ◐ sleep/wake + LoRA | ◐ none / `--api-key` | ✅ `/health` | ◐ `max_model_len` only | ✅ | `/version` + `/metrics` `vllm:` + `owned_by:"vllm"` |
|
|
@@ -38,7 +38,7 @@ adapter registers under (`oai` = `openai-completions`, `ant` = `anthropic-messag
|
|
|
38
38
|
|
|
39
39
|
1. `GET /` → `Ollama is running` ⇒ Ollama · redirect `/ui/` ⇒ llama-swap
|
|
40
40
|
2. `GET /api/extra/version` → `{"result":"KoboldCpp"}` ⇒ KoboldCpp
|
|
41
|
-
3. `GET /api/
|
|
41
|
+
3. `GET /api/v1/models` (v0 fallback) 200 w/ `state`/`compatibility_type` ⇒ LM Studio
|
|
42
42
|
4. `GET /props` w/ `default_generation_settings`+`build_info` ⇒ llama-server / llamafile
|
|
43
43
|
5. `GET /version` + `/metrics` `vllm:` ⇒ vLLM
|
|
44
44
|
6. `GET /v1/models` shape: `owned_by:"vllm"`⇒vLLM · `meta.n_ctx_train`⇒llama.cpp ·
|
package/package.json
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypabolic/crossbar",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "The local/self-hosted inference connector Pi should have shipped with — multi-backend discovery, model switching, and zero-JSON in-TUI onboarding for the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Hypabolic",
|
|
8
|
-
"homepage": "https://github.com/
|
|
8
|
+
"homepage": "https://github.com/Hypabolic/Crossbar#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/Hypabolic/Crossbar.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Hypabolic/Crossbar/issues"
|
|
15
|
+
},
|
|
9
16
|
"keywords": [
|
|
10
17
|
"pi-package",
|
|
11
18
|
"pi-extension",
|
package/src/adapters/lmstudio.ts
CHANGED
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
* LM Studio backend adapter.
|
|
3
3
|
*
|
|
4
4
|
* Implements the BackendAdapter contract for LM Studio's local server.
|
|
5
|
-
* Uses the LM Studio-native
|
|
6
|
-
*
|
|
5
|
+
* Uses the LM Studio-native REST API for discovery and management, and delegates
|
|
6
|
+
* inference to the OpenAI-compatible /v1/* layer.
|
|
7
|
+
*
|
|
8
|
+
* LM Studio 0.4.0+ ships a native `/api/v1/*` REST API (recommended); the older
|
|
9
|
+
* `/api/v0/*` API carries the same rich model fields and is kept as a fallback for
|
|
10
|
+
* pre-0.4.0 servers. We prefer v1 and fall back to v0 only on a 404.
|
|
7
11
|
*
|
|
8
12
|
* Key API endpoints:
|
|
9
|
-
* GET /api/v0/models
|
|
10
|
-
* POST /api/v1/models/load
|
|
11
|
-
* POST /api/v1/models/unload
|
|
13
|
+
* GET /api/v1/models (→ /api/v0/models fallback) — model list with state, type, context length
|
|
14
|
+
* POST /api/v1/models/load — load a model by id
|
|
15
|
+
* POST /api/v1/models/unload — unload a model by id
|
|
12
16
|
*
|
|
13
17
|
* Fingerprint discriminator: data[] entries have both `state` and
|
|
14
|
-
* `compatibility_type` fields (unique to LM Studio's
|
|
18
|
+
* `compatibility_type` fields (unique to LM Studio's native API).
|
|
15
19
|
*/
|
|
16
20
|
|
|
17
21
|
import { Capability } from "../core/capability.ts";
|
|
@@ -24,9 +28,14 @@ import type {
|
|
|
24
28
|
ModelDescriptor,
|
|
25
29
|
PiModelEntry,
|
|
26
30
|
Probe,
|
|
31
|
+
ProbeResult,
|
|
27
32
|
ServerCredential,
|
|
28
33
|
} from "../core/types.ts";
|
|
29
34
|
|
|
35
|
+
/** Native model-list endpoints, in preference order (v1 first, v0 fallback for <0.4.0). */
|
|
36
|
+
const MODELS_V1 = "/api/v1/models";
|
|
37
|
+
const MODELS_V0 = "/api/v0/models";
|
|
38
|
+
|
|
30
39
|
// ---------------------------------------------------------------------------
|
|
31
40
|
// LM Studio API shapes (narrowed from unknown JSON)
|
|
32
41
|
// ---------------------------------------------------------------------------
|
|
@@ -140,10 +149,21 @@ class LmStudioAdapter implements BackendAdapter {
|
|
|
140
149
|
Capability.Streaming,
|
|
141
150
|
]);
|
|
142
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Fetch the native model list, preferring /api/v1/models and falling back to
|
|
154
|
+
* /api/v0/models for LM Studio < 0.4.0 (which only exposes the v0 REST API).
|
|
155
|
+
* Falls back ONLY on a 404 so auth (401) and unreachable (0) errors propagate.
|
|
156
|
+
*/
|
|
157
|
+
private async modelsResponse(probe: Probe): Promise<ProbeResult> {
|
|
158
|
+
const v1 = await probe(MODELS_V1);
|
|
159
|
+
if (v1.status === 404) return probe(MODELS_V0);
|
|
160
|
+
return v1;
|
|
161
|
+
}
|
|
162
|
+
|
|
143
163
|
// --- fingerprint ----------------------------------------------------------
|
|
144
164
|
|
|
145
165
|
async fingerprint(baseUrl: string, probe: Probe): Promise<DiscoveredServer | null> {
|
|
146
|
-
const r = await probe
|
|
166
|
+
const r = await this.modelsResponse(probe);
|
|
147
167
|
if (!r.ok || r.status === 0) return null;
|
|
148
168
|
if (!hasLmsDiscriminator(r.json)) return null;
|
|
149
169
|
|
|
@@ -163,7 +183,7 @@ class LmStudioAdapter implements BackendAdapter {
|
|
|
163
183
|
_cred: ServerCredential,
|
|
164
184
|
probe: Probe,
|
|
165
185
|
): Promise<HealthStatus> {
|
|
166
|
-
const r = await probe
|
|
186
|
+
const r = await this.modelsResponse(probe);
|
|
167
187
|
if (r.status === 0) return { state: "unreachable" };
|
|
168
188
|
if (r.status === 401) return { state: "unauthorized" };
|
|
169
189
|
if (!r.ok) return { state: "degraded" };
|
|
@@ -179,7 +199,7 @@ class LmStudioAdapter implements BackendAdapter {
|
|
|
179
199
|
_cred: ServerCredential,
|
|
180
200
|
probe: Probe,
|
|
181
201
|
): Promise<ModelDescriptor[]> {
|
|
182
|
-
const r = await probe
|
|
202
|
+
const r = await this.modelsResponse(probe);
|
|
183
203
|
if (!r.ok) {
|
|
184
204
|
if (r.status === 401) throw new Error("401 Unauthorized");
|
|
185
205
|
if (r.status === 0) throw new Error("listModels failed: server unreachable");
|
|
@@ -197,7 +217,7 @@ class LmStudioAdapter implements BackendAdapter {
|
|
|
197
217
|
_cred: ServerCredential,
|
|
198
218
|
probe: Probe,
|
|
199
219
|
): Promise<LoadedState> {
|
|
200
|
-
const r = await probe
|
|
220
|
+
const r = await this.modelsResponse(probe);
|
|
201
221
|
if (!r.ok) {
|
|
202
222
|
if (r.status === 401) throw new Error("401 Unauthorized");
|
|
203
223
|
if (r.status === 0) throw new Error("introspectLoaded failed: server unreachable");
|
|
@@ -242,7 +262,7 @@ class LmStudioAdapter implements BackendAdapter {
|
|
|
242
262
|
}
|
|
243
263
|
|
|
244
264
|
// Step 2: Confirm via model list that the target is now loaded
|
|
245
|
-
const r2 = await probe
|
|
265
|
+
const r2 = await this.modelsResponse(probe);
|
|
246
266
|
if (!r2.ok) {
|
|
247
267
|
if (r2.status === 0) throw new Error("switchModel confirmation failed: server went down");
|
|
248
268
|
if (r2.status === 401) throw new Error("401 Unauthorized");
|