├── .github └── workflows │ ├── banned_file_changes_pr.yml │ ├── cla.yml │ ├── license_audit.yml │ ├── release-zip-file.yml │ ├── repolinter.yml │ └── sonarcloud.yml ├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── encodings.xml ├── jarRepositories.xml ├── misc.xml ├── springai-rag-db23ai.iml └── vcs.xml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docs ├── get-started-java-development.pdf └── oracle-database-23c-new-features-guide.pdf ├── img ├── addIngress.png ├── bootvolume.png ├── cover.png ├── create.png ├── dbtype.png ├── dbverify.png ├── ide.png ├── image.png ├── instance.png ├── rule.png ├── securitylist.png ├── shape.png └── vcn.png ├── json-dual.sql ├── license_policy.yml ├── pom.xml ├── release_files.json ├── repolinter.json ├── sonar-project.properties ├── src ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ ├── demoai │ │ │ ├── DemoaiApplication.java │ │ │ ├── OracleDBVectorStore.java │ │ │ ├── controller │ │ │ │ └── DemoaiController.java │ │ │ └── service │ │ │ │ └── VectorService.java │ │ │ └── model │ │ │ ├── JsonDualDTO.java │ │ │ ├── MessageDTO.java │ │ │ └── VectorData.java │ └── resources │ │ ├── application-dev.properties │ │ ├── application-prod.properties │ │ ├── application.properties │ │ ├── json_dual.json │ │ └── prompt-template.txt └── test │ └── java │ └── com │ └── example │ └── demoai │ └── DemoaiApplicationTests.java └── target └── classes ├── application-dev.properties ├── application-prod.properties ├── application.properties ├── json_dual.json └── prompt-template.txt /.github/workflows/banned_file_changes_pr.yml: -------------------------------------------------------------------------------- 1 | name: Banned file changes (PR) 2 | on: 3 | # pull_request: 4 | # branches: [ "**/*" ] 5 | pull_request_target: 6 | 7 | jobs: 8 | check_for_banned_file_changes: 9 | name: Look for unsupported (banned) file modifications on PRs 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: 'Get number of git commits' 13 | uses: oracle-devrel/action-git-num-commits@v0.1-alpha6 14 | id: num_commits 15 | with: 16 | pull_url: ${{ github.event.pull_request.url }} 17 | - name: 'Checkout repo' 18 | uses: actions/checkout@v2 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | fetch-depth: ${{ steps.num_commits.outputs.fetch_depth }} 23 | - name: Get file changes 24 | uses: oracle-devrel/action-git-files-changed@v0.1-alpha2 25 | id: files 26 | with: 27 | pull_url: ${{ github.event.pull_request.url }} 28 | - name: Look for changes to .github 29 | if: contains(steps.files.outputs.all_files_changed, '.github') 30 | run: | 31 | echo 'Changes to files in .github are not allowed.' 32 | - name: Comment if .github changed 33 | if: contains(steps.files.outputs.all_files_changed, '.github') 34 | uses: mshick/add-pr-comment@v1 35 | with: 36 | message: | 37 | :no_entry: **Banned Files Modified** 38 | Changes to files in `.github` are not permitted. Please revert your changes and re-submit a new PR. Simply changing the file back to its original state and re-committing won't work (you must revert the changes made to it). 39 | repo-token: ${{ secrets.GITHUB_TOKEN }} 40 | - name: Look for changes to license_policy.yml 41 | if: contains(steps.files.outputs.all_files_changed, '"license_policy.yml"') 42 | run: | 43 | echo 'Changes to license_policy.yml are not allowed.' 44 | - name: Comment if license_policy.yml changed 45 | if: contains(steps.files.outputs.all_files_changed, '"license_policy.yml"') 46 | uses: mshick/add-pr-comment@v1 47 | with: 48 | message: | 49 | :no_entry: **Banned Files Modified** 50 | Changes to `license_policy.yml` are not permitted. Please revert your changes and re-submit a new PR. Simply changing the file back to its original state and re-committing won't work (you must revert the changes made to it). 51 | repo-token: ${{ secrets.GITHUB_TOKEN }} 52 | - name: Look for changes to repolinter.json 53 | if: contains(steps.files.outputs.all_files_changed, '"repolinter.json"') 54 | uses: mshick/add-pr-comment@v1 55 | with: 56 | message: | 57 | :no_entry: **Banned Files Modified** 58 | Changes to `repolinter.json` are not permitted. Please revert your changes and re-submit a new PR. Simply changing the file back to its original state and re-committing won't work (you must revert the changes made to it). 59 | repo-token: ${{ secrets.GITHUB_TOKEN }} 60 | - name: Comment if repolinter.json changed 61 | if: contains(steps.files.outputs.all_files_changed, '"repolinter.json"') 62 | run: | 63 | echo 'Changes to repolinter.json are not allowed.' 64 | - name: Look for changes to sonar-project.properties 65 | if: contains(steps.files.outputs.all_files_changed, '"sonar-project.properties"') 66 | uses: mshick/add-pr-comment@v1 67 | with: 68 | message: | 69 | :no_entry: **Banned Files Modified** 70 | Changes to `sonar-project.properties` are not permitted. Please revert your changes and re-submit a new PR. Simply changing the file back to its original state and re-committing won't work (you must revert the changes made to it). 71 | repo-token: ${{ secrets.GITHUB_TOKEN }} 72 | - name: Comment if sonar-project.properties changed 73 | if: contains(steps.files.outputs.all_files_changed, '"sonar-project.properties"') 74 | run: | 75 | echo 'Changes to sonar-project.properties are not allowed.' 76 | - name: Fail on banned file changes 77 | if: contains(steps.files.outputs.all_files_changed, '.github') || contains(steps.files.outputs.all_files_changed, '"license_policy.yml"') || contains(steps.files.outputs.all_files_changed, '"repolinter.json"') || contains(steps.files.outputs.all_files_changed, '"sonar-project.properties"') 78 | run: | 79 | exit 1 -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | name: "CLA Assistant" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened,closed,synchronize] 7 | 8 | jobs: 9 | CLAssistant: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "CLA Assistant" 13 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 14 | # Beta Release 15 | uses: cla-assistant/github-action@v2.1.2-beta 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | # the below token should have repo scope and must be manually added by you in the repository's secret 19 | PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }} 20 | with: 21 | # for per-repo CLA-acceptance: 22 | # path-to-signatures: 'signatures/oca-20210504/${{ github.repository }}' 23 | # for per-GHO CLA-acceptance: 24 | path-to-signatures: 'signatures/oca-20210504/oracledevrel' 25 | path-to-document: 'https://github.com/oracledevrel/devrel-oca-mgmt/blob/main/oca-20210504.md' # e.g. a CLA or a DCO document 26 | # branch should not be protected 27 | branch: 'main' 28 | allowlist: bot* 29 | 30 | #below are the optional inputs - If the optional inputs are not given, then default values will be taken 31 | remote-organization-name: "oracledevrel" # enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) 32 | remote-repository-name: "devrel-oca-mgmt" # enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository) 33 | #create-file-commit-message: 'For example: Creating file for storing CLA Signatures' 34 | #signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo' 35 | #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign' 36 | #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' 37 | #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' 38 | #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) 39 | #use-dco-flag: true - If you are using DCO instead of CLA 40 | -------------------------------------------------------------------------------- /.github/workflows/license_audit.yml: -------------------------------------------------------------------------------- 1 | name: Audit licenses 2 | on: 3 | pull_request_target: 4 | 5 | jobs: 6 | run_scancode_toolkit: 7 | name: Get inventory of licenses used in project 8 | runs-on: ubuntu-latest 9 | container: 10 | image: ghcr.io/oracledevrel/scancode-toolkit:v21.3.31 11 | credentials: 12 | username: ${{ github.actor }} 13 | password: ${{ secrets.GHCR_PAT }} 14 | steps: 15 | - name: 'Checkout repo' 16 | uses: actions/checkout@v2 17 | with: 18 | ref: ${{ github.event.pull_request.head.ref }} 19 | repository: ${{ github.event.pull_request.head.repo.full_name }} 20 | - name: Run Scancode-toolkit 21 | run: | 22 | scancode -l --ignore licenses.json --ignore .github/**/* --ignore license_policy.yml --license-policy license_policy.yml --only-findings --summary --json-pp licenses.json * 23 | echo "\n\nHere is the licenses.json:\n" 24 | echo $(cat licenses.json) 25 | - name: Look for non-approved licenses 26 | uses: oracle-devrel/action-license-audit@1.0.2 27 | id: analysis 28 | with: 29 | licenses_file: '/github/workspace/licenses.json' 30 | - name: Analysis results 31 | run: echo "${{ steps.analysis.outputs.unapproved_licenses }}" 32 | - name: Comment if analysis finds unapproved licenses 33 | if: steps.analysis.outputs.unapproved_licenses == 'true' 34 | uses: mshick/add-pr-comment@v1 35 | with: 36 | message: | 37 | :no_entry: **License Inspection** 38 | Requires manual inspection. There are some licenses which dictate further analysis and review. 39 | repo-token: ${{ secrets.GITHUB_TOKEN }} 40 | - name: Halt pipeline on unapproved licenses 41 | if: steps.analysis.outputs.unapproved_licenses == 'true' 42 | run: exit 1 43 | -------------------------------------------------------------------------------- /.github/workflows/release-zip-file.yml: -------------------------------------------------------------------------------- 1 | name: Release ZIP file packaging 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | create_zip: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: 'Checkout repo' 12 | uses: actions/checkout@v2 13 | - name: 'Make (and upload) ZIP file(s)' 14 | uses: oracle-devrel/action-release-zip-maker@v0.5 15 | id: zip_maker 16 | with: 17 | github_token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/repolinter.yml: -------------------------------------------------------------------------------- 1 | name: Repolinter 2 | on: 3 | pull_request_target: 4 | jobs: 5 | run_repolinter: 6 | name: Run Repolinter on pull request 7 | runs-on: ubuntu-latest 8 | container: 9 | image: ghcr.io/oracledevrel/repolinter:v0.11.1 10 | credentials: 11 | username: ${{ github.actor }} 12 | password: ${{ secrets.GHCR_PAT }} 13 | steps: 14 | - name: 'Checkout repo' 15 | uses: actions/checkout@v2 16 | with: 17 | ref: ${{ github.event.pull_request.head.ref }} 18 | repository: ${{ github.event.pull_request.head.repo.full_name }} 19 | - name: Run Repolinter 20 | run: | 21 | set +e 22 | bundle exec /app/bin/repolinter.js lint --format json --rulesetFile repolinter.json . > repolinter_results.json 23 | echo "\n\nHere is the repolinter_results.json:\n" 24 | echo $(cat repolinter_results.json) 25 | exit 0 26 | - name: Analyze the Repolinter results 27 | uses: oracle-devrel/action-repolinter-audit@v0.1-alpha2 28 | id: analysis 29 | with: 30 | json_results_file: '/github/workspace/repolinter_results.json' 31 | - name: Overall analysis results 32 | run: | 33 | echo "Passed: ${{ steps.analysis.outputs.passed }}" 34 | echo "Errored: ${{ steps.analysis.outputs.errored }}" 35 | - name: Comment if analysis finds missing disclaimer 36 | if: steps.analysis.outputs.disclaimer_found == 'false' 37 | uses: mshick/add-pr-comment@v1 38 | with: 39 | message: | 40 | :no_entry: **FAILURE: Missing Disclaimer** 41 | The standard Oracle Disclaimer seems to be missing from the readme. Please add it: 42 | 43 | ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT ANY REVIEW. USE AT YOUR OWN RISK. 44 | 45 | Details: 46 | ${{ steps.analysis.outputs.disclaimer_details }} 47 | repo-token: ${{ secrets.GITHUB_TOKEN }} 48 | - name: Comment if analysis finds missing readme 49 | if: steps.analysis.outputs.readme_file_found == 'false' 50 | uses: mshick/add-pr-comment@v1 51 | with: 52 | message: | 53 | :no_entry: **FAILURE: Missing README** 54 | The README file seems to be missing. Please add it. 55 | 56 | Details: 57 | ${{ steps.analysis.outputs.readme_file_details }} 58 | repo-token: ${{ secrets.GITHUB_TOKEN }} 59 | - name: Comment if analysis finds missing license 60 | if: steps.analysis.outputs.license_file_found == 'false' 61 | uses: mshick/add-pr-comment@v1 62 | with: 63 | message: | 64 | :no_entry: **FAILURE: Missing LICENSE** 65 | The LICENSE file seems to be missing. Please add it. 66 | 67 | Details: 68 | ${{ steps.analysis.outputs.license_file_details }} 69 | repo-token: ${{ secrets.GITHUB_TOKEN }} 70 | - name: Comment if analysis finds copyright notice missing 71 | if: steps.analysis.outputs.copyright_found == 'false' 72 | uses: mshick/add-pr-comment@v1 73 | with: 74 | message: | 75 | :warning: **WARNING: Missing Copyright Notice(s)** 76 | It's a good idea to have copyright notices at the top of each file. It looks like at least one file was missing this (though it might be further down in the file - this might be a false-positive). 77 | 78 | Details: 79 | ${{ steps.analysis.outputs.copyright_details }} 80 | repo-token: ${{ secrets.GITHUB_TOKEN }} 81 | - name: Halt pipeline if README is missing 82 | if: steps.analysis.outputs.readme_file_found == 'false' 83 | run: exit 1 84 | - name: Halt pipeline if LICENSE is missing 85 | if: steps.analysis.outputs.license_file_found == 'false' 86 | run: exit 1 87 | - name: Halt pipeline if disclaimer is missing 88 | if: steps.analysis.outputs.disclaimer_found == 'false' 89 | run: exit 1 90 | -------------------------------------------------------------------------------- /.github/workflows/sonarcloud.yml: -------------------------------------------------------------------------------- 1 | name: SonarCloud Scan 2 | on: 3 | pull_request_target: 4 | jobs: 5 | sonarcloud: 6 | name: SonarCloud 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout repo 10 | uses: actions/checkout@v2 11 | with: 12 | ref: ${{ github.event.pull_request.head.ref }} 13 | repository: ${{ github.event.pull_request.head.repo.full_name }} 14 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 15 | - name: SonarCloud Scan 16 | uses: SonarSource/sonarcloud-github-action@master 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 19 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Icon must end with two \r 7 | Icon 8 | 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | 29 | # ignore common security keys 30 | .key 31 | .crt 32 | .csr 33 | .pem -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/springai-rag-db23ai.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this repository 2 | 3 | We welcome your contributions! There are multiple ways to contribute. 4 | 5 | ## Opening issues 6 | 7 | For bugs or enhancement requests, please file a GitHub issue unless it's 8 | security related. When filing a bug remember that the better written the bug is, 9 | the more likely it is to be fixed. If you think you've found a security 10 | vulnerability, do not raise a GitHub issue and follow the instructions in our 11 | [security policy](./SECURITY.md). 12 | 13 | ## Contributing code 14 | 15 | We welcome your code contributions. Before submitting code via a pull request, 16 | you will need to have signed the [Oracle Contributor Agreement][OCA] (OCA) and 17 | your commits need to include the following line using the name and e-mail 18 | address you used to sign the OCA: 19 | 20 | ```text 21 | Signed-off-by: Your Name 22 | ``` 23 | 24 | This can be automatically added to pull requests by committing with `--sign-off` 25 | or `-s`, e.g. 26 | 27 | ```text 28 | git commit --signoff 29 | ``` 30 | 31 | Only pull requests from committers that can be verified as having signed the OCA 32 | can be accepted. 33 | 34 | ## Pull request process 35 | 36 | 1. Ensure there is an issue created to track and discuss the fix or enhancement 37 | you intend to submit. 38 | 1. Fork this repository. 39 | 1. Create a branch in your fork to implement the changes. We recommend using 40 | the issue number as part of your branch name, e.g. `1234-fixes`. 41 | 1. Ensure that any documentation is updated with the changes that are required 42 | by your change. 43 | 1. Ensure that any samples are updated if the base image has been changed. 44 | 1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly 45 | what your changes are meant to do and provide simple steps on how to validate. 46 | your changes. Ensure that you reference the issue you created as well. 47 | 1. We will assign the pull request to 2-3 people for review before it is merged. 48 | 49 | ## Code of conduct 50 | 51 | Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd 52 | like more specific guidelines, see the [Contributor Covenant Code of Conduct][COC]. 53 | 54 | [OCA]: https://oca.opensource.oracle.com 55 | [COC]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/ 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Oracle and/or its affiliates. 2 | 3 | The Universal Permissive License (UPL), Version 1.0 4 | 5 | Subject to the condition set forth below, permission is hereby granted to any 6 | person obtaining a copy of this software, associated documentation and/or data 7 | (collectively the "Software"), free of charge and under any and all copyright 8 | rights in the Software, and any and all patent rights owned or freely 9 | licensable by each licensor hereunder covering either (i) the unmodified 10 | Software as contributed to or provided by such licensor, or (ii) the Larger 11 | Works (as defined below), to deal in both 12 | 13 | (a) the Software, and 14 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 15 | one is included with the Software (each a "Larger Work" to which the Software 16 | is contributed by such licensors), 17 | 18 | without restriction, including without limitation the rights to copy, create 19 | derivative works of, display, perform, and distribute the Software and make, 20 | use, sell, offer for sale, import, export, have made, and have sold the 21 | Software and the Larger Work(s), and to sublicense the foregoing rights on 22 | either these or other terms. 23 | 24 | This license is subject to the following condition: 25 | The above copyright notice and either this complete permission notice or at 26 | a minimum a reference to the UPL must be included in all copies or 27 | substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring AI for RAG on Oracle 23ai Vector DB with OpenAI and private LLMs 2 | 3 | ![cover](./img/cover.png) 4 | 5 | ## Introduction 6 | 7 | In this demo, we'll guide you through the process of leveraging Java, Spring Boot, Oracle DB23ai and the innovative Spring AI APIs to create next-generation applications. 8 | 9 | - Build a Spring Boot Application with RAG (Retrieval Augmented Generation): Discover how to leverage Spring AI to implement a knowledge management system that retrieves relevant information and utilizes large language models to generate insightful responses. 10 | - Integrate Domain Knowledge from Oracle 23ai: Learn how to connect your Spring Boot application with Oracle's 23ai to access and utilize domain-specific knowledge for more accurate and relevant responses. 11 | - Transition to Production with Oracle Backend Platform: We'll address the challenges of moving your knowledge management system from development to production using the Oracle Backend Platform for Spring Boot and Microservices. 12 | 13 | Check out [demo here](https://www.youtube.com/watch?v=H2w6oULzFCo&list=PLPIzp-E1msraY9To-BB-vVzPsK08s4tQD&index=26) 14 | 15 | The demo shows a Retrieval-Augmented Generation using the following modules: 16 | 17 | * Spring AI API 18 | * Oracle DB 23ai 19 | * OpenAI Embeddings 20 | * OpenAI Chat 21 | * OLLAMA local LLM embeddings model 22 | * OLLAMA local LLM LLama2 model for chat 23 | 24 | This demo is based on a early draft example of **Spring AI API**'s implementation for the **Oracle 23ai** as vector store, according to the specifications reported here: **[Vector DBs](https://docs.spring.io/spring-ai/reference/api/vectordbs.html)**. 25 | 26 | There are two different types of files that contribute to the Retrieval-Augmented Generation (RAG) system in this solution: 27 | 28 | - **PDF** file is split in chunks and stored as text with vector embeddings. 29 | - **JSON** docs are created exploiting the **JSON-Duality** capability on existing tables 30 | 31 | The interface, that uses Oracle Database 23ai as a Vector Store in a Spring AI pipeline, is the following: 32 | 33 | ``` 34 | public interface VectorStore { 35 | 36 | void add(List documents); 37 | 38 | Optional delete(List idList); 39 | 40 | List similaritySearch(SearchRequest request); 41 | 42 | List similaritySearch(String query); 43 | } 44 | ``` 45 | 46 | These operations allow uploading documents into a vector database, searching for similar documents using the specific vector distance algorithm chosen (you can change this in the `.properties` files). 47 | 48 | ``` 49 | default List similaritySearch(String query) { 50 | return this.similaritySearch(SearchRequest.query(query)); 51 | } 52 | ``` 53 | 54 | The file `src/main/java/com/example/demoai/OracleDBVectorStore.java` holds this implementation. 55 | 56 | The Vector Store saves the data in this **VECTORTABLE**: 57 | 58 | ``` 59 | CREATE TABLE VECTORTAB ( 60 | id NUMBER GENERATED AS IDENTITY, 61 | text CLOB, 62 | embeddings VECTOR, 63 | metadata JSON, 64 | PRIMARY KEY (id) 65 | ); 66 | ``` 67 | 68 | The **id** will be based on an generated **Identity** Column key, but this can be changed if you prefer. 69 | 70 | The metadata content depends on what's coming from Document object, and in this case it will hold the following data: 71 | 72 | ``` 73 | { 74 | "page_number":"xxx", 75 | "file_name":"xxx", 76 | } 77 | ``` 78 | 79 | This table is created at each application startup by default but, by configuring the `config.dropDb` parameter to `false` in `application-dev.properties`, you can accumulate data every time you start up the application startup, in the same vector tab, and these documents will increase the vector database's knowledge base. 80 | 81 | ## Docs 82 | 83 | With regards to endpoint services, you can find the implementation in [DemoaiController.java](src/main/java/com/example/demoai/controller/DemoaiController.java). The following main REST services have been implemented: 84 | 85 | - **/store** 86 | 87 | Accepts a PDF doc to be chunked, vector embeddings are created and stored in the **VECTORTABLE**. 88 | 89 | - **/store-json** 90 | 91 | Providing the name of a **relational duality view** created on the DB, this service creates, for each JSON record, a vector embedding, chunks it, and stores it in the **VECTORTABLE**. This service shows that you can put both structured and unstructured text data into the RAG, and you'll be able to query this data in natural language as querying a JSON document. 92 | 93 | - **/rag** 94 | Providing a query in natural language, it manages in a Retrieval-Augmented Generation pipeline that uses the content of **VECTORTABLE**, adding the most similar chunks to the question to the context and sending everything using a template in the file: [prompt-template.txt](src/main/resources/prompt-template.txt) 95 | 96 | The following tests have also been implemented, to debug and play with the solution if you're really interested: 97 | 98 | - **/search-similar** 99 | 100 | Returns a list of the nearest chunks to the message provided stored in the **VECTORTABLE**. This means, you can check the "closest matches" in your vector database. It's useful to get info about the context used to determine the prompt sent to the LLM for the completion process and use as references to provide a response. 101 | 102 | - **/delete** 103 | 104 | Allows you to remove a list of chunks, identified by their IDs, from **VECTORTABLE**. 105 | 106 | - **/embedding** 107 | 108 | Provide, given an input string, its corresponding generated vector embedding. 109 | 110 | - **/generate** 111 | 112 | Chat client that doesn't use the RAG pipeline. It could be used as a baseline to show the differences between a response provided by the LLM service as-is (OpenAI, OLLAMA) and an augmented request. It's useful to check if any public content has been used for LLM training, whether the response is near to what you expect, without providing your documents. 113 | 114 | ## 0. Prerequisites 115 | 116 | ### JDBC driver for Oracle DB 23ai 117 | 118 | 119 | 120 | This demo works with the latest `ojdbc11.jar` driver related to the Oracle DBMS (23.4). To run this project, download this driver from Oracle site or directly from your DB server, looking in the directory: `$ORACLE_HOME/jdbc/lib/ojdbc11.jar`. After downloading in your local home dir, import it as a local Maven artifact with this command: 121 | 122 | ``` 123 | mvn install:install-file -Dfile=/ojdbc11.jar -DgroupId=com.oracle.database.jdbc -DartifactId=ojdbc11 -Dversion=23.4.0.0 -Dpackaging=jar -DgeneratePom=true 124 | ``` 125 | or including in the `pom.xml` the following dependency: 126 | 127 | ```xml 128 | 129 | com.oracle.database.jdbc 130 | ojdbc11 131 | 23.4.0.24.05 132 | 133 | ``` 134 | 135 | ### Environment variables 136 | 137 | Set the correct environment variables in a `env.sh` (or put these directly into `/home/$USER/.bashrc`) file with this content, according your server IPs (if you're planning on deploying with oLLaMA): 138 | 139 | ``` 140 | export OPENAI_URL=https://api.openai.com 141 | export OPENAI_MODEL=gpt-3.5-turbo 142 | export OPENAI_EMBEDDING_MODEL=text-embedding-ada-002 143 | export VECTORDB=[VECTORDB_IP] 144 | export DB_USER=vector 145 | export DB_PASSWORD=vector 146 | export OLLAMA_URL=http://[GPU_SERVER_IP]:11434 147 | export OLLAMA_EMBEDDINGS=NousResearch--llama-2-7b-chat-hf 148 | export OLLAMA_MODEL=llama2:7b-chat-fp16 149 | export OPENAI_API_KEY=[YOUR_OPENAI_KEY] 150 | #export OPENAI_URL=http://[GPU_SERVER_IP]:3000 151 | #export OPENAI_MODEL=NousResearch--llama-2-7b-chat-hf 152 | ``` 153 | 154 | To invoke both OpenAI `gpt-3.5-turbo` and `text-embedding-ada-002`, you'll also need your `YOUR_OPENAI_KEY`, which must be obtained directly from the [Open AI developer platform](https://platform.openai.com/). 155 | 156 | About the OLLAMA_EMBEDDINGS/MODEL used, you are free for your experiment to go on the [OLLAMA Library](https://ollama.com/library) and choose other models. 157 | 158 | As you can see, you can configure also the `OPENAI_URL`, which helps to invoke OpenAI LLMs providers compatible with the OpenAI APIs. This way, you can switch easly to other providers, even private ones. 159 | 160 | Set env with command in a shell: 161 | 162 | ``` 163 | source ./env.sh 164 | ``` 165 | 166 | ## 1. Setup 167 | 168 | ### Oracle Database 23ai setup 169 | 170 | 1. Download and install from [Oracle Database Free Get Started](https://www.oracle.com/database/free/get-started/) site an **Oracle Database 23ai Free**, for example, as a docker container in this way: 171 | 172 | ``` 173 | docker run -d -p 1521:1521 --name db23ai container-registry.oracle.com/database/free:latest 174 | docker exec db23ai ./setPassword.sh manager 175 | ``` 176 | 177 | 2. After startup, download and install an Oracle Instant Client from the same [site](https://www.oracle.com/database/free/get-started/), and connect to the instance as shown here: 178 | 179 | ``` 180 | sqlplus sys/manager@"${VECTORDB}:1521/FREEPDB1" as sysdba 181 | ``` 182 | 183 | 3. If running locally: 184 | 185 | ``` 186 | sqlplus sys/manager@"localhost:1521/FREEPDB1" as sysdba 187 | ``` 188 | 189 | to create a **vector** user to run the example: 190 | 191 | ``` 192 | create user vector identified by "vector"; 193 | grant connect to vector; 194 | grant resource to vector; 195 | alter user vector default role connect, resource; 196 | alter user vector quota unlimited on users; 197 | ``` 198 | 199 | Once we've created the user, we'll be able to use it in our Spring AI application by modifying `application-dev.properties`. 200 | 201 | If running locally: 202 | 203 | ``` 204 | sqlplus vector/vector@"localhost:1521/FREEPDB1" as sysdba 205 | ``` 206 | 207 | We can check the content by connecting to the Oracle DB: 208 | 209 | ``` 210 | sqlplus vector/vector@"${VECTORDB}:1521/FREEPDB1" 211 | ``` 212 | 213 | ### Application 214 | 215 | In the `application-dev.properties` files will be used the environment variables set at the step before: 216 | 217 | ``` 218 | spring.ai.openai.api-key=${OPENAI_API_KEY} 219 | spring.ai.openai.base-url=${OPENAI_URL} 220 | spring.ai.openai.chat.options.model=${OPENAI_MODEL} 221 | spring.ai.openai.embedding.options.model=${OPENAI_EMBEDDING_MODEL} 222 | spring.ai.openai.chat.options.temperature=0.3 223 | spring.datasource.url=jdbc:oracle:thin:@${VECTORDB}:1521/ORCLPDB1 224 | spring.datasource.username=${DB_USER} 225 | spring.datasource.password=${DB_PASSWORD} 226 | spring.datasource.driver-class-name=oracle.jdbc.OracleDriver 227 | config.tempDir=tempDir 228 | config.dropDb=true 229 | config.vectorDB=vectortable 230 | config.distance=EUCLIDEAN 231 | spring.servlet.multipart.max-file-size=10MB 232 | spring.servlet.multipart.max-request-size=20MB 233 | spring.ai.ollama.base-url=${OLLAMA_URL} 234 | spring.ai.ollama.embedding.options.model=${OLLAMA_EMBEDDINGS} 235 | spring.ai.ollama.chat.options.model=${OLLAMA_MODEL} 236 | ``` 237 | 238 | In `application.properties`, check if the default env is set as `dev`: 239 | 240 | ``` 241 | spring.profiles.active=dev 242 | ``` 243 | 244 | Then build and run the application: 245 | 246 | - Set env: `source ./env.sh` 247 | - Build: `mvn clean package -Dmaven.test.skip=true` 248 | - Run: `mvn spring-boot:run` 249 | 250 | For each source update, repeat these two steps. 251 | 252 | ## 1. Test OpenAI version 253 | 254 | Check code: 255 | 256 | pom.xml: 257 | 258 | ``` 259 | 260 | 266 | ``` 267 | 268 | DemoaiController.java: 269 | 270 | ``` 271 | //CHANGE 272 | //import org.springframework.ai.ollama.OllamaEmbeddingClient; 273 | //import org.springframework.ai.ollama.OllamaChatClient; 274 | ... 275 | 276 | //CHANGE 277 | private final EmbeddingClient embeddingClient; 278 | //private final OllamaEmbeddingClient embeddingClient; 279 | 280 | //CHANGE 281 | private final ChatClient chatClient; 282 | //private final OllamaChatClient chatClient; 283 | 284 | ... 285 | 286 | //CHANGE 287 | @Autowired 288 | public DemoaiController(EmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // OpenAI full 289 | //public DemoaiController(OllamaEmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // Ollama Embeddings - OpenAI Completion 290 | //public DemoaiController(OllamaEmbeddingClient embeddingClient, OllamaChatClient chatClient, VectorService vectorService) { // Ollama full 291 | 292 | ``` 293 | 294 | VectorService.java: 295 | 296 | ``` 297 | //CHANGE 298 | //import org.springframework.ai.ollama.OllamaChatClient; 299 | ... 300 | //CHANGE 301 | private final ChatClient aiClient; 302 | //private final OllamaChatClient aiClient; 303 | 304 | //CHANGE 305 | VectorService(@Qualifier("openAiChatClient") ChatClient aiClient) { 306 | //VectorService(OllamaChatClient aiClient) { 307 | ``` 308 | 309 | DemoaiApplication.java: 310 | 311 | ``` 312 | //CHANGE 313 | //import org.springframework.ai.ollama.OllamaEmbeddingClient; 314 | 315 | ... 316 | //CHANGE 317 | @Bean 318 | VectorStore vectorStore(EmbeddingClient ec, JdbcTemplate t) { 319 | //VectorStore vectorStore(OllamaEmbeddingClient ec, JdbcTemplate t) { 320 | return new OracleDBVectorStore(t, ec); 321 | } 322 | 323 | ``` 324 | 325 | ### Pre document store 326 | 327 | #### Generic chat 328 | 329 | ```bash 330 | curl -X POST http://localhost:8080/ai/generate \ 331 | -H "Content-Type: application/json" \ 332 | -d '{"message":"What is a Generative AI?"}' | jq -r .generation 333 | ``` 334 | 335 | Here's a sample output from the command: 336 | 337 | ``` 338 | Generative AI refers to artificial intelligence systems that are capable of creating new content, such as images, text, or music, based on patterns and examples provided to them. These systems use algorithms and machine learning techniques to generate realistic and original content that mimics human creativity. Generative AI can be used in a variety of applications, such as creating art, writing stories, or designing products. 339 | ``` 340 | 341 | #### RAG request without any data stored in the DB 342 | 343 | ``` 344 | curl -X POST http://localhost:8080/ai/rag \ 345 | -H "Content-Type: application/json" \ 346 | -d '{"message":"Can I use any kind of development environment to run the example?"}' 347 | ``` 348 | 349 | Output from the command: 350 | 351 | ``` 352 | { 353 | "generation" : "Based on the provided documents, it is not specified whether any kind of development environment can be used to run the example. Therefore, I'm sorry but I haven't enough information to answer." 354 | } 355 | ``` 356 | 357 | ### Search on data coming from a PDF stored 358 | 359 | Store a PDF document in the DBMC 23c library: [**Oracle® Database: Get Started with Java Development**](https://docs.oracle.com/en/database/oracle/oracle-database/23/tdpjd/get-started-java-development.pdf) in the Oracle DB 23ai with embeddings coming from the OpenAI Embedding service. Dowload locally, and run in a shell: 360 | 361 | ``` 362 | curl -X POST -F "file=@./docs/get-started-java-development.pdf" http://localhost:8080/ai/store 363 | ``` 364 | 365 | **Note**: this process usually takes time because document will be splitted in hundreds or thousands of chunks, and for each one it will asked for an embeddings vector to OpenAI API service. In this case has been choosen a small document to wait a few seconds. 366 | 367 | #### Q&A Sample 368 | 369 | Let's look at some info in this document and try to query comparing the results with the actual content: 370 | 371 | - **4.1.1 Oracle Database** 372 | 373 | ![dbtype](./img/dbtype.png) 374 | 375 | ``` 376 | curl -X POST http://localhost:8080/ai/rag \ 377 | -H "Content-Type: application/json" \ 378 | -d '{"message":"Which kind of database you can use to run the Java Web example application) "}' | jq -r .generation 379 | ``` 380 | 381 | Response: 382 | 383 | ``` 384 | You can use either Oracle Autonomous Database or Oracle Database Free available on OTN to run the Java Web example application. 385 | ``` 386 | 387 | - **4.1.5 Integrated Development Environment** 388 | 389 | ![ide](./img/ide.png) 390 | 391 | ``` 392 | curl -X POST http://localhost:8080/ai/rag \ 393 | -H "Content-Type: application/json" \ 394 | -d '{"message":"Can I use any kind of development environment to run the example?"}' | jq -r .generation 395 | ``` 396 | 397 | Response: 398 | 399 | ``` 400 | Based on the information provided in the documents, you can use an Integrated Development Environment (IDE) like IntelliJ Idea community version to develop the Java application that connects to the Oracle Database. The guide specifically mentions using IntelliJ Idea for creating and updating the files for the application. Therefore, it is recommended to use IntelliJ Idea as the development environment for running the example. 401 | ``` 402 | 403 | - **4.2 Verifying the Oracle Database Installation** 404 | 405 | ![dbverify](./img/dbverify.png) 406 | 407 | ``` 408 | curl -X POST http://localhost:8080/ai/rag \ 409 | -H "Content-Type: application/json" \ 410 | -d '{"message":"To run the example, how can I check if the dbms it is working correctly?"}' | jq -r .generation 411 | ``` 412 | 413 | Response: 414 | 415 | ``` 416 | To check if the Oracle Database is working correctly, you can verify the installation by connecting to the database using the following commands: 417 | 1. Navigate to the Oracle Database bin directory: $ cd $ORACLE_HOME/bin 418 | 2. Connect to the database as sysdba: $ ./sqlplus / as sysdba 419 | 420 | If the connection is successful, you will see an output confirming that you are connected to the root container of the database. This indicates that the Oracle Database installation is working correctly. Additionally, you can download the Client Credentials for an ATP instance and verify the connection by following the steps provided in the documentation. 421 | ``` 422 | 423 | First, let's ask for a question not related to the document stored: 424 | 425 | ``` 426 | curl -X POST http://localhost:8080/ai/rag \ 427 | -H "Content-Type: application/json" \ 428 | -d '{"message":"How is the weather tomorrow?"}' | jq -r .generation 429 | ``` 430 | 431 | Response: 432 | 433 | ``` 434 | { 435 | "generation" : "I'm sorry but I haven't enough info to answer." 436 | } 437 | ``` 438 | 439 | Then, let's test similarity search for message **"To run the example, how can I check if the dbms it is working correctly?"** example. The `top_k` parameter determines how many nearest chunks to retrieve is set to **4** by default, and the result set is by default in reverse order. So, we need to execute the fololwing command: 440 | 441 | ``` 442 | curl -X POST http://localhost:8080/ai/search-similar \ 443 | -H "Content-Type: application/json" \ 444 | -d '{"message":"To run the example, how can I check if the dbms it is working correctly?"}' | jq '.[3]' 445 | ``` 446 | 447 | Then, we test the deletion. Indexes begin counting at `1`, so let's execute the following command to delete occurrences 1, 4 and 5: 448 | 449 | ``` 450 | curl "http://localhost:8080/ai/delete?id=1&id=5&id=4" 451 | ``` 452 | 453 | ## 2. Running generations and chat with private LLMs through OLLAMA 454 | 455 | We'll need to create an OCI Compute instance and install OLLAMA inside. Then, we will expose the server through an Internet Gateway and allow our Spring AI application connect to the OLLAMA server and make the equivalent requests as with OpenAI generations. 456 | 457 | The following shape and images are recommended for the server: (it will require a GPU, as we'll be running an HPC load that will require lots of computing! More than the CPU can handle at this moment without quantization enabled.) 458 | 459 | - Shape: `VM.GPU.A10.2` (2x NVIDIA A10 Tensor Cores) 460 | - OCPU: 30 461 | - GPU Memory: 48GB 462 | - CPU Memory: 480GB 463 | - Storage: >250GB 464 | - Max Network Bandwidth: 48Gbps (6GBps) 465 | - Image: Oracle Linux 8.9 466 | 467 | 1. From OCI console, choose Compute/Instances menu: 468 | 469 | ![image](./img/instance.png) 470 | 471 | 2. Press **Create instance** button: 472 | 473 | ![image](./img/create.png) 474 | 475 | 3. Choose `VM.GPU.A10.2` shape, selecting **Virtual machine**/**Specialty and previous generation**: 476 | 477 | ![image](./img/shape.png) 478 | 479 | 4. Choose the Image `Oracle-Linux-8.9-Gen2-GPU-2024.02.26-0` from Oracle Linux 8 list of images: 480 | 481 | ![image](./img/image.png) 482 | 483 | 5. Specify a custom boot volume size and set 100 GB: 484 | 485 | ![image](./img/bootvolume.png) 486 | 487 | 6. Create the image. 488 | 489 | 7. At the end of creation process, obtain the **Public IPv4 address**, and with your private key (the one you generated or uploaded during creation), connect to: 490 | 491 | ``` 492 | ssh -i ./.key opc@[GPU_SERVER_IP] 493 | ``` 494 | 495 | 8. Install and configure docker to use GPUs: 496 | 497 | ``` 498 | sudo /usr/libexec/oci-growfs 499 | curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo 500 | sudo dnf install -y dnf-utils zip unzip 501 | sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo 502 | sudo dnf remove -y runc 503 | sudo dnf install -y docker-ce --nobest 504 | sudo useradd docker_user 505 | ``` 506 | 507 | 9. We need to make sure that your Operating System user has permissions to run Docker containers. To do this, we can run the following command: 508 | 509 | ``` 510 | sudo visudo 511 | ``` 512 | 513 | And add this line at the end: 514 | 515 | ``` 516 | docker_user ALL=(ALL) NOPASSWD: /usr/bin/docker 517 | ``` 518 | 519 | 10. For convenience, we need to switch to our new user. For this, run: 520 | 521 | ``` 522 | sudo su - docker_user 523 | ``` 524 | 525 | 11. Finally, let's add an alias to execute Docker with admin privileges every time we type `docker` in our shell. For this, we need to modify a file, depending on your OS (in `.bash_profile` (MacOS) / `.bashrc` (Linux)). Insert, at the end of the file, this command: 526 | 527 | ``` 528 | alias docker="sudo /usr/bin/docker" 529 | exit 530 | ``` 531 | 532 | 12. We finalize our installation by executing: 533 | 534 | ``` 535 | sudo yum install -y nvidia-container-toolkit 536 | sudo nvidia-ctk runtime configure --runtime=docker 537 | sudo systemctl restart docker 538 | nvidia-ctk runtime configure --runtime=docker --config=$HOME/.config/docker/daemon.json 539 | ``` 540 | 541 | 13. If you're on Ubuntu instead, run: 542 | 543 | ``` 544 | sudo apt-get install nvidia-container-toolkit=1.14.3-1 \ 545 | nvidia-container-toolkit-base=1.14.3-1 \ 546 | libnvidia-container-tools=1.14.3-1 \ 547 | libnvidia-container1=1.14.3-1 548 | sudo apt-get install -y nvidia-docker2 549 | ``` 550 | 551 | 13. Let's reboot and re-connect to the VM, and run again: 552 | 553 | ``` 554 | sudo reboot now 555 | # after restart, run: 556 | sudo su - docker_user 557 | ``` 558 | 559 | 14. Run `docker` to check if everything it's ok. 560 | 561 | 15. Let's run a Docker container with the `ollama/llama2` model for embeddings/completion: 562 | 563 | ``` 564 | docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama serve 565 | docker exec -it ollama ollama pull nomic-embed-text 566 | docker exec -it ollama ollama pull llama2:13b-chat-fp16 567 | docker logs -f --tail 10 ollama 568 | ``` 569 | 570 | Both the model, for embeddings/completion will run under the same server, and they will be addressed providing in the REST request for the specific model required. 571 | 572 | To handle the firewall, we need to open port `11434` on our Security List. For this, let's: 573 | 574 | 1. In **Instance details** click on the **Virtual cloud network:** link: 575 | 576 | ![securitylist](./img/vcn.png) 577 | 578 | 2. In the menu **Resources** click on **Security Lists**: 579 | 580 | ![security](./img/securitylist.png) 581 | 582 | 3. Click on the link of **Default Security List...** 583 | 584 | 4. Click on the **Add Ingress Rules** button: 585 | 586 | ![security](./img/addIngress.png) 587 | 588 | 5. Click on the **Add Ingress Rules** button: 589 | 590 | ![security](./img/addIngress.png) 591 | 592 | 6. Insert details as shown in the following image and then click **Add Ingress Rules** button: 593 | 594 | ![security](./img/rule.png) 595 | 596 | 7. Update the `env.sh` file and run `source ./env.sh`: 597 | 598 | ``` 599 | #export OPENAI_URL=http://[GPU_SERVER_IP]:3000 600 | export OPENAI_URL=https://api.openai.com 601 | #export OPENAI_MODEL=NousResearch--llama-2-7b-chat-hf 602 | export OPENAI_MODEL=gpt-3.5-turbo 603 | export OPENAI_EMBEDDING_MODEL=text-embedding-ada-002 604 | export VECTORDB=[VECTORDB_IP] 605 | export DB_USER=vector 606 | export DB_PASSWORD=vector 607 | export OLLAMA_URL=http://[GPU_SERVER_IP]:11434 608 | export OLLAMA_EMBEDDINGS=NousResearch--llama-2-7b-chat-hf 609 | export OLLAMA_MODEL=llama2:7b-chat-fp16 610 | export OPENAI_API_KEY=[YOUR_OPENAI_KEY] 611 | ``` 612 | 613 | 8. Test with a shell running: 614 | 615 | ``` 616 | curl ${OLLAMA_URL}/api/generate -d '{ 617 | "model": "llama2:7b-chat-fp16", 618 | "prompt":"Why is the sky blue?" 619 | }' 620 | ``` 621 | 622 | You'll receive the response in continuous sequential responses, facilitating the delivery of the content little by little, instead of forcing users to wait for the whole response to be generated before it's desplayed to them. 623 | 624 | ### Customize for private LLMs: Vector Embeddings local, Open AI for Completion 625 | 626 | * pom.xml: uncomment the ollama dependency: 627 | 628 | ``` 629 | 630 | 631 | 632 | org.springframework.ai 633 | spring-ai-ollama-spring-boot-starter 634 | 635 | 636 | ``` 637 | 638 | * DemoaiController.java - uncomment with final source code: 639 | 640 | ``` 641 | //CHANGE 642 | import org.springframework.ai.ollama.OllamaEmbeddingClient; 643 | //import org.springframework.ai.ollama.OllamaChatClient; 644 | ... 645 | 646 | //CHANGE 647 | //private final EmbeddingClient embeddingClient; 648 | private final OllamaEmbeddingClient embeddingClient; 649 | 650 | //CHANGE 651 | private final ChatClient chatClient; 652 | //private final OllamaChatClient chatClient; 653 | ... 654 | 655 | //CHANGE 656 | //public DemoaiController(EmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // OpenAI full 657 | public DemoaiController(OllamaEmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // Ollama Embeddings - OpenAI Completion 658 | //public DemoaiController(OllamaEmbeddingClient embeddingClient, OllamaChatClient chatClient, VectorService vectorService) { // Ollama full 659 | 660 | 661 | ``` 662 | 663 | VectorService.java - check if it's like this: 664 | 665 | ``` 666 | //CHANGE 667 | //import org.springframework.ai.ollama.OllamaChatClient; 668 | 669 | ... 670 | 671 | //CHANGE 672 | private final ChatClient aiClient; 673 | //private final OllamaChatClient aiClient; 674 | 675 | //CHANGE 676 | VectorService(@Qualifier("openAiChatClient") ChatClient aiClient) { 677 | //VectorService(OllamaChatClient aiClient) { 678 | 679 | ``` 680 | 681 | Test as done before. In the gpu docker logs, you'll see the chunks coming to be embedded. 682 | 683 | ### Full private LLMs with `llama2:7b-chat-fp16` 684 | 685 | DemoaiController.java - uncomment with final source code: 686 | 687 | ``` 688 | //CHANGE 689 | import org.springframework.ai.ollama.OllamaEmbeddingClient; 690 | import org.springframework.ai.ollama.OllamaChatClient; 691 | 692 | ... 693 | //CHANGE 694 | //private final EmbeddingClient embeddingClient; 695 | private final OllamaEmbeddingClient embeddingClient; 696 | 697 | //CHANGE 698 | //private final ChatClient chatClient; 699 | private final OllamaChatClient chatClient; 700 | ... 701 | 702 | 703 | //CHANGE 704 | @Autowired 705 | //public DemoaiController(EmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // OpenAI full 706 | //public DemoaiController(OllamaEmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // Ollama Embeddings - OpenAI Completion 707 | public DemoaiController(OllamaEmbeddingClient embeddingClient, OllamaChatClient chatClient, VectorService vectorService) { // Ollama full 708 | 709 | 710 | ``` 711 | 712 | VectorService.java - uncomment with final source code: 713 | 714 | ``` 715 | //CHANGE 716 | import org.springframework.ai.ollama.OllamaChatClient; 717 | 718 | ... 719 | 720 | 721 | //CHANGE 722 | //private final ChatClient aiClient; 723 | private final OllamaChatClient aiClient; 724 | 725 | //CHANGE 726 | //VectorService(@Qualifier("openAiChatClient") ChatClient aiClient) { 727 | VectorService(OllamaChatClient aiClient) { 728 | ``` 729 | 730 | Test as before. Now, you could see a tiny degradation of quality (since the model is quite small), mantaining the same embeddings and context retrieval. For example, with the question: 731 | 732 | ``` 733 | Q: Which kind of database you can use to run the Java Web example application: 734 | A: The document states that the application uses Oracle JDBC Thin driver, Universal Connection Pool (UCP), and Java in the Database (using embedded OJVM). Therefore, you can use an Oracle database to run the Java Web example application. 735 | ``` 736 | 737 | This first result doesn't return a very good, personalized result. However, if we good. But for: 738 | 739 | ``` 740 | Q: Can I use any kind of development environment to run the example? 741 | A: the user can use any development environment to run the example as long as it has a Java compiler and a database manager installed. The DOCUMENTS text does not specify any particular IDE that must be used, so any IDE with Java development capabilities should work. However, the guide does recommend using IntelliJ Idea Community Edition for ease of development, but this is not a requirement.\n\nTo answer the user's question, you could say: \"Yes, you can use any development environment to run the example as long as it has a Java compiler and a database manager installed. While the guide recommends using IntelliJ Idea Community Edition for ease of development, any IDE with Java development capabilities should work.\ 742 | ``` 743 | 744 | The result is more customized and acceptable. 745 | 746 | This trade-off in using private LLMs model could be overcome choosing *larger models*, enough to mantain a good quality. 747 | 748 | > **Note**: the number of billions of parameters of a model version usually has a direct correlation with the size of the model, and its generation quality. The higher, the better, although you also need to watch out for OOM (out of memory) errors and a slower generation throughput. 749 | 750 | ## 3. Deploy on Oracle Backend for Spring Boot and Microservices 751 | 752 | Let's show what Oracle can offer to deploy on an enterprise grade the GenAI application developed so far. 753 | 754 | The platform [**Oracle Backend for Spring Boot and Microservices**](https://oracle.github.io/microservices-datadriven/spring/) allows developers to build microservices in Spring Boot and provision a backend as a service with the Oracle Database and other infrastructure components that operate on multiple clouds. This service vastly simplifies the task of building, testing, and operating microservices platforms for reliable, secure, and scalable enterprise applications. 755 | 756 | To setup this platform, follow the instruction included in **Lab1: Provision an instance** and **Lab 2: Setup your Development Environment** of the [LiveLabs: CloudBank - Building an App with Spring Boot and Mobile APIs with Oracle Database and Kubernetes](https://apexapps.oracle.com/pls/apex/f?p=133:180:7384418726808::::wid:3607). At the end, proceed with the following steps: 757 | 758 | 1. In the `application.properties` change the active env as `prod`: 759 | 760 | ``` 761 | spring.profiles.active=prod 762 | ``` 763 | 764 | 2. In the `application-prod.properties`, change the parameters in `< >` with the values set in `env.sh`: 765 | 766 | ``` 767 | spring.ai.openai.api-key= 768 | spring.ai.openai.base-url= 769 | spring.ai.openai.chat.options.model=gpt-3.5-turbo 770 | spring.ai.openai.embedding.options.model=text-embedding-ada-002 771 | spring.datasource.url=jdbc:oracle:thin:@:1521/ORCLPDB1 772 | spring.datasource.username=vector 773 | spring.datasource.password=vector 774 | spring.datasource.driver-class-name=oracle.jdbc.OracleDriver 775 | config.tempDir=tempDir 776 | config.dropDb=true 777 | config.vectorDB=vectortable 778 | config.distance=EUCLIDEAN 779 | spring.servlet.multipart.max-file-size=10MB 780 | spring.servlet.multipart.max-request-size=20MB 781 | spring.ai.ollama.base-url= 782 | spring.ai.ollama.embedding.options.model=nomic-embed-text 783 | spring.ai.ollama.chat.options.model=llama2:7b-chat-fp16 784 | ``` 785 | 786 | 3. Open a terminal, and using the **Kubernetes** admin command, open a port forward to the backend: 787 | 788 | ``` 789 | kubectl -n obaas-admin port-forward svc/obaas-admin 8080:8080 790 | ``` 791 | 792 | 4. Using the command-line tool `oractl`, deploy the application running the following commands: 793 | 794 | ``` 795 | oractl:>connect 796 | ? username obaas-admin 797 | ? password ************** 798 | 799 | oractl:>create --app-name rag 800 | oractl:>deploy --app-name rag --service-name demoai --artifact-path /Users/cdebari/Documents/GitHub/spring-ai-demo/target/demoai-0.0.1-SNAPSHOT.jar --image-version 0.0.1 --service-profile prod 801 | 802 | ``` 803 | 804 | 5. Let's test the application with port forwarding. First, we need to stop the current `demoai` instance running on the background, to free the previous port being used; and, in a different terminal, run a port forwarding on port 8080 to the remote service on the **Oracle Backend for Spring Boot and Microservices**: 805 | 806 | ``` 807 | kubectl -n rag port-forward svc/demoai 8080:8080 808 | ``` 809 | 810 | 6. In a different terminal, test the service as done before, for example: 811 | 812 | ``` 813 | curl -X POST http://localhost:8080/ai/rag \ 814 | -H "Content-Type: application/json" \ 815 | -d '{"message":"Can I use any kind of development environment to run the example?"}' | jq -r .generation 816 | ``` 817 | 818 | ## Notes/Issues 819 | 820 | Additional Use Cases like summarization and embedding coming soon. 821 | 822 | ## URLs 823 | 824 | - [Oracle AI](https://www.oracle.com/artificial-intelligence/) 825 | - [AI for Developers](https://developer.oracle.com/technologies/ai.html) 826 | 827 | ## Contributing 828 | 829 | This project is open source. Please submit your contributions by forking this repository and submitting a pull request! Oracle appreciates any contributions that are made by the open-source community. 830 | 831 | ## License 832 | 833 | Copyright (c) 2024 Oracle and/or its affiliates. 834 | 835 | Licensed under the Universal Permissive License (UPL), Version 1.0. 836 | 837 | See [LICENSE](LICENSE) for more details. 838 | 839 | ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT ANY REVIEW. USE AT YOUR OWN RISK. 840 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting security vulnerabilities 2 | 3 | Oracle values the independent security research community and believes that 4 | responsible disclosure of security vulnerabilities helps us ensure the security 5 | and privacy of all our users. 6 | 7 | Please do NOT raise a GitHub Issue to report a security vulnerability. If you 8 | believe you have found a security vulnerability, please submit a report to 9 | [secalert_us@oracle.com][1] preferably with a proof of concept. Please review 10 | some additional information on [how to report security vulnerabilities to Oracle][2]. 11 | We encourage people who contact Oracle Security to use email encryption using 12 | [our encryption key][3]. 13 | 14 | We ask that you do not use other channels or contact the project maintainers 15 | directly. 16 | 17 | Non-vulnerability related security issues including ideas for new or improved 18 | security features are welcome on GitHub Issues. 19 | 20 | ## Security updates, alerts and bulletins 21 | 22 | Security updates will be released on a regular cadence. Many of our projects 23 | will typically release security fixes in conjunction with the 24 | Oracle Critical Patch Update program. Additional 25 | information, including past advisories, is available on our [security alerts][4] 26 | page. 27 | 28 | ## Security-related information 29 | 30 | We will provide security related information such as a threat model, considerations 31 | for secure use, or any known security issues in our documentation. Please note 32 | that labs and sample code are intended to demonstrate a concept and may not be 33 | sufficiently hardened for production use. 34 | 35 | [1]: mailto:secalert_us@oracle.com 36 | [2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html 37 | [3]: https://www.oracle.com/security-alerts/encryptionkey.html 38 | [4]: https://www.oracle.com/security-alerts/ 39 | -------------------------------------------------------------------------------- /docs/get-started-java-development.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/docs/get-started-java-development.pdf -------------------------------------------------------------------------------- /docs/oracle-database-23c-new-features-guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/docs/oracle-database-23c-new-features-guide.pdf -------------------------------------------------------------------------------- /img/addIngress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/addIngress.png -------------------------------------------------------------------------------- /img/bootvolume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/bootvolume.png -------------------------------------------------------------------------------- /img/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/cover.png -------------------------------------------------------------------------------- /img/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/create.png -------------------------------------------------------------------------------- /img/dbtype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/dbtype.png -------------------------------------------------------------------------------- /img/dbverify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/dbverify.png -------------------------------------------------------------------------------- /img/ide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/ide.png -------------------------------------------------------------------------------- /img/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/image.png -------------------------------------------------------------------------------- /img/instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/instance.png -------------------------------------------------------------------------------- /img/rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/rule.png -------------------------------------------------------------------------------- /img/securitylist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/securitylist.png -------------------------------------------------------------------------------- /img/shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/shape.png -------------------------------------------------------------------------------- /img/vcn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/springai-rag-db23ai/956c11ea1b3c3ba0c372113b0d54262c47c6a5fa/img/vcn.png -------------------------------------------------------------------------------- /json-dual.sql: -------------------------------------------------------------------------------- 1 | drop table if exists emp purge; 2 | drop table if exists dept purge; 3 | drop view if exists department_dv; 4 | 5 | 6 | create table dept ( 7 | deptno number(2) constraint pk_dept primary key, 8 | dname varchar2(14), 9 | loc varchar2(13) 10 | ) ; 11 | 12 | create table emp ( 13 | empno number(4) constraint pk_emp primary key, 14 | ename varchar2(10), 15 | job varchar2(9), 16 | mgr number(4), 17 | hiredate date, 18 | sal number(7,2), 19 | comm number(7,2), 20 | deptno number(2) constraint fk_deptno references dept 21 | ); 22 | 23 | create index emp_dept_fk_i on emp(deptno); 24 | 25 | insert into dept values (10,'ACCOUNTING','NEW YORK'); 26 | insert into dept values (20,'RESEARCH','DALLAS'); 27 | insert into dept values (30,'SALES','CHICAGO'); 28 | insert into dept values (40,'OPERATIONS','BOSTON'); 29 | 30 | insert into dept values (50,'STORAGE','LOS ANGELES'); 31 | insert into dept values (60,'HEADQUARTER','PHOENIX'); 32 | insert into dept values (70,'LOGISTICS','SAN DIEGO'); 33 | insert into dept values (80,'RETAIL','SEATTLE'); 34 | 35 | insert into emp values (7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,null,20); 36 | insert into emp values (7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30); 37 | insert into emp values (7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30); 38 | insert into emp values (7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,null,20); 39 | insert into emp values (7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30); 40 | insert into emp values (7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,null,30); 41 | insert into emp values (7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,null,10); 42 | insert into emp values (7788,'SCOTT','ANALYST',7566,to_date('13-JUL-87','dd-mm-rr')-85,3000,null,20); 43 | insert into emp values (7839,'KING','PRESIDENT',null,to_date('17-11-1981','dd-mm-yyyy'),5000,null,10); 44 | insert into emp values (7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30); 45 | insert into emp values (7876,'ADAMS','CLERK',7788,to_date('13-JUL-87', 'dd-mm-rr')-51,1100,null,20); 46 | insert into emp values (7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,null,30); 47 | insert into emp values (7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,null,20); 48 | insert into emp values (7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,null,10); 49 | 50 | insert into emp values (7544,'MICHAEL','ANALYST',7698,to_date('8-9-1982','dd-mm-yyyy'),1500,0,50); 51 | insert into emp values (7996,'HENRY','CLERK',7788,to_date('13-JUL-83', 'dd-mm-rr')-51,2100,null,60); 52 | insert into emp values (7977,'MARK','CLERK',7698,to_date('3-12-1985','dd-mm-yyyy'),1950,null,60); 53 | insert into emp values (7190,'ANDY','ANALYST',7566,to_date('3-12-1982','dd-mm-yyyy'),1000,null,70); 54 | insert into emp values (7230,'DOUG','CLERK',7782,to_date('23-2-1984','dd-mm-yyyy'),2000,null,70); 55 | insert into emp values (7276,'SANJAY','CLERK',7782,to_date('23-2-1984','dd-mm-yyyy'),1000,null,80); 56 | commit; 57 | 58 | create or replace json relational duality view department_dv as 59 | select json { 60 | '_id' : d.deptno, 61 | 'departmentName' : d.dname, 62 | 'location' : d.loc, 63 | 'employees' : 64 | [ select json {'employeeNumber' : e.empno, 65 | 'employeeName' : e.ename, 66 | 'job' : e.job, 67 | 'salary' : e.sal} 68 | from emp e with insert update delete 69 | where d.deptno = e.deptno ]} 70 | from dept d with insert update delete; 71 | commit; 72 | exit; -------------------------------------------------------------------------------- /license_policy.yml: -------------------------------------------------------------------------------- 1 | license_policies: 2 | - license_key: upl-1.0 3 | label: Approved License 4 | color_code: '#00800' 5 | icon: icon-ok-circle 6 | - license_key: bsd-simplified 7 | label: Approved License 8 | color_code: '#00800' 9 | icon: icon-ok-circle 10 | - license_key: bsd-new 11 | label: Approved License 12 | color_code: '#00800' 13 | icon: icon-ok-circle 14 | - license_key: mit 15 | label: Approved License 16 | color_code: '#00800' 17 | icon: icon-ok-circle 18 | - license_key: apache-1.1 19 | label: Approved License 20 | color_code: '#00800' 21 | icon: icon-ok-circle 22 | - license_key: apache-2.0 23 | label: Approved License 24 | color_code: '#00800' 25 | icon: icon-ok-circle 26 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.2.0 10 | 11 | 12 | com.example 13 | demoai 14 | 0.0.1-SNAPSHOT 15 | demoai 16 | Demo project for Spring Boot 17 | 18 | 17 19 | 23.4.0.0 20 | 0.8.0-SNAPSHOT 21 | 22 | 23 | 24 | 25 | 26 | ch.qos.logback 27 | logback-classic 28 | 1.4.14 29 | 30 | 31 | ch.qos.logback 32 | logback-core 33 | 1.4.14 34 | 35 | 36 | 37 | 38 | com.fasterxml.jackson.core 39 | jackson-databind 40 | 2.15.3 41 | 42 | 43 | com.fasterxml.jackson.datatype 44 | jackson-datatype-jdk8 45 | 2.15.3 46 | 47 | 48 | com.fasterxml.jackson.datatype 49 | jackson-datatype-jsr310 50 | 2.15.3 51 | 52 | 53 | com.fasterxml.jackson.module 54 | jackson-module-parameter-names 55 | 2.15.3 56 | 57 | 58 | 59 | 60 | org.apache.commons 61 | commons-compress 62 | 1.24.0 63 | 64 | 65 | org.springframework.ai 66 | spring-ai-bom 67 | 0.8.0-SNAPSHOT 68 | pom 69 | import 70 | 71 | 72 | 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-web 77 | 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-starter-test 82 | test 83 | 84 | 91 | 92 | org.springframework.ai 93 | spring-ai-openai-spring-boot-starter 94 | 0.8.0-SNAPSHOT 95 | 96 | 97 | 103 | 104 | 105 | 106 | com.oracle.database.jdbc 107 | ojdbc11 108 | 23.4.0.24.05 109 | 110 | 111 | 112 | 113 | jakarta.json.bind 114 | jakarta.json.bind-api 115 | 3.0.0 116 | 117 | 118 | org.eclipse 119 | yasson 120 | 3.0.3 121 | 122 | 123 | 124 | org.springframework.ai 125 | spring-ai-pdf-document-reader 126 | ${spring-ai.version} 127 | 128 | 129 | org.springframework.ai 130 | spring-ai-tika-document-reader 131 | ${spring-ai.version} 132 | 133 | 134 | org.springframework.boot 135 | spring-boot-starter-jdbc 136 | 137 | 138 | 139 | 140 | 141 | 142 | org.springframework.boot 143 | spring-boot-maven-plugin 144 | 145 | 146 | 147 | 148 | 149 | spring-snapshots 150 | Spring Snapshots 151 | https://repo.spring.io/snapshot 152 | 153 | true 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /release_files.json: -------------------------------------------------------------------------------- 1 | // see https://github.com/oracle-devrel/action-release-zip-maker for docs 2 | [ 3 | ] 4 | -------------------------------------------------------------------------------- /repolinter.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/todogroup/repolinter/master/rulesets/schema.json", 3 | "version": 2, 4 | "axioms": {}, 5 | "rules": { 6 | "readme-file-exists" : { 7 | "level": "error", 8 | "rule": { 9 | "type": "file-existence", 10 | "options": { 11 | "globsAny": ["README*"] 12 | } 13 | } 14 | }, 15 | "disclaimer-present" : { 16 | "level": "error", 17 | "rule": { 18 | "type": "file-contents", 19 | "options": { 20 | "globsAll": ["README*"], 21 | "noCase": true, 22 | "fail-on-non-existent": true, 23 | "content": "ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND" 24 | } 25 | } 26 | }, 27 | "license-file-exists" : { 28 | "level": "error", 29 | "rule": { 30 | "type": "file-existence", 31 | "options": { 32 | "globsAny": ["LICENSE*"] 33 | } 34 | } 35 | }, 36 | "copyright-notice-present" : { 37 | "level": "warning", 38 | "rule": { 39 | "type": "file-starts-with", 40 | "options": { 41 | "globsAll": ["**/*"], 42 | "skip-binary-files": true, 43 | "skip-paths-matching": { 44 | "extensions": ["yaml","yml","md","json","xml","tpl","ipynb","pickle","joblib","properties"], 45 | "patterns": ["\\.github"], 46 | "flags": "" 47 | }, 48 | "lineCount": 2, 49 | "patterns": [ 50 | "Copyright \\([cC]\\) [12][90]\\d\\d(\\-[12][90]\\d\\d)? Oracle and/or its affiliates\\." 51 | ], 52 | "succeed-on-non-exist": true 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=oracle-devrel_springai-rag-db23ai 2 | sonar.organization=oracle-devrel 3 | 4 | # This is the name and version displayed in the SonarCloud UI. 5 | #sonar.projectName=test 6 | #sonar.projectVersion=1.0 7 | 8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 9 | #sonar.sources=. 10 | 11 | # Encoding of the source code. Default is default system encoding 12 | #sonar.sourceEncoding=UTF-8 -------------------------------------------------------------------------------- /src/main/java/com/example/demoai/DemoaiApplication.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | package com.example.demoai; 13 | 14 | 15 | import org.springframework.ai.embedding.EmbeddingClient; 16 | //import org.springframework.ai.openai.OpenAiEmbeddingClient; 17 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 18 | import org.springframework.ai.vectorstore.VectorStore; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.boot.SpringApplication; 21 | import org.springframework.boot.autoconfigure.SpringBootApplication; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.jdbc.core.JdbcTemplate; 24 | //CHANGE 25 | //import org.springframework.ai.ollama.OllamaEmbeddingClient; 26 | 27 | 28 | @SpringBootApplication 29 | public class DemoaiApplication { 30 | 31 | 32 | public static void main(String[] args) { 33 | SpringApplication.run(DemoaiApplication.class, args); 34 | } 35 | 36 | //CHANGE 37 | @Bean 38 | VectorStore vectorStore(EmbeddingClient ec, JdbcTemplate t) { 39 | //VectorStore vectorStore(OllamaEmbeddingClient ec, JdbcTemplate t) { 40 | return new OracleDBVectorStore(t, ec); 41 | } 42 | 43 | @Bean 44 | TokenTextSplitter tokenTextSplitter() { 45 | return new TokenTextSplitter(); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/com/example/demoai/OracleDBVectorStore.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | 13 | package com.example.demoai; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.Optional; 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | 21 | import org.springframework.ai.document.Document; 22 | import org.springframework.ai.embedding.EmbeddingClient; 23 | import org.springframework.ai.vectorstore.SearchRequest; 24 | import org.springframework.ai.vectorstore.VectorStore; 25 | import org.springframework.beans.factory.InitializingBean; 26 | import org.springframework.beans.factory.annotation.Value; 27 | import org.springframework.jdbc.core.BatchPreparedStatementSetter; 28 | import org.springframework.jdbc.core.JdbcTemplate; 29 | import org.springframework.jdbc.core.PreparedStatementSetter; 30 | import org.springframework.jdbc.core.RowMapper; 31 | 32 | import com.example.model.VectorData; 33 | 34 | 35 | //Oracle DB 36 | import oracle.jdbc.OracleType; 37 | import oracle.sql.json.OracleJsonFactory; 38 | import oracle.sql.json.OracleJsonObject; 39 | 40 | 41 | import java.sql.*; 42 | import java.util.ArrayList; 43 | import java.util.HashMap; 44 | 45 | import org.slf4j.Logger; 46 | import org.slf4j.LoggerFactory; 47 | 48 | public class OracleDBVectorStore implements VectorStore, InitializingBean { 49 | 50 | private static final List DISTANCE_METRICS = Collections.unmodifiableList( 51 | List.of("MANHATTAN", "EUCLIDEAN", "DOT", "COSINE")); 52 | 53 | private Map DISTANCE_METRICS_FUNC; 54 | 55 | @Value("${config.vectorDB:vectortab}") 56 | public String VECTOR_TABLE; 57 | 58 | public int BATCH_SIZE = 100; 59 | 60 | private JdbcTemplate jdbcTemplate; 61 | 62 | EmbeddingClient embeddingClient; 63 | 64 | @Value("${config.dropDb}") 65 | private Boolean dropAtStartup; 66 | 67 | @Value("${config.distance}") 68 | private String distance_metric = "COSINE"; 69 | 70 | private static final Logger logger = LoggerFactory.getLogger(OracleDBVectorStore.class); 71 | 72 | public OracleDBVectorStore(JdbcTemplate jdbcTemplate, EmbeddingClient embClient) { 73 | 74 | this.jdbcTemplate = jdbcTemplate; 75 | this.embeddingClient = embClient; 76 | this.DISTANCE_METRICS_FUNC = new HashMap<>(); 77 | this.DISTANCE_METRICS_FUNC.put("MANHATTAN", "L1_DISTANCE"); 78 | this.DISTANCE_METRICS_FUNC.put("EUCLIDEAN", "L2_DISTANCE"); 79 | this.DISTANCE_METRICS_FUNC.put("DOT", "INNER_PRODUCT"); 80 | this.DISTANCE_METRICS_FUNC.put("COSINE", "COSINE_DISTANCE"); 81 | } 82 | 83 | @Override 84 | public void add(List documents) { 85 | 86 | int size = documents.size(); 87 | 88 | this.jdbcTemplate.batchUpdate("INSERT INTO " + this.VECTOR_TABLE + " (text,embeddings,metadata) VALUES (?,?,?)", 89 | new BatchPreparedStatementSetter() { 90 | @Override 91 | public void setValues(PreparedStatement ps, int i) throws SQLException { 92 | 93 | var document = documents.get(i); 94 | var text = document.getContent(); 95 | 96 | OracleJsonFactory factory = new OracleJsonFactory(); 97 | OracleJsonObject jsonObj = factory.createObject(); 98 | Map metaData = document.getMetadata(); 99 | for (Map.Entry entry : metaData.entrySet()) { 100 | jsonObj.put(entry.getKey(), String.valueOf(entry.getValue())); 101 | } 102 | 103 | List vectorList = embeddingClient.embed(document); 104 | double[] embeddings = new double[vectorList.size()]; 105 | for (int j = 0; j < vectorList.size(); j++) { 106 | embeddings[j] = vectorList.get(j); 107 | } 108 | 109 | ps.setString(1, text); 110 | ps.setObject(2, embeddings, OracleType.VECTOR); 111 | ps.setObject(3, jsonObj, OracleType.JSON); 112 | 113 | } 114 | 115 | @Override 116 | public int getBatchSize() { 117 | return size; 118 | } 119 | }); 120 | 121 | } 122 | 123 | 124 | @Override 125 | public Optional delete(List idList) { 126 | 127 | String sql = "DELETE FROM " + this.VECTOR_TABLE + " WHERE id = ?"; 128 | int count[][] = jdbcTemplate.batchUpdate(sql, idList, BATCH_SIZE, (ps, argument) -> { 129 | ps.setString(1, argument); 130 | }); 131 | 132 | int sum = Arrays.stream(count).flatMapToInt(Arrays::stream).sum(); 133 | logger.info("MSG: Deleted " + sum + " records"); 134 | 135 | return Optional.of(sum == idList.size()); 136 | } 137 | 138 | 139 | @Override 140 | public List similaritySearch(SearchRequest request) { 141 | 142 | List nearest = new ArrayList<>(); 143 | 144 | logger.info("MSG: REQUESTED QUERY " + request.getQuery()); 145 | List queryEmbeddings = embeddingClient.embed(request.getQuery()); 146 | logger.info("MSG: EMBEDDINGS SIZE: " + queryEmbeddings.size()); 147 | 148 | logger.info("MSG: DISTANCE METRICS: " + distance_metric); 149 | if (DISTANCE_METRICS_FUNC.get(distance_metric) == null) { 150 | logger.error( 151 | "ERROR: wrong distance metrics set. Allowed values are: " + String.join(",", DISTANCE_METRICS)); 152 | System.exit(1); 153 | } 154 | logger.info("MSG: DISTANCE METRICS FUNCTION: " + DISTANCE_METRICS_FUNC.get(distance_metric)); 155 | int topK = request.getTopK(); 156 | 157 | try { 158 | nearest = similaritySearchByMetrics(VECTOR_TABLE, queryEmbeddings, topK, 159 | this.DISTANCE_METRICS_FUNC.get(distance_metric)); 160 | } catch (Exception e) { 161 | logger.error(e.toString()); 162 | } 163 | 164 | List documents = new ArrayList<>(); 165 | 166 | for (VectorData d : nearest) { 167 | OracleJsonObject metadata = d.getMetadata(); 168 | Map map = new HashMap<>(); 169 | for (String key : metadata.keySet()) { 170 | map.put(key, metadata.get(key).toString()); 171 | } 172 | Document doc = new Document(d.getText(), map); 173 | documents.add(doc); 174 | 175 | } 176 | return documents; 177 | 178 | } 179 | 180 | List similaritySearchByMetrics(String vectortab, List vector, int topK, 181 | String distance_metrics_func) throws SQLException { 182 | List results = new ArrayList<>(); 183 | double[] doubleVector = new double[vector.size()]; 184 | for (int i = 0; i < vector.size(); i++) { 185 | doubleVector[i] = vector.get(i); 186 | } 187 | 188 | try { 189 | 190 | String similaritySql = "SELECT id,embeddings,metadata,text FROM " + vectortab 191 | + " ORDER BY " + distance_metrics_func + "(embeddings, ?)" 192 | + " FETCH FIRST ? ROWS ONLY"; 193 | 194 | results = jdbcTemplate.query(similaritySql, 195 | new PreparedStatementSetter() { 196 | public void setValues(java.sql.PreparedStatement ps) throws SQLException { 197 | ps.setObject(1, doubleVector, OracleType.VECTOR); 198 | ps.setObject(2, topK, OracleType.NUMBER); 199 | } 200 | }, 201 | new RowMapper() { 202 | public VectorData mapRow(ResultSet rs, int rowNum) throws SQLException { 203 | return new VectorData(rs.getString("id"), 204 | rs.getObject("embeddings", double[].class), 205 | rs.getObject("text", String.class), 206 | rs.getObject("metadata", OracleJsonObject.class)); 207 | } 208 | }); 209 | 210 | } catch (Exception e) { 211 | logger.error("ERROR: " + e.getMessage()); 212 | } 213 | return results; 214 | } 215 | 216 | 217 | @Override 218 | public void afterPropertiesSet() throws Exception { 219 | int initialSize = 0; 220 | try { 221 | 222 | initialSize = jdbcTemplate.queryForObject("select count(*) from " + this.VECTOR_TABLE, Integer.class); 223 | logger.info("MSG: table " + this.VECTOR_TABLE + " exists with " + initialSize + " chunks"); 224 | 225 | if (dropAtStartup) { 226 | logger.info("MSG: DROP TABLE " + this.VECTOR_TABLE + " AT EVERY STARTUP"); 227 | throw new Exception("DROP TABLE EVERY STARTUP"); 228 | } 229 | } catch (Exception e) { 230 | try { 231 | logger.info("MSG: DROPPING TABLE " + this.VECTOR_TABLE); 232 | jdbcTemplate.execute( 233 | "BEGIN\n" + 234 | " EXECUTE IMMEDIATE 'DROP TABLE " + this.VECTOR_TABLE + " CASCADE CONSTRAINTS';\n" + 235 | "EXCEPTION\n" + 236 | " WHEN OTHERS THEN\n" + 237 | " IF SQLCODE != -942 THEN\n" + 238 | " RAISE;\n" + 239 | " END IF;\n" + 240 | "END;"); 241 | } catch (Exception ex1) { 242 | logger.error("ERROR: DROP TABLE " + this.VECTOR_TABLE + " \n" + e.getMessage()); 243 | System.exit(1); 244 | } 245 | String createTableSql = "CREATE TABLE " + this.VECTOR_TABLE + "(" + 246 | "id NUMBER GENERATED AS IDENTITY ," + 247 | "text CLOB," + 248 | "embeddings VECTOR," + 249 | "metadata JSON," + 250 | "PRIMARY KEY (id))"; 251 | try { 252 | this.jdbcTemplate.execute( 253 | "BEGIN\n" + 254 | " EXECUTE IMMEDIATE '" + 255 | createTableSql + "' ;\n" + 256 | "EXCEPTION\n" + 257 | " WHEN OTHERS THEN\n" + 258 | " IF SQLCODE != -942 THEN\n" + 259 | " RAISE;\n" + 260 | " END IF;\n" + 261 | "END;"); 262 | logger.info("OK: CREATE TABLE " + this.VECTOR_TABLE); 263 | } catch (Exception ex2) { 264 | logger.error("ERROR: CREATE TABLE" + e.getMessage()); 265 | System.exit(1); 266 | } 267 | } 268 | return; 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /src/main/java/com/example/demoai/controller/DemoaiController.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | 13 | package com.example.demoai.controller; 14 | 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Optional; 20 | 21 | import org.springframework.ai.chat.ChatClient; 22 | import org.springframework.ai.document.Document; 23 | import org.springframework.ai.embedding.EmbeddingClient; 24 | import org.springframework.ai.embedding.EmbeddingResponse; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.beans.factory.annotation.Qualifier; 27 | import org.springframework.beans.factory.annotation.Value; 28 | import org.springframework.core.io.FileSystemResource; 29 | import org.springframework.web.bind.annotation.GetMapping; 30 | import org.springframework.web.bind.annotation.PostMapping; 31 | import org.springframework.web.bind.annotation.RequestBody; 32 | import org.springframework.web.bind.annotation.RequestMapping; 33 | import org.springframework.web.bind.annotation.RequestParam; 34 | import org.springframework.web.bind.annotation.RestController; 35 | import org.springframework.web.multipart.MultipartFile; 36 | 37 | //CHANGE 38 | //import org.springframework.ai.ollama.OllamaEmbeddingClient; 39 | //import org.springframework.ai.ollama.OllamaChatClient; 40 | import org.springframework.ai.chat.ChatClient; 41 | 42 | import com.example.demoai.service.VectorService; 43 | import com.example.model.MessageDTO; 44 | 45 | import java.io.IOException; 46 | import java.nio.file.Files; 47 | import java.nio.file.Path; 48 | import java.nio.file.StandardCopyOption; 49 | 50 | import org.slf4j.Logger; 51 | import org.slf4j.LoggerFactory; 52 | 53 | @RestController 54 | @RequestMapping("ai") 55 | public class DemoaiController { 56 | 57 | //CHANGE 58 | private final EmbeddingClient embeddingClient; 59 | //private final OllamaEmbeddingClient embeddingClient; 60 | 61 | //CHANGE 62 | private final ChatClient chatClient; 63 | //private final OllamaChatClient chatClient; 64 | 65 | 66 | private final VectorService vectorService; 67 | 68 | @Value("${config.tempDir}") 69 | private String TEMP_DIR; 70 | 71 | private static final Logger logger = LoggerFactory.getLogger(DemoaiController.class); 72 | 73 | //CHANGE 74 | @Autowired 75 | public DemoaiController(EmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // OpenAI full 76 | //public DemoaiController(OllamaEmbeddingClient embeddingClient, @Qualifier("openAiChatClient") ChatClient chatClient, VectorService vectorService) { // Ollama Embeddings - OpenAI Completion 77 | //public DemoaiController(OllamaEmbeddingClient embeddingClient, OllamaChatClient chatClient, VectorService vectorService) { // Ollama full 78 | this.embeddingClient = embeddingClient; 79 | this.chatClient = chatClient; 80 | this.vectorService = vectorService; 81 | 82 | logger.info("Controller started."); 83 | } 84 | 85 | @GetMapping("/ping") 86 | public String ping(@RequestParam(value = "message", defaultValue = "ping success") String message) { 87 | 88 | return "Hello: " + message; 89 | } 90 | 91 | /** 92 | * Call OpenAI Chat completion 93 | * 94 | * @param message query in natural language 95 | * @return generation 96 | */ 97 | @GetMapping("/generate") 98 | public Map generate(@RequestParam(value = "message", defaultValue = "Give me a trading strategy") String message) { 99 | return Map.of("generation", chatClient.call(message)); 100 | } 101 | 102 | /** 103 | * Call OpenAI Chat completion 104 | * 105 | * @RequestBody MessageDTO message query in natural language in the request body 106 | * @return generation 107 | */ 108 | @PostMapping("/generate") 109 | public Map generate(@RequestBody MessageDTO message) { 110 | return Map.of("generation", chatClient.call(message.getMessage())); 111 | } 112 | 113 | /** 114 | * RAG 115 | * 116 | * @param message query in natural language 117 | * @return generation 118 | */ 119 | @GetMapping("/rag") 120 | public Map rag(@RequestParam(value = "message", defaultValue = "Give me a trading strategy") String message) { 121 | return Map.of("generation", this.vectorService.rag(message)); 122 | } 123 | 124 | /** 125 | * RAG 126 | * 127 | * @RequestBody MessageDTO message query in natural language in the request body 128 | * @return generation 129 | */ 130 | @PostMapping("/rag") 131 | public Map rag(@RequestBody MessageDTO message) { 132 | 133 | return Map.of("generation", this.vectorService.rag(message.getMessage())); 134 | } 135 | 136 | @GetMapping("/embedding") 137 | public Map embed(@RequestParam(value = "message", defaultValue = "Give me a trading strategy") String message) { 138 | EmbeddingResponse embeddingResponse = this.embeddingClient.embedForResponse(List.of(message)); 139 | return Map.of("embedding", embeddingResponse); 140 | } 141 | 142 | @PostMapping("/embedding") 143 | public Map embed(@RequestBody MessageDTO message) { 144 | EmbeddingResponse embeddingResponse = this.embeddingClient.embedForResponse(List.of(message.getMessage())); 145 | return Map.of("embedding", embeddingResponse); 146 | } 147 | 148 | /** 149 | * Store a pdf file in Oracle Vector DB 23c 150 | * 151 | * @param file pdf file sent as Multipart 152 | * @return success/fail message 153 | */ 154 | @PostMapping("/store") 155 | public String uploadFile(@RequestParam("file") MultipartFile file) { 156 | if (file.isEmpty()) { 157 | return "Failed to upload empty file."; 158 | } 159 | try { 160 | String currentDir = System.getProperty("user.dir"); 161 | Path parentDir = Path.of(currentDir); 162 | Path tempPath = parentDir.resolve(TEMP_DIR); 163 | if (!Files.exists(tempPath)) { 164 | Files.createDirectories(tempPath); 165 | } 166 | Path tempDir = Files.createTempDirectory(tempPath, "uploads_"); 167 | Path filePath = tempDir.resolve(file.getOriginalFilename()); 168 | Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); 169 | 170 | this.vectorService.putDocument(new FileSystemResource(filePath.toString())); 171 | 172 | return "File stored successfully " + filePath; 173 | } catch (IOException e) { 174 | logger.info(e.getMessage()); 175 | return "Failed to upload file: " + e.getMessage(); 176 | } 177 | } 178 | 179 | @GetMapping("/delete") 180 | public Map delete(@RequestParam(value = "id", defaultValue = "XXXXXXXXX") List ids) { 181 | if (ids == null || ids.isEmpty()) { 182 | return Map.of("return", "No IDs provided"); 183 | } 184 | Optional ret = vectorService.getVectorStore().delete(ids); 185 | return Map.of("return", ret.get().toString()); 186 | } 187 | 188 | @PostMapping("/search-similar") 189 | public List> search(@RequestBody MessageDTO message) { 190 | 191 | List> resultList = new ArrayList<>(); 192 | List similarDocs = this.vectorService.getSimilarDocs(message.getMessage()); 193 | 194 | for (Document d : similarDocs) { 195 | Map metadata = d.getMetadata(); 196 | Map doc = new HashMap<>(); 197 | doc.put("id", d.getId()); 198 | doc.put("text", d.getContent()); 199 | doc.put("metadata", metadata); 200 | resultList.add(doc); 201 | } 202 | ; 203 | return resultList; 204 | } 205 | 206 | /** 207 | * Store a Json Duality view as documents: EXPERIMENTAL - NOT FULLY TESTED 208 | * 209 | * @param view the name of Json-Duality view already created in the DB 210 | * @return success/fail message 211 | */ 212 | @GetMapping("/store-json") 213 | public String storeJson(@RequestParam("view") String viewName) { 214 | if (viewName.isEmpty()) { 215 | return "view empty"; 216 | } 217 | 218 | Boolean retMessage = this.vectorService.putJsonView(viewName); 219 | if (retMessage) { 220 | return "SUCCESS: Json Duality View stored as embedding "; 221 | } else { 222 | return "ERROR: Failed to store Json Duality View as embedding!"; 223 | } 224 | 225 | } 226 | 227 | } 228 | -------------------------------------------------------------------------------- /src/main/java/com/example/demoai/service/VectorService.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | 13 | package com.example.demoai.service; 14 | 15 | import java.io.IOException; 16 | 17 | import java.nio.charset.StandardCharsets; 18 | import java.nio.file.Files; 19 | import java.sql.ResultSet; 20 | import java.sql.SQLException; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import org.springframework.ai.chat.ChatClient; 26 | import org.springframework.ai.chat.ChatResponse; 27 | import org.springframework.ai.chat.prompt.Prompt; 28 | import org.springframework.ai.chat.prompt.PromptTemplate; 29 | import org.springframework.ai.document.Document; 30 | import org.springframework.ai.reader.ExtractedTextFormatter; 31 | import org.springframework.ai.reader.JsonReader; 32 | import org.springframework.ai.reader.pdf.PagePdfDocumentReader; 33 | import org.springframework.ai.reader.pdf.ParagraphPdfDocumentReader; 34 | import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig; 35 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 36 | import org.springframework.ai.vectorstore.SearchRequest; 37 | import org.springframework.ai.vectorstore.VectorStore; 38 | import org.springframework.beans.factory.annotation.Autowired; 39 | import org.springframework.beans.factory.annotation.Qualifier; 40 | import org.springframework.beans.factory.annotation.Value; 41 | import org.springframework.core.io.ByteArrayResource; 42 | import org.springframework.core.io.Resource; 43 | import org.springframework.jdbc.core.JdbcTemplate; 44 | import org.springframework.jdbc.core.RowMapper; 45 | import org.springframework.stereotype.Service; 46 | 47 | //CHANGE 48 | //import org.springframework.ai.ollama.OllamaChatClient; 49 | 50 | import org.slf4j.Logger; 51 | import org.slf4j.LoggerFactory; 52 | 53 | import com.example.demoai.controller.DemoaiController; 54 | 55 | import com.example.model.JsonDualDTO; 56 | 57 | @Service 58 | public class VectorService { 59 | 60 | @Autowired 61 | private JdbcTemplate jdbcTemplate; 62 | 63 | @Autowired 64 | private volatile VectorStore vectorStore; 65 | 66 | private static final Logger logger = LoggerFactory.getLogger(VectorService.class); 67 | 68 | //CHANGE 69 | private final ChatClient aiClient; 70 | //private final OllamaChatClient aiClient; 71 | 72 | //CHANGE 73 | VectorService(@Qualifier("openAiChatClient") ChatClient aiClient) { 74 | //VectorService(OllamaChatClient aiClient) { 75 | 76 | this.aiClient = aiClient; 77 | } 78 | 79 | @Value("classpath:prompt-template.txt") 80 | private Resource templateFile; 81 | 82 | private final String templateBasic = """ 83 | DOCUMENTS: 84 | {documents} 85 | 86 | QUESTION: 87 | {question} 88 | 89 | INSTRUCTIONS: 90 | Answer the users question using the DOCUMENTS text above. 91 | Keep your answer ground in the facts of the DOCUMENTS. 92 | If the DOCUMENTS doesn’t contain the facts to answer the QUESTION, return: 93 | I'm sorry but I haven't enough information to answer. 94 | """; 95 | 96 | 97 | public VectorStore getVectorStore() { 98 | return vectorStore; 99 | } 100 | 101 | //EXPERIMENTAL - NOT FULLY TESTED 102 | public Boolean putJsonView(String jsonView) { 103 | 104 | String sqlStatement = "select DATA from " + jsonView; 105 | 106 | RowMapper rowMapper = new RowMapper() { 107 | @Override 108 | public JsonDualDTO mapRow(ResultSet rs, int rowNum) throws SQLException { 109 | JsonDualDTO jsonElem = new JsonDualDTO(); 110 | 111 | String data = rs.getString(1); 112 | jsonElem.setDATA(data); 113 | return jsonElem; 114 | } 115 | }; 116 | try { 117 | List ret = jdbcTemplate.query(sqlStatement, rowMapper); 118 | 119 | for (JsonDualDTO j : ret) { 120 | Resource resourceJsonDual = new ByteArrayResource(("[" + j.getDATA() + "]").getBytes()); 121 | JsonReader jsonReader = new JsonReader(resourceJsonDual); 122 | var textSplitter = new TokenTextSplitter(); 123 | this.vectorStore.accept(textSplitter.apply(jsonReader.get())); 124 | } 125 | logger.info("JSON view: " + jsonView + " stored as embedding"); 126 | return true; 127 | } catch (Exception ex) { 128 | logger.error(sqlStatement); 129 | return false; 130 | } 131 | } 132 | 133 | 134 | public void putDocument(Resource docResource) { 135 | 136 | PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(docResource, 137 | PdfDocumentReaderConfig.builder() 138 | .withPageTopMargin(0) 139 | .withPageExtractedTextFormatter(ExtractedTextFormatter.builder() 140 | .withNumberOfTopTextLinesToDelete(0) 141 | .build()) 142 | .withPagesPerDocument(1) 143 | .build()); 144 | 145 | var textSplitter = new TokenTextSplitter(); 146 | this.vectorStore.accept(textSplitter.apply(pdfReader.get())); 147 | } 148 | 149 | 150 | public String rag(String question) { 151 | 152 | String START = "\n
\n"; 153 | String STOP = "\n
\n"; 154 | 155 | List similarDocuments = this.vectorStore.similaritySearch( 156 | SearchRequest. 157 | query(question). 158 | withTopK(4)); 159 | 160 | Iterator iterator = similarDocuments.iterator(); 161 | StringBuilder context = new StringBuilder(); 162 | while (iterator.hasNext()) { 163 | Document document = iterator.next(); 164 | context.append(document.getId() + "."); 165 | context.append(START + document.getFormattedContent() + STOP); 166 | } 167 | 168 | String template = templateBasic; 169 | try { 170 | template = new String(Files.readAllBytes(templateFile.getFile().toPath()), StandardCharsets.UTF_8); 171 | 172 | } catch (IOException e) { 173 | logger.error(e.getMessage()); 174 | template = templateBasic; 175 | } 176 | ; 177 | 178 | PromptTemplate promptTemplate = new PromptTemplate(template); 179 | Prompt prompt = promptTemplate.create(Map.of("documents", context, "question", question)); 180 | logger.info(prompt.toString()); 181 | ChatResponse aiResponse = aiClient.call(prompt); 182 | return aiResponse.getResult().getOutput().getContent(); 183 | } 184 | 185 | public List getSimilarDocs(String message) { 186 | List similarDocuments = this.vectorStore.similaritySearch(message); 187 | return similarDocuments; 188 | 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/com/example/model/JsonDualDTO.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | 13 | package com.example.model; 14 | 15 | public class JsonDualDTO { 16 | 17 | private String DATA; 18 | 19 | public JsonDualDTO(String dATA) { 20 | DATA = dATA; 21 | } 22 | public JsonDualDTO() { 23 | } 24 | 25 | public String getDATA() { 26 | return DATA; 27 | } 28 | 29 | public void setDATA(String dATA) { 30 | DATA = dATA; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/example/model/MessageDTO.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | 13 | package com.example.model; 14 | 15 | public class MessageDTO { 16 | private String message; 17 | 18 | // Getter and Setter 19 | public String getMessage() { 20 | return message; 21 | } 22 | 23 | public void setMessage(String message) { 24 | this.message = message; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/example/model/VectorData.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | 13 | package com.example.model; 14 | 15 | import oracle.sql.json.OracleJsonObject; 16 | 17 | public class VectorData { 18 | private String id; 19 | private double[] embeddings; 20 | private String text ; 21 | private OracleJsonObject metadata; 22 | 23 | 24 | public String getText() { 25 | return text; 26 | } 27 | 28 | public void setText(String text) { 29 | this.text = text; 30 | } 31 | 32 | public OracleJsonObject getMetadata() { 33 | return metadata; 34 | } 35 | 36 | public void setMetadata(OracleJsonObject metadata) { 37 | this.metadata = metadata; 38 | } 39 | 40 | // Constructor 41 | public VectorData(String id, double[] embeddings, String text, OracleJsonObject metadata) { 42 | this.id = id; 43 | this.embeddings = embeddings; 44 | this.metadata = metadata; 45 | this.text = text; 46 | } 47 | 48 | // Getters and Setters 49 | public String getId() { 50 | return id; 51 | } 52 | 53 | public void setId(String id) { 54 | this.id = id; 55 | } 56 | 57 | public double[] getEmbeddings() { 58 | return embeddings; 59 | } 60 | 61 | public void setEmbeddings(double[] embeddings) { 62 | this.embeddings = embeddings; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.base-url=${OPENAI_URL} 3 | spring.ai.openai.chat.options.model=${OPENAI_MODEL} 4 | spring.ai.openai.embedding.options.model=${OPENAI_EMBEDDING_MODEL} 5 | spring.ai.openai.chat.options.temperature=0.5 6 | #spring.datasource.url=jdbc:oracle:thin:@${VECTORDB}:1521/ORCLPDB1 7 | spring.datasource.url=jdbc:oracle:thin:@${VECTORDB}:1521/FREEPDB1 8 | spring.datasource.username=${DB_USER} 9 | spring.datasource.password=${DB_PASSWORD} 10 | spring.datasource.driver-class-name=oracle.jdbc.OracleDriver 11 | config.tempDir=tempDir 12 | config.dropDb=true 13 | config.vectorDB=vectortable 14 | config.distance=EUCLIDEAN 15 | #config.distance=COSINE 16 | spring.servlet.multipart.max-file-size=10MB 17 | spring.servlet.multipart.max-request-size=20MB 18 | spring.ai.ollama.base-url=${OLLAMA_URL} 19 | spring.ai.ollama.embedding.options.model=${OLLAMA_EMBEDDINGS} 20 | spring.ai.ollama.chat.options.model=${OLLAMA_MODEL} -------------------------------------------------------------------------------- /src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.base-url=${OPENAI_URL} 3 | spring.ai.openai.chat.options.model=${OPENAI_MODEL} 4 | spring.ai.openai.embedding.options.model=${OPENAI_EMBEDDING_MODEL} 5 | spring.datasource.url=jdbc:oracle:thin:@${VECTORDB}:1521/FREEPDB1 6 | spring.datasource.username=${DB_USER} 7 | spring.datasource.password=${DB_PASSWORD} 8 | spring.datasource.driver-class-name=oracle.jdbc.OracleDriver 9 | config.tempDir=tempDir 10 | config.dropDb=true 11 | config.vectorDB=vectortable 12 | config.distance=EUCLIDEAN 13 | spring.servlet.multipart.max-file-size=10MB 14 | spring.servlet.multipart.max-request-size=20MB 15 | spring.ai.ollama.base-url=${OLLAMA_URL} 16 | spring.ai.ollama.embedding.options.model=${OLLAMA_EMBEDDINGS} 17 | spring.ai.ollama.chat.options.model=${OLLAMA_MODEL} -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=dev -------------------------------------------------------------------------------- /src/main/resources/json_dual.json: -------------------------------------------------------------------------------- 1 | [{"_id":10,"departmentName":"ACCOUNTING","location":"NEW YORK","employees":[{"employeeNumber":7782,"employeeName":"CLARK","job":"MANAGER","salary":2450},{"employeeNumber":7839,"employeeName":"KING","job":"PRESIDENT","salary":5000},{"employeeNumber":7934,"employeeName":"MILLER","job":"CLERK","salary":1300}],"_metadata":{"etag":"E546E2220E8F9620E36C2A7F8858D6F7","asof":"00000000004822A1"}}, 2 | {"_id":20,"departmentName":"RESEARCH","location":"DALLAS","employees":[{"employeeNumber":7369,"employeeName":"SMITH","job":"CLERK","salary":800},{"employeeNumber":7566,"employeeName":"JONES","job":"MANAGER","salary":2975},{"employeeNumber":7902,"employeeName":"FORD","job":"ANALYST","salary":3000}],"_metadata":{"etag":"3709C26AFF2507248097F11BF906C723","asof":"00000000004822A1"}}] -------------------------------------------------------------------------------- /src/main/resources/prompt-template.txt: -------------------------------------------------------------------------------- 1 | DOCUMENTS: 2 | {documents} 3 | 4 | QUESTION: 5 | {question} 6 | 7 | INSTRUCTIONS: 8 | Answer the users question using the DOCUMENTS text above. 9 | Keep your answer ground in the facts of the DOCUMENTS. 10 | If the DOCUMENTS doesn’t contain the facts to answer the QUESTION, return: 11 | I'm sorry but I haven't enough information to answer. 12 | -------------------------------------------------------------------------------- /src/test/java/com/example/demoai/DemoaiApplicationTests.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 3 | 4 | // ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, 5 | // FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, 6 | // AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, 7 | // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES 8 | // DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, 9 | // MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING 10 | // THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT 11 | // ANY REVIEW. USE AT YOUR OWN RISK. 12 | 13 | package com.example.demoai; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | 18 | @SpringBootTest 19 | class DemoaiApplicationTests { 20 | 21 | @Test 22 | void contextLoads() { 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /target/classes/application-dev.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.base-url=${OPENAI_URL} 3 | spring.ai.openai.chat.options.model=${OPENAI_MODEL} 4 | spring.ai.openai.embedding.options.model=${OPENAI_EMBEDDING_MODEL} 5 | spring.ai.openai.chat.options.temperature=0.5 6 | #spring.datasource.url=jdbc:oracle:thin:@${VECTORDB}:1521/ORCLPDB1 7 | spring.datasource.url=jdbc:oracle:thin:@${VECTORDB}:1521/FREEPDB1 8 | spring.datasource.username=${DB_USER} 9 | spring.datasource.password=${DB_PASSWORD} 10 | spring.datasource.driver-class-name=oracle.jdbc.OracleDriver 11 | config.tempDir=tempDir 12 | config.dropDb=true 13 | config.vectorDB=vectortable 14 | config.distance=EUCLIDEAN 15 | #config.distance=COSINE 16 | spring.servlet.multipart.max-file-size=10MB 17 | spring.servlet.multipart.max-request-size=20MB 18 | spring.ai.ollama.base-url=${OLLAMA_URL} 19 | spring.ai.ollama.embedding.options.model=${OLLAMA_EMBEDDINGS} 20 | spring.ai.ollama.chat.options.model=${OLLAMA_MODEL} -------------------------------------------------------------------------------- /target/classes/application-prod.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.base-url=${OPENAI_URL} 3 | spring.ai.openai.chat.options.model=${OPENAI_MODEL} 4 | spring.ai.openai.embedding.options.model=${OPENAI_EMBEDDING_MODEL} 5 | spring.datasource.url=jdbc:oracle:thin:@${VECTORDB}:1521/FREEPDB1 6 | spring.datasource.username=${DB_USER} 7 | spring.datasource.password=${DB_PASSWORD} 8 | spring.datasource.driver-class-name=oracle.jdbc.OracleDriver 9 | config.tempDir=tempDir 10 | config.dropDb=true 11 | config.vectorDB=vectortable 12 | config.distance=EUCLIDEAN 13 | spring.servlet.multipart.max-file-size=10MB 14 | spring.servlet.multipart.max-request-size=20MB 15 | spring.ai.ollama.base-url=${OLLAMA_URL} 16 | spring.ai.ollama.embedding.options.model=${OLLAMA_EMBEDDINGS} 17 | spring.ai.ollama.chat.options.model=${OLLAMA_MODEL} -------------------------------------------------------------------------------- /target/classes/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=dev -------------------------------------------------------------------------------- /target/classes/json_dual.json: -------------------------------------------------------------------------------- 1 | [{"_id":10,"departmentName":"ACCOUNTING","location":"NEW YORK","employees":[{"employeeNumber":7782,"employeeName":"CLARK","job":"MANAGER","salary":2450},{"employeeNumber":7839,"employeeName":"KING","job":"PRESIDENT","salary":5000},{"employeeNumber":7934,"employeeName":"MILLER","job":"CLERK","salary":1300}],"_metadata":{"etag":"E546E2220E8F9620E36C2A7F8858D6F7","asof":"00000000004822A1"}}, 2 | {"_id":20,"departmentName":"RESEARCH","location":"DALLAS","employees":[{"employeeNumber":7369,"employeeName":"SMITH","job":"CLERK","salary":800},{"employeeNumber":7566,"employeeName":"JONES","job":"MANAGER","salary":2975},{"employeeNumber":7902,"employeeName":"FORD","job":"ANALYST","salary":3000}],"_metadata":{"etag":"3709C26AFF2507248097F11BF906C723","asof":"00000000004822A1"}}] -------------------------------------------------------------------------------- /target/classes/prompt-template.txt: -------------------------------------------------------------------------------- 1 | DOCUMENTS: 2 | {documents} 3 | 4 | QUESTION: 5 | {question} 6 | 7 | INSTRUCTIONS: 8 | Answer the users question using the DOCUMENTS text above. 9 | Keep your answer ground in the facts of the DOCUMENTS. 10 | If the DOCUMENTS doesn’t contain the facts to answer the QUESTION, return: 11 | I'm sorry but I haven't enough information to answer. 12 | --------------------------------------------------------------------------------