├── .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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
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 | 
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 | 
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 | 
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 | 
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 | 
470 |
471 | 2. Press **Create instance** button:
472 |
473 | 
474 |
475 | 3. Choose `VM.GPU.A10.2` shape, selecting **Virtual machine**/**Specialty and previous generation**:
476 |
477 | 
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 | 
482 |
483 | 5. Specify a custom boot volume size and set 100 GB:
484 |
485 | 
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 | 
577 |
578 | 2. In the menu **Resources** click on **Security Lists**:
579 |
580 | 
581 |
582 | 3. Click on the link of **Default Security List...**
583 |
584 | 4. Click on the **Add Ingress Rules** button:
585 |
586 | 
587 |
588 | 5. Click on the **Add Ingress Rules** button:
589 |
590 | 
591 |
592 | 6. Insert details as shown in the following image and then click **Add Ingress Rules** button:
593 |
594 | 
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