commit - c07b6527d8b68986949d7889496b04f6756cbba1
commit + c5f3a1cbb149227b342688a63cb3375c4be6c547
blob - f749f8833bcf6f021552da601c88d996ac62cf97
blob + 4c5a83cb6030162b0b0e91b0acf055b0acbfa2e6
--- regress/anthropic/anthropic_test.c
+++ regress/anthropic/anthropic_test.c
}
static void
+test_build_suffix(void)
+{
+ struct msglist conv;
+ struct msg *base, *suffix;
+ struct anthropic_req r;
+ struct buf out;
+ struct json j;
+ char *s;
+ size_t sl;
+ int messages, message, content, block;
+
+ TAILQ_INIT(&conv);
+ base = msg_new(ROLE_USER);
+ msg_add_text(base, "base Projection", 15);
+ TAILQ_INSERT_TAIL(&conv, base, entry);
+ suffix = msg_new(ROLE_USER);
+ msg_add_text(suffix, "Write continuation notes to yourself.", 37);
+
+ memset(&r, 0, sizeof(r));
+ r.model = "m";
+ r.max_tokens = 10;
+ r.conv = &conv;
+ r.suffix = suffix;
+ r.cache = 1;
+ r.cache_tail_msg = 0;
+
+ buf_init(&out);
+ anthropic_build_request(&out, &r);
+ CHECK(json_parse(&j, out.data, out.len, 0) == 0);
+ messages = json_obj_get(&j, 0, "messages");
+ CHECK(json_arr_len(&j, messages) == 2);
+ message = json_arr_get(&j, messages, 1);
+ s = json_get_str(&j, json_obj_get(&j, message, "role"), &sl);
+ CHECK(s != NULL && sl == 4 && memcmp(s, "user", 4) == 0);
+ free(s);
+ content = json_obj_get(&j, message, "content");
+ CHECK(json_arr_len(&j, content) == 1);
+ block = json_arr_get(&j, content, 0);
+ s = json_get_str(&j, json_obj_get(&j, block, "text"), &sl);
+ CHECK(s != NULL && sl == 37 &&
+ memcmp(s, "Write continuation notes to yourself.", 37) == 0);
+ free(s);
+
+ /* The borrowed suffix is outside the base Projection cache index. */
+ CHECK(count_cache(&out) == 1);
+ message = json_arr_get(&j, messages, 0);
+ content = json_obj_get(&j, message, "content");
+ block = json_arr_get(&j, content, 0);
+ CHECK(json_obj_get(&j, block, "cache_control") != -1);
+ message = json_arr_get(&j, messages, 1);
+ content = json_obj_get(&j, message, "content");
+ block = json_arr_get(&j, content, 0);
+ CHECK(json_obj_get(&j, block, "cache_control") == -1);
+
+ /* The base Projection is unchanged and the borrowed suffix stays unlinked. */
+ CHECK(TAILQ_FIRST(&conv) == base);
+ CHECK(TAILQ_LAST(&conv, msglist) == base);
+ CHECK(TAILQ_NEXT(base, entry) == NULL);
+ CHECK(TAILQ_NEXT(suffix, entry) == NULL);
+ json_done(&j);
+ buf_free(&out);
+ msg_free(suffix);
+ msglist_free(&conv);
+}
+
+static void
test_build_openai_none(void)
{
/* the openai builder emits no cache fields; asserted in that
test_build_minimal();
test_build_full();
+ test_build_suffix();
test_build_openai_none();
test_build_first_request();
test_stream_whole();
blob - f6f1ca66e9305300295bc088651d172eb3e22379
blob + 65b444907cd4cf2102e48e4976abf77c9d5f71e9
--- regress/openai/openai_test.c
+++ regress/openai/openai_test.c
msglist_free(&conv);
}
+static void
+test_build_suffix(void)
+{
+ struct msglist conv;
+ struct msg *base, *suffix;
+ struct openai_req req;
+ struct buf out;
+ struct json j;
+ int messages, message;
+
+ TAILQ_INIT(&conv);
+ base = msg_new(ROLE_USER);
+ msg_add_text(base, "base Projection", 15);
+ TAILQ_INSERT_TAIL(&conv, base, entry);
+ suffix = msg_new(ROLE_USER);
+ msg_add_text(suffix, "Write continuation notes to yourself.", 37);
+
+ memset(&req, 0, sizeof(req));
+ req.model = "m";
+ req.max_tokens = 10;
+ req.conv = &conv;
+ req.suffix = suffix;
+
+ buf_init(&out);
+ openai_build_request(&out, &req);
+ CHECK(json_parse(&j, out.data, out.len, 0) == 0);
+ messages = json_obj_get(&j, 0, "messages");
+ CHECK(json_arr_len(&j, messages) == 2);
+ message = json_arr_get(&j, messages, 1);
+ CHECK(str_is(&j, json_obj_get(&j, message, "role"), "user"));
+ CHECK(str_is(&j, json_obj_get(&j, message, "content"),
+ "Write continuation notes to yourself."));
+ CHECK(!has(&out, "cache_control"));
+
+ /* The codec borrows the suffix without changing the base Projection. */
+ CHECK(TAILQ_FIRST(&conv) == base);
+ CHECK(TAILQ_LAST(&conv, msglist) == base);
+ CHECK(TAILQ_NEXT(base, entry) == NULL);
+ CHECK(TAILQ_NEXT(suffix, entry) == NULL);
+ json_done(&j);
+ buf_free(&out);
+ msg_free(suffix);
+ msglist_free(&conv);
+}
+
struct call_capture {
char id[64];
char name[64];
test_build_full();
test_build_tool_only();
+ test_build_suffix();
test_stream_whole();
test_stream_bytewise();
test_stream_error();
blob - 7c775a25ed45a86ada7b15e566e20bb88970738e
blob + e2298154da85b6f0253d974cee2c206bfb2fe2a3
--- src/common/anthropic.h
+++ src/common/anthropic.h
const struct tool_def *tools;
int ntools;
const struct msglist *conv;
+ /* Optional borrowed message serialized after conv without linking it.
+ * cache_tail_msg indexes conv only; the suffix is never cache-marked. */
+ const struct msg *suffix;
int cache; /* emit cache_control */
/*
* Index (0-based, into conv) of the newest message that was
blob - 2f954b21ca2d56abbf5f7951d5b7a522182ccfaa
blob + 47f9f35a570a4f5df2633c598e952a108c76eec7
--- src/common/anthropic_req.c
+++ src/common/anthropic_req.c
}
}
+static void
+emit_message(struct json_out *jo, const struct msg *m, int cache)
+{
+ struct block *b, *last;
+
+ last = TAILQ_LAST(&m->blocks, blocklist);
+ json_obj_begin(jo);
+ json_key(jo, "role");
+ json_cstr(jo, m->role == ROLE_USER ? "user" : "assistant");
+ json_key(jo, "content");
+ json_arr_begin(jo);
+ TAILQ_FOREACH(b, &m->blocks, entry)
+ emit_block(jo, b, cache && b == last);
+ json_arr_end(jo);
+ json_obj_end(jo);
+}
+
void
anthropic_build_request(struct buf *out, const struct anthropic_req *r)
{
json_arr_begin(&jo);
mi = 0;
TAILQ_FOREACH(m, r->conv, entry) {
- struct block *b, *last;
int mark;
/* the advancing tail breakpoint lands on this message's
* last block (behavior.md section 8) */
mark = r->cache && mi == r->cache_tail_msg;
- last = TAILQ_LAST(&m->blocks, blocklist);
-
- json_obj_begin(&jo);
- json_key(&jo, "role");
- json_cstr(&jo, m->role == ROLE_USER ? "user" : "assistant");
- json_key(&jo, "content");
- json_arr_begin(&jo);
- TAILQ_FOREACH(b, &m->blocks, entry)
- emit_block(&jo, b, mark && b == last);
- json_arr_end(&jo);
- json_obj_end(&jo);
+ emit_message(&jo, m, mark);
mi++;
}
+ if (r->suffix != NULL)
+ emit_message(&jo, r->suffix, 0);
json_arr_end(&jo);
json_obj_end(&jo);
blob - 4edb100bf069dda4e2490ed37a33893f62839270
blob + 668ad1f4bed9525859a8b9e5f17f69ceae7eb77c
--- src/common/openai.h
+++ src/common/openai.h
const struct tool_def *tools;
int ntools;
const struct msglist *conv;
+ /* Optional borrowed message serialized after conv without linking it. */
+ const struct msg *suffix;
};
void openai_build_request(struct buf *, const struct openai_req *);
blob - 90060aaf7b0325096a2055d8f40560fab152a26a
blob + e7271a034999b2a46d1c69ed74315833ce04260d
--- src/common/openai_req.c
+++ src/common/openai_req.c
buf_free(&text);
}
+static void
+emit_message(struct json_out *jo, const struct msg *m)
+{
+ if (m->role == ROLE_ASSISTANT)
+ emit_assistant(jo, m);
+ else
+ emit_user(jo, m);
+}
+
void
openai_build_request(struct buf *out, const struct openai_req *r)
{
json_arr_begin(&jo);
if (r->system != NULL)
emit_text_message(&jo, "system", r->system, r->systemlen);
- TAILQ_FOREACH(m, r->conv, entry) {
- if (m->role == ROLE_ASSISTANT)
- emit_assistant(&jo, m);
- else
- emit_user(&jo, m);
- }
+ TAILQ_FOREACH(m, r->conv, entry)
+ emit_message(&jo, m);
+ if (r->suffix != NULL)
+ emit_message(&jo, r->suffix);
json_arr_end(&jo);
json_obj_end(&jo);
}