Browse Source

[ML] Move the model parameter from task settings to service settings (#105458)

When configuring an OpenAI text embedding service the `model_id` should
have always been part of the service settings rather than task settings.
Task settings are overridable, service settings cannot be changed. If
different models are used the configured entities are considered
distinct. 

task_settings is now optional as it contains a single optional field
(`user`)

```
PUT _inference/text_embedding/openai_embeddings
{
  "service": "openai",
  "service_settings": {
    "api_key": "XXX",
    "model_id": "text-embedding-ada-002"
  }
}
```

Backwards compatibility with previously configured models is maintained
by moving the `model_id` (or `model`) from task settings to service
settings at the first stage of parsing. New configurations are persisted
with `model_id` in service settings, old configurations with `model_id`
in task settings are not modified and will be tolerated by a lenient
parser.
David Kyle 1 year ago
parent
commit
ecb01405e7
15 changed files with 336 additions and 354 deletions
  1. 5 0
      docs/changelog/105458.yaml
  2. 1 0
      server/src/main/java/org/elasticsearch/TransportVersions.java
  3. 1 1
      x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/external/request/openai/OpenAiEmbeddingsRequest.java
  4. 1 0
      x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/ServiceFields.java
  5. 10 0
      x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/ServiceUtils.java
  6. 40 6
      x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/OpenAiService.java
  7. 4 16
      x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsRequestTaskSettings.java
  8. 35 5
      x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsServiceSettings.java
  9. 38 46
      x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsTaskSettings.java
  10. 4 4
      x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/external/action/openai/OpenAiActionCreatorTests.java
  11. 116 56
      x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/OpenAiServiceTests.java
  12. 9 9
      x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsModelTests.java
  13. 2 50
      x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsRequestTaskSettingsTests.java
  14. 48 19
      x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsServiceSettingsTests.java
  15. 22 142
      x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsTaskSettingsTests.java

+ 5 - 0
docs/changelog/105458.yaml

@@ -0,0 +1,5 @@
+pr: 105458
+summary: The OpenAI model parameter should be in service settings not task settings. Move the configuration field to service settings
+area: Machine Learning
+type: bug
+issues: []

+ 1 - 0
server/src/main/java/org/elasticsearch/TransportVersions.java

@@ -132,6 +132,7 @@ public class TransportVersions {
     public static final TransportVersion ML_DIMENSIONS_SET_BY_USER_ADDED = def(8_592_00_0);
     public static final TransportVersion INDEX_REQUEST_NORMALIZED_BYTES_PARSED = def(8_593_00_0);
     public static final TransportVersion INGEST_GRAPH_STRUCTURE_EXCEPTION = def(8_594_00_0);
+    public static final TransportVersion ML_MODEL_IN_SERVICE_SETTINGS = def(8_595_00_0);
 
     /*
      * STOP! READ THIS FIRST! No, really,

+ 1 - 1
x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/external/request/openai/OpenAiEmbeddingsRequest.java

@@ -56,7 +56,7 @@ public class OpenAiEmbeddingsRequest implements Request {
             Strings.toString(
                 new OpenAiEmbeddingsRequestEntity(
                     truncationResult.input(),
-                    model.getTaskSettings().modelId(),
+                    model.getServiceSettings().modelId(),
                     model.getTaskSettings().user(),
                     model.getServiceSettings().dimensions(),
                     model.getServiceSettings().dimensionsSetByUser()

+ 1 - 0
x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/ServiceFields.java

@@ -16,6 +16,7 @@ public final class ServiceFields {
     public static final String DIMENSIONS = "dimensions";
     public static final String MAX_INPUT_TOKENS = "max_input_tokens";
     public static final String URL = "url";
+    public static final String MODEL_ID = "model_id";
 
     private ServiceFields() {
 

+ 10 - 0
x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/ServiceUtils.java

@@ -26,6 +26,7 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -75,6 +76,15 @@ public class ServiceUtils {
         return value;
     }
 
+    @SuppressWarnings("unchecked")
+    public static Map<String, Object> removeFromMapOrDefaultEmpty(Map<String, Object> sourceMap, String fieldName) {
+        Map<String, Object> value = (Map<String, Object>) sourceMap.remove(fieldName);
+        if (value == null) {
+            return new HashMap<>();
+        }
+        return value;
+    }
+
     public static String removeStringOrThrowIfNull(Map<String, Object> sourceMap, String key) {
         String value = removeAsType(sourceMap, key, String.class);
         if (value == null) {

+ 40 - 6
x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/OpenAiService.java

@@ -36,8 +36,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static org.elasticsearch.xpack.inference.services.ServiceFields.MODEL_ID;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.createInvalidModelException;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.parsePersistedConfigErrorMsg;
+import static org.elasticsearch.xpack.inference.services.ServiceUtils.removeFromMapOrDefaultEmpty;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.removeFromMapOrThrowIfNull;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.throwIfNotEmptyMap;
 
@@ -63,7 +65,9 @@ public class OpenAiService extends SenderService {
     ) {
         try {
             Map<String, Object> serviceSettingsMap = removeFromMapOrThrowIfNull(config, ModelConfigurations.SERVICE_SETTINGS);
-            Map<String, Object> taskSettingsMap = removeFromMapOrThrowIfNull(config, ModelConfigurations.TASK_SETTINGS);
+            Map<String, Object> taskSettingsMap = removeFromMapOrDefaultEmpty(config, ModelConfigurations.TASK_SETTINGS);
+
+            moveModelFromTaskToServiceSettings(taskSettingsMap, serviceSettingsMap);
 
             OpenAiModel model = createModel(
                 inferenceEntityId,
@@ -85,7 +89,7 @@ public class OpenAiService extends SenderService {
         }
     }
 
-    private static OpenAiModel createModelWithoutLoggingDeprecations(
+    private static OpenAiModel createModelFromPersistent(
         String inferenceEntityId,
         TaskType taskType,
         Map<String, Object> serviceSettings,
@@ -136,9 +140,11 @@ public class OpenAiService extends SenderService {
     ) {
         Map<String, Object> serviceSettingsMap = removeFromMapOrThrowIfNull(config, ModelConfigurations.SERVICE_SETTINGS);
         Map<String, Object> taskSettingsMap = removeFromMapOrThrowIfNull(config, ModelConfigurations.TASK_SETTINGS);
-        Map<String, Object> secretSettingsMap = removeFromMapOrThrowIfNull(secrets, ModelSecrets.SECRET_SETTINGS);
+        Map<String, Object> secretSettingsMap = removeFromMapOrDefaultEmpty(secrets, ModelSecrets.SECRET_SETTINGS);
+
+        moveModelFromTaskToServiceSettings(taskSettingsMap, serviceSettingsMap);
 
-        return createModelWithoutLoggingDeprecations(
+        return createModelFromPersistent(
             inferenceEntityId,
             taskType,
             serviceSettingsMap,
@@ -151,9 +157,11 @@ public class OpenAiService extends SenderService {
     @Override
     public OpenAiModel parsePersistedConfig(String inferenceEntityId, TaskType taskType, Map<String, Object> config) {
         Map<String, Object> serviceSettingsMap = removeFromMapOrThrowIfNull(config, ModelConfigurations.SERVICE_SETTINGS);
-        Map<String, Object> taskSettingsMap = removeFromMapOrThrowIfNull(config, ModelConfigurations.TASK_SETTINGS);
+        Map<String, Object> taskSettingsMap = removeFromMapOrDefaultEmpty(config, ModelConfigurations.TASK_SETTINGS);
+
+        moveModelFromTaskToServiceSettings(taskSettingsMap, serviceSettingsMap);
 
-        return createModelWithoutLoggingDeprecations(
+        return createModelFromPersistent(
             inferenceEntityId,
             taskType,
             serviceSettingsMap,
@@ -232,6 +240,7 @@ public class OpenAiService extends SenderService {
         }
 
         OpenAiEmbeddingsServiceSettings serviceSettings = new OpenAiEmbeddingsServiceSettings(
+            model.getServiceSettings().modelId(),
             model.getServiceSettings().uri(),
             model.getServiceSettings().organizationId(),
             SimilarityMeasure.DOT_PRODUCT,
@@ -247,4 +256,29 @@ public class OpenAiService extends SenderService {
     public TransportVersion getMinimalSupportedVersion() {
         return TransportVersions.V_8_12_0;
     }
+
+    /**
+     * Model was originally defined in task settings, but it should
+     * have been part of the service settings.
+     *
+     * If model or model_id are in the task settings map move
+     * them to service settings ready for parsing
+     *
+     * @param taskSettings Task settings map
+     * @param serviceSettings Service settings map
+     */
+    static void moveModelFromTaskToServiceSettings(Map<String, Object> taskSettings, Map<String, Object> serviceSettings) {
+        if (serviceSettings.containsKey(MODEL_ID)) {
+            return;
+        }
+
+        final String OLD_MODEL_ID_FIELD = "model";
+        var oldModelId = taskSettings.remove(OLD_MODEL_ID_FIELD);
+        if (oldModelId != null) {
+            serviceSettings.put(MODEL_ID, oldModelId);
+        } else {
+            var modelId = taskSettings.remove(MODEL_ID);
+            serviceSettings.put(MODEL_ID, modelId);
+        }
+    }
 }

+ 4 - 16
x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsRequestTaskSettings.java

@@ -16,25 +16,23 @@ import org.elasticsearch.inference.ModelConfigurations;
 import java.util.Map;
 
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.extractOptionalString;
-import static org.elasticsearch.xpack.inference.services.openai.embeddings.OpenAiEmbeddingsTaskSettings.MODEL_ID;
-import static org.elasticsearch.xpack.inference.services.openai.embeddings.OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD;
 import static org.elasticsearch.xpack.inference.services.openai.embeddings.OpenAiEmbeddingsTaskSettings.USER;
 
 /**
  * This class handles extracting OpenAI task settings from a request. The difference between this class and
  * {@link OpenAiEmbeddingsTaskSettings} is that this class considers all fields as optional. It will not throw an error if a field
  * is missing. This allows overriding persistent task settings.
- * @param modelId the name of the model to use with this request
  * @param user a unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse
  */
-public record OpenAiEmbeddingsRequestTaskSettings(@Nullable String modelId, @Nullable String user) {
+public record OpenAiEmbeddingsRequestTaskSettings(@Nullable String user) {
     private static final Logger logger = LogManager.getLogger(OpenAiEmbeddingsRequestTaskSettings.class);
 
-    public static final OpenAiEmbeddingsRequestTaskSettings EMPTY_SETTINGS = new OpenAiEmbeddingsRequestTaskSettings(null, null);
+    public static final OpenAiEmbeddingsRequestTaskSettings EMPTY_SETTINGS = new OpenAiEmbeddingsRequestTaskSettings(null);
 
     /**
      * Extracts the task settings from a map. All settings are considered optional and the absence of a setting
      * does not throw an error.
+     *
      * @param map the settings received from a request
      * @return a {@link OpenAiEmbeddingsRequestTaskSettings}
      */
@@ -45,22 +43,12 @@ public record OpenAiEmbeddingsRequestTaskSettings(@Nullable String modelId, @Nul
 
         ValidationException validationException = new ValidationException();
 
-        // I'm intentionally not logging if this is set because it would log on every request
-        String model = extractOptionalString(map, OLD_MODEL_ID_FIELD, ModelConfigurations.TASK_SETTINGS, validationException);
-
-        String modelId = extractOptionalString(map, MODEL_ID, ModelConfigurations.TASK_SETTINGS, validationException);
         String user = extractOptionalString(map, USER, ModelConfigurations.TASK_SETTINGS, validationException);
 
-        var modelIdToUse = getModelId(model, modelId);
-
         if (validationException.validationErrors().isEmpty() == false) {
             throw validationException;
         }
 
-        return new OpenAiEmbeddingsRequestTaskSettings(modelIdToUse, user);
-    }
-
-    private static String getModelId(@Nullable String model, @Nullable String modelId) {
-        return modelId != null ? modelId : model;
+        return new OpenAiEmbeddingsRequestTaskSettings(user);
     }
 }

+ 35 - 5
x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsServiceSettings.java

@@ -28,11 +28,13 @@ import java.util.Objects;
 
 import static org.elasticsearch.xpack.inference.services.ServiceFields.DIMENSIONS;
 import static org.elasticsearch.xpack.inference.services.ServiceFields.MAX_INPUT_TOKENS;
+import static org.elasticsearch.xpack.inference.services.ServiceFields.MODEL_ID;
 import static org.elasticsearch.xpack.inference.services.ServiceFields.SIMILARITY;
 import static org.elasticsearch.xpack.inference.services.ServiceFields.URL;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.convertToUri;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.createOptionalUri;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.extractOptionalString;
+import static org.elasticsearch.xpack.inference.services.ServiceUtils.extractRequiredString;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.extractSimilarity;
 import static org.elasticsearch.xpack.inference.services.ServiceUtils.removeAsType;
 
@@ -92,11 +94,13 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         Integer maxInputTokens = removeAsType(map, MAX_INPUT_TOKENS, Integer.class);
         Integer dims = removeAsType(map, DIMENSIONS, Integer.class);
         URI uri = convertToUri(url, URL, ModelConfigurations.SERVICE_SETTINGS, validationException);
+        String modelId = extractRequiredString(map, MODEL_ID, ModelConfigurations.SERVICE_SETTINGS, validationException);
 
-        return new CommonFields(uri, organizationId, similarity, maxInputTokens, dims);
+        return new CommonFields(modelId, uri, organizationId, similarity, maxInputTokens, dims);
     }
 
     private record CommonFields(
+        String modelId,
         @Nullable URI uri,
         @Nullable String organizationId,
         @Nullable SimilarityMeasure similarity,
@@ -104,6 +108,7 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         @Nullable Integer dimensions
     ) {}
 
+    private final String modelId;
     private final URI uri;
     private final String organizationId;
     private final SimilarityMeasure similarity;
@@ -112,6 +117,7 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
     private final Boolean dimensionsSetByUser;
 
     public OpenAiEmbeddingsServiceSettings(
+        String modelId,
         @Nullable URI uri,
         @Nullable String organizationId,
         @Nullable SimilarityMeasure similarity,
@@ -120,6 +126,7 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         Boolean dimensionsSetByUser
     ) {
         this.uri = uri;
+        this.modelId = modelId;
         this.organizationId = organizationId;
         this.similarity = similarity;
         this.dimensions = dimensions;
@@ -127,7 +134,8 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         this.dimensionsSetByUser = Objects.requireNonNull(dimensionsSetByUser);
     }
 
-    public OpenAiEmbeddingsServiceSettings(
+    OpenAiEmbeddingsServiceSettings(
+        String modelId,
         @Nullable String uri,
         @Nullable String organizationId,
         @Nullable SimilarityMeasure similarity,
@@ -135,7 +143,7 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         @Nullable Integer maxInputTokens,
         Boolean dimensionsSetByUser
     ) {
-        this(createOptionalUri(uri), organizationId, similarity, dimensions, maxInputTokens, dimensionsSetByUser);
+        this(modelId, createOptionalUri(uri), organizationId, similarity, dimensions, maxInputTokens, dimensionsSetByUser);
     }
 
     public OpenAiEmbeddingsServiceSettings(StreamInput in) throws IOException {
@@ -156,10 +164,23 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         } else {
             dimensionsSetByUser = false;
         }
+        if (in.getTransportVersion().onOrAfter(TransportVersions.ML_MODEL_IN_SERVICE_SETTINGS)) {
+            modelId = in.readString();
+        } else {
+            modelId = "unset";
+        }
     }
 
     private OpenAiEmbeddingsServiceSettings(CommonFields fields, Boolean dimensionsSetByUser) {
-        this(fields.uri, fields.organizationId, fields.similarity, fields.dimensions, fields.maxInputTokens, dimensionsSetByUser);
+        this(
+            fields.modelId,
+            fields.uri,
+            fields.organizationId,
+            fields.similarity,
+            fields.dimensions,
+            fields.maxInputTokens,
+            dimensionsSetByUser
+        );
     }
 
     public URI uri() {
@@ -186,6 +207,10 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         return maxInputTokens;
     }
 
+    public String modelId() {
+        return modelId;
+    }
+
     @Override
     public String getWriteableName() {
         return NAME;
@@ -206,6 +231,7 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
     }
 
     private void toXContentFragmentOfExposedFields(XContentBuilder builder, Params params) throws IOException {
+        builder.field(MODEL_ID, modelId);
         if (uri != null) {
             builder.field(URL, uri.toString());
         }
@@ -254,6 +280,9 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         if (out.getTransportVersion().onOrAfter(TransportVersions.ML_DIMENSIONS_SET_BY_USER_ADDED)) {
             out.writeBoolean(dimensionsSetByUser);
         }
+        if (out.getTransportVersion().onOrAfter(TransportVersions.ML_MODEL_IN_SERVICE_SETTINGS)) {
+            out.writeString(modelId);
+        }
     }
 
     @Override
@@ -262,6 +291,7 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
         if (o == null || getClass() != o.getClass()) return false;
         OpenAiEmbeddingsServiceSettings that = (OpenAiEmbeddingsServiceSettings) o;
         return Objects.equals(uri, that.uri)
+            && Objects.equals(modelId, that.modelId)
             && Objects.equals(organizationId, that.organizationId)
             && Objects.equals(similarity, that.similarity)
             && Objects.equals(dimensions, that.dimensions)
@@ -271,6 +301,6 @@ public class OpenAiEmbeddingsServiceSettings implements ServiceSettings {
 
     @Override
     public int hashCode() {
-        return Objects.hash(uri, organizationId, similarity, dimensions, maxInputTokens, dimensionsSetByUser);
+        return Objects.hash(uri, modelId, organizationId, similarity, dimensions, maxInputTokens, dimensionsSetByUser);
     }
 }

+ 38 - 46
x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsTaskSettings.java

@@ -7,8 +7,6 @@
 
 package org.elasticsearch.xpack.inference.services.openai.embeddings;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 import org.elasticsearch.TransportVersion;
 import org.elasticsearch.TransportVersions;
 import org.elasticsearch.common.ValidationException;
@@ -18,7 +16,6 @@ import org.elasticsearch.core.Nullable;
 import org.elasticsearch.inference.ModelConfigurations;
 import org.elasticsearch.inference.TaskSettings;
 import org.elasticsearch.xcontent.XContentBuilder;
-import org.elasticsearch.xpack.inference.services.ServiceUtils;
 import org.elasticsearch.xpack.inference.services.openai.OpenAiParseContext;
 
 import java.io.IOException;
@@ -30,53 +27,23 @@ import static org.elasticsearch.xpack.inference.services.ServiceUtils.extractOpt
 /**
  * Defines the task settings for the openai service.
  *
- * @param modelId the id of the model to use in the requests to openai
- * @param user an optional unique identifier representing the end-user, which can help OpenAI to monitor and detect abuse
- *             <a href="https://platform.openai.com/docs/api-reference/embeddings/create">see the openai docs for more details</a>
+ * User is an optional unique identifier representing the end-user, which can help OpenAI to monitor and detect abuse
+ *  <a href="https://platform.openai.com/docs/api-reference/embeddings/create">see the openai docs for more details</a>
  */
-public record OpenAiEmbeddingsTaskSettings(String modelId, @Nullable String user) implements TaskSettings {
+public class OpenAiEmbeddingsTaskSettings implements TaskSettings {
 
     public static final String NAME = "openai_embeddings_task_settings";
-    public static final String OLD_MODEL_ID_FIELD = "model";
-    public static final String MODEL_ID = "model_id";
     public static final String USER = "user";
-    private static final String MODEL_DEPRECATION_MESSAGE =
-        "The openai [task_settings.model] field is deprecated. Please use [task_settings.model_id] instead.";
-    private static final Logger logger = LogManager.getLogger(OpenAiEmbeddingsTaskSettings.class);
 
     public static OpenAiEmbeddingsTaskSettings fromMap(Map<String, Object> map, OpenAiParseContext context) {
         ValidationException validationException = new ValidationException();
 
-        String oldModelId = extractOptionalString(map, OLD_MODEL_ID_FIELD, ModelConfigurations.TASK_SETTINGS, validationException);
-        logOldModelDeprecation(oldModelId, context, logger);
-
-        String modelId = extractOptionalString(map, MODEL_ID, ModelConfigurations.TASK_SETTINGS, validationException);
         String user = extractOptionalString(map, USER, ModelConfigurations.TASK_SETTINGS, validationException);
-
-        var modelIdToUse = getModelId(oldModelId, modelId, validationException);
-
         if (validationException.validationErrors().isEmpty() == false) {
             throw validationException;
         }
 
-        return new OpenAiEmbeddingsTaskSettings(modelIdToUse, user);
-    }
-
-    // default for testing
-    static void logOldModelDeprecation(@Nullable String oldModelId, OpenAiParseContext context, Logger logger) {
-        if (OpenAiParseContext.isRequestContext(context) && oldModelId != null) {
-            logger.info(MODEL_DEPRECATION_MESSAGE);
-        }
-    }
-
-    private static String getModelId(@Nullable String oldModelId, @Nullable String modelId, ValidationException validationException) {
-        var modelIdToUse = modelId != null ? modelId : oldModelId;
-
-        if (modelIdToUse == null) {
-            validationException.addValidationError(ServiceUtils.missingSettingErrorMsg(MODEL_ID, ModelConfigurations.TASK_SETTINGS));
-        }
-
-        return modelIdToUse;
+        return new OpenAiEmbeddingsTaskSettings(user);
     }
 
     /**
@@ -90,24 +57,28 @@ public record OpenAiEmbeddingsTaskSettings(String modelId, @Nullable String user
         OpenAiEmbeddingsTaskSettings originalSettings,
         OpenAiEmbeddingsRequestTaskSettings requestSettings
     ) {
-        var modelToUse = requestSettings.modelId() == null ? originalSettings.modelId : requestSettings.modelId();
         var userToUse = requestSettings.user() == null ? originalSettings.user : requestSettings.user();
-
-        return new OpenAiEmbeddingsTaskSettings(modelToUse, userToUse);
+        return new OpenAiEmbeddingsTaskSettings(userToUse);
     }
 
-    public OpenAiEmbeddingsTaskSettings {
-        Objects.requireNonNull(modelId);
+    private final String user;
+
+    public OpenAiEmbeddingsTaskSettings(@Nullable String user) {
+        this.user = user;
     }
 
     public OpenAiEmbeddingsTaskSettings(StreamInput in) throws IOException {
-        this(in.readString(), in.readOptionalString());
+        if (in.getTransportVersion().onOrAfter(TransportVersions.ML_MODEL_IN_SERVICE_SETTINGS)) {
+            this.user = in.readOptionalString();
+        } else {
+            var discard = in.readString();
+            this.user = in.readOptionalString();
+        }
     }
 
     @Override
     public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
         builder.startObject();
-        builder.field(MODEL_ID, modelId);
         if (user != null) {
             builder.field(USER, user);
         }
@@ -115,6 +86,10 @@ public record OpenAiEmbeddingsTaskSettings(String modelId, @Nullable String user
         return builder;
     }
 
+    public String user() {
+        return user;
+    }
+
     @Override
     public String getWriteableName() {
         return NAME;
@@ -127,7 +102,24 @@ public record OpenAiEmbeddingsTaskSettings(String modelId, @Nullable String user
 
     @Override
     public void writeTo(StreamOutput out) throws IOException {
-        out.writeString(modelId);
-        out.writeOptionalString(user);
+        if (out.getTransportVersion().onOrAfter(TransportVersions.ML_MODEL_IN_SERVICE_SETTINGS)) {
+            out.writeOptionalString(user);
+        } else {
+            out.writeString("m"); // write any string
+            out.writeOptionalString(user);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        OpenAiEmbeddingsTaskSettings that = (OpenAiEmbeddingsTaskSettings) o;
+        return Objects.equals(user, that.user);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(user);
     }
 }

+ 4 - 4
x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/external/action/openai/OpenAiActionCreatorTests.java

@@ -92,7 +92,7 @@ public class OpenAiActionCreatorTests extends ESTestCase {
 
             var model = createModel(getUrl(webServer), "org", "secret", "model", "user");
             var actionCreator = new OpenAiActionCreator(sender, createWithEmptySettings(threadPool));
-            var overriddenTaskSettings = getRequestTaskSettingsMap(null, "overridden_user");
+            var overriddenTaskSettings = getRequestTaskSettingsMap("overridden_user");
             var action = actionCreator.create(model, overriddenTaskSettings);
 
             PlainActionFuture<InferenceServiceResults> listener = new PlainActionFuture<>();
@@ -161,7 +161,7 @@ public class OpenAiActionCreatorTests extends ESTestCase {
 
             var model = createModel(getUrl(webServer), "org", "secret", "model", "user");
             var actionCreator = new OpenAiActionCreator(sender, createWithEmptySettings(threadPool));
-            var overriddenTaskSettings = getRequestTaskSettingsMap(null, "overridden_user");
+            var overriddenTaskSettings = getRequestTaskSettingsMap("overridden_user");
             var action = actionCreator.create(model, overriddenTaskSettings);
 
             PlainActionFuture<InferenceServiceResults> listener = new PlainActionFuture<>();
@@ -244,7 +244,7 @@ public class OpenAiActionCreatorTests extends ESTestCase {
 
             var model = createModel(getUrl(webServer), "org", "secret", "model", "user");
             var actionCreator = new OpenAiActionCreator(sender, createWithEmptySettings(threadPool));
-            var overriddenTaskSettings = getRequestTaskSettingsMap(null, "overridden_user");
+            var overriddenTaskSettings = getRequestTaskSettingsMap("overridden_user");
             var action = actionCreator.create(model, overriddenTaskSettings);
 
             PlainActionFuture<InferenceServiceResults> listener = new PlainActionFuture<>();
@@ -312,7 +312,7 @@ public class OpenAiActionCreatorTests extends ESTestCase {
             // truncated to 1 token = 3 characters
             var model = createModel(getUrl(webServer), "org", "secret", "model", "user", 1);
             var actionCreator = new OpenAiActionCreator(sender, createWithEmptySettings(threadPool));
-            var overriddenTaskSettings = getRequestTaskSettingsMap(null, "overridden_user");
+            var overriddenTaskSettings = getRequestTaskSettingsMap("overridden_user");
             var action = actionCreator.create(model, overriddenTaskSettings);
 
             PlainActionFuture<InferenceServiceResults> listener = new PlainActionFuture<>();

+ 116 - 56
x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/OpenAiServiceTests.java

@@ -33,6 +33,7 @@ import org.elasticsearch.xpack.inference.external.http.HttpClientManager;
 import org.elasticsearch.xpack.inference.external.http.sender.HttpRequestSenderFactory;
 import org.elasticsearch.xpack.inference.external.http.sender.Sender;
 import org.elasticsearch.xpack.inference.logging.ThrottlerManager;
+import org.elasticsearch.xpack.inference.services.ServiceFields;
 import org.elasticsearch.xpack.inference.services.openai.embeddings.OpenAiEmbeddingsModel;
 import org.elasticsearch.xpack.inference.services.openai.embeddings.OpenAiEmbeddingsModelTests;
 import org.hamcrest.MatcherAssert;
@@ -60,6 +61,7 @@ import static org.elasticsearch.xpack.inference.services.openai.embeddings.OpenA
 import static org.elasticsearch.xpack.inference.services.settings.DefaultSecretSettingsTests.getSecretSettingsMap;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.instanceOf;
@@ -103,7 +105,7 @@ public class OpenAiServiceTests extends ESTestCase {
                 var embeddingsModel = (OpenAiEmbeddingsModel) model;
                 assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
                 assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-                assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+                assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
                 assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
                 assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
             }, exception -> fail("Unexpected exception: " + exception));
@@ -112,8 +114,8 @@ public class OpenAiServiceTests extends ESTestCase {
                 "id",
                 TaskType.TEXT_EMBEDDING,
                 getRequestConfigMap(
-                    getServiceSettingsMap("url", "org"),
-                    getTaskSettingsMap("model", "user"),
+                    getServiceSettingsMap("model", "url", "org"),
+                    getTaskSettingsMap("user"),
                     getSecretSettingsMap("secret")
                 ),
                 Set.of(),
@@ -141,8 +143,8 @@ public class OpenAiServiceTests extends ESTestCase {
                 "id",
                 TaskType.SPARSE_EMBEDDING,
                 getRequestConfigMap(
-                    getServiceSettingsMap("url", "org"),
-                    getTaskSettingsMap("model", "user"),
+                    getServiceSettingsMap("model", "url", "org"),
+                    getTaskSettingsMap("user"),
                     getSecretSettingsMap("secret")
                 ),
                 Set.of(),
@@ -159,8 +161,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var config = getRequestConfigMap(
-                getServiceSettingsMap("url", "org"),
-                getTaskSettingsMap("model", "user"),
+                getServiceSettingsMap("model", "url", "org"),
+                getTaskSettingsMap("user"),
                 getSecretSettingsMap("secret")
             );
             config.put("extra_key", "value");
@@ -187,10 +189,10 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var serviceSettings = getServiceSettingsMap("url", "org");
+            var serviceSettings = getServiceSettingsMap("model", "url", "org");
             serviceSettings.put("extra_key", "value");
 
-            var config = getRequestConfigMap(serviceSettings, getTaskSettingsMap("model", "user"), getSecretSettingsMap("secret"));
+            var config = getRequestConfigMap(serviceSettings, getTaskSettingsMap("user"), getSecretSettingsMap("secret"));
 
             ActionListener<Model> modelVerificationListener = ActionListener.<Model>wrap((model) -> {
                 fail("Expected exception, but got model: " + model);
@@ -210,10 +212,10 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var taskSettingsMap = getTaskSettingsMap("model", "user");
+            var taskSettingsMap = getTaskSettingsMap("user");
             taskSettingsMap.put("extra_key", "value");
 
-            var config = getRequestConfigMap(getServiceSettingsMap("url", "org"), taskSettingsMap, getSecretSettingsMap("secret"));
+            var config = getRequestConfigMap(getServiceSettingsMap("model", "url", "org"), taskSettingsMap, getSecretSettingsMap("secret"));
 
             ActionListener<Model> modelVerificationListener = ActionListener.<Model>wrap((model) -> {
                 fail("Expected exception, but got model: " + model);
@@ -236,7 +238,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var secretSettingsMap = getSecretSettingsMap("secret");
             secretSettingsMap.put("extra_key", "value");
 
-            var config = getRequestConfigMap(getServiceSettingsMap("url", "org"), getTaskSettingsMap("model", "user"), secretSettingsMap);
+            var config = getRequestConfigMap(getServiceSettingsMap("model", "url", "org"), getTaskSettingsMap("user"), secretSettingsMap);
 
             ActionListener<Model> modelVerificationListener = ActionListener.<Model>wrap((model) -> {
                 fail("Expected exception, but got model: " + model);
@@ -263,7 +265,7 @@ public class OpenAiServiceTests extends ESTestCase {
                 var embeddingsModel = (OpenAiEmbeddingsModel) model;
                 assertNull(embeddingsModel.getServiceSettings().uri());
                 assertNull(embeddingsModel.getServiceSettings().organizationId());
-                assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+                assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
                 assertNull(embeddingsModel.getTaskSettings().user());
                 assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
             }, exception -> fail("Unexpected exception: " + exception));
@@ -271,7 +273,39 @@ public class OpenAiServiceTests extends ESTestCase {
             service.parseRequestConfig(
                 "id",
                 TaskType.TEXT_EMBEDDING,
-                getRequestConfigMap(getServiceSettingsMap(null, null), getTaskSettingsMap("model", null), getSecretSettingsMap("secret")),
+                getRequestConfigMap(getServiceSettingsMap("model", null, null), getTaskSettingsMap(null), getSecretSettingsMap("secret")),
+                Set.of(),
+                modelVerificationListener
+            );
+        }
+    }
+
+    public void testParseRequestConfig_MovesModel() throws IOException {
+        try (
+            var service = new OpenAiService(
+                new SetOnce<>(mock(HttpRequestSenderFactory.class)),
+                new SetOnce<>(createWithEmptySettings(threadPool))
+            )
+        ) {
+            ActionListener<Model> modelVerificationListener = ActionListener.wrap(model -> {
+                assertThat(model, instanceOf(OpenAiEmbeddingsModel.class));
+
+                var embeddingsModel = (OpenAiEmbeddingsModel) model;
+                assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
+                assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
+                assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
+                assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
+                assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
+            }, exception -> fail("Unexpected exception: " + exception));
+
+            service.parseRequestConfig(
+                "id",
+                TaskType.TEXT_EMBEDDING,
+                getRequestConfigMap(
+                    getServiceSettingsMap("model", "url", "org"),
+                    getTaskSettingsMap("user"),
+                    getSecretSettingsMap("secret")
+                ),
                 Set.of(),
                 modelVerificationListener
             );
@@ -286,8 +320,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org", 100, false),
-                getTaskSettingsMap("model", "user"),
+                getServiceSettingsMap("model", "url", "org", 100, false),
+                getTaskSettingsMap("user"),
                 getSecretSettingsMap("secret")
             );
 
@@ -303,7 +337,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
         }
@@ -317,8 +351,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org"),
-                getTaskSettingsMap("model", "user"),
+                getServiceSettingsMap("model", "url", "org"),
+                getTaskSettingsMap("user"),
                 getSecretSettingsMap("secret")
             );
 
@@ -347,8 +381,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap(null, null, null, true),
-                getTaskSettingsMap("model", null),
+                getServiceSettingsMap("model", null, null, null, true),
+                getTaskSettingsMap(null),
                 getSecretSettingsMap("secret")
             );
 
@@ -364,7 +398,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertNull(embeddingsModel.getServiceSettings().uri());
             assertNull(embeddingsModel.getServiceSettings().organizationId());
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertNull(embeddingsModel.getTaskSettings().user());
             assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
         }
@@ -378,8 +412,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org", null, true),
-                getTaskSettingsMap("model", "user"),
+                getServiceSettingsMap("model", "url", "org", null, true),
+                getTaskSettingsMap("user"),
                 getSecretSettingsMap("secret")
             );
             persistedConfig.config().put("extra_key", "value");
@@ -394,9 +428,10 @@ public class OpenAiServiceTests extends ESTestCase {
             assertThat(model, instanceOf(OpenAiEmbeddingsModel.class));
 
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
         }
@@ -413,8 +448,8 @@ public class OpenAiServiceTests extends ESTestCase {
             secretSettingsMap.put("extra_key", "value");
 
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org", null, true),
-                getTaskSettingsMap("model", "user"),
+                getServiceSettingsMap("model", "url", "org", null, true),
+                getTaskSettingsMap("user"),
                 secretSettingsMap
             );
 
@@ -430,7 +465,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
         }
@@ -444,8 +479,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org", null, true),
-                getTaskSettingsMap("model", "user"),
+                getServiceSettingsMap("model", "url", "org", null, true),
+                getTaskSettingsMap("user"),
                 getSecretSettingsMap("secret")
             );
             persistedConfig.secrets.put("extra_key", "value");
@@ -460,9 +495,10 @@ public class OpenAiServiceTests extends ESTestCase {
             assertThat(model, instanceOf(OpenAiEmbeddingsModel.class));
 
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
         }
@@ -475,14 +511,10 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var serviceSettingsMap = getServiceSettingsMap("url", "org", null, true);
+            var serviceSettingsMap = getServiceSettingsMap("model", "url", "org", null, true);
             serviceSettingsMap.put("extra_key", "value");
 
-            var persistedConfig = getPersistedConfigMap(
-                serviceSettingsMap,
-                getTaskSettingsMap("model", "user"),
-                getSecretSettingsMap("secret")
-            );
+            var persistedConfig = getPersistedConfigMap(serviceSettingsMap, getTaskSettingsMap("user"), getSecretSettingsMap("secret"));
 
             var model = service.parsePersistedConfigWithSecrets(
                 "id",
@@ -496,7 +528,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
         }
@@ -509,11 +541,11 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var taskSettingsMap = getTaskSettingsMap("model", "user");
+            var taskSettingsMap = getTaskSettingsMap("user");
             taskSettingsMap.put("extra_key", "value");
 
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org", null, true),
+                getServiceSettingsMap("model", "url", "org", null, true),
                 taskSettingsMap,
                 getSecretSettingsMap("secret")
             );
@@ -530,7 +562,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertThat(embeddingsModel.getSecretSettings().apiKey().toString(), is("secret"));
         }
@@ -544,8 +576,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org", null, true),
-                getTaskSettingsMap("model", "user")
+                getServiceSettingsMap("model", "url", "org", null, true),
+                getTaskSettingsMap("user")
             );
 
             var model = service.parsePersistedConfig("id", TaskType.TEXT_EMBEDDING, persistedConfig.config());
@@ -555,7 +587,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertNull(embeddingsModel.getSecretSettings());
         }
@@ -568,7 +600,7 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var persistedConfig = getPersistedConfigMap(getServiceSettingsMap("url", "org"), getTaskSettingsMap("model", "user"));
+            var persistedConfig = getPersistedConfigMap(getServiceSettingsMap("model", "url", "org"), getTaskSettingsMap("user"));
 
             var thrownException = expectThrows(
                 ElasticsearchStatusException.class,
@@ -589,7 +621,7 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var persistedConfig = getPersistedConfigMap(getServiceSettingsMap(null, null, null, true), getTaskSettingsMap("model", null));
+            var persistedConfig = getPersistedConfigMap(getServiceSettingsMap("model", null, null, null, true), getTaskSettingsMap(null));
 
             var model = service.parsePersistedConfig("id", TaskType.TEXT_EMBEDDING, persistedConfig.config());
 
@@ -598,7 +630,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertNull(embeddingsModel.getServiceSettings().uri());
             assertNull(embeddingsModel.getServiceSettings().organizationId());
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertNull(embeddingsModel.getTaskSettings().user());
             assertNull(embeddingsModel.getSecretSettings());
         }
@@ -612,8 +644,8 @@ public class OpenAiServiceTests extends ESTestCase {
             )
         ) {
             var persistedConfig = getPersistedConfigMap(
-                getServiceSettingsMap("url", "org", null, true),
-                getTaskSettingsMap("model", "user")
+                getServiceSettingsMap("model", "url", "org", null, true),
+                getTaskSettingsMap("user")
             );
             persistedConfig.config().put("extra_key", "value");
 
@@ -624,7 +656,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertNull(embeddingsModel.getSecretSettings());
         }
@@ -637,10 +669,10 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var serviceSettingsMap = getServiceSettingsMap("url", "org", null, true);
+            var serviceSettingsMap = getServiceSettingsMap("model", "url", "org", null, true);
             serviceSettingsMap.put("extra_key", "value");
 
-            var persistedConfig = getPersistedConfigMap(serviceSettingsMap, getTaskSettingsMap("model", "user"));
+            var persistedConfig = getPersistedConfigMap(serviceSettingsMap, getTaskSettingsMap("user"));
 
             var model = service.parsePersistedConfig("id", TaskType.TEXT_EMBEDDING, persistedConfig.config());
 
@@ -649,7 +681,7 @@ public class OpenAiServiceTests extends ESTestCase {
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertNull(embeddingsModel.getSecretSettings());
         }
@@ -662,19 +694,20 @@ public class OpenAiServiceTests extends ESTestCase {
                 new SetOnce<>(createWithEmptySettings(threadPool))
             )
         ) {
-            var taskSettingsMap = getTaskSettingsMap("model", "user");
+            var taskSettingsMap = getTaskSettingsMap("user");
             taskSettingsMap.put("extra_key", "value");
 
-            var persistedConfig = getPersistedConfigMap(getServiceSettingsMap("url", "org", null, true), taskSettingsMap);
+            var persistedConfig = getPersistedConfigMap(getServiceSettingsMap("model", "url", "org", null, true), taskSettingsMap);
 
             var model = service.parsePersistedConfig("id", TaskType.TEXT_EMBEDDING, persistedConfig.config());
 
             assertThat(model, instanceOf(OpenAiEmbeddingsModel.class));
 
             var embeddingsModel = (OpenAiEmbeddingsModel) model;
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getServiceSettings().uri().toString(), is("url"));
             assertThat(embeddingsModel.getServiceSettings().organizationId(), is("org"));
-            assertThat(embeddingsModel.getTaskSettings().modelId(), is("model"));
+            assertThat(embeddingsModel.getServiceSettings().modelId(), is("model"));
             assertThat(embeddingsModel.getTaskSettings().user(), is("user"));
             assertNull(embeddingsModel.getSecretSettings());
         }
@@ -1058,6 +1091,33 @@ public class OpenAiServiceTests extends ESTestCase {
         }
     }
 
+    public void testMoveModelFromTaskToServiceSettings() {
+        var taskSettings = new HashMap<String, Object>();
+        taskSettings.put(ServiceFields.MODEL_ID, "model");
+        var serviceSettings = new HashMap<String, Object>();
+        OpenAiService.moveModelFromTaskToServiceSettings(taskSettings, serviceSettings);
+        assertThat(taskSettings.keySet(), empty());
+        assertEquals("model", serviceSettings.get(ServiceFields.MODEL_ID));
+    }
+
+    public void testMoveModelFromTaskToServiceSettings_OldID() {
+        var taskSettings = new HashMap<String, Object>();
+        taskSettings.put("model", "model");
+        var serviceSettings = new HashMap<String, Object>();
+        OpenAiService.moveModelFromTaskToServiceSettings(taskSettings, serviceSettings);
+        assertThat(taskSettings.keySet(), empty());
+        assertEquals("model", serviceSettings.get(ServiceFields.MODEL_ID));
+    }
+
+    public void testMoveModelFromTaskToServiceSettings_AlreadyMoved() {
+        var taskSettings = new HashMap<String, Object>();
+        var serviceSettings = new HashMap<String, Object>();
+        taskSettings.put(ServiceFields.MODEL_ID, "model");
+        OpenAiService.moveModelFromTaskToServiceSettings(taskSettings, serviceSettings);
+        assertThat(taskSettings.keySet(), empty());
+        assertEquals("model", serviceSettings.get(ServiceFields.MODEL_ID));
+    }
+
     private Map<String, Object> getRequestConfigMap(
         Map<String, Object> serviceSettings,
         Map<String, Object> taskSettings,

+ 9 - 9
x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsModelTests.java

@@ -24,7 +24,7 @@ public class OpenAiEmbeddingsModelTests extends ESTestCase {
 
     public void testOverrideWith_OverridesUser() {
         var model = createModel("url", "org", "api_key", "model_name", null);
-        var requestTaskSettingsMap = getRequestTaskSettingsMap(null, "user_override");
+        var requestTaskSettingsMap = getRequestTaskSettingsMap("user_override");
 
         var overriddenModel = OpenAiEmbeddingsModel.of(model, requestTaskSettingsMap);
 
@@ -58,8 +58,8 @@ public class OpenAiEmbeddingsModelTests extends ESTestCase {
             "id",
             TaskType.TEXT_EMBEDDING,
             "service",
-            new OpenAiEmbeddingsServiceSettings(url, org, SimilarityMeasure.DOT_PRODUCT, 1536, null, false),
-            new OpenAiEmbeddingsTaskSettings(modelName, user),
+            new OpenAiEmbeddingsServiceSettings(modelName, url, org, SimilarityMeasure.DOT_PRODUCT, 1536, null, false),
+            new OpenAiEmbeddingsTaskSettings(user),
             new DefaultSecretSettings(new SecureString(apiKey.toCharArray()))
         );
     }
@@ -76,8 +76,8 @@ public class OpenAiEmbeddingsModelTests extends ESTestCase {
             "id",
             TaskType.TEXT_EMBEDDING,
             "service",
-            new OpenAiEmbeddingsServiceSettings(url, org, SimilarityMeasure.DOT_PRODUCT, 1536, tokenLimit, false),
-            new OpenAiEmbeddingsTaskSettings(modelName, user),
+            new OpenAiEmbeddingsServiceSettings(modelName, url, org, SimilarityMeasure.DOT_PRODUCT, 1536, tokenLimit, false),
+            new OpenAiEmbeddingsTaskSettings(user),
             new DefaultSecretSettings(new SecureString(apiKey.toCharArray()))
         );
     }
@@ -95,8 +95,8 @@ public class OpenAiEmbeddingsModelTests extends ESTestCase {
             "id",
             TaskType.TEXT_EMBEDDING,
             "service",
-            new OpenAiEmbeddingsServiceSettings(url, org, SimilarityMeasure.DOT_PRODUCT, dimensions, tokenLimit, false),
-            new OpenAiEmbeddingsTaskSettings(modelName, user),
+            new OpenAiEmbeddingsServiceSettings(modelName, url, org, SimilarityMeasure.DOT_PRODUCT, dimensions, tokenLimit, false),
+            new OpenAiEmbeddingsTaskSettings(user),
             new DefaultSecretSettings(new SecureString(apiKey.toCharArray()))
         );
     }
@@ -116,8 +116,8 @@ public class OpenAiEmbeddingsModelTests extends ESTestCase {
             "id",
             TaskType.TEXT_EMBEDDING,
             "service",
-            new OpenAiEmbeddingsServiceSettings(url, org, similarityMeasure, dimensions, tokenLimit, dimensionsSetByUser),
-            new OpenAiEmbeddingsTaskSettings(modelName, user),
+            new OpenAiEmbeddingsServiceSettings(modelName, url, org, similarityMeasure, dimensions, tokenLimit, dimensionsSetByUser),
+            new OpenAiEmbeddingsTaskSettings(user),
             new DefaultSecretSettings(new SecureString(apiKey.toCharArray()))
         );
     }

+ 2 - 50
x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsRequestTaskSettingsTests.java

@@ -18,70 +18,22 @@ import static org.hamcrest.Matchers.is;
 public class OpenAiEmbeddingsRequestTaskSettingsTests extends ESTestCase {
     public void testFromMap_ReturnsEmptySettings_WhenTheMapIsEmpty() {
         var settings = OpenAiEmbeddingsRequestTaskSettings.fromMap(new HashMap<>(Map.of()));
-
-        assertNull(settings.modelId());
         assertNull(settings.user());
     }
 
     public void testFromMap_ReturnsEmptySettings_WhenTheMapDoesNotContainTheFields() {
         var settings = OpenAiEmbeddingsRequestTaskSettings.fromMap(new HashMap<>(Map.of("key", "model")));
-
-        assertNull(settings.modelId());
         assertNull(settings.user());
     }
 
-    public void testFromMap_ReturnsEmptyModel_WhenTheMapDoesNotContainThatField() {
+    public void testFromMap_ReturnsUser() {
         var settings = OpenAiEmbeddingsRequestTaskSettings.fromMap(new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.USER, "user")));
-
-        assertNull(settings.modelId());
         assertThat(settings.user(), is("user"));
     }
 
-    public void testFromMap_ReturnsEmptyUser_WhenTheDoesMapNotContainThatField() {
-        var settings = OpenAiEmbeddingsRequestTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model"))
-        );
-
-        assertNull(settings.user());
-        assertThat(settings.modelId(), is("model"));
-    }
-
-    public void testFromMap_PrefersModelId_OverModel() {
-        var settings = OpenAiEmbeddingsRequestTaskSettings.fromMap(
-            new HashMap<>(
-                Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model", OpenAiEmbeddingsTaskSettings.MODEL_ID, "model_id")
-            )
-        );
-
-        assertNull(settings.user());
-        assertThat(settings.modelId(), is("model_id"));
-    }
-
-    public static Map<String, Object> getRequestTaskSettingsMap(@Nullable String model, @Nullable String user) {
-        var map = new HashMap<String, Object>();
-
-        if (model != null) {
-            map.put(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, model);
-        }
-
-        if (user != null) {
-            map.put(OpenAiEmbeddingsTaskSettings.USER, user);
-        }
-
-        return map;
-    }
-
-    public static Map<String, Object> getRequestTaskSettingsMap(@Nullable String model, @Nullable String modelId, @Nullable String user) {
+    public static Map<String, Object> getRequestTaskSettingsMap(@Nullable String user) {
         var map = new HashMap<String, Object>();
 
-        if (model != null) {
-            map.put(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, model);
-        }
-
-        if (modelId != null) {
-            map.put(OpenAiEmbeddingsTaskSettings.MODEL_ID, model);
-        }
-
         if (user != null) {
             map.put(OpenAiEmbeddingsTaskSettings.USER, user);
         }

+ 48 - 19
x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsServiceSettingsTests.java

@@ -44,6 +44,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
     }
 
     private static OpenAiEmbeddingsServiceSettings createRandom(String url) {
+        var modelId = randomAlphaOfLength(8);
         var organizationId = randomBoolean() ? randomAlphaOfLength(15) : null;
         SimilarityMeasure similarityMeasure = null;
         Integer dims = null;
@@ -54,6 +55,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         }
         Integer maxInputTokens = randomBoolean() ? null : randomIntBetween(128, 256);
         return new OpenAiEmbeddingsServiceSettings(
+            modelId,
             ServiceUtils.createUri(url),
             organizationId,
             similarityMeasure,
@@ -64,6 +66,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
     }
 
     public void testFromMap_Request_CreatesSettingsCorrectly() {
+        var modelId = "model-foo";
         var url = "https://www.abc.com";
         var org = "organization";
         var similarity = SimilarityMeasure.DOT_PRODUCT.toString();
@@ -72,6 +75,8 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         var serviceSettings = OpenAiEmbeddingsServiceSettings.fromMap(
             new HashMap<>(
                 Map.of(
+                    ServiceFields.MODEL_ID,
+                    modelId,
                     ServiceFields.URL,
                     url,
                     OpenAiEmbeddingsServiceSettings.ORGANIZATION,
@@ -91,6 +96,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
             serviceSettings,
             is(
                 new OpenAiEmbeddingsServiceSettings(
+                    modelId,
                     ServiceUtils.createUri(url),
                     org,
                     SimilarityMeasure.DOT_PRODUCT,
@@ -103,6 +109,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
     }
 
     public void testFromMap_Request_DimensionsSetByUser_IsFalse_WhenDimensionsAreNotPresent() {
+        var modelId = "model-foo";
         var url = "https://www.abc.com";
         var org = "organization";
         var similarity = SimilarityMeasure.DOT_PRODUCT.toString();
@@ -110,6 +117,8 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         var serviceSettings = OpenAiEmbeddingsServiceSettings.fromMap(
             new HashMap<>(
                 Map.of(
+                    ServiceFields.MODEL_ID,
+                    modelId,
                     ServiceFields.URL,
                     url,
                     OpenAiEmbeddingsServiceSettings.ORGANIZATION,
@@ -127,6 +136,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
             serviceSettings,
             is(
                 new OpenAiEmbeddingsServiceSettings(
+                    modelId,
                     ServiceUtils.createUri(url),
                     org,
                     SimilarityMeasure.DOT_PRODUCT,
@@ -139,6 +149,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
     }
 
     public void testFromMap_Persistent_CreatesSettingsCorrectly() {
+        var modelId = "model-foo";
         var url = "https://www.abc.com";
         var org = "organization";
         var similarity = SimilarityMeasure.DOT_PRODUCT.toString();
@@ -147,6 +158,8 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         var serviceSettings = OpenAiEmbeddingsServiceSettings.fromMap(
             new HashMap<>(
                 Map.of(
+                    ServiceFields.MODEL_ID,
+                    modelId,
                     ServiceFields.URL,
                     url,
                     OpenAiEmbeddingsServiceSettings.ORGANIZATION,
@@ -168,6 +181,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
             serviceSettings,
             is(
                 new OpenAiEmbeddingsServiceSettings(
+                    modelId,
                     ServiceUtils.createUri(url),
                     org,
                     SimilarityMeasure.DOT_PRODUCT,
@@ -181,17 +195,20 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
 
     public void testFromMap_PersistentContext_DoesNotThrowException_WhenDimensionsIsNull() {
         var settings = OpenAiEmbeddingsServiceSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsServiceSettings.DIMENSIONS_SET_BY_USER, true)),
+            new HashMap<>(Map.of(OpenAiEmbeddingsServiceSettings.DIMENSIONS_SET_BY_USER, true, ServiceFields.MODEL_ID, "m")),
             OpenAiParseContext.PERSISTENT
         );
 
-        assertThat(settings, is(new OpenAiEmbeddingsServiceSettings((URI) null, null, null, null, null, true)));
+        assertThat(settings, is(new OpenAiEmbeddingsServiceSettings("m", (URI) null, null, null, null, null, true)));
     }
 
     public void testFromMap_PersistentContext_ThrowsException_WhenDimensionsSetByUserIsNull() {
         var exception = expectThrows(
             ValidationException.class,
-            () -> OpenAiEmbeddingsServiceSettings.fromMap(new HashMap<>(Map.of(ServiceFields.DIMENSIONS, 1)), OpenAiParseContext.PERSISTENT)
+            () -> OpenAiEmbeddingsServiceSettings.fromMap(
+                new HashMap<>(Map.of(ServiceFields.DIMENSIONS, 1, ServiceFields.MODEL_ID, "m")),
+                OpenAiParseContext.PERSISTENT
+            )
         );
 
         assertThat(
@@ -202,17 +219,21 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
 
     public void testFromMap_MissingUrl_DoesNotThrowException() {
         var serviceSettings = OpenAiEmbeddingsServiceSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsServiceSettings.ORGANIZATION, "org")),
+            new HashMap<>(Map.of(ServiceFields.MODEL_ID, "m", OpenAiEmbeddingsServiceSettings.ORGANIZATION, "org")),
             OpenAiParseContext.REQUEST
         );
         assertNull(serviceSettings.uri());
+        assertThat(serviceSettings.modelId(), is("m"));
         assertThat(serviceSettings.organizationId(), is("org"));
     }
 
     public void testFromMap_EmptyUrl_ThrowsError() {
         var thrownException = expectThrows(
             ValidationException.class,
-            () -> OpenAiEmbeddingsServiceSettings.fromMap(new HashMap<>(Map.of(ServiceFields.URL, "")), OpenAiParseContext.REQUEST)
+            () -> OpenAiEmbeddingsServiceSettings.fromMap(
+                new HashMap<>(Map.of(ServiceFields.URL, "", ServiceFields.MODEL_ID, "m")),
+                OpenAiParseContext.REQUEST
+            )
         );
 
         assertThat(
@@ -227,7 +248,10 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
     }
 
     public void testFromMap_MissingOrganization_DoesNotThrowException() {
-        var serviceSettings = OpenAiEmbeddingsServiceSettings.fromMap(new HashMap<>(), OpenAiParseContext.REQUEST);
+        var serviceSettings = OpenAiEmbeddingsServiceSettings.fromMap(
+            new HashMap<>(Map.of(ServiceFields.MODEL_ID, "m")),
+            OpenAiParseContext.REQUEST
+        );
         assertNull(serviceSettings.uri());
         assertNull(serviceSettings.organizationId());
     }
@@ -236,7 +260,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         var thrownException = expectThrows(
             ValidationException.class,
             () -> OpenAiEmbeddingsServiceSettings.fromMap(
-                new HashMap<>(Map.of(OpenAiEmbeddingsServiceSettings.ORGANIZATION, "")),
+                new HashMap<>(Map.of(OpenAiEmbeddingsServiceSettings.ORGANIZATION, "", ServiceFields.MODEL_ID, "m")),
                 OpenAiParseContext.REQUEST
             )
         );
@@ -256,7 +280,10 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         var url = "https://www.abc^.com";
         var thrownException = expectThrows(
             ValidationException.class,
-            () -> OpenAiEmbeddingsServiceSettings.fromMap(new HashMap<>(Map.of(ServiceFields.URL, url)), OpenAiParseContext.REQUEST)
+            () -> OpenAiEmbeddingsServiceSettings.fromMap(
+                new HashMap<>(Map.of(ServiceFields.URL, url, ServiceFields.MODEL_ID, "m")),
+                OpenAiParseContext.REQUEST
+            )
         );
 
         assertThat(
@@ -270,7 +297,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         var thrownException = expectThrows(
             ValidationException.class,
             () -> OpenAiEmbeddingsServiceSettings.fromMap(
-                new HashMap<>(Map.of(ServiceFields.SIMILARITY, similarity)),
+                new HashMap<>(Map.of(ServiceFields.SIMILARITY, similarity, ServiceFields.MODEL_ID, "m")),
                 OpenAiParseContext.REQUEST
             )
         );
@@ -279,41 +306,41 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
     }
 
     public void testToXContent_WritesDimensionsSetByUserTrue() throws IOException {
-        var entity = new OpenAiEmbeddingsServiceSettings("url", "org", null, null, null, true);
+        var entity = new OpenAiEmbeddingsServiceSettings("model", "url", "org", null, null, null, true);
 
         XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
         entity.toXContent(builder, null);
         String xContentResult = Strings.toString(builder);
 
         assertThat(xContentResult, CoreMatchers.is("""
-            {"url":"url","organization_id":"org","dimensions_set_by_user":true}"""));
+            {"model_id":"model","url":"url","organization_id":"org","dimensions_set_by_user":true}"""));
     }
 
     public void testToXContent_WritesDimensionsSetByUserFalse() throws IOException {
-        var entity = new OpenAiEmbeddingsServiceSettings("url", "org", null, null, null, false);
+        var entity = new OpenAiEmbeddingsServiceSettings("model", "url", "org", null, null, null, false);
 
         XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
         entity.toXContent(builder, null);
         String xContentResult = Strings.toString(builder);
 
         assertThat(xContentResult, CoreMatchers.is("""
-            {"url":"url","organization_id":"org","dimensions_set_by_user":false}"""));
+            {"model_id":"model","url":"url","organization_id":"org","dimensions_set_by_user":false}"""));
     }
 
     public void testToXContent_WritesAllValues() throws IOException {
-        var entity = new OpenAiEmbeddingsServiceSettings("url", "org", SimilarityMeasure.DOT_PRODUCT, 1, 2, false);
+        var entity = new OpenAiEmbeddingsServiceSettings("model", "url", "org", SimilarityMeasure.DOT_PRODUCT, 1, 2, false);
 
         XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
         entity.toXContent(builder, null);
         String xContentResult = Strings.toString(builder);
 
         assertThat(xContentResult, CoreMatchers.is("""
-            {"url":"url","organization_id":"org","similarity":"dot_product",""" + """
+            {"model_id":"model","url":"url","organization_id":"org","similarity":"dot_product",""" + """
             "dimensions":1,"max_input_tokens":2,"dimensions_set_by_user":false}"""));
     }
 
     public void testToFilteredXContent_WritesAllValues_ExceptDimensionsSetByUser() throws IOException {
-        var entity = new OpenAiEmbeddingsServiceSettings("url", "org", SimilarityMeasure.DOT_PRODUCT, 1, 2, false);
+        var entity = new OpenAiEmbeddingsServiceSettings("model", "url", "org", SimilarityMeasure.DOT_PRODUCT, 1, 2, false);
 
         XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
         var filteredXContent = entity.getFilteredXContentObject();
@@ -321,7 +348,7 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         String xContentResult = Strings.toString(builder);
 
         assertThat(xContentResult, CoreMatchers.is("""
-            {"url":"url","organization_id":"org","similarity":"dot_product",""" + """
+            {"model_id":"model","url":"url","organization_id":"org","similarity":"dot_product",""" + """
             "dimensions":1,"max_input_tokens":2}"""));
     }
 
@@ -340,9 +367,9 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
         return createRandomWithNonNullUrl();
     }
 
-    public static Map<String, Object> getServiceSettingsMap(@Nullable String url, @Nullable String org) {
+    public static Map<String, Object> getServiceSettingsMap(String modelId, @Nullable String url, @Nullable String org) {
         var map = new HashMap<String, Object>();
-
+        map.put(ServiceFields.MODEL_ID, modelId);
         if (url != null) {
             map.put(ServiceFields.URL, url);
         }
@@ -354,12 +381,14 @@ public class OpenAiEmbeddingsServiceSettingsTests extends AbstractWireSerializin
     }
 
     public static Map<String, Object> getServiceSettingsMap(
+        String model,
         @Nullable String url,
         @Nullable String org,
         @Nullable Integer dimensions,
         @Nullable Boolean dimensionsSetByUser
     ) {
         var map = new HashMap<String, Object>();
+        map.put(ServiceFields.MODEL_ID, model);
 
         if (url != null) {
             map.put(ServiceFields.URL, url);

+ 22 - 142
x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/openai/embeddings/OpenAiEmbeddingsTaskSettingsTests.java

@@ -7,17 +7,12 @@
 
 package org.elasticsearch.xpack.inference.services.openai.embeddings;
 
-import org.apache.logging.log4j.Logger;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.ValidationException;
 import org.elasticsearch.common.io.stream.Writeable;
 import org.elasticsearch.core.Nullable;
 import org.elasticsearch.test.AbstractWireSerializingTestCase;
-import org.elasticsearch.xcontent.XContentBuilder;
-import org.elasticsearch.xcontent.XContentFactory;
-import org.elasticsearch.xcontent.XContentType;
 import org.elasticsearch.xpack.inference.services.openai.OpenAiParseContext;
-import org.hamcrest.CoreMatchers;
 import org.hamcrest.MatcherAssert;
 
 import java.io.IOException;
@@ -25,17 +20,11 @@ import java.util.HashMap;
 import java.util.Map;
 
 import static org.hamcrest.Matchers.is;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 public class OpenAiEmbeddingsTaskSettingsTests extends AbstractWireSerializingTestCase<OpenAiEmbeddingsTaskSettings> {
 
     public static OpenAiEmbeddingsTaskSettings createRandomWithUser() {
-        return new OpenAiEmbeddingsTaskSettings(randomAlphaOfLength(15), randomAlphaOfLength(15));
+        return new OpenAiEmbeddingsTaskSettings(randomAlphaOfLength(15));
     }
 
     /**
@@ -43,81 +32,42 @@ public class OpenAiEmbeddingsTaskSettingsTests extends AbstractWireSerializingTe
      */
     public static OpenAiEmbeddingsTaskSettings createRandom() {
         var user = randomBoolean() ? randomAlphaOfLength(15) : null;
-        return new OpenAiEmbeddingsTaskSettings(randomAlphaOfLength(15), user);
+        return new OpenAiEmbeddingsTaskSettings(user);
     }
 
-    public void testFromMap_MissingModel_ThrowException() {
+    public void testFromMap_WithUser() {
+        assertEquals(
+            new OpenAiEmbeddingsTaskSettings("user"),
+            OpenAiEmbeddingsTaskSettings.fromMap(
+                new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.USER, "user")),
+                OpenAiParseContext.REQUEST
+            )
+        );
+    }
+
+    public void testFromMap_UserIsEmptyString() {
         var thrownException = expectThrows(
             ValidationException.class,
             () -> OpenAiEmbeddingsTaskSettings.fromMap(
-                new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.USER, "user")),
+                new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.USER, "")),
                 OpenAiParseContext.REQUEST
             )
         );
 
         MatcherAssert.assertThat(
             thrownException.getMessage(),
-            is(
-                Strings.format(
-                    "Validation Failed: 1: [task_settings] does not contain the required setting [%s];",
-                    OpenAiEmbeddingsTaskSettings.MODEL_ID
-                )
-            )
-        );
-    }
-
-    public void testFromMap_CreatesWithModelAndUser() {
-        var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model", OpenAiEmbeddingsTaskSettings.USER, "user")),
-            OpenAiParseContext.PERSISTENT
-        );
-
-        MatcherAssert.assertThat(taskSettings.modelId(), is("model"));
-        MatcherAssert.assertThat(taskSettings.user(), is("user"));
-    }
-
-    public void testFromMap_CreatesWithModelId() {
-        var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.MODEL_ID, "model", OpenAiEmbeddingsTaskSettings.USER, "user")),
-            OpenAiParseContext.PERSISTENT
-        );
-
-        MatcherAssert.assertThat(taskSettings.modelId(), is("model"));
-        MatcherAssert.assertThat(taskSettings.user(), is("user"));
-    }
-
-    public void testFromMap_PrefersModelId_OverModel() {
-        var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(
-                Map.of(
-                    OpenAiEmbeddingsTaskSettings.MODEL_ID,
-                    "model",
-                    OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD,
-                    "old_model",
-                    OpenAiEmbeddingsTaskSettings.USER,
-                    "user"
-                )
-            ),
-            OpenAiParseContext.PERSISTENT
+            is(Strings.format("Validation Failed: 1: [task_settings] Invalid value empty string. [user] must be a non-empty string;"))
         );
