-
-
Notifications
You must be signed in to change notification settings - Fork 749
Add Spring AI for OQL Assistant #11749
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
Open
FlorisVleugels
wants to merge
10
commits into
cBioPortal:master
Choose a base branch
from
FlorisVleugels:oql-assistant
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
35e893a
Start adding cbioportal assistant
FlorisVleugels 38658b4
Add openai integration
FlorisVleugels de6be6c
Simplify ai integration with Spring ai
FlorisVleugels 6ac09f6
Add oql context as system message to prompt
FlorisVleugels 7099dd7
Update oql context
FlorisVleugels a164ef3
Cleanup
FlorisVleugels 242ae7a
Add example properties for spring.ai
FlorisVleugels ac5e4d6
Swap to generic ChatModel
FlorisVleugels b9abbef
Update font awesome for new icons
FlorisVleugels 7d253e0
Add spring.ai.enabled to FrontendProperties
FlorisVleugels File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
src/main/java/org/cbioportal/application/assistant/GeneAssistantService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package org.cbioportal.application.assistant; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.UncheckedIOException; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.List; | ||
| import org.springframework.ai.chat.messages.Message; | ||
| import org.springframework.ai.chat.messages.SystemMessage; | ||
| import org.springframework.ai.chat.messages.UserMessage; | ||
| import org.springframework.ai.chat.model.ChatModel; | ||
| import org.springframework.ai.chat.model.ChatResponse; | ||
| import org.springframework.ai.chat.prompt.Prompt; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| import org.springframework.core.io.ClassPathResource; | ||
| import org.springframework.core.io.Resource; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| @Service | ||
| @ConditionalOnProperty(name = "spring.ai.enabled", havingValue = "true") | ||
| public class GeneAssistantService { | ||
|
|
||
| private static final String OQL_CONTEXT_FILE = "oql-context.st"; | ||
|
|
||
| private final ChatModel chatModel; | ||
|
|
||
| @Autowired | ||
| public GeneAssistantService(ChatModel chatModel) { | ||
| this.chatModel = chatModel; | ||
| } | ||
|
|
||
| public String generateResponse(String message) { | ||
| try { | ||
| Resource oqlContextResource = new ClassPathResource(OQL_CONTEXT_FILE); | ||
| String oqlContext = | ||
| new String(oqlContextResource.getInputStream().readAllBytes(), StandardCharsets.UTF_8); | ||
| Message systemMessage = new SystemMessage(oqlContext); | ||
| Message userMessage = new UserMessage(message); | ||
|
|
||
| Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); | ||
| ChatResponse response = this.chatModel.call(prompt); | ||
| return response.getResult().getOutput().getText().toString(); | ||
|
|
||
| } catch (IOException e) { | ||
| throw new UncheckedIOException("Failed to read oql context prompt resource", e); | ||
| } | ||
| } | ||
| } |
16 changes: 16 additions & 0 deletions
16
src/main/java/org/cbioportal/legacy/model/GeneAssistantResponse.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package org.cbioportal.legacy.model; | ||
|
|
||
| import java.io.Serializable; | ||
|
|
||
| public class GeneAssistantResponse implements Serializable { | ||
|
|
||
| private String aiResponse; | ||
|
|
||
| public String getAiResponse() { | ||
| return aiResponse; | ||
| } | ||
|
|
||
| public void setAiResponse(String aiResponse) { | ||
| this.aiResponse = aiResponse; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
src/main/java/org/cbioportal/legacy/web/GeneAssistantController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package org.cbioportal.legacy.web; | ||
|
|
||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import org.cbioportal.application.assistant.GeneAssistantService; | ||
| import org.cbioportal.legacy.model.GeneAssistantResponse; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.validation.annotation.Validated; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @Validated | ||
| @RestController() | ||
| @ConditionalOnProperty(name = "spring.ai.enabled", havingValue = "true") | ||
| public class GeneAssistantController { | ||
|
|
||
| private final GeneAssistantService geneAssistantService; | ||
|
|
||
| @Autowired | ||
| public GeneAssistantController(GeneAssistantService geneAssistantService) { | ||
| this.geneAssistantService = geneAssistantService; | ||
| } | ||
|
|
||
| @Operation(description = "Send query to AI model for gene assistance") | ||
| @ApiResponse( | ||
| responseCode = "200", | ||
| description = "OK", | ||
| content = @Content(schema = @Schema(implementation = GeneAssistantResponse.class))) | ||
| @PostMapping(value = "/api/assistant", produces = MediaType.APPLICATION_JSON_VALUE) | ||
| public ResponseEntity<GeneAssistantResponse> fetchGeneAssistantResponse( | ||
| @RequestBody String message) { | ||
|
|
||
| String response = geneAssistantService.generateResponse(message); | ||
| GeneAssistantResponse geneAssistantResponse = new GeneAssistantResponse(); | ||
| geneAssistantResponse.setAiResponse(response); | ||
|
|
||
| return ResponseEntity.ok(geneAssistantResponse); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # role: system | ||
| You are an expert in cBioPortal's Onco Query Language (OQL). Your job is to generate correct, minimal, and valid OQL queries based on user input. | ||
|
|
||
| Respond ONLY with a valid OQL query suitable for cBioPortal, using the following syntax and keywords. | ||
| Prepend OQL queries with `OQL: TRUE` and prepend verbal responses with `OQL: FALSE`. | ||
|
|
||
| --- | ||
|
|
||
| ## Syntax Format | ||
| GENE: OQL_KEYWORDS; OQL: TRUE | ||
|
|
||
| --- | ||
|
|
||
| ## OQL Keywords | ||
| - MUT: all non-synonymous mutations | ||
| - MUT = <protein change> (e.g., V600E) | ||
| - MUT = <mutation type> (MISSENSE, NONSENSE, NONSTART, NONSTOP, FRAMESHIFT, INFRAME, SPLICE, TRUNC) | ||
| - MUT = (<position range>) e.g., (12-13) or (718-854) | ||
| - FUSION: all gene fusions | ||
| - AMP: amplification | ||
| - HOMDEL: deep/homozygous deletion | ||
| - GAIN: copy number gain | ||
| - HETLOSS: shallow deletion / loss of heterozygosity | ||
| - CNA >= GAIN: equivalent to GAIN + AMP | ||
| - EXP > x or < -x: mRNA expression x SD above or below mean | ||
| - PROT > x or < -x: protein expression x SD above or below mean | ||
|
|
||
| --- | ||
|
|
||
| ## Modifiers | ||
| - DRIVER: restrict to driver events | ||
| - GERMLINE / SOMATIC: restrict to mutation origin | ||
|
|
||
| Modifiers may be combined, e.g.: | ||
| - DRIVER_MUT | ||
| - GERMLINE_MUT | ||
| - SOMATIC_MUT | ||
| - DRIVER_FUSION | ||
|
|
||
| --- | ||
|
|
||
| ## Operators | ||
| - `!=` : exclude a specific mutation | ||
| - `DATATYPES` : apply keywords to multiple genes if all genes are queried for the same variant type or modifier. | ||
|
|
||
| --- | ||
|
|
||
| ## Merged Tracks | ||
| Use square brackets to group genes, optionally with a label in double quotes. | ||
| Example: | ||
| ["TP53 PATHWAY" TP53 P53AIP1] OQL: TRUE | ||
|
|
||
| --- | ||
|
|
||
| ## Output Rules | ||
| - Always produce **only** the valid OQL query. | ||
| - Do **not** add explanations, natural language, or commentary. | ||
| - Combine multiple genes logically using per-gene syntax or DATATYPES: when appropriate. | ||
| - Use HUGO gene symbols instead of Ensembl IDs. | ||
|
|
||
| --- | ||
|
|
||
| ## Examples | ||
|
|
||
| User: "all TP53 mutations" | ||
| → `TP53: MUT OQL: TRUE` | ||
|
|
||
| User: "show me genes in the MAPK pathway" | ||
| → `KRAS NRAS BRAF MAP2K1 MAP2K2 MAP3K1 MAP3K3 MAP3K7 RAF1 RPS6KA3` | ||
|
|
||
| User: "query for all EGFR driver fusion events" | ||
| → `EGFR: FUSION_DRIVER OQL: TRUE` | ||
|
|
||
| User: "query TP53 mutations except for missense mutations" | ||
| → `TP53: MUT != MISSENSE OQL: TRUE` | ||
|
|
||
| User: "show me BRCA1 nonsense germline driver mutations" | ||
| → `BRCA1: NONSENSE_GERMLINE_DRIVER OQL: TRUE` | ||
|
|
||
| User: "search for all KRAS mutations at position 12" | ||
| → `KRAS: MUT = (12-12) OQL: TRUE` | ||
|
|
||
| User: "search for all KRAS mutations at positions 12 and 13" | ||
| → `KRAS: MUT = (12-13) OQL: TRUE` | ||
|
|
||
| --- | ||
|
|
||
| ## Instruction | ||
| Now respond to this user request using the OQL rules above: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wondering if we need to set CORS restrictions somewhere for these endpoints? So only users of the website can use m