github-actions[bot] commented on code in PR #67671:
URL: https://github.com/apache/doris/pull/67671#discussion_r3959649269


##########
be/src/exprs/function/ai/ai_adapter.h:
##########
@@ -838,15 +845,50 @@ class OpenAIAdapter : public VoyageAIAdapter {
             results.reserve(output.Size());
 
             for (rapidjson::SizeType i = 0; i < output.Size(); i++) {
-                if (!output[i].HasMember("content") || 
!output[i]["content"].IsArray() ||
-                    output[i]["content"].Empty() || 
!output[i]["content"][0].HasMember("text") ||
-                    !output[i]["content"][0]["text"].IsString()) {
+                const auto& item = output[i];
+                if (!item.IsObject() || !item.HasMember("type") || 
!item["type"].IsString()) {
                     return Status::InternalError("Invalid output format in {} 
response: {}",
                                                  _config.provider_type, 
response_body);
                 }
 
-                RETURN_IF_ERROR(append_parsed_text_result(
-                        output[i]["content"][0]["text"].GetString(), results));
+                // Responses output is heterogeneous. Reasoning and tool items 
are not final text.
+                if (std::string_view(item["type"].GetString(), 
item["type"].GetStringLength()) !=
+                    "message") {
+                    continue;
+                }
+
+                if (!item.HasMember("content") || !item["content"].IsArray()) {
+                    return Status::InternalError("Invalid output format in {} 
response: {}",
+                                                 _config.provider_type, 
response_body);
+                }
+
+                const auto& content = item["content"];
+                bool has_output_text = false;
+                for (rapidjson::SizeType j = 0; j < content.Size(); j++) {

Review Comment:
   [P1] Aggregate response text before expanding the batch result
   
   Responses message `content` is a list, and the official SDKs build 
`response.output_text` by concatenating every `output_text` block across all 
messages in order. Parsing inside this per-block loop changes those blocks into 
separate Doris rows. For a two-row batch, two fragments that concatenate to 
`["row0","row1"]` are each appended as raw strings; the result count is still 
two, so the cardinality check succeeds with corrupted row values. Please 
concatenate the selected text first, preserving explicit string lengths, then 
call the batch-result parser once.



##########
be/src/exprs/function/ai/ai_adapter.h:
##########
@@ -838,15 +845,50 @@ class OpenAIAdapter : public VoyageAIAdapter {
             results.reserve(output.Size());
 
             for (rapidjson::SizeType i = 0; i < output.Size(); i++) {
-                if (!output[i].HasMember("content") || 
!output[i]["content"].IsArray() ||
-                    output[i]["content"].Empty() || 
!output[i]["content"][0].HasMember("text") ||
-                    !output[i]["content"][0]["text"].IsString()) {
+                const auto& item = output[i];
+                if (!item.IsObject() || !item.HasMember("type") || 
!item["type"].IsString()) {
                     return Status::InternalError("Invalid output format in {} 
response: {}",
                                                  _config.provider_type, 
response_body);
                 }
 
-                RETURN_IF_ERROR(append_parsed_text_result(
-                        output[i]["content"][0]["text"].GetString(), results));
+                // Responses output is heterogeneous. Reasoning and tool items 
are not final text.
+                if (std::string_view(item["type"].GetString(), 
item["type"].GetStringLength()) !=
+                    "message") {
+                    continue;
+                }
+
+                if (!item.HasMember("content") || !item["content"].IsArray()) {
+                    return Status::InternalError("Invalid output format in {} 
response: {}",
+                                                 _config.provider_type, 
response_body);
+                }
+
+                const auto& content = item["content"];
+                bool has_output_text = false;
+                for (rapidjson::SizeType j = 0; j < content.Size(); j++) {
+                    const auto& part = content[j];
+                    if (!part.IsObject() || !part.HasMember("type") || 
!part["type"].IsString()) {
+                        return Status::InternalError("Invalid output format in 
{} response: {}",
+                                                     _config.provider_type, 
response_body);
+                    }
+
+                    if (std::string_view(part["type"].GetString(),
+                                         part["type"].GetStringLength()) != 
"output_text") {
+                        continue;
+                    }
+
+                    if (!part.HasMember("text") || !part["text"].IsString()) {
+                        return Status::InternalError("Invalid output format in 
{} response: {}",
+                                                     _config.provider_type, 
response_body);
+                    }
+
+                    has_output_text = true;
+                    
RETURN_IF_ERROR(append_parsed_text_result(part["text"].GetString(), results));

Review Comment:
   [P1] Keep aggregate output opaque instead of expanding batch rows
   
   This helper has a scalar-batch contract: a JSON array is expanded into one 
vector element per input row. The same adapter is also called by 
`AggregateFunctionAIAggData::_execute_task`, which sends one unbatched request 
and immediately returns `results[0]`. With the newly supported leading 
reasoning item, a completed message containing `[]` now returns OK and is 
indexed out of bounds, while `["north","south"]` silently returns only `north`. 
Please separate Responses text extraction from scalar batch expansion (or pass 
an explicit parse mode) so `ai_agg` receives the complete output as one value.



##########
be/src/exprs/function/ai/ai_adapter.h:
##########
@@ -838,15 +845,50 @@ class OpenAIAdapter : public VoyageAIAdapter {
             results.reserve(output.Size());
 
             for (rapidjson::SizeType i = 0; i < output.Size(); i++) {
-                if (!output[i].HasMember("content") || 
!output[i]["content"].IsArray() ||
-                    output[i]["content"].Empty() || 
!output[i]["content"][0].HasMember("text") ||
-                    !output[i]["content"][0]["text"].IsString()) {
+                const auto& item = output[i];
+                if (!item.IsObject() || !item.HasMember("type") || 
!item["type"].IsString()) {
                     return Status::InternalError("Invalid output format in {} 
response: {}",
                                                  _config.provider_type, 
response_body);
                 }
 
-                RETURN_IF_ERROR(append_parsed_text_result(
-                        output[i]["content"][0]["text"].GetString(), results));
+                // Responses output is heterogeneous. Reasoning and tool items 
are not final text.
+                if (std::string_view(item["type"].GetString(), 
item["type"].GetStringLength()) !=

Review Comment:
   [P1] Reject non-completed or textless Responses before returning success
   
   A non-streaming `/responses` call can return HTTP 200 with `status: 
"incomplete"` after hitting `max_output_tokens`. This new skip makes a 
reasoning-plus-partial-message envelope reach text parsing; an unterminated 
batch array becomes one raw string, so a one-row scalar call silently returns 
truncated output. If the budget is exhausted by reasoning alone, the loop 
instead returns OK with an empty vector, and 
`AggregateFunctionAIAggData::_execute_task` immediately indexes `results[0]`. 
Please reject explicit non-completed responses with their 
`incomplete_details`/provider error and ensure the Responses branch cannot 
return OK unless it found final text.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to