Skip to content

Commit d08276b

Browse files
shanshan
authored andcommitted
docs: add runtime kernel platform demo
1 parent a445292 commit d08276b

2 files changed

Lines changed: 389 additions & 0 deletions

File tree

Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
"""17 - Runtime Kernel Platform.
2+
3+
A compact application-style demo showing how the runtime kernel coordinates
4+
the seven primary mechanisms introduced by the public 0.8.1 API:
5+
6+
1. Tool use
7+
2. Memory
8+
3. Skills
9+
4. Harness
10+
5. Gateway/orchestration
11+
6. Knowledge
12+
7. Cron
13+
14+
The demo loads provider keys from `.env` when present. Without a provider key,
15+
Loom falls back to its local summary path, so the composition remains runnable.
16+
17+
Run:
18+
python examples/17_runtime_kernel_platform.py
19+
20+
Optional:
21+
LOOM_DEMO_RUN_CRON=1 python examples/17_runtime_kernel_platform.py
22+
"""
23+
24+
from __future__ import annotations
25+
26+
import asyncio
27+
import hashlib
28+
import os
29+
from collections import Counter
30+
from dataclasses import dataclass, field
31+
from datetime import datetime, timedelta
32+
from pathlib import Path
33+
34+
from loom import (
35+
Agent,
36+
Capability,
37+
Generation,
38+
Harness,
39+
HarnessCandidate,
40+
HarnessOutcome,
41+
HarnessRequest,
42+
KnowledgeDocument,
43+
KnowledgeQuery,
44+
KnowledgeResolver,
45+
KnowledgeSource,
46+
MemoryConfig,
47+
MemoryExtractor,
48+
MemoryQuery,
49+
MemoryRecord,
50+
MemoryResolver,
51+
MemorySource,
52+
MemoryStore,
53+
Model,
54+
RunContext,
55+
Runtime,
56+
RuntimeTask,
57+
ScheduleConfig,
58+
ScheduledJob,
59+
SessionConfig,
60+
SignalAdapter,
61+
SkillInjection,
62+
tool,
63+
)
64+
from loom.config import KnowledgeEvidence, KnowledgeEvidenceItem
65+
66+
67+
def load_dotenv(path: str | Path = ".env") -> None:
68+
"""Small `.env` loader for examples without adding a dependency."""
69+
env_path = Path(path)
70+
if not env_path.exists():
71+
return
72+
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
73+
line = raw_line.strip()
74+
if not line or line.startswith("#") or "=" not in line:
75+
continue
76+
key, value = line.split("=", 1)
77+
key = key.strip()
78+
value = value.strip().strip('"').strip("'")
79+
os.environ.setdefault(key, value)
80+
81+
82+
def resolve_model():
83+
provider = os.getenv("LOOM_PROVIDER")
84+
if provider is None:
85+
if os.getenv("OPENAI_API_KEY"):
86+
provider = "openai"
87+
elif os.getenv("ANTHROPIC_API_KEY"):
88+
provider = "anthropic"
89+
elif os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY"):
90+
provider = "gemini"
91+
elif os.getenv("DASHSCOPE_API_KEY"):
92+
provider = "qwen"
93+
else:
94+
provider = "openai"
95+
96+
provider = provider.lower()
97+
model_name = os.getenv("LOOM_MODEL_NAME")
98+
if provider == "anthropic":
99+
return Model.anthropic(model_name or "claude-sonnet-4")
100+
if provider == "gemini":
101+
return Model.gemini(model_name or "gemini-2.5-pro")
102+
if provider == "qwen":
103+
return Model.qwen(model_name or "qwen-max")
104+
if provider == "ollama":
105+
return Model.ollama(model_name or "llama3", api_base=os.getenv("OLLAMA_BASE_URL"))
106+
return Model.openai(
107+
model_name or os.getenv("OPENAI_MODEL") or "gpt-4o",
108+
api_base=os.getenv("OPENAI_BASE_URL"),
109+
api_key_env="OPENAI_API_KEY",
110+
)
111+
112+
113+
@dataclass
114+
class InMemoryPreferenceStore(MemoryStore):
115+
"""Tiny custom memory store used to demonstrate the MemoryStore boundary."""
116+
117+
records: dict[str, MemoryRecord] = field(default_factory=dict)
118+
119+
def search(self, query: MemoryQuery) -> list[MemoryRecord]:
120+
query_terms = Counter(query.text.lower().split())
121+
ranked: list[MemoryRecord] = []
122+
for record in self.records.values():
123+
record_terms = Counter(record.content.lower().split())
124+
overlap = sum((query_terms & record_terms).values())
125+
ranked.append(
126+
MemoryRecord(
127+
key=record.key,
128+
content=record.content,
129+
score=float(overlap),
130+
metadata=dict(record.metadata),
131+
)
132+
)
133+
ranked.sort(key=lambda item: item.score or 0.0, reverse=True)
134+
return ranked[: query.top_k]
135+
136+
def upsert(self, record: MemoryRecord, query: MemoryQuery | None = None) -> None:
137+
key = record.key or hashlib.sha1(record.content.encode("utf-8")).hexdigest()[:12]
138+
metadata = dict(record.metadata)
139+
if query is not None and query.session_id:
140+
metadata.setdefault("session_id", query.session_id)
141+
self.records[key] = MemoryRecord(
142+
key=key,
143+
content=record.content,
144+
score=record.score,
145+
metadata=metadata,
146+
)
147+
148+
149+
def extract_memory(
150+
user_content: str,
151+
assistant_content: str,
152+
*,
153+
session_id: str | None = None,
154+
) -> list[MemoryRecord]:
155+
"""Extract durable preferences from completed turns."""
156+
_ = assistant_content, session_id
157+
marker = "remember:"
158+
lowered = user_content.lower()
159+
if marker not in lowered:
160+
return []
161+
start = lowered.index(marker) + len(marker)
162+
content = user_content[start:].strip()
163+
return [MemoryRecord(content=content, metadata={"kind": "explicit_preference"})] if content else []
164+
165+
166+
def build_knowledge_source() -> KnowledgeSource:
167+
"""Show both fixed documents and a user-defined resolver."""
168+
169+
def resolve_runbook(query: KnowledgeQuery) -> KnowledgeEvidence:
170+
source_name = query.source_names[0] if query.source_names else "ops-runbook"
171+
text = query.text.lower()
172+
items: list[KnowledgeEvidenceItem] = []
173+
if "incident" in text or "deploy" in text or "platform" in text:
174+
items.append(
175+
KnowledgeEvidenceItem(
176+
source_name=source_name,
177+
title="Incident Platform Runbook",
178+
content=(
179+
"For production incidents: assess blast radius, check service health, "
180+
"prepare a rollback path, and require release-owner approval before "
181+
"executing production changes."
182+
),
183+
uri="runbook://incident-platform",
184+
score=0.96,
185+
)
186+
)
187+
return KnowledgeEvidence(query=query, items=items, relevance_score=0.96 if items else 0.0)
188+
189+
return KnowledgeSource.dynamic(
190+
"ops-runbook",
191+
KnowledgeResolver.callable(resolve_runbook, description="Runtime ops runbook resolver"),
192+
description="Dynamic operational knowledge",
193+
)
194+
195+
196+
@tool(description="Read a service health snapshot", read_only=True)
197+
async def lookup_service_status(service: str) -> str:
198+
samples = {
199+
"checkout": "checkout is degraded: p95 latency is 2.4s and error rate is 3.1%",
200+
"payments": "payments is healthy: p95 latency is 180ms and error rate is 0.02%",
201+
}
202+
return samples.get(service, f"{service} status is unknown")
203+
204+
205+
@tool(description="Draft a bounded change plan", read_only=True)
206+
async def create_change_plan(service: str, risk: str) -> str:
207+
return (
208+
f"Change plan for {service}: run canary, watch {risk}, keep rollback ready, "
209+
"and request release-owner approval before production execution."
210+
)
211+
212+
213+
async def bounded_harness(request: HarnessRequest) -> HarnessOutcome:
214+
"""A user-defined harness that can call the underlying runtime once."""
215+
result = await request.run_once()
216+
output = result.get("output", str(result)) if isinstance(result, dict) else str(result)
217+
candidate = HarnessCandidate(
218+
id="single-runtime-pass",
219+
content=output,
220+
score=1.0,
221+
rationale="Selected the bounded runtime pass for this demo.",
222+
)
223+
return HarnessOutcome(
224+
output=output,
225+
passed=True,
226+
iterations=int(result.get("iterations", 1)) if isinstance(result, dict) else 1,
227+
candidates=[candidate],
228+
selected_candidate_id=candidate.id,
229+
metadata={"harness": "bounded_harness", "session_id": request.session_id},
230+
)
231+
232+
233+
def build_agent(memory_store: InMemoryPreferenceStore) -> Agent:
234+
memory_store.upsert(
235+
MemoryRecord(
236+
key="deploy-style",
237+
content="The platform team prefers canary deploys with explicit rollback notes.",
238+
)
239+
)
240+
memory = MemoryConfig(
241+
sources=[
242+
MemorySource.long_term(
243+
"team-preferences",
244+
resolver=MemoryResolver.from_store(memory_store),
245+
extractor=MemoryExtractor.callable(extract_memory),
246+
store=memory_store,
247+
instructions="Use recalled team preferences when planning operational work.",
248+
top_k=3,
249+
)
250+
]
251+
)
252+
253+
runtime = Runtime.orchestrated(
254+
max_depth=2,
255+
planner=True,
256+
max_iterations=24,
257+
harness=Harness.custom(bounded_harness, name="bounded_harness"),
258+
skill_injection=SkillInjection.matching(max_skills=2, max_tokens=1200),
259+
)
260+
261+
return Agent(
262+
model=resolve_model(),
263+
generation=Generation(max_output_tokens=700),
264+
instructions=(
265+
"You are an operations agent platform. Use tools for live facts, "
266+
"knowledge for policy, memory for team preferences, and keep actions bounded."
267+
),
268+
tools=[lookup_service_status, create_change_plan],
269+
capabilities=[
270+
Capability.files(read_only=True),
271+
Capability.skill(
272+
"incident-platform",
273+
description="Operational incident planning skill",
274+
content=(
275+
"When handling incident or deploy work, produce: situation, evidence, "
276+
"risk, plan, rollback, and approval status."
277+
),
278+
when_to_use="incident, deploy, platform, rollback",
279+
allowed_tools=["lookup_service_status", "create_change_plan"],
280+
),
281+
],
282+
knowledge=[
283+
KnowledgeSource.inline(
284+
"faq",
285+
[
286+
KnowledgeDocument(
287+
title="Approval FAQ",
288+
content="Production changes require release-owner approval.",
289+
)
290+
],
291+
),
292+
build_knowledge_source(),
293+
],
294+
memory=memory,
295+
runtime=runtime,
296+
schedule=[
297+
ScheduledJob(
298+
id="daily-health",
299+
name="Daily health digest",
300+
prompt="Summarize checkout and payments health for the operations channel.",
301+
schedule=ScheduleConfig.interval(hours=24),
302+
metadata={"channel": "ops"},
303+
)
304+
],
305+
)
306+
307+
308+
async def main() -> None:
309+
load_dotenv()
310+
memory_store = InMemoryPreferenceStore()
311+
agent = build_agent(memory_store)
312+
session = agent.session(SessionConfig(id="platform-demo", metadata={"app": "ops-platform"}))
313+
314+
gateway = SignalAdapter(
315+
source="gateway:slack",
316+
type="message",
317+
summary=lambda event: event["text"],
318+
payload=lambda event: {"channel": event["channel"]},
319+
dedupe_key=lambda event: event["event_id"],
320+
)
321+
gateway_decision = await session.receive(
322+
{
323+
"event_id": "evt-checkout-1",
324+
"channel": "ops",
325+
"text": "Checkout latency is rising; prepare an incident response.",
326+
},
327+
adapter=gateway,
328+
urgency="high",
329+
)
330+
331+
task = RuntimeTask(
332+
goal=(
333+
"Remember: checkout incident responses must include owner approval. "
334+
"Build a response plan for the checkout incident and cite the runtime signals."
335+
),
336+
input={"service": "checkout", "risk": "latency regression"},
337+
criteria=["grounded", "bounded", "approval-aware"],
338+
metadata={"skills": ["incident-platform"]},
339+
)
340+
result = await session.run(
341+
task,
342+
context=RunContext(
343+
inputs={
344+
"platform_surface": "ops dashboard",
345+
"expected_sections": ["situation", "evidence", "plan", "rollback"],
346+
}
347+
),
348+
)
349+
350+
print("=== Runtime Kernel Platform Demo ===")
351+
print(f"Model: {agent.config.model.identifier}")
352+
print(f"Runtime profile: {agent.config.runtime.describe()['profile']}")
353+
print(f"Gateway decision: {gateway_decision.action}")
354+
print(f"Scheduled jobs: {len(agent.config.schedule)}")
355+
print(f"Memory records: {len(memory_store.records)}")
356+
print(f"Run state: {result.state.value}")
357+
print(f"Output: {result.output}")
358+
359+
recall = agent.config.memory.sources[0].prefetch("checkout deploy approval", session_id=session.id)
360+
print("\n=== Recalled Memory ===")
361+
print(recall or "(empty)")
362+
363+
print("\n=== Mechanism Map ===")
364+
print("Tool use: lookup_service_status, create_change_plan")
365+
print("Memory: MemorySource + custom MemoryStore/Extractor")
366+
print("Skills: Capability.skill + SkillInjection")
367+
print("Harness: Harness.custom(bounded_harness)")
368+
print("Gateway/orch: SignalAdapter + Runtime.orchestrated(max_depth=2)")
369+
print("Knowledge: inline FAQ + custom KnowledgeResolver")
370+
print("Cron: ScheduledJob + ScheduleConfig.interval")
371+
372+
if os.getenv("LOOM_DEMO_RUN_CRON") == "1":
373+
agent.once(
374+
datetime.now() + timedelta(seconds=1),
375+
id="demo-once",
376+
prompt="Acknowledge the scheduled health check in one sentence.",
377+
name="Demo one-shot cron",
378+
)
379+
ticker = agent.start_scheduler(interval_seconds=0.25)
380+
await asyncio.sleep(1.5)
381+
agent.stop_scheduler()
382+
print(f"\nCron ticker stopped: {not ticker.running}")
383+
384+
385+
if __name__ == "__main__":
386+
asyncio.run(main())

0 commit comments

Comments
 (0)