Skip to content

Commit b65e744

Browse files
author
beaglebyte
committed
First Classes, compiled with no errors
1 parent 5184f2e commit b65e744

22 files changed

+1203
-6
lines changed

pom.xml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
<dependency>
8484
<groupId>org.apache.tika</groupId>
8585
<artifactId>tika-core</artifactId>
86-
<version>2.9.1</version>
86+
<version>3.2.2</version>
8787
</dependency>
8888

8989
<!-- PDFBox for direct PDF property access -->
@@ -93,7 +93,21 @@
9393
<version>3.0.1</version>
9494
</dependency>
9595

96-
96+
<!-- gRPC dependencies for Qdrant client -->
97+
<dependency>
98+
<groupId>io.grpc</groupId>
99+
<artifactId>grpc-netty-shaded</artifactId>
100+
</dependency>
101+
<dependency>
102+
<groupId>io.grpc</groupId>
103+
<artifactId>grpc-protobuf</artifactId>
104+
<version>1.75.0</version>
105+
</dependency>
106+
<dependency>
107+
<groupId>io.grpc</groupId>
108+
<artifactId>grpc-stub</artifactId>
109+
<version>1.75.0</version>
110+
</dependency>
97111

98112
</dependencies>
99113
<dependencyManagement>
@@ -105,6 +119,12 @@
105119
<type>pom</type>
106120
<scope>import</scope>
107121
</dependency>
122+
<!-- Pin grpc-netty-shaded to a safe, updated version to override BOM/transitive versions -->
123+
<dependency>
124+
<groupId>io.grpc</groupId>
125+
<artifactId>grpc-netty-shaded</artifactId>
126+
<version>1.75.0</version>
127+
</dependency>
108128
</dependencies>
109129
</dependencyManagement>
110130

src/main/java/com/spring_rag/spring_rag/ServletInitializer.java renamed to src/main/java/com/spring_rag/ServletInitializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.spring_rag.spring_rag;
1+
package com.spring_rag;
22

33
import org.springframework.boot.builder.SpringApplicationBuilder;
44
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

