diff options
Diffstat (limited to 'src/domain')
| -rw-r--r-- | src/domain/message.c | 176 | ||||
| -rw-r--r-- | src/domain/message.h | 52 | ||||
| -rw-r--r-- | src/domain/search.c | 26 |
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++; |
