feat(common): 添加 RustFsClient 和 AiServiceClient (T018/T019)
This commit is contained in:
149
src/main/java/com/label/common/ai/AiServiceClient.java
Normal file
149
src/main/java/com/label/common/ai/AiServiceClient.java
Normal file
@@ -0,0 +1,149 @@
|
||||
package com.label.common.ai;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class AiServiceClient {
|
||||
|
||||
@Value("${ai-service.base-url}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${ai-service.timeout:30000}")
|
||||
private int timeoutMs;
|
||||
|
||||
private RestClient restClient;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
restClient = RestClient.builder()
|
||||
.baseUrl(baseUrl)
|
||||
.build();
|
||||
}
|
||||
|
||||
// DTO classes
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class ExtractionRequest {
|
||||
private Long sourceId;
|
||||
private String filePath;
|
||||
private String bucket;
|
||||
private String model;
|
||||
private String prompt;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ExtractionResponse {
|
||||
private List<Map<String, Object>> items; // triple/quadruple items
|
||||
private String rawOutput;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class VideoProcessRequest {
|
||||
private Long sourceId;
|
||||
private String filePath;
|
||||
private String bucket;
|
||||
private Map<String, Object> params; // frameInterval, mode etc.
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class QaGenResponse {
|
||||
private List<Map<String, Object>> qaPairs;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class FinetuneRequest {
|
||||
private String datasetPath; // RustFS path to JSONL file
|
||||
private String model;
|
||||
private Long batchId;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class FinetuneResponse {
|
||||
private String jobId;
|
||||
private String status;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class FinetuneStatusResponse {
|
||||
private String jobId;
|
||||
private String status; // PENDING/RUNNING/COMPLETED/FAILED
|
||||
private Integer progress; // 0-100
|
||||
private String errorMessage;
|
||||
}
|
||||
|
||||
// The 8 endpoints:
|
||||
|
||||
public ExtractionResponse extractText(ExtractionRequest request) {
|
||||
return restClient.post()
|
||||
.uri("/extract/text")
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.body(ExtractionResponse.class);
|
||||
}
|
||||
|
||||
public ExtractionResponse extractImage(ExtractionRequest request) {
|
||||
return restClient.post()
|
||||
.uri("/extract/image")
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.body(ExtractionResponse.class);
|
||||
}
|
||||
|
||||
public void extractFrames(VideoProcessRequest request) {
|
||||
restClient.post()
|
||||
.uri("/video/extract-frames")
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.toBodilessEntity();
|
||||
}
|
||||
|
||||
public void videoToText(VideoProcessRequest request) {
|
||||
restClient.post()
|
||||
.uri("/video/to-text")
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.toBodilessEntity();
|
||||
}
|
||||
|
||||
public QaGenResponse genTextQa(ExtractionRequest request) {
|
||||
return restClient.post()
|
||||
.uri("/qa/gen-text")
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.body(QaGenResponse.class);
|
||||
}
|
||||
|
||||
public QaGenResponse genImageQa(ExtractionRequest request) {
|
||||
return restClient.post()
|
||||
.uri("/qa/gen-image")
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.body(QaGenResponse.class);
|
||||
}
|
||||
|
||||
public FinetuneResponse startFinetune(FinetuneRequest request) {
|
||||
return restClient.post()
|
||||
.uri("/finetune/start")
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.body(FinetuneResponse.class);
|
||||
}
|
||||
|
||||
public FinetuneStatusResponse getFinetuneStatus(String jobId) {
|
||||
return restClient.get()
|
||||
.uri("/finetune/status/{jobId}", jobId)
|
||||
.retrieve()
|
||||
.body(FinetuneStatusResponse.class);
|
||||
}
|
||||
}
|
||||
118
src/main/java/com/label/common/storage/RustFsClient.java
Normal file
118
src/main/java/com/label/common/storage/RustFsClient.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package com.label.common.storage;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.core.sync.RequestBody;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.model.*;
|
||||
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
|
||||
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RustFsClient {
|
||||
|
||||
@Value("${rustfs.endpoint}")
|
||||
private String endpoint;
|
||||
|
||||
@Value("${rustfs.access-key}")
|
||||
private String accessKey;
|
||||
|
||||
@Value("${rustfs.secret-key}")
|
||||
private String secretKey;
|
||||
|
||||
private S3Client s3Client;
|
||||
private S3Presigner presigner;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
var credentials = StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create(accessKey, secretKey));
|
||||
|
||||
s3Client = S3Client.builder()
|
||||
.endpointOverride(URI.create(endpoint))
|
||||
.credentialsProvider(credentials)
|
||||
.region(Region.US_EAST_1)
|
||||
.forcePathStyle(true) // Required for MinIO/RustFS
|
||||
.build();
|
||||
|
||||
presigner = S3Presigner.builder()
|
||||
.endpointOverride(URI.create(endpoint))
|
||||
.credentialsProvider(credentials)
|
||||
.region(Region.US_EAST_1)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file to RustFS.
|
||||
* @param bucket bucket name
|
||||
* @param key object key (path)
|
||||
* @param inputStream file content
|
||||
* @param contentLength file size in bytes
|
||||
* @param contentType MIME type
|
||||
*/
|
||||
public void upload(String bucket, String key, InputStream inputStream,
|
||||
long contentLength, String contentType) {
|
||||
// Ensure bucket exists
|
||||
ensureBucketExists(bucket);
|
||||
|
||||
s3Client.putObject(
|
||||
PutObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key(key)
|
||||
.contentType(contentType)
|
||||
.contentLength(contentLength)
|
||||
.build(),
|
||||
RequestBody.fromInputStream(inputStream, contentLength)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file from RustFS.
|
||||
*/
|
||||
public InputStream download(String bucket, String key) {
|
||||
return s3Client.getObject(
|
||||
GetObjectRequest.builder().bucket(bucket).key(key).build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file from RustFS.
|
||||
*/
|
||||
public void delete(String bucket, String key) {
|
||||
s3Client.deleteObject(
|
||||
DeleteObjectRequest.builder().bucket(bucket).key(key).build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a presigned URL for temporary read access.
|
||||
* @param expirationMinutes URL validity in minutes
|
||||
*/
|
||||
public String getPresignedUrl(String bucket, String key, int expirationMinutes) {
|
||||
var presignRequest = GetObjectPresignRequest.builder()
|
||||
.signatureDuration(Duration.ofMinutes(expirationMinutes))
|
||||
.getObjectRequest(GetObjectRequest.builder().bucket(bucket).key(key).build())
|
||||
.build();
|
||||
|
||||
return presigner.presignGetObject(presignRequest).url().toString();
|
||||
}
|
||||
|
||||
private void ensureBucketExists(String bucket) {
|
||||
try {
|
||||
s3Client.headBucket(HeadBucketRequest.builder().bucket(bucket).build());
|
||||
} catch (NoSuchBucketException e) {
|
||||
s3Client.createBucket(CreateBucketRequest.builder().bucket(bucket).build());
|
||||
log.info("Created bucket: {}", bucket);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user