mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-17 13:52:14 +01:00
UI elements
This commit is contained in:
committed by
Dario Ghunney Ware
parent
f6ad398fb3
commit
1973c55d10
@@ -626,6 +626,8 @@ public class ApplicationProperties {
|
||||
private String primary = "gpt-5-nano";
|
||||
private String fallback = "gpt-5-mini";
|
||||
private String embedding = "text-embedding-3-small";
|
||||
private long connectTimeoutMillis = 10000;
|
||||
private long readTimeoutMillis = 60000;
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
@@ -52,9 +52,9 @@ dependencies {
|
||||
api 'org.springframework.boot:spring-boot-starter-cache'
|
||||
api 'com.github.ben-manes.caffeine:caffeine'
|
||||
api 'io.swagger.core.v3:swagger-core-jakarta:2.2.38'
|
||||
implementation 'org.springframework.ai:spring-ai-starter-model-openai'
|
||||
implementation 'org.springframework.ai:spring-ai-starter-model-ollama'
|
||||
implementation 'org.springframework.ai:spring-ai-redis-store'
|
||||
api 'org.springframework.ai:spring-ai-starter-model-openai'
|
||||
api 'org.springframework.ai:spring-ai-starter-model-ollama'
|
||||
api 'org.springframework.ai:spring-ai-redis-store'
|
||||
implementation 'com.bucket4j:bucket4j_jdk17-core:8.15.0'
|
||||
|
||||
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package stirling.software.proprietary.configuration;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.client.RestClientCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.model.ApplicationProperties.Premium;
|
||||
import stirling.software.common.model.ApplicationProperties.Premium.ProFeatures;
|
||||
import stirling.software.common.model.ApplicationProperties.Premium.ProFeatures.Chatbot;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RestClientCustomizer.class)
|
||||
@ConditionalOnProperty(value = "spring.ai.openai.enabled", havingValue = "true")
|
||||
public class ChatbotAiClientConfiguration {
|
||||
|
||||
@Bean
|
||||
public RestClientCustomizer chatbotRestClientCustomizer(
|
||||
ApplicationProperties applicationProperties) {
|
||||
long connectTimeout = resolveConnectTimeout(applicationProperties);
|
||||
long readTimeout = resolveReadTimeout(applicationProperties);
|
||||
return builder -> builder.requestFactory(createRequestFactory(connectTimeout, readTimeout));
|
||||
}
|
||||
|
||||
private JdkClientHttpRequestFactory createRequestFactory(
|
||||
long connectTimeoutMillis, long readTimeoutMillis) {
|
||||
HttpClient httpClient =
|
||||
HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMillis(connectTimeoutMillis))
|
||||
.build();
|
||||
JdkClientHttpRequestFactory factory = new JdkClientHttpRequestFactory(httpClient);
|
||||
factory.setReadTimeout((int) readTimeoutMillis);
|
||||
return factory;
|
||||
}
|
||||
|
||||
private long resolveConnectTimeout(ApplicationProperties properties) {
|
||||
long configured = resolveChatbot(properties).getModels().getConnectTimeoutMillis();
|
||||
return configured > 0 ? configured : 30000L;
|
||||
}
|
||||
|
||||
private long resolveReadTimeout(ApplicationProperties properties) {
|
||||
long configured = resolveChatbot(properties).getModels().getReadTimeoutMillis();
|
||||
return configured > 0 ? configured : 120000L;
|
||||
}
|
||||
|
||||
private Chatbot resolveChatbot(ApplicationProperties properties) {
|
||||
return Optional.ofNullable(properties)
|
||||
.map(ApplicationProperties::getPremium)
|
||||
.map(Premium::getProFeatures)
|
||||
.map(ProFeatures::getChatbot)
|
||||
.orElseGet(Chatbot::new);
|
||||
}
|
||||
}
|
||||
@@ -99,17 +99,16 @@ public class ChatbotController {
|
||||
|
||||
private List<String> sessionWarnings(ChatbotSettings settings, ChatbotSession session) {
|
||||
List<String> warnings = new ArrayList<>();
|
||||
if (settings.alphaWarning()) {
|
||||
warnings.add("Chatbot feature is in alpha and may change.");
|
||||
}
|
||||
warnings.add("Image-based content is not supported yet.");
|
||||
|
||||
if (session != null && session.isImageContentDetected()) {
|
||||
warnings.add("Detected images will be ignored until image support ships.");
|
||||
warnings.add("Images detected - Images are not currently supported.");
|
||||
}
|
||||
|
||||
warnings.add("Only extracted text is sent for analysis.");
|
||||
if (session != null && session.isOcrRequested()) {
|
||||
warnings.add("OCR was requested – extra processing charges may apply.");
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package stirling.software.proprietary.controller;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.client.HttpResponseException;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -37,6 +38,14 @@ public class ChatbotExceptionHandler {
|
||||
return buildResponse(HttpStatus.BAD_REQUEST, ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpResponseException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleProvider(HttpResponseException ex) {
|
||||
log.warn("Chatbot provider error", ex);
|
||||
return buildResponse(
|
||||
HttpStatus.BAD_GATEWAY,
|
||||
"Chatbot provider rejected the request: " + ex.getMessage());
|
||||
}
|
||||
|
||||
private ResponseEntity<Map<String, Object>> buildResponse(HttpStatus status, String message) {
|
||||
Map<String, Object> payload =
|
||||
Map.of(
|
||||
|
||||
@@ -204,7 +204,19 @@ public class ChatbotConversationService {
|
||||
List<ChatbotTextChunk> context,
|
||||
Map<String, String> metadata) {
|
||||
Prompt requestPrompt = buildPrompt(settings, model, prompt, session, context, metadata);
|
||||
ChatResponse response = chatModel.call(requestPrompt);
|
||||
ChatResponse response;
|
||||
try {
|
||||
response = chatModel.call(requestPrompt);
|
||||
} catch (org.eclipse.jetty.client.HttpResponseException ex) {
|
||||
throw new ChatbotException(
|
||||
"Chat model rejected the request: " + sanitizeRemoteMessage(ex.getMessage()),
|
||||
ex);
|
||||
} catch (RuntimeException ex) {
|
||||
throw new ChatbotException(
|
||||
"Failed to contact chat model provider: "
|
||||
+ sanitizeRemoteMessage(ex.getMessage()),
|
||||
ex);
|
||||
}
|
||||
String content =
|
||||
Optional.ofNullable(response)
|
||||
.map(ChatResponse::getResults)
|
||||
@@ -298,4 +310,11 @@ public class ChatbotConversationService {
|
||||
|
||||
private record ModelReply(
|
||||
String answer, double confidence, boolean requiresEscalation, String rationale) {}
|
||||
|
||||
private String sanitizeRemoteMessage(String message) {
|
||||
if (!StringUtils.hasText(message)) {
|
||||
return "unexpected provider error";
|
||||
}
|
||||
return message.replaceAll("(?i)api[-_ ]?key\\s*=[^\\s]+", "api-key=***");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,20 @@ public class ChatbotIngestionService {
|
||||
if (chunkTexts.isEmpty()) {
|
||||
throw new ChatbotException("Unable to split document text into retrievable chunks");
|
||||
}
|
||||
EmbeddingResponse response = embeddingModel.embedForResponse(chunkTexts);
|
||||
EmbeddingResponse response;
|
||||
try {
|
||||
response = embeddingModel.embedForResponse(chunkTexts);
|
||||
} catch (org.eclipse.jetty.client.HttpResponseException ex) {
|
||||
throw new ChatbotException(
|
||||
"Embedding provider rejected the request: "
|
||||
+ sanitizeRemoteMessage(ex.getMessage()),
|
||||
ex);
|
||||
} catch (RuntimeException ex) {
|
||||
throw new ChatbotException(
|
||||
"Failed to compute embeddings for chatbot ingestion: "
|
||||
+ sanitizeRemoteMessage(ex.getMessage()),
|
||||
ex);
|
||||
}
|
||||
if (response.getResults().size() != chunkTexts.size()) {
|
||||
throw new ChatbotException("Mismatch between chunks and embedding results");
|
||||
}
|
||||
@@ -165,4 +178,11 @@ public class ChatbotIngestionService {
|
||||
chunks.size());
|
||||
return chunks;
|
||||
}
|
||||
|
||||
private String sanitizeRemoteMessage(String message) {
|
||||
if (!StringUtils.hasText(message)) {
|
||||
return "unexpected provider error";
|
||||
}
|
||||
return message.replaceAll("(?i)api[-_ ]?key\\s*=[^\\s]+", "api-key=***");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user