Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add summarization #17

Merged
merged 1 commit into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 45 additions & 7 deletions LOCAL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Run Local

## Web
## Run components

### Run web

Run locally in a terminal with:

Expand All @@ -12,23 +14,59 @@ cd web
npm run dev
```

## Backend
### Run Backend

Run locally on another terminal with:

```bash
cd backend
```

Edit `/backend/src/main/resources/application.yaml` to have the correct values.
Copy `/backend/src/main/resources/application.yaml` to `/backend/src/main/resources/application-local.yaml` and modify the values required.

It should look like this:

```yaml
spring:
main:
banner-mode: "off"
profiles:
active: production
datasource:
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@ADB_SERVICE_NAME_GOES_HERE_high?TNS_ADMIN=/PATH/TO/WALLET/UNZIPPED/IN/TERRAFORM/GENERATED
username: ADMIN
password: "ADB_PASSWORD_GOES_HERE"
type: oracle.ucp.jdbc.PoolDataSource
oracleucp:
sql-for-validate-connection: SELECT * FROM dual
connection-pool-name: connectionPoolName1
initial-pool-size: 5
min-pool-size: 5
max-pool-size: 10
jpa:
hibernate:
use-new-id-generator-mappings: false
ddl-auto: update
oracle:
jdbc:
fanEnabled: true

