Skip to content

Commit c6f4071

Browse files
committed
fix: parse structured vectors.space responses
Read output_dimension, data[0].embedding, data[0].truncated, and nested usage token fields from the documented vectors.space response shape. This makes the remote embedding parser match the current API envelope instead of relying on flat key scans across the whole JSON payload.
1 parent a79fd96 commit c6f4071

1 file changed

Lines changed: 103 additions & 23 deletions

File tree

src/dbmem-rembed.c

Lines changed: 103 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,55 @@ static int set_json_error_message (dbmem_remote_engine_t *engine) {
210210
return -1;
211211
}
212212

213+
static int dbmem_json_skip_token (const jsmntok_t *tokens, int index) {
214+
int next = index + 1;
215+
216+
if (tokens[index].type == JSMN_ARRAY) {
217+
for (int i = 0; i < tokens[index].size; i++) {
218+
next = dbmem_json_skip_token(tokens, next);
219+
}
220+
return next;
221+
}
222+
223+
if (tokens[index].type == JSMN_OBJECT) {
224+
for (int i = 0; i < tokens[index].size; i++) {
225+
next += 1; // skip key token
226+
next = dbmem_json_skip_token(tokens, next);
227+
}
228+
return next;
229+
}
230+
231+
return next;
232+
}
233+
234+
static bool dbmem_json_token_equals (const char *json, const jsmntok_t *token, const char *text) {
235+
size_t len = strlen(text);
236+
size_t token_len = (size_t)(token->end - token->start);
237+
return token_len == len && memcmp(json + token->start, text, len) == 0;
238+
}
239+
240+
static int dbmem_json_object_find (const char *json, const jsmntok_t *tokens, int object_index, const char *key) {
241+
if (object_index < 0 || tokens[object_index].type != JSMN_OBJECT) return -1;
242+
243+
int index = object_index + 1;
244+
for (int i = 0; i < tokens[object_index].size; i++) {
245+
int key_index = index;
246+
int value_index = key_index + 1;
247+
248+
if (tokens[key_index].type != JSMN_STRING) return -1;
249+
if (dbmem_json_token_equals(json, &tokens[key_index], key)) return value_index;
250+
251+
index = dbmem_json_skip_token(tokens, value_index);
252+
}
253+
254+
return -1;
255+
}
256+
257+
static bool dbmem_json_parse_bool (const char *json, const jsmntok_t *token) {
258+
size_t len = (size_t)(token->end - token->start);
259+
return token->type == JSMN_PRIMITIVE && len == 4 && memcmp(json + token->start, "true", 4) == 0;
260+
}
261+
213262
#if ENABLE_DBMEM_DEBUG_EMBEDDING
214263
static void dbmem_remote_debug_log_response(dbmem_remote_engine_t *engine, long http_code) {
215264
const char *response = engine->data ? engine->data : "";
@@ -498,29 +547,60 @@ int dbmem_remote_compute_embedding (dbmem_remote_engine_t *engine, const char *t
498547
int emb_start = -1;
499548
size_t emb_count = 0;
500549

501-
for (int i = 0; i < ntokens - 1; i++) {
502-
if (tokens[i].type != JSMN_STRING) continue;
503-
int klen = tokens[i].end - tokens[i].start;
504-
const char *key = engine->data + tokens[i].start;
505-
506-
if (klen == 9 && memcmp(key, "embedding", 9) == 0 && tokens[i + 1].type == JSMN_ARRAY) {
507-
if (tokens[i + 1].size <= 0) {
508-
dbmem_context_set_error(engine->context, "Invalid embedding array size in API response");
509-
return -1;
510-
}
511-
emb_count = (size_t)tokens[i + 1].size;
512-
emb_start = i + 2;
513-
} else if (klen == 16 && memcmp(key, "output_dimension", 16) == 0) {
514-
n_embd = atoi(engine->data + tokens[i + 1].start);
515-
} else if (klen == 13 && memcmp(key, "prompt_tokens", 13) == 0 && tokens[i + 1].type == JSMN_PRIMITIVE) {
516-
prompt_tokens = atoi(engine->data + tokens[i + 1].start);
517-
} else if (klen == 19 && memcmp(key, "exact_prompt_tokens", 19) == 0 && tokens[i + 1].type == JSMN_PRIMITIVE) {
518-
exact_prompt_tokens = atoi(engine->data + tokens[i + 1].start);
519-
} else if (klen == 23 && memcmp(key, "estimated_prompt_tokens", 23) == 0) {
520-
estimated_prompt_tokens = atoi(engine->data + tokens[i + 1].start);
521-
} else if (klen == 9 && memcmp(key, "truncated", 9) == 0 && tokens[i + 1].type == JSMN_PRIMITIVE) {
522-
truncated = (tokens[i + 1].end - tokens[i + 1].start == 4) &&
523-
(memcmp(engine->data + tokens[i + 1].start, "true", 4) == 0);
550+
if (tokens[0].type != JSMN_OBJECT) {
551+
dbmem_context_set_error(engine->context, "Invalid API response shape");
552+
return -1;
553+
}
554+
555+
int output_dimension_index = dbmem_json_object_find(engine->data, tokens, 0, "output_dimension");
556+
if (output_dimension_index >= 0 && tokens[output_dimension_index].type == JSMN_PRIMITIVE) {
557+
n_embd = atoi(engine->data + tokens[output_dimension_index].start);
558+
}
559+
560+
int data_index = dbmem_json_object_find(engine->data, tokens, 0, "data");
561+
if (data_index < 0 || tokens[data_index].type != JSMN_ARRAY || tokens[data_index].size <= 0) {
562+
dbmem_context_set_error(engine->context, "Missing embedding data in API response");
563+
return -1;
564+
}
565+
566+
int item_index = data_index + 1;
567+
if (tokens[item_index].type != JSMN_OBJECT) {
568+
dbmem_context_set_error(engine->context, "Invalid embedding item in API response");
569+
return -1;
570+
}
571+
572+
int embedding_index = dbmem_json_object_find(engine->data, tokens, item_index, "embedding");
573+
if (embedding_index < 0 || tokens[embedding_index].type != JSMN_ARRAY) {
574+
dbmem_context_set_error(engine->context, "Missing embedding data in API response");
575+
return -1;
576+
}
577+
if (tokens[embedding_index].size <= 0) {
578+
dbmem_context_set_error(engine->context, "Invalid embedding array size in API response");
579+
return -1;
580+
}
581+
emb_count = (size_t)tokens[embedding_index].size;
582+
emb_start = embedding_index + 1;
583+
584+
int truncated_index = dbmem_json_object_find(engine->data, tokens, item_index, "truncated");
585+
if (truncated_index >= 0) {
586+
truncated = dbmem_json_parse_bool(engine->data, &tokens[truncated_index]);
587+
}
588+
589+
int usage_index = dbmem_json_object_find(engine->data, tokens, 0, "usage");
590+
if (usage_index >= 0 && tokens[usage_index].type == JSMN_OBJECT) {
591+
int prompt_tokens_index = dbmem_json_object_find(engine->data, tokens, usage_index, "prompt_tokens");
592+
if (prompt_tokens_index >= 0 && tokens[prompt_tokens_index].type == JSMN_PRIMITIVE) {
593+
prompt_tokens = atoi(engine->data + tokens[prompt_tokens_index].start);
594+
}
595+
596+
int exact_prompt_tokens_index = dbmem_json_object_find(engine->data, tokens, usage_index, "exact_prompt_tokens");
597+
if (exact_prompt_tokens_index >= 0 && tokens[exact_prompt_tokens_index].type == JSMN_PRIMITIVE) {
598+
exact_prompt_tokens = atoi(engine->data + tokens[exact_prompt_tokens_index].start);
599+
}
600+
601+
int estimated_prompt_tokens_index = dbmem_json_object_find(engine->data, tokens, usage_index, "estimated_prompt_tokens");
602+
if (estimated_prompt_tokens_index >= 0 && tokens[estimated_prompt_tokens_index].type == JSMN_PRIMITIVE) {
603+
estimated_prompt_tokens = atoi(engine->data + tokens[estimated_prompt_tokens_index].start);
524604
}
525605
}
526606

0 commit comments

Comments
 (0)