LmStudioSettings.java

package fr.tiogars.chat.domains.lmstudio.models;

import java.net.URI;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;

@Schema(description = "Global LM Studio connection settings")
public record LmStudioSettings(
        @NotBlank @Size(max = 2048)
        @Schema(example = "http://host.docker.internal:1234")
        String baseUrl,
        @NotBlank @Size(max = 255)
        @Schema(example = "llama3.2")
        String model,
        @Positive
        @Schema(description = "Connection and request timeout in seconds", example = "30")
        int timeoutSeconds,
        @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
        @Schema(description = "Bearer token. Omit or use null to keep it; empty string removes it.",
                accessMode = Schema.AccessMode.WRITE_ONLY)
        @Size(max = 4096)
        @Pattern(regexp = "(?:[A-Za-z0-9._~+/:\\-]+=*)?", message = "token must be a valid LM Studio API token")
        String token) {

    public LmStudioSettings(String baseUrl, String model, int timeoutSeconds) {
        this(baseUrl, model, timeoutSeconds, null);
    }

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
    public boolean isTokenConfigured() {
        return token != null && !token.isEmpty();
    }

    @Override
    public String toString() {
        return "LmStudioSettings[baseUrl=" + baseUrl + ", model=" + model
                + ", timeoutSeconds=" + timeoutSeconds + ", tokenConfigured=" + isTokenConfigured() + "]";
    }

    @JsonIgnore
    @AssertTrue(message = "baseUrl must be an absolute HTTP(S) URL without credentials, query or fragment")
    public boolean isBaseUrlValid() {
        if (baseUrl == null || baseUrl.isBlank()) {
            return false;
        }
        try {
            URI uri = URI.create(baseUrl);
            return ("http".equalsIgnoreCase(uri.getScheme()) || "https".equalsIgnoreCase(uri.getScheme()))
                    && uri.getHost() != null
                    && uri.getRawUserInfo() == null
                    && uri.getRawQuery() == null
                    && uri.getRawFragment() == null
                    && uri.getPort() >= -1 && uri.getPort() <= 65535;
        } catch (IllegalArgumentException exception) {
            return false;
        }
    }
}