View Javadoc
1   package fr.tiogars.chat.domains.lmstudio.models;
2   
3   import java.net.URI;
4   
5   import com.fasterxml.jackson.annotation.JsonIgnore;
6   import com.fasterxml.jackson.annotation.JsonProperty;
7   import io.swagger.v3.oas.annotations.media.Schema;
8   import jakarta.validation.constraints.AssertTrue;
9   import jakarta.validation.constraints.NotBlank;
10  import jakarta.validation.constraints.Positive;
11  import jakarta.validation.constraints.Pattern;
12  import jakarta.validation.constraints.Size;
13  
14  @Schema(description = "Global LM Studio connection settings")
15  public record LmStudioSettings(
16          @NotBlank @Size(max = 2048)
17          @Schema(example = "http://host.docker.internal:1234")
18          String baseUrl,
19          @NotBlank @Size(max = 255)
20          @Schema(example = "llama3.2")
21          String model,
22          @Positive
23          @Schema(description = "Connection and request timeout in seconds", example = "30")
24          int timeoutSeconds,
25          @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
26          @Schema(description = "Bearer token. Omit or use null to keep it; empty string removes it.",
27                  accessMode = Schema.AccessMode.WRITE_ONLY)
28          @Size(max = 4096)
29          @Pattern(regexp = "(?:[A-Za-z0-9._~+/:\\-]+=*)?", message = "token must be a valid LM Studio API token")
30          String token) {
31  
32      public LmStudioSettings(String baseUrl, String model, int timeoutSeconds) {
33          this(baseUrl, model, timeoutSeconds, null);
34      }
35  
36      @JsonProperty(access = JsonProperty.Access.READ_ONLY)
37      @Schema(accessMode = Schema.AccessMode.READ_ONLY)
38      public boolean isTokenConfigured() {
39          return token != null && !token.isEmpty();
40      }
41  
42      @Override
43      public String toString() {
44          return "LmStudioSettings[baseUrl=" + baseUrl + ", model=" + model
45                  + ", timeoutSeconds=" + timeoutSeconds + ", tokenConfigured=" + isTokenConfigured() + "]";
46      }
47  
48      @JsonIgnore
49      @AssertTrue(message = "baseUrl must be an absolute HTTP(S) URL without credentials, query or fragment")
50      public boolean isBaseUrlValid() {
51          if (baseUrl == null || baseUrl.isBlank()) {
52              return false;
53          }
54          try {
55              URI uri = URI.create(baseUrl);
56              return ("http".equalsIgnoreCase(uri.getScheme()) || "https".equalsIgnoreCase(uri.getScheme()))
57                      && uri.getHost() != null
58                      && uri.getRawUserInfo() == null
59                      && uri.getRawQuery() == null
60                      && uri.getRawFragment() == null
61                      && uri.getPort() >= -1 && uri.getPort() <= 65535;
62          } catch (IllegalArgumentException exception) {
63              return false;
64          }
65      }
66  }