Add inference service.
This commit is contained in:
parent
0b62e06646
commit
a38ac6b6f5
@ -0,0 +1,37 @@
|
||||
package app.mealsmadeeasy.api.inference;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/inferences")
|
||||
public class InferenceController {
|
||||
|
||||
private final InferenceService inferenceService;
|
||||
|
||||
public InferenceController(InferenceService inferenceService) {
|
||||
this.inferenceService = inferenceService;
|
||||
}
|
||||
|
||||
@PutMapping("/recipe-extract")
|
||||
public ResponseEntity<String> recipeExtract(@RequestParam MultipartFile recipeImageFile) throws IOException {
|
||||
return ResponseEntity.ok(this.inferenceService.extractRecipe(recipeImageFile.getInputStream()));
|
||||
}
|
||||
|
||||
@PutMapping(value = "/recipe-extract-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<ServerSentEvent<Map<String, String>>> recipeExtractStream(@RequestParam MultipartFile recipeImageFile) throws IOException {
|
||||
return this.inferenceService.extractRecipeStream(recipeImageFile.getInputStream())
|
||||
.map(data -> ServerSentEvent.builder(Map.of("delta", data)).build());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package app.mealsmadeeasy.api.inference;
|
||||
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.content.Media;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.MimeType;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@Service
|
||||
public class InferenceService {
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
public InferenceService(ChatClient.Builder chatClientBuilder) {
|
||||
this.chatClient = chatClientBuilder.build();
|
||||
}
|
||||
|
||||
public String extractRecipe(InputStream recipeImageInputStream) {
|
||||
final Media media = Media.builder()
|
||||
.data(new InputStreamResource(recipeImageInputStream))
|
||||
.mimeType(MimeType.valueOf("image/jpeg"))
|
||||
.build();
|
||||
|
||||
final String markdownResponse = this.chatClient.prompt()
|
||||
.user(u ->
|
||||
u.text(new ClassPathResource("app/mealsmadeeasy/api/inference/recipe-extract-user-prompt.md"))
|
||||
.media(media)
|
||||
)
|
||||
.call()
|
||||
.content();
|
||||
return markdownResponse;
|
||||
}
|
||||
|
||||
public Flux<String> extractRecipeStream(InputStream recipeImageInputStream) {
|
||||
final Media media = Media.builder()
|
||||
.data(new InputStreamResource(recipeImageInputStream))
|
||||
.mimeType(MimeType.valueOf("image/jpeg"))
|
||||
.build();
|
||||
|
||||
return this.chatClient.prompt()
|
||||
.user(u ->
|
||||
u.text(new ClassPathResource("app/mealsmadeeasy/api/inference/recipe-extract-user-prompt.md"))
|
||||
.media(media)
|
||||
)
|
||||
.stream().content();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package app.mealsmadeeasy.api.inference;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "recipe_extraction")
|
||||
public class RecipeExtractionEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Lob
|
||||
@Column(columnDefinition = "TEXT")
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String markdown;
|
||||
|
||||
public UUID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMarkdown() {
|
||||
return this.markdown;
|
||||
}
|
||||
|
||||
public void setMarkdown(String markdown) {
|
||||
this.markdown = markdown;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package app.mealsmadeeasy.api.inference;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class RecipeExtractionView {
|
||||
|
||||
public static RecipeExtractionView from(RecipeExtractionEntity entity) {
|
||||
final var view = new RecipeExtractionView();
|
||||
view.setId(entity.getId());
|
||||
view.setMarkdown(entity.getMarkdown());
|
||||
return view;
|
||||
}
|
||||
|
||||
private UUID id;
|
||||
private String markdown;
|
||||
|
||||
public UUID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMarkdown() {
|
||||
return this.markdown;
|
||||
}
|
||||
|
||||
public void setMarkdown(String markdown) {
|
||||
this.markdown = markdown;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
Convert the recipe in the image to Markdown.
|
||||
@ -19,3 +19,5 @@ app.mealsmadeeasy.api.images.bucketName=images
|
||||
|
||||
# AI
|
||||
spring.ai.vectorstore.pgvector.dimensions=1024
|
||||
spring.ai.ollama.chat.options.model=deepseek-ocr:latest
|
||||
spring.ai.ollama.init.pull-model-strategy=never
|
||||
Loading…
Reference in New Issue
Block a user