summaryrefslogtreecommitdiff
path: root/src/domain
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-07-04 14:54:45 +0900
committertanaka takashi <isazaka@gmail.com>2026-07-04 14:54:45 +0900
commit1efecaef1f867cb218366e2b4ab4b901fdd2835f (patch)
tree3080a3d80a1a0671e8e82819d3f785465aace162 /src/domain
parent34e00404e9561dcaf8d2d99df7470757a4fee915 (diff)
途中経過コミット
Diffstat (limited to 'src/domain')
-rw-r--r--src/domain/message.c176
-rw-r--r--src/domain/message.h52
-rw-r--r--src/domain/search.c26
3 files changed, 252 insertions, 2 deletions
diff --git a/src/domain/message.c b/src/domain/message.c
index b1db3e1..ec019c4 100644
--- a/src/domain/message.c
+++ b/src/domain/message.c
@@ -33,6 +33,9 @@ tiny_message_init(tiny_message_t *message)
}
memset(message, 0, sizeof(*message));
message->metadata.memory_tier = TINY_TIER_SHORT;
+ message->metadata.memory_kind = TINY_KIND_CONVERSATION;
+ message->metadata.importance = 50;
+ message->revision = 1;
}
void
@@ -46,11 +49,18 @@ tiny_message_free(tiny_message_t *message)
free(message->session_id);
free(message->content);
free(message->metadata.tool_call_id);
+ free(message->metadata.summary);
for (index = 0; index < message->metadata.tag_count; ++index)
{
free(message->metadata.tags[index]);
}
free(message->metadata.tags);
+ free(message->metadata.relations);
+ for (index = 0; index < message->metadata.source_count; ++index)
+ {
+ free(message->metadata.source_message_ids[index]);
+ }
+ free(message->metadata.source_message_ids);
tiny_message_init(message);
}
@@ -59,8 +69,7 @@ tiny_message_copy(tiny_message_t *destination, const tiny_message_t *source)
{
size_t index;
if (destination == NULL || source == NULL || destination == source || source->session_id == NULL
- || source->content == NULL
- || (source->metadata.tag_count != 0 && source->metadata.tags == NULL))
+ || source->content == NULL || !tiny_metadata_valid(&source->metadata))
{
return -1;
}
@@ -69,12 +78,56 @@ tiny_message_copy(tiny_message_t *destination, const tiny_message_t *source)
destination->timestamp_ms = source->timestamp_ms;
destination->role = source->role;
destination->metadata.memory_tier = source->metadata.memory_tier;
+ destination->metadata.memory_kind = source->metadata.memory_kind;
+ destination->metadata.importance = source->metadata.importance;
+ destination->revision = source->revision;
+ destination->updated_at_ms = source->updated_at_ms;
destination->session_id = duplicate_string(source->session_id);
destination->content = duplicate_string(source->content);
if (destination->session_id == NULL || destination->content == NULL)
{
return copy_failed(destination);
}
+ if (source->metadata.summary != NULL)
+ {
+ destination->metadata.summary = duplicate_string(source->metadata.summary);
+ if (destination->metadata.summary == NULL)
+ {
+ return copy_failed(destination);
+ }
+ }
+ if (source->metadata.relation_count > 0)
+ {
+ destination->metadata.relations
+ = malloc(source->metadata.relation_count * sizeof(*destination->metadata.relations));
+ if (destination->metadata.relations == NULL)
+ {
+ return copy_failed(destination);
+ }
+ memcpy(destination->metadata.relations,
+ source->metadata.relations,
+ source->metadata.relation_count * sizeof(*destination->metadata.relations));
+ destination->metadata.relation_count = source->metadata.relation_count;
+ }
+ if (source->metadata.source_count > 0)
+ {
+ destination->metadata.source_message_ids
+ = calloc(source->metadata.source_count, sizeof(char *));
+ if (destination->metadata.source_message_ids == NULL)
+ {
+ return copy_failed(destination);
+ }
+ for (index = 0; index < source->metadata.source_count; ++index)
+ {
+ destination->metadata.source_message_ids[index]
+ = duplicate_string(source->metadata.source_message_ids[index]);
+ if (destination->metadata.source_message_ids[index] == NULL)
+ {
+ return copy_failed(destination);
+ }
+ destination->metadata.source_count++;
+ }
+ }
if (source->metadata.tool_call_id != NULL)
{
destination->metadata.tool_call_id = duplicate_string(source->metadata.tool_call_id);
@@ -103,6 +156,70 @@ tiny_message_copy(tiny_message_t *destination, const tiny_message_t *source)
return 0;
}
+bool
+tiny_metadata_valid(const tiny_metadata_t *metadata)
+{
+ size_t index, other;
+ if (!metadata || metadata->memory_tier < TINY_TIER_SHORT
+ || metadata->memory_tier > TINY_TIER_DISCARD || metadata->memory_kind < TINY_KIND_CONVERSATION
+ || metadata->memory_kind > TINY_KIND_SUMMARY || metadata->importance > 100
+ || metadata->tag_count > TINY_TAG_COUNT_MAX || (metadata->tag_count && !metadata->tags)
+ || metadata->relation_count > TINY_RELATION_COUNT_MAX
+ || (metadata->relation_count && !metadata->relations)
+ || metadata->source_count > TINY_SOURCE_COUNT_MAX
+ || (metadata->source_count && !metadata->source_message_ids))
+ {
+ return false;
+ }
+ if (metadata->summary
+ && (strlen(metadata->summary) > TINY_SUMMARY_MAX
+ || !tiny_utf8_valid(metadata->summary, strlen(metadata->summary))))
+ {
+ return false;
+ }
+ for (index = 0; index < metadata->tag_count; ++index)
+ {
+ if (!metadata->tags[index] || !metadata->tags[index][0]
+ || strlen(metadata->tags[index]) > TINY_TAG_MAX)
+ {
+ return false;
+ }
+ }
+ for (index = 0; index < metadata->relation_count; ++index)
+ {
+ const tiny_relation_t *relation = &metadata->relations[index];
+ if (relation->type < TINY_RELATION_DEPENDS_ON || relation->type > TINY_RELATION_RELATED_TO
+ || !tiny_message_id_valid(relation->message_id))
+ {
+ return false;
+ }
+ for (other = 0; other < index; ++other)
+ {
+ if (relation->type == metadata->relations[other].type
+ && strcmp(relation->message_id, metadata->relations[other].message_id) == 0)
+ {
+ return false;
+ }
+ }
+ }
+ for (index = 0; index < metadata->source_count; ++index)
+ {
+ if (!metadata->source_message_ids[index]
+ || !tiny_message_id_valid(metadata->source_message_ids[index]))
+ {
+ return false;
+ }
+ for (other = 0; other < index; ++other)
+ {
+ if (strcmp(metadata->source_message_ids[index], metadata->source_message_ids[other]) == 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
const char *
tiny_role_name(tiny_role_t role)
{
@@ -193,6 +310,61 @@ tiny_tier_parse(const char *value, tiny_memory_tier_t *tier)
return true;
}
+const char *
+tiny_kind_name(tiny_memory_kind_t kind)
+{
+ static const char *names[]
+ = { "unknown", "conversation", "episode", "semantic", "procedure", "preference", "summary" };
+ return kind >= TINY_KIND_CONVERSATION && kind <= TINY_KIND_SUMMARY ? names[kind] : names[0];
+}
+
+bool
+tiny_kind_parse(const char *value, tiny_memory_kind_t *kind)
+{
+ tiny_memory_kind_t candidate;
+ if (value == NULL || kind == NULL)
+ {
+ return false;
+ }
+ for (candidate = TINY_KIND_CONVERSATION; candidate <= TINY_KIND_SUMMARY; ++candidate)
+ {
+ if (strcmp(value, tiny_kind_name(candidate)) == 0)
+ {
+ *kind = candidate;
+ return true;
+ }
+ }
+ return false;
+}
+
+const char *
+tiny_relation_type_name(tiny_relation_type_t type)
+{
+ static const char *names[]
+ = { "unknown", "depends_on", "derived_from", "supersedes", "related_to" };
+ return type >= TINY_RELATION_DEPENDS_ON && type <= TINY_RELATION_RELATED_TO ? names[type]
+ : names[0];
+}
+
+bool
+tiny_relation_type_parse(const char *value, tiny_relation_type_t *type)
+{
+ tiny_relation_type_t candidate;
+ if (value == NULL || type == NULL)
+ {
+ return false;
+ }
+ for (candidate = TINY_RELATION_DEPENDS_ON; candidate <= TINY_RELATION_RELATED_TO; ++candidate)
+ {
+ if (strcmp(value, tiny_relation_type_name(candidate)) == 0)
+ {
+ *type = candidate;
+ return true;
+ }
+ }
+ return false;
+}
+
bool
tiny_utf8_valid(const char *value, size_t length)
{
diff --git a/src/domain/message.h b/src/domain/message.h
index fa36d2a..cece77c 100644
--- a/src/domain/message.h
+++ b/src/domain/message.h
@@ -12,6 +12,9 @@
#define TINY_TAG_MAX 128
#define TINY_TAG_COUNT_MAX 32
#define TINY_QUERY_MAX 4096
+#define TINY_SUMMARY_MAX 4096
+#define TINY_RELATION_COUNT_MAX 32
+#define TINY_SOURCE_COUNT_MAX 64
typedef enum tiny_role
{
@@ -28,14 +31,56 @@ typedef enum tiny_memory_tier
TINY_TIER_DISCARD = 4
} tiny_memory_tier_t;
+typedef enum tiny_memory_kind
+{
+ TINY_KIND_CONVERSATION = 1,
+ TINY_KIND_EPISODE,
+ TINY_KIND_SEMANTIC,
+ TINY_KIND_PROCEDURE,
+ TINY_KIND_PREFERENCE,
+ TINY_KIND_SUMMARY
+} tiny_memory_kind_t;
+
+typedef enum tiny_relation_type
+{
+ TINY_RELATION_DEPENDS_ON = 1,
+ TINY_RELATION_DERIVED_FROM,
+ TINY_RELATION_SUPERSEDES,
+ TINY_RELATION_RELATED_TO
+} tiny_relation_type_t;
+
+typedef struct tiny_relation
+{
+ tiny_relation_type_t type;
+ char message_id[TINY_MESSAGE_ID_LENGTH + 1];
+} tiny_relation_t;
+
typedef struct tiny_metadata
{
char *tool_call_id;
char **tags;
size_t tag_count;
tiny_memory_tier_t memory_tier;
+ tiny_memory_kind_t memory_kind;
+ uint8_t importance;
+ char *summary;
+ tiny_relation_t *relations;
+ size_t relation_count;
+ char **source_message_ids;
+ size_t source_count;
} tiny_metadata_t;
+typedef uint32_t tiny_metadata_mask_t;
+#define TINY_METADATA_TOOL_CALL_ID UINT32_C(0x01)
+#define TINY_METADATA_TAGS UINT32_C(0x02)
+#define TINY_METADATA_TIER UINT32_C(0x04)
+#define TINY_METADATA_KIND UINT32_C(0x08)
+#define TINY_METADATA_IMPORTANCE UINT32_C(0x10)
+#define TINY_METADATA_SUMMARY UINT32_C(0x20)
+#define TINY_METADATA_RELATIONS UINT32_C(0x40)
+#define TINY_METADATA_SOURCES UINT32_C(0x80)
+#define TINY_METADATA_ALL UINT32_C(0xff)
+
typedef struct tiny_message
{
char message_id[TINY_MESSAGE_ID_LENGTH + 1];
@@ -44,6 +89,8 @@ typedef struct tiny_message
tiny_role_t role;
char *content;
tiny_metadata_t metadata;
+ uint64_t revision;
+ int64_t updated_at_ms;
} tiny_message_t;
void tiny_message_init(tiny_message_t *message);
@@ -54,7 +101,12 @@ const char *tiny_role_name(tiny_role_t role);
bool tiny_role_parse(const char *value, tiny_role_t *role);
const char *tiny_tier_name(tiny_memory_tier_t tier);
bool tiny_tier_parse(const char *value, tiny_memory_tier_t *tier);
+const char *tiny_kind_name(tiny_memory_kind_t kind);
+bool tiny_kind_parse(const char *value, tiny_memory_kind_t *kind);
+const char *tiny_relation_type_name(tiny_relation_type_t type);
+bool tiny_relation_type_parse(const char *value, tiny_relation_type_t *type);
bool tiny_utf8_valid(const char *value, size_t length);
bool tiny_message_id_valid(const char *value);
+bool tiny_metadata_valid(const tiny_metadata_t *metadata);
#endif
diff --git a/src/domain/search.c b/src/domain/search.c
index 0f9179d..10109a7 100644
--- a/src/domain/search.c
+++ b/src/domain/search.c
@@ -458,6 +458,12 @@ tiny_search_index_add(tiny_search_index_t *index,
tiny_search_index_free(delta);
return false;
}
+ if (message->metadata.summary
+ && !index_text_direct(delta, message->metadata.summary, message_index))
+ {
+ tiny_search_index_free(delta);
+ return false;
+ }
for (tag = 0; tag < message->metadata.tag_count; ++tag)
{
if (!index_text_direct(delta, message->metadata.tags[tag], message_index))
@@ -609,6 +615,26 @@ tiny_search_score(const char *query, const tiny_message_t *message)
}
free(tag_points);
}
+ if (!found && message->metadata.summary)
+ {
+ uint32_t *summary_points = NULL;
+ size_t summary_count = 0;
+ if (decode_codepoints(message->metadata.summary, &summary_points, &summary_count))
+ {
+ for (message_offset = 0; message_offset + width <= summary_count; ++message_offset)
+ {
+ if (memcmp(query_points + query_offset,
+ summary_points + message_offset,
+ width * sizeof(uint32_t))
+ == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ free(summary_points);
+ }
if (found)
{
matches++;