diff --git a/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..05deb71 --- /dev/null +++ b/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,6 @@ +de.extio.lmlib.profile.ProfileAutoConfiguration +de.extio.lmlib.token.TokenizerAutoConfiguration +de.extio.lmlib.client.RestClientConfiguration +de.extio.lmlib.prompt.PromptAutoConfiguration +de.extio.lmlib.client.ClientAutoConfiguration +de.extio.lmlib.agent.AgentAutoConfiguration diff --git a/README.md b/README.md index 457eca3..111bc81 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # lm-lib -The purpose of lm-lib is to provide a modular, extensible Java framework for interacting with large language models (LLMs) from various providers (such as OpenAI and local models). It abstracts away the complexities of prompt construction, conversation management, and model configuration, enabling developers to easily switch between models and providers. The library supports advanced features like streaming completions, request caching, statistics tracking, and agentic flow execution—allowing for the orchestration of multi-step, branching, and parallel conversational workflows. Integration with Spring ensures flexible configuration and dependency management, making it suitable for building scalable, maintainable, and sophisticated LLM-powered applications. +The purpose of lm-lib is to provide a modular, extensible Java framework for interacting with large language models (LLMs) from various providers (such as OpenAI and local models). It abstracts away the complexities of prompt construction, conversation management, and model configuration, enabling developers to easily switch between models and providers. The library supports advanced features like streaming completions, request caching, statistics tracking, and agentic flow execution, allowing for the orchestration of multi-step, branching, and parallel conversational workflows. Integration with Spring ensures flexible configuration and dependency management, making it suitable for building scalable, maintainable, and sophisticated LLM-powered applications. The libraries's key features include: @@ -14,7 +14,7 @@ The libraries's key features include: - Request caching and statistics tracking - Pluggable interceptors, e.g. for rate limiting - Modular architecture for easy extension and customization -- Spring integration for dependency injection and configuration +- Spring Boot auto-configuration with selective module enabling # Requirements @@ -34,7 +34,22 @@ To keep this library lean and compatible with different dependency trees, all de ## Configuration -The library can be configured using the following application properties: +The library uses Spring Boot auto-configuration with a modular architecture. Each domain can be selectively enabled or disabled. + +### Module Enable/Disable Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `lmlib.profile.enabled` | Boolean | `true` | Enable model profile service for managing LLM configurations. | +| `lmlib.tokenizer.enabled` | Boolean | `true` | Enable tokenizer service for token counting and encoding. | +| `lmlib.restclient.enabled` | Boolean | `true` | Enable REST client configuration for HTTP communication with LLM providers. | +| `lmlib.prompt.enabled` | Boolean | `true` | Enable prompt strategy services for text completion formatting. | +| `lmlib.client.enabled` | Boolean | `true` | Enable LLM client services for completion requests. Depends on profile, tokenizer, and restclient modules. | +| `lmlib.agent.enabled` | Boolean | `true` | Enable agent executor service for orchestrating agentic flows. Depends on client module. | + +**Note:** Modules have dependencies. Disabling a module that another depends on may cause runtime errors. For example, disabling `lmlib.client.enabled` while keeping `lmlib.agent.enabled=true` requires to provide a custom ClientService implementation. + +### REST Client Configuration | Property | Type | Default | Description | |----------|------|---------|-------------| @@ -45,25 +60,48 @@ The library can be configured using the following application properties: | `lmlib.client.proxy.password` | String | | HTTP proxy password (optional). | | `lmlib.client.tls.verification.disabled` | Boolean | `false` | Disable TLS certificate verification (not recommended for production). | | `lmlib.client.retry.max-attempts` | Integer | `5` | Maximum number of retry attempts for failed requests. | -| `lmlib.client.retry.backoff-interval-min` | Long | `200` | Minimum backoff interval in milliseconds between retry attempts. | +| `lmlib.client.retry.backoff-interval-min` | Long | `125` | Minimum backoff interval in milliseconds between retry attempts. | | `lmlib.client.retry.backoff-interval-max` | Long | `2500` | Maximum backoff interval in milliseconds between retry attempts. | +### Tokenizer Configuration + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `tokenizer.strategy` | String | `jTokkit` | Token counting strategy. Options: `jTokkit`, `llamaServer`. | + ## Optional dependencies -Some services benefit from Spring cache framework, e.g. the model profile service: +### Caching Support + +Some services benefit from Spring cache framework (e.g., model profile service, client service): org.springframework.boot spring-boot-starter-cache - + + + com.github.ben-manes.caffeine + caffeine + -Don't forget to activate a CacheManager afterwards: +Don't forget to activate a CacheManager: @Bean public CacheManager cacheManager() { return new CaffeineCacheManager(); } +### Tokenizer Dependencies + +For jTokkit tokenizer (recommended): + + + com.knuddels + jtokkit + + +For llamaServer tokenizer, no additional dependencies are needed (uses REST client to query llama.cpp server). + ## Model profiles Models are managed with profiles. You can maintain multiple profiles for different models and parameters such as temperature. diff --git a/src/main/java/de/extio/lmlib/LmLibAutoConfiguration.java b/src/main/java/de/extio/lmlib/LmLibAutoConfiguration.java new file mode 100644 index 0000000..230e129 --- /dev/null +++ b/src/main/java/de/extio/lmlib/LmLibAutoConfiguration.java @@ -0,0 +1,155 @@ +package de.extio.lmlib; + +import java.util.List; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; + +import de.extio.lmlib.agent.AgentExecutorService; +import de.extio.lmlib.client.ClientService; +import de.extio.lmlib.client.oai.ModelNameSupplier; +import de.extio.lmlib.client.oai.completion.chat.ChatCompletionClient; +import de.extio.lmlib.client.oai.completion.text.TextCompletionClient; +import de.extio.lmlib.profile.ModelProfileService; +import de.extio.lmlib.prompt.AlpacaShortPromptStrategy; +import de.extio.lmlib.prompt.ChatMLPromptStrategy; +import de.extio.lmlib.prompt.GemmaPromptStrategy; +import de.extio.lmlib.prompt.GptOssPromptStrategy; +import de.extio.lmlib.prompt.Llama3PromptStrategy; +import de.extio.lmlib.prompt.Llama4PromptStrategy; +import de.extio.lmlib.prompt.MistralNemoPromptStrategy; +import de.extio.lmlib.prompt.MistralPromptStrategy; +import de.extio.lmlib.prompt.NoPromptStrategy; +import de.extio.lmlib.prompt.Phi3PromptStrategy; +import de.extio.lmlib.prompt.Phi4PromptStrategy; +import de.extio.lmlib.prompt.PromptStrategy; +import de.extio.lmlib.prompt.PromptStrategyFactory; +import de.extio.lmlib.prompt.Qwen3PromptStrategy; +import de.extio.lmlib.prompt.VicunaPromptStrategy; + +@AutoConfiguration +public class LmLibAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + ModelProfileService modelProfileService(final Environment environment) { + return new ModelProfileService(environment); + } + + @Bean + @ConditionalOnMissingBean + ClientService clientService() { + return new ClientService(); + } + + @Bean + @ConditionalOnMissingBean + AgentExecutorService agentExecutorService() { + return new AgentExecutorService(); + } + + @Bean + @ConditionalOnMissingBean + ModelNameSupplier modelNameSupplier() { + return new ModelNameSupplier(); + } + + @Bean + @ConditionalOnMissingBean + TextCompletionClient textCompletionClient() { + return new TextCompletionClient(); + } + + @Bean + @ConditionalOnMissingBean + ChatCompletionClient chatCompletionClient() { + return new ChatCompletionClient(); + } + + @Bean + @ConditionalOnMissingBean + PromptStrategyFactory promptStrategyFactory(final List strategies) { + return new PromptStrategyFactory(strategies); + } + + @Bean + @ConditionalOnMissingBean + AlpacaShortPromptStrategy alpacaShortPromptStrategy() { + return new AlpacaShortPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + ChatMLPromptStrategy chatMLPromptStrategy() { + return new ChatMLPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + GemmaPromptStrategy gemmaPromptStrategy() { + return new GemmaPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + GptOssPromptStrategy gptOssPromptStrategy() { + return new GptOssPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Llama3PromptStrategy llama3PromptStrategy() { + return new Llama3PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Llama4PromptStrategy llama4PromptStrategy() { + return new Llama4PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + MistralNemoPromptStrategy mistralNemoPromptStrategy() { + return new MistralNemoPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + MistralPromptStrategy mistralPromptStrategy() { + return new MistralPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + NoPromptStrategy noPromptStrategy() { + return new NoPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Phi3PromptStrategy phi3PromptStrategy() { + return new Phi3PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Phi4PromptStrategy phi4PromptStrategy() { + return new Phi4PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Qwen3PromptStrategy qwen3PromptStrategy() { + return new Qwen3PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + VicunaPromptStrategy vicunaPromptStrategy() { + return new VicunaPromptStrategy(); + } + +} diff --git a/src/main/java/de/extio/lmlib/agent/AgentAutoConfiguration.java b/src/main/java/de/extio/lmlib/agent/AgentAutoConfiguration.java new file mode 100644 index 0000000..9281fc4 --- /dev/null +++ b/src/main/java/de/extio/lmlib/agent/AgentAutoConfiguration.java @@ -0,0 +1,22 @@ +package de.extio.lmlib.agent; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; + +import de.extio.lmlib.client.ClientAutoConfiguration; + +@AutoConfiguration +@AutoConfigureAfter(ClientAutoConfiguration.class) +@ConditionalOnProperty(prefix = "lmlib.agent", name = "enabled", havingValue = "true", matchIfMissing = true) +public class AgentAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + AgentExecutorService agentExecutorService() { + return new AgentExecutorService(); + } + +} diff --git a/src/main/java/de/extio/lmlib/agent/AgentExecutorService.java b/src/main/java/de/extio/lmlib/agent/AgentExecutorService.java index 6bb3fb6..32e186d 100755 --- a/src/main/java/de/extio/lmlib/agent/AgentExecutorService.java +++ b/src/main/java/de/extio/lmlib/agent/AgentExecutorService.java @@ -15,12 +15,10 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; import de.extio.lmlib.client.Client; import de.extio.lmlib.client.ClientService; -@Service public class AgentExecutorService implements InitializingBean, DisposableBean { private static final Logger LOGGER = LoggerFactory.getLogger(AgentExecutorService.class); diff --git a/src/main/java/de/extio/lmlib/client/ClientAutoConfiguration.java b/src/main/java/de/extio/lmlib/client/ClientAutoConfiguration.java new file mode 100644 index 0000000..f86373a --- /dev/null +++ b/src/main/java/de/extio/lmlib/client/ClientAutoConfiguration.java @@ -0,0 +1,44 @@ +package de.extio.lmlib.client; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; + +import de.extio.lmlib.client.oai.ModelNameSupplier; +import de.extio.lmlib.client.oai.completion.chat.ChatCompletionClient; +import de.extio.lmlib.client.oai.completion.text.TextCompletionClient; +import de.extio.lmlib.profile.ProfileAutoConfiguration; +import de.extio.lmlib.token.TokenizerAutoConfiguration; + +@AutoConfiguration +@AutoConfigureAfter({ProfileAutoConfiguration.class, TokenizerAutoConfiguration.class, RestClientConfiguration.class}) +@ConditionalOnProperty(prefix = "lmlib.client", name = "enabled", havingValue = "true", matchIfMissing = true) +public class ClientAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + ClientService clientService() { + return new ClientService(); + } + + @Bean + @ConditionalOnMissingBean + ModelNameSupplier modelNameSupplier() { + return new ModelNameSupplier(); + } + + @Bean + @ConditionalOnMissingBean + TextCompletionClient textCompletionClient() { + return new TextCompletionClient(); + } + + @Bean + @ConditionalOnMissingBean + ChatCompletionClient chatCompletionClient() { + return new ChatCompletionClient(); + } + +} diff --git a/src/main/java/de/extio/lmlib/client/ClientService.java b/src/main/java/de/extio/lmlib/client/ClientService.java index 0456cc7..d134f0d 100755 --- a/src/main/java/de/extio/lmlib/client/ClientService.java +++ b/src/main/java/de/extio/lmlib/client/ClientService.java @@ -5,7 +5,6 @@ import java.util.function.Consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; import de.extio.lmlib.client.cached.CachedClient; import de.extio.lmlib.client.cached.CachedClientRepository; @@ -14,7 +13,6 @@ import de.extio.lmlib.profile.ModelProfile; import de.extio.lmlib.profile.ModelProfile.ModelProvider; import de.extio.lmlib.profile.ModelProfileService; -@Service public class ClientService { @Autowired diff --git a/src/main/java/de/extio/lmlib/client/RestClientConfiguration.java b/src/main/java/de/extio/lmlib/client/RestClientConfiguration.java index 714209a..c0587ef 100644 --- a/src/main/java/de/extio/lmlib/client/RestClientConfiguration.java +++ b/src/main/java/de/extio/lmlib/client/RestClientConfiguration.java @@ -19,8 +19,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpRequestInterceptor; @@ -30,7 +31,8 @@ import org.springframework.util.backoff.BackOffExecution; import org.springframework.util.backoff.ExponentialBackOff; import org.springframework.web.client.RestClient; -@Configuration +@AutoConfiguration +@ConditionalOnProperty(prefix = "lmlib.restclient", name = "enabled", havingValue = "true", matchIfMissing = true) public class RestClientConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(RestClientConfiguration.class); diff --git a/src/main/java/de/extio/lmlib/client/oai/ModelNameSupplier.java b/src/main/java/de/extio/lmlib/client/oai/ModelNameSupplier.java index 9e43fe2..59b82ea 100755 --- a/src/main/java/de/extio/lmlib/client/oai/ModelNameSupplier.java +++ b/src/main/java/de/extio/lmlib/client/oai/ModelNameSupplier.java @@ -5,13 +5,11 @@ import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; import de.extio.lmlib.profile.ModelProfile; import de.extio.lmlib.profile.ModelProfile.ModelProvider; -@Component public class ModelNameSupplier { @Autowired diff --git a/src/main/java/de/extio/lmlib/client/oai/completion/chat/ChatCompletionClient.java b/src/main/java/de/extio/lmlib/client/oai/completion/chat/ChatCompletionClient.java index 05656b5..901c390 100755 --- a/src/main/java/de/extio/lmlib/client/oai/completion/chat/ChatCompletionClient.java +++ b/src/main/java/de/extio/lmlib/client/oai/completion/chat/ChatCompletionClient.java @@ -14,7 +14,6 @@ import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; import tools.jackson.core.JacksonException; @@ -27,7 +26,6 @@ import de.extio.lmlib.client.oai.completion.AbstractCompletionClient; import de.extio.lmlib.profile.ModelProfile; import de.extio.lmlib.profile.ModelProfile.ModelProvider; -@Component public class ChatCompletionClient extends AbstractCompletionClient { private static final Logger LOGGER = LoggerFactory.getLogger(ChatCompletionClient.class); diff --git a/src/main/java/de/extio/lmlib/client/oai/completion/text/TextCompletionClient.java b/src/main/java/de/extio/lmlib/client/oai/completion/text/TextCompletionClient.java index bd48ab1..abd0552 100755 --- a/src/main/java/de/extio/lmlib/client/oai/completion/text/TextCompletionClient.java +++ b/src/main/java/de/extio/lmlib/client/oai/completion/text/TextCompletionClient.java @@ -13,7 +13,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; import tools.jackson.core.JacksonException; @@ -29,7 +28,6 @@ import de.extio.lmlib.profile.ModelProfile.ModelProvider; import de.extio.lmlib.prompt.PromptStrategy; import de.extio.lmlib.prompt.PromptStrategyFactory; -@Component public class TextCompletionClient extends AbstractCompletionClient { private static final Logger LOGGER = LoggerFactory.getLogger(TextCompletionClient.class); diff --git a/src/main/java/de/extio/lmlib/profile/ModelProfileService.java b/src/main/java/de/extio/lmlib/profile/ModelProfileService.java index 459e698..decddb5 100755 --- a/src/main/java/de/extio/lmlib/profile/ModelProfileService.java +++ b/src/main/java/de/extio/lmlib/profile/ModelProfileService.java @@ -10,11 +10,9 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.io.support.ResourcePropertySource; -import org.springframework.stereotype.Service; import de.extio.lmlib.profile.ModelProfile.ModelProvider; -@Service public class ModelProfileService { private final Environment environment; diff --git a/src/main/java/de/extio/lmlib/profile/ProfileAutoConfiguration.java b/src/main/java/de/extio/lmlib/profile/ProfileAutoConfiguration.java new file mode 100644 index 0000000..f9c7268 --- /dev/null +++ b/src/main/java/de/extio/lmlib/profile/ProfileAutoConfiguration.java @@ -0,0 +1,19 @@ +package de.extio.lmlib.profile; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; + +@AutoConfiguration +@ConditionalOnProperty(prefix = "lmlib.profile", name = "enabled", havingValue = "true", matchIfMissing = true) +public class ProfileAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + ModelProfileService modelProfileService(final Environment environment) { + return new ModelProfileService(environment); + } + +} diff --git a/src/main/java/de/extio/lmlib/prompt/ChatMLPromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/ChatMLPromptStrategy.java index 494511a..dc71e75 100755 --- a/src/main/java/de/extio/lmlib/prompt/ChatMLPromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/ChatMLPromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class ChatMLPromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/GemmaPromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/GemmaPromptStrategy.java index 2332eb8..db414bb 100755 --- a/src/main/java/de/extio/lmlib/prompt/GemmaPromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/GemmaPromptStrategy.java @@ -1,12 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -/** - * Works for both Gemma2 and Gemma3 - * @see https://ai.google.dev/gemma/docs/core/prompt-structure - */ -@Component public class GemmaPromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/GptOssPromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/GptOssPromptStrategy.java index 6f69b49..617e083 100644 --- a/src/main/java/de/extio/lmlib/prompt/GptOssPromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/GptOssPromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class GptOssPromptStrategy implements PromptStrategy { private static final String finalMarker = "<|channel|>final<|message|>"; diff --git a/src/main/java/de/extio/lmlib/prompt/Llama3PromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/Llama3PromptStrategy.java index ab5d1e1..77504c7 100755 --- a/src/main/java/de/extio/lmlib/prompt/Llama3PromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/Llama3PromptStrategy.java @@ -1,11 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -/** - * @see https://llama.meta.com/docs/model-cards-and-prompt-formats/meta-llama-3 - */ -@Component public class Llama3PromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/Llama4PromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/Llama4PromptStrategy.java index 0a7a649..46b7b49 100644 --- a/src/main/java/de/extio/lmlib/prompt/Llama4PromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/Llama4PromptStrategy.java @@ -1,11 +1,6 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; -/** - * @see https://www.llama.com/docs/model-cards-and-prompt-formats/llama4/ - */ -@Component public class Llama4PromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/MistralNemoPromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/MistralNemoPromptStrategy.java index 9e04c6d..0ab0fc8 100644 --- a/src/main/java/de/extio/lmlib/prompt/MistralNemoPromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/MistralNemoPromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class MistralNemoPromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/MistralPromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/MistralPromptStrategy.java index 17593e3..bb52e66 100755 --- a/src/main/java/de/extio/lmlib/prompt/MistralPromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/MistralPromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class MistralPromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/NoPromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/NoPromptStrategy.java index c4b55f9..e6f3b2a 100644 --- a/src/main/java/de/extio/lmlib/prompt/NoPromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/NoPromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class NoPromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/Phi3PromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/Phi3PromptStrategy.java index 90db04a..8de0484 100755 --- a/src/main/java/de/extio/lmlib/prompt/Phi3PromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/Phi3PromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class Phi3PromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/Phi4PromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/Phi4PromptStrategy.java index 77eb22a..7d16b90 100644 --- a/src/main/java/de/extio/lmlib/prompt/Phi4PromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/Phi4PromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class Phi4PromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/prompt/PromptAutoConfiguration.java b/src/main/java/de/extio/lmlib/prompt/PromptAutoConfiguration.java new file mode 100644 index 0000000..9748a56 --- /dev/null +++ b/src/main/java/de/extio/lmlib/prompt/PromptAutoConfiguration.java @@ -0,0 +1,98 @@ +package de.extio.lmlib.prompt; + +import java.util.List; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; + +@AutoConfiguration +@ConditionalOnProperty(prefix = "lmlib.prompt", name = "enabled", havingValue = "true", matchIfMissing = true) +public class PromptAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + PromptStrategyFactory promptStrategyFactory(final List strategies) { + return new PromptStrategyFactory(strategies); + } + + @Bean + @ConditionalOnMissingBean + AlpacaShortPromptStrategy alpacaShortPromptStrategy() { + return new AlpacaShortPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + ChatMLPromptStrategy chatMLPromptStrategy() { + return new ChatMLPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + GemmaPromptStrategy gemmaPromptStrategy() { + return new GemmaPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + GptOssPromptStrategy gptOssPromptStrategy() { + return new GptOssPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Llama3PromptStrategy llama3PromptStrategy() { + return new Llama3PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Llama4PromptStrategy llama4PromptStrategy() { + return new Llama4PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + MistralNemoPromptStrategy mistralNemoPromptStrategy() { + return new MistralNemoPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + MistralPromptStrategy mistralPromptStrategy() { + return new MistralPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + NoPromptStrategy noPromptStrategy() { + return new NoPromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Phi3PromptStrategy phi3PromptStrategy() { + return new Phi3PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Phi4PromptStrategy phi4PromptStrategy() { + return new Phi4PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + Qwen3PromptStrategy qwen3PromptStrategy() { + return new Qwen3PromptStrategy(); + } + + @Bean + @ConditionalOnMissingBean + VicunaPromptStrategy vicunaPromptStrategy() { + return new VicunaPromptStrategy(); + } + +} diff --git a/src/main/java/de/extio/lmlib/prompt/PromptStrategyFactory.java b/src/main/java/de/extio/lmlib/prompt/PromptStrategyFactory.java index e78b6db..f192525 100755 --- a/src/main/java/de/extio/lmlib/prompt/PromptStrategyFactory.java +++ b/src/main/java/de/extio/lmlib/prompt/PromptStrategyFactory.java @@ -4,9 +4,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.stereotype.Component; - -@Component public class PromptStrategyFactory { private final Map strategies = new HashMap<>(); diff --git a/src/main/java/de/extio/lmlib/prompt/Qwen3PromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/Qwen3PromptStrategy.java index 3a08357..e0f45de 100644 --- a/src/main/java/de/extio/lmlib/prompt/Qwen3PromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/Qwen3PromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class Qwen3PromptStrategy implements PromptStrategy { private static final String IM_START = "<|im_start|>"; diff --git a/src/main/java/de/extio/lmlib/prompt/VicunaPromptStrategy.java b/src/main/java/de/extio/lmlib/prompt/VicunaPromptStrategy.java index cecc892..fbba80e 100755 --- a/src/main/java/de/extio/lmlib/prompt/VicunaPromptStrategy.java +++ b/src/main/java/de/extio/lmlib/prompt/VicunaPromptStrategy.java @@ -1,8 +1,5 @@ package de.extio.lmlib.prompt; -import org.springframework.stereotype.Component; - -@Component public class VicunaPromptStrategy implements PromptStrategy { @Override diff --git a/src/main/java/de/extio/lmlib/token/TokenizerAutoConfiguration.java b/src/main/java/de/extio/lmlib/token/TokenizerAutoConfiguration.java index 10cca8f..094480b 100644 --- a/src/main/java/de/extio/lmlib/token/TokenizerAutoConfiguration.java +++ b/src/main/java/de/extio/lmlib/token/TokenizerAutoConfiguration.java @@ -6,10 +6,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestClient; @AutoConfiguration +@ConditionalOnProperty(prefix = "lmlib.tokenizer", name = "enabled", havingValue = "true", matchIfMissing = true) public class TokenizerAutoConfiguration { @Bean @@ -19,17 +19,12 @@ public class TokenizerAutoConfiguration { return new LlamaServerTokenizer(restClientBuilder); } - @Configuration(proxyBeanMethods = false) + @Bean @ConditionalOnClass(name = "com.knuddels.jtokkit.Encodings") - @ConditionalOnProperty(name = "tokenizer.strategy", havingValue = "jTokkit") + @ConditionalOnProperty(name = "tokenizer.strategy", havingValue = "jTokkit", matchIfMissing = true) @ConditionalOnMissingBean(type = "de.extio.lmlib.token.Tokenizer") - public static class SomeServiceConfiguration { - - @Bean - Tokenizer jTokkitTokenizer() { - return new JTokkitTokenizer(); - } - + Tokenizer jTokkitTokenizer() { + return new JTokkitTokenizer(); } - + } diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 5fc3c3d..05deb71 100644 --- a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1,6 @@ +de.extio.lmlib.profile.ProfileAutoConfiguration de.extio.lmlib.token.TokenizerAutoConfiguration +de.extio.lmlib.client.RestClientConfiguration +de.extio.lmlib.prompt.PromptAutoConfiguration +de.extio.lmlib.client.ClientAutoConfiguration +de.extio.lmlib.agent.AgentAutoConfiguration diff --git a/src/test/java/de/extio/lmlib/agent/AgentTest.java b/src/test/java/de/extio/lmlib/agent/AgentTest.java index 2b83f64..a30507a 100644 --- a/src/test/java/de/extio/lmlib/agent/AgentTest.java +++ b/src/test/java/de/extio/lmlib/agent/AgentTest.java @@ -17,9 +17,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; import de.extio.lmlib.agent.responsehandler.JsonAgentResponseHandler; @@ -34,7 +34,7 @@ import de.extio.lmlib.profile.ModelCategory; @Disabled("This test requires a running Llama server or a cloud subscription") @SpringBootTest(webEnvironment = WebEnvironment.NONE) @SpringBootConfiguration -@ComponentScan(basePackages = "de.extio.lmlib") +@EnableAutoConfiguration @PropertySource("classpath:/application-test.properties") public class AgentTest { diff --git a/src/test/java/de/extio/lmlib/client/cached/CachedClientTest.java b/src/test/java/de/extio/lmlib/client/cached/CachedClientTest.java index be9cc91..542b3b1 100755 --- a/src/test/java/de/extio/lmlib/client/cached/CachedClientTest.java +++ b/src/test/java/de/extio/lmlib/client/cached/CachedClientTest.java @@ -12,9 +12,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import de.extio.lmlib.client.ClientService; @@ -26,7 +27,8 @@ import de.extio.lmlib.profile.ModelCategory; @Disabled("This test requires a running Llama server or a cloud subscription (setup key in model profile)") @SpringBootTest(webEnvironment = WebEnvironment.NONE) @SpringBootConfiguration -@ComponentScan(basePackages = "de.extio.lmlib") +@EnableAutoConfiguration +@Import({ TestCachedClientRepository.Config.class }) @PropertySource("classpath:/application-test.properties") public class CachedClientTest { diff --git a/src/test/java/de/extio/lmlib/client/cached/TestCachedClientRepository.java b/src/test/java/de/extio/lmlib/client/cached/TestCachedClientRepository.java index 24d1524..7fa096f 100755 --- a/src/test/java/de/extio/lmlib/client/cached/TestCachedClientRepository.java +++ b/src/test/java/de/extio/lmlib/client/cached/TestCachedClientRepository.java @@ -3,6 +3,8 @@ package de.extio.lmlib.client.cached; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; @Component @@ -10,6 +12,16 @@ class TestCachedClientRepository implements CachedClientRepository { private final ConcurrentMap cache = new ConcurrentHashMap<>(); + @Configuration + static class Config { + + @Bean + CachedClientRepository cachedClientRepository() { + return new TestCachedClientRepository(); + } + + } + @Override public CachedCompletion get(final String key) { return this.cache.get(key); diff --git a/src/test/java/de/extio/lmlib/client/oai/CompletionClientTest.java b/src/test/java/de/extio/lmlib/client/oai/CompletionClientTest.java index 16983cf..a29f4b5 100755 --- a/src/test/java/de/extio/lmlib/client/oai/CompletionClientTest.java +++ b/src/test/java/de/extio/lmlib/client/oai/CompletionClientTest.java @@ -15,9 +15,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; import de.extio.lmlib.client.ClientService; @@ -32,7 +32,7 @@ import de.extio.lmlib.profile.ModelProfileService; @Disabled("This test requires a running Llama server") @SpringBootTest(webEnvironment = WebEnvironment.NONE) @SpringBootConfiguration -@ComponentScan(basePackages = "de.extio.lmlib") +@EnableAutoConfiguration @PropertySource("classpath:/application-test.properties") public class CompletionClientTest { diff --git a/src/test/java/de/extio/lmlib/grader/GraderTest.java b/src/test/java/de/extio/lmlib/grader/GraderTest.java index bc3eb8e..c79cf50 100644 --- a/src/test/java/de/extio/lmlib/grader/GraderTest.java +++ b/src/test/java/de/extio/lmlib/grader/GraderTest.java @@ -25,9 +25,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; @@ -44,7 +44,7 @@ import de.extio.lmlib.profile.ModelCategory; @Disabled("This test requires a running Llama server or a cloud subscription (setup key in model profile)") @SpringBootTest(webEnvironment = WebEnvironment.NONE) @SpringBootConfiguration -@ComponentScan(basePackages = "de.extio.lmlib") +@EnableAutoConfiguration @PropertySource("classpath:/application-test.properties") @TestInstance(Lifecycle.PER_CLASS) public class GraderTest { diff --git a/src/test/java/de/extio/lmlib/token/TokenizerTest.java b/src/test/java/de/extio/lmlib/token/TokenizerTest.java index 0669f57..a6ea610 100644 --- a/src/test/java/de/extio/lmlib/token/TokenizerTest.java +++ b/src/test/java/de/extio/lmlib/token/TokenizerTest.java @@ -7,9 +7,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; import org.springframework.web.client.RestClient; @@ -18,7 +18,7 @@ import de.extio.lmlib.profile.ModelProfileService; @SpringBootTest(webEnvironment = WebEnvironment.NONE) @SpringBootConfiguration -@ComponentScan(basePackages = "de.extio.lmlib") +@EnableAutoConfiguration @PropertySource("classpath:/application-test.properties") public class TokenizerTest {