@@ -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
214263static 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