Let the console search, browse, forget and mount
The store had a write path, a mount path and a search path, and the console
still showed a dashboard of counters. Nothing in the UI could ask what someone
knows, page through atoms, look at a wordless picture, forget one memory, or
mount a cartridge — so the mechanisms built in P0 were invisible.
Backend:
- senses.py: one function decides what this machine can perceive. /api/status
and doctor each computed it before, which is two truths. Picture atoms are now
counted by image_ref rather than by modality, so a screenshot crop from a PDF
counts and a picture whose bytes were deleted stops claiming it can be encoded.
- browse.py: GET /api/atoms pages newest-first through v_atoms_current with
modality/holder/source filters; GET /api/atoms/{id}/image serves the stored
bytes so a picture with no OCR text can be looked at.
- Deleted 8 HTTP routes nobody calls (4 benchmark, research modules, dense index,
visual-contradiction-proof, memory-map). Their functions stay: the CLI and the
proof checks use them. The surface is 30 routes, pinned as a set by a test so a
new unused route fails the suite.
Frontend: App.tsx (517 lines) became a 131-line shell plus seven panels that own
their own fetches, and lib/format.ts. types.ts now carries the memory dimensions
the backend has been sending since schema v2.
Two invented fields were rendering lies: AdapterPanel showed gpu_available and
probe_status, which AdapterLoadReport does not have — every machine looked GPU-less
including this one. The panel now shows only what the server says.
Proof: scripts/ui_smoke.sh starts the API on a fresh temp home, serves the built
UI from the same origin, and drives five interactions in a real browser — search
as somebody in a scenario, page and view a wordless picture, forget, mount, read
adapter states with reasons. It prints the mount refusal verbatim rather than
pretending the mount succeeded.
Gates: 263 pytest passed; ruff and mypy clean on the 8 touched backend files
(repo-wide mypy unchanged at 24 errors / 6 files); pyright 0 errors there;
go build/vet/test/gofmt clean; npm build + eslint clean; ui_smoke and
mcp_write_smoke both PASS.
This commit is contained in:
1 parent
a5008e1476
commit
9f91ff7ed7
27 files changed
+2146
-616
No files matched your search
+34
-62
@@ -5,8 +5,9 @@ import shutil
|
||||
from pathlib import Path
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, File, HTTPException, UploadFile
|
||||
from fastapi import Depends, FastAPI, File, HTTPException, Query, UploadFile
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -14,33 +15,18 @@ from . import __version__
|
||||
from .adapters import plan as adapter_plan
|
||||
from .api_cartridges import register as register_cartridge_routes
|
||||
from .api_holders import register as register_holder_routes
|
||||
from .benchmarks import (
|
||||
benchmark_prediction_report,
|
||||
benchmark_registry_report,
|
||||
benchmark_sample_report,
|
||||
benchmark_source_report,
|
||||
)
|
||||
from .browse import browse, picture
|
||||
from .cache_fabric import CacheFabric
|
||||
from .cartridge import CartridgeService
|
||||
from .compiler import ContextCompiler
|
||||
from .config import Settings, get_settings
|
||||
from .db import Database
|
||||
from .embeddings import EmbeddingService
|
||||
from .evaluation_schemas import (
|
||||
BenchmarkPredictionReport,
|
||||
BenchmarkRegistryReport,
|
||||
BenchmarkSampleReport,
|
||||
BenchmarkSourceReport,
|
||||
HashHopProof,
|
||||
ProofCheck,
|
||||
ProofSuiteReport,
|
||||
)
|
||||
from .evaluation_schemas import HashHopProof, ProofSuiteReport
|
||||
from .evidence_graph import EvidenceGraphService
|
||||
from .ingestion import IngestionService
|
||||
from .memory import MemoryService
|
||||
from .profiles import runtime_profile_report
|
||||
from .proofs import HashHopEvaluator, PRDProofSuite
|
||||
from .research_modules import ResearchModuleService
|
||||
from .retrieval import RetrievalService
|
||||
from .runtime import QwenRuntime, RuntimeUnavailableError
|
||||
from .runtime_schemas import AdapterLoadReport
|
||||
@@ -48,11 +34,13 @@ from .schemas import (
|
||||
AdapterManifest,
|
||||
AdapterStatus,
|
||||
AnswerResponse,
|
||||
AtomPage,
|
||||
EvidenceAtom,
|
||||
ForgetReport,
|
||||
ForgetRequest,
|
||||
MemoryOrigin,
|
||||
MemoryStatus,
|
||||
Modality,
|
||||
PathIngestRequest,
|
||||
RememberRequest,
|
||||
RuntimeProfile,
|
||||
@@ -63,6 +51,7 @@ from .schemas import (
|
||||
SystemStatus,
|
||||
UploadIngestRequest,
|
||||
)
|
||||
from .senses import sense_facts
|
||||
from .training import safetensor_weight_stats
|
||||
from .verifier import Verifier, VisualContradictionLens
|
||||
|
||||
@@ -114,6 +103,7 @@ def create_app() -> FastAPI:
|
||||
version=__version__,
|
||||
runtime=runtime,
|
||||
memory=memory,
|
||||
senses=sense_facts(settings, db),
|
||||
data_home=str(settings.home),
|
||||
profiles={
|
||||
"Live Demo": {"context_tokens": settings.live_context_tokens, "purpose": "stable 262K workflow"},
|
||||
@@ -260,21 +250,33 @@ def create_app() -> FastAPI:
|
||||
def adapters(settings: Annotated[Settings, Depends(settings_dep)]) -> list[AdapterStatus]:
|
||||
return list_adapter_statuses(settings)
|
||||
|
||||
@app.get("/api/benchmarks/registry", response_model=BenchmarkRegistryReport)
|
||||
def benchmarks_registry() -> BenchmarkRegistryReport:
|
||||
return benchmark_registry_report()
|
||||
@app.get("/api/atoms", response_model=AtomPage)
|
||||
def atoms(
|
||||
settings: Annotated[Settings, Depends(settings_dep)],
|
||||
limit: Annotated[int, Query(ge=1, le=200)] = 50,
|
||||
cursor: str | None = None,
|
||||
modality: Modality | None = None,
|
||||
source_id: str | None = None,
|
||||
holder: str | None = None,
|
||||
) -> AtomPage:
|
||||
try:
|
||||
return browse(
|
||||
settings,
|
||||
limit=limit,
|
||||
cursor=cursor,
|
||||
modality=modality,
|
||||
source_id=source_id,
|
||||
holder=holder,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
@app.get("/api/benchmarks/sources", response_model=BenchmarkSourceReport)
|
||||
def benchmarks_sources() -> BenchmarkSourceReport:
|
||||
return benchmark_source_report()
|
||||
|
||||
@app.get("/api/benchmarks/samples", response_model=BenchmarkSampleReport)
|
||||
def benchmarks_samples(settings: Annotated[Settings, Depends(settings_dep)]) -> BenchmarkSampleReport:
|
||||
return benchmark_sample_report(settings.data_dir / "benchmark_samples")
|
||||
|
||||
@app.get("/api/benchmarks/predictions", response_model=BenchmarkPredictionReport)
|
||||
def benchmarks_predictions(settings: Annotated[Settings, Depends(settings_dep)]) -> BenchmarkPredictionReport:
|
||||
return benchmark_prediction_report(settings, settings.data_dir / "benchmark_predictions")
|
||||
@app.get("/api/atoms/{atom_id}/image")
|
||||
def atom_image(atom_id: str, settings: Annotated[Settings, Depends(settings_dep)]) -> FileResponse:
|
||||
found = picture(settings, atom_id)
|
||||
if found is None:
|
||||
raise HTTPException(status_code=404, detail=f"{atom_id} has no picture to show")
|
||||
return FileResponse(found.path, media_type=found.mime)
|
||||
|
||||
@app.get("/api/runtime/profiles", response_model=RuntimeProfileReport)
|
||||
def runtime_profiles(
|
||||
@@ -286,22 +288,6 @@ def create_app() -> FastAPI:
|
||||
def runtime_adapters(settings: Annotated[Settings, Depends(settings_dep)]) -> AdapterLoadReport:
|
||||
return adapter_plan(settings, Database(settings))
|
||||
|
||||
@app.get("/api/research/modules")
|
||||
def research_modules(settings: Annotated[Settings, Depends(settings_dep)]):
|
||||
db = Database(settings)
|
||||
compiler = ContextCompiler(settings, db)
|
||||
context = compiler.compile(
|
||||
"Ely-Eye V-NSA DVKV x-modal KV speculative prefetch evidence graph",
|
||||
RuntimeProfile.research_theater,
|
||||
)
|
||||
return ResearchModuleService(settings, db).build_report(context.plan.question, context.hits)
|
||||
|
||||
@app.post("/api/index/dense")
|
||||
def build_dense_index(settings: Annotated[Settings, Depends(settings_dep)]):
|
||||
service = EmbeddingService(settings)
|
||||
index = service.build_index()
|
||||
return {"atoms": len(index.atom_ids), "dimensions": int(index.vectors.shape[1]) if index.vectors.size else 0}
|
||||
|
||||
@app.post("/api/hashhop", response_model=HashHopProof)
|
||||
def hashhop(request: HashHopRequest, settings: Annotated[Settings, Depends(settings_dep)]) -> HashHopProof:
|
||||
evaluator = HashHopEvaluator(settings)
|
||||
@@ -328,20 +314,6 @@ def create_app() -> FastAPI:
|
||||
raise HTTPException(status_code=404, detail="No PRD proof suite has been generated.")
|
||||
return report
|
||||
|
||||
@app.post("/api/visual-contradiction-proof", response_model=ProofCheck)
|
||||
def visual_contradiction_proof(settings: Annotated[Settings, Depends(settings_dep)]) -> ProofCheck:
|
||||
return PRDProofSuite(settings).visual_contradiction_proof()
|
||||
|
||||
@app.get("/api/memory-map")
|
||||
def memory_map(settings: Annotated[Settings, Depends(settings_dep)]):
|
||||
db = Database(settings)
|
||||
atoms = db.list_atoms(limit=200)
|
||||
return {
|
||||
"atoms": [atom.model_dump(mode="json") for atom in atoms],
|
||||
"layers": CacheFabric(settings, db).statuses(),
|
||||
"cartridges": CartridgeService(settings, db).list_manifests(),
|
||||
}
|
||||
|
||||
mounted_ui = get_settings().ui_dir
|
||||
if mounted_ui is not None:
|
||||
if not mounted_ui.is_dir():
|
||||
|
||||
Reference in new issue
Block a user