genai:
endpoint: "https://inference.generativeai.us-chicago-1.oci.oraclecloud.com"
region: "us-chicago-1"
compartment_id: "GENAI_COMPARTMENT_OCID_GOES_HERE"
chat_model_id: "GEN_AI_CHAT_MODEL_OCID_GOES_HERE"
summarization_model_id: "GEN_AI_SUMMARIZATION_MODEL_OCID_GOES_HERE"
```

Run the Spring Boot backend application in local profile:

```bash
./gradlew bootRun
./gradlew bootRun -Plocal
```

## Build for distribution

## Build artifacts
## Other tasks

### Build Java Application:

Expand Down
1 change: 1 addition & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation 'com.oracle.database.security:oraclepki:21.8.0.0'
implementation 'com.oracle.database.security:osdt_cert:21.8.0.0'
implementation 'com.oracle.database.security:osdt_core:21.8.0.0'
implementation 'org.apache.pdfbox:pdfbox:3.0.2' exclude(group: 'commons-logging', module: 'commons-logging')
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Invalid request params")
public class InvalidPromptRequest extends RuntimeException {


}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.victormartin.oci.genai.backend.backend;
package dev.victormartin.oci.genai.backend.backend.config;

import com.oracle.bmc.ClientConfiguration;
import com.oracle.bmc.retrier.RetryConfiguration;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package dev.victormartin.oci.genai.backend.backend;
package dev.victormartin.oci.genai.backend.backend.config;

import com.oracle.bmc.ClientConfiguration;
import com.oracle.bmc.ConfigFileReader;
import com.oracle.bmc.Region;
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider;
import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider;
import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider;
import com.oracle.bmc.generativeai.GenerativeAiClient;
import jakarta.annotation.PostConstruct;
Expand Down Expand Up @@ -39,8 +38,11 @@ public class GenerativeAiClientConfig {
@Value("${genai.config.profile}")
private String CONFIG_PROFILE;

@Value("${genai.model_id}")
private String modelId;
@Value("${genai.chat_model_id}")
private String chatModelId;

@Value("${genai.summarization_model_id}")
private String summarizationModelId;

private Region region;

Expand All @@ -62,12 +64,11 @@ GenerativeAiClient genAiClient() throws IOException {
}

GenerativeAiClient instancePrincipalConfig() throws IOException {
final OkeWorkloadIdentityAuthenticationDetailsProvider okeProvider =
new OkeWorkloadIdentityAuthenticationDetailsProvider
.OkeWorkloadIdentityAuthenticationDetailsProviderBuilder()
.build();
// final InstancePrincipalsAuthenticationDetailsProvider provider =
// new InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder().build();
final OkeWorkloadIdentityAuthenticationDetailsProvider okeProvider = new OkeWorkloadIdentityAuthenticationDetailsProvider.OkeWorkloadIdentityAuthenticationDetailsProviderBuilder()
.build();
// final InstancePrincipalsAuthenticationDetailsProvider provider =
// new
// InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder().build();

GenerativeAiClient generativeAiClient = new GenerativeAiClient(okeProvider, clientConfiguration);
generativeAiClient.setRegion(okeProvider.getRegion());
Expand All @@ -76,9 +77,11 @@ GenerativeAiClient instancePrincipalConfig() throws IOException {
}

GenerativeAiClient localConfig() throws IOException {
// Configuring the AuthenticationDetailsProvider. It's assuming there is a default OCI config file
// "~/.oci/config", and a profile in that config with the name defined in CONFIG_PROFILE variable.
final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(CONFIG_LOCATION, CONFIG_PROFILE);
// Configuring the AuthenticationDetailsProvider. It's assuming there is a
// default OCI config file
// "~/.oci/config", and a profile in that config with the name defined in
// CONFIG_PROFILE variable.
final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(CONFIG_LOCATION, CONFIG_PROFILE);
final AuthenticationDetailsProvider provider = new ConfigFileAuthenticationDetailsProvider(configFile);

GenerativeAiClient generativeAiClient = new GenerativeAiClient(provider,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.victormartin.oci.genai.backend.backend;
package dev.victormartin.oci.genai.backend.backend.config;

import com.oracle.bmc.ClientConfiguration;
import com.oracle.bmc.ConfigFileReader;
Expand Down Expand Up @@ -38,8 +38,11 @@ public class GenerativeAiInferenceClientConfig {
@Value("${genai.config.profile}")
private String CONFIG_PROFILE;

@Value("${genai.model_id}")
private String modelId;
@Value("${genai.chat_model_id}")
private String modelChatId;

@Value("${genai.summarization_model_id}")
private String modelSummarizationId;

private Region region;

Expand All @@ -62,8 +65,8 @@ GenerativeAiInferenceClient genAiInferenceClient() throws IOException {
}

GenerativeAiInferenceClient instancePrincipalConfig() throws IOException {
final InstancePrincipalsAuthenticationDetailsProvider provider =
new InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder().build();
final InstancePrincipalsAuthenticationDetailsProvider provider = new InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder()
.build();

GenerativeAiInferenceClient generativeAiInferenceClient = new GenerativeAiInferenceClient(provider,
clientConfiguration);
Expand All @@ -73,12 +76,14 @@ GenerativeAiInferenceClient instancePrincipalConfig() throws IOException {
}

GenerativeAiInferenceClient localConfig() throws IOException {
// Configuring the AuthenticationDetailsProvider. It's assuming there is a default OCI config file
// "~/.oci/config", and a profile in that config with the name defined in CONFIG_PROFILE variable.
final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(CONFIG_LOCATION, CONFIG_PROFILE);
// Configuring the AuthenticationDetailsProvider. It's assuming there is a
// default OCI config file
// "~/.oci/config", and a profile in that config with the name defined in
// CONFIG_PROFILE variable.
final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(CONFIG_LOCATION, CONFIG_PROFILE);
final AuthenticationDetailsProvider provider = new ConfigFileAuthenticationDetailsProvider(configFile);

GenerativeAiInferenceClient generativeAiInferenceClient = new GenerativeAiInferenceClient(provider,
GenerativeAiInferenceClient generativeAiInferenceClient = new GenerativeAiInferenceClient(provider,
clientConfiguration);
generativeAiInferenceClient.setEndpoint(ENDPOINT);
generativeAiInferenceClient.setRegion(region);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.victormartin.oci.genai.backend.backend;
package dev.victormartin.oci.genai.backend.backend.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package dev.victormartin.oci.genai.backend.backend;
package dev.victormartin.oci.genai.backend.backend.controller;

import com.oracle.bmc.ClientConfiguration;
import com.oracle.bmc.generativeai.GenerativeAiClient;
import com.oracle.bmc.generativeai.model.ModelCapability;
import com.oracle.bmc.generativeai.requests.ListModelsRequest;
import com.oracle.bmc.generativeai.responses.ListModelsResponse;
import dev.victormartin.oci.genai.backend.backend.dao.GenAiModel;
import org.bouncycastle.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package dev.victormartin.oci.genai.backend.backend.controller;


import dev.victormartin.oci.genai.backend.backend.service.OCIGenAIService;
import dev.victormartin.oci.genai.backend.backend.service.PDFConvertorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;

@RestController
public class PDFConvertorController {
Logger log = LoggerFactory.getLogger(PDFConvertorController.class);

@Value("${storage.path}")
String storagePath;

@Value("${genai.summarization_model_id}")
String summarizationModelId;

@Autowired
OCIGenAIService ociGenAIService;

@Autowired
PDFConvertorService pdfConvertorService;

@Autowired
SummaryController summaryController;

@PostMapping("/api/upload")
public String fileUploading(@RequestParam("file") MultipartFile multipartFile) {
String filename = StringUtils.cleanPath(multipartFile.getOriginalFilename());
log.info("File uploaded {} {} bytes ({})", filename, multipartFile.getSize(), multipartFile.getContentType());
try {
if (filename.contains("..")) {
throw new Exception("Filename contains invalid path sequence");
}
if (multipartFile.getBytes().length > (1024 * 1024)) {
throw new Exception("File size exceeds maximum limit");
}
String fileDestinationPath = StringUtils.cleanPath(storagePath);
File file = new File(fileDestinationPath + File.separator + filename);
multipartFile.transferTo(file);
log.info("File destination path: {}", file.getAbsolutePath());
String convertedText = pdfConvertorService.convert(file.getAbsolutePath());
String summaryText = ociGenAIService.summaryText(convertedText, summarizationModelId);
log.info("Summary text: {}(...)", summaryText.substring(0, 40));
summaryController.handleSummary(summaryText);
return summaryText;
} catch (MaxUploadSizeExceededException maxUploadSizeExceededException) {
log.error(maxUploadSizeExceededException.getMessage());
throw new RuntimeException(maxUploadSizeExceededException);
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.victormartin.oci.genai.backend.backend;
package dev.victormartin.oci.genai.backend.backend.controller;

import com.oracle.bmc.model.BmcException;
import dev.victormartin.oci.genai.backend.backend.InvalidPromptRequest;
import dev.victormartin.oci.genai.backend.backend.service.OCIGenAIService;
import dev.victormartin.oci.genai.backend.backend.dao.Answer;
import dev.victormartin.oci.genai.backend.backend.dao.Prompt;
import dev.victormartin.oci.genai.backend.backend.data.Interaction;
Expand All @@ -21,14 +23,17 @@
public class PromptController {
Logger logger = LoggerFactory.getLogger(PromptController.class);

@Value("${genai.model_id}")
private String hardcodedModelId;
@Value("${genai.chat_model_id}")
private String hardcodedChatModelId;

@Value("${genai.summarization_model_id}")
private String hardcodedSummarizationModelId;

@Autowired
private final InteractionRepository interactionRepository;

@Autowired
OCIGenAIService genAI;
OCIGenAIService genAI;

public PromptController(InteractionRepository interactionRepository, OCIGenAIService genAI) {
this.interactionRepository = interactionRepository;
Expand All @@ -40,17 +45,21 @@ public PromptController(InteractionRepository interactionRepository, OCIGenAISer
public Answer handlePrompt(Prompt prompt) {
String promptEscaped = HtmlUtils.htmlEscape(prompt.content());
logger.info("Prompt " + promptEscaped + " received, on model " + prompt.modelId() + " but using hardcoded one" +
" " + hardcodedModelId);
" " + hardcodedChatModelId);
Interaction interaction = new Interaction();
interaction.setConversationId(prompt.conversationId());
interaction.setDatetimeRequest(new Date());
interaction.setModelId(hardcodedModelId);
interaction.setModelId(hardcodedChatModelId);
interaction.setRequest(promptEscaped);
Interaction saved = interactionRepository.save(interaction);
try {
if (prompt.content() == null || prompt.content().length()< 1) { throw new InvalidPromptRequest(); }
// if (prompt.modelId() == null || !prompt.modelId().startsWith("ocid1.generativeaimodel.")) { throw new InvalidPromptRequest(); }
String responseFromGenAI = genAI.request(promptEscaped, hardcodedModelId);
if (prompt.content() == null || prompt.content().length() < 1) {
throw new InvalidPromptRequest();
}
// if (prompt.modelId() == null ||
// !prompt.modelId().startsWith("ocid1.generativeaimodel.")) { throw new
// InvalidPromptRequest(); }
String responseFromGenAI = genAI.request(promptEscaped, hardcodedChatModelId);
saved.setDatetimeResponse(new Date());
saved.setResponse(responseFromGenAI);
interactionRepository.save(saved);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.victormartin.oci.genai.backend.backend.controller;

import com.oracle.bmc.model.BmcException;
import dev.victormartin.oci.genai.backend.backend.InvalidPromptRequest;
import dev.victormartin.oci.genai.backend.backend.dao.Answer;
import dev.victormartin.oci.genai.backend.backend.dao.Prompt;
import dev.victormartin.oci.genai.backend.backend.data.Interaction;
import dev.victormartin.oci.genai.backend.backend.data.InteractionRepository;
import dev.victormartin.oci.genai.backend.backend.service.OCIGenAIService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;

import java.util.Date;

@Controller
public class SummaryController {
Logger logger = LoggerFactory.getLogger(SummaryController.class);

@SendToUser("/queue/summary")
public Answer handleSummary(String summary) {
logger.info("handleSummary");
return new Answer(summary , "");
}

}
Loading