-
-        MatcherAssert.assertThat(taskSettings.modelId(), is("model"));
-        MatcherAssert.assertThat(taskSettings.user(), is("user"));
     }
 
     public void testFromMap_MissingUser_DoesNotThrowException() {
-        var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model")),
-            OpenAiParseContext.PERSISTENT
-        );
-
-        MatcherAssert.assertThat(taskSettings.modelId(), is("model"));
+        var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(new HashMap<>(Map.of()), OpenAiParseContext.PERSISTENT);
         assertNull(taskSettings.user());
     }
 
     public void testOverrideWith_KeepsOriginalValuesWithOverridesAreNull() {
         var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model", OpenAiEmbeddingsTaskSettings.USER, "user")),
+            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.USER, "user")),
             OpenAiParseContext.PERSISTENT
         );
 
@@ -127,86 +77,16 @@ public class OpenAiEmbeddingsTaskSettingsTests extends AbstractWireSerializingTe
 
     public void testOverrideWith_UsesOverriddenSettings() {
         var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model", OpenAiEmbeddingsTaskSettings.USER, "user")),
-            OpenAiParseContext.PERSISTENT
-        );
-
-        var requestTaskSettings = OpenAiEmbeddingsRequestTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model2", OpenAiEmbeddingsTaskSettings.USER, "user2"))
-        );
-
-        var overriddenTaskSettings = OpenAiEmbeddingsTaskSettings.of(taskSettings, requestTaskSettings);
-        MatcherAssert.assertThat(overriddenTaskSettings, is(new OpenAiEmbeddingsTaskSettings("model2", "user2")));
-    }
-
-    public void testOverrideWith_UsesOverriddenSettings_UsesModel2_FromModelIdField() {
-        var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model", OpenAiEmbeddingsTaskSettings.USER, "user")),
-            OpenAiParseContext.PERSISTENT
-        );
-
-        var requestTaskSettings = OpenAiEmbeddingsRequestTaskSettings.fromMap(
-            new HashMap<>(
-                Map.of(
-                    OpenAiEmbeddingsTaskSettings.MODEL_ID,
-                    "model2",
-                    OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD,
-                    "model3",
-                    OpenAiEmbeddingsTaskSettings.USER,
-                    "user2"
-                )
-            )
-        );
-
-        var overriddenTaskSettings = OpenAiEmbeddingsTaskSettings.of(taskSettings, requestTaskSettings);
-        MatcherAssert.assertThat(overriddenTaskSettings, is(new OpenAiEmbeddingsTaskSettings("model2", "user2")));
-    }
-
-    public void testOverrideWith_UsesOnlyNonNullModelSetting() {
-        var taskSettings = OpenAiEmbeddingsTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model", OpenAiEmbeddingsTaskSettings.USER, "user")),
+            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.USER, "user")),
             OpenAiParseContext.PERSISTENT
         );
 
         var requestTaskSettings = OpenAiEmbeddingsRequestTaskSettings.fromMap(
-            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, "model2"))
+            new HashMap<>(Map.of(OpenAiEmbeddingsTaskSettings.USER, "user2"))
         );
 
         var overriddenTaskSettings = OpenAiEmbeddingsTaskSettings.of(taskSettings, requestTaskSettings);
