Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/provider_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ provider_count_array_dimensions(const char *p)
*
* Reads up to `dim` float values from the current position (after '[').
* Advances *pos past the parsed values (to ']' or end of parsed data).
* Returns the number of values successfully parsed.
* Returns the number of values successfully parsed, or
* PROVIDER_PARSE_MALFORMED on a numeric literal too long to be one, in which
* case *pos is left on the offending run rather than past it.
*/
int
provider_parse_float_array(const char **pos, float *output, int dim)
Expand All @@ -250,8 +252,20 @@ provider_parse_float_array(const char **pos, float *output, int dim)
while (*p && (isdigit((unsigned char) *p) || *p == '.' || *p == '-' ||
*p == '+' || *p == 'e' || *p == 'E'))
{
if (value_pos < (int) sizeof(value_buf) - 1)
value_buf[value_pos++] = *p;
/*
* Stop rather than truncate. No double needs this many
* characters -- %.17g never exceeds 24 -- so a longer run means a
* malformed response, and keeping only the leading digits would
* feed atof() a number of an entirely different magnitude and
* score on it silently.
*/
if (value_pos >= (int) sizeof(value_buf) - 1)
{
*pos = p;
return PROVIDER_PARSE_MALFORMED;
}

value_buf[value_pos++] = *p;
p++;
}
value_buf[value_pos] = '\0';
Expand Down Expand Up @@ -457,8 +471,11 @@ provider_parse_openai_embedding_response(const char *json_response, int count,

if (parsed != *dim)
{
*error_msg = psprintf("Dimension mismatch: expected %d, got %d",
*dim, parsed);
*error_msg = (parsed == PROVIDER_PARSE_MALFORMED)
? pstrdup("Malformed embedding response: numeric literal too "
"long to be a number")
: psprintf("Dimension mismatch: expected %d, got %d",
*dim, parsed);
provider_free_embeddings(embeddings, embedding_idx + 1);
return NULL;
}
Expand Down
9 changes: 8 additions & 1 deletion src/provider_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ float **provider_parse_openai_embedding_response(const char *json_response,
/* Count dimensions by counting commas in a JSON float array (after '[') */
int provider_count_array_dimensions(const char *p);

/* Parse a JSON float array into pre-allocated output; returns count parsed */
/*
* Parse a JSON float array into pre-allocated output; returns count parsed,
* or PROVIDER_PARSE_MALFORMED if a numeric literal was too long to be one.
* Callers that only compare against the expected count still reject that,
* since it can never equal a dimension.
*/
#define PROVIDER_PARSE_MALFORMED (-1)

int provider_parse_float_array(const char **pos, float *output, int dim);

/* Append extra headers from GUC to a curl header list */
Expand Down
14 changes: 9 additions & 5 deletions src/provider_gemini.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ gemini_generate_batch(const char **texts, int count, int *dim, char **error_msg)
char *json_request;
char *url;
const char *base_url;
char auth_header[512];
char *auth_header;
StringInfoData request_buf;
ResponseBuffer response;
float **embeddings;
Expand Down Expand Up @@ -164,15 +164,15 @@ gemini_generate_batch(const char **texts, int count, int *dim, char **error_msg)
pgedge_vectorizer_model);

/* Build auth header */
snprintf(auth_header, sizeof(auth_header),
"x-goog-api-key: %s", api_key);
auth_header = psprintf("x-goog-api-key: %s", api_key);

/* Perform request */
if (!provider_do_curl_request(url, auth_header, json_request,
"Gemini", &response, error_msg))
{
pfree(json_request);
pfree(url);
pfree(auth_header);
if (response.data)
pfree(response.data);
return NULL;
Expand All @@ -184,6 +184,7 @@ gemini_generate_batch(const char **texts, int count, int *dim, char **error_msg)

pfree(json_request);
pfree(url);
pfree(auth_header);
pfree(response.data);
return embeddings;
}
Expand Down Expand Up @@ -236,8 +237,11 @@ parse_gemini_batch_embedding_response(const char *json_response, int count,

if (parsed != *dim)
{
*error_msg = psprintf("Dimension mismatch: expected %d, got %d",
*dim, parsed);
*error_msg = (parsed == PROVIDER_PARSE_MALFORMED)
? pstrdup("Malformed embedding response: numeric literal too "
"long to be a number")
: psprintf("Dimension mismatch: expected %d, got %d",
*dim, parsed);
provider_free_embeddings(embeddings, embedding_idx + 1);
return NULL;
}
Expand Down
7 changes: 5 additions & 2 deletions src/provider_ollama.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,11 @@ parse_ollama_embedding_response(const char *json_response, int *dim,

if (parsed != *dim)
{
*error_msg = psprintf("Dimension mismatch: expected %d, got %d",
*dim, parsed);
*error_msg = (parsed == PROVIDER_PARSE_MALFORMED)
? pstrdup("Malformed embedding response: numeric literal too "
"long to be a number")
: psprintf("Dimension mismatch: expected %d, got %d",
*dim, parsed);
pfree(embedding);
return NULL;
}
Expand Down
13 changes: 7 additions & 6 deletions src/provider_openai.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,7 @@ openai_generate_batch(const char **texts, int count, int *dim, char **error_msg)
char *json_request;
char *url;
const char *base_url;
char auth_header[512];
const char *auth_header_ptr = NULL;
char *auth_header = NULL;
ResponseBuffer response;
float **embeddings;

Expand All @@ -160,17 +159,17 @@ openai_generate_batch(const char **texts, int count, int *dim, char **error_msg)
/* Build auth header if we have a key */
if (api_key != NULL)
{
snprintf(auth_header, sizeof(auth_header),
"Authorization: Bearer %s", api_key);
auth_header_ptr = auth_header;
auth_header = psprintf("Authorization: Bearer %s", api_key);
}

/* Perform request */
if (!provider_do_curl_request(url, auth_header_ptr, json_request,
if (!provider_do_curl_request(url, auth_header, json_request,
"OpenAI", &response, error_msg))
{
pfree(json_request);
pfree(url);
if (auth_header)
pfree(auth_header);
if (response.data)
pfree(response.data);
return NULL;
Expand All @@ -182,6 +181,8 @@ openai_generate_batch(const char **texts, int count, int *dim, char **error_msg)

pfree(json_request);
pfree(url);
if (auth_header)
pfree(auth_header);
pfree(response.data);
return embeddings;
}
7 changes: 4 additions & 3 deletions src/provider_voyage.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ voyage_generate_batch(const char **texts, int count, int *dim, char **error_msg)
char *json_request;
char *url;
const char *base_url;
char auth_header[512];
char *auth_header;
ResponseBuffer response;
float **embeddings;

Expand All @@ -138,15 +138,15 @@ voyage_generate_batch(const char **texts, int count, int *dim, char **error_msg)
url = psprintf("%s/embeddings", base_url);

/* Build auth header */
snprintf(auth_header, sizeof(auth_header),
"Authorization: Bearer %s", api_key);
auth_header = psprintf("Authorization: Bearer %s", api_key);

/* Perform request */
if (!provider_do_curl_request(url, auth_header, json_request,
"Voyage AI", &response, error_msg))
{
pfree(json_request);
pfree(url);
pfree(auth_header);
if (response.data)
pfree(response.data);
return NULL;
Expand All @@ -158,6 +158,7 @@ voyage_generate_batch(const char **texts, int count, int *dim, char **error_msg)

pfree(json_request);
pfree(url);
pfree(auth_header);
pfree(response.data);
return embeddings;
}
5 changes: 5 additions & 0 deletions src/worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ static time_t last_cleanup_time = 0;
* a fixed string tells them the item failed, which they can already see from
* its status, but not why. A fixed buffer rather than a palloc'd copy, since
* the transaction context this is captured in does not survive the abort.
*
* Truncation here is deliberate and harmless: this is diagnostic text, the
* untruncated message has already gone to the server log via
* EmitErrorReport(), and a bounded buffer is what lets the copy outlive the
* abort at all.
*/
#define FAILED_ITEM_ERROR_LEN 1024

Expand Down
Loading