-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhost_control.py
More file actions
93 lines (81 loc) · 2.95 KB
/
Copy pathhost_control.py
File metadata and controls
93 lines (81 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python3
"""Drive the Rust kernel one step at a time, with no key or network.
Run after installing the Python binding:
python bindings/python/examples/host_control.py
"""
from pathlib import Path
from tempfile import TemporaryDirectory
import kerness
GAMEPLAN = """---
name: host-control
agents:
orchestrator: false
participants: {min: 1, max: 1}
loop:
max_turns: 4
max_rounds: 2
terminate_on: [DONE]
result:
summary: {type: str}
---
Answer the host's request.
"""
class Scripted(kerness.Provider):
def __init__(self):
super().__init__(retries=0, backoff_sec=0)
self.calls = 0
def chat(self, model, messages):
self.calls += 1
if self.calls != 1:
raise RuntimeError("Unexpected extra provider call")
return kerness.ProviderResponse(
content="Write-through keeps the invalidation rules simple.", model=model,
)
def main():
with TemporaryDirectory(prefix="kerness-host-control-") as directory:
workspace = Path(directory)
gameplan = workspace / "gameplan.md"
gameplan.write_text(GAMEPLAN, encoding="utf-8")
provider = Scripted()
session = kerness.Session(
gameplan=str(gameplan), topic="Which cache policy should we use?",
provider=provider, model="offline-model", turn_delay_sec=0,
memory=str(workspace / "memory.md"),
access_policy=kerness.AccessPolicy(workspace=workspace),
)
session.add_agent("Advisor")
run = session.start(
mode="host_driven", budget={"max_provider_operations": 1},
event_sink=lambda event: print(
f"event {event['sequence']}: {event['event']['kind']}"
),
)
# start consumes session. Only the independent run handle is used now.
asked = False
while True:
step = run.step()
if step["status"] == "progress":
continue
if step["status"] != "waiting" or step["reason"]["kind"] != "input":
raise RuntimeError(f"Unexpected step: {step}")
if not asked:
asked = True
run.step({
"kind": "select_agent", "agent": "Advisor",
"instruction": "Recommend one cache policy.",
})
continue
# Rust validates this shape. Finish makes no provider/judge call.
finished = run.step({
"kind": "finish", "result": {"summary": "Use write-through caching."},
})
assert finished["status"] == "finished"
outcome = finished["outcome"]
assert outcome["reason"]["kind"] == "completed"
assert outcome["diagnostics"]["valid"]
assert provider.calls == 1
print("Result:", outcome["result"]["fields"]["summary"])
print("Usage:", outcome["usage"])
break
if __name__ == "__main__":
main()