-        MatcherAssert.assertThat(overriddenTaskSettings, is(new OpenAiEmbeddingsTaskSettings("model2", "user")));
-    }
-
-    public void testXContent_WritesModelId() throws IOException {
-        var entity = new OpenAiEmbeddingsTaskSettings("modelId", null);
-
-        XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
-        entity.toXContent(builder, null);
-        String xContentResult = Strings.toString(builder);
-
-        assertThat(xContentResult, CoreMatchers.is("""
-            {"model_id":"modelId"}"""));
-    }
-
-    public void testLogModelDeprecation_CallsInfo_WhenContextIsRequest_AndOldModelIdIsDefined() {
-        var mockLogger = mock(Logger.class);
-
-        OpenAiEmbeddingsTaskSettings.logOldModelDeprecation("model", OpenAiParseContext.REQUEST, mockLogger);
-        verify(mockLogger, times(1)).info(anyString());
-        verifyNoMoreInteractions(mockLogger);
-    }
-
-    public void testLogModelDeprecation_DoesNotCallInfo_WhenContextIsRequest_AndOldModelIdIsNull() {
-        var mockLogger = mock(Logger.class);
-
-        OpenAiEmbeddingsTaskSettings.logOldModelDeprecation(null, OpenAiParseContext.PERSISTENT, mockLogger);
-        verifyNoInteractions(mockLogger);
-    }
-
-    public void testLogModelDeprecation_DoesNotCallInfo_WhenContextIsPersistent_AndOldModelIdIsDefined() {
-        var mockLogger = mock(Logger.class);
-
-        OpenAiEmbeddingsTaskSettings.logOldModelDeprecation("model", OpenAiParseContext.PERSISTENT, mockLogger);
-        verifyNoInteractions(mockLogger);
+        MatcherAssert.assertThat(overriddenTaskSettings, is(new OpenAiEmbeddingsTaskSettings("user2")));
     }
 
     @Override
@@ -224,8 +104,8 @@ public class OpenAiEmbeddingsTaskSettingsTests extends AbstractWireSerializingTe
         return createRandomWithUser();
     }
 
-    public static Map<String, Object> getTaskSettingsMap(String model, @Nullable String user) {
-        var map = new HashMap<String, Object>(Map.of(OpenAiEmbeddingsTaskSettings.OLD_MODEL_ID_FIELD, model));
+    public static Map<String, Object> getTaskSettingsMap(@Nullable String user) {
+        var map = new HashMap<String, Object>();
 
         if (user != null) {
             map.put(OpenAiEmbeddingsTaskSettings.USER, user);