12b2300f6f
---ci--- phase: 7 milestone: v0.3 status: review lessons: - P0 CORS: allow_methods lacked PUT while the build surface writes files with PUT — every cross-origin Save failed preflight; pinned with tests/api/test_cors.py - P0 ingest leak: queue-overflow flood path returned without the disconnect sentinel, parking the drainer forever (one leaked task-set per flooded trace); sentinel now always enqueued, real-server regression test added - P1 perf: flood cap counted rows via len(get_trace(...)) — O(trace) per append, O(n²) per session; TraceStore.count() (COUNT(*)) added and wired - P0 security: file routes followed exec-planted symlinks out of the workspace bind; _resolve_in_workspace refuses escapes (422), read/write now 404 on unknown sandboxes (was 500) - P1 security: WS ingest accepted any browser Origin (CORS middleware does not cover WS); localhost dev origins + no-Origin (capture agent) allowed, others 1008 - P1 correctness: use-sandbox-session leaked a created sandbox on any mid-start failure (per-learner cap 1 → all retries 429 forever); failed starts now destroy what they created - P2 testing: reconnect-flush test killed mid-burst (nondeterministic under load, reproduced on pre-change code); now waits for server-side observation of the pre-kill burst — the underlying one-line replay-margin/ACK gap is documented for v0.4 - maintainability: grading-store/templates/grading.ts docstrings claimed grading is variant-blind (stale pre-P4 text) — updated; ARCHITECTURE.md referenced nonexistent voice/openai_audio.py; dead if TYPE_CHECKING: pass blocks removed ---/ci---
40 lines
1.5 KiB
Python
40 lines
1.5 KiB
Python
"""LabAgent — in-flow feedback over LIVE sandbox telemetry (REQ-3-007).
|
|
|
|
v0.3 re-grounding: consumes a TraceDigest computed from the learner's real
|
|
trace (grading/features.compute_digest over TraceStore events) — the v0.2
|
|
corpus scenarios are retired from this path (corpus dormancy, Task 6-1-04).
|
|
No session chat — each request is one live-trace read.
|
|
"""
|
|
|
|
from collections.abc import AsyncIterator
|
|
|
|
from ..config import Settings
|
|
from ..corpus.learner_context import LearnerContext, get_learner_context
|
|
from ..grading.features import TraceDigest
|
|
from ..llm.base import LLMProvider
|
|
from ..prompts.lab import SYSTEM_PROMPT, render_context, render_digest_timeline
|
|
from .base import BaseAgent
|
|
|
|
|
|
class LabAgent(BaseAgent):
|
|
name = "lab"
|
|
|
|
def __init__(self, provider: LLMProvider, settings: Settings) -> None:
|
|
super().__init__(provider, settings)
|
|
|
|
def system_prompt(self, learner_context: LearnerContext | None = None) -> str:
|
|
ctx = learner_context or get_learner_context()
|
|
return SYSTEM_PROMPT.format_map(render_context(ctx))
|
|
|
|
async def stream_feedback(
|
|
self,
|
|
digest: TraceDigest | None,
|
|
learner_context: LearnerContext | None = None,
|
|
) -> AsyncIterator[str]:
|
|
"""Feedback grounded in the learner's live trace digest."""
|
|
timeline = render_digest_timeline(digest)
|
|
async for token in self.stream_reply(
|
|
history=None, user_input=timeline, learner_context=learner_context
|
|
):
|
|
yield token
|