├── .github └── workflows │ ├── banned_file_changes_pr.yml │ ├── cla.yml │ ├── license_audit.yml │ ├── release-zip-file.yml │ ├── repolinter.yml │ └── sonarcloud.yml ├── .gitignore ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── FAQ.md ├── JET.md ├── K8S.md ├── LICENSE ├── LOCAL.md ├── README.md ├── SECURITY.md ├── app ├── .gitignore ├── Dockerfile ├── oraclejafconfig.json ├── oraclejetconfig.json ├── package.json ├── path_mapping.json ├── scripts │ └── hooks │ │ ├── after_app_create.js │ │ ├── after_app_restore.js │ │ ├── after_app_typescript.js │ │ ├── after_build.js │ │ ├── after_component_build.js │ │ ├── after_component_create.js │ │ ├── after_component_package.js │ │ ├── after_component_typescript.js │ │ ├── after_serve.js │ │ ├── after_watch.js │ │ ├── before_app_typescript.js │ │ ├── before_build.js │ │ ├── before_component_optimize.js │ │ ├── before_component_package.js │ │ ├── before_component_typescript.js │ │ ├── before_injection.js │ │ ├── before_optimize.js │ │ ├── before_release_build.js │ │ ├── before_serve.js │ │ ├── before_watch.js │ │ ├── before_webpack.js │ │ └── hooks.json ├── src │ ├── components │ │ ├── app.tsx │ │ ├── content │ │ │ ├── answer.tsx │ │ │ ├── chat.tsx │ │ │ ├── data │ │ │ │ ├── answers.json │ │ │ │ └── questions.json │ │ │ ├── index.tsx │ │ │ ├── loading.tsx │ │ │ ├── question.tsx │ │ │ ├── settings.tsx │ │ │ ├── simulation.tsx │ │ │ ├── stomp-interface.tsx │ │ │ ├── summary.tsx │ │ │ └── websocket-interface.tsx │ │ ├── header.tsx │ │ ├── md-wrapper │ │ │ ├── component.json │ │ │ ├── loader.js │ │ │ ├── md-wrapper-styles.css │ │ │ ├── md-wrapper-view.html │ │ │ ├── md-wrapper-viewModel.js │ │ │ └── types │ │ │ │ └── loader.d.ts │ │ ├── oj-ref-marked │ │ │ ├── LICENSE.txt │ │ │ ├── README.md │ │ │ ├── THIRD_PARTY_LICENSE.txt │ │ │ ├── component.json │ │ │ └── marked.svg │ │ └── oj-sample │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE.txt │ │ │ ├── README.md │ │ │ ├── SUPPORT.txt │ │ │ ├── component.json │ │ │ ├── coverimage.jpg │ │ │ ├── markdown-viewer │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE.txt │ │ │ ├── README.md │ │ │ ├── SUPPORT.txt │ │ │ ├── THIRD_PARTY_LICENSE.txt │ │ │ ├── component.json │ │ │ ├── extension │ │ │ │ ├── images │ │ │ │ │ └── cca-markdown-viewer.svg │ │ │ │ └── tests │ │ │ │ │ ├── tests.js │ │ │ │ │ ├── viewModels │ │ │ │ │ └── testModel.js │ │ │ │ │ └── views │ │ │ │ │ └── test.html │ │ │ ├── lib │ │ │ │ └── markdownCleaner.js │ │ │ ├── loader.js │ │ │ ├── markdown-viewer-styles.css │ │ │ ├── markdown-viewer-view.html │ │ │ ├── markdown-viewer-viewModel.js │ │ │ └── resources │ │ │ │ └── nls │ │ │ │ ├── markdown-viewer-strings.js │ │ │ │ └── root │ │ │ │ └── markdown-viewer-strings.js │ │ │ ├── min │ │ │ ├── calendar-bundle.js │ │ │ ├── calendar-bundle.js.map │ │ │ ├── controls-bundle.js │ │ │ ├── controls-bundle.js.map │ │ │ ├── layout-bundle.js │ │ │ ├── layout-bundle.js.map │ │ │ └── markdown-viewer │ │ │ │ ├── loader.js │ │ │ │ ├── loader.js.map │ │ │ │ ├── markdown-viewer-styles.css │ │ │ │ └── resources │ │ │ │ └── nls │ │ │ │ ├── markdown-viewer-strings.js │ │ │ │ └── root │ │ │ │ └── markdown-viewer-strings.js │ │ │ ├── oj-sample-audits.js │ │ │ └── types │ │ │ └── markdown-viewer │ │ │ └── loader.d.ts │ ├── index.html │ ├── index.ts │ ├── libs │ │ └── marked │ │ │ └── marked.min.js │ ├── main.js │ └── styles │ │ ├── app.css │ │ ├── fonts │ │ └── App_iconfont.woff │ │ └── images │ │ ├── JET-Favicon-Red-32x32.png │ │ ├── ROC_bg_texture.png │ │ ├── ai.svg │ │ ├── carousel-bg.png │ │ ├── oracle_logo.svg │ │ ├── placeholder-female-01.png │ │ ├── placeholder-female-02.png │ │ ├── placeholder-male-01.png │ │ ├── placeholder-male-05.png │ │ ├── question2-black32x32.png │ │ ├── question2-white32x32.png │ │ ├── question2.png │ │ └── welcomeBanner-foreground2.png └── tsconfig.json ├── architecture.drawio ├── backend ├── .dockerignore ├── Dockerfile ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── main │ ├── java │ │ └── dev │ │ │ └── victormartin │ │ │ └── oci │ │ │ └── genai │ │ │ └── backend │ │ │ └── backend │ │ │ ├── BackendApplication.java │ │ │ ├── InvalidPromptRequest.java │ │ │ ├── config │ │ │ └── WebSocketConfig.java │ │ │ ├── controller │ │ │ ├── GenAIController.java │ │ │ ├── PDFConvertorController.java │ │ │ ├── PromptController.java │ │ │ └── SummaryController.java │ │ │ ├── dao │ │ │ ├── Answer.java │ │ │ ├── GenAiEndpoint.java │ │ │ ├── GenAiModel.java │ │ │ ├── Prompt.java │ │ │ └── SummaryRequest.java │ │ │ ├── data │ │ │ ├── Interaction.java │ │ │ ├── InteractionRepository.java │ │ │ └── InteractionType.java │ │ │ └── service │ │ │ ├── GenAIModelsService.java │ │ │ ├── GenAiClientService.java │ │ │ ├── GenAiInferenceClientService.java │ │ │ ├── OCIGenAIService.java │ │ │ └── PDFConvertorService.java │ └── resources │ │ └── application.yaml │ └── test │ └── java │ └── dev │ └── victormartin │ └── oci │ └── genai │ └── backend │ └── backend │ └── BackendApplicationTests.java ├── deploy ├── k8s │ ├── app │ │ ├── app-svc.yaml │ │ ├── app.yaml │ │ └── kustomization.yaml │ ├── backend │ │ ├── application.yaml.mustache │ │ ├── backend-svc.yaml │ │ ├── backend.yaml │ │ ├── kustomization.yaml │ │ └── service-account.yaml │ ├── ingress │ │ ├── ingress-controller.yaml │ │ ├── ingress-ns.yaml │ │ ├── ingress-rbac.yaml │ │ ├── ingress.yaml │ │ └── kustomization.yaml │ ├── overlays │ │ └── prod │ │ │ └── kustomization.yaml.mustache │ └── web │ │ ├── kustomization.yaml │ │ ├── web-svc.yaml │ │ └── web.yaml └── terraform │ ├── .terraform.lock.hcl │ ├── adb.tf │ ├── data.tf │ ├── iam.tf │ ├── main.tf │ ├── oke.tf │ ├── outputs.tf │ ├── provider.tf │ ├── random.tf │ ├── storage.tf │ ├── terraform.tfvars.mustache │ ├── variables.tf │ └── versions.tf ├── images ├── Markdown.png ├── QandA.png ├── architecture.png ├── demo.gif └── main.png ├── license_policy.yml ├── release_files.json ├── repolinter.json ├── scripts ├── clean.mjs ├── kustom.mjs ├── lib │ ├── README.md │ ├── container.mjs │ ├── crypto.mjs │ ├── fn.js │ ├── gradle.mjs │ ├── npm.mjs │ ├── oci.mjs │ ├── terraform.mjs │ └── utils.mjs ├── package-lock.json ├── package.json ├── release.mjs ├── setenv.mjs └── tfvars.mjs ├── serverStart.sh ├── service └── python │ ├── .gitignore │ ├── requirements.txt │ └── server.py ├── sonar-project.properties └── web ├── .dockerignore ├── .eslintrc.cjs ├── Dockerfile ├── README.md ├── index.html ├── nginx └── default.conf ├── package-lock.json ├── package.json ├── public └── favicon.ico ├── src ├── App.jsx ├── Chat.jsx ├── Conversation.jsx ├── IdentityContext.jsx ├── PromptInput.jsx ├── Routing.jsx ├── Summary.jsx ├── SummaryText.jsx ├── main.jsx └── stompHook │ ├── Provider.jsx │ ├── hook.js │ └── index.js └── vite.config.js /.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/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 | # Thumbnails 10 | ._* 11 | 12 | # Files that might appear in the root of a volume 13 | .DocumentRevisions-V100 14 | .fseventsd 15 | .Spotlight-V100 16 | .TemporaryItems 17 | .Trashes 18 | .VolumeIcon.icns 19 | .com.apple.timemachine.donotpresent 20 | 21 | # Directories potentially created on remote AFP share 22 | .AppleDB 23 | .AppleDesktop 24 | Network Trash Folder 25 | Temporary Items 26 | .apdisk 27 | 28 | # ignore common security keys 29 | .key 30 | .crt 31 | .csr 32 | .pem 33 | 34 | #temp directory ignore 35 | # deploy/ 36 | service/python/Dockerfile 37 | 38 | .certs 39 | .artifacts 40 | deploy/k8s/backend/application.yaml 41 | deploy/k8s/overlays/prod/kustomization.yaml 42 | deploy/k8s/backend/wallet/ 43 | 44 | generated/ 45 | 46 | .env.json 47 | *.bkp 48 | *.zip 49 | kubeconfig 50 | 51 | # Terraform 52 | **/.terraform/* 53 | *.tfstate 54 | *.tfstate.* 55 | crash.log 56 | crash.*.log 57 | *.tfvars 58 | *.tfvars.json 59 | override.tf 60 | override.tf.json 61 | *_override.tf 62 | *_override.tf.json 63 | .terraformrc 64 | terraform.rc 65 | 66 | # Node 67 | node_modules/ 68 | 69 | # Java 70 | .gradle 71 | build/ 72 | !gradle/wrapper/gradle-wrapper.jar 73 | !**/src/main/**/build/ 74 | !**/src/test/**/build/ 75 | .idea 76 | bin/ 77 | dist/ 78 | application-local.yaml -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.compile.nullAnalysis.mode": "automatic" 3 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## Technical help 4 | 5 | ### Get the Load Balancer Public IP address 6 | 7 | ```bash 8 | kubectl get service -n ingress-nginx -o jsonpath='{.items[?(@.spec.type=="LoadBalancer")].status.loadBalancer.ingress[0].ip}' 9 | ``` 10 | 11 | ### Get the `dockerconfigjson` from the secret 12 | 13 | ```bash 14 | kubectl get secret ocir-secret --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode | jq 15 | ``` 16 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /LOCAL.md: -------------------------------------------------------------------------------- 1 | # Run Local 2 | 3 | ## Run components 4 | 5 | ### Run web 6 | 7 | Run locally in a terminal with: 8 | 9 | ```bash 10 | cd web 11 | ``` 12 | 13 | ```bash 14 | npm run dev 15 | ``` 16 | 17 | ### Run Backend 18 | 19 | Run locally on another terminal with: 20 | 21 | ```bash 22 | cd backend 23 | ``` 24 | 25 | Copy `/backend/src/main/resources/application.yaml` to `/backend/src/main/resources/application-local.yaml` and modify the values required. 26 | 27 | It should look like this: 28 | 29 | ```yaml 30 | spring: 31 | main: 32 | banner-mode: "off" 33 | profiles: 34 | active: production 35 | datasource: 36 | driver-class-name: oracle.jdbc.OracleDriver 37 | url: jdbc:oracle:thin:@ADB_SERVICE_NAME_GOES_HERE_high?TNS_ADMIN=/PATH/TO/WALLET/UNZIPPED/IN/TERRAFORM/GENERATED 38 | username: ADMIN 39 | password: "ADB_PASSWORD_GOES_HERE" 40 | type: oracle.ucp.jdbc.PoolDataSource 41 | oracleucp: 42 | sql-for-validate-connection: SELECT * FROM dual 43 | connection-pool-name: connectionPoolName1 44 | initial-pool-size: 5 45 | min-pool-size: 5 46 | max-pool-size: 10 47 | jpa: 48 | hibernate: 49 | use-new-id-generator-mappings: false 50 | ddl-auto: update 51 | oracle: 52 | jdbc: 53 | fanEnabled: true 54 | 55 | genai: 56 | endpoint: "https://inference.generativeai.us-chicago-1.oci.oraclecloud.com" 57 | region: "us-chicago-1" 58 | compartment_id: "GENAI_COMPARTMENT_OCID_GOES_HERE" 59 | chat_model_id: "GEN_AI_CHAT_MODEL_OCID_GOES_HERE" 60 | summarization_model_id: "GEN_AI_SUMMARIZATION_MODEL_OCID_GOES_HERE" 61 | ``` 62 | 63 | Run the Spring Boot backend application in local profile: 64 | 65 | ```bash 66 | ./gradlew bootRun -Plocal 67 | ``` 68 | 69 | ## Other tasks 70 | 71 | ### Build Java Application: 72 | 73 | ```bash 74 | cd backend 75 | ``` 76 | 77 | ```bash 78 | ./gradlew bootJar 79 | ``` 80 | 81 | > `build/libs/backend-0.0.1.jar` jar file generated 82 | 83 | ```bash 84 | cd .. 85 | ``` 86 | 87 | ### Build Web Application: 88 | 89 | ```bash 90 | cd web 91 | ``` 92 | 93 | ```bash 94 | npm run build 95 | ``` 96 | 97 | > `dist` folder generated 98 | 99 | ```bash 100 | cd .. 101 | ``` 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OCI Generative AI toolkit 2 | 3 | [![License: UPL](https://img.shields.io/badge/license-UPL-green)](https://img.shields.io/badge/license-UPL-green) [![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=oracle-devrel_oci-generative-ai-jet-ui)](https://sonarcloud.io/dashboard?id=oracle-devrel_oci-generative-ai-jet-ui) 4 | 5 | 6 | ## Introduction 7 | 8 | Using Oracle JET, create a user-friendly prompt-led user interface (UI) to interact with Oracle's new Generative AI service. This toolkit will configure your Generative AI Service connection so you can begin your journey with AI, or migrate your existing (local or Cloud) LLMs to the Oracle AppDev ecosystem. 9 | [Enhance Engagement Using Content Generation with OCI Generative AI](JET.md) 10 | 11 | This project deploys an AI pipeline with a multipurpose front end for text generation and summarization. The pipeline integrates with a database to track interactions, enabling fine-tuning and performance monitoring for application optimization. It leverages OCI Generative AI APIs on a Kubernetes cluster. 12 | [Accelerating AI Application Deployment Using Cloud Native Strategies](K8S.md) 13 | 14 | ## Contributing 15 | 16 | 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. 17 | 18 | ## License 19 | 20 | Copyright (c) 2024 Oracle and/or its affiliates. 21 | 22 | Licensed under the Universal Permissive License (UPL), Version 1.0. 23 | 24 | See [LICENSE](LICENSE) for more details. 25 | 26 | 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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /jet_components 2 | /exchange_components 3 | /node_modules 4 | /bower_components 5 | /dist 6 | /web 7 | /staged-themes 8 | /themes 9 | /jaftmp@ 10 | 11 | /hybrid/node_modules 12 | /hybrid/platforms 13 | /hybrid/www/* 14 | 15 | !hybrid/plugins 16 | hybrid/plugins/* 17 | !hybrid/plugins/fetch.json 18 | 19 | package-lock.json 20 | 21 | .DS_Store 22 | Thumbs.db 23 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 node:18-slim AS builder 2 | 3 | WORKDIR /usr/src/app 4 | RUN mkdir -p ./src 5 | RUN mkdir -p ./scripts 6 | 7 | COPY src/ ./src/ 8 | COPY scripts/ ./scripts/ 9 | COPY *.json ./ 10 | 11 | 12 | RUN npm install -g @oracle/ojet-cli@16.1.0 13 | RUN npm install 14 | RUN ojet build web --release 15 | 16 | FROM --platform=linux/amd64 nginx:1.23-alpine-slim 17 | RUN rm -rf /usr/share/nginx/html/* 18 | COPY --from=builder /usr/src/app/web/ /usr/share/nginx/html/ 19 | 20 | EXPOSE 80 21 | 22 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /app/oraclejafconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": [ 3 | "+---------------------------------------------------------------------+", 4 | "| OJET Application Audit |", 5 | "+---------------------------------------------------------------------+", 6 | "JAF $jafver - Jet $jetver : ($jafdate, $jaftime)\n" 7 | ], 8 | "base": "$jafcwd", 9 | "files": [ 10 | "./src/**/*.html", 11 | "./src/**/*.js", 12 | "./src/**/*.ts", 13 | "./src/**/component.json", 14 | "./src/styles/**/*.css" 15 | ], 16 | "exclude": [ 17 | "./src/**/*-min.js", 18 | "./src/**/*-min.css", 19 | "./src/styles/*", 20 | "./**/node_modules/**/*.*" 21 | ], 22 | "components": ["./jet_components/**/component.json"], 23 | "builtinJetRules": true, 24 | "jetVer": "15.1", 25 | "ecmaVer": 14, 26 | "format": "prose", 27 | "severity": "all", 28 | "groups": ["all"], 29 | "theme": "Redwood", 30 | "typescript": { 31 | "tsconfig": "." 32 | }, 33 | "options": { 34 | "verbose": false, 35 | "color": true 36 | }, 37 | "ojet": { 38 | "update": true, 39 | "md5": "157a41b6c41e0ea3a2cf1220cb184de3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/oraclejetconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "paths": { 3 | "source": { 4 | "common": "src", 5 | "web": "src-web", 6 | "javascript": ".", 7 | "typescript": ".", 8 | "styles": "styles", 9 | "themes": "themes", 10 | "components": "components", 11 | "exchangeComponents": "exchange_components" 12 | }, 13 | "staging": { 14 | "web": "web", 15 | "themes": "staged-themes" 16 | } 17 | }, 18 | "defaultBrowser": "chrome", 19 | "sassVer": "8.0.0", 20 | "defaultTheme": "redwood", 21 | "typescriptLibraries": "typescript@5.3.2 yargs-parser@~13.1.2", 22 | "webpackLibraries": "webpack@5.75.0 @types/node@18.16.3 webpack-dev-server style-loader css-loader sass-loader sass ts-loader@8.4.0 raw-loader noop-loader html-webpack-plugin html-replace-webpack-plugin copy-webpack-plugin @prefresh/webpack @prefresh/babel-plugin webpack-merge compression-webpack-plugin mini-css-extract-plugin clean-webpack-plugin css-fix-url-loader", 23 | "mochaTestingLibraries": "karma mocha sinon chai@^4 coverage karma-chai@0.1.0 karma-coverage@2.2.0 karma-chrome-launcher@3.1.1 karma-mocha@2.0.1 karma-mocha-reporter@2.2.5 karma-requirejs@1.1.0 karma-fixture@0.2.6 karma-sinon@1.0.5 karma-typescript@5.5.4 @types/chai@4.3.4 @types/karma-fixture@0.2.5 @types/mocha@10.0.1 @types/sinon@10.0.13", 24 | "jestTestingLibraries": "jest@^29 @testing-library/preact@2.0.1 @types/jest@29.0 jest-environment-jsdom@27.5.1 @oracle/oraclejet-jest-preset@~16.0.0", 25 | "architecture": "vdom", 26 | "watchInterval": 1000, 27 | "exchange-url": "https://exchange.oraclecorp.com/api/0.2.0/" 28 | } -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JETGenAI", 3 | "version": "1.0.0", 4 | "description": "Sample Client app showing communication with OCI Generative AI services via Websocket", 5 | "dependencies": { 6 | "@oracle/oraclejet": "~16.1.0", 7 | "@oracle/oraclejet-core-pack": "~16.1.0", 8 | "@stomp/stompjs": "^7.0.0", 9 | "marked": "^4.3.0", 10 | "uuid": "^9.0.1" 11 | }, 12 | "devDependencies": { 13 | "@oracle/ojet-cli": "~16.1.0", 14 | "@oracle/oraclejet-audit": "^16.1.0", 15 | "@types/uuid": "^9.0.7", 16 | "express-http-proxy": "^2.0.0", 17 | "extract-zip": "^1.7.0", 18 | "fs-extra": "^8.1.0", 19 | "glob": "7.2.0", 20 | "typescript": "5.3.2", 21 | "underscore": "^1.10.2", 22 | "url": "^0.11.3", 23 | "yargs-parser": "13.1.2" 24 | }, 25 | "engines": { 26 | "node": ">=16.0.0" 27 | }, 28 | "private": true 29 | } 30 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_app_create.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | module.exports = function () { 10 | return new Promise((resolve) => { 11 | console.log('Running after_app_create hook.'); 12 | resolve(); 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_app_restore.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | module.exports = function () { 10 | return new Promise((resolve) => { 11 | console.log('Running after_app_restore hook.'); 12 | resolve(); 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_app_typescript.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | module.exports = function (configObj) { 10 | return new Promise((resolve) => { 11 | console.log("Running after_app_typescript hook."); 12 | resolve(configObj); 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_build.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running after_build hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_component_build.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running after_component_build hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_component_create.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | module.exports = function (configObj) { 10 | return new Promise((resolve) => { 11 | console.log('Running after_component_create hook.'); 12 | // const componentPath = configObj.componentPath; 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_component_package.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | module.exports = function (configObj) { 8 | return new Promise((resolve) => { 9 | console.log('Running after_component_package hook.'); 10 | // const componentName = configObj.component. 11 | resolve(configObj); 12 | }); 13 | }; -------------------------------------------------------------------------------- /app/scripts/hooks/after_component_typescript.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | module.exports = function (configObj) { 10 | return new Promise((resolve) => { 11 | console.log("Running after_component_typescript hook."); 12 | resolve(configObj); 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_serve.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running after_serve hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/after_watch.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running after_watch hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_app_typescript.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | module.exports = function (configObj) { 10 | return new Promise((resolve) => { 11 | console.log("Running before_app_typescript hook."); 12 | //const { tsconfigJson } = configObj.typescript; 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_build.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_build hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_component_optimize.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_component_optimize hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_component_package.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | module.exports = function (configObj) { 8 | return new Promise((resolve) => { 9 | console.log('Running before_component_package hook.'); 10 | // const componentName = configObj.component. 11 | resolve(configObj); 12 | }); 13 | }; -------------------------------------------------------------------------------- /app/scripts/hooks/before_component_typescript.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | module.exports = function (configObj) { 10 | return new Promise((resolve) => { 11 | console.log("Running before_component_typescript hook."); 12 | //const { tsconfigJson } = configObj.typescript; 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_injection.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_injection hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_optimize.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_optimize hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_release_build.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_release_build hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_serve.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | "use strict"; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_serve hook."); 13 | // ojet custom connect and serve options 14 | const { connectOpts, serveOpts } = configObj; 15 | const express = require("express"); 16 | const http = require("http"); 17 | const proxy = require("express-http-proxy"); 18 | const url = require("url"); 19 | 20 | // New hostname+path as specified by question: 21 | const apiProxy = proxy("http://localhost:8080", { 22 | proxyReqPathResolver: (req) => url.parse("/api" + req.url).path, 23 | }); 24 | const app = express(); 25 | app.use("/api", apiProxy); 26 | // pass back custom http 27 | configObj["http"] = http; 28 | // pass back custom express app 29 | configObj["express"] = app; 30 | // pass back custom options for http.createServer 31 | // const serverOptions = {...}; 32 | // configObj['serverOptions'] = serverOptions; 33 | // pass back custom server 34 | // configObj['server'] = http.createServer(serverOptions, express()); 35 | // const tinylr = require('tiny-lr'); 36 | // pass back custom live reload server 37 | // configObj['liveReloadServer'] = tinylr({ port: PORT }); 38 | // pass back a replacement set of middleware 39 | // configObj['middleware'] = [...]; 40 | // pass back a set of middleware that goes before the default middleware 41 | // configObj['preMiddleware'] = [...]; 42 | // pass back a set of middleware that goes after the default middleware 43 | // configObj['postMiddleware'] = [...]; 44 | resolve(configObj); 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_watch.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_watch hook."); 13 | resolve(configObj); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/scripts/hooks/before_webpack.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | module.exports = function (configObj) { 11 | return new Promise((resolve, reject) => { 12 | console.log("Running before_webpack hook."); 13 | // const { config } = configObj.webpack; 14 | resolve(configObj); 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /app/scripts/hooks/hooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "OJET-CLI hooks configuration file", 3 | "hooks": { 4 | "after_app_create": "scripts/hooks/after_app_create.js", 5 | "after_app_restore": "scripts/hooks/after_app_restore.js", 6 | "after_component_create": "scripts/hooks/after_component_create.js", 7 | "after_component_package": "scripts/hooks/after_component_package.js", 8 | "before_build": "scripts/hooks/before_build.js", 9 | "before_injection": "scripts/hooks/before_injection.js", 10 | "before_optimize": "scripts/hooks/before_optimize.js", 11 | "before_component_optimize": "scripts/hooks/before_component_optimize.js", 12 | "before_component_package": "scripts/hooks/before_component_package.js", 13 | "before_release": "scripts/hooks/before_release.js", 14 | "before_watch": "scripts/hooks/before_watch.js", 15 | "after_build": "scripts/hooks/after_build.js", 16 | "after_component_build": "scripts/hooks/after_component_build.js", 17 | "before_release_build": "scripts/hooks/before_release_build.js", 18 | "before_serve": "scripts/hooks/before_serve.js", 19 | "after_serve": "scripts/hooks/after_serve.js", 20 | "after_watch": "scripts/hooks/after_watch.js", 21 | "before_app_typescript": "scripts/hooks/before_app_typescript.js", 22 | "before_component_typescript": "scripts/hooks/before_component_typescript.js", 23 | "after_app_typescript": "scripts/hooks/after_app_typescript.js", 24 | "after_component_typescript": "scripts/hooks/after_component_typescript.js", 25 | "before_webpack": "scripts/hooks/before_webpack.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/components/app.tsx: -------------------------------------------------------------------------------- 1 | import { Header } from "./header"; 2 | import Content from "./content/index"; 3 | import { registerCustomElement } from "ojs/ojvcomponent"; 4 | import { createContext } from "preact"; 5 | 6 | type Props = { 7 | appName: string; 8 | }; 9 | const tempArray = new Uint32Array(10); 10 | const convoUUID = window.crypto.getRandomValues(tempArray); 11 | 12 | export const ConvoCtx = createContext(""); 13 | 14 | export const App = registerCustomElement("app-root", (props: Props) => { 15 | props.appName = "Generative AI JET UI"; 16 | 17 | return ( 18 |
19 | 20 | {console.log("UUID: ", convoUUID[0].toString())} 21 |
22 | 23 | 24 |
25 | ); 26 | }); 27 | -------------------------------------------------------------------------------- /app/src/components/content/answer.tsx: -------------------------------------------------------------------------------- 1 | import "preact"; 2 | import "md-wrapper/loader"; 3 | import { ojListView } from "ojs/ojlistview"; 4 | import "ojs/ojavatar"; 5 | 6 | declare global { 7 | namespace preact.JSX { 8 | interface IntrinsicElements { 9 | "md-wrapper": any; 10 | } 11 | } 12 | } 13 | 14 | type Props = { 15 | item: ojListView.ItemTemplateContext; 16 | sim: boolean; 17 | }; 18 | 19 | export const Answer = ({ item, sim }: Props) => { 20 | const answer = item.data.answer; 21 | return ( 22 | <> 23 | {sim && ( 24 |
  • 25 |
    26 |
    27 | 32 |
    33 |
    34 | 40 |
    41 |
    42 |
  • 43 | )} 44 | {!sim && ( 45 |
  • 46 |
    47 |
    48 | 53 |
    54 | {/*
    55 | 61 |
    */} 62 |
    63 |
  • 64 | )} 65 | 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /app/src/components/content/chat.tsx: -------------------------------------------------------------------------------- 1 | import { Question } from "./question"; 2 | import { Answer } from "./answer"; 3 | import { Loading } from "./loading"; 4 | import { ComponentProps } from "preact"; 5 | import { useEffect, useRef, useState, MutableRef } from "preact/hooks"; 6 | import "ojs/ojlistview"; 7 | import { ojListView } from "ojs/ojlistview"; 8 | import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); 9 | import Context = require("ojs/ojcontext"); 10 | 11 | type Props = { 12 | testId?: string; 13 | data: any; 14 | questionChanged: (event: any) => void; 15 | question: MutableRef; 16 | }; 17 | 18 | type Item = { 19 | id: number; 20 | answer?: string; 21 | question?: string; 22 | loading?: string; 23 | }; 24 | 25 | type ListProps = ComponentProps<"oj-list-view">; 26 | const madp = new MutableArrayDataProvider([], { 27 | keyAttributes: "id", 28 | }); 29 | 30 | export const Chat = ({ testId, data, questionChanged, question }: Props) => { 31 | const dataProvider = useRef(madp); 32 | const listRef = useRef>(null); 33 | const [lastKey, setLastKey] = useState(0); 34 | 35 | const scrollPos: ListProps["scrollPosition"] = { key: lastKey }; 36 | let busyContext = Context.getContext( 37 | listRef.current as ojListView 38 | ).getBusyContext(); 39 | 40 | useEffect(() => { 41 | dataProvider.current.data = data; 42 | console.log("lastKey before set: ", lastKey); 43 | 44 | // the use of BusyContext here should not be required. It's a workaround for JET-64237. 45 | // it can be removed once the bug is fixed. 46 | busyContext.whenReady().then(() => { 47 | setLastKey(data.length - 1); 48 | }); 49 | }, [data, busyContext]); 50 | 51 | const chatNoDataTemplate = () => { 52 | return ( 53 |
    54 |
    55 | Be the first to ask a question! 56 |
    57 |
    58 | ); 59 | }; 60 | 61 | const chatItemTemplate = (item: ojListView.ItemTemplateContext) => { 62 | return ( 63 | <> 64 | {item.data.answer && } 65 | {item.data.loading && } 66 | {item.data.question && } 67 | 68 | ); 69 | }; 70 | 71 | return ( 72 | <> 73 |
    74 | 84 | 85 | 86 | 87 |
    88 | 96 | 97 | ); 98 | }; 99 | -------------------------------------------------------------------------------- /app/src/components/content/data/answers.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Answer 1", 3 | "Answer 2", 4 | "Answer 3", 5 | "Answer 4" 6 | ] 7 | -------------------------------------------------------------------------------- /app/src/components/content/data/questions.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Question 1", 3 | "Question 2", 4 | "Question 3", 5 | "Question 4" 6 | ] 7 | -------------------------------------------------------------------------------- /app/src/components/content/loading.tsx: -------------------------------------------------------------------------------- 1 | import "preact"; 2 | import "oj-c/progress-bar"; 3 | 4 | export const Loading = () => { 5 | return ( 6 |
  • 7 |
    8 |
    9 | {/*
    */} 10 |
    11 | 15 |
    16 |
    17 |
    18 |
  • 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /app/src/components/content/question.tsx: -------------------------------------------------------------------------------- 1 | import "preact"; 2 | import { ojListView } from "ojs/ojlistview"; 3 | import "ojs/ojavatar"; 4 | 5 | type Props = { 6 | item: ojListView.ItemTemplateContext; 7 | sim: boolean; 8 | }; 9 | export const Question = ({ item, sim }: Props) => { 10 | return ( 11 | <> 12 | {sim && ( 13 |
  • 14 |
    15 |
    16 | 22 |
    23 |
    24 | {item.data.question} 25 |
    26 |
    27 |
  • 28 | )} 29 | {!sim && ( 30 |
  • 31 |
    32 |
    33 | 39 |
    40 |
    41 | {item.data.question} 42 |
    43 |
    44 |
  • 45 | )} 46 | 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /app/src/components/content/simulation.tsx: -------------------------------------------------------------------------------- 1 | import { Question } from "./question"; 2 | import { Answer } from "./answer"; 3 | import { Loading } from "./loading"; 4 | import { ComponentProps } from "preact"; 5 | import { useEffect, useRef, useState, MutableRef } from "preact/hooks"; 6 | import "ojs/ojlistview"; 7 | import { ojListView } from "ojs/ojlistview"; 8 | import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); 9 | import Context = require("ojs/ojcontext"); 10 | 11 | type Props = { 12 | testId?: string; 13 | data: any; 14 | questionChanged: (event: any) => void; 15 | question: MutableRef; 16 | }; 17 | 18 | type Item = { 19 | id: number; 20 | answer?: string; 21 | question?: string; 22 | loading?: string; 23 | }; 24 | 25 | type ListProps = ComponentProps<"oj-list-view">; 26 | const madp = new MutableArrayDataProvider([], { 27 | keyAttributes: "id", 28 | }); 29 | 30 | export const Simulation = ({ 31 | testId, 32 | data, 33 | questionChanged, 34 | question, 35 | }: Props) => { 36 | const dataProvider = useRef(madp); 37 | const listRef = useRef>(null); 38 | const [lastKey, setLastKey] = useState(0); 39 | 40 | const scrollPos: ListProps["scrollPosition"] = { key: lastKey }; 41 | let busyContext = Context.getContext( 42 | listRef.current as ojListView 43 | ).getBusyContext(); 44 | 45 | useEffect(() => { 46 | dataProvider.current.data = data; 47 | console.log("lastKey before set: ", lastKey); 48 | 49 | // the use of BusyContext here should not be required. It's a workaround for JET-64237. 50 | // it can be removed once the bug is fixed. 51 | busyContext.whenReady().then(() => { 52 | setLastKey(data.length - 1); 53 | }); 54 | }, [data, busyContext]); 55 | 56 | const chatNoDataTemplate = () => { 57 | return ( 58 |
    59 |
    60 | Be the first to ask a question! 61 |
    62 |
    63 | ); 64 | }; 65 | 66 | const chatItemTemplate = (item: ojListView.ItemTemplateContext) => { 67 | return ( 68 | <> 69 | {item.data.answer && } 70 | {item.data.loading && } 71 | {item.data.question && } 72 | 73 | ); 74 | }; 75 | 76 | return ( 77 | <> 78 |
    79 | 89 | 90 | 91 | 92 |
    93 | 101 | 102 | ); 103 | }; 104 | -------------------------------------------------------------------------------- /app/src/components/content/websocket-interface.tsx: -------------------------------------------------------------------------------- 1 | import "preact"; 2 | 3 | const gateway = `ws://${window.location.hostname}:1986`; 4 | let sockTimer: any = null; 5 | 6 | export const initWebSocket = ( 7 | setSummaryResults: any, 8 | setBusy: any, 9 | setUpdate: any, 10 | messagesDP: any, 11 | socket: any, 12 | chatData: any 13 | ) => { 14 | // const [connState, setConnState] = useState("Disconnected"); 15 | console.log("Trying to open a WebSocket connection..."); 16 | socket.current = new WebSocket(gateway); 17 | socket.current.binaryType = "arraybuffer"; 18 | 19 | // handle all messages coming from the websocket service 20 | const onMessage = (event: any) => { 21 | const msg = JSON.parse(event.data); 22 | 23 | switch (msg.msgType) { 24 | // switch (Object.keys(msg)[0]) { 25 | case "message": 26 | console.log("message: ", msg.data); 27 | return msg.data; 28 | case "question": 29 | console.log("question: ", msg.data); 30 | return msg.data; 31 | case "summary": 32 | console.log("summary: ", msg.data); 33 | setSummaryResults(msg.data); 34 | return; 35 | case "answer": 36 | console.log("answer: ", msg.data); 37 | if (msg.data !== "connected") { 38 | let tempArray = [...chatData.current]; 39 | // remove the animation item before adding answer 40 | setBusy(false); 41 | tempArray.pop(); 42 | messagesDP.current.data = []; 43 | tempArray.push({ 44 | id: tempArray.length as number, 45 | answer: msg.data, 46 | }); 47 | chatData.current = tempArray; 48 | setUpdate(chatData.current); 49 | } 50 | return msg.data; 51 | default: 52 | return "unknown"; 53 | } 54 | }; 55 | 56 | const onOpen = () => { 57 | clearInterval(sockTimer); 58 | console.log("Connection opened"); 59 | socket.current?.send( 60 | JSON.stringify({ msgType: "message", data: "connected" }) 61 | ); 62 | //setConnState("Connected"); 63 | }; 64 | 65 | // if the connection is lost, wait one minute and try again. 66 | const onError = () => { 67 | //sockTimer = setInterval(initWebSocket, 1000 * 60); 68 | }; 69 | function onClose() { 70 | console.log("Connection closed"); 71 | //setConnState("Disconnected"); 72 | socket.current ? (socket.current.onclose = () => {}) : null; 73 | socket.current?.close(); 74 | } 75 | 76 | socket.current.onopen = onOpen; 77 | socket.current.onerror = onError; 78 | socket.current.onclose = onClose; 79 | socket.current.onmessage = onMessage; 80 | }; 81 | -------------------------------------------------------------------------------- /app/src/components/header.tsx: -------------------------------------------------------------------------------- 1 | import "preact"; 2 | import { useRef, useState, useEffect } from "preact/hooks"; 3 | import * as ResponsiveUtils from "ojs/ojresponsiveutils"; 4 | import "ojs/ojbutton"; 5 | 6 | type Props = Readonly<{ 7 | appName: string; 8 | }>; 9 | 10 | export const Header = (props: Props) => { 11 | const mediaQueryRef = useRef( 12 | window.matchMedia(ResponsiveUtils.getFrameworkQuery("sm-only")!) 13 | ); 14 | 15 | const [isSmallWidth, setIsSmallWidth] = useState( 16 | mediaQueryRef.current.matches 17 | ); 18 | 19 | useEffect(() => { 20 | mediaQueryRef.current.addEventListener("change", handleMediaQueryChange); 21 | return () => 22 | mediaQueryRef.current.removeEventListener( 23 | "change", 24 | handleMediaQueryChange 25 | ); 26 | }, [mediaQueryRef]); 27 | 28 | const handleMediaQueryChange = (e: MediaQueryListEvent) => { 29 | setIsSmallWidth(e.matches); 30 | }; 31 | 32 | return ( 33 | 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /app/src/components/md-wrapper/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "md-wrapper", 3 | "version": "1.0.0", 4 | "jetVersion": "^15.0.0", 5 | "displayName": "Wrapper component for markdown viewer", 6 | "dependencies": { 7 | "oj-sample-markdown-viewer": "^9.0.0" 8 | }, 9 | "properties": { 10 | "markdown": { 11 | "type": "string" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/components/md-wrapper/loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | define([ 8 | "ojs/ojcomposite", 9 | "text!./md-wrapper-view.html", 10 | "./md-wrapper-viewModel", 11 | "text!./component.json", 12 | "css!./md-wrapper-styles.css", 13 | ], function (Composite, view, viewModel, metadata) { 14 | Composite.register("md-wrapper", { 15 | view: view, 16 | viewModel: viewModel, 17 | metadata: JSON.parse(metadata), 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /app/src/components/md-wrapper/md-wrapper-styles.css: -------------------------------------------------------------------------------- 1 | /* This file is where css selectors for the component go. 2 | There will be hard coded properties and properties controlled by variables, for example 3 | 4 | md-wrapper .md-wrapper-value-text { 5 | color: var(--md-wrapper-value-text-color); 6 | display: inline-block; 7 | } 8 | */ 9 | 10 | md-wrapper:not(.oj-complete){ 11 | visibility: hidden; 12 | } 13 | 14 | md-wrapper{ 15 | min-height: 50px; 16 | width: 50px; 17 | } 18 | 19 | md-wrapper[hidden] 20 | { 21 | display: none; 22 | } -------------------------------------------------------------------------------- /app/src/components/md-wrapper/md-wrapper-view.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /app/src/components/md-wrapper/md-wrapper-viewModel.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2023, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | "use strict"; 8 | define([ 9 | "knockout", 10 | "ojs/ojcontext", 11 | "ojs/ojknockout", 12 | "oj-sample/markdown-viewer/loader", 13 | ], function (ko, Context) { 14 | function MdWrapper(context) { 15 | var self = this; 16 | self.composite = context.element; 17 | self.properties = context.properties; 18 | } 19 | 20 | return MdWrapper; 21 | }); 22 | -------------------------------------------------------------------------------- /app/src/components/md-wrapper/types/loader.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace preact.JSX { 3 | interface IntrinsicElements { 4 | "md-wrapper": any; 5 | } 6 | } 7 | } 8 | 9 | export {}; 10 | -------------------------------------------------------------------------------- /app/src/components/oj-ref-marked/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. 2 | The Universal Permissive License (UPL), Version 1.0 3 | 4 | Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software, associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering either (i) the unmodified Software as contributed to or provided by such licensor, or (ii) the Larger Works (as defined below), to deal in both 5 | 6 | (a) the Software, and 7 | 8 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software (each a “Larger Work” to which the Software is contributed by such licensors), 9 | 10 | without restriction, including without limitation the rights to copy, create derivative works of, display, perform, and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms. 11 | 12 | This license is subject to the following condition: 13 | 14 | The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /app/src/components/oj-ref-marked/README.md: -------------------------------------------------------------------------------- 1 | # Marked Reference Component 2 | 3 | This component provides reference information for the Marked library which is used to render Markdown (md) documents in HTML 4 | 5 | For more information and documentation see [Marked on GITHub](https://github.com/markedjs/marked) -------------------------------------------------------------------------------- /app/src/components/oj-ref-marked/THIRD_PARTY_LICENSE.txt: -------------------------------------------------------------------------------- 1 | # License information 2 | 3 | ## Marked 4 | 5 | Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/) 6 | Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | ## Markdown 27 | 28 | Copyright © 2004, John Gruber 29 | http://daringfireball.net/ 30 | All rights reserved. 31 | 32 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 33 | 34 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 35 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 36 | * Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 37 | 38 | This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. 39 | -------------------------------------------------------------------------------- /app/src/components/oj-ref-marked/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oj-ref-marked", 3 | "displayName": "Marked reference component", 4 | "description": "Reference component for the marked library used for viewing documents in Markdown format ", 5 | "type": "reference", 6 | "package": "marked", 7 | "version": "4.3.0", 8 | "license": "https://opensource.org/licenses/MIT", 9 | "paths": { 10 | "npm": { 11 | "min": "marked.min", 12 | "debug": "marked.min" 13 | }, 14 | "cdn": { 15 | "min": "https://static.oracle.com/cdn/jet/packs/3rdparty/marked/4.3.0/marked.min", 16 | "debug": "https://static.oracle.com/cdn/jet/packs/3rdparty/marked/4.3.0/marked.min" 17 | } 18 | }, 19 | "extension": { 20 | "oracle": { 21 | "businessApprovals": { 22 | "marked": "142506" 23 | } 24 | }, 25 | "catalog": { 26 | "category": "Reference", 27 | "coverImage": "marked.svg" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/components/oj-sample/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019,2023 Oracle and/or its affiliates. All rights reserved. 2 | The Universal Permissive License (UPL), Version 1.0 3 | 4 | Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software, associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering either (i) the unmodified Software as contributed to or provided by such licensor, or (ii) the Larger Works (as defined below), to deal in both 5 | 6 | (a) the Software, and 7 | 8 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software (each a “Larger Work” to which the Software is contributed by such licensors), 9 | 10 | without restriction, including without limitation the rights to copy, create derivative works of, display, perform, and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms. 11 | 12 | This license is subject to the following condition: 13 | 14 | The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | Support Information: 19 | -------------------- 20 | This component is an unsupported sample for demonstration purposes only. -------------------------------------------------------------------------------- /app/src/components/oj-sample/README.md: -------------------------------------------------------------------------------- 1 | # OJ-SAMPLE Component Set 2 | 3 | ## Support Notice ## 4 | 5 | > December 2023: **This sample component set has been retired and will no longer be updated.** 6 | > 7 | > If you want to take a copy of this code for your own use see [Viewing the Source Code for the oj-sample Components](https://blogs.oracle.com/groundside/post/viewing-the-source-code-for-the-oj-sample-components) 8 | 9 | ---- 10 | 11 | The oj-sample JET Pack defines a set of re-usable JET components for Oracle JET and Visual Builder applications to use 12 | 13 | ## Support Information 14 | 15 | This component is an unsupported sample for demonstration purposes only. If you encounter any problems feel free to report them via the Oracle forums and we will do our best to address them, however, note that this will be on a best-effort basis only. -------------------------------------------------------------------------------- /app/src/components/oj-sample/SUPPORT.txt: -------------------------------------------------------------------------------- 1 | This component is an unsupported sample for demonstration purposes only. -------------------------------------------------------------------------------- /app/src/components/oj-sample/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oj-sample", 3 | "displayName": "oj-sample JET Pack", 4 | "type": "pack", 5 | "version": "9.0.0", 6 | "jetVersion": ">=13.0.0 <17.0.0", 7 | "description": "Consolidating JET Pack for a set of example components for use in JET and Visual Builder", 8 | "license": "https://opensource.org/licenses/UPL", 9 | "dependencies": { 10 | "oj-sample-markdown-viewer": "9.0.0" 11 | }, 12 | "bundles": {}, 13 | "paths": { 14 | "cdn": { 15 | "debug": "https://static.oracle.com/cdn/jet/packs/oj-sample/9.0.0", 16 | "min": "https://static.oracle.com/cdn/jet/packs/oj-sample/9.0.0/min" 17 | } 18 | }, 19 | "extension": { 20 | "vbdt": { 21 | "audits": "oj-sample-audits" 22 | }, 23 | "catalog": { 24 | "category": "Sample Components", 25 | "coverImage": "coverimage.jpg" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/coverimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/components/oj-sample/coverimage.jpg -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log for <oj-sample-markdown-viewer> Component 2 | 3 | ## Version 9.0.0 4 | 5 | * Upgrade to JET 15 base. Supported versions of JET for this version are now JET 13 through 15 6 | * This is the terminal release of this sample component - no further updates will be issued 7 | 8 | ## Version 8.0.0 9 | 10 | * Upgrade to JET 14 base. Supported versions of JET for this version are now JET 12 through 14 11 | * Upgrade markdown processing library to marked 4.3.0 12 | * Remove the deprecated _ghost_ option for the flavor property 13 | 14 | ## Version 7.0.0 15 | 16 | * Upgrade to JET 13 base. Supported versions of JET for this version are now JET 11 through 13 17 | 18 | ## Version 6.0.1 19 | 20 | * Provision of type definition to improve design time experience for consumers using TypeScript and virtual DOM 21 | 22 | ## Version 6.0.0 23 | 24 | * Upgrade to a JET 12 base 25 | * Bump in support range to >=10.0.0 <13.0.0 26 | 27 | ## Version 5.0.2 28 | 29 | * Documentation and descriptive metadata clean up 30 | 31 | ## Version 5.0.1 32 | 33 | * Code clean up 34 | 35 | ## Version 5.0.0 36 | 37 | * Update of supported JET version range to >=9.0.0 <12.0.0 38 | * Introduction of the new html-rendering property to provide more control over how the generated HTML markup is styled 39 | 40 | ## Version 4.0.0 41 | 42 | * Switch to the marked library (MIT licence) for markdown interpretation from Showdown 43 | * Removal of support for ghost variant (vanilla will be used instead) 44 | 45 | ## Version 3.0.1 46 | 47 | * Design time metadata improvements 48 | 49 | ## Version 3.0.0 50 | 51 | * Update JET compatibility range to include JET 10 52 | * Set minimum JET support version to 8.0.0 53 | 54 | ## Version 2.2.2 55 | 56 | * Minification packaging into distribution 57 | 58 | ## Version 2.2.1 59 | 60 | * New icon for design time 61 | 62 | ## Version 2.2.0 63 | 64 | * Expansion of JET support range to include JET 9 65 | 66 | ## Version 2.1.0 67 | 68 | * Expansion of JET support range to include JET 8 69 | 70 | ## Version 2.0.1 71 | 72 | * Expansion of JET support range to include version from 6.1.0 upwards 73 | 74 | ## Version 2.0.0 75 | 76 | * Update to support JET 7.0.0 77 | 78 | ## Version 1.1.0 79 | 80 | * Update baseline to JET 6.2.0 and replace use of deprecated JET APIs 81 | 82 | ## 1.0.0 83 | 84 | * Initial release 85 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, 2023 Oracle and/or its affiliates. All rights reserved. 2 | The Universal Permissive License (UPL), Version 1.0 3 | 4 | Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software, associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering either (i) the unmodified Software as contributed to or provided by such licensor, or (ii) the Larger Works (as defined below), to deal in both 5 | 6 | (a) the Software, and 7 | 8 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software (each a “Larger Work” to which the Software is contributed by such licensors), 9 | 10 | without restriction, including without limitation the rights to copy, create derivative works of, display, perform, and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms. 11 | 12 | This license is subject to the following condition: 13 | 14 | The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | Support Information: 19 | -------------------- 20 | This component is an unsupported sample for demonstration purposes only. -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/README.md: -------------------------------------------------------------------------------- 1 | # Markdown Document Viewer Component 2 | 3 | ## Support Notice ## 4 | 5 | > December 2023: **This sample component has been retired and will no longer be updated.** 6 | > 7 | > If you want to take a copy of this code for your own use see [Viewing the Source Code for the oj-sample Components](https://blogs.oracle.com/groundside/post/viewing-the-source-code-for-the-oj-sample-components) 8 | 9 | ---- 10 | 11 | The **``** provides a simple viewer which will take a string as a value and interprets markdown formatting for display. 12 | 13 | The component supports interpreting markdown in several variants including the [GitHub Flavor of Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/). This is controlled via the **flavor** property. 14 | 15 | ## Example 16 | 17 | A sample markdown file might look a little like this 18 | 19 | > \# Title 20 | > \## Subtitle 21 | > Normal paragraph text 22 | > \* Bullet Point 1 23 | > \* Bullet Point 2 24 | > \* Bullet Point 3 25 | > And a code block 26 | > \`\`\` JavaScript 27 | > var self = this; 28 | > \`\`\` 29 | 30 | Assuming the above file had been sucked into a JavaScript variable called markupToDisplay then the component would 31 | simply look like this 32 | 33 | ``` JavaScript 34 | 35 | ``` 36 | 37 | And the result in the UI would be: 38 | 39 | > # Title 40 | > ## Subtitle 41 | > Normal paragraph text 42 | > * Bullet Point 1 43 | > * Bullet Point 2 44 | > * Bullet Point 3 45 | > And a code block 46 | > ``` JavaScript 47 | > var self = this; 48 | > ``` 49 | 50 | ## Notes 51 | 52 | 1. The **value** property is read-only. 53 | 2. Because markdown is a very flexible format it can be used to embed undesirable content into your page. If you are receiving the markdown string from an unknown source (such as an uploaded file) you must be careful to sanitize the HTML created by the conversion process in order to remove unwanted HTML or injected JavaScript. The output-filter function callback is provided for you to do so. This function should take a string containing the candidate HTML output and should return a promise to the sanitized version. 54 | 3. This Custom Web Component uses a 3rd party library to manage the markdown conversion process. Currently this is [Marked 4.3.0](https://github.com/markedjs/marked) 55 | 56 | ## Support Information 57 | 58 | This component is an unsupported sample for demonstration purposes only. If you encounter any problems feel free to report them via the Oracle forums and we will do our best to address them, however, note that this will be on a best-effort basis only. -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/SUPPORT.txt: -------------------------------------------------------------------------------- 1 | This component is an unsupported sample for demonstration purposes only. -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/THIRD_PARTY_LICENSE.txt: -------------------------------------------------------------------------------- 1 | ## Marked 2 | 3 | Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/) 4 | Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | ## Markdown 25 | 26 | Copyright © 2004, John Gruber 27 | http://daringfireball.net/ 28 | All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 31 | 32 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 33 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 34 | * Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 35 | 36 | This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. 37 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/component.json: -------------------------------------------------------------------------------- 1 | {"name":"markdown-viewer","pack":"oj-sample","type":"composite","implements":["SampleMarkdownViewer"],"status":[{"type":"deprecated","since":"9.0.0","description":"The sample component set has reached end of life and will not be updated"}],"displayName":"Markdown Document Viewer","description":"A component which interprets and displays strings in the GITHub version of markdown (.md) format","version":"9.0.0","license":"https://opensource.org/licenses/UPL","jetVersion":">=13.0.0 <16.0.0","dependencies":{"oj-ref-marked":"^4.3.0"},"icon":{"iconPath":"extension/images/cca-markdown-viewer.svg","selectedIconPath":"extension/images/cca-markdown-viewer.svg","hoverIconPath":"extension/images/cca-markdown-viewer.svg"},"properties":{"value":{"displayName":"Markdown","description":"String in markdown format to be converted for display (Required)","type":"string","propertyGroup":"data","extension":{"vbdt":{"placeholderValue":"Markdown content"}}},"flavor":{"displayName":"Markdown Variant","description":"Select one of the sub-dialects of markdown to support","value":"vanilla","type":"string","enumValues":["vanilla","github"],"propertyGroup":"common"},"outputFilter":{"displayName":"Output Filter Function","description":"Callback function which you can define to sanitize the HTML output generated for the viewer. This function should return a promise to the sanitized HTML string","type":"function(string):promise"},"htmlRendering":{"displayName":"HTML Rendering","description":"Provides additional control over styling such as header sizes in rendered markdown","value":"legacy","type":"string","enumValues":["legacy","inherit","redwood"],"propertyEditorValues":{"legacy":{"description":"Provides some minor override formatting for lists and pre elements"},"inherit":{"description":"Pick up all HTML styles from the application"},"redwood":{"description":"Optimized header sizing for when the application is running in the Redwood theme"}},"propertyGroup":"common"}},"extension":{"oracle":{"businessApprovals":{"marked":"142506"}},"catalog":{"category":"Sample Components","tags":["markdown"],"coverImage":"extension/images/cca-markdown-viewer.svg"}}} 2 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/extension/tests/tests.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, 2019, Oracle and/or its affiliates. 3 | The Universal Permissive License (UPL), Version 1.0 4 | */ 5 | /* 6 | * QUnit Tests for Markdown Viewer 7 | */ 8 | 'use strict'; 9 | define(['ojs/ojcontext', 'knockout', 10 | 'oj-sample/markdown-viewer/extension/tests/viewModels/testModel', 11 | 'text!oj-sample/markdown-viewer/extension/tests/views/test.html' 12 | ], 13 | function (Context, ko, TestModel, testMarkup) { 14 | QUnit.module('markdown-viewer:properties'); 15 | QUnit.test('Default properties test', function (assert) { 16 | //insert the testing DOM structure 17 | var insertSite = document.getElementById('qunit-fixture'); 18 | var template = document.createElement('template'); 19 | template.innerHTML = testMarkup; 20 | insertSite.appendChild(template.content); 21 | var done = assert.async(); 22 | assert.expect(1); 23 | 24 | require(['oj-sample/markdown-viewer/loader'], function () { 25 | var componentDom = document.getElementById('oj-sample-markdown-viewer-tests'); 26 | var testModel = new TestModel(); 27 | var busyContext = Context.getContext(componentDom).getBusyContext(); 28 | ko.applyBindings(testModel, componentDom); 29 | 30 | busyContext.whenReady().then(function () { 31 | //did it instantiate? 32 | var ccaElement = document.getElementById('demoMarkdown'); 33 | var flavor = ccaElement.getProperty('flavor'); 34 | 35 | assert.ok(flavor === 'vanilla', 'Default markdown flavor is: \"vanilla\"'); 36 | done(); 37 | }); 38 | }); 39 | }); 40 | 41 | }); -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/extension/tests/viewModels/testModel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017, 2019, Oracle and/or its affiliates. 3 | * The Universal Permissive License (UPL), Version 1.0 4 | * Markdown Viewer Test Model 5 | */ 6 | define(['knockout'], 7 | function (ko) { 8 | 'use strict'; 9 | 10 | function markdownTestViewModel() { 11 | 12 | } 13 | 14 | return markdownTestViewModel; 15 | }); -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/extension/tests/views/test.html: -------------------------------------------------------------------------------- 1 |
    2 | 7 |
    8 | 9 |
    10 |
    -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/lib/markdownCleaner.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018, 2023, Oracle and/or its affiliates. 3 | * The Universal Permissive License (UPL), Version 1.0 4 | */ 5 | define([], 6 | function(){ 7 | 'use strict'; 8 | function MarkdownCleaner() { 9 | } 10 | 11 | MarkdownCleaner.prototype.defaultCleanse = function(rawOutput){ 12 | var scriptTag = /<[//]*script[\s]*>/gi; 13 | return rawOutput.replace(scriptTag,'<script>'); 14 | }; 15 | 16 | 17 | return new MarkdownCleaner; 18 | }); 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017, 2023, Oracle and/or its affiliates. 3 | The Universal Permissive License (UPL), Version 1.0 4 | */ 5 | define(['ojs/ojcomposite', 'text!./markdown-viewer-view.html', './markdown-viewer-viewModel', 'text!./component.json', 'css!./markdown-viewer-styles'], 6 | function(Composite, view, viewModel, metadata) { 7 | Composite.register('oj-sample-markdown-viewer', { 8 | view: view, 9 | viewModel: viewModel, 10 | metadata: JSON.parse(metadata) 11 | }); 12 | } 13 | ); -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/markdown-viewer-styles.css: -------------------------------------------------------------------------------- 1 | oj-sample-markdown-viewer:not(.oj-complete){ 2 | visibility: hidden; 3 | } 4 | 5 | oj-sample-markdown-viewer{ 6 | display:inline; 7 | } 8 | 9 | oj-sample-markdown-viewer .legacyStyling blockquote { 10 | border-left: 5px solid #DCE3E4; 11 | background:#fcfcfc; 12 | margin: 10px; 13 | padding: 10px; 14 | } 15 | 16 | oj-sample-markdown-viewer .legacyStyling blockquote p { 17 | display: inline; 18 | } 19 | 20 | oj-sample-markdown-viewer .legacyStyling pre { 21 | background:#fcfcfc; 22 | margin: 10px; 23 | padding: 10px; 24 | } 25 | 26 | oj-sample-markdown-viewer .legacyStyling table { 27 | border-collapse: collapse; 28 | text-align:left; 29 | padding-right: 0.5em; 30 | padding-left: 0.5em; 31 | border: 1px solid #8DA6B1; 32 | } 33 | 34 | oj-sample-markdown-viewer .legacyStyling th { 35 | text-align:left; 36 | padding-right: 0.5em; 37 | padding-left: 0.5em; 38 | border: 1px solid #8DA6B1; 39 | } 40 | 41 | oj-sample-markdown-viewer .legacyStyling td { 42 | text-align:left; 43 | padding-right: 0.5em; 44 | padding-left: 0.5em; 45 | border: 1px solid #8DA6B1; 46 | vertical-align: top; 47 | } 48 | 49 | oj-sample-markdown-viewer .redwoodStyling h1 { 50 | font-size: 1.75rem; 51 | font-weight: 700; 52 | line-height: 1.2857; 53 | } 54 | 55 | oj-sample-markdown-viewer .redwoodStyling h2 { 56 | font-size: 1.5rem; 57 | font-weight: 700; 58 | line-height: 1.3333; 59 | } 60 | 61 | oj-sample-markdown-viewer .redwoodStyling h3 { 62 | font-size: 1.25rem; 63 | font-weight: 700; 64 | line-height: 1.4; 65 | } 66 | 67 | oj-sample-markdown-viewer .redwoodStyling h4 { 68 | font-size: 1rem; 69 | font-weight: 700; 70 | line-height: 1.5; 71 | } 72 | 73 | oj-sample-markdown-viewer .redwoodStyling h5 { 74 | font-size: 0.9167rem; 75 | font-weight: 600; 76 | line-height: 1.4; 77 | } 78 | 79 | oj-sample-markdown-viewer .redwoodStyling h6 { 80 | font-size: 0.8333rem; 81 | font-weight: 600; 82 | line-height: 1.4; 83 | } 84 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/markdown-viewer-view.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
    10 |
    11 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/resources/nls/markdown-viewer-strings.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. */ 2 | /* 3 | * Standard String resources used within the component 4 | */ 5 | define({ 6 | "root": true 7 | }); 8 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/markdown-viewer/resources/nls/root/markdown-viewer-strings.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2017, 2019 Oracle and/or its affiliates. All rights reserved. */ 2 | /* 3 | * Standard String resources used within the component 4 | */ 5 | define({ 6 | "ojsampleMarkdownViewer": { 7 | "designTimeMarker": "Set value property for content", 8 | "@designTimeMarker": { "description": "Text displayed in the Visual Builder Design Time environment to help the developer to locate the component when it would be otherwise invisible" }, 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/min/markdown-viewer/loader.js: -------------------------------------------------------------------------------- 1 | define("text!oj-sample/markdown-viewer/markdown-viewer-view.html",[],(function(){return'\x3c!--\n Copyright (c) 2019,2023 Oracle and/or its affiliates.\n The Universal Permissive License (UPL), Version 1.0\n--\x3e\n\n
    \n
    '})),define("oj-sample/markdown-viewer/resources/nls/markdown-viewer-strings",{root:!0}),define("oj-sample/markdown-viewer/lib/markdownCleaner",[],(function(){"use strict";function e(){}return e.prototype.defaultCleanse=function(e){return e.replace(/<[//]*script[\s]*>/gi,"<script>")},new e})),define("oj-sample/markdown-viewer/markdown-viewer-viewModel",["ojs/ojcontext","ojs/ojlogger","knockout","ojL10n!./resources/nls/markdown-viewer-strings","marked","./lib/markdownCleaner"],(function(e,t,n,o,r,i){"use strict";function a(e){var t=this;t.composite=e.element,t.res=o.ojsampleMarkdownViewer,t.contentSubId=e.uniqueId+"_content",t.inputValue=n.observable(),t.isContent=n.observable(!1),t.applyRedwoodStyling=n.observable("redwood"===e.properties.htmlRendering.toLowerCase()),t.applyLegacyStyling=n.observable(["legacy","redwood"].includes(e.properties.htmlRendering.toLowerCase())),t.converter=r,t.converter.setOptions({gfm:"github"===e.properties.flavor}),t.doFilter=!1,t.loggingIdentity="oj-sample-markdown-viewer ("+e.uniqueId+"): ",t.properties=e.properties,void 0!==t.properties.outputFilter&&(t.doFilter=!0)}return a.prototype.bindingsApplied=function(o){var r=this;r.markdownValue=n.computed((function(){var n=e.getContext(o.element).getBusyContext().addBusyState({description:"Processing of markdown content"});if(void 0!==r.inputValue()&&r.inputValue().length>0){var a=r.inputValue().replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,""),s=i.defaultCleanse(r.converter.parse(a));(r.doFilter?r.properties.outputFilter(s):Promise.resolve(s)).then((function(e){void 0!==e&&"string"==typeof e?e.length>0?(r.isContent(!0),document.getElementById(r.contentSubId).innerHTML=e):r.isContent(!1):r.isContent(!1);n()})).catch((function(e){r.isContent(!1),t.error(r.loggingIdentity+" "+e),n()}))}else r.isContent(!1),n()})),r.inputValue(r.properties.value)},a.prototype.propertyChanged=function(e){var t=this;if("external"===e.updatedFrom)switch(e.property){case"value":t.inputValue(e.value);break;case"flavor":t.converter.setOptions({gfm:"github"===e.value}),t.inputValue.valueHasMutated();break;case"htmlRendering":t.applyRedwoodStyling("redwood"===e.value.toLowerCase()),t.applyLegacyStyling(["legacy","redwood"].includes(e.value.toLowerCase()))}},a})),define("text!oj-sample/markdown-viewer/component.json",[],(function(){return'{"name":"markdown-viewer","pack":"oj-sample","version":"9.0.0","jetVersion":">=13.0.0 <16.0.0","dependencies":{"oj-ref-marked":"^4.3.0"},"properties":{"value":{"type":"string"},"flavor":{"value":"vanilla","type":"string","enumValues":["vanilla","github"]},"outputFilter":{"type":"function(string):promise"},"htmlRendering":{"value":"legacy","type":"string","enumValues":["legacy","inherit","redwood"]}}}\n'})),define("oj-sample/markdown-viewer/loader",["ojs/ojcomposite","text!./markdown-viewer-view.html","./markdown-viewer-viewModel","text!./component.json","css!./markdown-viewer-styles"],(function(e,t,n,o){e.register("oj-sample-markdown-viewer",{view:t,viewModel:n,metadata:JSON.parse(o)})})); 2 | //# sourceMappingURL=loader.js.map -------------------------------------------------------------------------------- /app/src/components/oj-sample/min/markdown-viewer/loader.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"loader.js","names":["define","root","MarkdownCleaner","prototype","defaultCleanse","rawOutput","replace","Context","Logger","ko","componentStrings","marked","cleaner","MarkdownViewerComponentModel","context","self","this","composite","element","res","ojsampleMarkdownViewer","contentSubId","uniqueId","inputValue","observable","isContent","applyRedwoodStyling","properties","htmlRendering","toLowerCase","applyLegacyStyling","includes","converter","setOptions","gfm","flavor","doFilter","loggingIdentity","undefined","outputFilter","bindingsApplied","markdownValue","computed","busyResolve","getContext","getBusyContext","addBusyState","description","length","toParse","html","parse","Promise","resolve","then","cleanedHTML","document","getElementById","innerHTML","catch","error","value","propertyChanged","updatedFrom","property","valueHasMutated","Composite","view","viewModel","metadata","register","JSON"],"sources":["0"],"mappings":"AACAA,OAAO,2DAA2D,IAAG,WAAc,MAAO,wTAAmT,IAM7YA,OAAO,kEAAkE,CACvEC,MAAQ,IAQVD,OAAO,gDAAgD,IACvD,WACE,aACE,SAASE,IACT,CAQA,OANAA,EAAgBC,UAAUC,eAAiB,SAASC,GAElD,OAAOA,EAAUC,QADD,uBACmB,iBACrC,EAGO,IAAIJ,CACf,IAQAF,OACI,sDAAsD,CAAC,gBAAgB,eAAe,WAAY,iDAAkD,SAAU,0BAC3J,SAAUO,EAASC,EAAQC,EAAIC,EAAkBC,EAAQC,GACxD,aACA,SAASC,EAA6BC,GAClC,IAAIC,EAAOC,KACXD,EAAKE,UAAYH,EAAQI,QACzBH,EAAKI,IAAMT,EAAiBU,uBAE5BL,EAAKM,aAAeP,EAAQQ,SAAW,WAGvCP,EAAKQ,WAAad,EAAGe,aACrBT,EAAKU,UAAahB,EAAGe,YAAW,GAEhCT,EAAKW,oBAAsBjB,EAAGe,WAA8D,YAAnDV,EAAQa,WAAWC,cAAcC,eAC1Ed,EAAKe,mBAAqBrB,EAAGe,WAAW,CAAC,SAAS,WAAWO,SAASjB,EAAQa,WAAWC,cAAcC,gBAEvGd,EAAKiB,UAAYrB,EACjBI,EAAKiB,UAAUC,WAAW,CAACC,IAAmC,WAA9BpB,EAAQa,WAAWQ,SAEnDpB,EAAKqB,UAAW,EAChBrB,EAAKsB,gBAAkB,8BAAgCvB,EAAQQ,SAAW,MAE1EP,EAAKY,WAAab,EAAQa,gBACWW,IAAjCvB,EAAKY,WAAWY,eAChBxB,EAAKqB,UAAW,EAExB,CAgFA,OAvEAvB,EAA6BV,UAAUqC,gBAAkB,SAAU1B,GAC/D,IAAIC,EAAOC,KACXD,EAAK0B,cAAgBhC,EAAGiC,UAAS,WAG7B,IAEIC,EAFcpC,EAAQqC,WAAW9B,EAAQI,SAAS2B,iBAExBC,aADhB,CAAEC,YAAe,mCAE/B,QAA0BT,IAAtBvB,EAAKQ,cAA8BR,EAAKQ,aAAayB,OAAS,EAAG,CAEjE,IAAIC,EAAUlC,EAAKQ,aAAajB,QAAQ,0CAA0C,IAC9E4C,EAAOtC,EAAQR,eAAeW,EAAKiB,UAAUmB,MAAMF,KAInDlC,EAAKqB,SACWrB,EAAKY,WAAWY,aAAaW,GAG7BE,QAAQC,QAAQH,IAItBI,MAAK,SAAUC,QACLjB,IAAhBiB,GAAoD,iBAAhBA,EAChCA,EAAYP,OAAS,GACrBjC,EAAKU,WAAU,GACF+B,SAASC,eAAe1C,EAAKM,cACnCqC,UAAYH,GAGnBxC,EAAKU,WAAU,GAInBV,EAAKU,WAAU,GAEnBkB,GACJ,IAAGgB,OAAM,SAASC,GACd7C,EAAKU,WAAU,GACfjB,EAAOoD,MAAM7C,EAAKsB,gBAAkB,IAAMuB,GAC1CjB,GACJ,GACJ,MAEI5B,EAAKU,WAAU,GACfkB,GAER,IACA5B,EAAKQ,WAAWR,EAAKY,WAAWkC,MACpC,EAEAhD,EAA6BV,UAAU2D,gBAAkB,SAAUhD,GAC/D,IAAIC,EAAOC,KACX,GAA4B,aAAxBF,EAAQiD,YACR,OAAQjD,EAAQkD,UACZ,IAAK,QACDjD,EAAKQ,WAAWT,EAAQ+C,OACxB,MACJ,IAAK,SACD9C,EAAKiB,UAAUC,WAAW,CAACC,IAAuB,WAAlBpB,EAAQ+C,QACxC9C,EAAKQ,WAAW0C,kBAChB,MACJ,IAAK,gBACDlD,EAAKW,oBAAoD,YAAhCZ,EAAQ+C,MAAMhC,eACvCd,EAAKe,mBAAmB,CAAC,SAAS,WAAWC,SAASjB,EAAQ+C,MAAMhC,gBAIpF,EAEOhB,CACX,IAGJb,OAAO,gDAAgD,IAAG,WAAc,MAAO,uZAAwZ,IAOveA,OAAO,mCAAmC,CAAC,kBAAmB,mCAAoC,8BAA+B,wBAAyB,iCACxJ,SAASkE,EAAWC,EAAMC,EAAWC,GACnCH,EAAUI,SAAS,4BAA6B,CAC9CH,KAAMA,EACNC,UAAWA,EACXC,SAAUE,KAAKpB,MAAMkB,IAEzB"} -------------------------------------------------------------------------------- /app/src/components/oj-sample/min/markdown-viewer/markdown-viewer-styles.css: -------------------------------------------------------------------------------- 1 | oj-sample-markdown-viewer:not(.oj-complete){ 2 | visibility: hidden; 3 | } 4 | 5 | oj-sample-markdown-viewer{ 6 | display:inline; 7 | } 8 | 9 | oj-sample-markdown-viewer .legacyStyling blockquote { 10 | border-left: 5px solid #DCE3E4; 11 | background:#fcfcfc; 12 | margin: 10px; 13 | padding: 10px; 14 | } 15 | 16 | oj-sample-markdown-viewer .legacyStyling blockquote p { 17 | display: inline; 18 | } 19 | 20 | oj-sample-markdown-viewer .legacyStyling pre { 21 | background:#fcfcfc; 22 | margin: 10px; 23 | padding: 10px; 24 | } 25 | 26 | oj-sample-markdown-viewer .legacyStyling table { 27 | border-collapse: collapse; 28 | text-align:left; 29 | padding-right: 0.5em; 30 | padding-left: 0.5em; 31 | border: 1px solid #8DA6B1; 32 | } 33 | 34 | oj-sample-markdown-viewer .legacyStyling th { 35 | text-align:left; 36 | padding-right: 0.5em; 37 | padding-left: 0.5em; 38 | border: 1px solid #8DA6B1; 39 | } 40 | 41 | oj-sample-markdown-viewer .legacyStyling td { 42 | text-align:left; 43 | padding-right: 0.5em; 44 | padding-left: 0.5em; 45 | border: 1px solid #8DA6B1; 46 | vertical-align: top; 47 | } 48 | 49 | oj-sample-markdown-viewer .redwoodStyling h1 { 50 | font-size: 1.75rem; 51 | font-weight: 700; 52 | line-height: 1.2857; 53 | } 54 | 55 | oj-sample-markdown-viewer .redwoodStyling h2 { 56 | font-size: 1.5rem; 57 | font-weight: 700; 58 | line-height: 1.3333; 59 | } 60 | 61 | oj-sample-markdown-viewer .redwoodStyling h3 { 62 | font-size: 1.25rem; 63 | font-weight: 700; 64 | line-height: 1.4; 65 | } 66 | 67 | oj-sample-markdown-viewer .redwoodStyling h4 { 68 | font-size: 1rem; 69 | font-weight: 700; 70 | line-height: 1.5; 71 | } 72 | 73 | oj-sample-markdown-viewer .redwoodStyling h5 { 74 | font-size: 0.9167rem; 75 | font-weight: 600; 76 | line-height: 1.4; 77 | } 78 | 79 | oj-sample-markdown-viewer .redwoodStyling h6 { 80 | font-size: 0.8333rem; 81 | font-weight: 600; 82 | line-height: 1.4; 83 | } 84 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/min/markdown-viewer/resources/nls/markdown-viewer-strings.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. */ 2 | /* 3 | * Standard String resources used within the component 4 | */ 5 | define({ 6 | "root": true 7 | }); 8 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/min/markdown-viewer/resources/nls/root/markdown-viewer-strings.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2017, 2019 Oracle and/or its affiliates. All rights reserved. */ 2 | /* 3 | * Standard String resources used within the component 4 | */ 5 | define({ 6 | "ojsampleMarkdownViewer": { 7 | "designTimeMarker": "Set value property for content", 8 | "@designTimeMarker": { "description": "Text displayed in the Visual Builder Design Time environment to help the developer to locate the component when it would be otherwise invisible" }, 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /app/src/components/oj-sample/types/markdown-viewer/loader.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace preact.JSX { 3 | interface IntrinsicElements { 4 | 'oj-sample-markdown-viewer': any; 5 | } 6 | } 7 | } 8 | 9 | export {}; 10 | -------------------------------------------------------------------------------- /app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 27 | 28 | 29 | Oracle JET Conversational UI 30 | 31 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | 53 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014, 2023, Oracle and/or its affiliates. 4 | * Licensed under The Universal Permissive License (UPL), Version 1.0 5 | * as shown at https://oss.oracle.com/licenses/upl/ 6 | * @ignore 7 | */ 8 | import "./components/app"; 9 | 10 | declare global { 11 | namespace preact.JSX { 12 | interface IntrinsicElements { 13 | "md-wrapper": any; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014, 2023, Oracle and/or its affiliates. 4 | * Licensed under The Universal Permissive License (UPL), Version 1.0 5 | * as shown at https://oss.oracle.com/licenses/upl/ 6 | * @ignore 7 | */ 8 | "use strict"; 9 | 10 | /** 11 | * Example of Require.js bootstrap javascript 12 | */ 13 | 14 | (function () { 15 | requirejs.config({ 16 | // injector:baseUrl 17 | baseUrl: ".", 18 | // endinjector 19 | paths: 20 | /* DO NOT MODIFY 21 | ** All paths are dynamically generated from the path_mappings.json file. 22 | ** Add any new library dependencies in path_mappings json file 23 | */ 24 | // injector:mainReleasePaths 25 | { 26 | ojs: "libs/oj/15.1.0/debug", 27 | ojL10n: "libs/oj/15.1.0/ojL10n", 28 | ojtranslations: "libs/oj/15.1.0/resources", 29 | knockout: "libs/knockout/knockout-3.5.1.debug", 30 | jquery: "libs/jquery/jquery-3.6.4", 31 | "jqueryui-amd": "libs/jquery/jqueryui-amd-1.13.2", 32 | text: "libs/require/text", 33 | hammerjs: "libs/hammer/hammer-2.0.8", 34 | signals: "libs/js-signals/signals", 35 | ojdnd: "libs/dnd-polyfill/dnd-polyfill-1.0.2", 36 | css: "libs/require-css/css.min", 37 | "css-builder": "libs/require-css/css-builder", 38 | normalize: "libs/require-css/normalize", 39 | "@oracle/oraclejet-preact": "libs/oraclejet-preact/amd", 40 | preact: "libs/preact/dist/preact.umd", 41 | "preact/hooks": "libs/preact/hooks/dist/hooks.umd", 42 | "preact/compat": "libs/preact/compat/dist/compat.umd", 43 | "preact/jsx-runtime": "libs/preact/jsx-runtime/dist/jsxRuntime.umd", 44 | proj4: "libs/proj4js/dist/proj4-src", 45 | touchr: "libs/touchr/touchr", 46 | chai: "libs/chai/chai-4.3.7", 47 | }, 48 | // endinjector 49 | }); 50 | })(); 51 | 52 | /** 53 | * Load the application's entry point file 54 | */ 55 | require(["./index"]); 56 | -------------------------------------------------------------------------------- /app/src/styles/fonts/App_iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/fonts/App_iconfont.woff -------------------------------------------------------------------------------- /app/src/styles/images/JET-Favicon-Red-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/JET-Favicon-Red-32x32.png -------------------------------------------------------------------------------- /app/src/styles/images/ROC_bg_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/ROC_bg_texture.png -------------------------------------------------------------------------------- /app/src/styles/images/ai.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/styles/images/carousel-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/carousel-bg.png -------------------------------------------------------------------------------- /app/src/styles/images/oracle_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/styles/images/placeholder-female-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/placeholder-female-01.png -------------------------------------------------------------------------------- /app/src/styles/images/placeholder-female-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/placeholder-female-02.png -------------------------------------------------------------------------------- /app/src/styles/images/placeholder-male-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/placeholder-male-01.png -------------------------------------------------------------------------------- /app/src/styles/images/placeholder-male-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/placeholder-male-05.png -------------------------------------------------------------------------------- /app/src/styles/images/question2-black32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/question2-black32x32.png -------------------------------------------------------------------------------- /app/src/styles/images/question2-white32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/question2-white32x32.png -------------------------------------------------------------------------------- /app/src/styles/images/question2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/question2.png -------------------------------------------------------------------------------- /app/src/styles/images/welcomeBanner-foreground2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/app/src/styles/images/welcomeBanner-foreground2.png -------------------------------------------------------------------------------- /app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "target": "es6", 6 | "strict": true, 7 | "module": "amd", 8 | "moduleResolution": "node", 9 | "jsx": "react-jsx", 10 | "jsxImportSource": "preact", 11 | "lib": ["es2021", "dom", "esnext.asynciterable"], 12 | "typeRoots": [ 13 | "./node_modules/@oracle/oraclejet/dist/types", 14 | "./node_modules/@types" 15 | ], 16 | "paths": { 17 | "ojs/*": ["./node_modules/@oracle/oraclejet/dist/types/*"], 18 | "@oracle/oraclejet-preact/*": [ 19 | "./node_modules/@oracle/oraclejet-preact/*" 20 | ], 21 | "oj-c/*": ["./node_modules/@oracle/oraclejet-core-pack/oj-c/types/*"], 22 | "preact": ["./node_modules/preact"], 23 | "marked/*": ["./node_modules/marked/*"], 24 | "oj-sample/*": ["./src/components/oj-sample/types/*"], 25 | "md-wrapper": ["./src/components/md-wrapper/types/index"], 26 | "stompjs": ["./node_modules/@stomp/stompjs/index"] 27 | }, 28 | "declaration": true, 29 | "noEmitOnError": true, 30 | "experimentalDecorators": true, 31 | "skipLibCheck": true, 32 | "removeComments": true 33 | }, 34 | "include": ["./src/**/*"] 35 | } 36 | -------------------------------------------------------------------------------- /backend/.dockerignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/backend/.dockerignore -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 container-registry.oracle.com/java/jdk-no-fee-term:21-oraclelinux8 as build 2 | 3 | RUN mkdir /opt/src 4 | COPY . /opt/src 5 | WORKDIR /opt/src 6 | RUN ./gradlew clean bootJar 7 | 8 | FROM --platform=linux/amd64 container-registry.oracle.com/java/jdk-no-fee-term:21-oraclelinux8 9 | 10 | VOLUME /tmp 11 | COPY --from=build /opt/src/build/libs/*.jar /tmp/ 12 | RUN mkdir /app 13 | RUN mv /tmp/backend*.jar /app/backend.jar 14 | 15 | ENTRYPOINT ["java","-jar","/app/backend.jar"] -------------------------------------------------------------------------------- /backend/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '3.2.2' 4 | id 'io.spring.dependency-management' version '1.1.4' 5 | } 6 | 7 | group = 'dev.victormartin.oci.genai.backend' 8 | version = '0.0.6' 9 | 10 | java { 11 | sourceCompatibility = '17' 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | } 17 | 18 | dependencies { 19 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 20 | implementation 'org.springframework.boot:spring-boot-starter-data-rest' 21 | implementation 'org.springframework.boot:spring-boot-starter-websocket' 22 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 23 | implementation 'org.netbeans.external:org-apache-commons-io:RELEASE113' 24 | implementation 'com.oracle.oci.sdk:oci-java-sdk-shaded-full:3.52.1' 25 | implementation 'com.oracle.oci.sdk:oci-java-sdk-core:3.52.1' 26 | implementation 'com.oracle.oci.sdk:oci-java-sdk-common:3.52.1' 27 | implementation 'com.oracle.oci.sdk:oci-java-sdk-addons-oke-workload-identity:3.52.1' 28 | implementation 'com.oracle.oci.sdk:oci-java-sdk-generativeai:3.52.1' 29 | implementation 'com.oracle.database.jdbc:ojdbc11-production:21.8.0.0' 30 | implementation 'com.oracle.database.jdbc:ucp:21.8.0.0' 31 | implementation 'com.oracle.database.security:oraclepki:21.8.0.0' 32 | implementation 'com.oracle.database.security:osdt_cert:21.8.0.0' 33 | implementation 'com.oracle.database.security:osdt_core:21.8.0.0' 34 | implementation 'org.apache.pdfbox:pdfbox:3.0.3' exclude(group: 'commons-logging', module: 'commons-logging') 35 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 36 | } 37 | 38 | tasks.named('test') { 39 | useJUnitPlatform() 40 | } 41 | -------------------------------------------------------------------------------- /backend/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/backend/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /backend/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /backend/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /backend/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'backend' 2 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/BackendApplication.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class BackendApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(BackendApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/InvalidPromptRequest.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Invalid request params") 7 | public class InvalidPromptRequest extends RuntimeException { 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/config/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.messaging.simp.config.MessageBrokerRegistry; 5 | import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; 6 | import org.springframework.web.socket.config.annotation.StompEndpointRegistry; 7 | import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; 8 | 9 | @Configuration 10 | @EnableWebSocketMessageBroker 11 | public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { 12 | 13 | @Override 14 | public void configureMessageBroker(MessageBrokerRegistry config) { 15 | config.enableSimpleBroker("/queue"); 16 | config.setApplicationDestinationPrefixes("/genai"); 17 | config.setUserDestinationPrefix("/user"); 18 | } 19 | 20 | @Override 21 | public void registerStompEndpoints(StompEndpointRegistry registry) { 22 | registry.addEndpoint("/websocket").setAllowedOrigins("*"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/controller/GenAIController.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.controller; 2 | 3 | import com.oracle.bmc.generativeai.GenerativeAiClient; 4 | import com.oracle.bmc.generativeai.model.ModelCapability; 5 | import com.oracle.bmc.generativeai.requests.ListModelsRequest; 6 | import com.oracle.bmc.generativeai.requests.ListEndpointsRequest; 7 | import com.oracle.bmc.generativeai.responses.ListModelsResponse; 8 | import com.oracle.bmc.generativeai.responses.ListEndpointsResponse; 9 | import dev.victormartin.oci.genai.backend.backend.dao.GenAiModel; 10 | import dev.victormartin.oci.genai.backend.backend.dao.GenAiEndpoint; 11 | import dev.victormartin.oci.genai.backend.backend.service.GenAIModelsService; 12 | import dev.victormartin.oci.genai.backend.backend.service.GenAiClientService; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.web.bind.annotation.GetMapping; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | @RestController 24 | public class GenAIController { 25 | Logger logger = LoggerFactory.getLogger(GenAIController.class); 26 | 27 | @Value("${genai.compartment_id}") 28 | private String COMPARTMENT_ID; 29 | 30 | @Autowired 31 | private GenAiClientService generativeAiClientService; 32 | 33 | @Autowired 34 | private GenAIModelsService genAIModelsService; 35 | 36 | @GetMapping("/api/genai/models") 37 | public List getModels() { 38 | logger.info("getModels()"); 39 | List models = genAIModelsService.getModels(); 40 | return models.stream() 41 | .filter(m -> m.capabilities().contains("CHAT")) 42 | .collect(Collectors.toList()); 43 | } 44 | 45 | @GetMapping("/api/genai/endpoints") 46 | public List getEndpoints() { 47 | logger.info("getEndpoints()"); 48 | ListEndpointsRequest listEndpointsRequest = ListEndpointsRequest.builder().compartmentId(COMPARTMENT_ID) 49 | .build(); 50 | GenerativeAiClient client = generativeAiClientService.getClient(); 51 | ListEndpointsResponse response = client.listEndpoints(listEndpointsRequest); 52 | return response.getEndpointCollection().getItems().stream().map(e -> { 53 | GenAiEndpoint endpoint = new GenAiEndpoint(e.getId(), e.getDisplayName(), e.getLifecycleState(), 54 | e.getModelId(), e.getTimeCreated()); 55 | return endpoint; 56 | }).collect(Collectors.toList()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/controller/SummaryController.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.controller; 2 | 3 | import com.oracle.bmc.model.BmcException; 4 | import dev.victormartin.oci.genai.backend.backend.dao.Answer; 5 | import dev.victormartin.oci.genai.backend.backend.dao.SummaryRequest; 6 | import dev.victormartin.oci.genai.backend.backend.data.Interaction; 7 | import dev.victormartin.oci.genai.backend.backend.data.InteractionRepository; 8 | import dev.victormartin.oci.genai.backend.backend.data.InteractionType; 9 | import dev.victormartin.oci.genai.backend.backend.service.OCIGenAIService; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestHeader; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.util.HtmlUtils; 19 | 20 | import java.util.Date; 21 | 22 | @RestController 23 | public class SummaryController { 24 | Logger logger = LoggerFactory.getLogger(SummaryController.class); 25 | 26 | @Value("${genai.summarization_model_id}") 27 | String summarizationModelId; 28 | 29 | @Autowired 30 | OCIGenAIService ociGenAIService; 31 | 32 | @Autowired 33 | private InteractionRepository interactionRepository; 34 | 35 | @PostMapping("/api/genai/summary") 36 | public Answer postSummaryText(@RequestBody SummaryRequest summaryRequest, 37 | @RequestHeader("conversationID") String conversationId, 38 | @RequestHeader("modelId") String modelId) { 39 | logger.info("postSummaryText()"); 40 | String contentEscaped = HtmlUtils.htmlEscape(summaryRequest.content()); 41 | logger.info("contentEscaped: {}...", contentEscaped.substring(0, 50)); 42 | Interaction interaction = new Interaction(); 43 | interaction.setType(InteractionType.SUMMARY_TEXT); 44 | interaction.setConversationId(conversationId); 45 | interaction.setDatetimeRequest(new Date()); 46 | interaction.setModelId(summarizationModelId); 47 | interaction.setRequest(contentEscaped); 48 | Interaction saved = interactionRepository.save(interaction); 49 | try { 50 | String summaryText = ociGenAIService.summaryText(contentEscaped, summarizationModelId, false); 51 | saved.setDatetimeResponse(new Date()); 52 | saved.setResponse(summaryText); 53 | interactionRepository.save(saved); 54 | logger.info("summaryText: {}...", summaryText.substring(0, 50)); 55 | Answer answer = new Answer(); 56 | answer.setContent(summaryText); 57 | answer.setErrorMessage(""); 58 | return answer; 59 | } catch (BmcException e) { 60 | String unmodifiedMessage = e.getUnmodifiedMessage(); 61 | int statusCode = e.getStatusCode(); 62 | String errorMessage = statusCode + " " + unmodifiedMessage; 63 | logger.error(errorMessage); 64 | saved.setErrorMessage(errorMessage); 65 | interactionRepository.save(saved); 66 | Answer answer = new Answer("", errorMessage); 67 | return answer; 68 | } catch (Exception e) { 69 | String errorMessage = e.getLocalizedMessage(); 70 | logger.error(errorMessage); 71 | saved.setErrorMessage(errorMessage); 72 | interactionRepository.save(saved); 73 | Answer answer = new Answer("", errorMessage); 74 | return answer; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/dao/Answer.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.dao; 2 | 3 | public class Answer { 4 | 5 | private String content; 6 | 7 | private String errorMessage; 8 | 9 | public Answer() { 10 | } 11 | 12 | public Answer(String content, String errorMessage) { 13 | this.content = content; 14 | this.errorMessage = errorMessage; 15 | } 16 | public void setContent(String content) { 17 | this.content = content; 18 | } 19 | 20 | public String getContent() { 21 | return content; 22 | } 23 | 24 | public String getErrorMessage() { 25 | return errorMessage; 26 | } 27 | 28 | public void setErrorMessage(String errorMessage) { 29 | this.errorMessage = errorMessage; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/dao/GenAiEndpoint.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.dao; 2 | 3 | import java.util.Date; 4 | import com.oracle.bmc.generativeai.model.Endpoint; 5 | 6 | public record GenAiEndpoint(String id, String name, Endpoint.LifecycleState state, String model, Date timeCreated) { 7 | } 8 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/dao/GenAiModel.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.dao; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | public record GenAiModel(String id, String name, String vendor, String version, List capabilities, 7 | Date timeCreated) { 8 | } 9 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/dao/Prompt.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.dao; 2 | 3 | public record Prompt(String content, String conversationId, String modelId, boolean finetune) { 4 | }; -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/dao/SummaryRequest.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.dao; 2 | 3 | public record SummaryRequest(String content) { 4 | } 5 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/data/Interaction.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.data; 2 | 3 | import jakarta.persistence.*; 4 | 5 | import java.util.Date; 6 | import java.util.Objects; 7 | 8 | @Entity 9 | public class Interaction { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.AUTO) 12 | Long id; 13 | 14 | String conversationId; 15 | 16 | @Enumerated(EnumType.STRING) 17 | InteractionType type; 18 | 19 | @Temporal(TemporalType.DATE) 20 | Date datetimeRequest; 21 | 22 | String modelId; 23 | 24 | @Lob 25 | @Column 26 | String request; 27 | 28 | @Temporal(TemporalType.DATE) 29 | Date datetimeResponse; 30 | 31 | @Lob 32 | @Column 33 | String response; 34 | 35 | @Lob 36 | @Column 37 | String errorMessage; 38 | 39 | public Interaction() { 40 | } 41 | 42 | @Override 43 | public boolean equals(Object o) { 44 | if (this == o) return true; 45 | if (o == null || getClass() != o.getClass()) return false; 46 | Interaction that = (Interaction) o; 47 | return Objects.equals(id, that.id) && Objects.equals(conversationId, that.conversationId) && type == that.type && Objects.equals(datetimeRequest, that.datetimeRequest) && Objects.equals(modelId, that.modelId) && Objects.equals(request, that.request) && Objects.equals(datetimeResponse, that.datetimeResponse) && Objects.equals(response, that.response) && Objects.equals(errorMessage, that.errorMessage); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hash(id, conversationId, type, datetimeRequest, modelId, request, datetimeResponse, response, errorMessage); 53 | } 54 | 55 | public Long getId() { 56 | return id; 57 | } 58 | 59 | public String getConversationId() { 60 | return conversationId; 61 | } 62 | 63 | public void setConversationId(String conversationId) { 64 | this.conversationId = conversationId; 65 | } 66 | 67 | public InteractionType getType() { 68 | return type; 69 | } 70 | 71 | public void setType(InteractionType type) { 72 | this.type = type; 73 | } 74 | 75 | public Date getDatetimeRequest() { 76 | return datetimeRequest; 77 | } 78 | 79 | public void setDatetimeRequest(Date datetimeRequest) { 80 | this.datetimeRequest = datetimeRequest; 81 | } 82 | 83 | public String getModelId() { 84 | return modelId; 85 | } 86 | 87 | public void setModelId(String modelId) { 88 | this.modelId = modelId; 89 | } 90 | 91 | public String getRequest() { 92 | return request; 93 | } 94 | 95 | public void setRequest(String request) { 96 | this.request = request; 97 | } 98 | 99 | public Date getDatetimeResponse() { 100 | return datetimeResponse; 101 | } 102 | 103 | public void setDatetimeResponse(Date datetimeResponse) { 104 | this.datetimeResponse = datetimeResponse; 105 | } 106 | 107 | public String getResponse() { 108 | return response; 109 | } 110 | 111 | public void setResponse(String response) { 112 | this.response = response; 113 | } 114 | 115 | public String getErrorMessage() { 116 | return errorMessage; 117 | } 118 | 119 | public void setErrorMessage(String errorMessage) { 120 | this.errorMessage = errorMessage; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/data/InteractionRepository.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.data; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface InteractionRepository extends JpaRepository { 6 | } 7 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/data/InteractionType.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.data; 2 | 3 | public enum InteractionType { 4 | CHAT, SUMMARY_FILE, SUMMARY_TEXT 5 | } 6 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/service/GenAIModelsService.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.service; 2 | 3 | import com.oracle.bmc.generativeai.GenerativeAiClient; 4 | import com.oracle.bmc.generativeai.model.ModelCapability; 5 | import com.oracle.bmc.generativeai.requests.ListModelsRequest; 6 | import com.oracle.bmc.generativeai.responses.ListModelsResponse; 7 | import dev.victormartin.oci.genai.backend.backend.dao.GenAiModel; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.List; 15 | import java.util.stream.Collectors; 16 | 17 | @Service 18 | public class GenAIModelsService { 19 | Logger log = LoggerFactory.getLogger(GenAIModelsService.class); 20 | 21 | @Value("${genai.compartment_id}") 22 | private String COMPARTMENT_ID; 23 | 24 | @Autowired 25 | private GenAiClientService generativeAiClientService; 26 | 27 | public List getModels() { 28 | log.info("getModels()"); 29 | ListModelsRequest listModelsRequest = ListModelsRequest.builder() 30 | .compartmentId(COMPARTMENT_ID) 31 | .build(); 32 | GenerativeAiClient client = generativeAiClientService.getClient(); 33 | ListModelsResponse response = client.listModels(listModelsRequest); 34 | return response.getModelCollection().getItems().stream() 35 | .map(m -> { 36 | List capabilities = m.getCapabilities().stream() 37 | .map(ModelCapability::getValue).collect(Collectors.toList()); 38 | GenAiModel model = new GenAiModel( 39 | m.getId(), m.getDisplayName(), m.getVendor(), 40 | m.getVersion(), capabilities, m.getTimeCreated()); 41 | return model; 42 | }).collect(Collectors.toList()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/service/GenAiClientService.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.service; 2 | 3 | import com.oracle.bmc.ConfigFileReader; 4 | import com.oracle.bmc.Region; 5 | import com.oracle.bmc.auth.AuthenticationDetailsProvider; 6 | import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; 7 | import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider; 8 | import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider; 9 | import com.oracle.bmc.generativeai.GenerativeAiClient; 10 | import jakarta.annotation.PostConstruct; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.core.env.Environment; 16 | import org.springframework.stereotype.Service; 17 | 18 | import java.io.IOException; 19 | 20 | @Service 21 | public class GenAiClientService { 22 | 23 | Logger log = LoggerFactory.getLogger(GenAiClientService.class); 24 | 25 | @Autowired 26 | private Environment environment; 27 | 28 | private GenerativeAiClient client; 29 | 30 | @Value("${genai.region}") 31 | private String regionCode; 32 | @Value("${genai.config.location}") 33 | private String CONFIG_LOCATION; 34 | @Value("${genai.config.profile}") 35 | private String CONFIG_PROFILE; 36 | 37 | @PostConstruct 38 | private void postConstruct() { 39 | String[] activeProfiles = environment.getActiveProfiles(); 40 | String profile = activeProfiles[0]; 41 | switch (profile) { 42 | case "oke": 43 | okeGenAiClient(); 44 | break; 45 | case "compute": 46 | instancePrincipalClient(); 47 | break; 48 | default: 49 | localClient(); 50 | break; 51 | } 52 | } 53 | 54 | private void okeGenAiClient() { 55 | final OkeWorkloadIdentityAuthenticationDetailsProvider provider = new OkeWorkloadIdentityAuthenticationDetailsProvider 56 | .OkeWorkloadIdentityAuthenticationDetailsProviderBuilder() 57 | .build(); 58 | GenerativeAiClient okeClient = GenerativeAiClient.builder() 59 | .region(Region.fromRegionCode(regionCode)) 60 | .build(provider); 61 | setClient(okeClient); 62 | } 63 | 64 | 65 | private void instancePrincipalClient() { 66 | final InstancePrincipalsAuthenticationDetailsProvider provider = new InstancePrincipalsAuthenticationDetailsProvider 67 | .InstancePrincipalsAuthenticationDetailsProviderBuilder() 68 | .build(); 69 | 70 | GenerativeAiClient instancePrinciplaClient = GenerativeAiClient.builder() 71 | .region(Region.fromRegionCode(regionCode)) 72 | .build(provider); 73 | setClient(instancePrinciplaClient); 74 | } 75 | 76 | private void localClient() { 77 | final ConfigFileReader.ConfigFile configFile; 78 | try { 79 | configFile = ConfigFileReader.parse(CONFIG_LOCATION, CONFIG_PROFILE); 80 | } catch (IOException e) { 81 | log.error("Failed to load config file at {}", CONFIG_LOCATION); 82 | log.error(e.getMessage()); 83 | throw new RuntimeException(e); 84 | } 85 | final AuthenticationDetailsProvider provider = new ConfigFileAuthenticationDetailsProvider(configFile); 86 | 87 | GenerativeAiClient localClient = GenerativeAiClient.builder() 88 | .region(Region.fromRegionCode(regionCode)) 89 | .build(provider); 90 | setClient(localClient); 91 | } 92 | 93 | public GenerativeAiClient getClient() { 94 | return client; 95 | } 96 | 97 | public void setClient(GenerativeAiClient client) { 98 | this.client = client; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/service/GenAiInferenceClientService.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.service; 2 | 3 | import com.oracle.bmc.ConfigFileReader; 4 | import com.oracle.bmc.Region; 5 | import com.oracle.bmc.auth.AuthenticationDetailsProvider; 6 | import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; 7 | import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider; 8 | import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider; 9 | import com.oracle.bmc.generativeaiinference.GenerativeAiInferenceClient; 10 | import jakarta.annotation.PostConstruct; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.core.env.Environment; 16 | import org.springframework.stereotype.Service; 17 | 18 | import java.io.IOException; 19 | 20 | @Service 21 | public class GenAiInferenceClientService { 22 | 23 | Logger log = LoggerFactory.getLogger(GenAiInferenceClientService.class); 24 | 25 | private GenerativeAiInferenceClient client; 26 | 27 | @Autowired 28 | private Environment environment; 29 | 30 | @Value("${genai.region}") 31 | private String regionCode; 32 | @Value("${genai.config.location}") 33 | private String CONFIG_LOCATION; 34 | @Value("${genai.config.profile}") 35 | private String CONFIG_PROFILE; 36 | 37 | @PostConstruct 38 | private void postConstruct() { 39 | String[] activeProfiles = environment.getActiveProfiles(); 40 | String profile = activeProfiles[0]; 41 | log.info("Profile: {}", profile); 42 | switch (profile) { 43 | case "oke": 44 | okeGenAiClient(); 45 | break; 46 | case "compute": 47 | instancePrincipalClient(); 48 | break; 49 | default: 50 | localConfig(); 51 | break; 52 | } 53 | } 54 | 55 | private void okeGenAiClient() { 56 | final OkeWorkloadIdentityAuthenticationDetailsProvider provider = new OkeWorkloadIdentityAuthenticationDetailsProvider 57 | .OkeWorkloadIdentityAuthenticationDetailsProviderBuilder() 58 | .build(); 59 | GenerativeAiInferenceClient okeClient = GenerativeAiInferenceClient.builder() 60 | .region(Region.fromRegionCode(regionCode)) 61 | .build(provider); 62 | setClient(okeClient); 63 | } 64 | 65 | private void instancePrincipalClient() { 66 | final InstancePrincipalsAuthenticationDetailsProvider provider = new InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder() 67 | .build(); 68 | 69 | GenerativeAiInferenceClient okeClient = GenerativeAiInferenceClient.builder() 70 | .region(Region.fromRegionCode(regionCode)) 71 | .build(provider); 72 | setClient(okeClient); 73 | } 74 | 75 | private void localConfig() { 76 | final ConfigFileReader.ConfigFile configFile; 77 | try { 78 | configFile = ConfigFileReader.parse(CONFIG_LOCATION, CONFIG_PROFILE); 79 | } catch (IOException e) { 80 | throw new RuntimeException(e); 81 | } 82 | final AuthenticationDetailsProvider provider = new ConfigFileAuthenticationDetailsProvider(configFile); 83 | 84 | GenerativeAiInferenceClient okeClient = GenerativeAiInferenceClient.builder() 85 | .region(Region.fromRegionCode(regionCode)) 86 | .build(provider); 87 | setClient(okeClient); 88 | } 89 | 90 | public GenerativeAiInferenceClient getClient() { 91 | return client; 92 | } 93 | 94 | public void setClient(GenerativeAiInferenceClient client) { 95 | this.client = client; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /backend/src/main/java/dev/victormartin/oci/genai/backend/backend/service/PDFConvertorService.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend.service; 2 | 3 | import org.apache.pdfbox.Loader; 4 | import org.apache.pdfbox.pdmodel.PDDocument; 5 | import org.apache.pdfbox.text.PDFTextStripper; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.util.ResourceUtils; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | @Service 15 | public class PDFConvertorService { 16 | Logger log = LoggerFactory.getLogger(PDFConvertorService.class); 17 | 18 | public String convert(String filePath) { 19 | try { 20 | File file = ResourceUtils.getFile(filePath); 21 | PDDocument doc = Loader.loadPDF(file); 22 | return new PDFTextStripper().getText(doc); 23 | } catch (IOException e) { 24 | log.error(e.getMessage()); 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /backend/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | main: 3 | banner-mode: "off" 4 | profiles: 5 | active: default 6 | datasource: 7 | driver-class-name: oracle.jdbc.OracleDriver 8 | url: jdbc:oracle:thin:@DB_SERVICE_high?TNS_ADMIN=/PATH/TO/WALLET 9 | username: ADMIN 10 | password: "PASSWORD" 11 | type: oracle.ucp.jdbc.PoolDataSource 12 | oracleucp: 13 | sql-for-validate-connection: SELECT * FROM dual 14 | connection-pool-name: connectionPoolName1 15 | initial-pool-size: 5 16 | min-pool-size: 5 17 | max-pool-size: 10 18 | jpa: 19 | hibernate: 20 | use-new-id-generator-mappings: false 21 | ddl-auto: update 22 | oracle: 23 | jdbc: 24 | fanEnabled: true 25 | 26 | genai: 27 | region: "US_CHICAGO_1" 28 | config: 29 | location: "~/.oci/config" 30 | profile: "DEFAULT" 31 | compartment_id: "COMPARTMENT_ID" 32 | chat_model_id: "ocid1.generativeaimodel.AAAAAAAAAAAAA" 33 | summarization_model_id: "ocid1.generativeaimodel.AAAAAAAAAAAAA" 34 | storage: 35 | path: "/PATH/FOR/UPLOAD/FILES" 36 | 37 | -------------------------------------------------------------------------------- /backend/src/test/java/dev/victormartin/oci/genai/backend/backend/BackendApplicationTests.java: -------------------------------------------------------------------------------- 1 | package dev.victormartin.oci.genai.backend.backend; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class BackendApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /deploy/k8s/app/app-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: app 6 | name: app 7 | spec: 8 | ports: 9 | - port: 80 10 | protocol: TCP 11 | targetPort: 80 12 | selector: 13 | app: app -------------------------------------------------------------------------------- /deploy/k8s/app/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: app 6 | name: app 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: app 12 | strategy: 13 | type: RollingUpdate 14 | rollingUpdate: 15 | maxSurge: 1 16 | maxUnavailable: 1 17 | template: 18 | metadata: 19 | labels: 20 | app: app 21 | spec: 22 | containers: 23 | - image: app 24 | imagePullPolicy: "Always" 25 | name: app 26 | readinessProbe: 27 | httpGet: 28 | scheme: HTTP 29 | path: /index.html 30 | port: 80 31 | initialDelaySeconds: 10 32 | periodSeconds: 5 33 | ports: 34 | - containerPort: 80 35 | resources: 36 | requests: 37 | cpu: 100m 38 | memory: 256Mi 39 | limits: 40 | cpu: 250m 41 | memory: 521Mi 42 | imagePullSecrets: 43 | - name: ocir-secret -------------------------------------------------------------------------------- /deploy/k8s/app/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - app.yaml 3 | - app-svc.yaml 4 | namespace: backend -------------------------------------------------------------------------------- /deploy/k8s/backend/application.yaml.mustache: -------------------------------------------------------------------------------- 1 | spring: 2 | main: 3 | banner-mode: "off" 4 | profiles: 5 | active: oke 6 | datasource: 7 | driver-class-name: oracle.jdbc.OracleDriver 8 | url: jdbc:oracle:thin:@{{{db_service}}}_high?TNS_ADMIN={{{path_to_wallet}}} 9 | username: ADMIN 10 | password: "{{{db_password}}}" 11 | type: oracle.ucp.jdbc.PoolDataSource 12 | oracleucp: 13 | sql-for-validate-connection: SELECT * FROM dual 14 | connection-pool-name: connectionPoolName1 15 | initial-pool-size: 5 16 | min-pool-size: 5 17 | max-pool-size: 10 18 | jpa: 19 | hibernate: 20 | use-new-id-generator-mappings: false 21 | ddl-auto: update 22 | oracle: 23 | jdbc: 24 | fanEnabled: true 25 | 26 | genai: 27 | region: "{{{region_name}}}" 28 | compartment_id: "{{{compartment_ocid}}}" 29 | chat_model_id: "{{{genai_model_chat_ocid}}}" 30 | summarization_model_id: "{{{genai_model_summarization_ocid}}}" 31 | storage: 32 | path: "/temp" 33 | -------------------------------------------------------------------------------- /deploy/k8s/backend/backend-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: backend 6 | name: backend 7 | spec: 8 | ports: 9 | - port: 8080 10 | protocol: TCP 11 | targetPort: 8080 12 | selector: 13 | app: backend -------------------------------------------------------------------------------- /deploy/k8s/backend/backend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: backend 6 | name: backend 7 | spec: 8 | replicas: 1 9 | minReadySeconds: 60 10 | selector: 11 | matchLabels: 12 | app: backend 13 | strategy: 14 | type: RollingUpdate 15 | rollingUpdate: 16 | maxSurge: 1 17 | maxUnavailable: 1 18 | template: 19 | metadata: 20 | labels: 21 | app: backend 22 | spec: 23 | serviceAccountName: oci-service-account 24 | automountServiceAccountToken: true 25 | initContainers: 26 | - name: unzip 27 | image: busybox 28 | command: ["unzip", "/walletzip/wallet.zip", "-d", "/wallet"] 29 | volumeMounts: 30 | - name: wallet-config 31 | mountPath: /walletzip 32 | - name: wallet-volume 33 | mountPath: /wallet 34 | containers: 35 | - image: backend 36 | imagePullPolicy: "Always" 37 | name: backend 38 | livenessProbe: 39 | httpGet: 40 | path: /actuator/health/liveness 41 | port: 8080 42 | initialDelaySeconds: 90 43 | periodSeconds: 5 44 | readinessProbe: 45 | httpGet: 46 | path: /actuator/health/readiness 47 | port: 8080 48 | initialDelaySeconds: 90 49 | periodSeconds: 5 50 | ports: 51 | - containerPort: 8080 52 | resources: 53 | requests: 54 | cpu: 250m 55 | memory: 512Mi 56 | limits: 57 | cpu: 500m 58 | memory: 1024Mi 59 | volumeMounts: 60 | - name: config-volume 61 | mountPath: /config 62 | - name: wallet-volume 63 | mountPath: /wallet 64 | - name: temp 65 | mountPath: /temp 66 | volumes: 67 | - name: config-volume 68 | configMap: 69 | name: backend-properties 70 | - name: wallet-config 71 | configMap: 72 | name: wallet-zip 73 | - name: wallet-volume 74 | emptyDir: 75 | sizeLimit: 50Mi 76 | - name: temp 77 | emptyDir: 78 | sizeLimit: 500Mi 79 | imagePullSecrets: 80 | - name: ocir-secret -------------------------------------------------------------------------------- /deploy/k8s/backend/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - service-account.yaml 3 | - backend.yaml 4 | - backend-svc.yaml 5 | configMapGenerator: 6 | - name: backend-properties 7 | files: 8 | - application.yaml 9 | - name: wallet-zip 10 | files: 11 | - wallet/wallet.zip 12 | namespace: backend -------------------------------------------------------------------------------- /deploy/k8s/backend/service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: oci-service-account 5 | -------------------------------------------------------------------------------- /deploy/k8s/ingress/ingress-ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | app.kubernetes.io/instance: ingress-nginx 6 | app.kubernetes.io/name: ingress-nginx 7 | name: ingress-nginx -------------------------------------------------------------------------------- /deploy/k8s/ingress/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress 5 | spec: 6 | ingressClassName: nginx 7 | tls: 8 | - secretName: tls 9 | rules: 10 | - http: 11 | paths: 12 | - path: /api 13 | pathType: Prefix 14 | backend: 15 | service: 16 | name: backend 17 | port: 18 | number: 8080 19 | - path: /websocket 20 | pathType: Prefix 21 | backend: 22 | service: 23 | name: backend 24 | port: 25 | number: 8080 26 | # - path: /summary 27 | # pathType: Prefix 28 | # backend: 29 | # service: 30 | # name: web 31 | # port: 32 | # number: 80 33 | - path: / 34 | pathType: Prefix 35 | backend: 36 | service: 37 | name: app 38 | port: 39 | number: 80 -------------------------------------------------------------------------------- /deploy/k8s/ingress/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ingress-ns.yaml 3 | - ingress-rbac.yaml 4 | - ingress-controller.yaml 5 | - ingress.yaml 6 | secretGenerator: 7 | - name: tls 8 | files: 9 | - .certs/tls.crt 10 | - .certs/tls.key 11 | type: "kubernetes.io/tls" 12 | namespace: backend -------------------------------------------------------------------------------- /deploy/k8s/overlays/prod/kustomization.yaml.mustache: -------------------------------------------------------------------------------- 1 | resources: 2 | - "../../ingress" 3 | - "../../app" 4 | - "../../backend" 5 | 6 | images: 7 | - name: app 8 | newName: {{region_key}}.ocir.io/{{{tenancy_namespace}}}/{{{project_name}}}/app 9 | newTag: {{{app_version}}} 10 | - name: backend 11 | newName: {{region_key}}.ocir.io/{{{tenancy_namespace}}}/{{{project_name}}}/backend 12 | newTag: {{{backend_version}}} -------------------------------------------------------------------------------- /deploy/k8s/web/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - web.yaml 3 | - web-svc.yaml 4 | namespace: backend -------------------------------------------------------------------------------- /deploy/k8s/web/web-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: web 6 | name: web 7 | spec: 8 | ports: 9 | - port: 80 10 | protocol: TCP 11 | targetPort: 80 12 | selector: 13 | app: web -------------------------------------------------------------------------------- /deploy/k8s/web/web.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: web 6 | name: web 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: web 12 | strategy: 13 | type: RollingUpdate 14 | rollingUpdate: 15 | maxSurge: 1 16 | maxUnavailable: 1 17 | template: 18 | metadata: 19 | labels: 20 | app: web 21 | spec: 22 | containers: 23 | - image: web 24 | name: web 25 | readinessProbe: 26 | httpGet: 27 | scheme: HTTP 28 | path: /index.html 29 | port: 80 30 | initialDelaySeconds: 10 31 | periodSeconds: 5 32 | ports: 33 | - containerPort: 80 34 | resources: 35 | requests: 36 | cpu: 100m 37 | memory: 256Mi 38 | limits: 39 | cpu: 250m 40 | memory: 521Mi 41 | imagePullSecrets: 42 | - name: ocir-secret -------------------------------------------------------------------------------- /deploy/terraform/adb.tf: -------------------------------------------------------------------------------- 1 | variable "autonomous_database_db_workload" { 2 | type = string 3 | default = "OLTP" 4 | } 5 | 6 | variable "autonomous_database_db_license" { 7 | type = string 8 | default = "BRING_YOUR_OWN_LICENSE" 9 | } 10 | 11 | variable "autonomous_database_db_whitelisted_ips" { 12 | type = list(string) 13 | default = ["0.0.0.0/0"] # Don't do this in prod 14 | } 15 | 16 | variable "autonomous_database_cpu_core_count" { 17 | type = number 18 | default = 1 19 | } 20 | 21 | variable "autonomous_database_data_storage_size_in_tbs" { 22 | type = number 23 | default = 1 24 | } 25 | 26 | resource "random_password" "adb_admin_password" { 27 | length = 16 28 | special = true 29 | min_numeric = 3 30 | min_special = 3 31 | min_lower = 3 32 | min_upper = 3 33 | override_special = "()-_[]{}?" 34 | } 35 | 36 | resource "oci_database_autonomous_database" "adb" { 37 | compartment_id = var.compartment_ocid 38 | db_name = "${local.project_name}${local.deploy_id}" 39 | 40 | #Optional 41 | admin_password = random_password.adb_admin_password.result 42 | cpu_core_count = var.autonomous_database_cpu_core_count 43 | data_storage_size_in_tbs = var.autonomous_database_data_storage_size_in_tbs 44 | db_workload = var.autonomous_database_db_workload 45 | display_name = "${local.project_name}${local.deploy_id}" 46 | is_mtls_connection_required = true 47 | whitelisted_ips = var.autonomous_database_db_whitelisted_ips 48 | is_auto_scaling_enabled = true 49 | license_model = var.autonomous_database_db_license 50 | } 51 | 52 | # For mTLS and Wallet connectivity consider the following code 53 | 54 | resource "oci_database_autonomous_database_wallet" "adb_wallet" { 55 | autonomous_database_id = oci_database_autonomous_database.adb.id 56 | password = random_password.adb_admin_password.result 57 | base64_encode_content = "true" 58 | } 59 | 60 | resource "local_file" "adb_wallet_file" { 61 | content_base64 = oci_database_autonomous_database_wallet.adb_wallet.content 62 | filename = "${path.module}/generated/wallet.zip" 63 | } -------------------------------------------------------------------------------- /deploy/terraform/data.tf: -------------------------------------------------------------------------------- 1 | data "oci_identity_tenancy" "tenant_details" { 2 | tenancy_id = var.tenancy_ocid 3 | 4 | provider = oci 5 | } 6 | 7 | data "oci_identity_region_subscriptions" "region_subscriptions" { 8 | tenancy_id = var.tenancy_ocid 9 | 10 | provider = oci 11 | } 12 | 13 | data "oci_identity_compartment" "compartment" { 14 | id = var.compartment_ocid 15 | } 16 | 17 | data "oci_identity_availability_domains" "ads" { 18 | compartment_id = var.tenancy_ocid 19 | } 20 | 21 | data "oci_objectstorage_namespace" "objectstorage_namespace" { 22 | compartment_id = var.tenancy_ocid 23 | } 24 | 25 | 26 | data "oci_containerengine_cluster_option" "oke" { 27 | cluster_option_id = "all" 28 | } 29 | -------------------------------------------------------------------------------- /deploy/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | oke_policy_name = "${local.project_name}_${local.deploy_id}_oke" 3 | ocir_group_name = "${local.project_name}-${local.deploy_id}-group" 4 | } 5 | 6 | # TODO restrict permissions, all gen ai family is too much 7 | # but it doesn't seems to work in other than tenancy level 8 | resource "oci_identity_policy" "allow-oke-genai-policy" { 9 | provider = oci.home 10 | compartment_id = var.tenancy_ocid 11 | name = "${local.oke_policy_name}" 12 | description = "Allow OKE workload to manage gen ai service for ${local.project_name} ${local.deploy_id}" 13 | statements = [ 14 | "Allow any-user to manage generative-ai-family in tenancy where all { request.principal.type = 'workload', request.principal.namespace = 'backend', request.principal.service_account = 'oci-service-account', request.principal.cluster_id = '${module.oke.cluster_id}'}" 15 | # "Allow any-user to manage generative-ai-chat in tenancy where all { request.principal.type = 'workload', request.principal.namespace = 'backend', request.principal.service_account = 'oci-service-account', request.principal.cluster_id = '${module.oke.cluster_id}'}", 16 | # "Allow any-user to manage generative-ai-text-generation in tenancy where all { request.principal.type = 'workload', request.principal.namespace = 'backend', request.principal.service_account = 'oci-service-account', request.principal.cluster_id = '${module.oke.cluster_id}'}", 17 | # "Allow any-user to manage generative-ai-text-summarization in tenancy where all { request.principal.type = 'workload', request.principal.namespace = 'backend', request.principal.service_account = 'oci-service-account', request.principal.cluster_id = '${module.oke.cluster_id}'}", 18 | # "Allow any-user to manage generative-ai-text-embedding in tenancy where all { request.principal.type = 'workload', request.principal.namespace = 'backend', request.principal.service_account = 'oci-service-account', request.principal.cluster_id = '${module.oke.cluster_id}'}", 19 | # "Allow any-user to manage generative-ai-work-request in tenancy where all { request.principal.type = 'workload', request.principal.namespace = 'backend', request.principal.service_account = 'oci-service-account', request.principal.cluster_id = '${module.oke.cluster_id}'}", 20 | # "Allow any-user to manage generative-ai-model in tenancy where all { request.principal.type = 'workload', request.principal.namespace = 'backend', request.principal.service_account = 'oci-service-account', request.principal.cluster_id = '${module.oke.cluster_id}'}" 21 | ] 22 | } 23 | 24 | resource "oci_identity_group" "ocir_group" { 25 | provider = oci.home 26 | compartment_id = var.tenancy_ocid 27 | description = "Group for Oracle Container Image Registry users for ${local.project_name}${local.deploy_id}" 28 | name = local.ocir_group_name 29 | } 30 | 31 | resource "oci_identity_user" "ocir_user" { 32 | provider = oci.home 33 | compartment_id = var.tenancy_ocid 34 | description = "User for pushing images to OCIR" 35 | name = "${local.project_name}-${local.deploy_id}-ocir-user" 36 | 37 | email = "${local.project_name}-${local.deploy_id}-ocir-user@example.com" 38 | } 39 | 40 | resource "oci_identity_user_group_membership" "ocir_user_group_membership" { 41 | provider = oci.home 42 | group_id = oci_identity_group.ocir_group.id 43 | user_id = oci_identity_user.ocir_user.id 44 | } 45 | 46 | resource "oci_identity_auth_token" "ocir_user_auth_token" { 47 | provider = oci.home 48 | description = "User Auth Token to publish images to OCIR" 49 | user_id = oci_identity_user.ocir_user.id 50 | } 51 | 52 | // FIXME allowing manage repos in tenancy, compartment will throw a 403 when pushing image 53 | // FIXME make it compartment specific 54 | resource "oci_identity_policy" "manage_ocir_compartment" { 55 | provider = oci.home 56 | compartment_id = var.tenancy_ocid 57 | name = "manage_ocir_in_compartment_for_${local.project_name}_${random_string.deploy_id.result}" 58 | description = "Allow group to manage ocir at compartment level for ${local.project_name} ${random_string.deploy_id.result}" 59 | statements = [ 60 | "allow group ${local.ocir_group_name} to manage repos in tenancy", 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /deploy/terraform/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | project_name = var.project_name 3 | deploy_id = random_string.deploy_id.result 4 | anywhere = "0.0.0.0/0" 5 | tcp = "6" 6 | } -------------------------------------------------------------------------------- /deploy/terraform/oke.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | cluster_k8s_latest_version = reverse(sort(data.oci_containerengine_cluster_option.oke.kubernetes_versions))[0] 3 | lb_subnet_cidr = "10.22.128.0/27" 4 | workers_subnet_cidr = "10.22.144.0/20" 5 | cp_subnet_cidr = "10.22.0.8/29" 6 | vcn_cidr = "10.22.0.0/16" 7 | } 8 | 9 | module "oke" { 10 | source = "oracle-terraform-modules/oke/oci" 11 | version = "5.2.4" 12 | 13 | tenancy_id = var.tenancy_ocid 14 | compartment_id = var.compartment_ocid 15 | region = var.region 16 | home_region = var.tenancy_ocid 17 | 18 | providers = { 19 | oci = oci 20 | oci.home = oci.home 21 | } 22 | 23 | kubernetes_version = local.cluster_k8s_latest_version 24 | cluster_name = "${local.project_name}-${local.deploy_id}-oke" 25 | vcn_name = "${local.project_name}-${local.deploy_id}-vcn" 26 | 27 | assign_public_ip_to_control_plane = true 28 | control_plane_is_public = true 29 | control_plane_allowed_cidrs = ["0.0.0.0/0"] 30 | 31 | create_bastion = false 32 | create_operator = false 33 | 34 | ssh_private_key_path = var.ssh_private_key_path 35 | ssh_public_key = var.ssh_public_key 36 | 37 | # IAM - Policies 38 | create_iam_autoscaler_policy = "never" 39 | create_iam_kms_policy = "never" 40 | create_iam_operator_policy = "never" 41 | create_iam_worker_policy = "never" 42 | # Network module - VCN 43 | subnets = { 44 | bastion = { 45 | create = "never" 46 | } 47 | operator = { 48 | create = "never" 49 | } 50 | cp = { 51 | create = "always", 52 | cidr = local.cp_subnet_cidr 53 | } 54 | pub_lb = { 55 | create = "always", 56 | cidr = local.lb_subnet_cidr 57 | } 58 | workers = { 59 | create = "always", 60 | cidr = local.workers_subnet_cidr 61 | } 62 | int_lb = { 63 | create = "never" 64 | } 65 | pods = { 66 | create = "never" 67 | } 68 | } 69 | nsgs = { 70 | bastion = { create = "never" } 71 | operator = { create = "never" } 72 | cp = { create = "always" } 73 | int_lb = { create = "never" } 74 | pub_lb = { create = "always" } // never 75 | workers = { create = "always" } 76 | pods = { create = "always" } // never 77 | } 78 | 79 | assign_dns = true 80 | create_vcn = true 81 | vcn_cidrs = [local.vcn_cidr] 82 | vcn_dns_label = "oke" 83 | 84 | # lockdown_default_seclist = true 85 | # allow_rules_public_lb = { 86 | # "Allow TCP ingress to public load balancers for HTTPS traffic from anywhere" : { protocol = 6, port = 443, source = "0.0.0.0/0", source_type = "CIDR_BLOCK" }, 87 | # "Allow TCP ingress to public load balancers for HTTP traffic from anywhere" : { protocol = 6, port = 80, source = "0.0.0.0/0", source_type = "CIDR_BLOCK" } 88 | # } 89 | # Network module - security 90 | # allow_node_port_access = true 91 | # allow_worker_internet_access = true 92 | # allow_worker_ssh_access = true 93 | # enable_waf = false 94 | # load_balancers = "public" 95 | # preferred_load_balancer = "public" 96 | # worker_is_public = false 97 | 98 | # Cluster module 99 | create_cluster = true 100 | cni_type = "npn" 101 | cluster_type = "enhanced" 102 | 103 | pods_cidr = "10.244.0.0/16" 104 | services_cidr = "10.96.0.0/16" 105 | use_signed_images = false 106 | use_defined_tags = false 107 | 108 | # Workers 109 | worker_pool_mode = "node-pool" 110 | worker_pool_size = 2 111 | 112 | worker_pools = { 113 | node_pool_1 = { 114 | shape = "VM.Standard.E5.Flex", 115 | ocpus = 1, 116 | memory = 32, 117 | boot_volume_size = 120, 118 | create = true 119 | } 120 | } 121 | } 122 | 123 | data "oci_containerengine_cluster_kube_config" "kube_config" { 124 | cluster_id = module.oke.cluster_id 125 | } 126 | 127 | 128 | resource "local_file" "kubeconfig" { 129 | content = data.oci_containerengine_cluster_kube_config.kube_config.content 130 | filename = "${path.module}/generated/kubeconfig" 131 | } 132 | -------------------------------------------------------------------------------- /deploy/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | 2 | output "project" { 3 | value = "${var.project_name}${random_string.deploy_id.result}" 4 | } 5 | 6 | output "db_service" { 7 | value = "${local.project_name}${local.deploy_id}" 8 | } 9 | 10 | output "db_password" { 11 | value = random_password.adb_admin_password.result 12 | sensitive = true 13 | } 14 | 15 | output "kubeconfig" { 16 | value = data.oci_containerengine_cluster_kube_config.kube_config.content 17 | sensitive = true 18 | } 19 | 20 | output "oke_cluster_ocid" { 21 | value = module.oke.cluster_id 22 | } 23 | 24 | output "ocir_user" { 25 | value = oci_identity_user.ocir_user.name 26 | } 27 | 28 | output "ocir_user_token" { 29 | sensitive = true 30 | value = oci_identity_auth_token.ocir_user_auth_token.token 31 | } 32 | 33 | output "ocir_user_email" { 34 | sensitive = false 35 | value = oci_identity_user.ocir_user.email 36 | } -------------------------------------------------------------------------------- /deploy/terraform/provider.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | home_region_key = [for key, value in data.oci_identity_region_subscriptions.region_subscriptions.region_subscriptions: key if value.is_home_region == true][0] 3 | home_region = data.oci_identity_region_subscriptions.region_subscriptions.region_subscriptions[local.home_region_key] 4 | } 5 | 6 | provider "oci" { 7 | tenancy_ocid = var.tenancy_ocid 8 | region = var.region 9 | config_file_profile = var.config_file_profile 10 | } 11 | 12 | provider "oci" { 13 | alias = "home" 14 | tenancy_ocid = var.tenancy_ocid 15 | region = local.home_region.region_name 16 | config_file_profile = var.config_file_profile 17 | } 18 | -------------------------------------------------------------------------------- /deploy/terraform/random.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "deploy_id" { 2 | length = 2 3 | special = false 4 | upper = false 5 | } -------------------------------------------------------------------------------- /deploy/terraform/storage.tf: -------------------------------------------------------------------------------- 1 | resource "oci_objectstorage_bucket" "artifacts_bucket" { 2 | compartment_id = var.compartment_ocid 3 | name = "artifacts_${local.project_name}_${local.deploy_id}" 4 | namespace = data.oci_objectstorage_namespace.objectstorage_namespace.namespace 5 | } 6 | 7 | resource "oci_objectstorage_object" "wallet_object" { 8 | bucket = oci_objectstorage_bucket.artifacts_bucket.name 9 | content = oci_database_autonomous_database_wallet.adb_wallet.content 10 | namespace = data.oci_objectstorage_namespace.objectstorage_namespace.namespace 11 | object = "wallet.zip.b64" 12 | } 13 | 14 | 15 | resource "oci_objectstorage_preauthrequest" "wallet_par" { 16 | namespace = data.oci_objectstorage_namespace.objectstorage_namespace.namespace 17 | bucket = oci_objectstorage_bucket.artifacts_bucket.name 18 | name = "wallet_par" 19 | access_type = "ObjectRead" 20 | object_name = oci_objectstorage_object.wallet_object.object 21 | time_expires = timeadd(timestamp(), "${var.artifacts_par_expiration_in_days * 24}h") 22 | } -------------------------------------------------------------------------------- /deploy/terraform/terraform.tfvars.mustache: -------------------------------------------------------------------------------- 1 | region = "{{ regionName }}" 2 | tenancy_ocid = "{{ tenancyId }}" 3 | config_file_profile = "{{ profile }}" 4 | compartment_ocid = "{{ compartmentId }}" 5 | ssh_public_key = "{{{ ssh_public_key }}}" 6 | ssh_private_key_path = "{{{ ssh_private_key_path }}}" 7 | cert_fullchain = "{{{ cert_fullchain }}}" 8 | cert_private_key = "{{{ cert_private_key }}}" -------------------------------------------------------------------------------- /deploy/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "tenancy_ocid" { 2 | type = string 3 | } 4 | 5 | variable "region" { 6 | type = string 7 | default = "us-chicago-1" 8 | } 9 | 10 | variable "config_file_profile" { 11 | type = string 12 | } 13 | 14 | variable "compartment_ocid" { 15 | type = string 16 | } 17 | 18 | variable "cert_fullchain" { 19 | type = string 20 | } 21 | 22 | variable "cert_private_key" { 23 | type = string 24 | } 25 | 26 | variable "ssh_private_key_path" { 27 | type = string 28 | } 29 | 30 | variable "ssh_public_key" { 31 | type = string 32 | } 33 | 34 | variable "project_name" { 35 | type = string 36 | default = "genai" 37 | } 38 | 39 | variable "artifacts_par_expiration_in_days" { 40 | type = number 41 | default = 7 42 | } -------------------------------------------------------------------------------- /deploy/terraform/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | oci = { 4 | source = "oracle/oci" 5 | version = "~> 6.30" 6 | configuration_aliases = [oci.home] 7 | } 8 | local = { 9 | source = "hashicorp/local" 10 | version = "~> 2" 11 | # https://registry.terraform.io/providers/hashicorp/local/ 12 | } 13 | random = { 14 | source = "hashicorp/random" 15 | version = "~> 3" 16 | # https://registry.terraform.io/providers/hashicorp/random/ 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /images/Markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/images/Markdown.png -------------------------------------------------------------------------------- /images/QandA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/images/QandA.png -------------------------------------------------------------------------------- /images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/images/architecture.png -------------------------------------------------------------------------------- /images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/images/demo.gif -------------------------------------------------------------------------------- /images/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/images/main.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /scripts/clean.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import Configstore from "configstore"; 3 | import clear from "clear"; 4 | 5 | $.verbose = false; 6 | 7 | clear(); 8 | console.log( 9 | "Clean up config files, certs, ssh keys and Object Storage bucket..." 10 | ); 11 | 12 | const projectName = "genai"; 13 | 14 | const config = new Configstore(projectName, { projectName }); 15 | 16 | const privateKeyPath = config.get("privateKeyPath"); 17 | await $`rm -f ${privateKeyPath}`; 18 | const publicKeyPath = config.get("publicKeyPath"); 19 | await $`rm -f ${publicKeyPath}`; 20 | 21 | const certPath = path.join(__dirname, "..", ".certs"); 22 | await $`rm -rf ${certPath}`; 23 | 24 | await $`rm -f deploy/k8s/backend/application.yaml`; 25 | await $`rm -f deploy/k8s/overlays/prod/kustomization.yaml`; 26 | 27 | const certsK8sPath = "deploy/k8s/ingress/.certs"; 28 | await $`rm -rf ${certsK8sPath}`; 29 | console.log(`Files in ${chalk.green(certsK8sPath)} deleted`); 30 | const walletK8sPath = "deploy/k8s/backend/wallet"; 31 | await $`rm -rf ${walletK8sPath}`; 32 | console.log(`Files in ${chalk.green(walletK8sPath)} deleted`); 33 | 34 | // TODO delete images pushed 35 | // ${regionKey}.ocir.io/${namespace}/${projectName}/web:${webVersion} 36 | // ${regionKey}.ocir.io/${namespace}/${projectName}/backend:${backendBersion} 37 | 38 | await $`rm -rf ./.artifacts`; 39 | await $`rm -rf ./.certs`; 40 | await $`rm -rf ./deploy/terraform/generated/wallet`; 41 | 42 | config.clear(); 43 | -------------------------------------------------------------------------------- /scripts/lib/container.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import { exitWithError } from "./utils.mjs"; 3 | 4 | export async function whichContainerEngine() { 5 | try { 6 | const dockerPath = await which("docker"); 7 | return !dockerPath ? "podman" : "docker"; 8 | } catch (err) { 9 | return "podman"; 10 | } 11 | } 12 | 13 | const ce = await whichContainerEngine(); 14 | 15 | export async function checkPodmanMachineRunning() { 16 | if (ce === "podman") { 17 | const isMachineRunning = ( 18 | await $`podman machine info --format {{.Host.MachineState}}` 19 | ).stdout.trim(); 20 | if (isMachineRunning === "Stopped") { 21 | console.log( 22 | `Run ${chalk.yellow("podman machine start")} before continue` 23 | ); 24 | exitWithError("Podman machine stopped"); 25 | } else { 26 | console.log(`${chalk.green("[ok]")} podman machine running`); 27 | } 28 | } 29 | } 30 | 31 | export async function containerLogin(namespace, user, token, url) { 32 | try { 33 | const { stdout, stderr, exitCode } = 34 | await $`${ce} login -u ${namespace}/${user} -p ${token} ${url}`; 35 | if (exitCode == 0) { 36 | console.log(`${chalk.yellow(url)}: ${chalk.green(stdout.trim())}`); 37 | } else { 38 | console.error(chalk.red(stderr.trim())); 39 | } 40 | } catch (error) { 41 | console.error(chalk.red(error.stderr.trim())); 42 | const yellowUserString = chalk.yellow(user); 43 | exitWithError( 44 | `Review the user ${yellowUserString} and token pair, and try again.` 45 | ); 46 | } 47 | } 48 | 49 | export async function tagImage(local, remote) { 50 | console.log(`${ce} tag ${local} ${remote}`); 51 | try { 52 | await $`${ce} tag ${local} ${remote}`; 53 | } catch (error) { 54 | exitWithError(error.stderr); 55 | } 56 | } 57 | 58 | export async function pushImage(remote) { 59 | console.log(`${ce} push ${remote}`); 60 | try { 61 | await $`${ce} push ${remote}`; 62 | } catch (error) { 63 | exitWithError(error.stderr); 64 | } 65 | } 66 | 67 | export async function buildImage(name, version) { 68 | console.log(`${ce} build . -t ${name}:${version}`); 69 | try { 70 | await $`${ce} build . -t ${name}:${version}`; 71 | } catch (error) { 72 | exitWithError(error.stderr); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /scripts/lib/crypto.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import { exitWithError } from "./utils.mjs"; 3 | 4 | export async function createSelfSignedCert(outputPath = ".") { 5 | await $`mkdir -p ${outputPath}`; 6 | const keyPath = path.normalize(path.join(outputPath, "tls.key")); 7 | const certPath = path.normalize(path.join(outputPath, "tls.crt")); 8 | try { 9 | await $`openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ${keyPath} -out ${certPath} -subj "/CN=nginxsvc/O=nginxsvc"`; 10 | console.log(`Cert Key created: ${chalk.green(keyPath)}`); 11 | console.log(`Cert created: ${chalk.green(certPath)}`); 12 | } catch (error) { 13 | exitWithError(error.stderr); 14 | } 15 | } 16 | 17 | export async function generateRandomString(length = 20) { 18 | if (length < 12) { 19 | exitWithError("Password length too short, >= 12 required"); 20 | } 21 | try { 22 | const output = (await $`openssl rand -base64 ${length + 15}`).stdout.trim(); 23 | if (output.length) { 24 | const cleanPassword = output 25 | .replaceAll("/", "") 26 | .replaceAll("\\", "") 27 | .replaceAll("=", ""); 28 | return cleanPassword.slice(0, length); 29 | } else { 30 | exitWithError("random string generation failed"); 31 | } 32 | } catch (error) { 33 | exitWithError(error.stderr); 34 | } 35 | } 36 | 37 | export async function createRSAKeyPair(outputPath = ".") { 38 | await $`mkdir -p ${outputPath}`; 39 | const privateKeyPath = path.normalize(path.join(outputPath, "rsa.pem")); 40 | const publicKeyPath = path.normalize(path.join(outputPath, "rsa_public.pem")); 41 | try { 42 | await $`openssl genrsa -out ${privateKeyPath} 2048`; 43 | console.log(`RSA Private Key written to: ${chalk.yellow(privateKeyPath)}`); 44 | await $`openssl rsa -in ${privateKeyPath} -outform PEM -pubout -out ${publicKeyPath}`; 45 | console.log(`RSA Public Key written to: ${chalk.yellow(publicKeyPath)}`); 46 | } catch (error) { 47 | exitWithError(error.stderr); 48 | } 49 | } 50 | 51 | export async function createSSHKeyPair(sshPathParam) { 52 | const defaultSSHPath = path.join(os.homedir(), ".ssh", "id_rsa"); 53 | const sshPath = sshPathParam || defaultSSHPath; 54 | try { 55 | await $`ssh-keygen -t rsa -b 4096 -f ${sshPath} -q -N "" << parseInt(n)); 7 | let newVersion; 8 | switch (level) { 9 | case "patch": 10 | newVersion = `${major}.${minor}.${patch + 1}`; 11 | break; 12 | case "minor": 13 | newVersion = `${major}.${minor + 1}.${0}`; 14 | break; 15 | case "major": 16 | newVersion = `${major + 1}.${0}.${0}`; 17 | break; 18 | default: 19 | console.log("No version bump"); 20 | break; 21 | } 22 | try { 23 | const { exitCode, stderr } = 24 | await $`sed -I -e '/^version/s/${oldVersion}/${newVersion}/g' build.gradle`; 25 | if (exitCode !== 0) { 26 | exitWithError(stderr); 27 | } 28 | return newVersion; 29 | } catch (error) { 30 | exitWithError(error.stderr); 31 | } 32 | } 33 | 34 | export async function getVersionGradle() { 35 | try { 36 | const { stdout, exitCode, stderr } = 37 | await $`grep "version = " build.gradle`; 38 | if (exitCode !== 0) { 39 | exitWithError(stderr); 40 | } 41 | const version = stdout.trim().split("version = ")[1].replaceAll("'", ""); 42 | return version; 43 | } catch (error) { 44 | exitWithError(error.stderr); 45 | } 46 | } 47 | 48 | export async function cleanGradle() { 49 | try { 50 | const { stdout, exitCode, stderr } = await $`./gradlew clean`; 51 | if (exitCode !== 0) { 52 | exitWithError(stderr); 53 | } 54 | console.log(stdout.trim()); 55 | } catch (error) { 56 | exitWithError(error.stderr); 57 | } 58 | } 59 | 60 | export async function buildJarGradle() { 61 | try { 62 | const { stdout, exitCode, stderr } = await $`./gradlew bootJar`; 63 | if (exitCode !== 0) { 64 | exitWithError(stderr); 65 | } 66 | console.log(stdout.trim()); 67 | } catch (error) { 68 | exitWithError(error.stderr); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /scripts/lib/npm.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import { exitWithError } from "./utils.mjs"; 3 | 4 | export async function getNpmVersion() { 5 | const { version } = await fs.readJson("./package.json"); 6 | return version; 7 | } 8 | 9 | export async function bump(level = "patch") { 10 | try { 11 | let newVersion; 12 | switch (level) { 13 | case "patch": 14 | newVersion = (await $`npm version patch`).stdout.trim(); 15 | console.log(`New version: ${newVersion}`); 16 | break; 17 | case "minor": 18 | newVersion = (await $`npm version minor`).stdout.trim(); 19 | console.log(`New version: ${newVersion}`); 20 | break; 21 | case "major": 22 | newVersion = (await $`npm version major`).stdout.trim(); 23 | console.log(`New version: ${newVersion}`); 24 | break; 25 | 26 | default: 27 | console.log("No version bump"); 28 | break; 29 | } 30 | return newVersion; 31 | } catch (error) { 32 | exitWithError(error.stderr); 33 | } 34 | } 35 | 36 | export async function buildWeb() { 37 | try { 38 | console.log(`Install dependencies`); 39 | await $`npm install`; 40 | console.log(`Build static content`); 41 | const output = (await $`npm run build`).stdout.trim(); 42 | console.log(output); 43 | } catch (error) { 44 | exitWithError(error.stderr); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /scripts/lib/terraform.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import { exitWithError } from "./utils.mjs"; 3 | 4 | export async function getOutputValues(terraformPath = "") { 5 | const tfOutputPathExists = await fs.pathExists(terraformPath); 6 | if (!tfOutputPathExists) exitWithError("Terraform path doesn't exist"); 7 | 8 | const { stdout: stdoutPwd } = await $`pwd`; 9 | const currentPath = stdoutPwd.trim(); 10 | await cd(path.normalize(terraformPath)); 11 | 12 | const { stdout } = await $`terraform output -json`; 13 | const terraformOutput = JSON.parse(stdout); 14 | 15 | const values = {}; 16 | for (const [key, content] of Object.entries(terraformOutput)) { 17 | values[key] = content.value; 18 | } 19 | 20 | await cd(currentPath); 21 | 22 | return values; 23 | } 24 | -------------------------------------------------------------------------------- /scripts/lib/utils.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | 3 | export async function readEnvJson() { 4 | const envFilePath = ".env.json"; 5 | const envFileExists = await fs.pathExists(envFilePath); 6 | if (envFileExists) { 7 | return fs.readJson(envFilePath); 8 | } 9 | return writeEnvJson({}); 10 | } 11 | 12 | export async function writeEnvJson(properties) { 13 | const envFilePath = ".env.json"; 14 | await fs.writeJson(envFilePath, properties, { spaces: 2 }); 15 | return properties; 16 | } 17 | 18 | export async function validateBumpLevel(level) { 19 | if (!["major", "minor", "patch"].includes(level)) { 20 | exitWithError("Error: release version must be 'major', 'minor' or 'patch'"); 21 | } 22 | return level; 23 | } 24 | 25 | export async function printRegionNames(regions) { 26 | console.log("printRegionNames"); 27 | const regionsByZone = regions.reduce((acc, cur) => { 28 | const zone = cur.name.split("-")[0]; 29 | if (acc[zone]) { 30 | acc[zone].push(cur.name); 31 | } else { 32 | acc[zone] = [cur.name]; 33 | } 34 | return acc; 35 | }, {}); 36 | Object.keys(regionsByZone).forEach((zone) => 37 | console.log(`\t${chalk.yellow(zone)}: ${regionsByZone[zone].join(", ")}`) 38 | ); 39 | } 40 | 41 | export async function getRegionByKey(code = "fra") { 42 | const output = (await $`oci iam region list`).stdout.trim(); 43 | const { data } = JSON.parse(output); 44 | return data.find((r) => code.toUpperCase() === r.key); 45 | } 46 | 47 | export async function setVariableFromEnvOrPrompt( 48 | envKey, 49 | questionText, 50 | printChoices 51 | ) { 52 | const envValue = process.env[envKey]; 53 | if (envValue) { 54 | return envValue; 55 | } else { 56 | if (printChoices) { 57 | printChoices(); 58 | } 59 | const answer = await question(`${questionText}: `); 60 | return answer; 61 | } 62 | } 63 | 64 | export async function exitWithError(errorMessage = "") { 65 | console.error(chalk.red(errorMessage.trim())); 66 | process.exit(1); 67 | } 68 | 69 | export async function checkRequiredProgramsExist(programs) { 70 | try { 71 | for (let program of programs) { 72 | await which(program); 73 | console.log(`${chalk.green("[ok]")} ${program}`); 74 | } 75 | } catch (error) { 76 | exitWithError(`Error: Required command ${error.message}`); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scripts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "clear": "^0.1.0", 16 | "configstore": "^6.0.0", 17 | "ini": "^4.1.3", 18 | "inquirer": "^9.2.19", 19 | "moment": "^2.30.1", 20 | "mustache": "^4.2.0", 21 | "underscore": "^1.13.6", 22 | "yaml": "^2.5.0", 23 | "zx": "^8.1.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/release.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import Configstore from "configstore"; 3 | import clear from "clear"; 4 | import { 5 | buildJarGradle, 6 | cleanGradle, 7 | getVersionGradle, 8 | } from "./lib/gradle.mjs"; 9 | import { getNpmVersion } from "./lib/npm.mjs"; 10 | import { 11 | buildImage, 12 | tagImage, 13 | pushImage, 14 | checkPodmanMachineRunning, 15 | containerLogin, 16 | } from "./lib/container.mjs"; 17 | import { getOutputValues } from "./lib/terraform.mjs"; 18 | 19 | $.verbose = false; 20 | 21 | clear(); 22 | console.log("Release latest backend and web version..."); 23 | 24 | const projectName = "genai"; 25 | 26 | const config = new Configstore(projectName, { projectName }); 27 | 28 | const namespace = config.get("namespace"); 29 | const regionKey = config.get("regionKey"); 30 | 31 | const pwdOutput = (await $`pwd`).stdout.trim(); 32 | await cd(`${pwdOutput}/web`); 33 | const webVersion = await getNpmVersion(); 34 | config.set("webVersion", webVersion); 35 | await cd(`${pwdOutput}/app`); 36 | const appVersion = await getNpmVersion(); 37 | config.set("appVersion", appVersion); 38 | await cd(`${pwdOutput}/backend`); 39 | const backendVersion = await getVersionGradle(); 40 | config.set("backendVersion", backendVersion); 41 | await cd(pwdOutput); 42 | 43 | await checkPodmanMachineRunning(); 44 | 45 | const ocirUrl = `${regionKey}.ocir.io`; 46 | 47 | // FIXME use OCI Vault for the token 48 | const { ocir_user, ocir_user_token, ocir_user_email } = await getOutputValues( 49 | "./deploy/terraform" 50 | ); 51 | config.set("ocir_user", ocir_user); 52 | config.set("ocir_user_email", ocir_user_email); 53 | config.set("ocir_user_token", ocir_user_token); 54 | 55 | await containerLogin(namespace, ocir_user, ocir_user_token, ocirUrl); 56 | await releaseWeb(); 57 | await releaseApp(); 58 | await releaseBackend(); 59 | 60 | async function releaseWeb() { 61 | const service = "web"; 62 | await cd(service); 63 | const imageName = `${projectName}/${service}`; 64 | await buildImage(`localhost/${imageName}`, webVersion); 65 | const localImage = `localhost/${imageName}:${webVersion}`; 66 | const remoteImage = `${ocirUrl}/${namespace}/${imageName}:${webVersion}`; 67 | await tagImage(localImage, remoteImage); 68 | await pushImage(remoteImage); 69 | console.log(`${chalk.green(remoteImage)} pushed`); 70 | await cd(".."); 71 | } 72 | 73 | async function releaseApp() { 74 | const service = "app"; 75 | await cd(service); 76 | const imageName = `${projectName}/${service}`; 77 | await buildImage(`localhost/${imageName}`, appVersion); 78 | const localImage = `localhost/${imageName}:${appVersion}`; 79 | const remoteImage = `${ocirUrl}/${namespace}/${imageName}:${appVersion}`; 80 | await tagImage(localImage, remoteImage); 81 | await pushImage(remoteImage); 82 | console.log(`${chalk.green(remoteImage)} pushed`); 83 | await cd(".."); 84 | } 85 | 86 | async function releaseBackend() { 87 | const service = "backend"; 88 | await cd(service); 89 | await cleanGradle(); 90 | await buildJarGradle(); 91 | const currentVersion = await getVersionGradle(); 92 | const imageName = `${projectName}/${service}`; 93 | await buildImage(`localhost/${imageName}`, currentVersion); 94 | const localImage = `localhost/${imageName}:${currentVersion}`; 95 | const remoteImage = `${ocirUrl}/${namespace}/${imageName}:${currentVersion}`; 96 | await tagImage(localImage, remoteImage); 97 | await pushImage(remoteImage); 98 | console.log(`${chalk.green(remoteImage)} pushed`); 99 | await cd(".."); 100 | } 101 | -------------------------------------------------------------------------------- /scripts/tfvars.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import Mustache from "mustache"; 3 | import Configstore from "configstore"; 4 | import clear from "clear"; 5 | 6 | $.verbose = false; 7 | 8 | clear(); 9 | console.log("Create terraform.tfvars..."); 10 | 11 | const projectName = "genai"; 12 | 13 | const config = new Configstore(projectName, { projectName }); 14 | 15 | await generateTFVars(); 16 | 17 | async function generateTFVars() { 18 | const compartmentId = config.get("compartmentId"); 19 | const compartmentName = config.get("compartmentName"); 20 | const profile = config.get("profile"); 21 | const regionName = config.get("regionName"); 22 | const tenancyId = config.get("tenancyId"); 23 | const publicKeyContent = config.get("publicKeyContent"); 24 | const sshPrivateKeyPath = config.get("privateKeyPath"); 25 | const certFullchain = config.get("certFullchain"); 26 | const certPrivateKey = config.get("certPrivateKey"); 27 | 28 | const tfVarsPath = "deploy/terraform/terraform.tfvars"; 29 | 30 | const tfvarsTemplate = await fs.readFile(`${tfVarsPath}.mustache`, "utf-8"); 31 | 32 | const output = Mustache.render(tfvarsTemplate, { 33 | tenancyId, 34 | regionName, 35 | compartmentId, 36 | profile, 37 | ssh_public_key: publicKeyContent, 38 | ssh_private_key_path: sshPrivateKeyPath, 39 | cert_fullchain: certFullchain, 40 | cert_private_key: certPrivateKey, 41 | }); 42 | 43 | console.log( 44 | `Terraform will deploy resources in ${chalk.green( 45 | regionName 46 | )} in compartment ${ 47 | compartmentName ? chalk.green(compartmentName) : chalk.green("root") 48 | }` 49 | ); 50 | 51 | await fs.writeFile(tfVarsPath, output); 52 | 53 | console.log(`File ${chalk.green(tfVarsPath)} created`); 54 | 55 | console.log(`1. ${chalk.yellow("cd deploy/terraform/")}`); 56 | console.log(`2. ${chalk.yellow("terraform init")}`); 57 | console.log(`3. ${chalk.yellow("terraform apply -auto-approve")}`); 58 | } 59 | -------------------------------------------------------------------------------- /serverStart.sh: -------------------------------------------------------------------------------- 1 | cd service/python/ 2 | python3 -m venv venv 3 | . venv/bin/activate 4 | pip install -r requirements.txt 5 | python server.py -------------------------------------------------------------------------------- /service/python/.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 34 | 35 | #temp directory ignore 36 | venv/ -------------------------------------------------------------------------------- /service/python/requirements.txt: -------------------------------------------------------------------------------- 1 | asn1crypto==1.5.1 2 | asyncio==3.4.3 3 | cached-property==1.5.2 4 | certifi==2024.7.4 5 | cffi==1.16.0 6 | circuitbreaker==1.4.0 7 | cryptography==43.0.1 8 | oci==2.126.2 9 | pycparser==2.21 10 | pyOpenSSL==24.1.0 11 | python-dateutil==2.8.2 12 | python-pkcs11==0.7.0 13 | pytz==2023.3.post1 14 | six==1.16.0 15 | throttler==1.2.2 16 | websockets==12.0 17 | pypdf==4.2.0 -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=oracle-devrel_oci-generative-ai-jet-ui 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 -------------------------------------------------------------------------------- /web/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | Dockerfile -------------------------------------------------------------------------------- /web/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 node:22-slim AS builder 2 | 3 | ENV NODE_ENV=production 4 | ENV NODE_MAX_MEM=2048 5 | 6 | WORKDIR /usr/src/web 7 | 8 | COPY package*.json ./ 9 | 10 | RUN npm ci --only=production --include=dev 11 | 12 | COPY public/ ./public 13 | COPY src/ ./src 14 | COPY index.html . 15 | COPY .eslintrc.cjs . 16 | COPY vite.config.js . 17 | 18 | RUN npm run build 19 | 20 | FROM --platform=linux/amd64 nginx:1.23-alpine-slim 21 | 22 | COPY --from=builder /usr/src/web/dist/ /usr/share/nginx/html/ 23 | RUN rm /etc/nginx/conf.d/default.conf 24 | COPY nginx/default.conf /etc/nginx/conf.d 25 | 26 | EXPOSE 80 27 | 28 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OCI GenAI PoC 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /web/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | location / { 5 | root /usr/share/nginx/html; 6 | index index.html index.htm; 7 | try_files $uri $uri/ /index.html; 8 | } 9 | 10 | error_page 500 502 503 504 /50x.html; 11 | location = /50x.html { 12 | root /usr/share/nginx/html; 13 | } 14 | } -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "private": true, 4 | "version": "0.0.4", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@emotion/react": "^11.11.3", 14 | "@emotion/styled": "^11.11.0", 15 | "@fontsource/roboto": "^5.0.8", 16 | "@mui/icons-material": "^5.15.4", 17 | "@mui/material": "^5.15.4", 18 | "@stomp/stompjs": "^7.0.0", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0", 21 | "react-hook-form": "^7.52.0", 22 | "react-router-dom": "^6.23.1", 23 | "uuid": "^9.0.1", 24 | "ws": "^8.17.1" 25 | }, 26 | "devDependencies": { 27 | "@types/react": "^18.2.43", 28 | "@types/react-dom": "^18.2.17", 29 | "@vitejs/plugin-react": "^4.2.1", 30 | "eslint": "^8.55.0", 31 | "eslint-plugin-react": "^7.33.2", 32 | "eslint-plugin-react-hooks": "^4.6.0", 33 | "eslint-plugin-react-refresh": "^0.4.5", 34 | "vite": "^5.4.6" 35 | } 36 | } -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle-devrel/oci-generative-ai-jet-ui/bc7020352fe8b46a530fe80708476887e8d5a1cf/web/public/favicon.ico -------------------------------------------------------------------------------- /web/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Container, Typography } from "@mui/material"; 2 | import { StompProvider } from "./stompHook"; 3 | import Routing from "./Routing"; 4 | import { v4 as uuidv4 } from "uuid"; 5 | import IdentityContext from "./IdentityContext"; 6 | 7 | let conversationId; 8 | if (localStorage.getItem("conversationId")) { 9 | conversationId = localStorage.getItem("conversationId"); 10 | } else { 11 | conversationId = uuidv4(); 12 | localStorage.setItem("conversationId", conversationId); 13 | } 14 | 15 | const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"; 16 | const hostname = 17 | window.location.hostname === "localhost" 18 | ? "localhost:8080" 19 | : window.location.hostname; 20 | const brokerURL = `${protocol}${hostname}/websocket`; 21 | 22 | function App() { 23 | return ( 24 | 29 | 30 | 31 | 32 | OCI GenAI PoC 33 | 34 | 35 | 36 | 37 | 38 | ); 39 | } 40 | 41 | export default App; 42 | -------------------------------------------------------------------------------- /web/src/Conversation.jsx: -------------------------------------------------------------------------------- 1 | import { Avatar, Box, Paper, Stack, Typography } from "@mui/material"; 2 | import { deepOrange, deepPurple } from "@mui/material/colors"; 3 | 4 | function Conversation({ children: conversation }) { 5 | if (!conversation.length) return; 6 | return ( 7 | 8 | 9 | {conversation.map(({ id, user, content }) => { 10 | return ( 11 | 20 | {user === "ai" && } 21 | {content} 22 | {user !== "ai" && } 23 | 24 | ); 25 | })} 26 | 27 | 28 | ); 29 | } 30 | 31 | function AIAvatar() { 32 | return AI; 33 | } 34 | 35 | function MeAvatar() { 36 | return Me; 37 | } 38 | 39 | export default Conversation; 40 | -------------------------------------------------------------------------------- /web/src/IdentityContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | const IdentityContext = createContext(undefined); 4 | 5 | export default IdentityContext; 6 | -------------------------------------------------------------------------------- /web/src/PromptInput.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Stack, TextField } from "@mui/material"; 2 | import { useState, useRef } from "react"; 3 | 4 | function PromptInput({ setConversation, setPromptValue, disabled }) { 5 | const [inputValue, setInputValue] = useState(""); 6 | const textRef = useRef(); 7 | return ( 8 | 9 | setInputValue(e.target.value)} 18 | /> 19 | 39 | 40 | ); 41 | } 42 | 43 | export default PromptInput; 44 | -------------------------------------------------------------------------------- /web/src/Routing.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter, RouterProvider } from "react-router-dom"; 2 | import Chat from "./Chat"; 3 | import Summary from "./Summary"; 4 | import SummaryText from "./SummaryText"; 5 | 6 | const router = createBrowserRouter([ 7 | { 8 | path: "/", 9 | element: , 10 | }, 11 | { 12 | path: "/summary", 13 | element: , 14 | }, 15 | { 16 | path: "/summaryText", 17 | element: , 18 | }, 19 | ]); 20 | 21 | function Routing() { 22 | return ; 23 | } 24 | 25 | export default Routing; 26 | -------------------------------------------------------------------------------- /web/src/Summary.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Button, 4 | Snackbar, 5 | Stack, 6 | TextField, 7 | Typography, 8 | } from "@mui/material"; 9 | import { useEffect, useState, useContext } from "react"; 10 | import { useForm } from "react-hook-form"; 11 | import { useStomp } from "./stompHook"; 12 | import IdentityContext from "./IdentityContext"; 13 | 14 | function Summary() { 15 | const identity = useContext(IdentityContext); 16 | const { register, handleSubmit, reset } = useForm(); 17 | const [waiting, setWaiting] = useState(false); 18 | const [showError, setShowError] = useState(false); 19 | const [errorMessage, setErrorMessage] = useState(); 20 | const [summary, setSummary] = useState(""); 21 | const { subscribe, unsubscribe, isConnected } = useStomp(); 22 | 23 | useEffect(() => { 24 | if (isConnected) { 25 | subscribe("/user/queue/summary", (message) => { 26 | if (message.errorMessage.length > 0) { 27 | setErrorMessage(message.errorMessage); 28 | setShowError(true); 29 | } else { 30 | console.log("/user/queue/summary"); 31 | console.log(message); 32 | setSummary(message); 33 | } 34 | }); 35 | } 36 | 37 | return () => { 38 | unsubscribe("/user/queue/summary"); 39 | }; 40 | }, [isConnected]); 41 | 42 | const onSubmit = async (data) => { 43 | setWaiting(true); 44 | const formData = new FormData(); 45 | formData.append("file", data.file[0]); 46 | 47 | const res = await fetch("/api/upload", { 48 | method: "POST", 49 | body: formData, 50 | headers: { conversationId: identity, modelId: "n/a" }, 51 | }); 52 | const responseData = await res.json(); 53 | const { content, errorMessage } = responseData; 54 | if (errorMessage.length) { 55 | setErrorMessage(errorMessage); 56 | setShowError(true); 57 | } else { 58 | console.log(content); 59 | setSummary(content); 60 | } 61 | setWaiting(false); 62 | reset(); 63 | }; 64 | return ( 65 | 66 |
    67 | 68 | 73 | 74 | 77 | 78 |
    79 | {summary.length !== 0 && {summary}} 80 | { 85 | setErrorMessage(); 86 | setShowError(false); 87 | }} 88 | message={errorMessage} 89 | /> 90 |
    91 | ); 92 | } 93 | 94 | export default Summary; 95 | -------------------------------------------------------------------------------- /web/src/SummaryText.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Button, 4 | Snackbar, 5 | Stack, 6 | TextField, 7 | Typography, 8 | } from "@mui/material"; 9 | import { useState, useContext } from "react"; 10 | import { useForm } from "react-hook-form"; 11 | import IdentityContext from "./IdentityContext"; 12 | 13 | function SummaryText() { 14 | const identity = useContext(IdentityContext); 15 | const { register, handleSubmit, reset } = useForm(); 16 | const [waiting, setWaiting] = useState(false); 17 | const [showError, setShowError] = useState(false); 18 | const [errorMessage, setErrorMessage] = useState(); 19 | const [summary, setSummary] = useState(""); 20 | 21 | const onSubmit = async (data) => { 22 | setWaiting(true); 23 | const body = JSON.stringify({ 24 | content: data.fullText, 25 | }); 26 | 27 | const res = await fetch("/api/genai/summary", { 28 | method: "POST", 29 | body: body, 30 | headers: { 31 | "Content-Type": "application/json", 32 | conversationId: identity, 33 | modelId: "n/a", 34 | }, 35 | }); 36 | const responseData = await res.json(); 37 | const { content, errorMessage } = responseData; 38 | if (errorMessage.length) { 39 | setErrorMessage(errorMessage); 40 | setShowError(true); 41 | } else { 42 | setSummary(content); 43 | } 44 | setWaiting(false); 45 | reset(); 46 | }; 47 | return ( 48 | 49 |
    50 | 51 | 57 | 60 | 61 |
    62 | {summary.length !== 0 && {summary}} 63 | { 68 | setErrorMessage(); 69 | setShowError(false); 70 | }} 71 | message={errorMessage} 72 | /> 73 |
    74 | ); 75 | } 76 | 77 | export default SummaryText; 78 | -------------------------------------------------------------------------------- /web/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "@fontsource/roboto/300.css"; 5 | import "@fontsource/roboto/400.css"; 6 | import "@fontsource/roboto/500.css"; 7 | import "@fontsource/roboto/700.css"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root")).render( 10 | 11 | 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /web/src/stompHook/Provider.jsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useEffect, useState } from "react"; 2 | import { Client } from "@stomp/stompjs"; 3 | 4 | const defaultValue = { 5 | isConnected: false, 6 | stompClient: null, 7 | subscriptions: {}, 8 | setSubscriptions: () => {}, 9 | }; 10 | 11 | export const StompContext = createContext(defaultValue); 12 | 13 | export const StompProvider = ({ children, config, onConnected }) => { 14 | const [stompClient, setStompClient] = useState(new Client(config)); 15 | const [subscriptions, setSubscriptions] = useState({}); 16 | 17 | useEffect(() => { 18 | stompClient?.activate(); 19 | onConnected?.(stompClient); 20 | return () => { 21 | stompClient?.deactivate(); 22 | }; 23 | }, [stompClient]); 24 | 25 | return ( 26 | 33 | {children} 34 | 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /web/src/stompHook/hook.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { StompContext } from "./Provider"; 3 | 4 | export const useStomp = () => { 5 | const value = useContext(StompContext); 6 | const { stompClient, subscriptions, setSubscriptions } = value; 7 | 8 | const send = (path, body, headers) => { 9 | stompClient?.publish({ 10 | destination: path, 11 | headers, 12 | body: JSON.stringify(body), 13 | }); 14 | }; 15 | 16 | const subscribe = (path, callback) => { 17 | if (!stompClient) return; 18 | if (subscriptions[path]) return; 19 | 20 | const subscription = stompClient.subscribe(path, (message) => { 21 | const body = JSON.parse(message.body); 22 | callback(body); 23 | }); 24 | setSubscriptions((prev) => { 25 | return { ...prev, [path]: subscription }; 26 | }); 27 | }; 28 | 29 | const unsubscribe = (path) => { 30 | const copy = { ...subscriptions }; 31 | if (!copy[path]) return subscriptions; 32 | copy[path].unsubscribe(); 33 | delete copy[path]; 34 | setSubscriptions((prev) => { 35 | return { ...copy }; 36 | }); 37 | }; 38 | 39 | const disconnect = () => { 40 | stompClient?.deactivate(); 41 | }; 42 | 43 | return { 44 | disconnect, 45 | subscribe, 46 | unsubscribe, 47 | subscriptions, 48 | send, 49 | isConnected: !!stompClient?.connected, 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /web/src/stompHook/index.js: -------------------------------------------------------------------------------- 1 | export { StompProvider } from "./Provider"; 2 | export { useStomp } from "./hook"; 3 | -------------------------------------------------------------------------------- /web/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | server: { 8 | proxy: { 9 | "/api": { 10 | target: "http://localhost:8080", 11 | changeOrigin: true, 12 | secure: false, 13 | }, 14 | }, 15 | }, 16 | }); 17 | --------------------------------------------------------------------------------