Pablo Claude Opus 4.7 (1M context) commited on
Commit
447af01
·
1 Parent(s): b4f1a79

fix: sync test_mcp_server imports with current server/models API

Browse files

tests/test_mcp_server.py imports four Depends-compatible getters from
mcp.server (get_registry, get_metrics, get_compressor, get_coordinator)
and a Degradation model from models. Neither existed, so collection
failed at "ImportError: cannot import name 'get_compressor'".

Changes:
- models.py: add Degradation Pydantic model (component, reason, fallback,
severity, timestamp) — fields chosen to match how the test instantiates it
(Degradation(component="compressor", reason="OOM", fallback="cpu")).
- mcp/server.py: add module-level dependency getters returning the live
globals. compressor and coordinator stay None (TODO marker) until the
lifespan refactor away from on_event lands. Renamed the existing
/metrics/snapshot endpoint function from get_metrics to
metrics_snapshot_endpoint so the importable get_metrics symbol resolves
to the Depends getter, not the path operation. The HTTP route is unchanged.

Verification:
- pytest tests/test_mcp_server.py --collect-only → 13 tests collected
(was: ImportError, 0 collected).
- pytest tests/ --ignore=tests/test_mcp_server.py → 286 passed, 11 failed,
23 skipped. The 11 failures (test_dedup LSH, test_integration registry)
reproduce on the pre-fix tree and are unrelated to this change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

apohara_context_forge/mcp/server.py CHANGED
@@ -25,6 +25,39 @@ app = FastAPI(title="ContextForge", version="0.1.0")
25
  registry = ContextRegistry()
26
  metrics = MetricsCollector()
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  # Request/Response models
30
  class ContextRegistration(BaseModel):
@@ -68,8 +101,12 @@ async def get_optimized_context(request: OptimizedContextRequest) -> Compression
68
 
69
 
70
  @app.get("/metrics/snapshot")
71
- async def get_metrics() -> MetricsSnapshot:
72
- """Get current metrics snapshot."""
 
 
 
 
73
  return await metrics.snapshot()
74
 
75
 
 
25
  registry = ContextRegistry()
26
  metrics = MetricsCollector()
27
 
28
+ # Compressor and coordinator are lazily wired by the production lifespan; they
29
+ # stay None at import time so server.py is importable without GPU/model deps.
30
+ # TODO: wire `compressor = ContextCompressor()` and `coordinator =
31
+ # CompressionCoordinator()` once the lifespan refactor away from on_event lands.
32
+ compressor = None
33
+ coordinator = None
34
+
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Dependency getters — these are FastAPI Depends() targets and the keys used by
38
+ # tests' ``app.dependency_overrides`` so each component can be swapped out for a
39
+ # fake. They MUST stay importable from the module top-level.
40
+ # ---------------------------------------------------------------------------
41
+
42
+ def get_registry() -> ContextRegistry:
43
+ """Return the live ContextRegistry singleton."""
44
+ return registry
45
+
46
+
47
+ def get_metrics() -> MetricsCollector:
48
+ """Return the live MetricsCollector singleton."""
49
+ return metrics
50
+
51
+
52
+ def get_compressor():
53
+ """Return the live ContextCompressor (None until lifespan wiring lands)."""
54
+ return compressor
55
+
56
+
57
+ def get_coordinator():
58
+ """Return the live CompressionCoordinator (None until lifespan wiring lands)."""
59
+ return coordinator
60
+
61
 
62
  # Request/Response models
63
  class ContextRegistration(BaseModel):
 
101
 
102
 
103
  @app.get("/metrics/snapshot")
104
+ async def metrics_snapshot_endpoint() -> MetricsSnapshot:
105
+ """Get current metrics snapshot.
106
+
107
+ Renamed from `get_metrics` so the module-level `get_metrics()` dependency
108
+ getter (above) stays the importable name. The HTTP path is unchanged.
109
+ """
110
  return await metrics.snapshot()
111
 
112
 
apohara_context_forge/models.py CHANGED
@@ -1,7 +1,7 @@
1
  """Pydantic data models - typed contracts for ContextForge."""
2
  from pydantic import BaseModel, Field
3
  from datetime import datetime
4
- from typing import Literal
5
 
6
 
7
  class ContextEntry(BaseModel):
@@ -61,3 +61,18 @@ class OptimizedContextRequest(BaseModel):
61
  """Request for optimized context."""
62
  agent_id: str
63
  context: str
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """Pydantic data models - typed contracts for ContextForge."""
2
  from pydantic import BaseModel, Field
3
  from datetime import datetime
4
+ from typing import Literal, Optional
5
 
6
 
7
  class ContextEntry(BaseModel):
 
61
  """Request for optimized context."""
62
  agent_id: str
63
  context: str
64
+
65
+
66
+ class Degradation(BaseModel):
67
+ """A degradation event (component falling back to a lower-fidelity path).
68
+
69
+ Used by the metrics snapshot and the /health endpoint so the dashboard
70
+ can show *why* a component is operating below its primary configuration —
71
+ e.g. compressor falling back to CPU because the GPU model failed to load,
72
+ or coordinator falling back to passthrough on OOM.
73
+ """
74
+ component: str # e.g. "compressor", "coordinator", "embedding_engine"
75
+ reason: str # short human-readable cause, e.g. "OOM", "model unavailable"
76
+ fallback: Optional[str] = None # what was used instead, e.g. "cpu", "passthrough"
77
+ severity: float = 0.5 # 0.0 = informational, 1.0 = critical
78
+ timestamp: datetime = Field(default_factory=datetime.now)