@smilintux/skcapstone 0.5.3 → 0.5.5
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/package.json +1 -1
- package/src/skcapstone/consciousness_loop.py +81 -103
- package/src/skcapstone/onboard.py +32 -15
package/package.json
CHANGED
|
@@ -140,21 +140,14 @@ def _backend_from_model(model_name: str, tier: ModelTier) -> str:
|
|
|
140
140
|
|
|
141
141
|
Returns:
|
|
142
142
|
Backend string: ``"ollama"``, ``"anthropic"``, ``"openai"``, ``"grok"``,
|
|
143
|
-
``"kimi"``, ``"nvidia"``, ``"passthrough"``, or ``"unknown"``.
|
|
143
|
+
``"kimi"``, ``"minimax"``, ``"nvidia"``, ``"passthrough"``, or ``"unknown"``.
|
|
144
144
|
"""
|
|
145
145
|
if tier == ModelTier.LOCAL:
|
|
146
146
|
return "ollama"
|
|
147
147
|
name_base = model_name.lower().split(":")[0]
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return "openai"
|
|
152
|
-
if "grok" in name_base:
|
|
153
|
-
return "grok"
|
|
154
|
-
if "kimi" in name_base or "moonshot" in name_base:
|
|
155
|
-
return "kimi"
|
|
156
|
-
if "nvidia" in name_base:
|
|
157
|
-
return "nvidia"
|
|
148
|
+
for patterns, backend in LLMBridge._MODEL_PATTERNS:
|
|
149
|
+
if any(p in name_base for p in patterns):
|
|
150
|
+
return backend
|
|
158
151
|
if any(p in name_base for p in _OLLAMA_MODEL_PATTERNS):
|
|
159
152
|
return "ollama"
|
|
160
153
|
return "unknown"
|
|
@@ -251,6 +244,7 @@ class LLMBridge:
|
|
|
251
244
|
adapter: Optional[PromptAdapter] = None,
|
|
252
245
|
cache: Optional[ResponseCache] = None,
|
|
253
246
|
) -> None:
|
|
247
|
+
self._config = config
|
|
254
248
|
self._router = ModelRouter(config=router_config)
|
|
255
249
|
self._adapter = adapter or PromptAdapter()
|
|
256
250
|
self._fallback_chain = config.fallback_chain
|
|
@@ -261,17 +255,29 @@ class LLMBridge:
|
|
|
261
255
|
self._ollama_pool = _OllamaPool(os.environ.get("OLLAMA_HOST", config.ollama_host))
|
|
262
256
|
self._probe_available_backends()
|
|
263
257
|
|
|
258
|
+
# Maps backend name → env var that activates it.
|
|
259
|
+
# Backends with None are probed separately (ollama) or always on (passthrough).
|
|
260
|
+
_BACKEND_ENV_KEYS: dict[str, Optional[str]] = {
|
|
261
|
+
"ollama": None,
|
|
262
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
263
|
+
"openai": "OPENAI_API_KEY",
|
|
264
|
+
"grok": "XAI_API_KEY",
|
|
265
|
+
"kimi": "MOONSHOT_API_KEY",
|
|
266
|
+
"minimax": "MINIMAX_API_KEY",
|
|
267
|
+
"nvidia": "NVIDIA_API_KEY",
|
|
268
|
+
"passthrough": None,
|
|
269
|
+
}
|
|
270
|
+
|
|
264
271
|
def _probe_available_backends(self) -> None:
|
|
265
272
|
"""Probe all backends for availability."""
|
|
266
|
-
self._available = {
|
|
267
|
-
|
|
268
|
-
"
|
|
269
|
-
|
|
270
|
-
"
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
273
|
+
self._available = {}
|
|
274
|
+
for name, env_key in self._BACKEND_ENV_KEYS.items():
|
|
275
|
+
if name == "ollama":
|
|
276
|
+
self._available[name] = self._probe_ollama()
|
|
277
|
+
elif name == "passthrough":
|
|
278
|
+
self._available[name] = True
|
|
279
|
+
else:
|
|
280
|
+
self._available[name] = bool(os.environ.get(env_key or ""))
|
|
275
281
|
available = [k for k, v in self._available.items() if v]
|
|
276
282
|
logger.info("LLM backends available: %s", available)
|
|
277
283
|
|
|
@@ -287,9 +293,23 @@ class LLMBridge:
|
|
|
287
293
|
self._ollama_pool.invalidate()
|
|
288
294
|
return False
|
|
289
295
|
|
|
296
|
+
# Maps model-name substring → backend name for pattern matching.
|
|
297
|
+
_MODEL_PATTERNS: list[tuple[tuple[str, ...], str]] = [
|
|
298
|
+
(("claude",), "anthropic"),
|
|
299
|
+
(("gpt", "o1", "o3", "o4"), "openai"),
|
|
300
|
+
(("grok",), "grok"),
|
|
301
|
+
(("kimi", "moonshot"), "kimi"),
|
|
302
|
+
(("minimax",), "minimax"),
|
|
303
|
+
(("nvidia",), "nvidia"),
|
|
304
|
+
]
|
|
305
|
+
|
|
290
306
|
def _resolve_callback(self, tier: ModelTier, model_name: str):
|
|
291
307
|
"""Map tier+model to a skseed callback.
|
|
292
308
|
|
|
309
|
+
Uses the configured ollama_model for local inference and
|
|
310
|
+
resolves cloud backends by model-name pattern matching.
|
|
311
|
+
Falls back through the configured fallback_chain.
|
|
312
|
+
|
|
293
313
|
Args:
|
|
294
314
|
tier: The routing tier.
|
|
295
315
|
model_name: The concrete model name.
|
|
@@ -297,73 +317,58 @@ class LLMBridge:
|
|
|
297
317
|
Returns:
|
|
298
318
|
An LLMCallback callable.
|
|
299
319
|
"""
|
|
300
|
-
from skseed.llm import
|
|
301
|
-
anthropic_callback,
|
|
302
|
-
grok_callback,
|
|
303
|
-
kimi_callback,
|
|
304
|
-
nvidia_callback,
|
|
305
|
-
ollama_callback,
|
|
306
|
-
openai_callback,
|
|
307
|
-
passthrough_callback,
|
|
308
|
-
)
|
|
320
|
+
from skseed.llm import ollama_callback
|
|
309
321
|
|
|
310
|
-
|
|
311
|
-
# Strip Ollama :tag suffix for pattern matching (e.g. "deepseek-r1:8b" -> "deepseek-r1")
|
|
312
|
-
name_base = name_lower.split(":")[0]
|
|
322
|
+
name_base = model_name.lower().split(":")[0]
|
|
313
323
|
|
|
314
324
|
# LOCAL tier always goes to Ollama
|
|
315
325
|
if tier == ModelTier.LOCAL:
|
|
316
326
|
return ollama_callback(model=model_name)
|
|
317
327
|
|
|
318
|
-
# Pattern matching on model name
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return openai_callback(model=model_name)
|
|
323
|
-
if "grok" in name_base:
|
|
324
|
-
return grok_callback(model=model_name)
|
|
325
|
-
if "kimi" in name_base or "moonshot" in name_base:
|
|
326
|
-
return kimi_callback(model=model_name)
|
|
327
|
-
if "minimax" in name_base:
|
|
328
|
-
return minimax_callback(model=model_name)
|
|
329
|
-
if "nvidia" in name_base:
|
|
330
|
-
return nvidia_callback(model=model_name)
|
|
328
|
+
# Pattern matching on model name
|
|
329
|
+
for patterns, backend in self._MODEL_PATTERNS:
|
|
330
|
+
if any(p in name_base for p in patterns):
|
|
331
|
+
return self._callback_for_backend(backend, model=model_name)
|
|
331
332
|
|
|
332
333
|
# Models that run on Ollama (local inference)
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
"mistral",
|
|
336
|
-
"nemotron",
|
|
337
|
-
"devstral",
|
|
338
|
-
"deepseek",
|
|
339
|
-
"qwen",
|
|
340
|
-
"codestral",
|
|
341
|
-
)
|
|
342
|
-
for pattern in ollama_patterns:
|
|
343
|
-
if pattern in name_base:
|
|
344
|
-
return ollama_callback(model=model_name)
|
|
334
|
+
if any(p in name_base for p in _OLLAMA_MODEL_PATTERNS):
|
|
335
|
+
return ollama_callback(model=model_name)
|
|
345
336
|
|
|
346
337
|
# Walk fallback chain for first available backend
|
|
347
338
|
for backend in self._fallback_chain:
|
|
348
|
-
if
|
|
349
|
-
|
|
350
|
-
if backend == "ollama":
|
|
351
|
-
return ollama_callback(model="llama3.2")
|
|
352
|
-
elif backend == "anthropic":
|
|
353
|
-
return anthropic_callback()
|
|
354
|
-
elif backend == "openai":
|
|
355
|
-
return openai_callback()
|
|
356
|
-
elif backend == "grok":
|
|
357
|
-
return grok_callback()
|
|
358
|
-
elif backend == "kimi":
|
|
359
|
-
return kimi_callback()
|
|
360
|
-
elif backend == "nvidia":
|
|
361
|
-
return nvidia_callback()
|
|
362
|
-
elif backend == "passthrough":
|
|
363
|
-
return self._make_passthrough_callback()
|
|
339
|
+
if self._available.get(backend, False):
|
|
340
|
+
return self._callback_for_backend(backend)
|
|
364
341
|
|
|
365
342
|
return self._make_passthrough_callback()
|
|
366
343
|
|
|
344
|
+
def _callback_for_backend(self, backend: str, model: Optional[str] = None):
|
|
345
|
+
"""Return the skseed callback for *backend*, importing only what's needed.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
backend: Backend name (e.g. "ollama", "anthropic", "openai").
|
|
349
|
+
model: Optional model override. When None, uses each provider's default.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
An LLMCallback callable.
|
|
353
|
+
"""
|
|
354
|
+
import skseed.llm as _llm
|
|
355
|
+
|
|
356
|
+
if backend == "ollama":
|
|
357
|
+
return _llm.ollama_callback(model=model or self._config.ollama_model)
|
|
358
|
+
if backend == "passthrough":
|
|
359
|
+
return self._make_passthrough_callback()
|
|
360
|
+
|
|
361
|
+
# All other backends follow the same pattern: <backend>_callback(model=…)
|
|
362
|
+
factory = getattr(_llm, f"{backend}_callback", None)
|
|
363
|
+
if factory is None:
|
|
364
|
+
logger.warning("No skseed callback for backend %r — using passthrough", backend)
|
|
365
|
+
return self._make_passthrough_callback()
|
|
366
|
+
|
|
367
|
+
kwargs: dict[str, Any] = {}
|
|
368
|
+
if model:
|
|
369
|
+
kwargs["model"] = model
|
|
370
|
+
return factory(**kwargs)
|
|
371
|
+
|
|
367
372
|
@staticmethod
|
|
368
373
|
def _make_passthrough_callback():
|
|
369
374
|
"""Return a passthrough callback that always produces a plain str.
|
|
@@ -460,16 +465,6 @@ class LLMBridge:
|
|
|
460
465
|
Returns:
|
|
461
466
|
LLM response text, or a fallback error message.
|
|
462
467
|
"""
|
|
463
|
-
from skseed.llm import (
|
|
464
|
-
anthropic_callback,
|
|
465
|
-
grok_callback,
|
|
466
|
-
kimi_callback,
|
|
467
|
-
minimax_callback,
|
|
468
|
-
nvidia_callback,
|
|
469
|
-
ollama_callback,
|
|
470
|
-
openai_callback,
|
|
471
|
-
)
|
|
472
|
-
|
|
473
468
|
decision = self._router.route(signal)
|
|
474
469
|
logger.info(
|
|
475
470
|
"Routed to tier=%s model=%s: %s",
|
|
@@ -609,31 +604,14 @@ class LLMBridge:
|
|
|
609
604
|
)
|
|
610
605
|
)
|
|
611
606
|
|
|
612
|
-
# Cross-provider cascade via fallback chain —
|
|
613
|
-
#
|
|
607
|
+
# Cross-provider cascade via fallback chain — uses _callback_for_backend
|
|
608
|
+
# so adding a new provider only requires updating the registry, not this loop.
|
|
614
609
|
for backend in self._fallback_chain:
|
|
615
610
|
if not self._available.get(backend, False):
|
|
616
611
|
continue
|
|
617
612
|
try:
|
|
618
613
|
logger.info("Fallback cascade: %s", backend)
|
|
619
|
-
|
|
620
|
-
callback = ollama_callback(model="llama3.2")
|
|
621
|
-
elif backend == "anthropic":
|
|
622
|
-
callback = anthropic_callback()
|
|
623
|
-
elif backend == "grok":
|
|
624
|
-
callback = grok_callback()
|
|
625
|
-
elif backend == "kimi":
|
|
626
|
-
callback = kimi_callback()
|
|
627
|
-
elif backend == "minimax":
|
|
628
|
-
callback = minimax_callback()
|
|
629
|
-
elif backend == "nvidia":
|
|
630
|
-
callback = nvidia_callback()
|
|
631
|
-
elif backend == "openai":
|
|
632
|
-
callback = openai_callback()
|
|
633
|
-
elif backend == "passthrough":
|
|
634
|
-
callback = self._make_passthrough_callback()
|
|
635
|
-
else:
|
|
636
|
-
continue
|
|
614
|
+
callback = self._callback_for_backend(backend)
|
|
637
615
|
result = self._timed_call(callback, adapted, ModelTier.FAST)
|
|
638
616
|
if _out_info is not None:
|
|
639
617
|
_out_info["backend"] = backend
|
|
@@ -1283,44 +1283,61 @@ def _step_doctor_check(home_path: Path) -> "object":
|
|
|
1283
1283
|
|
|
1284
1284
|
|
|
1285
1285
|
def _step_test_consciousness(home_path: Path) -> bool:
|
|
1286
|
-
"""Send a test message
|
|
1286
|
+
"""Send a quick test message to the configured LLM backend.
|
|
1287
|
+
|
|
1288
|
+
Reads the consciousness config to determine the default backend
|
|
1289
|
+
(typically the local Ollama model chosen during onboarding) and
|
|
1290
|
+
sends a single prompt to verify the pipeline works end-to-end.
|
|
1287
1291
|
|
|
1288
1292
|
Args:
|
|
1289
1293
|
home_path: Agent home directory.
|
|
1290
1294
|
|
|
1291
1295
|
Returns:
|
|
1292
|
-
True if the
|
|
1296
|
+
True if the LLM responded successfully.
|
|
1293
1297
|
"""
|
|
1294
|
-
if not click.confirm(" Send a test message to verify the
|
|
1298
|
+
if not click.confirm(" Send a test message to verify the LLM backend?", default=False):
|
|
1295
1299
|
click.echo(
|
|
1296
1300
|
click.style(" ↷ ", fg="bright_black")
|
|
1297
1301
|
+ "Skipped — test later: skcapstone consciousness test 'hello'"
|
|
1298
1302
|
)
|
|
1299
1303
|
return False
|
|
1300
1304
|
|
|
1301
|
-
|
|
1305
|
+
# Load config to discover which backend/model was configured
|
|
1302
1306
|
try:
|
|
1303
1307
|
from .consciousness_config import load_consciousness_config
|
|
1304
|
-
from .consciousness_loop import LLMBridge, SystemPromptBuilder, _classify_message
|
|
1305
|
-
|
|
1306
1308
|
config = load_consciousness_config(home_path)
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1309
|
+
except Exception:
|
|
1310
|
+
# Fall back to defaults
|
|
1311
|
+
ollama_model = "llama3.2"
|
|
1312
|
+
ollama_host = "http://localhost:11434"
|
|
1313
|
+
config = None
|
|
1314
|
+
else:
|
|
1315
|
+
ollama_model = config.ollama_model
|
|
1316
|
+
ollama_host = config.ollama_host
|
|
1317
|
+
|
|
1318
|
+
click.echo(
|
|
1319
|
+
click.style(" Testing ", fg="bright_black")
|
|
1320
|
+
+ click.style(f"{ollama_model}", fg="cyan")
|
|
1321
|
+
+ click.style(f" @ {ollama_host}…", fg="bright_black")
|
|
1322
|
+
)
|
|
1323
|
+
|
|
1324
|
+
try:
|
|
1325
|
+
from skseed.llm import ollama_callback
|
|
1326
|
+
|
|
1327
|
+
callback = ollama_callback(model=ollama_model, base_url=ollama_host)
|
|
1328
|
+
response = callback("Respond in one sentence: are you online?")
|
|
1312
1329
|
if response:
|
|
1313
1330
|
preview = response[:80].replace("\n", " ")
|
|
1314
|
-
click.echo(click.style(" ✓ ", fg="green") +
|
|
1331
|
+
click.echo(click.style(" ✓ ", fg="green") + "LLM backend active")
|
|
1315
1332
|
click.echo(click.style(" ", fg="bright_black") + f"Response: {preview!r}")
|
|
1316
1333
|
return True
|
|
1317
1334
|
else:
|
|
1318
|
-
click.echo(click.style(" ⚠ ", fg="yellow") + "Empty response —
|
|
1319
|
-
click.echo(click.style(" ", fg="bright_black") + "
|
|
1335
|
+
click.echo(click.style(" ⚠ ", fg="yellow") + "Empty response — model may still be loading")
|
|
1336
|
+
click.echo(click.style(" ", fg="bright_black") + f"Try: ollama run {ollama_model}")
|
|
1320
1337
|
return False
|
|
1321
1338
|
except Exception as exc:
|
|
1322
1339
|
click.echo(click.style(" ⚠ ", fg="yellow") + f"Test failed: {exc}")
|
|
1323
|
-
click.echo(click.style(" ", fg="bright_black") + "
|
|
1340
|
+
click.echo(click.style(" ", fg="bright_black") + f"Check: ollama serve && ollama run {ollama_model}")
|
|
1324
1341
|
return False
|
|
1325
1342
|
|
|
1326
1343
|
|