| """Helper script to add /rlm/query endpoint to main.py"""
|
|
|
| rlm_endpoint = '''
|
|
|
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| # Phase 4.5: Recursive Synthesis Engine Endpoint
|
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
| class RLMQueryRequest(BaseModel):
|
| """Request model for Phase 4.5 recursive memory query."""
|
| query: str = Field(..., min_length=1, max_length=4096, description="The query to synthesize (can be complex/multi-topic)")
|
| context_text: Optional[str] = Field(None, max_length=500000, description="Optional large external text (Ripple environment)")
|
| project_id: Optional[str] = Field(None, max_length=128, description="Optional project scope for isolation masking")
|
| max_depth: Optional[int] = Field(None, ge=0, le=5, description="Max recursion depth (0-5, default 3)")
|
| max_sub_queries: Optional[int] = Field(None, ge=1, le=10, description="Max sub-queries to decompose into (1-10, default 5)")
|
| top_k: Optional[int] = Field(None, ge=1, le=50, description="Final results to return (default 10)")
|
|
|
|
|
| class RLMQueryResponse(BaseModel):
|
| """Response model for Phase 4.5 recursive memory query."""
|
| ok: bool
|
| query: str
|
| sub_queries: List[str]
|
| results: List[Dict[str, Any]]
|
| synthesis: str
|
| max_depth_hit: int
|
| elapsed_ms: float
|
| ripple_snippets: List[str]
|
| stats: Dict[str, Any]
|
|
|
|
|
| @app.post(
|
| "/rlm/query",
|
| response_model=RLMQueryResponse,
|
| dependencies=[Depends(get_api_key), Depends(QueryRateLimiter())],
|
| tags=["Phase 4.5"],
|
| summary="Recursive Synthesis Query",
|
| description=(
|
| "Phase 4.5: Recursive Language Model (RLM) query. "
|
| "Decomposes complex queries into sub-questions, searches MnemoCore in parallel, "
|
| "recursively analyzes low-confidence clusters, and synthesizes a final answer. "
|
| "Implements the MIT CSAIL RLM paradigm to eliminate Context Rot."
|
| ),
|
| )
|
| @track_async_latency(API_REQUEST_LATENCY, {"method": "POST", "endpoint": "/rlm/query"})
|
| async def rlm_query(
|
| req: RLMQueryRequest,
|
| engine: HAIMEngine = Depends(get_engine),
|
| ):
|
| """
|
| Phase 4.5 Recursive Synthesis Engine.
|
|
|
| Instead of a single flat search, this endpoint:
|
| 1. Decomposes your query into focused sub-questions
|
| 2. Searches MnemoCore in PARALLEL for each sub-question
|
| 3. Recursively drills into low-confidence clusters
|
| 4. Synthesizes all results into a coherent answer
|
|
|
| Rate limit: 500/minute (shared with /query).
|
| """
|
| API_REQUEST_COUNT.labels(method="POST", endpoint="/rlm/query", status="200").inc()
|
|
|
| from mnemocore.core.recursive_synthesizer import RecursiveSynthesizer, SynthesizerConfig
|
| from mnemocore.core.ripple_context import RippleContext
|
|
|
| # Build config from request overrides
|
| synth_config = SynthesizerConfig(
|
| max_depth=req.max_depth if req.max_depth is not None else 3,
|
| max_sub_queries=req.max_sub_queries if req.max_sub_queries is not None else 5,
|
| final_top_k=req.top_k if req.top_k is not None else 10,
|
| )
|
|
|
| # Build RippleContext if external text provided
|
| ripple_ctx = None
|
| if req.context_text and req.context_text.strip():
|
| ripple_ctx = RippleContext(text=req.context_text, source_label="api_context")
|
|
|
| # Run recursive synthesis (no LLM wired at API level β use heuristic mode)
|
| # To enable LLM synthesis, configure via RLMIntegrator in your application code
|
| synthesizer = RecursiveSynthesizer(engine=engine, config=synth_config)
|
| result = await synthesizer.synthesize(
|
| query=req.query,
|
| ripple_context=ripple_ctx,
|
| project_id=req.project_id,
|
| )
|
|
|
| return {
|
| "ok": True,
|
| "query": result.query,
|
| "sub_queries": result.sub_queries,
|
| "results": result.results,
|
| "synthesis": result.synthesis,
|
| "max_depth_hit": result.max_depth_hit,
|
| "elapsed_ms": result.total_elapsed_ms,
|
| "ripple_snippets": result.ripple_snippets,
|
| "stats": result.stats,
|
| }
|
|
|
| '''
|
|
|
| path = "src/api/main.py"
|
| content = open(path, "r", encoding="utf-8").read()
|
|
|
|
|
| marker = '\nif __name__ == "__main__":'
|
| idx = content.find(marker)
|
| if idx == -1:
|
| print("ERROR: marker not found")
|
| else:
|
| new_content = content[:idx] + rlm_endpoint + content[idx:]
|
| open(path, "w", encoding="utf-8").write(new_content)
|
| print(f"OK: /rlm/query endpoint inserted at position {idx}")
|
| print(f"New length: {len(new_content)}")
|
|
|