From 4c6a831aa2a57e2bcc20aa3fc2f1b7172f5be285 Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Mon, 29 Jun 2026 02:36:40 +0900 Subject: feat: 会話メモリのプロトタイプを実装 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 6種類のMCPツールと入力検証を追加 - バイナリログ、スナップショット、復旧、圧縮処理を実装 - Unicode n-gram検索とドメインモデルを追加 - 単体テストと統合テストを追加 - アーキテクチャと実装状況を文書化 - Cコードの記述スタイルを統一 --- src/domain/message.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 src/domain/message.c (limited to 'src/domain/message.c') diff --git a/src/domain/message.c b/src/domain/message.c new file mode 100644 index 0000000..d973be4 --- /dev/null +++ b/src/domain/message.c @@ -0,0 +1,255 @@ +#include "domain/message.h" + +#include +#include + +static char * +duplicate_string(const char *value) +{ + const size_t length = strlen(value); + char *copy = malloc(length + 1); + if (copy != NULL) + { + memcpy(copy, value, length + 1); + } + return copy; +} + +void +tiny_message_init(tiny_message_t *message) +{ + memset(message, 0, sizeof(*message)); + message->metadata.memory_tier = TINY_TIER_SHORT; +} + +void +tiny_message_free(tiny_message_t *message) +{ + size_t index; + free(message->session_id); + free(message->content); + free(message->metadata.tool_call_id); + for (index = 0; index < message->metadata.tag_count; ++index) + { + free(message->metadata.tags[index]); + } + free(message->metadata.tags); + tiny_message_init(message); +} + +int +tiny_message_copy(tiny_message_t *destination, const tiny_message_t *source) +{ + size_t index; + tiny_message_init(destination); + memcpy(destination->message_id, source->message_id, sizeof(destination->message_id)); + destination->timestamp_ms = source->timestamp_ms; + destination->role = source->role; + destination->metadata.memory_tier = source->metadata.memory_tier; + destination->session_id = duplicate_string(source->session_id); + destination->content = duplicate_string(source->content); + if (destination->session_id == NULL || destination->content == NULL) + { + goto failure; + } + if (source->metadata.tool_call_id != NULL) + { + destination->metadata.tool_call_id = duplicate_string(source->metadata.tool_call_id); + if (destination->metadata.tool_call_id == NULL) + { + goto failure; + } + } + if (source->metadata.tag_count > 0) + { + destination->metadata.tags = calloc(source->metadata.tag_count, sizeof(char *)); + if (destination->metadata.tags == NULL) + { + goto failure; + } + for (index = 0; index < source->metadata.tag_count; ++index) + { + destination->metadata.tags[index] = duplicate_string(source->metadata.tags[index]); + if (destination->metadata.tags[index] == NULL) + { + goto failure; + } + destination->metadata.tag_count++; + } + } + return 0; + +failure: + tiny_message_free(destination); + return -1; +} + +const char * +tiny_role_name(tiny_role_t role) +{ + switch (role) + { + case TINY_ROLE_USER: + return "user"; + case TINY_ROLE_ASSISTANT: + return "assistant"; + case TINY_ROLE_SYSTEM: + return "system"; + default: + return "unknown"; + } +} + +bool +tiny_role_parse(const char *value, tiny_role_t *role) +{ + if (strcmp(value, "user") == 0) + { + *role = TINY_ROLE_USER; + } + else if (strcmp(value, "assistant") == 0) + { + *role = TINY_ROLE_ASSISTANT; + } + else if (strcmp(value, "system") == 0) + { + *role = TINY_ROLE_SYSTEM; + } + else + { + return false; + } + return true; +} + +const char * +tiny_tier_name(tiny_memory_tier_t tier) +{ + switch (tier) + { + case TINY_TIER_SHORT: + return "short"; + case TINY_TIER_MID: + return "mid"; + case TINY_TIER_LONG: + return "long"; + case TINY_TIER_DISCARD: + return "discard"; + default: + return "unknown"; + } +} + +bool +tiny_tier_parse(const char *value, tiny_memory_tier_t *tier) +{ + if (strcmp(value, "short") == 0) + { + *tier = TINY_TIER_SHORT; + } + else if (strcmp(value, "mid") == 0) + { + *tier = TINY_TIER_MID; + } + else if (strcmp(value, "long") == 0) + { + *tier = TINY_TIER_LONG; + } + else if (strcmp(value, "discard") == 0) + { + *tier = TINY_TIER_DISCARD; + } + else + { + return false; + } + return true; +} + +bool +tiny_utf8_valid(const char *value, size_t length) +{ + size_t index = 0; + while (index < length) + { + const unsigned char first = (unsigned char)value[index++]; + unsigned needed; + uint32_t codepoint; + if (first < 0x80) + { + if (first == 0) + { + return false; + } + continue; + } + if ((first & 0xe0) == 0xc0) + { + needed = 1; + codepoint = first & 0x1f; + } + else if ((first & 0xf0) == 0xe0) + { + needed = 2; + codepoint = first & 0x0f; + } + else if ((first & 0xf8) == 0xf0) + { + needed = 3; + codepoint = first & 0x07; + } + else + { + return false; + } + if (index + needed > length) + { + return false; + } + while (needed-- > 0) + { + const unsigned char next = (unsigned char)value[index++]; + if ((next & 0xc0) != 0x80) + { + return false; + } + codepoint = (codepoint << 6) | (next & 0x3f); + } + if (codepoint > 0x10ffff || (codepoint >= 0xd800 && codepoint <= 0xdfff)) + { + return false; + } + if ((first & 0xe0) == 0xc0 && codepoint < 0x80) + { + return false; + } + if ((first & 0xf0) == 0xe0 && codepoint < 0x800) + { + return false; + } + if ((first & 0xf8) == 0xf0 && codepoint < 0x10000) + { + return false; + } + } + return true; +} + +bool +tiny_message_id_valid(const char *value) +{ + static const char alphabet[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; + size_t index; + if (strlen(value) != TINY_MESSAGE_ID_LENGTH || value[0] > '7') + { + return false; + } + for (index = 0; index < TINY_MESSAGE_ID_LENGTH; ++index) + { + if (strchr(alphabet, value[index]) == NULL) + { + return false; + } + } + return true; +} -- cgit v1.2.3