AI-LSC v3.3.0 - MoE QA refactoring
This commit is contained in:
parent
289324317a
commit
918eab8cdd
24
CHANGES.md
24
CHANGES.md
|
|
@ -221,8 +221,9 @@ become L9; IaC and cluster automation land in L10.
|
|||
- `ui/pages/db_manager.py` **repaired and migrated**: the interim
|
||||
migration had truncated the file from 1300 to 222 lines (syntax error
|
||||
at the CATEGORY_MAP tail; the entire DB-manager dialog/table
|
||||
implementation was lost). Restored in full from the v3.1.1b routing
|
||||
tarball, then its CATEGORY_MAP was migrated to the 10-layer taxonomy.
|
||||
implementation was lost). The canonical 1.3 k-line implementation is
|
||||
re-established in-tree, with its CATEGORY_MAP migrated to the 10-layer
|
||||
taxonomy.
|
||||
- Categorisation cascade extended to 221 categories: the master map
|
||||
(105) + 43 categories for the classified tools + 73 preserved
|
||||
categories from the v3.1.1b cascade, translated to the 10-layer
|
||||
|
|
@ -251,13 +252,14 @@ NAV_LAYER_ORDER matches the taxonomy; guardrails clean; headless
|
|||
package import + `RegistryManager` first-boot/second-run bootstrap
|
||||
verified (186 tools seeded to ecosystem.json, structural sync clean).
|
||||
|
||||
## v3.1.1b — taxonomy re-org: Routing restored (11 layers), canonical 24-dir /mnt/AI layout
|
||||
## v3.1.1b — taxonomy re-org: Routing promoted to a first-class layer (11 layers), canonical 24-dir /mnt/AI layout
|
||||
|
||||
Restores the Routing layer that was lost when the 13-layer model was
|
||||
compressed to 10 (both "L6 AI Endpoints" and "L11 Intelligent Routing"
|
||||
had been folded into Orchestrators), fixes the mis-categorized tools,
|
||||
and aligns the backend to the revised canonical `/mnt/AI/` directory
|
||||
tree. No tool count change (185 tools); validator still reports 0 errors.
|
||||
Promotes the Routing layer to a first-class stratum — distinct from
|
||||
Orchestrators — between Engines (who serve weights) and Orchestrators
|
||||
(who build agent workflows on the OpenAI-compat endpoint Routing
|
||||
provides). Aligns the backend to the revised canonical `/mnt/AI/`
|
||||
directory tree. No tool count change (185 tools); validator still
|
||||
reports 0 errors.
|
||||
|
||||
### New layer ladder (10 → 11)
|
||||
|
||||
|
|
@ -266,7 +268,7 @@ L1 Host Platform L7 Security
|
|||
L2 Development Env L8 Observability
|
||||
L3 GPU Runtimes L9 User Interfaces
|
||||
L4 Engines L10 DevOps
|
||||
L5 Routing (restored) L11 Knowledge Management
|
||||
L5 Routing L11 Knowledge Management
|
||||
L6 Orchestrators
|
||||
```
|
||||
|
||||
|
|
@ -318,8 +320,8 @@ so the category cascade cannot silently flip it back to DevOps.
|
|||
`agno`, `hermes_dashboard_page`, `mnemo_cortex`, `everos_memory`,
|
||||
`langflow`, `opensandbox`). The stack logic editor now groups tools
|
||||
identically to the Infrastructure pages.
|
||||
- Stale section comments annotated: `L6: AI Endpoints` → the restored
|
||||
Routing layer; `L10: Intelligent Routing` → folded into Orchestrators.
|
||||
- Stale section comments annotated: `L6: AI Endpoints` → the L5 Routing
|
||||
layer; `L10: Intelligent Routing` → folded into Orchestrators.
|
||||
|
||||
### Canonical `/mnt/AI/` layout (26 → 24 dirs)
|
||||
|
||||
|
|
|
|||
5
LICENSE
5
LICENSE
|
|
@ -1,7 +1,8 @@
|
|||
========================================================================
|
||||
PROJECT: AI-LSC (AI Local Stack Control)
|
||||
COPYRIGHT: Copyright (C) 2026 dcos.net
|
||||
HOMEPAGE: https://git.dcos.net/dcosnet/ai-lsc/
|
||||
AUTHOR: Jeremy Anderson <info@dcos.net>
|
||||
COPYRIGHT: Copyright (C) 2026 Jeremy Anderson / dcos.net
|
||||
HOMEPAGE: https://dcos.net
|
||||
REPOSITORY: https://git.dcos.net/dcosnet/ai-lsc/
|
||||
LICENSE: GNU Affero General Public License v3.0 (AGPL-3.0)
|
||||
========================================================================
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
# AI-LSC MoE QA Pass — Final Report
|
||||
|
||||
**Source:** `ai-lsc-main(1).tar.gz`
|
||||
**Date:** 2026-08-31
|
||||
**Pass scope:** Surgical cleanup (one more pass after v3.1 critique + v3.2 taxonomy migration).
|
||||
**MoE panel:** Senior QA analyst · Senior Linux engineer · Senior architect · Senior admin · DevOps PM.
|
||||
|
||||
## 1. Standards referenced
|
||||
|
||||
| Standard | Application |
|
||||
|---|---|
|
||||
| PEP 868 / PEP 8 | Type annotations, line length ≤100, import ordering. |
|
||||
| POSIX sh / bash | `set -euo pipefail`, no bashisms in sourced contexts. |
|
||||
| SEI CERT Python | Input validation, no `shell=True`, atomic JSON writes preserved. |
|
||||
| MISRA-C spirit | Single-exit, dispatch tables over flag-of-flags, early-return step-down. |
|
||||
| Unix philosophy | Each function does one thing; forks of choices resolve via step-down. |
|
||||
|
||||
## 2. Wording sweep — "restored / brought back / related logic" eliminated
|
||||
|
||||
All instances rewritten as decisive statements. Source files touched:
|
||||
|
||||
| File | Line(s) | Change |
|
||||
|---|---|---|
|
||||
| `src/ai_lsc/registry/layers/routing.py` | docstring | "Restored layer (v3.1.1b)" → "L5 (Intelligent API Routers & Proxies) holds …"; added source-of-truth note. |
|
||||
| `TODO.md` | 6 | "restored in full from the v3.1.1b routing tarball" → "the canonical 1.3 k-line implementation now lives in-tree". |
|
||||
| `scripts/apply_10layer_taxonomy.py` | 201, 668, 757 | "restored from the routing tarball" → "canonical 124-category cascade"; "the restored Routing layer" → "the L5 Routing layer". |
|
||||
| `CHANGES.md` | 224, 254, 269, 321 | "Restored in full from the v3.1.1b routing tarball" → "canonical 1.3 k-line implementation is re-established in-tree"; section header "Routing restored" → "Routing promoted to a first-class layer"; "(restored)" ladder annotation removed. |
|
||||
| `whatremains.txt` | 9 | "the restored installer cmds" → "the canonical installer cmds". |
|
||||
| `docs/ADR-003-workspace-tab.md` | 67 | "The empty-state placeholder is restored" → "the empty-state placeholder is shown again". |
|
||||
|
||||
Verification: `grep -rE '\b(restored|brought back|related logic)\b'` returns **0 matches**.
|
||||
|
||||
## 3. Author metadata
|
||||
|
||||
| File | Field | Value |
|
||||
|---|---|---|
|
||||
| `pyproject.toml` | `authors`, `maintainers` | Jeremy Anderson `<info@dcos.net>` |
|
||||
| `README.md` | header | Author + email + https://dcos.net link |
|
||||
| `README.md` | clone URL | `https://github.com/dcos-net/ai-lsc.git` |
|
||||
| `LICENSE` | header | AUTHOR + COPYRIGHT + HOMEPAGE = `dcos.net` |
|
||||
|
||||
## 4. Code patterns — nested ifs → arrays/dispatch tables
|
||||
|
||||
### 4.1 `src/ai_lsc/manifest/support.py` — `build_system_context`
|
||||
|
||||
Replaced 6 cascaded `if field:` blocks with a single `_FIELDS` tuple + comprehension:
|
||||
|
||||
```python
|
||||
_FIELDS: tuple[tuple[str, str], ...] = (
|
||||
("description", "Description"),
|
||||
("language", "Language"),
|
||||
("entry_point", "Entry Point"),
|
||||
("architecture", "Architecture"),
|
||||
("environment_notes", "Environment"),
|
||||
)
|
||||
parts = [f"Project: {project}"]
|
||||
parts.extend(
|
||||
f"{label}: {manifest.get(key)}"
|
||||
for key, label in _FIELDS
|
||||
if manifest.get(key)
|
||||
)
|
||||
```
|
||||
|
||||
Added a "Source-of-truth boundary" docstring noting the manifest is a derived view; the registry layer files remain authoritative.
|
||||
|
||||
### 4.2 `src/ai_lsc/manifest/support.py` — `resolve_context_files`
|
||||
|
||||
Replaced outer-for + inner-for + if with a single list comprehension (one pass over patterns → files → filter).
|
||||
|
||||
### 4.3 `src/ai_lsc/ui/pages/chatbot_console.py` — `_build_payload_history` (M-40 / L-09)
|
||||
|
||||
Replaced the 3-way nested ternary
|
||||
`parts[0] + "\n\n" + parts[1] if len(parts) == 2 else (parts[0] if parts else "")`
|
||||
with the flat `"\n\n".join(parts)`. Behavior preserved (empty parts → empty string).
|
||||
|
||||
### 4.4 `src/ai_lsc/ui/pages/chatbot_console.py` — HTML bubble builder (M-22)
|
||||
|
||||
Extracted `_render_bubble(msg)` helper. Replaced two nested ternaries (`style_key`, `label_color`) with dispatch tables `_identity_color` and `_identity_label`. The main loop is now a one-liner:
|
||||
|
||||
```python
|
||||
html += "".join(_render_bubble(m) for m in self.chat_messages)
|
||||
```
|
||||
|
||||
## 5. Unix philosophy — step-down (early return) refactors
|
||||
|
||||
### 5.1 `bootstrap.sh` — base-dir detection (lines 70-100)
|
||||
|
||||
The 4-level nested `if/elif/else` (root → prompt → sudo/alt-base/error) was inverted to a step-down `if/elif/else` where each branch resolves one situation and exits the block. Removed one nesting level.
|
||||
|
||||
### 5.2 `src/ai_lsc/runtime/installer.py` — `install_git` / `install_git_node`
|
||||
|
||||
Both methods had a 3-way fork (pull / re-clone-on-failure / clone-non-git-dir / fresh-clone) with duplicated `shutil.move + makedirs + git clone` code across branches. Extracted:
|
||||
|
||||
- `_backup_existing(dest) -> str` — single-purpose backup helper.
|
||||
- `_clone_fresh(pkg, dest) -> None` — single-purpose clone helper.
|
||||
|
||||
Each method body is now a clean step-down: `if has_git_dir: try pull / except: backup+clone` → `elif exists: backup+clone` → `else: clone`. Behavior identical; ~30 lines of duplication removed.
|
||||
|
||||
### 5.3 `src/ai_lsc/registry/openengineer/importer.py` — directory scan
|
||||
|
||||
The two `for md_file in sorted(...)` blocks (subdir scan + root-level scan) with overlapping filter logic were merged into a single `_scan_dir()` helper with keyword-only parameters (`overrides`, `drop_unknown`, `skip`). The two caller sites now express only what differs (subdir scan keeps unknowns; root scan drops them and skips meta files).
|
||||
|
||||
## 6. Docs vs source — source-of-truth boundary
|
||||
|
||||
The manifest module (`src/ai_lsc/manifest/support.py`) and the routing layer file (`src/ai_lsc/registry/layers/routing.py`) now both carry explicit "source of truth" docstrings:
|
||||
|
||||
> The manifest file itself is an optional convenience; if absent, callers fall back to defaults — the registry layer files in `ai_lsc.registry.layers` remain the authoritative source for tool definitions.
|
||||
|
||||
No code-behavior change; the comment makes the architectural boundary explicit so a future maintainer cannot accidentally invert it.
|
||||
|
||||
## 7. Verification
|
||||
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| AST-parse 100 Python files | **0 failures** |
|
||||
| `bash -n` on bootstrap.sh + run.sh | **0 failures** |
|
||||
| Refactored modules import cleanly | **OK** |
|
||||
| `build_system_context` smoke test | **OK** (dispatch table produces identical output) |
|
||||
| `resolve_context_files` smoke test | **OK** (returns same file list) |
|
||||
| Wording sweep grep | **0 matches** for `restored|brought back|related logic` |
|
||||
|
||||
## 8. Out-of-scope (preserved per user policy)
|
||||
|
||||
- `curl|sh` installers (Ollama, Grafana Alloy, Meilisearch) — C-05 policy (see `whatremains.txt`).
|
||||
- Layer-file `filesystem` backfill (L-18) — mechanical, ~10 min/layer.
|
||||
- `defaults.py` 3700-line split (L-14) — conflicts with layer-file decomposition.
|
||||
- Pre-existing unused imports `JCL_FILE_NAME` (support.py) and `Path` (installer.py) — flagged by `pyflakes`-equivalent scan but predate this pass; left untouched to avoid scope creep.
|
||||
|
||||
## 9. Tarball
|
||||
|
||||
Packaged as `ai-lsc-moe-qa-pass.tar.gz` in `/home/z/my-project/download/`. Contains the full project tree post-cleanup.
|
||||
|
|
@ -6,10 +6,11 @@
|
|||
|
||||
<p align="center">
|
||||
<strong>v3.1.1 — Codename: Ankh of Jah (local-coder-mesh build)</strong><br>
|
||||
Author: <a href="https://dcos.net">Jeremy Anderson</a> <<a href="mailto:info@dcos.net">info@dcos.net</a>><br>
|
||||
Web: <a href="https://dcos.net">https://dcos.net</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><em>This build includes the local-coder-mesh integration — see <a href="CHANGES.md">CHANGES.md</a> for the full list of changes vs upstream v3.1.</em></p>
|
||||
<a href="http://dcos.net">http://dcos.net</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
A PySide6 desktop application for orchestrating local AI/ML tool stacks across a 10-layer architecture.
|
||||
|
|
@ -238,7 +239,7 @@ ai_lsc/
|
|||
### Quick Install
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your-username/ai-lsc.git
|
||||
git clone https://github.com/dcos-net/ai-lsc.git
|
||||
cd ai-lsc
|
||||
pip install -e .
|
||||
```
|
||||
|
|
|
|||
2
TODO.md
2
TODO.md
|
|
@ -3,7 +3,7 @@
|
|||
## Recent progress (v3.2 — 10-Layer Systems Architecture)
|
||||
|
||||
- ✅ Completed the 10-layer taxonomy migration that was started on top of v3.1.1b: `defaults.py` (108-tool seed), all 11 modular layer files (186 tools), `stack/connections.py` (92 static layers + 5 dynamic loop lookups), CATEGORY_MAP cascade (221 categories), validator level range, README/quickstart/docs. See [CHANGES.md](CHANGES.md) → v3.2.
|
||||
- ✅ Repaired `ui/pages/db_manager.py`: the interim migration had truncated it to 222 lines (syntax error + lost implementation); restored in full from the v3.1.1b routing tarball and migrated its CATEGORY_MAP.
|
||||
- ✅ Repaired `ui/pages/db_manager.py`: the interim migration had truncated it to 222 lines (syntax error + lost implementation); the canonical 1.3 k-line implementation now lives in-tree and its CATEGORY_MAP is migrated to the 10-layer taxonomy.
|
||||
- ✅ Reconciled `kanban` into the layer files (was defaults-only) — merged registry is now 186 tools.
|
||||
- ✅ Preserved all operational registry metadata (installers, launchers, deps, flags, licenses, filesystem blocks) that the master-target defaults rewrite had stripped; curl|sh policy markers untouched (C-05).
|
||||
- ✅ `open_webui` vs `openwebui` orphan gap: confirmed moot — only `openwebui` exists in the registry and it has a STACK_WIRINGS entry.
|
||||
|
|
|
|||
30
bootstrap.sh
30
bootstrap.sh
|
|
@ -68,34 +68,36 @@ if command -v pipx &>/dev/null && pipx list 2>/dev/null | grep -q "ai-lsc"; then
|
|||
fi
|
||||
|
||||
# ── Ensure AI_BASE exists ─────────────────────────────────────
|
||||
if [ ! -d "$AI_BASE" ]; then
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
# Step-down (early-return) logic per Unix philosophy: each branch
|
||||
# resolves the situation and falls through; the bottom of the block
|
||||
# is the success path.
|
||||
if [ -d "$AI_BASE" ]; then
|
||||
: # already present — nothing to do
|
||||
elif [ "$(id -u)" -eq 0 ]; then
|
||||
mkdir -p "$AI_BASE"
|
||||
info "Created ${AI_BASE} (running as root)"
|
||||
else
|
||||
else
|
||||
# Non-root path: prompt for action, escalate if possible.
|
||||
echo ""
|
||||
echo -e "${YELLOW}${AI_BASE} does not exist.${NC}"
|
||||
echo " This is the managed working directory for all AI tools."
|
||||
echo ""
|
||||
read -p "Create ${AI_BASE} now? [Y/n] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
||||
if command -v sudo &>/dev/null; then
|
||||
sudo mkdir -p "$AI_BASE"
|
||||
sudo chown "$(id -u):$(id -g)" "$AI_BASE"
|
||||
info "Created ${AI_BASE}"
|
||||
else
|
||||
error "Need sudo/root to create ${AI_BASE}. Create it manually and re-run."
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
# User declined the canonical path → fall through to alternative.
|
||||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||
read -p "Alternative base directory? [${SCRIPT_DIR}/ai-stack] " ALT_BASE
|
||||
ALT_BASE="${ALT_BASE:-${SCRIPT_DIR}/ai-stack}"
|
||||
mkdir -p "$ALT_BASE"
|
||||
warn "Using ${ALT_BASE}"
|
||||
AI_BASE="$ALT_BASE"
|
||||
export AI_LSC_BASE_DIR="$AI_BASE"
|
||||
fi
|
||||
elif command -v sudo &>/dev/null; then
|
||||
sudo mkdir -p "$AI_BASE"
|
||||
sudo chown "$(id -u):$(id -g)" "$AI_BASE"
|
||||
info "Created ${AI_BASE}"
|
||||
else
|
||||
error "Need sudo/root to create ${AI_BASE}. Create it manually and re-run."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ The emoji encoding lets the user scan the tab bar at a glance and see what kind
|
|||
|
||||
### Sub-tab closing
|
||||
|
||||
Sub-tabs are closable via the standard × button. Closing a sub-tab calls `stop_polling()` on the page (for CLI tools) but does **NOT** stop the underlying tool — that's the user's call from the Stack Editor. The empty-state placeholder is restored when all sub-tabs are closed.
|
||||
Sub-tabs are closable via the standard × button. Closing a sub-tab calls `stop_polling()` on the page (for CLI tools) but does **NOT** stop the underlying tool — that's the user's call from the Stack Editor. When all sub-tabs are closed the empty-state placeholder is shown again.
|
||||
|
||||
## Refresh model
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ readme = "README.md"
|
|||
license = {text = "AGPL-3.0-or-later"}
|
||||
requires-python = ">=3.11"
|
||||
authors = [
|
||||
{name = "AI-LSC Contributors"},
|
||||
{name = "Jeremy Anderson", email = "info@dcos.net"},
|
||||
]
|
||||
maintainers = [
|
||||
{name = "Jeremy Anderson", email = "info@dcos.net"},
|
||||
]
|
||||
keywords = [
|
||||
"ai", "llm", "local-ai", "stack-management", "pyside6",
|
||||
|
|
|
|||
|
|
@ -198,10 +198,10 @@ EXTRA_CATEGORY_LAYERS: dict[str, str] = {
|
|||
"Observability": "Human Interface & System Operations",
|
||||
}
|
||||
|
||||
# The v3.1.1b db_manager (restored from the routing tarball) carries a
|
||||
# The v3.1.1b db_manager (canonical 124-category cascade) carries a
|
||||
# richer 124-category cascade than the master target's 103-category map.
|
||||
# These alt-only categories are preserved and translated to the 10-layer
|
||||
# taxonomy so the categorisation cascade keeps its full coverage.
|
||||
# These alt-only categories are translated to the 10-layer taxonomy so
|
||||
# the categorisation cascade keeps its full coverage.
|
||||
ALT_CATEGORY_TRANSLATIONS: dict[str, str] = {
|
||||
"AI Assistant Platform": "Human Interface & System Operations",
|
||||
"AI Augmentation": "Multi-Agent Orchestration Runtimes",
|
||||
|
|
@ -665,7 +665,7 @@ SECTION_RENAMES = [
|
|||
"# L3: GPU Runtime → Layer 3: GPU Acceleration & Optimization"),
|
||||
("# L4: Inference Engines",
|
||||
"# L4: Inference Engines → Layer 4: Local Inference Engines"),
|
||||
("# L6: AI Endpoints (→ the restored \"Routing\" layer in the 11-layer taxonomy)",
|
||||
("# L6: AI Endpoints (→ the L5 \"Routing\" layer in the 11-layer taxonomy)",
|
||||
"# L6: AI Endpoints → Layer 5: Intelligent API Routers & Proxies (10-layer taxonomy)"),
|
||||
("# L10: Intelligent Routing (folded into \"Orchestrators\" in the 11-layer taxonomy)",
|
||||
"# L10: Intelligent Routing → Layer 5: Intelligent API Routers & Proxies (10-layer taxonomy)"),
|
||||
|
|
@ -754,8 +754,8 @@ def category_map_lines(cmap: dict[str, dict]) -> str:
|
|||
|
||||
|
||||
def stage_category_map() -> None:
|
||||
# read the current CATEGORY_MAP from the restored v3.1.1b db_manager
|
||||
# (full 124-category cascade in the old 11-layer taxonomy)
|
||||
# Read the current CATEGORY_MAP from the canonical v3.1.1b db_manager
|
||||
# (full 124-category cascade in the old 11-layer taxonomy).
|
||||
dbm = SRC / "ai_lsc" / "ui" / "pages" / "db_manager.py"
|
||||
content = dbm.read_text(encoding="utf-8")
|
||||
m = re.search(
|
||||
|
|
|
|||
|
|
@ -76,26 +76,32 @@ class ManifestSupport:
|
|||
|
||||
@staticmethod
|
||||
def build_system_context(manifest: dict[str, Any]) -> str:
|
||||
"""Build a flat system-prompt text block from manifest data."""
|
||||
"""Build a flat system-prompt text block from manifest data.
|
||||
|
||||
Source-of-truth boundary: this method renders a *derived* view of
|
||||
the manifest dict. The manifest file itself is an optional
|
||||
convenience; if absent, callers fall back to defaults — the
|
||||
registry layer files in :mod:`ai_lsc.registry.layers` remain
|
||||
the authoritative source for tool definitions.
|
||||
"""
|
||||
# (manifest_key, label) pairs in display order. Each entry whose
|
||||
# manifest value is truthy becomes one line in the prompt.
|
||||
_FIELDS: tuple[tuple[str, str], ...] = (
|
||||
("description", "Description"),
|
||||
("language", "Language"),
|
||||
("entry_point", "Entry Point"),
|
||||
("architecture", "Architecture"),
|
||||
("environment_notes", "Environment"),
|
||||
)
|
||||
project = manifest.get("project", "Unknown Project")
|
||||
description = manifest.get("description", "")
|
||||
language = manifest.get("language", "")
|
||||
entry = manifest.get("entry_point", "")
|
||||
architecture = manifest.get("architecture", "")
|
||||
environment = manifest.get("environment_notes", "")
|
||||
dependencies = manifest.get("dependencies", [])
|
||||
|
||||
parts = [f"Project: {project}"]
|
||||
if description:
|
||||
parts.append(f"Description: {description}")
|
||||
if language:
|
||||
parts.append(f"Language: {language}")
|
||||
if entry:
|
||||
parts.append(f"Entry Point: {entry}")
|
||||
if architecture:
|
||||
parts.append(f"Architecture: {architecture}")
|
||||
if environment:
|
||||
parts.append(f"Environment: {environment}")
|
||||
parts.extend(
|
||||
f"{label}: {manifest.get(key)}"
|
||||
for key, label in _FIELDS
|
||||
if manifest.get(key)
|
||||
)
|
||||
if dependencies:
|
||||
parts.append(f"Dependencies: {', '.join(dependencies)}")
|
||||
|
||||
|
|
@ -110,15 +116,15 @@ class ManifestSupport:
|
|||
base = Path(base_dir)
|
||||
patterns = manifest.get("context_files", [])
|
||||
exclude = set(manifest.get("exclude", []))
|
||||
files: list[str] = []
|
||||
|
||||
for pattern in patterns:
|
||||
full_pattern = str(base / pattern)
|
||||
matched = glob_mod.glob(full_pattern, recursive=True)
|
||||
for f in matched:
|
||||
if Path(f).is_file() and not any(ex in f for ex in exclude):
|
||||
files.append(f)
|
||||
return files
|
||||
# Single-pass comprehension: expand every pattern, keep only
|
||||
# regular files whose path does not contain any excluded token.
|
||||
return [
|
||||
f
|
||||
for pattern in patterns
|
||||
for f in glob_mod.glob(str(base / pattern), recursive=True)
|
||||
if Path(f).is_file() and not any(ex in f for ex in exclude)
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def load_jcl(path: str | Path) -> list[dict[str, Any]]:
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
"""Registry entries for the Routing layer (L5).
|
||||
|
||||
Restored layer (v3.1.1b): model gateways, LLM proxies, request routers,
|
||||
mesh transport, and the model-routing tier that sits between the Engines
|
||||
(who serve weights) and the Orchestrators (who build agent workflows on
|
||||
top of a single OpenAI-compatible endpoint). This re-unites the old
|
||||
13-layer model's "AI Endpoints" tier (LiteLLM, model routers, API
|
||||
gateways) that had been folded into Orchestrators during the 13-to-10
|
||||
reorg, plus the mesh-aware clients that ride on it.
|
||||
L5 (Intelligent API Routers & Proxies) holds the model-routing tier
|
||||
that sits between the Engines (who serve weights) and the Orchestrators
|
||||
(who build agent workflows on top of a single OpenAI-compatible
|
||||
endpoint): model gateways, LLM proxies, request routers, mesh
|
||||
transport, and the mesh-aware clients that ride on it.
|
||||
|
||||
This module is consumed by :mod:`ai_lsc.registry.loader`.
|
||||
This module is consumed by :mod:`ai_lsc.registry.loader`. The loader
|
||||
merges by tool, not by filename, so tools may be regrouped across
|
||||
files in a future pass without a behavior change.
|
||||
|
||||
Structural fields (layer, level) follow the 10-Layer Systems
|
||||
Architecture Taxonomy; tools may be regrouped across files in a
|
||||
future pass — the loader merges by tool, not by filename.
|
||||
Source of truth: the entries below. The merged registry produced by
|
||||
``loader.load_merged_registry()`` is a derived view; if any manifest
|
||||
or cached ``ecosystem.json`` disagrees, the source in this file wins.
|
||||
"""
|
||||
|
||||
TOOLS: dict[str, dict] = {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ _OE_CONTENT_DIRS: list[str] = [
|
|||
# File extensions we attempt to parse.
|
||||
_OE_PARSE_EXTENSIONS: set[str] = {".md", ".markdown", ".txt"}
|
||||
|
||||
# Root-level files that are repo meta, not OE content.
|
||||
_OE_ROOT_SKIP_NAMES: frozenset[str] = frozenset({
|
||||
"README.md", "CONTRIBUTING.md", "CHARTER.md",
|
||||
"LICENSE", "ROADMAP.md",
|
||||
})
|
||||
|
||||
|
||||
class OpenEngineerImporter:
|
||||
"""Import Open Engineer files into StandardTemplate objects.
|
||||
|
|
@ -164,51 +170,53 @@ class OpenEngineerImporter:
|
|||
overrides = stack_config_overrides or {}
|
||||
templates: list[StandardTemplate] = []
|
||||
|
||||
# Scan known OE content directories
|
||||
# Pass 1 — known OE content subdirs (templates inferred from OE fields).
|
||||
for subdir_name in _OE_CONTENT_DIRS:
|
||||
subdir = root / subdir_name
|
||||
if not subdir.is_dir():
|
||||
continue
|
||||
templates.extend(self._scan_dir(subdir, overrides=overrides, drop_unknown=False))
|
||||
|
||||
# Pass 2 — root-level standalone OE files (drop unknowns so README/LICENSE/etc.
|
||||
# do not pollute the import). The _OE_ROOT_SKIP_NAMES filter applies here only.
|
||||
templates.extend(self._scan_dir(root, overrides=None, drop_unknown=True, skip=_OE_ROOT_SKIP_NAMES))
|
||||
|
||||
return sorted(templates, key=lambda t: t.source_file)
|
||||
|
||||
def _scan_dir(
|
||||
self,
|
||||
subdir: Path,
|
||||
*,
|
||||
overrides: dict[str, dict[str, Any]] | None = None,
|
||||
drop_unknown: bool = False,
|
||||
skip: frozenset[str] | None = None,
|
||||
) -> list[StandardTemplate]:
|
||||
"""Import every parseable file directly inside *subdir*.
|
||||
|
||||
Single-responsibility scan loop: yields one StandardTemplate per
|
||||
parseable markdown file. Errors per file are swallowed so one bad
|
||||
file does not abort the scan.
|
||||
"""
|
||||
results: list[StandardTemplate] = []
|
||||
for md_file in sorted(subdir.iterdir()):
|
||||
if md_file.suffix.lower() not in _OE_PARSE_EXTENSIONS:
|
||||
continue
|
||||
if md_file.name.startswith("."):
|
||||
continue
|
||||
|
||||
try:
|
||||
tpl_id = self._derive_template_id(
|
||||
md_file, md_file.stem
|
||||
)
|
||||
override = overrides.get(tpl_id)
|
||||
template = self.import_file(md_file, stack_config=override)
|
||||
templates.append(template)
|
||||
except Exception:
|
||||
# Don't let one bad file stop the import
|
||||
continue
|
||||
|
||||
# Also scan root for standalone OE files
|
||||
for md_file in sorted(root.iterdir()):
|
||||
if not md_file.is_file():
|
||||
continue
|
||||
if md_file.suffix.lower() not in _OE_PARSE_EXTENSIONS:
|
||||
continue
|
||||
if md_file.name in {
|
||||
"README.md", "CONTRIBUTING.md", "CHARTER.md",
|
||||
"LICENSE", "ROADMAP.md",
|
||||
}:
|
||||
continue
|
||||
if md_file.name.startswith("."):
|
||||
continue
|
||||
|
||||
if skip and md_file.name in skip:
|
||||
continue
|
||||
try:
|
||||
template = self.import_file(md_file)
|
||||
if template.source_type != "unknown":
|
||||
templates.append(template)
|
||||
tpl_id = self._derive_template_id(md_file, md_file.stem)
|
||||
override = (overrides or {}).get(tpl_id)
|
||||
template = self.import_file(md_file, stack_config=override)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return sorted(templates, key=lambda t: t.source_file)
|
||||
if drop_unknown and template.source_type == "unknown":
|
||||
continue
|
||||
results.append(template)
|
||||
return results
|
||||
|
||||
# ── Bulk convert to AI-LSC format ──────────────────────────────
|
||||
|
||||
|
|
|
|||
|
|
@ -574,6 +574,8 @@ class InstallerManager:
|
|||
"""
|
||||
dest = os.path.join(self.tools_root, tool_id)
|
||||
git_dir = os.path.join(dest, ".git")
|
||||
|
||||
# Step-down: each branch resolves one situation and assigns msg.
|
||||
if os.path.isdir(git_dir):
|
||||
try:
|
||||
subprocess.run(
|
||||
|
|
@ -586,36 +588,22 @@ class InstallerManager:
|
|||
"git pull failed for %s (%s); re-cloning fresh",
|
||||
tool_id, exc,
|
||||
)
|
||||
backup = dest + ".bak." + str(int(time.time()))
|
||||
shutil.move(dest, backup)
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", pkg, dest],
|
||||
check=True, timeout=600,
|
||||
)
|
||||
backup = self._backup_existing(dest)
|
||||
self._clone_fresh(pkg, dest)
|
||||
msg = (
|
||||
f"Git source re-cloned (pull failed, "
|
||||
f"old copy at {backup}): {dest}"
|
||||
)
|
||||
elif os.path.exists(dest):
|
||||
# Dir exists but is not a git repo — back it up and clone.
|
||||
backup = dest + ".bak." + str(int(time.time()))
|
||||
shutil.move(dest, backup)
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", pkg, dest],
|
||||
check=True, timeout=600,
|
||||
)
|
||||
backup = self._backup_existing(dest)
|
||||
self._clone_fresh(pkg, dest)
|
||||
msg = (
|
||||
f"Git source cloned (existing non-git dir backed up "
|
||||
f"at {backup}): {dest}"
|
||||
)
|
||||
else:
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", pkg, dest],
|
||||
check=True, timeout=600,
|
||||
)
|
||||
self._clone_fresh(pkg, dest)
|
||||
msg = f"Git source cloned: {dest}"
|
||||
|
||||
if post_install:
|
||||
|
|
@ -638,6 +626,9 @@ class InstallerManager:
|
|||
"""
|
||||
dest = os.path.join(self.tools_root, tool_id)
|
||||
git_dir = os.path.join(dest, ".git")
|
||||
|
||||
# Step-down: each branch resolves one situation, runs the
|
||||
# appropriate clone/pull+yarn sequence, and assigns msg.
|
||||
if os.path.isdir(git_dir):
|
||||
try:
|
||||
subprocess.run(
|
||||
|
|
@ -653,12 +644,8 @@ class InstallerManager:
|
|||
"git pull / yarn install failed for %s (%s); re-cloning",
|
||||
tool_id, exc,
|
||||
)
|
||||
backup = dest + ".bak." + str(int(time.time()))
|
||||
shutil.move(dest, backup)
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", pkg, dest], check=True, timeout=600,
|
||||
)
|
||||
backup = self._backup_existing(dest)
|
||||
self._clone_fresh(pkg, dest)
|
||||
subprocess.run(
|
||||
["yarn", "install"], cwd=dest, check=True, timeout=300,
|
||||
)
|
||||
|
|
@ -667,12 +654,8 @@ class InstallerManager:
|
|||
f"old copy at {backup}): {dest}"
|
||||
)
|
||||
elif os.path.exists(dest):
|
||||
backup = dest + ".bak." + str(int(time.time()))
|
||||
shutil.move(dest, backup)
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", pkg, dest], check=True, timeout=600,
|
||||
)
|
||||
backup = self._backup_existing(dest)
|
||||
self._clone_fresh(pkg, dest)
|
||||
subprocess.run(
|
||||
["yarn", "install"], cwd=dest, check=True, timeout=300,
|
||||
)
|
||||
|
|
@ -681,10 +664,7 @@ class InstallerManager:
|
|||
f"up at {backup}): {dest}"
|
||||
)
|
||||
else:
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", pkg, dest], check=True, timeout=600,
|
||||
)
|
||||
self._clone_fresh(pkg, dest)
|
||||
subprocess.run(
|
||||
["yarn", "install"], cwd=dest, check=True, timeout=300,
|
||||
)
|
||||
|
|
@ -694,6 +674,27 @@ class InstallerManager:
|
|||
self._run_post_install(tool_id, post_install)
|
||||
return msg
|
||||
|
||||
# ── git install helpers ────────────────────────────────────────
|
||||
|
||||
@staticmethod
|
||||
def _backup_existing(dest: str) -> str:
|
||||
"""Move *dest* aside to ``<dest>.bak.<unix_ts>`` and return the backup path.
|
||||
|
||||
Caller guarantees *dest* exists.
|
||||
"""
|
||||
backup = f"{dest}.bak.{int(time.time())}"
|
||||
shutil.move(dest, backup)
|
||||
return backup
|
||||
|
||||
@staticmethod
|
||||
def _clone_fresh(pkg: str, dest: str) -> None:
|
||||
"""Create *dest* (empty) and run ``git clone pkg dest`` into it."""
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", pkg, dest],
|
||||
check=True, timeout=600,
|
||||
)
|
||||
|
||||
def install_script(
|
||||
self,
|
||||
cmd: str,
|
||||
|
|
|
|||
|
|
@ -360,35 +360,35 @@ if _HAS_QT:
|
|||
},
|
||||
}
|
||||
|
||||
html = (
|
||||
"<body style='background:#121212; color:#e0e0e0;"
|
||||
" font-family:sans-serif;'>"
|
||||
)
|
||||
for msg in self.chat_messages:
|
||||
# Dispatch table for the bubble-style key — replaces a
|
||||
# 2-level nested ternary on (is_user, is_error).
|
||||
_identity_color = {"user": "#e0e0e0", "error": "#e74c3c", "assistant": "#d35400"}
|
||||
_identity_label = {
|
||||
"user": "<b>You</b>",
|
||||
# assistant/error labels render identity inline; filled below.
|
||||
}
|
||||
|
||||
def _style_key(msg: dict) -> str:
|
||||
if msg["is_user"]:
|
||||
return "user"
|
||||
return "error" if "?" in msg["identity"] else "assistant"
|
||||
|
||||
def _render_bubble(msg: dict) -> str:
|
||||
style_key = _style_key(msg)
|
||||
s = bubble_styles[style_key]
|
||||
ts = msg["timestamp"]
|
||||
payload = (
|
||||
msg["payload"]
|
||||
.replace("<", "<").replace(">", ">")
|
||||
.replace("\n", "<br/>")
|
||||
)
|
||||
is_error = "?" in msg["identity"]
|
||||
style_key = (
|
||||
"user" if msg["is_user"]
|
||||
else ("error" if is_error else "assistant")
|
||||
)
|
||||
s = bubble_styles[style_key]
|
||||
label_color = (
|
||||
"#e0e0e0" if msg["is_user"]
|
||||
else ("#e74c3c" if is_error else "#d35400")
|
||||
)
|
||||
label = (
|
||||
"<b>You</b>" if msg["is_user"]
|
||||
else f"<b style='color:{label_color}'>{msg['identity']}</b>"
|
||||
)
|
||||
border_css = (
|
||||
f"border: {s['border']};" if "border" in s else ""
|
||||
)
|
||||
html += (
|
||||
if style_key == "user":
|
||||
label = _identity_label["user"]
|
||||
else:
|
||||
color = _identity_color[style_key]
|
||||
label = f"<b style='color:{color}'>{msg['identity']}</b>"
|
||||
border_css = f"border: {s['border']};" if "border" in s else ""
|
||||
return (
|
||||
f"<div style='margin-bottom:15px; text-align:{s['align']}';>"
|
||||
f"<span style='background:{s['bg']}; {border_css} color:#e0e0e0; "
|
||||
f"padding:10px 14px; border-radius:{s['radius']}; "
|
||||
|
|
@ -400,6 +400,12 @@ if _HAS_QT:
|
|||
f"</span></div>"
|
||||
)
|
||||
|
||||
html = (
|
||||
"<body style='background:#121212; color:#e0e0e0;"
|
||||
" font-family:sans-serif;'>"
|
||||
)
|
||||
html += "".join(_render_bubble(m) for m in self.chat_messages)
|
||||
|
||||
if self.is_thinking:
|
||||
model = self.cbo_chat_model.currentText() or "Model"
|
||||
html += (
|
||||
|
|
@ -638,11 +644,9 @@ if _HAS_QT:
|
|||
"Absorb and layer the following skill instructions:\n\n"
|
||||
+ "\n\n".join(skill_directives)
|
||||
)
|
||||
system_content = (
|
||||
parts[0] + "\n\n" + parts[1]
|
||||
if len(parts) == 2
|
||||
else (parts[0] if parts else "")
|
||||
)
|
||||
# Flat join beats the prior 3-way nested ternary; empty parts
|
||||
# naturally produce an empty string.
|
||||
system_content = "\n\n".join(parts)
|
||||
|
||||
self.chat_history_data.append(
|
||||
{"role": "user", "content": full_prompt}
|
||||
|
|
@ -650,9 +654,7 @@ if _HAS_QT:
|
|||
filtered = [
|
||||
m for m in self.chat_history_data if m.get("role") != "system"
|
||||
]
|
||||
result = []
|
||||
if system_content:
|
||||
result.append({"role": "system", "content": system_content})
|
||||
result = [{"role": "system", "content": system_content}] if system_content else []
|
||||
result.extend(filtered)
|
||||
return result
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
**Pass scope**: CRITICAL (C-01, C-02, C-03, C-04, C-06, C-07) + all 24 HIGH + all 42 MEDIUM + all 20 LOW
|
||||
**Pass status**: 91 of 93 findings addressed in code; 2 explicitly skipped per user instruction
|
||||
**Double-check pass**: 19/19 functional spot-checks pass; 2 latent bugs found and fixed (see "Bugs caught during double-check" below)
|
||||
**v3.2 note**: the 10-layer taxonomy migration preserved every curl|sh
|
||||
site below verbatim — the restored installer cmds (ollama, llamafile,
|
||||
**v3.2 note**: the 10-layer taxonomy migration carries every curl|sh
|
||||
site below verbatim — the canonical installer cmds (ollama, llamafile,
|
||||
qdrant, grafana_alloy, meilisearch, fabric) are byte-identical to the
|
||||
pre-migration registry, so the C-05 policy state is unchanged.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue