feat: Enhance agent functionality with ModelProfile integration
- Added ModelProfile to the Agent record to allow for more detailed model configurations. - Updated AgentExecutorService to utilize ModelProfile when fetching clients. - Modified BaseAgent interface to include a default modelProfile method. - Adjusted Client interface and implementations to support ModelProfile in conversation and streaming methods. - Enhanced ClientService to retrieve clients based on ModelProfile. - Updated CompletionInterceptor to work with ModelProfile instead of ModelCategory. - Refactored AzureAiClient and other client implementations to handle ModelProfile correctly. - Changed CachedClient to cache responses based on ModelProfile. - Updated ModelCategory to a record type for better validation and immutability. - Enhanced ModelProfile with validation checks for its fields. - Updated ModelProfileService to support fetching profiles based on ModelCategory. - Adjusted tests to accommodate changes in Agent and ModelProfile. - Updated properties files to include category information for model profiles.
This commit is contained in:
@@ -1,23 +1,23 @@
|
||||
package de.extio.lmlib.profile;
|
||||
|
||||
public enum ModelCategory {
|
||||
public record ModelCategory(String modelProfile, String shortName) {
|
||||
|
||||
public static final ModelCategory SMALL = new ModelCategory("profile.model.s", "S");
|
||||
public static final ModelCategory MEDIUM = new ModelCategory("profile.model.m", "M");
|
||||
public static final ModelCategory LARGE = new ModelCategory("profile.model.l", "L");
|
||||
public static final ModelCategory XLARGE = new ModelCategory("profile.model.xl", "XL");
|
||||
public static final ModelCategory HOT = new ModelCategory("profile.model.hot", "HOT");
|
||||
public static final ModelCategory COLD = new ModelCategory("profile.model.cold", "COLD");
|
||||
|
||||
SMALL("profile.model.s", "S"),
|
||||
MEDIUM("profile.model.m", "M"),
|
||||
LARGE("profile.model.l", "L"),
|
||||
XLARGE("profile.model.xl", "XL"),
|
||||
HOT("profile.model.hot", "HOT"),
|
||||
COLD("profile.model.cold", "COLD");
|
||||
|
||||
private final String modelProfile;
|
||||
|
||||
private final String shortName;
|
||||
|
||||
ModelCategory(final String modelProfile, final String shortName) {
|
||||
this.modelProfile = modelProfile;
|
||||
this.shortName = shortName;
|
||||
public ModelCategory {
|
||||
if (modelProfile == null || modelProfile.isEmpty()) {
|
||||
throw new IllegalArgumentException("modelProfile must not be null or empty");
|
||||
}
|
||||
if (shortName == null || shortName.isEmpty()) {
|
||||
throw new IllegalArgumentException("shortName must not be null or empty");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getModelProfile() {
|
||||
return this.modelProfile;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,32 @@ public record ModelProfile(
|
||||
BigDecimal costInToken,
|
||||
BigDecimal costOutToken,
|
||||
String reasoningEffort,
|
||||
String reasoningSummaryDetails) {
|
||||
String reasoningSummaryDetails,
|
||||
String category) {
|
||||
|
||||
public ModelProfile {
|
||||
if (maxTokens <= 0) {
|
||||
throw new IllegalArgumentException("Model profile maxTokens must be greater than zero");
|
||||
}
|
||||
if (maxContextLength <= 0) {
|
||||
throw new IllegalArgumentException("Model profile maxContextLength must be greater than zero");
|
||||
}
|
||||
if (temperature <= 0) {
|
||||
throw new IllegalArgumentException("Model profile temperature must be greater than zero");
|
||||
}
|
||||
if (topP <= 0) {
|
||||
throw new IllegalArgumentException("Model profile topP must be greater than zero");
|
||||
}
|
||||
if (modelProvider == null) {
|
||||
throw new IllegalArgumentException("Model profile modelProvider is mandatory and cannot be null");
|
||||
}
|
||||
if (url == null || url.isBlank()) {
|
||||
throw new IllegalArgumentException("Model profile url is mandatory and cannot be empty");
|
||||
}
|
||||
if (category == null || category.isBlank()) {
|
||||
throw new IllegalArgumentException("Model profile category is mandatory and cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
public static enum ModelProvider {
|
||||
AZURE_AI, OAI_TEXT_COMPLETION, OAI_CHAT_COMPLETION;
|
||||
|
||||
@@ -33,8 +33,12 @@ public class ModelProfileService {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Cacheable("modelProfiles")
|
||||
public ModelProfile getModelProfile(final String modelProfile) {
|
||||
return this.getModelProfile(modelProfile, null);
|
||||
}
|
||||
|
||||
@Cacheable("modelProfiles")
|
||||
public ModelProfile getModelProfile(final String modelProfile, final ModelCategory modelCategory) {
|
||||
final var modelName = this.environment.getProperty(modelProfile);
|
||||
if (modelName == null) {
|
||||
throw new IllegalStateException("No model found for profile: " + modelProfile);
|
||||
@@ -45,7 +49,7 @@ public class ModelProfileService {
|
||||
|
||||
final var promptTemplate = Optional.ofNullable(resource.getProperty("prompts"))
|
||||
.map(Object::toString)
|
||||
.orElseThrow(() -> new IllegalStateException("No model name found for model: " + modelName));
|
||||
.orElse("");
|
||||
final var tokenEncoding = Optional.ofNullable(resource.getProperty("tokenEncoding"))
|
||||
.map(Object::toString)
|
||||
.orElse("none");
|
||||
@@ -71,7 +75,7 @@ public class ModelProfileService {
|
||||
.orElseThrow(() -> new IllegalStateException("No model modelProvider found for model: " + modelName));
|
||||
final var modelNameCfg = Optional.ofNullable(resource.getProperty("modelName"))
|
||||
.map(Object::toString)
|
||||
.orElseThrow(() -> new IllegalStateException("No model modelName found for model: " + modelName));
|
||||
.orElse(null);
|
||||
final var url = Optional.ofNullable(resource.getProperty("url"))
|
||||
.map(Object::toString)
|
||||
.orElseThrow(() -> new IllegalStateException("No model url found for model: " + modelName));
|
||||
@@ -91,6 +95,10 @@ public class ModelProfileService {
|
||||
final var reasoningSummaryDetails = Optional.ofNullable(resource.getProperty("reasoningSummaryDetails"))
|
||||
.map(Object::toString)
|
||||
.orElse(null);
|
||||
final var category = Optional
|
||||
.ofNullable(modelCategory)
|
||||
.map(ModelCategory::getShortName)
|
||||
.orElseGet(() -> (String)resource.getProperty("category"));
|
||||
|
||||
//To resolve api key placeholder ${apikey} in model-name.properties file
|
||||
String apiKey = null;
|
||||
@@ -100,10 +108,10 @@ public class ModelProfileService {
|
||||
}
|
||||
apiKey = Optional.ofNullable(this.environment.getProperty("apiKey"))
|
||||
.map(Object::toString)
|
||||
.orElseThrow(() -> new IllegalStateException("No model apiKey found for model: " + modelName));
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
return new ModelProfile(promptTemplate, tokenEncoding, maxTokens, maxContextLength, temperature, topP, modelProvider, modelNameCfg, url, apiKey, costInToken, costOutToken, reasoningEffort, reasoningSummaryDetails);
|
||||
return new ModelProfile(promptTemplate, tokenEncoding, maxTokens, maxContextLength, temperature, topP, modelProvider, modelNameCfg, url, apiKey, costInToken, costOutToken, reasoningEffort, reasoningSummaryDetails, category);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
LOGGER.error("Error while reading model profile", e);
|
||||
@@ -111,6 +119,10 @@ public class ModelProfileService {
|
||||
}
|
||||
}
|
||||
|
||||
public ModelProfile getModelProfile(final ModelCategory modelCategory) {
|
||||
return this.getModelProfile(modelCategory.getModelProfile(), modelCategory);
|
||||
}
|
||||
|
||||
public List<String> getModelProfileUrls(final ModelProvider... modelProviders) {
|
||||
final var modelProfiles = Binder.get(this.environment)
|
||||
.bind(MODEL_PROFILE_PREFIX, Bindable.mapOf(String.class, String.class))
|
||||
|
||||
Reference in New Issue
Block a user