okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffauffeat(api)!: migrate daemon and clients to protocol 361c43ca

main
2.9 KiB97 linesraw
1//// A refused retrieval call answers with an error payload the model can
2//// correct, instead of ending the turn: subagents died whole on
3//// "run failed: LCM node not found in this session" after handing a
4//// transcript row seq to an LCM node tool.
5
6import albedo/daemon/conversation
7import albedo/daemon/family
8import albedo/harness/extensions
9import albedo/harness/extensions/lcm/memory as lcm_memory
10import albedo/harness/runtime
11import albedo/openai_api/types
12import gleam/dynamic/decode
13import gleam/json
14import gleam/option.{None}
15import gleam/string
16import gleeunit/should
17
18fn host() -> #(runtime.Runtime, runtime.Session) {
19  let assert Ok(host) =
20    runtime.start_with_config(
21      ":memory:",
22      extensions.Config([lcm_memory.extension()], ["lcm-memory"]),
23    )
24  let ledger = runtime.ledger(host)
25  let assert Ok(_) = conversation.initialise(ledger)
26  let assert Ok(_) = family.initialise(ledger)
27  let assert Ok(_) =
28    conversation.create(
29      ledger,
30      conversation.Info(
31        "tool-error-test",
32        "new session",
33        "/tmp",
34        "provider",
35        "model",
36        types.Responses,
37        conversation.Idle,
38        None,
39        None,
40      ),
41    )
42  let assert Ok(session) = runtime.open_session(host, "tool-error-test", "/tmp")
43  #(host, session)
44}
45
46fn answer(
47  host: runtime.Runtime,
48  session: runtime.Session,
49  name: String,
50  arguments: String,
51) -> String {
52  // The regression is Ok against Error: a refused read must not fail the run.
53  let assert Ok(output) =
54    runtime.invoke(
55      host,
56      session,
57      types.ToolCall("call_1", name, arguments),
58      types.any_images,
59    )
60  let assert types.ToolOutput(_, body, []) = output
61  body
62}
63
64pub fn missing_lcm_node_answers_as_tool_output_test() -> Nil {
65  let #(host, session) = host()
66  // 35049 is a transcript row seq, the exact confusion that killed a child.
67  let body = answer(host, session, "lcm_describe", "{\"id\":35049}")
68  let assert Ok(reason) =
69    json.parse(body, decode.field("error", decode.string, decode.success))
70  string.contains(reason, "LCM node 35049 not found") |> should.be_true
71  string.contains(reason, "transcript_read") |> should.be_true
72  runtime.stop(host)
73}
74
75pub fn missing_summary_scope_answers_as_tool_output_test() -> Nil {
76  let #(host, session) = host()
77  let body =
78    answer(
79      host,
80      session,
81      "lcm_grep",
82      "{\"pattern\":\"needle\",\"summary_id\":7}",
83    )
84  let assert Ok(reason) =
85    json.parse(body, decode.field("error", decode.string, decode.success))
86  string.contains(reason, "LCM node 7 not found") |> should.be_true
87  runtime.stop(host)
88}
89
90pub fn answering_tools_still_answer_normally_test() -> Nil {
91  let #(host, session) = host()
92  let body = answer(host, session, "lcm_list", "{}")
93  let assert Ok(total) =
94    json.parse(body, decode.field("total", decode.int, decode.success))
95  total |> should.equal(0)
96  runtime.stop(host)
97}