Files
tai/taicpp/tests/llm_history_test.cpp
T

65 lines
2.4 KiB
C++
Raw Normal View History

// clean_history must never hand a provider a tool result whose tool call was not issued: devplace answers
// that with HTTP 400 "No function call found for function call output with call_id ..." and the whole turn
// fails (everythingwrong.md; reproduced against the live endpoint with an orphan result).
#include <cstdio>
#include "helpers.h"
#include "json.h"
using detz::json::Value;
static int g_fail = 0;
#define CHECK(cond) do { if (!(cond)) { g_fail++; std::printf("FAIL %s:%d %s\n", __FILE__, __LINE__, #cond); } } while (0)
static Value msg(const char* role, const char* content) {
Value m = Value::object();
m.set("role", role);
m.set("content", content);
return m;
}
static Value assistant_calling(const char* id) {
Value fn = Value::object();
fn.set("name", "fork");
fn.set("arguments", "{}");
Value call = Value::object();
call.set("id", id);
call.set("type", "function");
call.set("function", fn);
Value calls = Value::array();
calls.push(call);
Value m = Value::object();
m.set("role", "assistant");
m.set("content", Value());
m.set("tool_calls", calls);
return m;
}
static Value tool_result(const char* id) {
Value m = msg("tool", "done");
m.set("tool_call_id", id);
return m;
}
int main() {
// a window that starts on a tool result (compaction cut its assistant message away)
Value cut = Value::array();
cut.push(msg("system", "s"));
cut.push(tool_result("call_orphan"));
cut.push(msg("user", "go"));
cut.push(assistant_calling("call_a"));
cut.push(tool_result("call_a"));
Value cleaned = detz::llm::drop_orphan_tool_results(detz::llm::clean_history(cut));
CHECK(cleaned.size() == 4);
bool orphan_left = false;
for (const auto& m : cleaned.items()) {
if (m.get("role").str() == "tool" && m.get("tool_call_id").str() == "call_orphan") orphan_left = true;
}
CHECK(!orphan_left);
// a healthy exchange is untouched
Value ok = Value::array();
ok.push(msg("user", "go"));
ok.push(assistant_calling("call_b"));
ok.push(tool_result("call_b"));
CHECK(detz::llm::drop_orphan_tool_results(detz::llm::clean_history(ok)).size() == 3);
// clean_history itself stays byte-for-byte tai.py behaviour: it keeps the orphan (agent_diff.py compares it)
CHECK(detz::llm::clean_history(cut).size() == 5);
std::printf(g_fail ? "llm_history_test: %d FAILED\n" : "llm_history_test: ok\n", g_fail);
return g_fail ? 1 : 0;
}