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


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/property/constants/AIProperties.java:
##########
@@ -53,29 +57,24 @@ public class AIProperties extends BaseProperties {
     public static final String VALIDITY_CHECK = "ai.validity_check";
 
     public static final List<String> REQUIRED_FIELDS = Arrays.asList(ENDPOINT, 
PROVIDER_TYPE, MODEL_NAME);
+    public static final List<String> EMBED_REQUIRED_FIELDS =
+            Arrays.asList(EMBED_ENDPOINT, EMBED_PROVIDER_TYPE, 
EMBED_MODEL_NAME);
     public static final List<String> PROVIDERS
             = Arrays.asList("OPENAI", "LOCAL", "GEMINI", "DEEPSEEK", 
"ANTHROPIC",
             "MOONSHOT", "QWEN", "MINIMAX", "ZHIPU", "BAICHUAN", "VOYAGEAI", 
"JINA");
 
     public static void requiredAIProperties(Map<String, String> properties) 
throws DdlException {
-        // Check required field
-        for (String field : REQUIRED_FIELDS) {
-            if (Strings.isNullOrEmpty(properties.get(field))) {
-                throw new DdlException("Missing [" + field + "] in 
properties.");
-            }
+        boolean hasGeneralProperties = hasAnyProperty(properties, 
REQUIRED_FIELDS, API_KEY);
+        boolean hasEmbedProperties = hasAnyProperty(properties, 
EMBED_REQUIRED_FIELDS, EMBED_API_KEY);
+        if (!hasGeneralProperties && !hasEmbedProperties) {

Review Comment:
   [P1] Reject embed-only resources for consumers that require the general 
group. This condition now makes an embed-only resource valid, but the FE checks 
for the other scalar AI functions and `AI_AGG` only verify the resource type. 
They transport it with the general fields unset; scalar execution then reaches 
`DORIS_CHECK(adapter)` for an empty provider, and `AI_AGG` dereferences the 
null factory result. Please add a resource-capability check during FE legality 
analysis, with negative scalar and aggregate tests, so these valid SQL 
statements return an analysis error instead of aborting a BE.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/property/constants/AIProperties.java:
##########
@@ -32,9 +32,13 @@ public class AIProperties extends BaseProperties {
     public static final String ENDPOINT = "ai.endpoint";
     public static final String PROVIDER_TYPE = "ai.provider_type";
     public static final String MODEL_NAME = "ai.model_name";
+    public static final String EMBED_ENDPOINT = "ai.embed.endpoint";
+    public static final String EMBED_PROVIDER_TYPE = "ai.embed.provider_type";
+    public static final String EMBED_MODEL_NAME = "ai.embed.model_name";
 
     // optional
     public static final String API_KEY = "ai.api_key";
+    public static final String EMBED_API_KEY = "ai.embed.api_key";

Review Comment:
   [P1] Register the embed API key with statement/audit masking. 
`NeedAuditEncryption` rewrites CREATE/ALTER RESOURCE properties through 
`DatasourcePrintableMap`, but its sensitive-key set contains only `ai.api_key`. 
As a result, `ai.embed.api_key` remains verbatim in the encrypted SQL used by 
statement and audit logging; the new test covers only SHOW RESOURCES. Please 
add this key to the generic masker and cover both CREATE and ALTER 
encryption/logging paths.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/property/constants/AIProperties.java:
##########
@@ -97,6 +96,30 @@ public static void requiredAIProperties(Map<String, String> 
properties) throws D
         }
     }
 
+    private static boolean hasAnyProperty(Map<String, String> properties, 
List<String> requiredFields,
+            String apiKeyField) {
+        return properties.containsKey(apiKeyField) || 
requiredFields.stream().anyMatch(properties::containsKey);
+    }
+
+    private static void validatePropertyGroup(Map<String, String> properties, 
List<String> requiredFields,
+            String providerTypeField, String apiKeyField) throws DdlException {
+        for (String field : requiredFields) {
+            if (Strings.isNullOrEmpty(properties.get(field))) {
+                throw new DdlException("Missing [" + field + "] in 
properties.");
+            }
+        }
+
+        String providerType = properties.get(providerTypeField).toUpperCase();
+        properties.put(providerTypeField, providerType);

Review Comment:
   [P1] Persist the normalized provider validated here. During ALTER, 
`requiredAIProperties` mutates only `changedProperties`, while 
`modifyProperties` subsequently writes the original ALTER map. For a resource 
with positive dimensions, setting `ai.embed.provider_type` to lowercase 
`openai` therefore validates successfully but persists lowercase; the BE 
factory lookup is case-sensitive and reaches the null-adapter invariant. Please 
install the validated/normalized merged snapshot, and test ALTER plus 
serialization/replay, instead of discarding its normalization.



##########
be/src/exprs/function/ai/embed.h:
##########
@@ -44,8 +44,15 @@ class FunctionEmbed : public AIFunction<FunctionEmbed> {
 
     using PreparedFunctionImpl::execute;
 
+    AIResource select_ai_resource(const TAIResource& resource) const {
+        bool has_embed_properties = resource.__isset.embed_endpoint ||

Review Comment:
   [P1] Do not treat a single embed field as a complete dedicated group. ALTER 
skips `requiredAIProperties` when `ai.validity_check=false` or the general 
provider is `LOCAL`, so adding only `ai.embed.endpoint` is persisted and sets 
this bit. EMBED then selects an empty provider/model/key and reaches the fatal 
adapter check. Please make group-shape validation unconditional, separate it 
from remote validity checking, reject incomplete wire groups normally, and add 
partial-group ALTER tests.



##########
fe/fe-core/src/main/java/org/apache/doris/catalog/AIResource.java:
##########
@@ -149,10 +151,30 @@ protected void getProcNodeData(BaseProcResult result) {
 
     public TAIResource toThrift() throws NumberFormatException {
         TAIResource tAIResource = new TAIResource();
-        
tAIResource.setProviderType(properties.get(AIProperties.PROVIDER_TYPE));
-        tAIResource.setEndpoint(properties.get(AIProperties.ENDPOINT));
-        tAIResource.setApiKey(properties.get(AIProperties.API_KEY));
-        tAIResource.setModelName(properties.get(AIProperties.MODEL_NAME));
+        if (properties.containsKey(AIProperties.PROVIDER_TYPE)) {
+            
tAIResource.setProviderType(properties.get(AIProperties.PROVIDER_TYPE));
+        }
+        if (properties.containsKey(AIProperties.ENDPOINT)) {
+            tAIResource.setEndpoint(properties.get(AIProperties.ENDPOINT));
+        }
+        if (properties.containsKey(AIProperties.API_KEY)) {
+            tAIResource.setApiKey(properties.get(AIProperties.API_KEY));
+        }
+        if (properties.containsKey(AIProperties.MODEL_NAME)) {
+            tAIResource.setModelName(properties.get(AIProperties.MODEL_NAME));
+        }
+        if (properties.containsKey(AIProperties.EMBED_PROVIDER_TYPE)) {

Review Comment:
   [P1] Serialize one read-locked resource snapshot. ALTER mutates the shared 
`HashMap` entry by entry under `writeLock`, but both query-planning transport 
paths call this lock-free `toThrift` method. A concurrent plan can therefore 
emit only part of the new dedicated group, or mix old and new 
endpoint/provider/key values; any observed field makes the BE choose that 
group. Please copy under `readLock` or atomically replace an immutable map, and 
add a concurrency test proving serialization sees only the complete old or new 
group.



-- 
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