src/main/java/com/spring_rag/spring_rag/SpringRagApplication.java renamed to src/main/java/com/spring_rag/SpringRagApplication.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.spring_rag.spring_rag;
1+
package com.spring_rag;
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.spring_rag.config;
2+
3+
import io.qdrant.client.QdrantClient;
4+
import io.qdrant.client.QdrantGrpcClient;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
/**
11+
* Qdrant client configuration
12+
*/
13+
@Slf4j
14+
@Configuration
15+
public class QdrantConfig {
16+
17+
@Value("${spring.vectorstore.qdrant.host:localhost}")
18+
private String qdrantHost;
19+
20+
@Value("${spring.vectorstore.qdrant.port:6334}")
21+
private int qdrantPort;
22+
23+
@Bean
24+
public QdrantClient qdrantClient() {
25+
log.info("Initializing Qdrant client: {}:{}", qdrantHost, qdrantPort);
26+
27+
// Create QdrantClient directly with host and port (Qdrant Java client API)
28+
return new QdrantClient(
29+
QdrantGrpcClient.newBuilder(qdrantHost, qdrantPort, false).build()
30+
);
31+
}
32+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.spring_rag.config;
2+
3+
import lombok.Data;
4+
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
import org.springframework.stereotype.Component;
6+
7+
import java.util.Map;
8+
9+
@Component
10+
@ConfigurationProperties(prefix = "rag")
11+
@Data
12+
public class TopicConfig {
13+
private Map<String, TopicInfo> topics;
14+
15+
@Data
16+
public static class TopicInfo {
17+
private String collectionName;
18+
private String description;
19+
private String model;
20+
}
21+
22+
/**
23+
* Get collection name for a topic
24+
*/
25+
public String getCollectionName(String topic) {
26+
TopicInfo info = topics.get(topic);
27+
if (info == null) {
28+
throw new IllegalArgumentException("Unknown topic: " + topic);
29+
}
30+
return info.getCollectionName();
31+
}
32+
33+
/**
34+
* Check if topic exists
35+
*/
36+
public boolean hasTopic(String topic) {
37+
return topics.containsKey(topic);
38+
}
39+
40+
/**
41+
* Get all topics
42+
*/
43+
public Map<String, TopicInfo> getAllTopics() {
44+
return topics;
45+
}
46+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.spring_rag.config;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.web.servlet.config.annotation.CorsRegistry;
5+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6+
7+
@Configuration
8+
public class WebConfig implements WebMvcConfigurer {
9+
10+
@Override
11+
public void addCorsMappings(CorsRegistry registry) {
12+
registry.addMapping("/api/**")
13+
.allowedOrigins("*")
14+
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
15+
.allowedHeaders("*")
16+
.maxAge(3600);
17+
}
18+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.spring_rag.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j. Slf4j;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org. springframework.web.bind.annotation. RequestMapping;
7+
import org.springframework. web.bind.annotation.RestController;
8+
import reactor.core.publisher.Mono;
9+
import com.spring_rag.config.TopicConfig;
10+
import com.spring_rag.service.VectorStoreFactory;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
@Slf4j
16+
@RestController
17+
@RequestMapping("/api/health")
18+
@RequiredArgsConstructor
19+
public class HealthController {
20+
21+
private final TopicConfig topicConfig;
22+
private final VectorStoreFactory vectorStoreFactory;
23+
24+
@GetMapping
25+
public Mono<Map<String, Object>> health() {
26+
return Mono.fromCallable(() -> {
27+
Map<String, Object> health = new HashMap<>();
28+
health. put("status", "UP");
29+
health.put("timestamp", System.currentTimeMillis());
30+
health.put("topics_configured", topicConfig.getAllTopics().size());
31+
health.put("topics", topicConfig.getAllTopics().keySet());
32+
33+
try {
34+
Map<String, Object> collectionStats = vectorStoreFactory. getCollectionStats();
35+
health.put("collections", collectionStats);
36+
health.put("database_status", "CONNECTED");
37+
} catch (Exception ex) {
38+
health.put("database_status", "DISCONNECTED");
39+
health.put("error", ex.getMessage());
40+
}
41+
42+
return health;
43+
});
44+
}
45+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.spring_rag.controller;
2+
3+
import com.spring_rag.config.TopicConfig;
4+
import com.spring_rag.dto.DocumentResponse;
5+
import com.spring_rag.dto.QueryRequest;
6+
import com.spring_rag.dto.QueryResponse;
7+
import com.spring_rag.service.TopicDocumentService;
8+
import com.spring_rag.service.TopicRagService;
9+
import com.spring_rag.service.VectorStoreFactory;
10+
import lombok.RequiredArgsConstructor;
11+
import lombok.extern.slf4j.Slf4j;
12+
import org.springframework.http.MediaType;
13+
import org.springframework.http.codec.multipart.FilePart;
14+
import org.springframework.web.bind.annotation.*;
15+
import reactor.core.publisher.Mono;
16+
17+
import java.util.List;
18+
import java.util.Map;
19+
20+
@Slf4j
21+
@RestController
22+
@RequestMapping("/api/v1/topics")
23+
@RequiredArgsConstructor
24+
@CrossOrigin(origins = "*", maxAge = 3600)
25+
public class TopicRagController {
26+
27+
private final TopicDocumentService documentService;
28+
private final TopicRagService ragService;
29+
private final TopicConfig topicConfig;
30+
private final VectorStoreFactory vectorStoreFactory;
31+
32+
/**
33+
* Get all available topics
34+
*/
35+
@GetMapping
36+
public Mono<Map<String, TopicConfig. TopicInfo>> getTopics() {
37+
return Mono.just(topicConfig.getAllTopics());
38+
}
39+
40+
/**
41+
* Get collection stats for all topics
42+
*/
43+
@GetMapping("/stats")
44+
public Mono<Map<String, Object>> getStats() {
45+
return Mono.fromCallable(vectorStoreFactory::getCollectionStats);
46+
}
47+
48+
/**
49+
* Upload PDF to a specific topic
50+
*/
51+
@PostMapping(value = "/{topic}/documents/upload/pdf",
52+
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
53+
public Mono<DocumentResponse> uploadPdfToTopic(
54+
@PathVariable String topic,
55+
@RequestPart("file") FilePart file) {
56+
log.info("Uploading PDF to topic '{}': {}", topic, file.filename());
57+
return documentService.uploadPdfToTopic(topic, file);
58+
}
59+
60+
/**
61+
* Upload Markdown to a specific topic
62+
*/
63+
@PostMapping(value = "/{topic}/documents/upload/markdown",
64+
consumes = MediaType. MULTIPART_FORM_DATA_VALUE)
65+
public Mono<DocumentResponse> uploadMarkdownToTopic(
66+
@PathVariable String topic,
67+
@RequestPart("file") FilePart file) {
68+
log.info("Uploading Markdown to topic '{}': {}", topic, file. filename());
69+
return documentService. uploadMarkdownToTopic(topic, file);
70+
}
71+
72+
/**
73+
* Query a specific topic RAG
74+
*/
75+
@PostMapping(value = "/{topic}/query",
76+
consumes = MediaType.APPLICATION_JSON_VALUE)
77+
public Mono<QueryResponse> queryTopic(
78+
@PathVariable String topic,
79+
@RequestBody QueryRequest request) {
80+
log.info("Query topic '{}': {}", topic, request.getQuery());
81+
return ragService.queryTopic(topic, request);
82+
}
83+
84+
/**
85+
* Query across multiple topics (cross-topic search)
86+
*/
87+
@PostMapping(value = "/query/cross",
88+
consumes = MediaType.APPLICATION_JSON_VALUE)
89+
public Mono<QueryResponse> queryCrossTopic(
90+
@RequestParam List<String> topics,
91+
@RequestBody QueryRequest request) {
92+
log.info("Cross-topic query across {}: {}", topics, request. getQuery());
93+
return ragService.queryCrossTopic(topics, request);
94+
}
95+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.spring_rag.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@NoArgsConstructor
10+
@AllArgsConstructor
11+
@Builder
12+
public class DocumentResponse {
13+
private String id;
14+
private String filename;
15+
private String title;
16+
private String author;
17+
private Integer publishingYear;
18+
private String type;
19+
private String topic; // NEW: topic identifier
20+
private int chunksCount;
21+
private String status;
22+
private long uploadedAt;
23+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.spring_rag.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
@Data
8+
@NoArgsConstructor
9+
@AllArgsConstructor
10+
public class QueryRequest {
11+
private String query;
12+
private int topK = 5; // Number of relevant documents to retrieve
13+
}

0 commit comments

Comments
 (0)