178 lines
5.9 KiB
C
178 lines
5.9 KiB
C
/*
|
|
* agent_chat_store.c — thin wrapper over db.c for agent chat sessions
|
|
*
|
|
* Manages the "current session" concept and provides convenience
|
|
* functions for adding messages and loading history in OpenAI format.
|
|
*/
|
|
|
|
#include "agent_chat_store.h"
|
|
#include "db.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* ── Internal state ────────────────────────────────────────────────── */
|
|
|
|
/* Owned by the store; freed on replacement or shutdown. */
|
|
static char *g_current_session_id = NULL;
|
|
|
|
/* ── Helpers ───────────────────────────────────────────────────────── */
|
|
|
|
/*
|
|
* Replace g_current_session_id with a newly-allocated copy of `id`.
|
|
* Frees the previous value. Takes ownership of nothing (copies `id`).
|
|
* Returns a pointer to the stored string (owned by the store).
|
|
*/
|
|
static const char *store_session_id(const char *id) {
|
|
if (id == NULL) return NULL;
|
|
if (g_current_session_id) {
|
|
g_free(g_current_session_id);
|
|
}
|
|
g_current_session_id = g_strdup(id);
|
|
return g_current_session_id;
|
|
}
|
|
|
|
/* ── Public API ────────────────────────────────────────────────────── */
|
|
|
|
const char *agent_chat_store_get_session(void) {
|
|
if (g_current_session_id) {
|
|
return g_current_session_id;
|
|
}
|
|
|
|
/* Try to load the latest existing session. */
|
|
char *latest = db_agent_session_get_latest();
|
|
if (latest) {
|
|
const char *stored = store_session_id(latest);
|
|
g_free(latest);
|
|
return stored;
|
|
}
|
|
|
|
/* No sessions exist — create a new one. */
|
|
char *id = db_agent_session_create(NULL);
|
|
if (id == NULL) {
|
|
g_printerr("[agent_chat_store] failed to create new session\n");
|
|
return NULL;
|
|
}
|
|
const char *stored = store_session_id(id);
|
|
g_free(id);
|
|
return stored;
|
|
}
|
|
|
|
const char *agent_chat_store_new_session(const char *title) {
|
|
char *id = db_agent_session_create(title);
|
|
if (id == NULL) {
|
|
g_printerr("[agent_chat_store] failed to create new session\n");
|
|
return NULL;
|
|
}
|
|
const char *stored = store_session_id(id);
|
|
g_free(id);
|
|
return stored;
|
|
}
|
|
|
|
const char *agent_chat_store_set_session(const char *session_id) {
|
|
if (session_id == NULL) return NULL;
|
|
return store_session_id(session_id);
|
|
}
|
|
|
|
int agent_chat_store_add_user_message(const char *content) {
|
|
const char *sid = agent_chat_store_get_session();
|
|
if (sid == NULL) return -1;
|
|
if (content == NULL) return -1;
|
|
|
|
int row = db_agent_message_add(sid, "user", content, NULL, NULL);
|
|
if (row < 0) {
|
|
g_printerr("[agent_chat_store] failed to add user message\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int agent_chat_store_add_assistant_message(const char *content,
|
|
const char *tool_calls_json) {
|
|
const char *sid = agent_chat_store_get_session();
|
|
if (sid == NULL) return -1;
|
|
|
|
int row = db_agent_message_add(sid, "assistant", content,
|
|
tool_calls_json, NULL);
|
|
if (row < 0) {
|
|
g_printerr("[agent_chat_store] failed to add assistant message\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int agent_chat_store_add_tool_result(const char *tool_call_id,
|
|
const char *result_json) {
|
|
const char *sid = agent_chat_store_get_session();
|
|
if (sid == NULL) return -1;
|
|
if (tool_call_id == NULL || result_json == NULL) return -1;
|
|
|
|
int row = db_agent_message_add(sid, "tool", result_json, NULL,
|
|
tool_call_id);
|
|
if (row < 0) {
|
|
g_printerr("[agent_chat_store] failed to add tool result\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
cJSON *agent_chat_store_get_messages(void) {
|
|
const char *sid = agent_chat_store_get_session();
|
|
if (sid == NULL) return NULL;
|
|
|
|
cJSON *raw = db_agent_message_list(sid);
|
|
if (raw == NULL) {
|
|
g_printerr("[agent_chat_store] failed to load messages\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Convert to OpenAI-format array. */
|
|
cJSON *out = cJSON_CreateArray();
|
|
cJSON *msg;
|
|
cJSON_ArrayForEach(msg, raw) {
|
|
cJSON *role_item = cJSON_GetObjectItemCaseSensitive(msg, "role");
|
|
const char *role = (cJSON_IsString(role_item))
|
|
? role_item->valuestring : "";
|
|
|
|
cJSON *content_item = cJSON_GetObjectItemCaseSensitive(msg, "content");
|
|
cJSON *tool_calls_item = cJSON_GetObjectItemCaseSensitive(msg, "tool_calls");
|
|
cJSON *tool_call_id_item = cJSON_GetObjectItemCaseSensitive(msg, "tool_call_id");
|
|
|
|
cJSON *obj = cJSON_CreateObject();
|
|
cJSON_AddStringToObject(obj, "role", role);
|
|
|
|
/* content: include only if non-null. */
|
|
if (cJSON_IsString(content_item)) {
|
|
cJSON_AddStringToObject(obj, "content", content_item->valuestring);
|
|
}
|
|
|
|
/* tool_calls: parse the stored JSON string into an array object. */
|
|
if (cJSON_IsString(tool_calls_item) &&
|
|
tool_calls_item->valuestring[0] != '\0') {
|
|
cJSON *parsed = cJSON_Parse(tool_calls_item->valuestring);
|
|
if (parsed) {
|
|
cJSON_AddItemToObject(obj, "tool_calls", parsed);
|
|
} else {
|
|
/* Store the raw string if it isn't valid JSON. */
|
|
cJSON_AddStringToObject(obj, "tool_calls",
|
|
tool_calls_item->valuestring);
|
|
}
|
|
}
|
|
|
|
/* tool_call_id: for role="tool". */
|
|
if (cJSON_IsString(tool_call_id_item)) {
|
|
cJSON_AddStringToObject(obj, "tool_call_id",
|
|
tool_call_id_item->valuestring);
|
|
}
|
|
|
|
cJSON_AddItemToArray(out, obj);
|
|
}
|
|
|
|
cJSON_Delete(raw);
|
|
return out;
|
|
}
|
|
|
|
const char *agent_chat_store_session_id(void) {
|
|
return g_current_session_id;
|
|
}
|