feat: add error handling and logging for tokenization and detokenization requests in LlamaServerTokenizer

This commit is contained in:
extio-de
2026-05-09 21:25:30 +02:00
parent 8c92e6fd7a
commit 1561b17f9e
2 changed files with 26 additions and 8 deletions

View File

@@ -121,7 +121,7 @@ public CacheManager cacheManager() {
### Tokenizer Dependencies ### Tokenizer Dependencies
For the recommended `jTokkit` tokenizer: For the `jTokkit` tokenizer:
```xml ```xml
<dependency> <dependency>
@@ -130,6 +130,14 @@ For the recommended `jTokkit` tokenizer:
</dependency> </dependency>
``` ```
## Tokenizers
lm-lib registers tokenizer implementations as beans when their dependencies are available, then resolves the tokenizer at runtime from the active `ModelProfile`. Applications can implement the `Tokenizer` interface to inject their own Tokenizers.
Tokenizer selection is configured per model profile with the `tokenizer` property. If a profile does not specify a tokenizer, lm-lib prefers `jTokkit` when the dependency is present and otherwise falls back to the built-in `fallback` tokenizer. The fallback tokenizer is deterministic and reversible, but it only provides approximate token counts.
As an alternative, the library supports Hugging Face-style /tokenize and /detokenize endpoints (`llamaServer`). These endpoints must be available below base URL from the modelProfile.
These endpoints are provided, for example, by llama.cpp llama-server and vLLM.
For `llamaServer`, no extra dependency is required because tokenization is delegated to the remote server through the configured model endpoint. For `llamaServer`, no extra dependency is required because tokenization is delegated to the remote server through the configured model endpoint.
Available built-in tokenizer names: Available built-in tokenizer names:
@@ -138,12 +146,6 @@ Available built-in tokenizer names:
- `llamaServer` - `llamaServer`
- `fallback` - `fallback`
### Tokenizer Configuration
lm-lib registers tokenizer implementations as beans when their dependencies are available, then resolves the tokenizer at runtime from the active `ModelProfile`. Applications can implement the `Tokenizer` interface to inject their own Tokenizers.
Tokenizer selection is configured per model profile with the `tokenizer` property. If a profile does not specify a tokenizer, lm-lib prefers `jTokkit` when the dependency is present and otherwise falls back to the built-in `fallback` tokenizer. The fallback tokenizer is deterministic and reversible, but it only provides approximate token counts.
## Model Profiles ## Model Profiles
Model profiles describe how a request should be sent to a model and how its usage should be accounted for. They can be loaded from `.properties` files, selected dynamically at runtime, or created programmatically. Model profiles describe how a request should be sent to a model and how its usage should be accounted for. They can be loaded from `.properties` files, selected dynamically at runtime, or created programmatically.

View File

@@ -1,5 +1,7 @@
package de.extio.lmlib.token; package de.extio.lmlib.token;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -36,6 +38,13 @@ final class LlamaServerTokenizer implements Tokenizer {
.uri("/tokenize") .uri("/tokenize")
.body(request) .body(request)
.retrieve() .retrieve()
.onStatus(status -> {
if (status.getStatusCode().isError()) {
LOGGER.error("Tokenization request failed with status code {} {}: {}", status.getStatusCode(), status.getStatusText(), new String(status.getBody().readAllBytes(), StandardCharsets.UTF_8));
throw new RuntimeException("Tokenization request failed with status code " + status.getStatusCode() + " " + status.getStatusText());
}
return false;
})
.body(TokenizeResponse.class); .body(TokenizeResponse.class);
return response.tokens(); return response.tokens();
} }
@@ -55,6 +64,13 @@ final class LlamaServerTokenizer implements Tokenizer {
.uri("/detokenize") .uri("/detokenize")
.body(request) .body(request)
.retrieve() .retrieve()
.onStatus(status -> {
if (status.getStatusCode().isError()) {
LOGGER.error("Detokenization request failed with status code {} {}: {}", status.getStatusCode(), status.getStatusText(), new String(status.getBody().readAllBytes(), StandardCharsets.UTF_8));
throw new RuntimeException("Detokenization request failed with status code " + status.getStatusCode() + " " + status.getStatusText());
}
return false;
})
.body(DetokenizeResponse.class); .body(DetokenizeResponse.class);
return response.content(); return response.content();
} }