mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-12-18 20:04:17 +01:00
wip - working on timeout issue
This commit is contained in:
parent
98d4949930
commit
3695a9a70a
@ -4,9 +4,8 @@ logging.level.org.springframework.security=WARN
|
||||
logging.level.org.hibernate=WARN
|
||||
logging.level.org.eclipse.jetty=WARN
|
||||
#logging.level.org.springframework.security.oauth2=DEBUG
|
||||
#logging.level.org.springframework.security=DEBUG
|
||||
#logging.level.org.opensaml=DEBUG
|
||||
#logging.level.stirling.software.proprietary.security=DEBUG
|
||||
logging.level.stirling.software.proprietary.security=DEBUG
|
||||
logging.level.com.zaxxer.hikari=WARN
|
||||
logging.level.stirling.software.SPDF.service.PdfJsonConversionService=INFO
|
||||
logging.level.stirling.software.common.service.JobExecutorService=INFO
|
||||
@ -52,14 +51,43 @@ server.servlet.session.timeout:30m
|
||||
springdoc.api-docs.path=/v1/api-docs
|
||||
# Set the URL of the OpenAPI JSON for the Swagger UI
|
||||
springdoc.swagger-ui.url=/v1/api-docs
|
||||
springdoc.swagger-ui.path=/swagger-ui.html
|
||||
|
||||
# Spring AI OpenAI Configuration
|
||||
# Uses GPT-5-nano as primary model and GPT-5-mini as fallback (configured in settings.yml)
|
||||
spring.ai.openai.enabled=true
|
||||
spring.ai.openai.api-key=# todo <API-KEY-HERE>
|
||||
spring.ai.openai.base-url=https://api.openai.com
|
||||
spring.ai.openai.chat.enabled=true
|
||||
spring.ai.openai.chat.options.model=gpt-5-nano
|
||||
# Note: Some models only support default temperature value of 1.0
|
||||
spring.ai.openai.chat.options.temperature=1.0
|
||||
# For newer models, use max-completion-tokens instead of max-tokens
|
||||
spring.ai.openai.chat.options.max-completion-tokens=4000
|
||||
spring.ai.openai.embedding.enabled=true
|
||||
spring.ai.openai.embedding.options.model=text-embedding-ada-002
|
||||
# Increase timeout for OpenAI API calls (default is 10 seconds)
|
||||
spring.ai.openai.chat.options.connection-timeout=60s
|
||||
spring.ai.openai.chat.options.read-timeout=60s
|
||||
spring.ai.openai.embedding.options.connection-timeout=60s
|
||||
spring.ai.openai.embedding.options.read-timeout=60s
|
||||
|
||||
# Spring AI Ollama Configuration (disabled to avoid bean conflicts)
|
||||
spring.ai.ollama.enabled=false
|
||||
spring.ai.ollama.base-url=http://localhost:11434
|
||||
spring.ai.ollama.chat.enabled=false
|
||||
spring.ai.ollama.chat.options.model=llama3
|
||||
spring.ai.ollama.chat.options.temperature=1.0
|
||||
spring.ai.ollama.embedding.enabled=false
|
||||
spring.ai.ollama.embedding.options.model=nomic-embed-text
|
||||
|
||||
# Force OpenAPI 3.0 specification version
|
||||
springdoc.swagger-ui.path=/swagger-ui.html
|
||||
springdoc.api-docs.version=OPENAPI_3_0
|
||||
|
||||
posthog.api.key=phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq
|
||||
posthog.host=https://eu.i.posthog.com
|
||||
|
||||
spring.main.allow-bean-definition-overriding=true
|
||||
spring.ai.openai.enabled=false
|
||||
|
||||
# Set up a consistent temporary directory location
|
||||
java.io.tmpdir=${stirling.tempfiles.directory:${java.io.tmpdir}/stirling-pdf}
|
||||
|
||||
@ -52,8 +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-openai'
|
||||
implementation 'org.springframework.ai:spring-ai-ollama'
|
||||
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'
|
||||
implementation 'com.bucket4j:bucket4j_jdk17-core:8.15.0'
|
||||
|
||||
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
package stirling.software.proprietary.config;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Spring AI Configuration for Stirling PDF Chatbot
|
||||
*
|
||||
* <p>This configuration enables Spring AI auto-configuration for chatbot features. The actual
|
||||
* ChatModel and EmbeddingModel beans are provided by Spring Boot's auto-configuration based on the
|
||||
* spring.ai.* properties in application-proprietary.properties
|
||||
*
|
||||
* <p>For OpenAI: - spring.ai.openai.enabled=true - spring.ai.openai.api-key=your-api-key
|
||||
*
|
||||
* <p>For Ollama (as fallback): - spring.ai.ollama.enabled=true -
|
||||
* spring.ai.ollama.base-url=http://localhost:11434
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class SpringAIConfig {
|
||||
|
||||
public SpringAIConfig() {
|
||||
log.info("Spring AI Configuration enabled for Stirling PDF Chatbot");
|
||||
log.info(
|
||||
"ChatModel and EmbeddingModel beans will be auto-configured based on spring.ai.* properties");
|
||||
}
|
||||
|
||||
/** Primary ChatModel bean that delegates to OpenAI's auto-configured bean */
|
||||
@Bean
|
||||
@Primary
|
||||
public ChatModel primaryChatModel(@Qualifier("openAiChatModel") ChatModel openAiChatModel) {
|
||||
log.info("Using OpenAI ChatModel as primary");
|
||||
return openAiChatModel;
|
||||
}
|
||||
|
||||
/** Primary EmbeddingModel bean that delegates to OpenAI's auto-configured bean */
|
||||
@Bean
|
||||
@Primary
|
||||
public EmbeddingModel primaryEmbeddingModel(
|
||||
@Qualifier("openAiEmbeddingModel") EmbeddingModel openAiEmbeddingModel) {
|
||||
log.info("Using OpenAI EmbeddingModel as primary");
|
||||
return openAiEmbeddingModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom RestTemplate for Spring AI OpenAI client with increased timeouts. This helps prevent
|
||||
* timeout errors when processing large documents or complex queries.
|
||||
*/
|
||||
@Bean(name = "openAiRestTemplate")
|
||||
public RestTemplate openAiRestTemplate(RestTemplateBuilder builder) {
|
||||
log.info("Creating custom RestTemplate for OpenAI with 60s timeouts");
|
||||
return builder.connectTimeout(Duration.ofSeconds(60))
|
||||
.readTimeout(Duration.ofSeconds(60))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom RestClient for Spring AI OpenAI with increased timeouts. Spring AI 1.0.3+ prefers
|
||||
* RestClient over RestTemplate.
|
||||
*/
|
||||
@Bean(name = "openAiRestClient")
|
||||
public RestClient openAiRestClient() {
|
||||
log.info("Creating custom RestClient for OpenAI with 60s timeouts");
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(Duration.ofSeconds(60));
|
||||
factory.setReadTimeout(Duration.ofSeconds(60));
|
||||
|
||||
return RestClient.builder().requestFactory(factory).build();
|
||||
}
|
||||
}
|
||||
@ -3,8 +3,6 @@ package stirling.software.proprietary.controller;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@ -31,11 +29,11 @@ import stirling.software.proprietary.service.chatbot.ChatbotSessionRegistry;
|
||||
import stirling.software.proprietary.service.chatbot.exception.ChatbotException;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/internal/chatbot")
|
||||
@RequestMapping("/api/v1/internal/chatbot")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
@ConditionalOnBean(ChatbotService.class)
|
||||
// @ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
// @ConditionalOnBean(ChatbotService.class)
|
||||
public class ChatbotController {
|
||||
|
||||
private final ChatbotService chatbotService;
|
||||
|
||||
@ -4,7 +4,6 @@ import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
@ -18,7 +17,7 @@ import stirling.software.proprietary.service.chatbot.exception.NoTextDetectedExc
|
||||
|
||||
@RestControllerAdvice(assignableTypes = ChatbotController.class)
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
// @ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
@ConditionalOnBean(ChatbotService.class)
|
||||
public class ChatbotExceptionHandler {
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
@ -26,7 +25,7 @@ import stirling.software.proprietary.model.chatbot.ChatbotTextChunk;
|
||||
import stirling.software.proprietary.service.chatbot.exception.ChatbotException;
|
||||
|
||||
@Service
|
||||
@ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
// @ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
@Slf4j
|
||||
public class ChatbotCacheService {
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package stirling.software.proprietary.service.chatbot;
|
||||
|
||||
import static stirling.software.proprietary.service.chatbot.ChatbotFeatureProperties.ChatbotSettings.ModelProvider.OLLAMA;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
@ -15,11 +17,8 @@ import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.ollama.OllamaChatModel;
|
||||
import org.springframework.ai.ollama.api.OllamaOptions;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@ -35,14 +34,11 @@ import stirling.software.proprietary.model.chatbot.ChatbotResponse;
|
||||
import stirling.software.proprietary.model.chatbot.ChatbotSession;
|
||||
import stirling.software.proprietary.model.chatbot.ChatbotTextChunk;
|
||||
import stirling.software.proprietary.service.chatbot.ChatbotFeatureProperties.ChatbotSettings;
|
||||
import stirling.software.proprietary.service.chatbot.ChatbotFeatureProperties.ChatbotSettings.ModelProvider;
|
||||
import stirling.software.proprietary.service.chatbot.exception.ChatbotException;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
@ConditionalOnBean(ChatModel.class)
|
||||
public class ChatbotConversationService {
|
||||
|
||||
private final ChatModel chatModel;
|
||||
@ -159,7 +155,7 @@ public class ChatbotConversationService {
|
||||
}
|
||||
|
||||
private void ensureModelSwitchCapability(ChatbotSettings settings) {
|
||||
ModelProvider provider = settings.models().provider();
|
||||
ChatbotSettings.ModelProvider provider = settings.models().provider();
|
||||
switch (provider) {
|
||||
case OPENAI -> {
|
||||
if (!(chatModel instanceof OpenAiChatModel)) {
|
||||
@ -262,22 +258,15 @@ public class ChatbotConversationService {
|
||||
+ "Question: "
|
||||
+ question;
|
||||
|
||||
Object options = buildChatOptions(settings, model);
|
||||
OpenAiChatOptions options = buildChatOptions(model);
|
||||
|
||||
return new Prompt(
|
||||
List.of(new SystemMessage(systemPrompt), new UserMessage(userPrompt)), options);
|
||||
}
|
||||
|
||||
private Object buildChatOptions(ChatbotSettings settings, String model) {
|
||||
return switch (settings.models().provider()) {
|
||||
case OPENAI ->
|
||||
OpenAiChatOptions.builder()
|
||||
.model(model)
|
||||
.temperature(0.2)
|
||||
.responseFormat("json_object")
|
||||
.build();
|
||||
case OLLAMA -> OllamaOptions.builder().model(model).temperature(0.2).build();
|
||||
};
|
||||
private OpenAiChatOptions buildChatOptions(String model) {
|
||||
// Note: Some models only support default temperature value of 1.0
|
||||
return OpenAiChatOptions.builder().model(model).temperature(1.0).build();
|
||||
}
|
||||
|
||||
private ModelReply parseModelResponse(String raw) {
|
||||
|
||||
@ -9,8 +9,6 @@ import java.util.UUID;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@ -25,8 +23,6 @@ import stirling.software.proprietary.service.chatbot.exception.ChatbotException;
|
||||
import stirling.software.proprietary.service.chatbot.exception.NoTextDetectedException;
|
||||
|
||||
@Service
|
||||
@ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
@ConditionalOnBean(EmbeddingModel.class)
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ChatbotIngestionService {
|
||||
|
||||
@ -7,8 +7,6 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@ -23,8 +21,6 @@ import stirling.software.proprietary.service.chatbot.exception.ChatbotException;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
@ConditionalOnBean(EmbeddingModel.class)
|
||||
public class ChatbotRetrievalService {
|
||||
|
||||
private final ChatbotCacheService cacheService;
|
||||
|
||||
@ -3,8 +3,6 @@ package stirling.software.proprietary.service.chatbot;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -18,8 +16,6 @@ import stirling.software.proprietary.service.AuditService;
|
||||
import stirling.software.proprietary.service.chatbot.exception.ChatbotException;
|
||||
|
||||
@Service
|
||||
@ConditionalOnProperty(value = "premium.proFeatures.chatbot.enabled", havingValue = "true")
|
||||
@ConditionalOnBean({ChatbotIngestionService.class, ChatbotConversationService.class})
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ChatbotService {
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
spring.ai.openai.enabled=true
|
||||
spring.ai.openai.api-key=your-proprietary-api-key-here
|
||||
@ -48,7 +48,11 @@ class ChatbotServiceTest {
|
||||
true,
|
||||
4000,
|
||||
0.5D,
|
||||
new ChatbotSettings.ModelSettings("gpt-5-nano", "gpt-5-mini", "embed"),
|
||||
new ChatbotSettings.ModelSettings(
|
||||
ChatbotSettings.ModelProvider.OPENAI,
|
||||
"gpt-5-nano",
|
||||
"gpt-5-mini",
|
||||
"embed"),
|
||||
new ChatbotSettings.RagSettings(512, 128, 4),
|
||||
new ChatbotSettings.CacheSettings(60, 10, 1000),
|
||||
new ChatbotSettings.OcrSettings(false),
|
||||
@ -60,7 +64,11 @@ class ChatbotServiceTest {
|
||||
true,
|
||||
4000,
|
||||
0.5D,
|
||||
new ChatbotSettings.ModelSettings("gpt-5-nano", "gpt-5-mini", "embed"),
|
||||
new ChatbotSettings.ModelSettings(
|
||||
ChatbotSettings.ModelProvider.OPENAI,
|
||||
"gpt-5-nano",
|
||||
"gpt-5-mini",
|
||||
"embed"),
|
||||
new ChatbotSettings.RagSettings(512, 128, 4),
|
||||
new ChatbotSettings.CacheSettings(60, 10, 1000),
|
||||
new ChatbotSettings.OcrSettings(false),
|
||||
|
||||
@ -40,12 +40,12 @@ export interface ChatbotMessageResponse {
|
||||
}
|
||||
|
||||
export async function createChatbotSession(payload: ChatbotSessionPayload) {
|
||||
const { data } = await apiClient.post<ChatbotSessionInfo>('/api/internal/chatbot/session', payload);
|
||||
const { data } = await apiClient.post<ChatbotSessionInfo>('/api/v1/internal/chatbot/session', payload);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function sendChatbotPrompt(payload: ChatbotQueryPayload) {
|
||||
const { data } = await apiClient.post<ChatbotMessageResponse>('/api/internal/chatbot/query', payload);
|
||||
const { data } = await apiClient.post<ChatbotMessageResponse>('/api/v1/internal/chatbot/query', payload);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user