├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── bindrole.png ├── image-1.png ├── image-2.png ├── image-3.png ├── image-4.png ├── image-cf1.png ├── image-cf2.png ├── image-cf21.png ├── image-cf3.png ├── image-cf4.png ├── image-cf5.png ├── image-cf6.png ├── image-cn-cf1.png ├── image-cn-cf2.png ├── image-pm2list.png ├── image_iamrole.png ├── image_iamrole2.png └── image_iamrole3.png ├── backend ├── .gitignore ├── 0.setup-cn.sh ├── 01.setup.sh ├── 02.start_backend.sh ├── 03.upgrade.sh ├── README.md ├── README_en.md ├── byoc │ ├── Dockerfile │ ├── Dockerfile.sglang │ ├── app │ │ └── serve │ ├── app_sglang │ │ └── serve │ ├── build_and_push.sh │ ├── build_and_push_sglang.sh │ ├── deploy_and_test.ipynb │ ├── deploy_and_test_inference_componet.ipynb │ ├── deploy_and_test_sglang.ipynb │ ├── ecr-policy.json │ ├── lmi.ipynb │ ├── model_tar │ │ ├── env │ │ └── s5cmd │ ├── requirements.txt │ ├── startup.py │ └── vllm_by_scripts │ │ └── start.sh ├── db_management │ ├── database.py │ └── sqlite_helper.py ├── docker │ ├── Dockerfile │ ├── build_and_push.sh │ ├── ecr-policy.json │ ├── requirements_deps.txt │ ├── s5cmd │ └── train.py ├── docker_easyr1 │ ├── Dockerfile │ ├── build_and_push.sh │ ├── ecr-policy.json │ ├── requirements_deps.txt │ ├── s5cmd │ └── train.py ├── env.sample ├── examples │ ├── history.zip │ ├── huanhuan.json │ ├── invoke_endpoint.py │ ├── multl-modal-data-preprocessin.ipynb │ └── test_images │ │ ├── demo_pic_1.jpg │ │ └── demo_pic_2.jpg ├── inference │ ├── endpoint_management.py │ ├── model_utils.py │ └── serving.py ├── logger_config.py ├── model │ ├── __init__.py │ └── data_model.py ├── processing_engine │ ├── __init__.py │ ├── job_state_machine.py │ └── main.py ├── requirements.txt ├── scripts │ └── mysql_setup.sql ├── server.py ├── training │ ├── __init__.py │ ├── helper.py │ ├── jobs.py │ └── training_job.py ├── users │ ├── add_user.py │ ├── delete_user.py │ └── login.py └── utils │ ├── __init__.py │ ├── config.py │ ├── get_factory_config.py │ ├── llamafactory │ ├── data │ │ └── dataset_info.json │ ├── extras │ │ └── constants.py │ └── hparams │ │ └── model_args.py │ └── outputs.py ├── cloudformation-template-cn.yaml ├── cloudformation-template.yaml ├── cn-region-deploy.sh ├── env.sample ├── package.json ├── pm2run.config.js ├── public ├── download.webp ├── index.html ├── manifest.json ├── model-hub-essential-logo.svg ├── modelhublog.webp └── robots.txt ├── src ├── App.css ├── App.test.tsx ├── App.tsx ├── common │ ├── api-gateway.js │ ├── api-test.js │ ├── apply-mode.ts │ ├── columnDefinitionsHelper.js │ ├── i18n.js │ ├── localStorage.ts │ ├── property-filter-operators.ts │ ├── property-filter │ │ └── matcher.js │ ├── s3-resource-selector │ │ ├── mock-data.js │ │ └── mock-request.js │ ├── types.ts │ └── utils.js ├── fake-server │ ├── content-origins.js │ ├── distributions.js │ ├── index.js │ ├── tags.js │ └── utils │ │ ├── fake-delay.js │ │ ├── fetch-json.js │ │ └── paginate.js ├── i18n-strings │ ├── app-layout.ts │ ├── collection-preferences.ts │ ├── flashbar.ts │ ├── header.ts │ ├── index.ts │ ├── pagination.ts │ ├── property-filter.ts │ ├── s3-resource-selector.ts │ ├── split-panel.ts │ ├── table.ts │ ├── tag-editor.ts │ └── text-filter.ts ├── index.css ├── index.tsx ├── pages │ ├── cards │ │ ├── cards-config.jsx │ │ ├── common-components.jsx │ │ └── index.jsx │ ├── chat │ │ ├── chatmain.tsx │ │ ├── common-components.tsx │ │ ├── content.tsx │ │ ├── conversations.tsx │ │ └── prompt-panel.tsx │ ├── commons │ │ ├── breadcrumbs.tsx │ │ ├── chat-settings.tsx │ │ ├── common-components.tsx │ │ ├── data-provider.jsx │ │ ├── disclaimer-flashbar-item.tsx │ │ ├── external-link-group.tsx │ │ ├── external-link.tsx │ │ ├── fake-delay.ts │ │ ├── help-panel.tsx │ │ ├── index.ts │ │ ├── info-link.tsx │ │ ├── navigation.tsx │ │ ├── no-found.tsx │ │ ├── notifications.tsx │ │ ├── private-route.jsx │ │ ├── separated-list │ │ │ ├── index.tsx │ │ │ └── styles.module.scss │ │ ├── table-config.jsx │ │ ├── top-nav.tsx │ │ ├── use-async-data.js │ │ ├── use-auth.js │ │ ├── use-column-widths.js │ │ ├── use-content-origins.js │ │ ├── use-id.js │ │ ├── use-local-storage.ts │ │ └── use-notifications.js │ ├── endpoints │ │ ├── create-ed.tsx │ │ ├── delete-ed.tsx │ │ ├── full-page-header.tsx │ │ ├── hooks.js │ │ ├── index.jsx │ │ ├── table-config.jsx │ │ └── view-code.tsx │ ├── jobs │ │ ├── create-job │ │ │ ├── components │ │ │ │ ├── code-editor.jsx │ │ │ │ ├── form.jsx │ │ │ │ ├── jobinfo-panel.jsx │ │ │ │ ├── log-display.jsx │ │ │ │ └── output-path.jsx │ │ │ ├── form-config.jsx │ │ │ ├── form-validation-config.tsx │ │ │ └── index.jsx │ │ ├── create-r1-job │ │ │ └── index.jsx │ │ ├── full-page-header.tsx │ │ ├── hooks.js │ │ ├── index.jsx │ │ ├── job-detail.jsx │ │ └── table-config.jsx │ ├── login │ │ └── login.jsx │ ├── table-property-filter │ │ ├── index.jsx │ │ ├── property-filter-table.jsx │ │ └── table-property-filter-config.ts │ └── table │ │ └── index.jsx ├── react-app-env.d.ts ├── reportWebVitals.ts ├── resources │ ├── Graphin.svg │ ├── Res_Amazon-SageMaker_Model_48_Light.svg │ ├── behaviors.json │ ├── content-origins.json │ ├── distributions.json │ ├── distributionsFilteringProperties.json │ ├── ec2-instances.js │ ├── icons8-user-96.png │ ├── image-placeholder.png │ ├── instances.js │ ├── logs.json │ ├── model-hub-high-resolution-logo-transparent.webp │ ├── modelhublog.webp │ ├── origins.json │ ├── related-instances.ts │ ├── smlogo.svg │ ├── tags.json │ └── user.svg ├── setupTests.ts └── styles │ ├── base.scss │ ├── dashboard.scss │ ├── form.scss │ ├── onboarding.scss │ ├── product-page.scss │ ├── table-date-filter.scss │ ├── table-select.scss │ ├── top-navigation.scss │ └── wizard.scss ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | cache/ 19 | demos/ 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | .vscode/ 27 | *.tar.gz 28 | miniconda3/ 29 | Miniconda3-* 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # Snowpack dependency directory (https://snowpack.dev/) 50 | web_modules/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional stylelint cache 62 | .stylelintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variable files 80 | .env 81 | .env.development.local 82 | .env.test.local 83 | .env.production.local 84 | .env.local 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | .parcel-cache 89 | 90 | # Next.js build output 91 | .next 92 | out 93 | 94 | # Nuxt.js build / generate output 95 | .nuxt 96 | dist 97 | 98 | # Gatsby files 99 | .cache/ 100 | # Comment in the public line in if your project uses Gatsby and not Next.js 101 | # https://nextjs.org/blog/next-9-1#public-directory-support 102 | # public 103 | 104 | # vuepress build output 105 | .vuepress/dist 106 | 107 | # vuepress v2.x temp and cache directory 108 | .temp 109 | .cache 110 | 111 | # Docusaurus cache and generated files 112 | .docusaurus 113 | 114 | # Serverless directories 115 | .serverless/ 116 | 117 | # FuseBox cache 118 | .fusebox/ 119 | 120 | # DynamoDB Local files 121 | .dynamodb/ 122 | 123 | # TernJS port file 124 | .tern-port 125 | 126 | # Stores VSCode versions used for testing VSCode extensions 127 | .vscode-test 128 | 129 | # yarn v2 130 | .yarn/cache 131 | .yarn/unplugged 132 | .yarn/build-state.yml 133 | .yarn/install-state.gz 134 | .pnp.* 135 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 136 | # Byte-compiled / optimized / DLL files 137 | __pycache__/ 138 | *.py[cod] 139 | *$py.class 140 | 141 | # C extensions 142 | *.so 143 | # dependencies 144 | /node_modules 145 | /.pnp 146 | .pnp.js 147 | .env 148 | # testing 149 | /coverage 150 | 151 | # production 152 | /build 153 | 154 | # misc 155 | .DS_Store 156 | .env.local 157 | .env.development.local 158 | .env.test.local 159 | .env.production.local 160 | 161 | npm-debug.log* 162 | yarn-debug.log* 163 | yarn-error.log* 164 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "backend/docker/LLaMA-Factory"] 2 | path = backend/docker/LLaMA-Factory 3 | url = https://github.com/hiyouga/LLaMA-Factory.git 4 | [submodule "backend/docker_easyr1/EasyR1"] 5 | path = backend/docker_easyr1/EasyR1 6 | url = https://github.com/hiyouga/EasyR1.git 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /assets/bindrole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/bindrole.png -------------------------------------------------------------------------------- /assets/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-1.png -------------------------------------------------------------------------------- /assets/image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-2.png -------------------------------------------------------------------------------- /assets/image-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-3.png -------------------------------------------------------------------------------- /assets/image-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-4.png -------------------------------------------------------------------------------- /assets/image-cf1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cf1.png -------------------------------------------------------------------------------- /assets/image-cf2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cf2.png -------------------------------------------------------------------------------- /assets/image-cf21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cf21.png -------------------------------------------------------------------------------- /assets/image-cf3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cf3.png -------------------------------------------------------------------------------- /assets/image-cf4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cf4.png -------------------------------------------------------------------------------- /assets/image-cf5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cf5.png -------------------------------------------------------------------------------- /assets/image-cf6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cf6.png -------------------------------------------------------------------------------- /assets/image-cn-cf1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cn-cf1.png -------------------------------------------------------------------------------- /assets/image-cn-cf2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-cn-cf2.png -------------------------------------------------------------------------------- /assets/image-pm2list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image-pm2list.png -------------------------------------------------------------------------------- /assets/image_iamrole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image_iamrole.png -------------------------------------------------------------------------------- /assets/image_iamrole2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image_iamrole2.png -------------------------------------------------------------------------------- /assets/image_iamrole3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/assets/image_iamrole3.png -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | cache/ 6 | # C extensions 7 | *.so 8 | *.db 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | *.log 29 | *.pkl 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | *.key 36 | *.cert 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # poetry 100 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 101 | # This is especially recommended for binary packages to ensure reproducibility, and is more 102 | # commonly ignored for libraries. 103 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 104 | #poetry.lock 105 | 106 | # pdm 107 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 108 | #pdm.lock 109 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 110 | # in version control. 111 | # https://pdm.fming.dev/#use-with-ide 112 | .pdm.toml 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | .idea/ 163 | 164 | # custom .gitignore 165 | user.config 166 | saves/ 167 | cache/ 168 | -------------------------------------------------------------------------------- /backend/0.setup-cn.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | #安装miniconda 4 | echo "install miniconda...." 5 | wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 6 | chmod +x Miniconda3-latest-Linux-x86_64.sh 7 | ./Miniconda3-latest-Linux-x86_64.sh -b -f -p ../miniconda3 8 | source ../miniconda3/bin/activate 9 | conda create -n py311 python=3.11 -y 10 | conda activate py311 11 | 12 | # 定义要添加的内容 13 | PIP_INDEX="https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" 14 | 15 | pip config set global.index-url "$PIP_INDEX" && pip config set global.extra-index-url "$PIP_INDEX" 16 | # 安装 requirements 17 | echo "install requirements....." 18 | pip install -r requirements.txt 19 | 20 | ##设置默认aws region 21 | sudo apt install awscli 22 | aws configure set region cn-northwest-1 23 | 24 | 25 | # 安装Docker 26 | sudo apt-get update 27 | sudo apt install python3-pip git -y && pip3 install -U awscli && pip install pyyaml==5.3.1 28 | sudo apt install docker.io -y 29 | # Configure components 30 | sudo systemctl enable docker && sudo systemctl start docker && sudo usermod -aG docker $USER 31 | sudo chmod 666 /var/run/docker.sock 32 | 33 | # 添加 Docker 配置 34 | DOCKER_CONFIG="/etc/docker/daemon.json" 35 | sudo mkdir -p /etc/docker 36 | sudo tee "$DOCKER_CONFIG" > /dev/null < Response:\n\ 22 | \ return Response(status_code=200)\n\ 23 | \n\ 24 | @app.post("/invocations")\n\ 25 | async def invocations(raw_request: Request):\n\ 26 | \ return await v1_chat_completions(_global_state.tokenizer_manager, raw_request)\n\ 27 | ' ${PYTHON_SITEPACKAGES}/srt/entrypoints/http_server.py; \ 28 | chmod +x /app/serve 29 | 30 | # 让端口8080在容器外可用 31 | EXPOSE 8080 32 | 33 | # 定义环境变量 34 | ENV PATH="/app:${PATH}" 35 | 36 | # 运行serve 37 | ENTRYPOINT [] 38 | CMD ["serve"] -------------------------------------------------------------------------------- /backend/byoc/app_sglang/serve: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Set the directory to check 3 | base_dir="/opt/ml/model/" 4 | file_dir="/tmp/model_file/" 5 | export SAGEMAKER_BIND_TO_PORT=${SAGEMAKER_BIND_TO_PORT:-8080} 6 | export PYTHONPATH=/user/bin 7 | # Check if the directory exists 8 | if [ ! -d "$base_dir" ]; then 9 | echo "Error: $base_dir directory does not exist" 10 | exit 1 11 | fi 12 | 13 | # Find the first subdirectory in the base directory 14 | model_dir=$(find "$base_dir" -mindepth 1 -maxdepth 1 -type d -print -quit) 15 | 16 | # Check if a subdirectory was found 17 | if [ -z "$model_dir" ]; then 18 | echo "No subdirectory found" 19 | exit 0 20 | else 21 | # Set the model_path 22 | model_path="$model_dir" 23 | max_model_len=$(expr $MAX_MODEL_LEN + 0) 24 | max_num_seqs=$(expr $MAX_NUM_SEQS + 0) 25 | tensor_parallel_size=$(expr ${TENSOR_PARALLEL_SIZE:-1} + 0) 26 | dtype=${DTYPE:-auto} 27 | echo "Found model directory" $model_dir 28 | if [ -f "$model_dir/.env" ]; then 29 | source $model_dir/.env 30 | fi 31 | 32 | # Get model_id from environment variable 33 | model_id=$HF_MODEL_ID 34 | s3_model_path=$S3_MODEL_PATH 35 | #检查是否是s3 url 36 | if [[ $s3_model_path == s3://* ]]; then 37 | #检查是否以/结尾,如果是则去掉 38 | if [[ $s3_model_path == */ ]]; then 39 | s3_model_path=${s3_model_path%/} 40 | s3_model_path="${s3_model_path}/*" 41 | else 42 | s3_model_path="${s3_model_path}/*" 43 | fi 44 | #mv s5cmd to app foler 45 | cp $model_dir/s5cmd /app/s5cmd 46 | chmod +x /app/s5cmd 47 | # If model_id is an S3 address, sync files to base_dir 48 | 49 | echo "Syncing files from S3: $s3_model_path" 50 | echo "using: $model_id" 51 | s5cmd sync $s3_model_path $file_dir 52 | # Set model_id to base_dir for local loading 53 | echo "Using model: $file_dir" 54 | 55 | command="python3 -m sglang.launch_server --model-path $file_dir --tp $tensor_parallel_size --dtype $dtype --port $SAGEMAKER_BIND_TO_PORT --host 0.0.0.0 --trust-remote-code" 56 | 57 | 58 | if [ -n "${CHAT_TEMPLATE}" ]; then 59 | command="$command --chat-template=$CHAT_TEMPLATE" 60 | fi 61 | 62 | eval $command 63 | 64 | 65 | else 66 | # If model_id file exists, use its content as model_id 67 | # model_id=$(cat "$model_dir/model_id") 68 | echo "using: $model_id" 69 | 70 | command="python3 -m sglang.launch_server --model-path $model_id --tp $tensor_parallel_size --dtype $dtype --port $SAGEMAKER_BIND_TO_PORT --host 0.0.0.0 --trust-remote-code" 71 | 72 | if [ -n "${CHAT_TEMPLATE}" ]; then 73 | command="$command --chat-template=$CHAT_TEMPLATE" 74 | fi 75 | eval $command 76 | fi 77 | fi 78 | -------------------------------------------------------------------------------- /backend/byoc/build_and_push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -v 3 | set -e 4 | 5 | # This script shows how to build the Docker image and push it to ECR to be ready for use 6 | # by SageMaker. 7 | 8 | # The argument to this script is the region name. 9 | # 尝试使用 IMDSv2 获取 token 10 | TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") 11 | 12 | # Get the current region and write it to the backend .env file 13 | region=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/placement/region) 14 | # region=$(aws configure get region) 15 | suffix="com" 16 | 17 | if [[ $region =~ ^cn ]]; then 18 | suffix="com.cn" 19 | fi 20 | 21 | # Get the account number associated with the current IAM credentials 22 | account=$(aws sts get-caller-identity --query Account --output text) 23 | 24 | VLLM_VERSION=v0.8.4 25 | inference_image=sagemaker_endpoint/vllm 26 | inference_fullname=${account}.dkr.ecr.${region}.amazonaws.${suffix}/${inference_image}:${VLLM_VERSION} 27 | 28 | # If the repository doesn't exist in ECR, create it. 29 | aws ecr describe-repositories --repository-names "${inference_image}" --region ${region} || aws ecr create-repository --repository-name "${inference_image}" --region ${region} 30 | 31 | if [ $? -ne 0 ] 32 | then 33 | aws ecr create-repository --repository-name "${inference_image}" --region ${region} 34 | fi 35 | 36 | # Get the login command from ECR and execute it directly 37 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin $account.dkr.ecr.$region.amazonaws.${suffix} 38 | 39 | aws ecr set-repository-policy \ 40 | --repository-name "${inference_image}" \ 41 | --policy-text "file://ecr-policy.json" \ 42 | --region ${region} 43 | 44 | # Build the docker image locally with the image name and then push it to ECR 45 | # with the full name. 46 | 47 | docker build --build-arg VLLM_VERSION=${VLLM_VERSION} -t ${inference_image}:${VLLM_VERSION} -f Dockerfile . 48 | 49 | docker tag ${inference_image}:${VLLM_VERSION} ${inference_fullname} 50 | 51 | docker push ${inference_fullname} 52 | # 删除 .env 文件中的 vllm_image= 这一行 53 | sed -i '/^vllm_image=/d' /home/ubuntu/llm_model_hub/backend/.env 54 | echo "" >> /home/ubuntu/llm_model_hub/backend/.env 55 | echo "vllm_image=${inference_fullname}" >> /home/ubuntu/llm_model_hub/backend/.env -------------------------------------------------------------------------------- /backend/byoc/build_and_push_sglang.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -v 3 | set -e 4 | 5 | # This script shows how to build the Docker image and push it to ECR to be ready for use 6 | # by SageMaker. 7 | 8 | # The argument to this script is the region name. 9 | # 尝试使用 IMDSv2 获取 token 10 | TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") 11 | 12 | # Get the current region and write it to the backend .env file 13 | region=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/placement/region) 14 | # region=$(aws configure get region) 15 | suffix="com" 16 | 17 | if [[ $region =~ ^cn ]]; then 18 | suffix="com.cn" 19 | fi 20 | 21 | # Get the account number associated with the current IAM credentials 22 | account=$(aws sts get-caller-identity --query Account --output text) 23 | 24 | SGL_VERSION=v0.4.6.post1-cu125 25 | inference_image=sagemaker_endpoint/sglang 26 | inference_fullname=${account}.dkr.ecr.${region}.amazonaws.${suffix}/${inference_image}:${SGL_VERSION} 27 | 28 | # If the repository doesn't exist in ECR, create it. 29 | aws ecr describe-repositories --repository-names "${inference_image}" --region ${region} || aws ecr create-repository --repository-name "${inference_image}" --region ${region} 30 | 31 | if [ $? -ne 0 ] 32 | then 33 | aws ecr create-repository --repository-name "${inference_image}" --region ${region} 34 | fi 35 | 36 | # Get the login command from ECR and execute it directly 37 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin $account.dkr.ecr.$region.amazonaws.${suffix} 38 | 39 | aws ecr set-repository-policy \ 40 | --repository-name "${inference_image}" \ 41 | --policy-text "file://ecr-policy.json" \ 42 | --region ${region} 43 | 44 | # Build the docker image locally with the image name and then push it to ECR 45 | # with the full name. 46 | 47 | docker build --build-arg SGL_VERSION=${SGL_VERSION} -t ${inference_image}:${SGL_VERSION} -f Dockerfile.sglang . 48 | 49 | docker tag ${inference_image}:${SGL_VERSION} ${inference_fullname} 50 | 51 | docker push ${inference_fullname} 52 | # 删除 .env 文件中的 sglang_image= 这一行 53 | sed -i '/^sglang_image=/d' /home/ubuntu/llm_model_hub/backend/.env 54 | echo "" >> /home/ubuntu/llm_model_hub/backend/.env 55 | echo "sglang_image=${inference_fullname}" >> /home/ubuntu/llm_model_hub/backend/.env -------------------------------------------------------------------------------- /backend/byoc/ecr-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2008-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "new statement", 6 | "Effect": "Allow", 7 | "Principal": "*", 8 | "Action": [ 9 | "ecr: CompleteLayerUpload", 10 | "ecr: InitiateLayerUpload", 11 | "ecr: ListImages", 12 | "ecr:BatchCheckLayerAvailability", 13 | "ecr:BatchGetImage", 14 | "ecr:DescribeImages", 15 | "ecr:DescribeRepositories", 16 | "ecr:GetDownloadUrlForLayer" 17 | ] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /backend/byoc/model_tar/env: -------------------------------------------------------------------------------- 1 | # Environment Variables: https://docs.vllm.ai/en/latest/serving/env_vars.html 2 | # export HF_TOKEN="hf_xxx" -------------------------------------------------------------------------------- /backend/byoc/model_tar/s5cmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/byoc/model_tar/s5cmd -------------------------------------------------------------------------------- /backend/byoc/requirements.txt: -------------------------------------------------------------------------------- 1 | transformers >=4.45.0 2 | mistral_common >= 1.4.1 -------------------------------------------------------------------------------- /backend/byoc/startup.py: -------------------------------------------------------------------------------- 1 | import re 2 | import json 3 | import os,dotenv 4 | import boto3 5 | import sagemaker 6 | 7 | dotenv.load_dotenv() 8 | print(os.environ) 9 | def init(): 10 | boto_sess = boto3.Session( 11 | region_name=os.environ.get('region') 12 | ) 13 | sess = sagemaker.session.Session(boto_session=boto_sess) 14 | role = os.environ.get('role') 15 | bucket = sess.default_bucket() 16 | s3_code_prefix = f"sagemaker_endpoint/vllm" 17 | os.system("tar czvf model.tar.gz model_tar/") 18 | model_artifact = sess.upload_data("model.tar.gz", bucket, s3_code_prefix) 19 | print(f"S3 Code or Model tar ball uploaded to --- > {model_artifact}") 20 | 21 | #write code_artifact to .env file 22 | with open("/home/ubuntu/llm_model_hub/backend/.env", "a") as f: 23 | f.write(f"model_artifact={model_artifact}") 24 | 25 | if __name__ == "__main__": 26 | init() 27 | 28 | -------------------------------------------------------------------------------- /backend/byoc/vllm_by_scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # port needs to be 8080 4 | 5 | python3 -m vllm.entrypoints.openai.api_server \ 6 | --port 8080 \ 7 | --trust-remote-code \ 8 | --model deepseek-ai/deepseek-coder-1.3b-instruct -------------------------------------------------------------------------------- /backend/db_management/sqlite_helper.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from pydantic import BaseModel 3 | from typing import Annotated, Sequence, TypedDict, Dict, Optional,List, Any,TypedDict 4 | 5 | class SQLiteHelper(BaseModel): 6 | db_path:str 7 | conn:Any = None 8 | cursor:Any = None 9 | 10 | def open(self): 11 | self.conn = sqlite3.connect(self.db_path,check_same_thread=False) 12 | self.cursor = self.conn.cursor() 13 | 14 | def close(self): 15 | if self.cursor: 16 | self.cursor.close() 17 | if self.conn: 18 | self.conn.close() 19 | 20 | def execute(self, sql, params=None): 21 | if not self.conn: 22 | self.open() 23 | try: 24 | if params: 25 | self.cursor.execute(sql, params) 26 | else: 27 | self.cursor.execute(sql) 28 | self.conn.commit() 29 | except Exception as e: 30 | print(f"Error executing SQL: {e}") 31 | self.conn.rollback() 32 | raise e 33 | 34 | def query(self, sql, params=None): 35 | if not self.conn: 36 | self.open() 37 | try: 38 | if params: 39 | self.cursor.execute(sql, params) 40 | else: 41 | self.cursor.execute(sql) 42 | return self.cursor.fetchall() 43 | except Exception as e: 44 | print(f"Error executing SQL: {e}") 45 | return None 46 | 47 | -------------------------------------------------------------------------------- /backend/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Default use the NVIDIA official image with PyTorch 2.3.0 2 | # https://docs.nvidia.com/deeplearning/frameworks/pytorch-release-notes/index.html 3 | # SageMaker Framework Container Versions 4 | # https://github.com/aws/deep-learning-containers/blob/master/available_images.md 5 | ARG BASE_IMAGE 6 | ARG PIP_INDEX 7 | 8 | # Use the BASE_IMAGE argument 9 | FROM ${BASE_IMAGE} 10 | 11 | # Install system dependencies 12 | RUN apt-get update && apt-get install -y \ 13 | wget \ 14 | git \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | 18 | # Make PIP_INDEX available after FROM 19 | ARG PIP_INDEX 20 | # Install sagemaker-training toolkit that contains the common functionality necessary to create a container compatible with SageMaker and the Python SDK. 21 | RUN pip3 install sagemaker-training 22 | 23 | 24 | 25 | # Install the requirements 26 | COPY requirements_deps.txt /opt/ml/code/ 27 | COPY s5cmd /opt/ml/code/ 28 | COPY train.py /opt/ml/code/ 29 | COPY LLaMA-Factory/ /opt/ml/code/ 30 | 31 | 32 | # Set the working directory 33 | WORKDIR /opt/ml/code/ 34 | RUN pip install "unsloth[cu126-torch260]" 35 | RUN pip config set global.index-url "$PIP_INDEX" && \ 36 | pip config set global.extra-index-url "$PIP_INDEX" && \ 37 | python -m pip install --upgrade pip && \ 38 | python -m pip install -r requirements.txt && pip install -e ".[metrics,bitsandbytes,deepspeed,liger-kernel,awq,qwen,modelscope,swanlab]" 39 | 40 | 41 | 42 | # Instal extral dependencies 43 | RUN python -m pip install -r requirements_deps.txt 44 | RUN pip uninstall -y intel-extension-for-pytorch 45 | 46 | 47 | 48 | # 定义环境变量 49 | ENV PATH="/opt/ml/code:${PATH}" 50 | 51 | WORKDIR / 52 | # Defines train.py as script entrypoint 53 | ENV SAGEMAKER_PROGRAM train.py 54 | -------------------------------------------------------------------------------- /backend/docker/build_and_push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -v 3 | set -e 4 | 5 | # This script shows how to build the Docker image and push it to ECR to be ready for use 6 | # by SageMaker. 7 | 8 | # The argument to this script is the region name. 9 | # 尝试使用 IMDSv2 获取 token 10 | TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") 11 | 12 | # Get the current region and write it to the backend .env file 13 | region=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/placement/region) 14 | # region=$(aws configure get region) 15 | suffix="com" 16 | 17 | if [[ $region =~ ^cn ]]; then 18 | suffix="com.cn" 19 | fi 20 | 21 | # Get the account number associated with the current IAM credentials 22 | account=$(aws sts get-caller-identity --query Account --output text) 23 | 24 | VERSION=0.9.2 25 | inference_image=sagemaker/llamafactory 26 | inference_fullname=${account}.dkr.ecr.${region}.amazonaws.${suffix}/${inference_image}:${VERSION} 27 | 28 | # If the repository doesn't exist in ECR, create it. 29 | aws ecr describe-repositories --repository-names "${inference_image}" --region ${region} || aws ecr create-repository --repository-name "${inference_image}" --region ${region} 30 | 31 | if [ $? -ne 0 ] 32 | then 33 | aws ecr create-repository --repository-name "${inference_image}" --region ${region} 34 | fi 35 | 36 | # Get the login command from ECR and execute it directly 37 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin $account.dkr.ecr.$region.amazonaws.${suffix} 38 | 39 | # First, authenticate with AWS ECR 40 | # Run these commands in your terminal before building: 41 | 42 | if [[ $region =~ ^cn ]]; then 43 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin 727897471807.dkr.ecr.$region.amazonaws.${suffix} 44 | else 45 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin 763104351884.dkr.ecr.$region.amazonaws.${suffix} 46 | fi 47 | 48 | aws ecr set-repository-policy \ 49 | --repository-name "${inference_image}" \ 50 | --policy-text "file://ecr-policy.json" \ 51 | --region ${region} 52 | 53 | # Build the docker image locally with the image name and then push it to ECR 54 | # with the full name. 55 | 56 | # Add variables for build arguments pytorch-training:2.5.1-gpu-py311-cu124-ubuntu22.04-sagemaker 57 | # https://github.com/aws/deep-learning-containers/blob/master/available_images.md 58 | if [[ $region =~ ^cn ]]; then 59 | BASE_IMAGE="727897471807.dkr.ecr.${region}.amazonaws.${suffix}/pytorch-training:2.6.0-gpu-py312-cu126-ubuntu22.04-sagemaker" 60 | PIP_INDEX="https://mirrors.aliyun.com/pypi/simple" 61 | sed -i '/^RUN pip install "unsloth[cu126-torch260]/d' /home/ubuntu/llm_model_hub/backend/docker/Dockerfile 62 | 63 | else 64 | BASE_IMAGE="763104351884.dkr.ecr.${region}.amazonaws.${suffix}/pytorch-training:2.6.0-gpu-py312-cu126-ubuntu22.04-sagemaker" 65 | PIP_INDEX="https://pypi.org/simple" 66 | fi 67 | 68 | 69 | docker build \ 70 | --build-arg BASE_IMAGE="${BASE_IMAGE}" \ 71 | --build-arg PIP_INDEX="${PIP_INDEX}" \ 72 | -t ${inference_image}:${VERSION} . 73 | 74 | docker tag ${inference_image}:${VERSION} ${inference_fullname} 75 | 76 | docker push ${inference_fullname} 77 | # 删除 .env 文件中的 training_image= 这一行 78 | sed -i '/^training_image=/d' /home/ubuntu/llm_model_hub/backend/.env 79 | echo "" >> /home/ubuntu/llm_model_hub/backend/.env 80 | echo "training_image=${inference_fullname}" >> /home/ubuntu/llm_model_hub/backend/.env -------------------------------------------------------------------------------- /backend/docker/ecr-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2008-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "new statement", 6 | "Effect": "Allow", 7 | "Principal": "*", 8 | "Action": [ 9 | "ecr: CompleteLayerUpload", 10 | "ecr: InitiateLayerUpload", 11 | "ecr: ListImages", 12 | "ecr:BatchCheckLayerAvailability", 13 | "ecr:BatchGetImage", 14 | "ecr:DescribeImages", 15 | "ecr:DescribeRepositories", 16 | "ecr:GetDownloadUrlForLayer" 17 | ] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /backend/docker/requirements_deps.txt: -------------------------------------------------------------------------------- 1 | # deepspeed<=0.15.4 2 | # intel-extension-for-pytorch==2.4.0 3 | # autoawq @git+https://github.com/casper-hansen/AutoAWQ.git@79547665bdb27768a9b392ef375776b020acbf0c 4 | # autoawq<=0.2.7 5 | # metrics 6 | # bitsandbytes>=0.39.0 7 | # rouge-chinese 8 | # jieba 9 | peft>=0.11.1 10 | pandas 11 | # modelscope 12 | wandb 13 | # nltk 14 | # swanlab 15 | flash_attn==2.7.3 16 | unsloth 17 | -------------------------------------------------------------------------------- /backend/docker/s5cmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/docker/s5cmd -------------------------------------------------------------------------------- /backend/docker_easyr1/Dockerfile: -------------------------------------------------------------------------------- 1 | # Default use the NVIDIA official image with PyTorch 2.3.0 2 | # https://docs.nvidia.com/deeplearning/frameworks/pytorch-release-notes/index.html 3 | # SageMaker Framework Container Versions 4 | # https://github.com/aws/deep-learning-containers/blob/master/available_images.md 5 | ARG BASE_IMAGE 6 | ARG PIP_INDEX 7 | 8 | # Use the BASE_IMAGE argument 9 | FROM ${BASE_IMAGE} 10 | 11 | # Install system dependencies 12 | RUN apt-get update && apt-get install -y \ 13 | wget \ 14 | git \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | 18 | # Make PIP_INDEX available after FROM 19 | ARG PIP_INDEX 20 | # Install sagemaker-training toolkit that contains the common functionality necessary to create a container compatible with SageMaker and the Python SDK. 21 | RUN pip3 install sagemaker-training boto3 22 | 23 | 24 | 25 | # Install the requirements 26 | COPY requirements_deps.txt /opt/ml/code/ 27 | COPY train.py /opt/ml/code/ 28 | COPY s5cmd /opt/ml/code/ 29 | COPY EasyR1/ /opt/ml/code/ 30 | 31 | # Set the working directory 32 | WORKDIR /opt/ml/code/ 33 | RUN pip config set global.index-url "$PIP_INDEX" && \ 34 | pip config set global.extra-index-url "$PIP_INDEX" && \ 35 | python -m pip install --upgrade pip && \ 36 | pip install -e . && \ 37 | pip install -r requirements_deps.txt 38 | 39 | 40 | 41 | 42 | # 定义环境变量 43 | ENV PATH="/opt/ml/code:${PATH}" 44 | 45 | WORKDIR / 46 | # Defines train.py as script entrypoint 47 | ENV SAGEMAKER_PROGRAM train.py 48 | -------------------------------------------------------------------------------- /backend/docker_easyr1/build_and_push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -v 3 | set -e 4 | 5 | # This script shows how to build the Docker image and push it to ECR to be ready for use 6 | # by SageMaker. 7 | 8 | # The argument to this script is the region name. 9 | # 尝试使用 IMDSv2 获取 token 10 | TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") 11 | 12 | # Get the current region and write it to the backend .env file 13 | region=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/placement/region) 14 | # region=$(aws configure get region) 15 | suffix="com" 16 | 17 | if [[ $region =~ ^cn ]]; then 18 | suffix="com.cn" 19 | fi 20 | 21 | # Get the account number associated with the current IAM credentials 22 | account=$(aws sts get-caller-identity --query Account --output text) 23 | 24 | VERSION=latest 25 | BASE_IMAGE=hiyouga/verl:ngc-th2.6.0-cu126-vllm0.8.3-flashinfer0.2.2-cxx11abi0 26 | inference_image=sagemaker/easyr1 27 | inference_fullname=${account}.dkr.ecr.${region}.amazonaws.${suffix}/${inference_image}:${VERSION} 28 | 29 | # If the repository doesn't exist in ECR, create it. 30 | aws ecr describe-repositories --repository-names "${inference_image}" --region ${region} || aws ecr create-repository --repository-name "${inference_image}" --region ${region} 31 | 32 | if [ $? -ne 0 ] 33 | then 34 | aws ecr create-repository --repository-name "${inference_image}" --region ${region} 35 | fi 36 | 37 | # Get the login command from ECR and execute it directly 38 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin $account.dkr.ecr.$region.amazonaws.${suffix} 39 | 40 | # First, authenticate with AWS ECR 41 | # Run these commands in your terminal before building: 42 | 43 | if [[ $region =~ ^cn ]]; then 44 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin 727897471807.dkr.ecr.$region.amazonaws.${suffix} 45 | else 46 | aws ecr get-login-password --region $region | docker login --username AWS --password-stdin 763104351884.dkr.ecr.$region.amazonaws.${suffix} 47 | fi 48 | 49 | aws ecr set-repository-policy \ 50 | --repository-name "${inference_image}" \ 51 | --policy-text "file://ecr-policy.json" \ 52 | --region ${region} 53 | 54 | # Build the docker image locally with the image name and then push it to ECR 55 | # with the full name. 56 | 57 | # Add variables for build arguments pytorch-training:2.5.1-gpu-py311-cu124-ubuntu22.04-sagemaker 58 | # https://github.com/aws/deep-learning-containers/blob/master/available_images.md 59 | if [[ $region =~ ^cn ]]; then 60 | BASE_IMAGE="727897471807.dkr.ecr.${region}.amazonaws.${suffix}/pytorch-training:2.4.0-gpu-py311" 61 | PIP_INDEX="https://mirrors.aliyun.com/pypi/simple" 62 | 63 | else 64 | BASE_IMAGE="${BASE_IMAGE}" 65 | PIP_INDEX="https://pypi.org/simple" 66 | fi 67 | 68 | 69 | docker build \ 70 | --build-arg BASE_IMAGE="${BASE_IMAGE}" \ 71 | --build-arg PIP_INDEX="${PIP_INDEX}" \ 72 | -t ${inference_image}:${VERSION} . 73 | 74 | docker tag ${inference_image}:${VERSION} ${inference_fullname} 75 | 76 | docker push ${inference_fullname} 77 | # 删除 .env 文件中的 easyr1_training_image= 这一行 78 | sed -i '/^easyr1_training_image=/d' /home/ubuntu/llm_model_hub/backend/.env 79 | echo "" >> /home/ubuntu/llm_model_hub/backend/.env 80 | echo "easyr1_training_image=${inference_fullname}" >> /home/ubuntu/llm_model_hub/backend/.env -------------------------------------------------------------------------------- /backend/docker_easyr1/ecr-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2008-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "new statement", 6 | "Effect": "Allow", 7 | "Principal": "*", 8 | "Action": [ 9 | "ecr: CompleteLayerUpload", 10 | "ecr: InitiateLayerUpload", 11 | "ecr: ListImages", 12 | "ecr:BatchCheckLayerAvailability", 13 | "ecr:BatchGetImage", 14 | "ecr:DescribeImages", 15 | "ecr:DescribeRepositories", 16 | "ecr:GetDownloadUrlForLayer" 17 | ] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /backend/docker_easyr1/requirements_deps.txt: -------------------------------------------------------------------------------- 1 | wandb 2 | swanlab -------------------------------------------------------------------------------- /backend/docker_easyr1/s5cmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/docker_easyr1/s5cmd -------------------------------------------------------------------------------- /backend/env.sample: -------------------------------------------------------------------------------- 1 | AK= 2 | SK= 3 | profile= 4 | region=us-west-2 5 | role=arn:aws:iam:: 6 | db_host=127.0.0.1 7 | db_name=llm 8 | db_user=llmdata 9 | db_password=llmdata 10 | api_keys=123456 11 | HUGGING_FACE_HUB_TOKEN= 12 | WANDB_API_KEY= 13 | WANDB_BASE_URL= 14 | SWANLAB_API_KEY= 15 | vllm_image=.dkr.ecr.us-east-1.amazonaws.com/sagemaker_endpoint/vllm:v0.6.4 16 | model_artifact=s3://sagemaker-us-east-1-/sagemaker_endpoint/vllm/model.tar.gz 17 | training_image=.dkr.ecr.us-east-1.amazonaws.com/llamafactory/llamafactory:0.9.2.dev0 -------------------------------------------------------------------------------- /backend/examples/history.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/examples/history.zip -------------------------------------------------------------------------------- /backend/examples/invoke_endpoint.py: -------------------------------------------------------------------------------- 1 | # !pip install boto3 2 | import re 3 | import json 4 | import boto3 5 | import time 6 | 7 | # 更改成model hub部署的endpoint名称和region name 8 | endpoint_name = "DeepSeek-R1-Distill-Qwen-32B-2025-02-07-07-58-47-569" 9 | region_name = 'us-east-1' 10 | runtime = boto3.client('runtime.sagemaker',region_name=region_name) 11 | payload = { 12 | "messages": [ 13 | { 14 | "role": "user", 15 | "content": "Write a quick sort in python" 16 | } 17 | ], 18 | "max_tokens": 128, 19 | "stream": False 20 | } 21 | 22 | # 非流式 23 | t1 = time.time() 24 | response = runtime.invoke_endpoint( 25 | EndpointName=endpoint_name, 26 | ContentType='application/json', 27 | Body=json.dumps(payload) 28 | ) 29 | t2 = time.time() 30 | body = json.loads(response['Body'].read()) 31 | cost = t2-t1 32 | print(f"cost time:{cost} s") 33 | completion_tokens = body['usage']['completion_tokens'] 34 | print(f"output tokens per s:{completion_tokens/cost}") 35 | print(body["choices"][0]["message"]["content"]) 36 | 37 | 38 | payload = { 39 | "messages": [ 40 | { 41 | "role": "user", 42 | "content": "Write a quick sort in python" 43 | } 44 | ], 45 | "max_tokens": 8000, 46 | "stream": True 47 | } 48 | 49 | # 流式 50 | response = runtime.invoke_endpoint_with_response_stream( 51 | EndpointName=endpoint_name, 52 | ContentType='application/json', 53 | Body=json.dumps(payload) 54 | ) 55 | 56 | buffer = "" 57 | for t in response['Body']: 58 | buffer += t["PayloadPart"]["Bytes"].decode() 59 | last_idx = 0 60 | for match in re.finditer(r'^data:\s*(.+?)(\n\n)', buffer): 61 | try: 62 | data = json.loads(match.group(1).strip()) 63 | last_idx = match.span()[1] 64 | print(data["choices"][0]["delta"]["content"], end="",flush=True) 65 | except (json.JSONDecodeError, KeyError, IndexError) as e: 66 | pass 67 | buffer = buffer[last_idx:] -------------------------------------------------------------------------------- /backend/examples/test_images/demo_pic_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/examples/test_images/demo_pic_1.jpg -------------------------------------------------------------------------------- /backend/examples/test_images/demo_pic_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/examples/test_images/demo_pic_2.jpg -------------------------------------------------------------------------------- /backend/logger_config.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | log_dir = "logs" 5 | 6 | if not os.path.exists(log_dir): 7 | os.makedirs(log_dir) 8 | print(f"目录 '{log_dir}' 已创建") 9 | else: 10 | print(f"目录 '{log_dir}' 已存在,无需创建") 11 | 12 | def setup_logger(name, log_file=None, level=logging.INFO): 13 | """设置日志器""" 14 | 15 | format='%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s' 16 | formatter = logging.Formatter(format) 17 | # 创建处理器:控制台处理器 18 | console_handler = logging.StreamHandler() 19 | console_handler.setFormatter(formatter) 20 | 21 | # 创建日志器,设置日志级别 22 | logger = logging.getLogger(name) 23 | logger.setLevel(level) 24 | logger.addHandler(console_handler) 25 | 26 | # 如果指定了日志文件,添加文件处理器 27 | if log_file: 28 | file_handler = logging.FileHandler(f"{log_dir}/{log_file}") 29 | file_handler.setFormatter(formatter) 30 | logger.addHandler(file_handler) 31 | 32 | return logger 33 | 34 | # 创建一个默认的日志器 35 | default_logger = setup_logger('default') 36 | 37 | # 提供一些便捷的日志记录函数 38 | def debug(msg): 39 | default_logger.debug(msg) 40 | 41 | def info(msg): 42 | default_logger.info(msg) 43 | 44 | def warning(msg): 45 | default_logger.warning(msg) 46 | 47 | def error(msg): 48 | default_logger.error(msg) 49 | 50 | def critical(msg): 51 | default_logger.critical(msg) 52 | -------------------------------------------------------------------------------- /backend/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/model/__init__.py -------------------------------------------------------------------------------- /backend/processing_engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/processing_engine/__init__.py -------------------------------------------------------------------------------- /backend/processing_engine/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | import uuid 3 | import sys 4 | sys.path.append('./') 5 | import json 6 | import logging 7 | import os 8 | from typing import Annotated, Sequence, TypedDict, Dict, Optional,List, Any,TypedDict 9 | import dotenv 10 | dotenv.load_dotenv('.env') 11 | # assert dotenv.load_dotenv('.env') 12 | # print(os.environ) 13 | # from backend.model.data_model import CommonResponse,CreateJobsRequest, \ 14 | # ListJobsRequest,JobInfo,JobType,ListJobsResponse,GetJobsRequest, \ 15 | # JobsResponse,JobStatus,DelJobsRequest 16 | from model.data_model import * 17 | from job_state_machine import JobStateMachine 18 | 19 | from db_management.database import DatabaseWrapper 20 | import threading 21 | from logger_config import setup_logger 22 | from training.jobs import get_job_status 23 | logger = setup_logger('main.py', log_file='processing_engine.log', level=logging.INFO) 24 | 25 | database = DatabaseWrapper() 26 | 27 | def get_submitted_jobs(): 28 | results = database.get_jobs_by_status(JobStatus.SUBMITTED) 29 | return [ret[0] for ret in results] 30 | 31 | def proccessing_job(job_id:str): 32 | logger.info(f"creating job:{job_id}") 33 | job = JobStateMachine.create(job_id) 34 | 35 | try: 36 | if not job.transition(JobStatus.CREATING): 37 | job.transition(JobStatus.ERROR) 38 | logger.info(f"CREATING job failed:{job_id}") 39 | return 40 | 41 | logger.info(f"running job:{job_id}") 42 | if not job.transition(JobStatus.RUNNING): 43 | job.transition(JobStatus.ERROR) 44 | logger.info(f"RUNNING job failed:{job_id}") 45 | return 46 | 47 | job_status = get_job_status(job_id) 48 | logger.info(f"finish running job:{job_id} with status:{job_status}") 49 | job.transition(job_status) 50 | except Exception as e: 51 | job.transition(JobStatus.ERROR) 52 | logger.error(f"RUNNING job failed:{e}") 53 | return True 54 | 55 | def start_processing_engine(): 56 | logger.info("start processing engine...") 57 | processing_threads = {} 58 | while True: 59 | results = get_submitted_jobs() 60 | if results: 61 | logger.info(f"scan job list:{results}") 62 | if results: 63 | for job_id in results: 64 | if job_id not in processing_threads: 65 | thread = threading.Thread(target=proccessing_job,args=(job_id,)) 66 | thread.start() 67 | processing_threads[job_id]=thread 68 | time.sleep(10)# 每10s扫描一次 69 | 70 | if __name__ == '__main__': 71 | start_processing_engine() 72 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | shortuuid 2 | fastapi 3 | uvicorn 4 | pydantic 5 | pytz 6 | transformers>=4.45.0,<=4.51.3,!=4.46.*,!=4.47.*,!=4.48.0 7 | datasets>=2.16.0,<=3.5.0 8 | peft>=0.14.0,<=0.15.1 9 | python-dotenv 10 | mysql-connector-python 11 | sagemaker==2.244.0 12 | PyJWT 13 | sentencepiece 14 | modelscope 15 | tiktoken -------------------------------------------------------------------------------- /backend/scripts/mysql_setup.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS JOB_TABLE ( 2 | id INT AUTO_INCREMENT PRIMARY KEY, 3 | job_id VARCHAR(255), 4 | job_name VARCHAR(255), 5 | job_run_name VARCHAR(255), 6 | output_s3_path TEXT, 7 | job_type VARCHAR(255), 8 | job_status VARCHAR(255), 9 | job_create_time DATETIME, 10 | job_start_time DATETIME, 11 | job_end_time DATETIME, 12 | job_payload TEXT, 13 | ts BIGINT 14 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 15 | 16 | 17 | CREATE TABLE IF NOT EXISTS EP_TABLE ( 18 | id INT AUTO_INCREMENT PRIMARY KEY, 19 | job_id VARCHAR(255), 20 | endpoint_name VARCHAR(255), 21 | model_name VARCHAR(255), 22 | engine VARCHAR(16), 23 | enable_lora BOOLEAN, 24 | instance_type VARCHAR(64), 25 | instance_count INT, 26 | model_s3_path TEXT, 27 | endpoint_status VARCHAR(16), 28 | endpoint_create_time DATETIME, 29 | endpoint_delete_time DATETIME, 30 | extra_config TEXT 31 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 32 | 33 | 34 | CREATE TABLE IF NOT EXISTS USER_TABLE ( 35 | id INT AUTO_INCREMENT PRIMARY KEY, 36 | username VARCHAR(32), 37 | userpwd VARCHAR(32), 38 | groupname VARCHAR(32), 39 | extra_config TEXT 40 | 41 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -------------------------------------------------------------------------------- /backend/training/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/training/__init__.py -------------------------------------------------------------------------------- /backend/training/helper.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('./') 3 | import logging 4 | import json 5 | from typing import Annotated, Sequence, TypedDict, Dict, Optional,List, Any,TypedDict 6 | import threading 7 | from datetime import datetime 8 | import boto3 9 | import os 10 | from utils.config import DATASET_INFO_FILE 11 | 12 | 13 | 14 | # from logger_config import setup_logger 15 | 16 | # logger = setup_logger('helper.py', log_file='training_helper.log', level=logging.INFO) 17 | file_lock = threading.Lock() 18 | def prepare_dataset_info(data_info:Dict[str,Any]): 19 | file_name = DATASET_INFO_FILE 20 | with file_lock: 21 | try: 22 | with open(file_name, 'r') as f: 23 | datainfo = json.load(f) 24 | for key in list(data_info.keys()): 25 | datainfo[key] = data_info[key] 26 | # with open(file_name, 'w') as f: 27 | # json.dump(datainfo, f) 28 | return datainfo 29 | # logger.info('Successfully saved dataset_info.json') 30 | except Exception as e: 31 | print(f'Error in prepare_dataset_info: {str(e)}') 32 | return {} 33 | 34 | 35 | def to_datetime_string(unix_timestamp, format_string="%Y-%m-%d %H:%M:%S.%f"): 36 | # Convert Unix timestamp to datetime object 37 | dt_object = datetime.fromtimestamp(unix_timestamp) 38 | 39 | return dt_object.strftime(format_string) 40 | 41 | # list all s3 objects from given s3 path 42 | def list_s3_objects(s3_url:str) -> List[str]: 43 | if not s3_url: 44 | return [] 45 | boto_sess = boto3.Session( 46 | profile_name=os.environ.get('profile','default'), 47 | region_name=os.environ.get('region') 48 | ) 49 | try: 50 | s3 = boto_sess.client('s3') 51 | bucket, prefix = s3_url.replace("s3://", "").split("/", 1) 52 | response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix) 53 | objects = response.get("Contents", []) 54 | return [obj['Key'].split("/")[-1] for obj in objects] 55 | except Exception as e: 56 | print(f'Error in list_s3_objects: {str(e)}') 57 | raise e 58 | 59 | import re 60 | 61 | def is_valid_s3_uri(uri): 62 | """ 63 | Validate if the given URI is a valid S3 URI. 64 | 65 | Args: 66 | uri (str): The URI to validate. 67 | 68 | Returns: 69 | bool: True if the URI is a valid S3 URI, False otherwise. 70 | """ 71 | # Regular expression pattern for S3 URI 72 | s3_uri_pattern = r'^s3://([^/]+)(/.*)?$' 73 | 74 | # Check if the URI matches the pattern 75 | match = re.match(s3_uri_pattern, uri) 76 | 77 | if match: 78 | bucket_name = match.group(1) 79 | 80 | # Additional checks for bucket name validity 81 | if len(bucket_name) < 3 or len(bucket_name) > 63: 82 | return False 83 | 84 | if not bucket_name[0].isalnum(): 85 | return False 86 | 87 | if not all(c.isalnum() or c in '-.' for c in bucket_name): 88 | return False 89 | 90 | if '..' in bucket_name: 91 | return False 92 | 93 | if bucket_name.endswith('-') or bucket_name.startswith('-'): 94 | return False 95 | 96 | if bucket_name.lower().startswith('xn--'): 97 | return False 98 | 99 | return True 100 | 101 | return False 102 | 103 | if __name__ == "__main__": 104 | # print(list_s3_objects('s3://sagemaker-us-west-2-434444145045/dataset-for-training/train/')) 105 | # Test the function 106 | print(is_valid_s3_uri("s3://sagemaker-us-west-2-434444145045/dataset-for-training/train/")) # True 107 | print(is_valid_s3_uri("s3://my-bucket")) # True 108 | print(is_valid_s3_uri("s3://My-Bucket")) # False (uppercase not allowed) 109 | print(is_valid_s3_uri("s3://my_bucket")) # False (underscore not allowed) 110 | print(is_valid_s3_uri("https://my-bucket.s3.amazonaws.com")) # False (not s3:// protocol) 111 | print(is_valid_s3_uri("s3://a")) # False (bucket name too short) 112 | 113 | -------------------------------------------------------------------------------- /backend/users/add_user.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('./') 3 | 4 | from db_management.database import DatabaseWrapper 5 | from logger_config import setup_logger 6 | import argparse 7 | 8 | database = DatabaseWrapper() 9 | 10 | def add_user(username:str, password:str, groupname:str): 11 | try: 12 | database.add_user(username, password, groupname) 13 | except Exception as e: 14 | print(e) 15 | return False 16 | return True 17 | 18 | if __name__ == "__main__": 19 | parser = argparse.ArgumentParser(description="Add a new user") 20 | parser.add_argument("username", help="Username for the new user") 21 | parser.add_argument("password", help="Password for the new user") 22 | parser.add_argument("groupname", choices=['admin', 'default'], help="User group (admin or default)") 23 | 24 | args = parser.parse_args() 25 | 26 | ret = add_user(args.username, args.password, args.groupname) 27 | if ret: 28 | print(f"添加用户成功:{args.username}/{args.password}/{args.groupname}") 29 | else: 30 | print("添加用户失败") -------------------------------------------------------------------------------- /backend/users/delete_user.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | sys.path.append('./') 4 | from db_management.database import DatabaseWrapper 5 | from logger_config import setup_logger 6 | database = DatabaseWrapper() 7 | 8 | 9 | def delete_user(username:str): 10 | try: 11 | database.delete_user(username) 12 | except Exception as e: 13 | print(e) 14 | return False 15 | return True 16 | 17 | if __name__ == "__main__": 18 | print("请输入需要删除的用户名:") 19 | username = input() 20 | print("确定删除用户吗? Y/N") 21 | confirm = input() 22 | if confirm in ['Y','y']: 23 | ret = delete_user(username) 24 | if ret: 25 | print(f"删除用户成功:{username}") 26 | else: 27 | print("删除用户失败") -------------------------------------------------------------------------------- /backend/users/login.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('./') 3 | import jwt 4 | import os 5 | from datetime import datetime, timedelta,UTC 6 | import json 7 | import logging 8 | from typing import Annotated, Sequence, TypedDict, Dict, Optional,List, Any,TypedDict 9 | 10 | from model.data_model import * 11 | from db_management.database import DatabaseWrapper 12 | from datetime import datetime 13 | from utils.config import boto_sess,role,default_bucket,sagemaker_session,DEFAULT_ENGINE,DEFAULT_REGION 14 | from logger_config import setup_logger 15 | database = DatabaseWrapper() 16 | logger = setup_logger('login.py', log_file='login.log', level=logging.INFO) 17 | 18 | 19 | def create_token(payload): 20 | return jwt.encode( 21 | { 22 | "payload": payload, 23 | "exp": datetime.now(UTC)+ timedelta(hours=168) 24 | }, 25 | os.environ.get("TOKEN_KEY","ssdfdamopwe2"), 26 | algorithm="HS256" 27 | ) 28 | 29 | 30 | def login_auth(username:str,password:str): 31 | logger.info(f"{username} is loging in") 32 | 33 | status = None 34 | error = '' 35 | token = '' 36 | groupname = '' 37 | try: 38 | result = database.query_users(username) 39 | print(result) 40 | if result is None: 41 | logger.info(f"{username} login failed, no user found") 42 | status = False 43 | error = 'login failed, no user found' 44 | else: 45 | pwd,groupname = result 46 | if pwd == password: 47 | logger.info(f"{username} login success") 48 | status = True 49 | token = create_token({"username":username,"group":groupname}) 50 | else: 51 | logger.info(f"{username} login faild, password incorrect") 52 | status = False 53 | error = 'login faild, password incorrect' 54 | 55 | except Exception as e: 56 | logger.error(f"{username} login failed, {e}") 57 | status = False 58 | error = 'login failed due to internal error' 59 | 60 | return { 61 | "status": status, 62 | "error":error, 63 | "isAuthorized":status, 64 | "token": token, 65 | "username":username, 66 | "groupname":groupname, 67 | "company":"default" 68 | } -------------------------------------------------------------------------------- /backend/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/backend/utils/__init__.py -------------------------------------------------------------------------------- /backend/utils/get_factory_config.py: -------------------------------------------------------------------------------- 1 | import time 2 | import uuid 3 | import os 4 | import sys 5 | sys.path.append('./') 6 | import json 7 | import logging 8 | import asyncio 9 | from typing import Annotated, Sequence, TypedDict, Dict, Optional,List, Any,TypedDict 10 | from model.data_model import CommonResponse,ListModelNamesResponse,GetFactoryConfigRequest 11 | from utils.llamafactory.extras.constants import SUPPORTED_MODELS,DEFAULT_TEMPLATE,TRAINING_STAGES,DATA_CONFIG,STAGES_USE_PAIR_DATA,DownloadSource 12 | DEFAULT_DATA_DIR = 'utils/llamafactory/data' 13 | logger = logging.getLogger() 14 | # print(os.listdir()) 15 | class APIException(Exception): 16 | def __init__(self, message, code: str = None): 17 | if code: 18 | super().__init__("[{}] {}".format(code, message)) 19 | else: 20 | super().__init__(message) 21 | 22 | EASY_R1_DATASETS = [ 23 | "hiyouga/math12k@train,hiyouga/math12k@test", 24 | "hiyouga/geometry3k@train,hiyouga/geometry3k@test" 25 | ] 26 | 27 | 28 | def load_dataset_info(dataset_dir: str) -> Dict[str, Dict[str, Any]]: 29 | r""" 30 | Loads dataset_info.json. 31 | """ 32 | if dataset_dir == "ONLINE": 33 | logger.info("dataset_dir is ONLINE, using online dataset.") 34 | return {} 35 | 36 | try: 37 | with open(os.path.join(dataset_dir, DATA_CONFIG), "r", encoding="utf-8") as f: 38 | return json.load(f) 39 | except Exception as err: 40 | logger.warning("Cannot open {} due to {}.".format(os.path.join(dataset_dir, DATA_CONFIG), str(err))) 41 | return {} 42 | 43 | 44 | def list_datasets(dataset_dir: str = None, training_stage: str = list(TRAINING_STAGES.keys())[0]): 45 | """ 46 | Lists all available datasets in the dataset dir for the training stage. 47 | """ 48 | if training_stage == 'grpo': 49 | return EASY_R1_DATASETS 50 | 51 | dataset_info = load_dataset_info(dataset_dir if dataset_dir is not None else DEFAULT_DATA_DIR) 52 | ranking = training_stage in STAGES_USE_PAIR_DATA 53 | datasets = [k for k, v in dataset_info.items() if v.get("ranking", False) == ranking] 54 | return datasets 55 | 56 | def get_model_path_by_name(name:str,repo=DownloadSource.DEFAULT) -> str: 57 | return SUPPORTED_MODELS[name].get(repo,'not exist') 58 | 59 | async def get_factory_config(request:GetFactoryConfigRequest,repo=DownloadSource.DEFAULT) ->CommonResponse: 60 | if request.config_name == 'model_name': 61 | model_names = [{"model_name":name,"model_path":SUPPORTED_MODELS[name].get(repo,'not exist')} for name in list(SUPPORTED_MODELS.keys()) if SUPPORTED_MODELS[name].get(repo) ] 62 | return CommonResponse(response_id=str(uuid.uuid4()),response={"body":model_names}) 63 | elif request.config_name == 'prompt_template': 64 | # templates = list(DEFAULT_TEMPLATE.keys()) 65 | templates = list(DEFAULT_TEMPLATE) 66 | # print(DEFAULT_TEMPLATE) 67 | return CommonResponse(response_id=str(uuid.uuid4()),response={"body":templates}) 68 | elif request.config_name == 'dataset': 69 | datasets = list_datasets(training_stage=request.stage) 70 | return CommonResponse(response_id=str(uuid.uuid4()),response={"body":datasets}) 71 | else: 72 | raise APIException(f"Invalid config_name: {request.config_name}") 73 | 74 | if __name__ == '__main__': 75 | request = GetFactoryConfigRequest(config_name="dataset") 76 | reply = asyncio.run(get_factory_config(request)) 77 | print(reply) -------------------------------------------------------------------------------- /backend/utils/llamafactory/data/dataset_info.json: -------------------------------------------------------------------------------- 1 | ../../../docker/LLaMA-Factory/data/dataset_info.json -------------------------------------------------------------------------------- /backend/utils/llamafactory/extras/constants.py: -------------------------------------------------------------------------------- 1 | ../../../docker/LLaMA-Factory/src/llamafactory/extras/constants.py -------------------------------------------------------------------------------- /backend/utils/llamafactory/hparams/model_args.py: -------------------------------------------------------------------------------- 1 | ../../../docker/LLaMA-Factory/src/llamafactory/hparams/model_args.py -------------------------------------------------------------------------------- /backend/utils/outputs.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from botocore.exceptions import ClientError 3 | from datetime import timedelta, timezone 4 | import re 5 | import dotenv 6 | import os 7 | from .config import boto_sess 8 | dotenv.load_dotenv() 9 | 10 | def list_s3_objects(s3_url:str): 11 | 12 | # Parse the S3 URL 13 | match = re.match(r's3://([^/]+)/?(.*)$', s3_url) 14 | if not match: 15 | raise ValueError("Invalid S3 URL format") 16 | 17 | bucket_name = match.group(1) 18 | prefix = match.group(2) 19 | 20 | # Initialize S3 client 21 | # boto_sess = boto3.Session( 22 | # profile_name=os.environ.get('profile'), 23 | # region_name=os.environ.get('region') 24 | # ) 25 | 26 | s3_client = boto_sess.client('s3') 27 | 28 | result = [] 29 | paginator = s3_client.get_paginator('list_objects_v2') 30 | tz_offset = timezone(timedelta(hours=8)) 31 | try: 32 | for page in paginator.paginate(Bucket=bucket_name, Prefix=prefix, Delimiter='/'): 33 | # Handle folders 34 | for common_prefix in page.get('CommonPrefixes', []): 35 | folder_name = common_prefix['Prefix'].rstrip('/').split('/')[-1].replace('s3://', '') 36 | result.append({ 37 | "Key": folder_name+'/', 38 | "IsFolder": True 39 | }) 40 | 41 | # Handle files 42 | for obj in page.get('Contents', []): 43 | # Skip the prefix itself if it's returned as an object 44 | if obj['Key'] == prefix: 45 | continue 46 | 47 | file_name = obj['Key'].split('/')[-1].replace('s3://', '') 48 | result.append({ 49 | "Key": file_name, 50 | "LastModified": obj['LastModified'].astimezone(tz_offset).strftime("%B %d, %Y, %H:%M:%S (UTC+08:00)"), 51 | "Size": obj['Size'], 52 | "IsFolder": False 53 | }) 54 | 55 | except ClientError as e: 56 | print(f"An error occurred: {e}") 57 | return [] 58 | 59 | return result 60 | 61 | if __name__ == '__main__': 62 | path = 's3://sagemaker-us-west-2-434444145045/Meta-Llama-3-8B-Instruct/' 63 | print(list_s3_objects(path)) -------------------------------------------------------------------------------- /env.sample: -------------------------------------------------------------------------------- 1 | REACT_APP_API_ENDPOINT=http://{ip}:8000/v1 2 | REACT_APP_API_KEY= 3 | REACT_APP_CALCULATOR=https://aws-gpu-memory-caculator.streamlit.app/ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "model_hub_v2", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 7 | "@cloudscape-design/components": "^3.0.660", 8 | "@cloudscape-design/design-tokens": "^3.0.36", 9 | "@cloudscape-design/global-styles": "^1.0.27", 10 | "@emotion/react": "^11.11.4", 11 | "@emotion/styled": "^11.11.5", 12 | "@mui/icons-material": "^5.16.6", 13 | "@mui/lab": "^5.0.0-alpha.173", 14 | "@mui/material": "^5.16.6", 15 | "@testing-library/jest-dom": "^5.17.0", 16 | "@testing-library/react": "^13.4.0", 17 | "@testing-library/user-event": "^13.5.0", 18 | "@types/jest": "^27.5.2", 19 | "@types/node": "^16.18.98", 20 | "@types/react": "^18.3.3", 21 | "@types/react-dom": "^18.3.0", 22 | "@types/react-syntax-highlighter": "^15.5.13", 23 | "axios": "^1.7.4", 24 | "https": "^1.0.0", 25 | "i18next": "^23.12.1", 26 | "i18next-browser-languagedetector": "^8.0.0", 27 | "lodash": "^4.17.21", 28 | "lodash.uniq": "^4.5.0", 29 | "nth-check": "^2.0.1", 30 | "react": "^18.3.1", 31 | "react-dom": "^18.3.1", 32 | "react-i18next": "^14.1.3", 33 | "react-markdown": "^9.0.1", 34 | "react-router-dom": "^6.23.1", 35 | "react-scripts": "^5.0.1", 36 | "react-syntax-highlighter": "^15.5.0", 37 | "remark-gfm": "^4.0.0", 38 | "sass": "^1.77.5", 39 | "typescript": "^4.9.5", 40 | "web-vitals": "^2.1.4" 41 | }, 42 | "scripts": { 43 | "start": "react-scripts start", 44 | "build": "react-scripts build", 45 | "test": "react-scripts test", 46 | "eject": "react-scripts eject" 47 | }, 48 | "eslintConfig": { 49 | "extends": [ 50 | "react-app", 51 | "react-app/jest" 52 | ] 53 | }, 54 | "browserslist": { 55 | "production": [ 56 | ">0.2%", 57 | "not dead", 58 | "not op_mini all" 59 | ], 60 | "development": [ 61 | "last 1 chrome version", 62 | "last 1 firefox version", 63 | "last 1 safari version" 64 | ] 65 | }, 66 | "devDependencies": { 67 | "@types/lodash": "^4.17.5" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /pm2run.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps: [{ 3 | name: "modelhub", 4 | script: "yarn", 5 | args: "start", 6 | interpreter: "node" 7 | }] 8 | } -------------------------------------------------------------------------------- /public/download.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/public/download.webp -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | LLM Model Hub 13 | 14 | 15 | 16 |
17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/modelhublog.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/public/modelhublog.webp -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import './App.css'; 3 | import { 4 | BrowserRouter as Router, 5 | Routes, 6 | Route, 7 | useNavigate, 8 | } from "react-router-dom"; 9 | 10 | import JobTable from './pages/jobs' 11 | import NotFound from './pages/commons/no-found'; 12 | import CreateJobApp from './pages/jobs/create-job'; 13 | import JobDetailApp from './pages/jobs/job-detail'; 14 | import EndpointsTable from './pages/endpoints'; 15 | import ChatBot from './pages/chat/chatmain'; 16 | import { ProvideAuth, useAuthSignout} from "./pages/commons/use-auth"; 17 | import {RequireAuth} from './pages/commons/private-route'; 18 | import LoginPage from "./pages/login/login"; 19 | import {SimpleNotifications} from "./pages/commons/use-notifications"; 20 | 21 | function App() { 22 | return ( 23 |
24 | 25 | 26 | 27 | 28 | } /> 29 | } /> 30 | } /> 31 | } /> 32 | } /> 33 | } /> 34 | } /> 35 | } /> 36 | } /> 37 | }/> 38 | 39 | 40 | 41 | 42 |
43 | ); 44 | } 45 | 46 | function SignOut(){ 47 | const signout = useAuthSignout(); 48 | const navigate = useNavigate(); 49 | useEffect(()=>{ 50 | navigate("/login"); 51 | signout(); 52 | },[]) 53 | return

sign out

; 54 | } 55 | 56 | 57 | export default App; 58 | -------------------------------------------------------------------------------- /src/common/api-gateway.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import axios from 'axios'; 4 | // console.log(process.env) 5 | export const API_ENDPOINT= process.env.REACT_APP_API_ENDPOINT; 6 | export const API_KEY = process.env.REACT_APP_API_KEY; 7 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 8 | 9 | export const remotePost = async(formdata,path,stream=false) =>{ 10 | console.log(formdata); 11 | const headers = {'Content-Type': 'application/json', 12 | 'Authorization': `Bearer ${API_KEY}` 13 | }; 14 | 15 | const args = stream ?{headers:headers,responseType:'stream'} :{headers:headers} 16 | try { 17 | const resp = await axios.post(`${API_ENDPOINT}/${path}`,JSON.stringify(formdata),args); 18 | 19 | if (resp.statusText === 'OK'){ 20 | return stream?resp.body:resp.data 21 | } else{ 22 | console.log(`Server error:${resp.status}`) 23 | throw `Server error:${resp.status}`; 24 | } 25 | 26 | } catch (err) { 27 | throw err; 28 | } 29 | } 30 | 31 | export const fetchPost = async (formData,path) => { 32 | const headers = { 33 | 'Content-Type': 'application/json', 34 | 'Authorization': `Bearer ${API_KEY}` 35 | }; 36 | 37 | try { 38 | const response = await fetch(`${API_ENDPOINT}/${path}`, { 39 | method: 'POST', 40 | headers: headers, 41 | body:JSON.stringify(formData), 42 | redirect: "follow", 43 | }); 44 | return response; 45 | } catch (err) { 46 | throw err; 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/common/api-test.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | 4 | const API_ENDPOINT = `http://127.0.0.1:8000/v1` 5 | const API_KEY = '123456' 6 | const remotePost = async(formdata,path) =>{ 7 | console.log('api:',`${API_ENDPOINT}/${path}`) 8 | const headers = {'Content-Type': 'application/json', 9 | 'Authorization': `Bearer ${API_KEY}` 10 | }; 11 | try { 12 | const resp = await axios.post(`${API_ENDPOINT}/${path}`,JSON.stringify(formdata), {headers}); 13 | return resp.data; 14 | } catch (err) { 15 | throw err; 16 | } 17 | } 18 | 19 | remotePost({"page_size":30,"page_index":1},'list_jobs').then(data => { 20 | console.log(data); 21 | }).catch(err => { 22 | console.log(err); 23 | }) -------------------------------------------------------------------------------- /src/common/apply-mode.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { applyDensity, disableMotion, Density } from '@cloudscape-design/global-styles'; 4 | import '@cloudscape-design/global-styles/index.css'; 5 | import * as localStorage from './localStorage'; 6 | 7 | (window as any).disableMotionForTests = disableMotion; 8 | 9 | // always `true` in this design 10 | export const isVisualRefresh = true; 11 | 12 | export let currentDensity: Density = localStorage.load('Awsui-Density-Preference') ?? Density.Comfortable; 13 | applyDensity(currentDensity); 14 | 15 | export function updateDensity(density: string) { 16 | applyDensity(density as Density); 17 | localStorage.save('Awsui-Density-Preference', density); 18 | currentDensity = density as Density; 19 | } 20 | -------------------------------------------------------------------------------- /src/common/columnDefinitionsHelper.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export const addToColumnDefinitions = (columnDefinitions, propertyName, columns) => 4 | columnDefinitions.map(colDef => { 5 | const column = (columns || []).find(col => col.id === colDef.id); 6 | return { 7 | ...colDef, 8 | [propertyName]: (column && column[propertyName]) || colDef[propertyName], 9 | }; 10 | }); 11 | 12 | export const mapWithColumnDefinitionIds = (columnDefinitions, propertyName, items) => 13 | columnDefinitions.map(({ id }, i) => ({ 14 | id, 15 | [propertyName]: items[i], 16 | })); 17 | -------------------------------------------------------------------------------- /src/common/localStorage.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { CookieConsent } from './types'; 4 | 5 | declare global { 6 | interface Window { 7 | AwsUiConsent: CookieConsent; 8 | } 9 | } 10 | 11 | const hasConsent = () => { 12 | if (typeof window.AwsUiConsent === 'undefined') { 13 | return false; 14 | } 15 | 16 | const cookieConsent = window.AwsUiConsent.getConsentCookie(); 17 | return cookieConsent?.functional ?? false; 18 | }; 19 | 20 | export const save = (key: string, value: any) => { 21 | if (hasConsent()) { 22 | localStorage.setItem(key, JSON.stringify(value)); 23 | } 24 | }; 25 | 26 | export const remove = (key: string) => localStorage.removeItem(key); 27 | 28 | export const load = (key: string) => { 29 | const value = localStorage.getItem(key); 30 | try { 31 | return value && JSON.parse(value); 32 | } catch (e) { 33 | console.warn( 34 | `⚠️ The ${key} value that is stored in localStorage is incorrect. Try to remove the value ${key} from localStorage and reload the page` 35 | ); 36 | return undefined; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /src/common/property-filter-operators.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export const stringOperators = [':', '!:', '=', '!=', '^', '!^']; 4 | -------------------------------------------------------------------------------- /src/common/property-filter/matcher.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const and = (a, b) => a && b; 4 | const or = (a, b) => a || b; 5 | const not = a => !a; 6 | const exact = (value, pattern) => value === pattern; 7 | const partial = (value, pattern) => 8 | typeof pattern === 'string' && typeof value === 'string' && pattern.toLowerCase().indexOf(value.toLowerCase()) > -1; 9 | 10 | export function buildMatcher(tokens, operation) { 11 | if (!tokens.length) { 12 | return () => true; 13 | } 14 | const matchers = tokens.map(({ isFreeText, value, negated, propertyKey }) => { 15 | return item => { 16 | const keys = isFreeText ? Object.keys(item) : [propertyKey]; 17 | const intermediate = keys.some(key => (isFreeText ? partial(value, item[key]) : exact(value, item[key]))); 18 | return negated ? not(intermediate) : intermediate; 19 | }; 20 | }); 21 | const reducer = matchers => { 22 | return item => { 23 | return matchers.reduce( 24 | (acc, matcher) => (operation === 'or' ? or(acc, matcher(item)) : and(acc, matcher(item))), 25 | operation === 'or' ? false : true 26 | ); 27 | }; 28 | }; 29 | return reducer(matchers); 30 | } 31 | -------------------------------------------------------------------------------- /src/common/s3-resource-selector/mock-request.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { amazonS3Data } from './mock-data'; 4 | 5 | amazonS3Data.map(bucket => { 6 | bucket.__objects = [...bucket.__folders, ...bucket.__objects]; 7 | }); 8 | 9 | const randomDelay = (min = 500) => ~~(Math.random() * 500) + min; 10 | 11 | const findItem = (items, name, itemsType) => { 12 | const item = items.filter(item => item.Name === name || item.Key === name)[0]; 13 | if (!item) { 14 | throw `"${name}" ${itemsType.substr(0, itemsType.length - 1)} doesn't exist`; 15 | } 16 | return item; 17 | }; 18 | 19 | const getItemsType = item => { 20 | return Object.keys(item) 21 | .filter(key => key === '__objects' || key === '__versions')[0] 22 | .replace('__', ''); 23 | }; 24 | 25 | export const getItems = (resourceType, bucket, path) => { 26 | const entities = []; 27 | if (bucket) { 28 | entities.push(bucket); 29 | } 30 | if (path && path.length > 0) { 31 | entities.push(...path.split('/')); 32 | } 33 | 34 | let items = amazonS3Data; 35 | let itemsType = 'buckets'; 36 | let item; 37 | 38 | for (const entity of entities) { 39 | try { 40 | item = findItem(items, entity, itemsType); 41 | } catch (e) { 42 | throw new Error(`Resource "s3://${bucket}/${path}" cannot be found: ${e}`); 43 | } 44 | itemsType = getItemsType(item); 45 | if (item.__error) { 46 | const error = new Error(item.__error.content); 47 | error.type = item.__error.type; 48 | error.header = item.__error.header; 49 | throw error; 50 | } 51 | items = item['__' + itemsType]; 52 | } 53 | 54 | return new Promise(resolve => { 55 | setTimeout(() => { 56 | resolve(items); 57 | }, randomDelay()); 58 | }); 59 | }; 60 | 61 | export const requestAsyncAttribute = (item, name) => { 62 | return new Promise(resolve => { 63 | setTimeout(() => { 64 | item[name] = item['__' + name.toLowerCase()]; 65 | resolve(); 66 | }, randomDelay(1000)); 67 | }); 68 | }; 69 | -------------------------------------------------------------------------------- /src/common/types.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | type ConsentCookie = { 4 | advertising: boolean; 5 | essential: boolean; 6 | functional: boolean; 7 | performance: boolean; 8 | }; 9 | 10 | export interface CookieConsent { 11 | checkForCookieConsent: () => void; 12 | getConsentCookie: () => ConsentCookie; 13 | } 14 | -------------------------------------------------------------------------------- /src/common/utils.js: -------------------------------------------------------------------------------- 1 | export function formatDateTime(timestamp) { 2 | const date = new Date(timestamp * 1000); // 将时间戳转换为毫秒 3 | const year = date.getFullYear(); 4 | const month = String(date.getMonth() + 1).padStart(2, '0'); 5 | const day = String(date.getDate()).padStart(2, '0'); 6 | const hours = String(date.getHours()).padStart(2, '0'); 7 | const minutes = String(date.getMinutes()).padStart(2, '0'); 8 | const seconds = String(date.getSeconds()).padStart(2, '0'); 9 | 10 | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; 11 | } -------------------------------------------------------------------------------- /src/fake-server/content-origins.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import fetchJson from './utils/fetch-json'; 4 | import paginate from './utils/paginate'; 5 | import fakeDelay from './utils/fake-delay'; 6 | 7 | export async function fetchContentOrigins({ filteringText, currentPageIndex }) { 8 | const [items] = await Promise.all([fetchJson('./resources/content-origins.json'), fakeDelay()]); 9 | const filteredItems = filteringText ? items.filter(item => item.label.indexOf(filteringText) > -1) : items; 10 | const { paginatedItems, hasNextPage } = paginate(filteredItems, currentPageIndex); 11 | return { origins: paginatedItems, hasNextPage }; 12 | } 13 | -------------------------------------------------------------------------------- /src/fake-server/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export { fetchDistributions, fetchDistributionFilteringOptions } from './distributions'; 4 | export { fetchContentOrigins } from './content-origins'; 5 | export { GetResources, GetTagKeys, GetTagValues } from './tags'; 6 | -------------------------------------------------------------------------------- /src/fake-server/tags.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import fetchJson from './utils/fetch-json'; 4 | import fakeDelay from './utils/fake-delay'; 5 | 6 | // Mimic AWS SDK API method to return a similar shape to be used by the component 7 | 8 | // @see https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_GetResources.html 9 | export async function GetResources() { 10 | const [{ resourceTags }] = await Promise.all([fetchJson('./resources/tags.json'), fakeDelay()]); 11 | return { 12 | ResourceTagMappingList: [ 13 | { 14 | Tags: resourceTags, 15 | }, 16 | ], 17 | }; 18 | } 19 | 20 | // @see https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_GetTagValues.html 21 | export async function GetTagValues(key) { 22 | if (!key) { 23 | return Promise.reject(); 24 | } 25 | const [{ valueMap }] = await Promise.all([fetchJson('./resources/tags.json'), fakeDelay()]); 26 | return { 27 | TagValues: valueMap[key] || [], 28 | }; 29 | } 30 | 31 | // @see https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_GetTagKeys.html 32 | export async function GetTagKeys() { 33 | const [{ valueMap }] = await Promise.all([fetchJson('./resources/tags.json'), fakeDelay()]); 34 | return { 35 | TagKeys: Object.keys(valueMap), 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/fake-server/utils/fake-delay.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export const DEFAULT_DEMO_DELAY = 500; 4 | 5 | export default function fakeDelay(ms = DEFAULT_DEMO_DELAY) { 6 | return new Promise(resolve => setTimeout(resolve, ms)); 7 | } 8 | -------------------------------------------------------------------------------- /src/fake-server/utils/fetch-json.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export default function fetchJson(options) { 4 | return fetch(options).then(response => { 5 | if (!response.ok) { 6 | throw new Error(`Response error: ${response.status}`); 7 | } else { 8 | return response.json(); 9 | } 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/fake-server/utils/paginate.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export default function paginate(items, pageNumber, pageSize = 10) { 4 | const totalPages = Math.ceil(items.length / pageSize); 5 | return { 6 | paginatedItems: items.slice((pageNumber - 1) * pageSize, pageNumber * pageSize), 7 | hasNextPage: pageNumber < totalPages, 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n-strings/app-layout.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { AppLayoutProps } from '@cloudscape-design/components'; 4 | 5 | export const appLayoutAriaLabels: AppLayoutProps.Labels = { 6 | navigation: 'Side navigation', 7 | navigationToggle: 'Open side navigation', 8 | navigationClose: 'Close side navigation', 9 | notifications: 'Notifications', 10 | tools: 'Help panel', 11 | toolsToggle: 'Open help panel', 12 | toolsClose: 'Close help panel', 13 | }; 14 | -------------------------------------------------------------------------------- /src/i18n-strings/collection-preferences.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { CollectionPreferencesProps } from '@cloudscape-design/components'; 4 | 5 | export const contentDisplayPreferenceI18nStrings: Partial = { 6 | liveAnnouncementDndStarted: (position, total) => `Picked up item at position ${position} of ${total}`, 7 | liveAnnouncementDndDiscarded: 'Reordering canceled', 8 | liveAnnouncementDndItemReordered: (initialPosition, currentPosition, total) => 9 | initialPosition === currentPosition 10 | ? `Moving item back to position ${currentPosition} of ${total}` 11 | : `Moving item to position ${currentPosition} of ${total}`, 12 | liveAnnouncementDndItemCommitted: (initialPosition, finalPosition, total) => 13 | initialPosition === finalPosition 14 | ? `Item moved back to its original position ${initialPosition} of ${total}` 15 | : `Item moved from position ${initialPosition} to position ${finalPosition} of ${total}`, 16 | dragHandleAriaDescription: 17 | "Use Space or Enter to activate drag for an item, then use the arrow keys to move the item's position. To complete the position move, use Space or Enter, or to discard the move, use Escape.", 18 | dragHandleAriaLabel: 'Drag handle', 19 | }; 20 | -------------------------------------------------------------------------------- /src/i18n-strings/flashbar.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { FlashbarProps } from '@cloudscape-design/components'; 4 | 5 | export const flashbarI18nStrings: FlashbarProps.I18nStrings = { 6 | ariaLabel: 'Notifications', 7 | notificationBarText: 'Notifications', 8 | notificationBarAriaLabel: 'View all notifications', 9 | errorIconAriaLabel: 'Error', 10 | successIconAriaLabel: 'Success', 11 | warningIconAriaLabel: 'Warning', 12 | infoIconAriaLabel: 'Information', 13 | inProgressIconAriaLabel: 'In progress', 14 | }; 15 | -------------------------------------------------------------------------------- /src/i18n-strings/header.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export const getHeaderCounterText = ( 4 | items: ReadonlyArray, 5 | selectedItems: ReadonlyArray | undefined 6 | ) => { 7 | return selectedItems && selectedItems?.length > 0 ? `(${selectedItems.length}/${items.length})` : `(${items.length})`; 8 | }; 9 | 10 | export const getHeaderCounterServerSideText = (totalCount: number, selectedCount: number | undefined) => { 11 | return selectedCount && selectedCount > 0 ? `(${selectedCount}/${totalCount}+)` : `(${totalCount}+)`; 12 | }; 13 | -------------------------------------------------------------------------------- /src/i18n-strings/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export * from './header'; 4 | export * from './pagination'; 5 | export * from './property-filter'; 6 | export * from './table'; 7 | export * from './text-filter'; 8 | -------------------------------------------------------------------------------- /src/i18n-strings/pagination.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { TableProps } from '@cloudscape-design/components'; 4 | 5 | export const renderAriaLive: TableProps['renderAriaLive'] = ({ firstIndex, lastIndex, totalItemsCount }) => { 6 | return totalItemsCount !== undefined 7 | ? `Displaying items ${firstIndex} to ${lastIndex} of ${totalItemsCount}` 8 | : `Displaying items ${firstIndex} to ${lastIndex}`; 9 | }; 10 | -------------------------------------------------------------------------------- /src/i18n-strings/property-filter.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { PropertyFilterProps } from '@cloudscape-design/components'; 4 | 5 | export const propertyFilterI18nStrings: PropertyFilterProps.I18nStrings = { 6 | filteringAriaLabel: 'Find distributions', 7 | filteringPlaceholder: 'Find distributions', 8 | }; 9 | -------------------------------------------------------------------------------- /src/i18n-strings/s3-resource-selector.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { S3ResourceSelectorProps } from '@cloudscape-design/components'; 4 | 5 | export const s3ResourceSelectorI18nStrings: S3ResourceSelectorProps.I18nStrings = { 6 | inContextInputPlaceholder: 's3://bucket/prefix/object', 7 | inContextInputClearAriaLabel: 'Clear', 8 | inContextSelectPlaceholder: 'Choose a version', 9 | inContextBrowseButton: 'Browse S3', 10 | inContextViewButton: 'View', 11 | inContextLoadingText: 'Loading resource', 12 | inContextUriLabel: 'S3 URI', 13 | inContextVersionSelectLabel: 'Object version', 14 | 15 | modalTitle: 'Choose simulation in S3', 16 | modalCancelButton: 'Cancel', 17 | modalSubmitButton: 'Choose', 18 | modalBreadcrumbRootItem: 'S3 buckets', 19 | 20 | selectionBuckets: 'Buckets', 21 | selectionObjects: 'Objects', 22 | selectionVersions: 'Versions', 23 | selectionBucketsSearchPlaceholder: 'Find bucket', 24 | selectionObjectsSearchPlaceholder: 'Find object by prefix', 25 | selectionVersionsSearchPlaceholder: 'Find version', 26 | selectionBucketsLoading: 'Loading buckets', 27 | selectionBucketsNoItems: 'No buckets', 28 | selectionObjectsLoading: 'Loading objects', 29 | selectionObjectsNoItems: 'No objects', 30 | selectionVersionsLoading: 'Loading versions', 31 | selectionVersionsNoItems: 'No versions', 32 | 33 | filteringCounterText: count => `${count} ${count === 1 ? 'match' : 'matches'}`, 34 | filteringNoMatches: 'No matches', 35 | filteringCantFindMatch: "We can't find a match.", 36 | clearFilterButtonText: 'Clear filter', 37 | 38 | columnBucketName: 'Name', 39 | columnBucketCreationDate: 'Creation date', 40 | columnBucketRegion: 'Region', 41 | columnObjectKey: 'Key', 42 | columnObjectLastModified: 'Last modified', 43 | columnObjectSize: 'Size', 44 | columnVersionID: 'Version ID', 45 | columnVersionLastModified: 'Last modified', 46 | columnVersionSize: 'Size', 47 | 48 | validationPathMustBegin: 'The path must begin with s3://', 49 | validationBucketLowerCase: 'The bucket name must start with a lowercase character or number.', 50 | validationBucketMustNotContain: 'The bucket name must not contain uppercase characters.', 51 | validationBucketMustComplyDns: 'The bucket name must comply with DNS naming conventions', 52 | validationBucketLength: 'The bucket name must be from 3 to 63 characters.', 53 | 54 | labelSortedDescending: columnName => `${columnName}, sorted descending`, 55 | labelSortedAscending: columnName => `${columnName}, sorted ascending`, 56 | labelNotSorted: columnName => `${columnName}, not sorted`, 57 | labelsPagination: { 58 | nextPageLabel: 'Next page', 59 | previousPageLabel: 'Previous page', 60 | pageLabel: pageNumber => `Page ${pageNumber} of all pages`, 61 | }, 62 | labelsBucketsSelection: { 63 | itemSelectionLabel: (data, item) => `${item.Name}`, 64 | selectionGroupLabel: 'Buckets', 65 | }, 66 | labelsObjectsSelection: { 67 | itemSelectionLabel: (data, item) => `${item.Key}`, 68 | selectionGroupLabel: 'Objects', 69 | }, 70 | labelsVersionsSelection: { 71 | itemSelectionLabel: (data, item) => `${item.LastModified}`, 72 | selectionGroupLabel: 'Versions', 73 | }, 74 | labelFiltering: itemsType => `Find ${itemsType}`, 75 | labelRefresh: 'Refresh the data', 76 | labelModalDismiss: 'Dismiss the modal', 77 | labelBreadcrumbs: 'S3 navigation', 78 | }; 79 | -------------------------------------------------------------------------------- /src/i18n-strings/split-panel.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { SplitPanelProps } from '@cloudscape-design/components'; 4 | 5 | export const splitPanelI18nStrings: SplitPanelProps.I18nStrings = { 6 | preferencesTitle: 'Split panel preferences', 7 | preferencesPositionLabel: 'Split panel position', 8 | preferencesPositionDescription: 'Choose the default split panel position for the service.', 9 | preferencesPositionSide: 'Side', 10 | preferencesPositionBottom: 'Bottom', 11 | preferencesConfirm: 'Confirm', 12 | preferencesCancel: 'Cancel', 13 | closeButtonAriaLabel: 'Close panel', 14 | openButtonAriaLabel: 'Open panel', 15 | resizeHandleAriaLabel: 'Resize split panel', 16 | }; 17 | -------------------------------------------------------------------------------- /src/i18n-strings/table.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { TableProps } from '@cloudscape-design/components'; 4 | 5 | export const baseTableAriaLabels: TableProps.AriaLabels = { 6 | allItemsSelectionLabel: () => 'select all', 7 | }; 8 | 9 | const baseEditableLabels: TableProps.AriaLabels<{ id: string }> = { 10 | activateEditLabel: (column, item) => `Edit ${item.id} ${column.header}`, 11 | cancelEditLabel: column => `Cancel editing ${column.header}`, 12 | submitEditLabel: column => `Submit edit ${column.header}`, 13 | }; 14 | 15 | export const distributionTableAriaLabels: TableProps.AriaLabels<{ id: string }> = { 16 | ...baseTableAriaLabels, 17 | itemSelectionLabel: (data, row) => `select ${row.id}`, 18 | selectionGroupLabel: 'Distribution selection', 19 | }; 20 | 21 | export const distributionEditableTableAriaLabels: TableProps.AriaLabels<{ id: string }> = { 22 | ...distributionTableAriaLabels, 23 | ...baseEditableLabels, 24 | }; 25 | 26 | export function createTableSortLabelFn( 27 | column: TableProps.ColumnDefinition 28 | ): TableProps.ColumnDefinition['ariaLabel'] { 29 | if (!column.sortingField && !column.sortingComparator && !column.ariaLabel) { 30 | return; 31 | } 32 | return ({ sorted, descending }) => { 33 | return `${column.header}, ${sorted ? `sorted ${descending ? 'descending' : 'ascending'}` : 'not sorted'}.`; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/i18n-strings/tag-editor.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { TagEditorProps } from '@cloudscape-design/components'; 4 | 5 | export const tagEditorI18nStrings: TagEditorProps.I18nStrings = { 6 | tagLimit: availableTags => `You can add up to ${availableTags} more tag${availableTags > 1 ? 's' : ''}.`, 7 | }; 8 | -------------------------------------------------------------------------------- /src/i18n-strings/text-filter.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export const getTextFilterCounterServerSideText = (items = [], pagesCount: number, pageSize: number) => { 4 | const count = pagesCount > 1 ? `${pageSize * (pagesCount - 1)}+` : items.length + ''; 5 | return count === '1' ? `1 match` : `${count} matches`; 6 | }; 7 | 8 | export const getTextFilterCounterText = (count: number) => `${count} ${count === 1 ? 'match' : 'matches'}`; 9 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot( 8 | document.getElementById('root') as HTMLElement 9 | ); 10 | root.render( 11 | // 12 | 13 | // 14 | ); 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals(); 20 | -------------------------------------------------------------------------------- /src/pages/cards/cards-config.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import { Link, StatusIndicator } from '@cloudscape-design/components'; 5 | 6 | export const CARD_DEFINITIONS = { 7 | header: item => ( 8 |
9 | 10 | {item.id} 11 | 12 |
13 | ), 14 | sections: [ 15 | { 16 | id: 'domainName', 17 | header: 'Domain name', 18 | content: item => item.domainName, 19 | }, 20 | { 21 | id: 'deliveryMethod', 22 | header: 'Delivery method', 23 | content: item => item.deliveryMethod, 24 | }, 25 | { 26 | id: 'sslCertificate', 27 | header: 'SSL certificate', 28 | content: item => item.sslCertificate, 29 | }, 30 | { 31 | id: 'priceClass', 32 | header: 'Price class', 33 | content: item => item.priceClass, 34 | }, 35 | { 36 | id: 'logging', 37 | header: 'Logging', 38 | content: item => item.logging, 39 | }, 40 | { 41 | id: 'origin', 42 | header: 'Origin', 43 | content: item => item.origin, 44 | }, 45 | { 46 | id: 'state', 47 | header: 'State', 48 | content: item => ( 49 | {item.state} 50 | ), 51 | }, 52 | ], 53 | }; 54 | 55 | export const VISIBLE_CONTENT_OPTIONS = [ 56 | { 57 | label: 'Main distribution properties', 58 | options: [ 59 | { id: 'domainName', label: 'Domain name' }, 60 | { id: 'deliveryMethod', label: 'Delivery method' }, 61 | { id: 'sslCertificate', label: 'SSL certificate' }, 62 | { id: 'priceClass', label: 'Price class' }, 63 | { id: 'logging', label: 'Logging' }, 64 | { id: 'origin', label: 'Origin' }, 65 | { id: 'state', label: 'State' }, 66 | ], 67 | }, 68 | ]; 69 | 70 | export const PAGE_SIZE_OPTIONS = [ 71 | { value: 10, label: '10 Distributions' }, 72 | { value: 30, label: '30 Distributions' }, 73 | { value: 50, label: '50 Distributions' }, 74 | ]; 75 | 76 | export const DEFAULT_PREFERENCES = { 77 | pageSize: 30, 78 | visibleContent: ['domainName', 'deliveryMethod', 'state'], 79 | }; 80 | -------------------------------------------------------------------------------- /src/pages/cards/common-components.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import { BreadcrumbGroup, HelpPanel } from '@cloudscape-design/components'; 5 | import { resourcesBreadcrumbs } from '../../common/breadcrumbs'; 6 | import { ExternalLinkGroup } from '../commons'; 7 | 8 | export const Breadcrumbs = () => ( 9 | 10 | ); 11 | 12 | const toolsFooter = ( 13 | 25 | ); 26 | export const ToolsContent = () => ( 27 | Distributions}> 28 |

29 | View your current distributions and related information such as the associated domain names, delivery methods, SSL 30 | certificates, and more. To drill down even further into the details, choose the name of an individual 31 | distribution. 32 |

33 |
34 | ); 35 | -------------------------------------------------------------------------------- /src/pages/chat/chatmain.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useEffect, useRef, useState } from "react"; 4 | import { CustomAppLayout, Navigation } from "../commons/common-components"; 5 | import { Breadcrumbs, chatBreadcrumbs } from '../commons/breadcrumbs' 6 | import { TopNav } from "../commons/top-nav"; 7 | import { useParams } from "react-router-dom"; 8 | 9 | import Content from "./content"; 10 | 11 | const ChatBot = () => { 12 | const appLayout = useRef(null); 13 | const { endpoint } = useParams(); 14 | 15 | 16 | return ( 17 |
18 | 19 | } 22 | breadcrumbs={} 23 | content={ 24 | 25 | } 26 | 27 | contentType="table" 28 | stickyNotifications 29 | /> 30 |
31 | ); 32 | }; 33 | 34 | export default ChatBot; 35 | -------------------------------------------------------------------------------- /src/pages/chat/common-components.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, {createContext, useContext} from 'react'; 4 | import { BreadcrumbGroup, HelpPanel, Icon, Box,Link } from '@cloudscape-design/components'; 5 | import { ExternalLinkItem } from '../commons/common-components'; 6 | // import langString from "../../common/language_string"; 7 | import i18n from '../../common/i18n'; 8 | 9 | 10 | export const ChatDataCtx = createContext({}); 11 | export const useChatData = ()=>{ 12 | return useContext(ChatDataCtx) 13 | } 14 | 15 | export const params_local_storage_key = "chat-params-prompt-panel-"; 16 | 17 | 18 | export function generateUniqueId() { 19 | const timestamp = Date.now(); 20 | const randomNumber = Math.random(); 21 | const hexadecimalString = randomNumber.toString(16).slice(3); 22 | 23 | return `id-${timestamp}-${hexadecimalString}`; 24 | } 25 | 26 | 27 | export const LabelVals =({label,value}:{label:string,value:string})=>{ 28 | return( 29 |
30 | {label} 31 | {value} 32 |
33 | ) 34 | } 35 | 36 | const toolsFooter = ( 37 | <> 38 |

39 | Learn more{' '} 40 | 41 | 42 | 43 |

44 |
    45 |
  • 46 | 50 |
  • 51 |
52 | 53 | ); 54 | export const ToolsContent = () => ( 55 | App}> 56 |

57 | .... 58 |

59 |
60 | ); 61 | -------------------------------------------------------------------------------- /src/pages/chat/content.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useState, useEffect } from "react"; 4 | import { ChatDataCtx } from "./common-components"; 5 | import { 6 | Header, 7 | SpaceBetween, 8 | ContentLayout, 9 | Alert, 10 | } from "@cloudscape-design/components"; 11 | import ConversationsPanel from "./conversations"; 12 | import { useTranslation } from "react-i18next"; 13 | import {params_local_storage_key} from "./common-components"; 14 | import { type ModelParamProps,defaultModelParams } from "./prompt-panel"; 15 | import { remotePost } from "../../common/api-gateway"; 16 | import { useLocalStorage } from '../commons/use-local-storage'; 17 | 18 | const Content = ({endpoint}:{endpoint:string|undefined}) => { 19 | const [modelParams, setModelParams] = useState(defaultModelParams); 20 | const [loading, setLoading] = useState(false); 21 | const [conversations, setConversations] = useState([]); 22 | const [feedBackModalVisible,setFeedBackModalVisible] = useState(false); 23 | const [modalData,setModalData] = useState({}); 24 | const [stopFlag,setStopFlag] = useState(false); 25 | const [newChatLoading, setNewChatLoading] = useState(false); 26 | const { t } = useTranslation(); 27 | const [localStoredMsgItems, setLocalStoredMsgItems] = useLocalStorage( 28 | params_local_storage_key + '-msgitems-'+endpoint, 29 | [] 30 | ); 31 | const [maxConversations, setMaxConversations] = useState(10); 32 | 33 | const [msgItems, setMsgItems] = useState(localStoredMsgItems); 34 | const [endpointName, setEndpointName] = useState(endpoint); 35 | const [modelName, setModelName] = useState(); 36 | const [base64Images, setBase64Images] = useState([]); 37 | 38 | 39 | useEffect(() => { 40 | 41 | const params = { 42 | "page_size": 100, 43 | "page_index": 1, 44 | "query_terms": { "endpoint_name": endpoint } 45 | } 46 | //如果设置了endpoint 47 | endpointName && ( 48 | remotePost(params, 'list_endpoints').then((resp) => { 49 | resp.endpoints.map((item: any) => setModelName(item.model_name)) 50 | }).catch(err => { 51 | console.log(err); 52 | })) 53 | }, [endpoint]); 54 | 55 | useEffect(()=>{ 56 | setModelParams((prev) =>({ 57 | ...prev, 58 | })) 59 | },[]); 60 | 61 | 62 | return ( 63 | 91 | {t("chat")}}> 92 | 93 | 94 | 95 | ); 96 | }; 97 | 98 | export default Content; 99 | -------------------------------------------------------------------------------- /src/pages/commons/breadcrumbs.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import BreadcrumbGroup, { BreadcrumbGroupProps } from '@cloudscape-design/components/breadcrumb-group'; 5 | 6 | export const jobsBreadcrumbs = [ 7 | { 8 | text: 'Jobs', 9 | href: '/jobs', 10 | }, 11 | ]; 12 | 13 | export const createjobBreadcrumbs = [ 14 | ...jobsBreadcrumbs, 15 | { 16 | text: 'Create Job', 17 | href: '/jobs/createjob', 18 | }, 19 | ]; 20 | 21 | export const endpointsBreadcrumbs = [ 22 | { 23 | text: 'Endpoints', 24 | href: '/endpoints', 25 | }, 26 | ]; 27 | 28 | export const chatBreadcrumbs = [ 29 | { 30 | text: 'Playground', 31 | href: '/playground', 32 | }, 33 | ]; 34 | 35 | 36 | export function Breadcrumbs({ items }: { items: BreadcrumbGroupProps['items'] }) { 37 | return ( 38 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /src/pages/commons/common-components.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { forwardRef,createContext ,useContext} from 'react'; 4 | import { AppLayout, AppLayoutProps, Badge, Box, Button, SpaceBetween } from '@cloudscape-design/components'; 5 | 6 | import { I18nProvider } from '@cloudscape-design/components/i18n'; 7 | import enMessages from '@cloudscape-design/components/i18n/messages/all.en.json'; 8 | 9 | interface settingCtxProps { 10 | modelSettingVisible:boolean 11 | setModelSettingVisible:(value:boolean)=>void 12 | } 13 | 14 | const settingCtx = createContext({}); 15 | export const useSettingCtx = ()=>{ 16 | return useContext(settingCtx); 17 | } 18 | 19 | 20 | // backward compatibility 21 | export * from './index'; 22 | export const params_local_storage_key = 'model_hub_params_local_storage_key'; 23 | export const TableNoMatchState = ({ onClearFilter }: { onClearFilter: () => void }) => ( 24 | 25 | 26 |
27 | No matches 28 | 29 | We can't find a match. 30 | 31 |
32 | 33 |
34 |
35 | ); 36 | 37 | export const TableEmptyState = ({ resourceName }: { resourceName: string }) => ( 38 | 39 | 40 |
41 | No {resourceName.toLowerCase()}s 42 | 43 | No {resourceName.toLowerCase()}s associated with this resource. 44 | 45 |
46 | 47 |
48 |
49 | ); 50 | 51 | export const CustomAppLayout = forwardRef((props, ref) => { 52 | return ( 53 | 54 | 55 | 56 | ); 57 | }); 58 | -------------------------------------------------------------------------------- /src/pages/commons/data-provider.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export default class DataProvider { 4 | getData(name) { 5 | return fetch(`./resources/${name}.json`) 6 | .then(response => { 7 | if (!response.ok) { 8 | throw new Error(`Response error: ${response.status}`); 9 | } 10 | return response.json(); 11 | }) 12 | .then(data => data.map(it => ({ ...it, date: new Date(it.date) }))); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/pages/commons/disclaimer-flashbar-item.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useId } from 'react'; 4 | import { FlashbarProps } from '@cloudscape-design/components/flashbar'; 5 | 6 | export function useDisclaimerFlashbarItem(onDismiss: (id: string) => void): FlashbarProps.MessageDefinition | null { 7 | const id = useId(); 8 | 9 | return { 10 | type: 'info', 11 | dismissible: true, 12 | dismissLabel: 'Dismiss message', 13 | onDismiss: () => onDismiss(id), 14 | statusIconAriaLabel: 'info', 15 | content: ( 16 | <> 17 | This demo is an example of Cloudscape Design System patterns and components, and may not reflect the current 18 | patterns and components of AWS services. 19 | 20 | ), 21 | id, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/commons/external-link-group.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useId } from 'react'; 4 | import Icon from '@cloudscape-design/components/icon'; 5 | import Link from '@cloudscape-design/components/link'; 6 | import Container from '@cloudscape-design/components/container'; 7 | import Header from '@cloudscape-design/components/header'; 8 | import { SeparatedList } from './separated-list'; 9 | 10 | interface ExternalLinkItemProps { 11 | href: string; 12 | text: string; 13 | } 14 | 15 | interface ExternalLinkGroupProps { 16 | variant?: 'default' | 'container'; 17 | header?: string; 18 | groupAriaLabel?: string; 19 | items: Array; 20 | } 21 | 22 | export function ExternalLinkItem({ href, text }: ExternalLinkItemProps) { 23 | return ( 24 | 25 | {text} 26 | 27 | ); 28 | } 29 | 30 | export function ExternalLinkGroup({ 31 | header = 'Learn more', 32 | groupAriaLabel, 33 | items, 34 | variant = 'default', 35 | }: ExternalLinkGroupProps) { 36 | const externalIcon = ( 37 | 38 | 39 | 40 | ); 41 | 42 | const headerId = `header-${useId()}`; 43 | 44 | if (variant === 'container') { 45 | return ( 46 | 49 | 50 | {header} {externalIcon} 51 | 52 | 53 | } 54 | > 55 | ( 59 | 60 | ))} 61 | /> 62 | 63 | ); 64 | } 65 | 66 | return ( 67 | <> 68 |

69 | {header} {externalIcon} 70 |

71 |
    72 | {items.map((item, index) => ( 73 |
  • 74 | 75 |
  • 76 | ))} 77 |
78 | 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /src/pages/commons/external-link.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import Link, { LinkProps } from '@cloudscape-design/components/link'; 5 | 6 | export function ExternalLink(props: LinkProps) { 7 | return ; 8 | } 9 | -------------------------------------------------------------------------------- /src/pages/commons/fake-delay.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export default function fakeDelay(delay: number) { 4 | return new Promise(resolve => setTimeout(resolve, delay)); 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/commons/help-panel.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { createContext, useContext } from 'react'; 4 | 5 | const HelpPanelContext = createContext<((newContent: React.ReactNode) => void) | null>(null); 6 | 7 | export const HelpPanelProvider = HelpPanelContext.Provider; 8 | 9 | export function useHelpPanel() { 10 | const ctx = useContext(HelpPanelContext); 11 | if (!ctx) { 12 | throw new Error('Missing HelpPanelProvider'); 13 | } 14 | return ctx; 15 | } 16 | -------------------------------------------------------------------------------- /src/pages/commons/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | export { ExternalLink } from './external-link'; 4 | export { ExternalLinkGroup,ExternalLinkItem } from './external-link-group'; 5 | export { HelpPanelProvider, useHelpPanel } from './help-panel'; 6 | export { InfoLink } from './info-link'; 7 | export { Navigation, navItems } from './navigation'; 8 | export { Notifications,type notificationItemProps } from './notifications'; 9 | export { SeparatedList } from './separated-list'; 10 | -------------------------------------------------------------------------------- /src/pages/commons/info-link.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import Link, { LinkProps } from '@cloudscape-design/components/link'; 5 | 6 | interface InfoLinkProps { 7 | id?: string; 8 | ariaLabel?: string; 9 | onFollow: LinkProps['onFollow']; 10 | } 11 | export const InfoLink = (props: InfoLinkProps) => ( 12 | 13 | Info 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /src/pages/commons/navigation.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import { SideNavigationProps,SideNavigation,Badge} from '@cloudscape-design/components'; 5 | import i18n from '../../common/i18n'; 6 | 7 | const navHeader = { text: 'Service', href: '#/' }; 8 | export const navItems: SideNavigationProps['items'] = [ 9 | { 10 | type: 'section', 11 | text: 'Train Management', 12 | items: [ 13 | { type: 'link', text: 'Training Jobs', href: '/jobs' }, 14 | ], 15 | }, 16 | { 17 | type: 'section', 18 | text: 'Endpoint Management', 19 | items: [ 20 | { type: 'link', text: 'Endpoints', href: '/endpoints' }, 21 | ], 22 | }, 23 | { 24 | type: 'section', 25 | text: 'Playground', 26 | items: [ 27 | { type: 'link', text: 'Chat', href: '/chat' }, 28 | ], 29 | }, 30 | { 31 | type: 'section', 32 | text: i18n.t('readme'), 33 | items: [ 34 | { type: 'link', external: true, 35 | info: 必读, 36 | text: '使用说明', href: 'https://amzn-chn.feishu.cn/docx/QniUdr7FroxShfxeoPacLJKtnXf' }, 37 | ], 38 | }, 39 | ]; 40 | 41 | 42 | interface NavigationProps { 43 | activeHref?: string; 44 | header?: SideNavigationProps['header']; 45 | items?: SideNavigationProps['items']; 46 | } 47 | 48 | export function Navigation({ 49 | activeHref, 50 | header = navHeader, 51 | items = navItems, 52 | }: NavigationProps) { 53 | return ; 54 | } 55 | -------------------------------------------------------------------------------- /src/pages/commons/no-found.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useRef, useState } from "react"; 4 | import { CustomAppLayout, Navigation } from "./common-components"; 5 | import { Header, ContentLayout, Link ,BreadcrumbGroup} from "@cloudscape-design/components"; 6 | 7 | 8 | const breadcrumbsItems = [ 9 | { 10 | text: 'Not ready', 11 | href: '/home', 12 | }, 13 | { 14 | text: 'Not ready', 15 | href: '#', 16 | }, 17 | ]; 18 | 19 | 20 | const Breadcrumbs = () => ( 21 | 22 | ); 23 | 24 | 25 | const NotFound = () => { 26 | const [toolsOpen, setToolsOpen] = useState(false); 27 | const appLayout = useRef(null); 28 | 29 | return ( 30 | } 33 | breadcrumbs={} 34 | content={ 35 | Info}> 38 | Not ready 39 | 40 | } 41 | > 42 |

43 |

Page not ready yet

44 |
45 | } 46 | onToolsChange={({ detail }) => setToolsOpen(detail.open)} 47 | stickyNotifications 48 | /> 49 | ); 50 | }; 51 | 52 | export default NotFound; -------------------------------------------------------------------------------- /src/pages/commons/notifications.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useId, useState } from 'react'; 4 | import Flashbar, { FlashbarProps } from '@cloudscape-design/components/flashbar'; 5 | import { useDisclaimerFlashbarItem } from './disclaimer-flashbar-item'; 6 | 7 | 8 | export interface notificationItemProps { 9 | status: FlashbarProps.Type; 10 | content: string; 11 | } 12 | 13 | 14 | function useNotifications(showSuccessNotification = false, data : notificationItemProps) { 15 | // console.log("useNotifications:",showSuccessNotification,data) 16 | const successId = useId(); 17 | const [successDismissed, dismissSuccess] = useState(false); 18 | // const [disclaimerDismissed, dismissDisclaimer] = useState(false); 19 | 20 | // const disclaimerItem = useDisclaimerFlashbarItem(() => dismissDisclaimer(true)); 21 | 22 | const notifications: Array = []; 23 | 24 | // if (disclaimerItem && !disclaimerDismissed) { 25 | // notifications.push(disclaimerItem); 26 | // } 27 | 28 | if (showSuccessNotification && !successDismissed) { 29 | notifications.push({ 30 | type: data.status, 31 | content: data.content, 32 | statusIconAriaLabel: data.status, 33 | dismissLabel: 'Dismiss message', 34 | dismissible: true, 35 | onDismiss: () => dismissSuccess(true), 36 | id: successId, 37 | }); 38 | } 39 | 40 | return notifications; 41 | } 42 | 43 | export interface NotificationsProps { 44 | successNotification?: boolean; 45 | data:notificationItemProps; 46 | } 47 | 48 | export function Notifications({ successNotification,data }: NotificationsProps) { 49 | 50 | const notifications = useNotifications(successNotification,data); 51 | // console.log(notifications); 52 | return ; 53 | } 54 | -------------------------------------------------------------------------------- /src/pages/commons/private-route.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useAuth } from "./use-auth"; 3 | import { Outlet, Navigate, useLocation } from "react-router-dom"; 4 | import { useLocalStorage } from './use-local-storage'; 5 | const localStoreKey = 'modelhub_credentials' 6 | 7 | // export default function PrivateRoute({ redirectPath }) { 8 | // const auth = useAuth(); 9 | // const isAuthenticated = auth.user ? auth.user.isAuthorized : false; 10 | // // console.log("PrivateRoute:",JSON.stringify(auth)); 11 | // return isAuthenticated ? : ; 12 | // } 13 | 14 | function isTokenExpired(exptime) { 15 | let d1 = new Date(); 16 | let d2 = new Date( 17 | d1.getUTCFullYear(), 18 | d1.getUTCMonth(), 19 | d1.getUTCDate(), 20 | d1.getUTCHours(), 21 | d1.getUTCMinutes(), 22 | d1.getUTCSeconds() 23 | ); 24 | const current = Number(d2) / 1000; 25 | // console.log("current time",current); 26 | return current > exptime; 27 | } 28 | 29 | export function RequireAuth({ children, redirectPath, requireAdmin }) { 30 | //use localstorage 31 | const [local_stored_tokendata] = useLocalStorage(localStoreKey, null); 32 | const location = useLocation(); 33 | 34 | //use context 35 | const auth = useAuth(); 36 | console.log('useAuth()',auth); 37 | //if context is empty, try to use local storage 38 | const user = auth.user? auth.user:local_stored_tokendata; 39 | // console.log(user); 40 | let isAuthenticated = user?user.isAuthorized:false; 41 | 42 | //if it is only granted to admin 43 | if (isAuthenticated && requireAdmin && user.groupname !== 'admin') { 44 | isAuthenticated = false; 45 | } 46 | 47 | if (isAuthenticated) { 48 | return children; 49 | } else return ; 50 | } 51 | -------------------------------------------------------------------------------- /src/pages/commons/separated-list/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import styles from './styles.module.scss'; 5 | 6 | interface SeparatedListProps { 7 | ariaLabel?: string; 8 | ariaLabelledBy?: string; 9 | items: Array; 10 | } 11 | 12 | export function SeparatedList({ ariaLabel, ariaLabelledBy, items }: SeparatedListProps) { 13 | return ( 14 |
    15 | {items.map((item, index) => ( 16 |
  • {item}
  • 17 | ))} 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/commons/separated-list/styles.module.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | @use '~@cloudscape-design/design-tokens' as cs; 5 | 6 | .root { 7 | list-style-type: none; 8 | margin: 0; 9 | padding: 0; 10 | 11 | li { 12 | border-top: 1px solid cs.$color-border-divider-secondary; 13 | padding: 0.8rem 0; 14 | display: flex; 15 | justify-content: space-between; 16 | flex-wrap: wrap; 17 | 18 | &:first-child { 19 | padding-top: 0; 20 | border-top: none; 21 | } 22 | 23 | &:last-child { 24 | padding-bottom: 0; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/pages/commons/top-nav.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useState } from 'react'; 4 | import { TopNavigation, ButtonDropdownProps } from '@cloudscape-design/components'; 5 | 6 | import '../../styles/base.scss'; 7 | import '../../styles/top-navigation.scss'; 8 | import {useAuthUserInfo} from './use-auth'; 9 | 10 | import logo from '../../resources/model-hub-high-resolution-logo-transparent.webp'; 11 | import { useTranslation, Trans } from 'react-i18next'; 12 | import i18n from '../../common/i18n'; 13 | import ModelSettings from './chat-settings'; 14 | 15 | const i18nStrings = { 16 | searchIconAriaLabel: 'Search', 17 | searchDismissIconAriaLabel: 'Close search', 18 | overflowMenuTriggerText: 'More', 19 | overflowMenuTitleText: 'All', 20 | overflowMenuBackIconAriaLabel: 'Back', 21 | overflowMenuDismissIconAriaLabel: 'Close menu', 22 | }; 23 | 24 | const profileActions = [ 25 | { id: 'profile', text: 'Profile' }, 26 | { id: 'preferences', text: 'Preferences' }, 27 | { id: 'security', text: 'Security' }, 28 | { id: 'signout', text: 'Sign out' } 29 | ]; 30 | 31 | 32 | 33 | export const TopNav = () => { 34 | 35 | const userInfo = useAuthUserInfo(); 36 | const {t,i18n} = useTranslation(); 37 | 38 | 39 | const [modelSettingVisible, setModelSettingVisible] = useState(false); 40 | 41 | return ( 42 |
43 | 44 | { 65 | i18n.changeLanguage(detail.id); 66 | window.location.reload(); 67 | }, 68 | items: [ 69 | { 70 | id: "zh", 71 | text: "简体中文" 72 | }, 73 | { 74 | id: "en", 75 | text: "English" 76 | } 77 | ] 78 | }, 79 | { 80 | type: 'menu-dropdown', 81 | text: userInfo.username+"@"+userInfo.groupname, 82 | iconName: 'user-profile', 83 | onItemClick:({detail}) =>{ 84 | if (detail.id === 'profile'){ 85 | setModelSettingVisible(true) 86 | } 87 | }, 88 | items: [{ id: 'profile', text: 'Profile' }, 89 | { id: "signout", text: t('signout'),href: "/signout"} 90 | ], 91 | }, 92 | ]} 93 | /> 94 |
95 | ) 96 | } -------------------------------------------------------------------------------- /src/pages/commons/use-async-data.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { useState, useEffect } from 'react'; 4 | 5 | export function useAsyncData(loadCallback) { 6 | const [items, setItems] = useState([]); 7 | const [loading, setLoading] = useState(true); 8 | useEffect(() => { 9 | let rendered = true; 10 | loadCallback().then(items => { 11 | if (rendered) { 12 | setItems(items); 13 | setLoading(false); 14 | } 15 | }); 16 | return () => { 17 | rendered = false; 18 | }; 19 | }, []); 20 | 21 | return [items, loading]; 22 | } 23 | -------------------------------------------------------------------------------- /src/pages/commons/use-auth.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useState, useEffect, useContext, createContext } from "react"; 4 | import {useLocalStorage} from "./use-local-storage"; 5 | import {remotePost} from '../../common/api-gateway'; 6 | const localStoreKey = 'modelhub_credentials' 7 | const authContext = createContext(); 8 | // Provider component that wraps your app and makes auth object ... 9 | // ... available to any child component that calls useAuth(). 10 | export function ProvideAuth({ children }) { 11 | const auth = useProvideAuth(); 12 | // console.log('ProvideAuth',auth); 13 | return {children}; 14 | } 15 | 16 | export function useAuthSignout () { 17 | const auth = useProvideAuth(); 18 | return auth.signout; 19 | } 20 | 21 | 22 | export function useAuthToken(){ 23 | const auth = useAuth(); 24 | const [local_stored_tokendata] = useLocalStorage(localStoreKey,null); 25 | const user = auth.user?auth.user:local_stored_tokendata; 26 | const token = user.token; 27 | return {'token':'Bearer '+token} 28 | } 29 | 30 | 31 | export function useAuthUserInfo(){ 32 | const auth = useAuth(); 33 | const [local_stored_tokendata] = useLocalStorage(localStoreKey,null) 34 | const user = auth.user?auth.user:local_stored_tokendata; 35 | return { 36 | username:user?user.username:undefined, 37 | company:user?user.company:undefined, 38 | groupname:user?user.groupname:undefined, 39 | token:user?user.token:undefined, 40 | }; 41 | } 42 | 43 | export function useAuthorizedHeader(){ 44 | const auth = useAuth(); 45 | const [local_stored_tokendata] = useLocalStorage(localStoreKey,null) 46 | const authdata = auth.user?auth.user:local_stored_tokendata; 47 | const token = authdata.token; 48 | return { 49 | 'Content-Type':'application/json;charset=utf-8', 50 | 'Authorization':'Bearer '+token 51 | }; 52 | } 53 | 54 | // Hook for child components to get the auth object ... 55 | // ... and re-render when it changes. 56 | export const useAuth = () => { 57 | return useContext(authContext); 58 | }; 59 | 60 | // Provider hook that creates auth object and handles state 61 | function useProvideAuth() { 62 | const [user, setUser] = useState(); 63 | const [,setToken] = useLocalStorage(localStoreKey,null); 64 | // Wrap any Firebase methods we want to use making sure ... 65 | // ... to save the user to state. 66 | const signin = async (email, password) => { 67 | const data = await remotePost({ username: email, password: password }, 'login'); 68 | setToken(data.response); 69 | setUser(data.response); 70 | return data.response; 71 | }; 72 | 73 | const signout = () => { 74 | setToken(null); 75 | return setUser(null); 76 | }; 77 | 78 | const signup =(username,email,password) =>{ 79 | // return remote_signup(username,email,password).then(data => data); 80 | }; 81 | 82 | const confirm_signup = (username,confirmcode)=>{ 83 | // return remote_confirm_signup(username,confirmcode).then(data => data); 84 | }; 85 | 86 | 87 | // Return the user object and auth methods 88 | return { 89 | user, 90 | signin, 91 | signout, 92 | signup, 93 | confirm_signup 94 | 95 | }; 96 | } -------------------------------------------------------------------------------- /src/pages/commons/use-column-widths.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { useMemo } from 'react'; 4 | import { addToColumnDefinitions, mapWithColumnDefinitionIds } from '../../common/columnDefinitionsHelper'; 5 | import { useLocalStorage } from './use-local-storage'; 6 | 7 | export function useColumnWidths(storageKey, columnDefinitions) { 8 | const [widths, saveWidths] = useLocalStorage(storageKey); 9 | 10 | function handleWidthChange(event) { 11 | saveWidths(mapWithColumnDefinitionIds(columnDefinitions, 'width', event.detail.widths)); 12 | } 13 | const memoDefinitions = useMemo(() => { 14 | return addToColumnDefinitions(columnDefinitions, 'width', widths); 15 | }, [widths, columnDefinitions]); 16 | 17 | return [memoDefinitions, handleWidthChange]; 18 | } 19 | -------------------------------------------------------------------------------- /src/pages/commons/use-content-origins.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { useState, useMemo, useRef } from 'react'; 4 | 5 | export default function useContentOrigins() { 6 | const requestParams = useRef({}); 7 | const [options, setOptions] = useState([]); 8 | const [status, setStatus] = useState('finished'); 9 | 10 | async function doRequest({ filteringText, currentPageIndex }) { 11 | setStatus('loading'); 12 | try { 13 | const { origins, hasNextPage } = await window.FakeServer.fetchContentOrigins({ 14 | filteringText, 15 | currentPageIndex, 16 | }); 17 | if (filteringText !== requestParams.current.filteringText) { 18 | return; 19 | } 20 | if (currentPageIndex === 1) { 21 | setOptions(origins); 22 | } else { 23 | setOptions(oldOptions => [...oldOptions, ...origins]); 24 | } 25 | setStatus(hasNextPage ? 'pending' : 'finished'); 26 | } catch (error) { 27 | setStatus('error'); 28 | } 29 | } 30 | 31 | const handlers = useMemo(() => { 32 | return { 33 | onLoadItems({ detail: { filteringText, firstPage, samePage } }) { 34 | if (firstPage) { 35 | requestParams.current = { 36 | filteringText, 37 | currentPageIndex: 1, 38 | }; 39 | setOptions([]); 40 | } else if (!samePage) { 41 | requestParams.current = { 42 | ...requestParams.current, 43 | currentPageIndex: requestParams.current.currentPageIndex + 1, 44 | }; 45 | } 46 | doRequest(requestParams.current); 47 | }, 48 | }; 49 | }, [requestParams]); 50 | return [{ options, filteringText: requestParams.current.filteringText, status }, handlers]; 51 | } 52 | -------------------------------------------------------------------------------- /src/pages/commons/use-id.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { useMemo } from 'react'; 4 | import { v4 as uuid4 } from 'uuid'; 5 | 6 | export const useId = () => useMemo(() => uuid4(), []); 7 | -------------------------------------------------------------------------------- /src/pages/commons/use-local-storage.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | // import { useState } from 'react'; 4 | // import { load, save } from '../../common/localStorage'; 5 | 6 | // export function useLocalStorage(key: string, defaultValue?: T) { 7 | // const [value, setValue] = useState(() => load(key) ?? defaultValue); 8 | 9 | // function handleValueChange(newValue: T) { 10 | // setValue(newValue); 11 | // save(key, newValue); 12 | // } 13 | 14 | // return [value, handleValueChange] as const; 15 | // } 16 | 17 | import { useState } from 'react'; 18 | 19 | export const save = (key: string, value: any): void => { 20 | localStorage.setItem(key, JSON.stringify(value)); 21 | }; 22 | 23 | export const load = (key: string): any | undefined => { 24 | const value = localStorage.getItem(key); 25 | try { 26 | return value && JSON.parse(value); 27 | } catch (e) { 28 | console.warn( 29 | `⚠️ The ${key} value that is stored in localStorage is incorrect. Try to remove the value ${key} from localStorage and reload the page` 30 | ); 31 | return undefined; 32 | } 33 | }; 34 | 35 | export const useLocalStorage = (key: string, defaultValue: T): [T, (newValue: T) => void] => { 36 | const [value, setValue] = useState(() => load(key) ?? defaultValue); 37 | 38 | function handleValueChange(newValue: T): void { 39 | setValue(newValue); 40 | save(key, newValue); 41 | } 42 | 43 | return [value, handleValueChange]; 44 | }; 45 | 46 | -------------------------------------------------------------------------------- /src/pages/commons/use-notifications.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useState, createContext,useContext} from 'react'; 4 | import { useId } from './use-id'; 5 | import { useDisclaimerFlashbarItem } from './disclaimer-flashbar-item'; 6 | import { Flashbar } from '@cloudscape-design/components'; 7 | 8 | const notificationCtx = createContext(); 9 | 10 | export function SimpleNotifications({children}){ 11 | const [notificationitems,setNotificationItems] = useState([]); 12 | return 13 | {children} 14 | 15 | } 16 | 17 | export const useSimpleNotifications = () => { 18 | return useContext(notificationCtx); 19 | }; 20 | 21 | export function useNotifications(successNotification) { 22 | const successId = useId(); 23 | const [successDismissed, dismissSuccess] = useState(false); 24 | const [disclaimerDismissed, dismissDisclaimer] = useState(true); 25 | 26 | const disclaimerItem = useDisclaimerFlashbarItem(() => dismissDisclaimer(false)); 27 | 28 | const notifications = []; 29 | 30 | if (disclaimerItem && !disclaimerDismissed) { 31 | notifications.push(disclaimerItem); 32 | } 33 | 34 | if (successNotification & !successDismissed) { 35 | notifications.push({ 36 | type: 'success', 37 | content: 'Resource created successfully', 38 | statusIconAriaLabel: 'success', 39 | dismissLabel: 'Dismiss message', 40 | dismissible: true, 41 | onDismiss: () => dismissSuccess(true), 42 | id: successId, 43 | }); 44 | } 45 | 46 | return notifications; 47 | } 48 | -------------------------------------------------------------------------------- /src/pages/endpoints/full-page-header.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import { Button, Header, HeaderProps, SpaceBetween } from '@cloudscape-design/components'; 5 | import { t } from 'i18next'; 6 | 7 | interface FullPageHeaderProps extends HeaderProps { 8 | title?: string; 9 | createButtonText?: string; 10 | extraActions?: React.ReactNode; 11 | selectedItemsCount: number; 12 | selectedItems:ReadonlyArray, 13 | setDisplayNotify: (value: boolean) => void; 14 | setNotificationData: (value: any) => void; 15 | onInfoLinkClick?: () => void; 16 | onDelete?: () => void; 17 | onRefresh?: () => void; 18 | onDeploy?: () => void; 19 | onViewCode?: () => void; 20 | } 21 | // || selectedItems[0].endpoint_status !== 'INSERVICE' 22 | export function FullPageHeader({ 23 | title = 'Endpoints', 24 | createButtonText = 'Start Chat', 25 | extraActions = null, 26 | selectedItemsCount, 27 | selectedItems, 28 | setDisplayNotify, 29 | setNotificationData, 30 | onInfoLinkClick, 31 | onDelete, 32 | onRefresh, 33 | onDeploy, 34 | onViewCode, 35 | ...props 36 | }: FullPageHeaderProps) { 37 | // console.log("selectedItems",selectedItems) 38 | return ( 39 |
44 | {extraActions} 45 | 48 | 51 | 54 | 57 | 60 | 61 | } 62 | {...props} 63 | > 64 | {title} 65 |
66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /src/pages/endpoints/hooks.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { useEffect, useState } from 'react'; 4 | import { remotePost } from '../../common/api-gateway'; 5 | export function useDistributions(params = {}) { 6 | const { pageSize, currentPageIndex: clientPageIndex } = params.pagination || {}; 7 | const { sortingDescending, sortingColumn } = params.sorting || {}; 8 | const { filteringText, filteringTokens, filteringOperation } = params.filtering || {}; 9 | const [loading, setLoading] = useState(false); 10 | const [items, setItems] = useState([]); 11 | const [totalCount, setTotalCount] = useState(0); 12 | const [currentPageIndex, setCurrentPageIndex] = useState(clientPageIndex); 13 | const [pagesCount, setPagesCount] = useState(0); 14 | 15 | useEffect(() => { 16 | setCurrentPageIndex(clientPageIndex); 17 | }, [clientPageIndex]); 18 | 19 | useEffect(() => { 20 | setLoading(true); 21 | const params1 = { 22 | filteringText, 23 | filteringTokens, 24 | filteringOperation, 25 | pageSize, 26 | currentPageIndex, 27 | sortingDescending, 28 | ...(sortingColumn 29 | ? { 30 | sortingColumn: sortingColumn.sortingField, 31 | } 32 | : {}), 33 | }; 34 | const controller = new AbortController(); 35 | const params = { 36 | "page_size":pageSize, 37 | "page_index":currentPageIndex 38 | } 39 | remotePost(params,'list_endpoints').then((res) => { 40 | // console.log(res); 41 | setLoading(false); 42 | setItems(res.endpoints); 43 | setPagesCount(Math.ceil(res.total_count/pageSize)); 44 | setCurrentPageIndex(currentPageIndex); 45 | setTotalCount(res.total_count); 46 | }).catch((error) => { 47 | console.log(error); 48 | setLoading(false); 49 | setItems([]); 50 | }); 51 | 52 | return ()=>{ 53 | controller.abort(); 54 | } 55 | }, [ 56 | pageSize, 57 | sortingDescending, 58 | sortingColumn, 59 | currentPageIndex, 60 | filteringText, 61 | filteringTokens, 62 | filteringOperation, 63 | params.refresh 64 | ]); 65 | 66 | return { 67 | items, 68 | loading, 69 | totalCount, 70 | pagesCount, 71 | currentPageIndex, 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /src/pages/jobs/create-job/components/code-editor.jsx: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from "react"; 2 | import 'ace-builds/css/ace.css'; 3 | import 'ace-builds/css/theme/cloud_editor.css'; 4 | import 'ace-builds/css/theme/cloud_editor_dark.css'; 5 | import { 6 | CodeEditor 7 | } from "@cloudscape-design/components"; 8 | 9 | export const JsonEditor = (props) =>{ 10 | const [preferences, setPreferences] = useState( 11 | undefined 12 | ); 13 | const [ace, setAce] = useState(); 14 | const [loading, setLoading] = useState(true); 15 | // console.log(props) 16 | useEffect(() => { 17 | async function loadAce() { 18 | const ace = await import('ace-builds'); 19 | await import('ace-builds/webpack-resolver'); 20 | ace.config.set('useStrictCSP', true); 21 | return ace; 22 | } 23 | 24 | loadAce() 25 | .then(ace => setAce(ace)) 26 | .finally(() => setLoading(false)); 27 | }, []); 28 | return setPreferences(e.detail)} 32 | loading={loading} 33 | ace={ace} 34 | language="json" 35 | themes={{ 36 | light: ["cloud_editor"], 37 | dark: ["cloud_editor_dark"] 38 | }} 39 | /> 40 | } 41 | 42 | export const PythonEditor = (props) =>{ 43 | const [preferences, setPreferences] = useState( 44 | undefined 45 | ); 46 | const [ace, setAce] = useState(); 47 | const [loading, setLoading] = useState(true); 48 | // console.log(props) 49 | useEffect(() => { 50 | async function loadAce() { 51 | const ace = await import('ace-builds'); 52 | await import('ace-builds/webpack-resolver'); 53 | ace.config.set('useStrictCSP', true); 54 | return ace; 55 | } 56 | 57 | loadAce() 58 | .then(ace => setAce(ace)) 59 | .finally(() => setLoading(false)); 60 | }, []); 61 | return setPreferences(e.detail)} 65 | loading={loading} 66 | ace={ace} 67 | language="python" 68 | themes={{ 69 | light: ["cloud_editor"], 70 | dark: ["cloud_editor_dark"] 71 | }} 72 | /> 73 | } -------------------------------------------------------------------------------- /src/pages/jobs/create-job/components/output-path.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useEffect, useState,useCallback,useRef } from 'react'; 4 | import { remotePost } from '../../../../common/api-gateway'; 5 | import FormField from "@cloudscape-design/components/form-field"; 6 | import Container from '@cloudscape-design/components/container'; 7 | import Header from "@cloudscape-design/components/header"; 8 | import S3ResourceSelector from "@cloudscape-design/components/s3-resource-selector"; 9 | import Alert from "@cloudscape-design/components/alert"; 10 | 11 | function SelfDismissibleAlert(props) { 12 | const [visible, setVisible] = useState(true); 13 | return (visible&& setVisible(false)} />); 14 | } 15 | 16 | 17 | export const S3Path = ({outputPath,label}) => { 18 | return {label}} > 21 | 22 | 27 | 28 | 29 | 30 | } 31 | export const S3Selector = ({outputPath,objectsIsItemDisabled,setOutputPath}) => { 32 | const [fetchError, setFetchError] = useState(null); 33 | 34 | const [resource, setResource] = useState({ 35 | uri: outputPath||""//.replace('s3://','') 36 | }); 37 | 38 | const onFetchObjects = async (bucketName, pathPrefix) => { 39 | // console.log(`bucketName:${bucketName},pathPrefix:${pathPrefix}`) 40 | try{ 41 | // const pathPrefixNew = pathPrefix.endsWith('/') ? pathPrefix : pathPrefix + '/'; 42 | const resp = await remotePost({"output_s3_path":bucketName+pathPrefix}, 'list_s3_path'); 43 | const objects = await resp.objects; 44 | // console.log('objects:',objects); 45 | return Promise.resolve(objects) 46 | }catch(err){ 47 | console.log(err); 48 | setFetchError(err.message); 49 | return Promise.resolve([]) 50 | } 51 | }; 52 | 53 | return ( 54 | { 56 | const uri = detail.resource.uri.replace('s3://s3://','s3://') 57 | const uri_new = uri.endsWith('/') ? uri : uri + '/'; 58 | // setResource(detail.resource); 59 | setResource({uri:uri_new}); 60 | setOutputPath&&setOutputPath(uri_new); 61 | 62 | } 63 | } 64 | alert={ 65 | fetchError && ( 66 | 67 | {fetchError} 68 | 69 | ) 70 | } 71 | objectsIsItemDisabled={objectsIsItemDisabled} 72 | resource={resource} 73 | // objectsIsItemDisabled={item => item.IsFolder} 74 | selectableItemsTypes={[ 75 | "buckets", 76 | "objects", 77 | "version", 78 | ]} 79 | fetchVersions={() => new Promise(() => {})} 80 | bucketsVisibleColumns={["Name"]} 81 | fetchObjects={onFetchObjects} 82 | fetchBuckets={() => 83 | Promise.resolve([ 84 | { 85 | Name: resource.uri.replace('s3://s3://','s3://') 86 | } 87 | ]) 88 | } 89 | /> 90 | 91 | ) 92 | } -------------------------------------------------------------------------------- /src/pages/jobs/create-job/form-validation-config.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import uniq from 'lodash/uniq'; 4 | import { SelectProps } from '@cloudscape-design/components'; 5 | 6 | type FormDataAttributes = 7 | | 'cloudFrontRootObject' 8 | | 'alternativeDomainNames' 9 | | 's3BucketSelectedOption' 10 | | 'certificateExpiryDate' 11 | | 'certificateExpiryTime' 12 | | 'httpVersion' 13 | | 'ipv6isOn' 14 | | 'functions' 15 | | 'originId' 16 | | 'customHeaders'; 17 | 18 | const validateEmpty = (value: string | undefined | null | File[]) => Boolean(value && value.length > 0); 19 | 20 | 21 | const validateS3Bucket = (value: string) => { 22 | return !value.includes('NO-ACCESS'); 23 | }; 24 | 25 | 26 | 27 | 28 | const validateNumers = (value: string) => { 29 | const numberRegex = new RegExp(/[^0-9]/gm); 30 | const isValid = !numberRegex.test(value); 31 | return isValid; 32 | }; 33 | 34 | 35 | type ValidationFunction = (value: any) => boolean; 36 | type ValidationText = string | ((value: string) => string); 37 | 38 | const validationConfig: Record< 39 | string, 40 | Array<{ validate: ValidationFunction; errorText?: ValidationText; warningText?: ValidationText }> 41 | > = { 42 | job_name: [{ validate: validateEmpty, errorText: 'Job name is required.' }], 43 | model_name: [{ validate: validateEmpty, errorText: 'Model name is required.' }], 44 | prompt_template: [{ validate: validateEmpty, errorText: 'Prompt template name is required.' }], 45 | job_type: [{ validate: validateEmpty, errorText: 'Finetune type is required.' }], 46 | // dataset:[{ validate: validateEmpty, errorText: 'Dataset is required.' }], 47 | datasetInfo:[{ validate: validateEmpty, errorText: 'DatasetInfo is required.' }], 48 | stage: [{ validate: validateEmpty, errorText: 'Training Stage type is required.' }], 49 | instance_type: [{ validate: validateEmpty, errorText: 'Instance type is required.' }], 50 | // instance_num: [{ validate: validateEmpty, errorText: 'Instance amount is required.' }, 51 | // {validate:validateNumers,errorText: 'Only integer is supported.' } 52 | // ], 53 | s3BucketSelectedOption: [ 54 | { 55 | validate: (selectedOption: SelectProps.Option) => validateEmpty(selectedOption?.value), 56 | errorText: 'S3 bucket is required.', 57 | }, 58 | { 59 | validate: (selectedOption: SelectProps.Option) => validateS3Bucket(selectedOption?.label || ''), 60 | errorText: 61 | "Model Hub isn't allowed to access to this bucket. You must enable access control lists (ACL) for the bucket.", 62 | }, 63 | ], 64 | }; 65 | 66 | export default function validateField(attribute: FormDataAttributes, value: any, customValue: string = value) { 67 | const validations = validationConfig[attribute]; 68 | // console.log('validations', attribute,validations); 69 | if(validations){ 70 | for (const validation of validations) { 71 | const { validate, errorText, warningText } = validation; 72 | 73 | const isValid = validate(value); 74 | if (!isValid) { 75 | return { 76 | errorText: typeof errorText === 'function' ? errorText(customValue) : errorText, 77 | warningText: typeof warningText === 'function' ? warningText(customValue) : warningText, 78 | }; 79 | } 80 | } 81 | } 82 | return { errorText: null }; 83 | } 84 | -------------------------------------------------------------------------------- /src/pages/jobs/create-r1-job/index.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useRef, useState } from 'react'; 4 | // import { createRoot } from 'react-dom/client'; 5 | import { CustomAppLayout, Navigation, Notifications } from '../../commons/common-components'; 6 | import { Breadcrumbs, createjobBreadcrumbs } from '../../commons/breadcrumbs' 7 | 8 | import { FormWithValidation } from './components/form'; 9 | // import ToolsContent from './components/tools-content'; 10 | import '../../../styles/form.scss'; 11 | 12 | const datasetInfoExample = `{"your_dataset_key1": 13 | { 14 | "file_name":"your_dataset_name.json", 15 | "columns": { 16 | "prompt": "instruction", 17 | "query": "input", 18 | "response": "output" 19 | } 20 | }, 21 | "your_dataset_key2": 22 | { 23 | "file_name":"your_dataset_name_2.json" 24 | } 25 | }` 26 | 27 | const defaultData = { 28 | model_name: null, 29 | prompt_template: null, 30 | job_type: 'lora', 31 | job_name: '', 32 | quantization_bit: 'none', 33 | finetuning_method: 'lora', 34 | stage: 'Supervised Fine-Tuning', 35 | learning_rate: '5e-5', 36 | per_device_train_batch_size: 2, 37 | gradient_accumulation_steps: 4, 38 | num_train_epochs: 2.0, 39 | training_precision: 'bf16', 40 | max_samples: 50000, 41 | cutoff_len: 1024, 42 | val_size: 0.05, 43 | logging_steps: 10, 44 | warmup_steps: 10, 45 | save_steps: 500, 46 | optimizer: 'adamw_torch', 47 | lora_rank: 8, 48 | lora_alpha: 16, 49 | instance_type: null, 50 | instance_num: 1, 51 | datasetInfo: datasetInfoExample, 52 | booster_option: 'auto', 53 | deepspeed: 'none', 54 | s3_checkpoint:'', 55 | s3_model_path:'', 56 | use_spot: false, 57 | max_spot_wait:72, 58 | max_job_run_hour:48, 59 | lora_target_modules:'all', 60 | pref_loss:'sigmoid', 61 | pref_beta:0.1, 62 | pref_ftx:0 63 | }; 64 | 65 | 66 | const CreateJobApp = () => { 67 | const [toolsIndex, setToolsIndex] = useState(0); 68 | const [toolsOpen, setToolsOpen] = useState(false); 69 | const appLayout = useRef(); 70 | const [notificationData, setNotificationData] = useState({}); 71 | const [displayNotify, setDisplayNotify] = useState(false); 72 | const [data, _setData] = useState(defaultData); 73 | const [loading, setLoading] = useState(false); 74 | 75 | const loadHelpPanelContent = index => { 76 | setToolsIndex(index); 77 | setToolsOpen(true); 78 | appLayout.current?.focusToolsClose(); 79 | }; 80 | 81 | return ( 82 | 95 | } 96 | breadcrumbs={} 97 | navigation={} 98 | // tools={ToolsContent[toolsIndex]} 99 | toolsOpen={toolsOpen} 100 | onToolsChange={({ detail }) => setToolsOpen(detail.open)} 101 | notifications={} 104 | /> 105 | ); 106 | } 107 | 108 | 109 | export default CreateJobApp; 110 | // createRoot(document.getElementById('app')).render(); 111 | -------------------------------------------------------------------------------- /src/pages/jobs/full-page-header.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React from 'react'; 4 | import { Button, Header, HeaderProps, SpaceBetween } from '@cloudscape-design/components'; 5 | import { t } from 'i18next'; 6 | 7 | interface FullPageHeaderProps extends HeaderProps { 8 | title?: string; 9 | createButtonText?: string; 10 | extraActions?: React.ReactNode; 11 | selectedItemsCount: number; 12 | selectedItems:ReadonlyArray, 13 | setDisplayNotify: (value: boolean) => void; 14 | setNotificationData: (value: any) => void; 15 | onInfoLinkClick?: () => void; 16 | onDelete?: () => void; 17 | onRefresh?: () => void; 18 | onDeploy?:()=>void; 19 | } 20 | 21 | export function FullPageHeader({ 22 | title = 'Jobs', 23 | createButtonText = 'Create Job', 24 | extraActions = null, 25 | selectedItemsCount, 26 | selectedItems, 27 | setDisplayNotify, 28 | setNotificationData, 29 | onInfoLinkClick, 30 | onDelete, 31 | onRefresh, 32 | onDeploy, 33 | ...props 34 | }: FullPageHeaderProps) { 35 | const isOnlyOneSelected = selectedItemsCount === 1; 36 | const selectedItem = selectedItems&&selectedItems[0]; 37 | return ( 38 |
} 41 | actions={ 42 | 43 | {extraActions} 44 | 47 | 50 | {/* */} 53 | 56 | 59 | 60 | } 61 | {...props} 62 | > 63 | {title} 64 |
65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /src/pages/jobs/hooks.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { useEffect, useState } from 'react'; 4 | import { remotePost } from '../../common/api-gateway'; 5 | export function useDistributions(params = {}) { 6 | const { pageSize, currentPageIndex: clientPageIndex } = params.pagination || {}; 7 | const { sortingDescending, sortingColumn } = params.sorting || {}; 8 | const { filteringText, filteringTokens, filteringOperation } = params.filtering || {}; 9 | const [loading, setLoading] = useState(false); 10 | const [items, setItems] = useState([]); 11 | const [totalCount, setTotalCount] = useState(0); 12 | const [currentPageIndex, setCurrentPageIndex] = useState(clientPageIndex); 13 | const [pagesCount, setPagesCount] = useState(0); 14 | useEffect(() => { 15 | setCurrentPageIndex(clientPageIndex); 16 | }, [clientPageIndex]); 17 | 18 | useEffect(() => { 19 | setLoading(true); 20 | const params1 = { 21 | filteringText, 22 | filteringTokens, 23 | filteringOperation, 24 | pageSize, 25 | currentPageIndex, 26 | sortingDescending, 27 | ...(sortingColumn 28 | ? { 29 | sortingColumn: sortingColumn.sortingField, 30 | } 31 | : {}), 32 | }; 33 | const controller = new AbortController(); 34 | 35 | const params = { 36 | "page_size":pageSize, 37 | "page_index":currentPageIndex 38 | }; 39 | remotePost(params,'list_jobs').then((res) => { 40 | setLoading(false); 41 | setItems(res.jobs); 42 | setPagesCount(Math.ceil(res.total_count/pageSize)); 43 | setCurrentPageIndex(currentPageIndex); 44 | setTotalCount(res.total_count); 45 | }).catch((error) => { 46 | console.log(error); 47 | setLoading(false); 48 | setItems([]); 49 | }); 50 | 51 | return ()=>{ 52 | controller.abort(); 53 | } 54 | }, [ 55 | pageSize, 56 | sortingDescending, 57 | sortingColumn, 58 | currentPageIndex, 59 | filteringText, 60 | filteringTokens, 61 | filteringOperation, 62 | params.refresh 63 | ]); 64 | 65 | return { 66 | items, 67 | loading, 68 | totalCount, 69 | pagesCount, 70 | currentPageIndex, 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /src/pages/jobs/job-detail.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useEffect, useRef, useState } from 'react'; 4 | // import { createRoot } from 'react-dom/client'; 5 | import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components'; 6 | import {Breadcrumbs,createjobBreadcrumbs} from '../commons/breadcrumbs' 7 | 8 | import { FormWithValidation} from './create-job/components/form'; 9 | import { useNavigate,useParams } from "react-router-dom"; 10 | import { remotePost } from '../../common/api-gateway'; 11 | import { 12 | Spinner 13 | } from "@cloudscape-design/components"; 14 | import '../../styles/form.scss'; 15 | 16 | 17 | const JobDetailApp =() => { 18 | const { id } = useParams(); 19 | const [toolsIndex, setToolsIndex] = useState(0); 20 | const [toolsOpen, setToolsOpen] = useState(false); 21 | const appLayout = useRef(); 22 | const [notificationData, setNotificationData] = useState({}); 23 | const [displayNotify, setDisplayNotify] = useState(false); 24 | const navigate = useNavigate(); 25 | const [data, _setData] = useState(); 26 | const [readOnly, setReadOnly] = useState(true); 27 | const [loading, setLoading] = useState(false); 28 | 29 | const loadHelpPanelContent = index => { 30 | setToolsIndex(index); 31 | setToolsOpen(true); 32 | appLayout.current?.focusToolsClose(); 33 | }; 34 | 35 | useEffect(()=>{ 36 | const controller = new AbortController(); 37 | remotePost({"job_id":id},'get_job') 38 | .then((res)=>{ 39 | console.log(res.body); 40 | _setData(res.body); 41 | }).catch(err=>console.log(err)); 42 | 43 | return ()=>{ 44 | controller.abort(); 45 | } 46 | 47 | },[]); 48 | 49 | return ( 50 | } 65 | />:( 66 | 67 | ) 68 | } 69 | breadcrumbs={} 70 | navigation={} 71 | // tools={ToolsContent[toolsIndex]} 72 | toolsOpen={toolsOpen} 73 | onToolsChange={({ detail }) => setToolsOpen(detail.open)} 74 | notifications={} 77 | /> 78 | ); 79 | } 80 | 81 | 82 | export default JobDetailApp; 83 | -------------------------------------------------------------------------------- /src/pages/table-property-filter/index.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useRef, useState } from 'react'; 4 | import { createRoot } from 'react-dom/client'; 5 | 6 | import { useColumnWidths } from '../commons/use-column-widths'; 7 | import { useLocalStorage } from '../commons/use-local-storage'; 8 | import { Breadcrumbs, ToolsContent } from '../table/common-components'; 9 | import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components'; 10 | import { FILTERING_PROPERTIES } from './table-property-filter-config'; 11 | import { COLUMN_DEFINITIONS, DEFAULT_PREFERENCES } from '../commons/table-config'; 12 | import { PropertyFilterTable } from './property-filter-table'; 13 | import '../../styles/base.scss'; 14 | 15 | function App() { 16 | const [columnDefinitions, saveWidths] = useColumnWidths('React-TablePropertyFilter-Widths', COLUMN_DEFINITIONS); 17 | const [preferences, setPreferences] = useLocalStorage('React-TablePropertyFilter-Preferences', DEFAULT_PREFERENCES); 18 | const [toolsOpen, setToolsOpen] = useState(false); 19 | const appLayout = useRef(); 20 | 21 | return ( 22 | } 25 | notifications={} 26 | breadcrumbs={} 27 | content={ 28 | { 30 | setToolsOpen(true); 31 | appLayout.current?.focusToolsClose(); 32 | }} 33 | columnDefinitions={columnDefinitions} 34 | saveWidths={saveWidths} 35 | preferences={preferences} 36 | setPreferences={setPreferences} 37 | filteringProperties={FILTERING_PROPERTIES} 38 | /> 39 | } 40 | contentType="table" 41 | tools={} 42 | toolsOpen={toolsOpen} 43 | onToolsChange={({ detail }) => setToolsOpen(detail.open)} 44 | stickyNotifications={true} 45 | /> 46 | ); 47 | } 48 | 49 | createRoot(document.getElementById('app')).render(); 50 | -------------------------------------------------------------------------------- /src/pages/table-property-filter/property-filter-table.jsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import React, { useEffect, useState } from 'react'; 4 | import { useCollection } from '@cloudscape-design/collection-hooks'; 5 | import PropertyFilter from '@cloudscape-design/components/property-filter'; 6 | import Pagination from '@cloudscape-design/components/pagination'; 7 | import Table from '@cloudscape-design/components/table'; 8 | 9 | import { FullPageHeader } from '../commons'; 10 | import { TableNoMatchState, TableEmptyState } from '../commons/common-components'; 11 | import { 12 | distributionTableAriaLabels, 13 | getHeaderCounterText, 14 | getTextFilterCounterText, 15 | propertyFilterI18nStrings, 16 | renderAriaLive, 17 | } from '../../i18n-strings'; 18 | import DataProvider from '../commons/data-provider'; 19 | import { Preferences } from '../commons/table-config'; 20 | 21 | import '../../styles/base.scss'; 22 | 23 | export function PropertyFilterTable({ 24 | loadHelpPanelContent, 25 | columnDefinitions, 26 | contentDisplayOptions, 27 | saveWidths, 28 | preferences, 29 | setPreferences, 30 | filteringProperties, 31 | }) { 32 | const [distributions, setDistributions] = useState([]); 33 | const [loading, setLoading] = useState(false); 34 | const { items, actions, filteredItemsCount, collectionProps, paginationProps, propertyFilterProps } = useCollection( 35 | distributions, 36 | { 37 | propertyFiltering: { 38 | filteringProperties, 39 | empty: , 40 | noMatch: ( 41 | { 43 | actions.setPropertyFiltering({ tokens: [], operation: 'and' }); 44 | }} 45 | /> 46 | ), 47 | }, 48 | pagination: { pageSize: preferences.pageSize }, 49 | sorting: { defaultState: { sortingColumn: columnDefinitions[0] } }, 50 | selection: {}, 51 | } 52 | ); 53 | 54 | useEffect(() => { 55 | new DataProvider().getData('distributions').then(distributions => { 56 | setDistributions(distributions); 57 | setLoading(false); 58 | }); 59 | }, []); 60 | 61 | return ( 62 | 85 | } 86 | loading={loading} 87 | loadingText="Loading distributions" 88 | filter={ 89 | 95 | } 96 | pagination={} 97 | preferences={ 98 | 103 | } 104 | /> 105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /src/pages/table-property-filter/table-property-filter-config.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import { stringOperators } from '../../common/property-filter-operators'; 4 | 5 | export const FILTERING_PROPERTIES = [ 6 | { 7 | propertyLabel: 'Domain name', 8 | key: 'domainName', 9 | groupValuesLabel: 'Domain name values', 10 | operators: stringOperators, 11 | }, 12 | { 13 | propertyLabel: 'Delivery method', 14 | key: 'deliveryMethod', 15 | groupValuesLabel: 'Delivery method values', 16 | operators: stringOperators, 17 | }, 18 | { 19 | propertyLabel: 'Price class', 20 | key: 'priceClass', 21 | groupValuesLabel: 'Price class values', 22 | operators: stringOperators, 23 | }, 24 | { 25 | propertyLabel: 'Origin', 26 | key: 'origin', 27 | groupValuesLabel: 'Origin values', 28 | operators: stringOperators, 29 | }, 30 | { 31 | propertyLabel: 'Status', 32 | key: 'status', 33 | groupValuesLabel: 'Status values', 34 | operators: stringOperators, 35 | }, 36 | { propertyLabel: 'State', key: 'state', groupValuesLabel: 'State values', operators: stringOperators }, 37 | { 38 | propertyLabel: 'Logging', 39 | key: 'logging', 40 | groupValuesLabel: 'Logging values', 41 | operators: stringOperators, 42 | }, 43 | { 44 | propertyLabel: 'SSL certificate', 45 | key: 'sslCertificate', 46 | groupValuesLabel: 'SSL certificate values', 47 | operators: stringOperators, 48 | }, 49 | ]; 50 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/resources/Graphin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/behaviors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "precedence": "0", 4 | "pathPattern": "Default (*)", 5 | "origin": "S3-aws-phoenix.example.com", 6 | "viewerProtocolPolicy": "HTTP, HTTPS", 7 | "forwardedQueryStrings": "No" 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /src/resources/content-origins.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "value": "bucket00", "label": "EXAMPLE-BUCKET0.s3.amazon.com" }, 3 | { "value": "bucket01", "label": "EXAMPLE-NO-ACCESS-BUCKET1.s3.amazon.com" }, 4 | { "value": "bucket02", "label": "EXAMPLE-BUCKET2.s3.amazon.com" }, 5 | { "value": "bucket03", "label": "EXAMPLE-NO-ACCESS-BUCKET3.s3.amazon.com" }, 6 | { "value": "bucket04", "label": "EXAMPLE-NO-ACCESS-BUCKET4.s3.amazon.com" }, 7 | { "value": "bucket05", "label": "EXAMPLE-BUCKET5.s3.amazon.com" }, 8 | { "value": "bucket06", "label": "EXAMPLE-BUCKET6.s3.amazon.com" }, 9 | { "value": "bucket07", "label": "EXAMPLE-NO-ACCESS-BUCKET7.s3.amazon.com" }, 10 | { "value": "bucket08", "label": "EXAMPLE-BUCKET8.s3.amazon.com" }, 11 | { "value": "bucket09", "label": "EXAMPLE-NO-ACCESS-BUCKET9.s3.amazon.com" }, 12 | { "value": "bucket10", "label": "EXAMPLE-NO-ACCESS-BUCKET10.s3.amazon.com" }, 13 | { "value": "bucket11", "label": "EXAMPLE-BUCKET11.s3.amazon.com" }, 14 | { "value": "bucket12", "label": "EXAMPLE-BUCKET12.s3.amazon.com" }, 15 | { "value": "bucket13", "label": "EXAMPLE-NO-ACCESS-BUCKET13.s3.amazon.com" }, 16 | { "value": "bucket14", "label": "EXAMPLE-NO-ACCESS-BUCKET14.s3.amazon.com" }, 17 | { "value": "bucket15", "label": "EXAMPLE-NO-ACCESS-BUCKET15.s3.amazon.com" }, 18 | { "value": "bucket16", "label": "EXAMPLE-NO-ACCESS-BUCKET16.s3.amazon.com" }, 19 | { "value": "bucket17", "label": "EXAMPLE-BUCKET17.s3.amazon.com" }, 20 | { "value": "bucket18", "label": "EXAMPLE-BUCKET18.s3.amazon.com" }, 21 | { "value": "bucket19", "label": "EXAMPLE-BUCKET19.s3.amazon.com" }, 22 | { "value": "bucket20", "label": "EXAMPLE-BUCKET20.s3.amazon.com" } 23 | ] 24 | -------------------------------------------------------------------------------- /src/resources/distributionsFilteringProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "propertyLabel": "Domain name", 4 | "key": "domainName", 5 | "groupValuesLabel": "Domain name values", 6 | "operators": [":", "!:", "=", "!=", "^", "!^"] 7 | }, 8 | { 9 | "propertyLabel": "Delivery method", 10 | "key": "deliveryMethod", 11 | "groupValuesLabel": "Delivery method values", 12 | "operators": [":", "!:", "=", "!=", "^", "!^"] 13 | }, 14 | { 15 | "propertyLabel": "Price class", 16 | "key": "priceClass", 17 | "groupValuesLabel": "Price class values", 18 | "operators": [":", "!:", "=", "!=", "^", "!^"] 19 | }, 20 | { 21 | "propertyLabel": "Origin", 22 | "key": "origin", 23 | "groupValuesLabel": "Origin values", 24 | "operators": [":", "!:", "=", "!=", "^", "!^"] 25 | }, 26 | { 27 | "propertyLabel": "Status", 28 | "key": "status", 29 | "groupValuesLabel": "Status values", 30 | "operators": [":", "!:", "=", "!=", "^", "!^"] 31 | }, 32 | { 33 | "propertyLabel": "State", 34 | "key": "state", 35 | "groupValuesLabel": "State values", 36 | "operators": [":", "!:", "=", "!=", "^", "!^"] 37 | }, 38 | { 39 | "propertyLabel": "Logging", 40 | "key": "logging", 41 | "groupValuesLabel": "Logging values", 42 | "operators": [":", "!:", "=", "!=", "^", "!^"] 43 | }, 44 | { 45 | "propertyLabel": "SSL certificate", 46 | "key": "sslCertificate", 47 | "groupValuesLabel": "SSL certificate values", 48 | "operators": [":", "!:", "=", "!=", "^", "!^"] 49 | }, 50 | { 51 | "propertyLabel": "environment", 52 | "key": "tag-indicator__environment", 53 | "groupValuesLabel": "Tags with key 'environment'", 54 | "group": "tags", 55 | "operators": [":", "!:", "=", "!=", "^", "!^"] 56 | }, 57 | { 58 | "propertyLabel": "department", 59 | "key": "tag-indicator__department", 60 | "groupValuesLabel": "Tags with key 'department'", 61 | "group": "tags", 62 | "operators": [":", "!:", "=", "!=", "^", "!^"] 63 | } 64 | ] 65 | -------------------------------------------------------------------------------- /src/resources/ec2-instances.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const getElement = (array, loopedIndex) => array[loopedIndex % array.length]; 4 | 5 | export default Array.from({ length: 50 }).map((item, i) => ({ 6 | id: `XLOWCQQFJJHM8${i}`, 7 | type: getElement(['m5.large', 'm5.xlarge', 'm5.4xlarge'], i), 8 | publicDns: `231.50.3.${i}`, 9 | monitoring: 'Default', 10 | state: getElement(['Activated', 'Deactivated'], i), 11 | platformDetails: getElement(['Linux', 'Windows'], i), 12 | terminalProtection: 'on', 13 | launchTime: `2021-05-12 16:53:${i.toString().padStart(2, '0')} GMT+0200 CEST`, 14 | volume: getElement([1, 2, 3, 4, 5], i), 15 | securityGroups: getElement([['groupA', 'groupB'], ['groupC', 'groupD', 'groupE'], ['groupF']], i), 16 | loadBalancers: getElement( 17 | [ 18 | ['lb-1', 'lb-2'], 19 | ['lb-3', 'lb-4', 'lb-5'], 20 | ['lb-6', 'lb-7', 'lb-8', 'lb-9'], 21 | ], 22 | i 23 | ), 24 | availabilityZone: getElement(['AZ 1', 'AZ 2'], i), 25 | numOfvCpu: getElement([3, 5, 9], i), 26 | inboundRules: [ 27 | { 28 | type: 'All traffic', 29 | protocol: 'All', 30 | portRange: 'All', 31 | source: `sg-abcdefg${i} (default)`, 32 | description: '-', 33 | }, 34 | { 35 | type: 'Custom TCP', 36 | protocol: 'TCP', 37 | portRange: '8182', 38 | source: `sg-dfs${i} (default)`, 39 | description: '-', 40 | }, 41 | ], 42 | })); 43 | -------------------------------------------------------------------------------- /src/resources/icons8-user-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/src/resources/icons8-user-96.png -------------------------------------------------------------------------------- /src/resources/image-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/src/resources/image-placeholder.png -------------------------------------------------------------------------------- /src/resources/logs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "trace/ORCL_mmon_1185.trm", 4 | "lastWritten": "Tue Feb 27 15:45:49 GMT-800 2018", 5 | "size": "116.9 kB" 6 | }, 7 | { 8 | "name": "audit/ORCL_j000_9748_20180227220003670535143795.aud", 9 | "lastWritten": "Tue Feb 27 14:00:24 GMT-800 2018", 10 | "size": "2.2 MB" 11 | }, 12 | { 13 | "name": "audit/ORCL_j000_12784_20180225140934404883143795.aud", 14 | "lastWritten": "Sun Feb 25 06:09:38 GMT-800 2018", 15 | "size": "417.5 kB" 16 | }, 17 | { 18 | "name": "audit/ORCL_j000_32052_20180224140833623442143795.aud", 19 | "lastWritten": "Sat Feb 24 06:08:39 GMT-800 2018", 20 | "size": "863.9 kB" 21 | }, 22 | { 23 | "name": "trace/alert_ORCL.log.2018-02-01", 24 | "lastWritten": "Thu Feb 01 15:55:20 GMT-800 2018", 25 | "size": "80.7 kB" 26 | }, 27 | { 28 | "name": "audit/ORCL_ora_17218_20180301180000121984143795.aud", 29 | "lastWritten": "Thu Mar 01 10:00:00 GMT-800 2018", 30 | "size": "1.5 kB" 31 | }, 32 | { 33 | "name": "trace/ORCL_m002_27079.trm", 34 | "lastWritten": "Sun Feb 25 16:19:56 GMT-800 2018", 35 | "size": "1.9 kB" 36 | }, 37 | { 38 | "name": "audit/ORCL_j000_15956_20180302220007526211143795.aud", 39 | "lastWritten": "Fri Mar 02 14:00:26 GMT-800 2018", 40 | "size": "2.2 MB" 41 | }, 42 | { 43 | "name": "trace/ORCL_dbrm_1161.trm", 44 | "lastWritten": "Wed Mar 07 18:00:00 GMT-800 2018", 45 | "size": "509.3 kB" 46 | }, 47 | { 48 | "name": "audit/ORCL_ora_14424_20180308130000145043143795.aud", 49 | "lastWritten": "Thu Mar 08 05:00:00 GMT-800 2018", 50 | "size": "1.5 kB" 51 | }, 52 | { 53 | "name": "trace/ORCL_dbrm_1161.trc", 54 | "lastWritten": "Wed Mar 07 18:00:00 GMT-800 2018", 55 | "size": "7.6 MB" 56 | }, 57 | { 58 | "name": "audit/ORCL_ora_13511_20180304014500107042143795.aud", 59 | "lastWritten": "Sat Mar 03 17:45:00 GMT-800 2018", 60 | "size": "1.5 kB" 61 | }, 62 | { 63 | "name": "trace/alert_ORCL.log.2018-02-18", 64 | "lastWritten": "Sun Feb 18 15:54:43 GMT-800 2018", 65 | "size": "90.4 kB" 66 | }, 67 | { 68 | "name": "trace/alert_ORCL.log.2018-02-22", 69 | "lastWritten": "Thu Feb 22 15:58:39 GMT-800 2018", 70 | "size": "81 kB" 71 | }, 72 | { 73 | "name": "audit/ORCL_ora_13811_20180301171500122862143795.aud", 74 | "lastWritten": "Thu Mar 01 09:15:00 GMT-800 2018", 75 | "size": "1.5 kB" 76 | }, 77 | { 78 | "name": "trace/alert_ORCL.log.2018-03-01", 79 | "lastWritten": "Thu Mar 01 15:57:09 GMT-800 2018", 80 | "size": "80.7 kB" 81 | }, 82 | { 83 | "name": "audit/ORCL_ora_5574_20180302194500108555143795.aud", 84 | "lastWritten": "Fri Mar 02 11:45:00 GMT-800 2018", 85 | "size": "1.5 kB" 86 | }, 87 | { 88 | "name": "trace/alert_ORCL.log.2018-02-11", 89 | "lastWritten": "Mon Feb 12 15:55:15 GMT-800 2018", 90 | "size": "81 kB" 91 | }, 92 | { 93 | "name": "trace/alert_ORCL.log.2018-02-15", 94 | "lastWritten": "Thu Feb 15 15:55:26 GMT-800 2018", 95 | "size": "80.7 kB" 96 | }, 97 | { 98 | "name": "audit/ORCL_ora_20606_20180301184500112864143795.aud", 99 | "lastWritten": "Thu Mar 01 10:45:00 GMT-800 2018", 100 | "size": "1.5 kB" 101 | }, 102 | { 103 | "name": "audit/ORCL_ora_27755_20180308074435851511143795.aud", 104 | "lastWritten": "Wed Mar 07 23:44:35 GMT-800 2018", 105 | "size": "2.2 kB" 106 | }, 107 | { 108 | "name": "audit/ORCL_ora_28635_20180308074923275146143795.aud", 109 | "lastWritten": "Wed Mar 07 23:49:23 GMT-800 2018", 110 | "size": "1.7 kB" 111 | }, 112 | { 113 | "name": "audit/ORCL_ora_12243_20180303041500110744143795.aud", 114 | "lastWritten": "Fri Mar 02 20:15:00 GMT-800 2018", 115 | "size": "1.5 kB" 116 | }, 117 | { 118 | "name": "trace/ORCL_dbrm_1161.trc", 119 | "lastWritten": "Wed Mar 07 18:00:00 GMT-800 2018", 120 | "size": "7.6 MB" 121 | } 122 | ] 123 | -------------------------------------------------------------------------------- /src/resources/model-hub-high-resolution-logo-transparent.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/src/resources/model-hub-high-resolution-logo-transparent.webp -------------------------------------------------------------------------------- /src/resources/modelhublog.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/llm_model_hub/c936773263eab00b4f63f01bf3ffe0744d97eda8/src/resources/modelhublog.webp -------------------------------------------------------------------------------- /src/resources/origins.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "AWS-PHOENIX-DEV.s3.amazon.com", 4 | "id": "S3-aws-phoenix.example.com", 5 | "type": "S3 origin", 6 | "accessIdentity": "-" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /src/resources/smlogo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceTags": [ 3 | { 4 | "key": "aws:cloudformation:stackid", 5 | "value": "CfBundlePackage_01" 6 | }, 7 | { 8 | "key": "owner", 9 | "value": "user@amazon.com" 10 | }, 11 | { 12 | "key": "project", 13 | "value": "mobile" 14 | }, 15 | { 16 | "key": "role", 17 | "value": "" 18 | } 19 | ], 20 | "valueMap": { 21 | "owner": ["user@amazon.com", "user-1@amazon.com", "user-2@amazon.com", "user-3@amazon.com", "user-4@amazon.com"], 22 | "project": ["mobile", "desktop", "tablet"], 23 | "role": ["admin"], 24 | "performance": [], 25 | "profile": [] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/resources/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/styles/base.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | @use '~@cloudscape-design/design-tokens' as cs; 4 | 5 | body { 6 | background: cs.$color-background-layout-main; 7 | position: relative; 8 | } 9 | 10 | .custom-main-header { 11 | display: block; 12 | position: sticky; 13 | top: 0; 14 | left: 0; 15 | right: 0; 16 | z-index: 1000; 17 | margin: 0; 18 | background-color: #0f1b2a; 19 | font-family: cs.$font-family-base; 20 | border-bottom: solid 1px #414d5c; 21 | } 22 | 23 | ul.menu-list { 24 | display: flex; 25 | align-items: center; 26 | height: 39px; 27 | margin: 0; 28 | padding: 0 40px; 29 | list-style: none; 30 | font-size: 14px; 31 | 32 | & > li { 33 | padding: 0; 34 | margin: 0; 35 | margin-right: 8px; 36 | 37 | > a { 38 | padding: 0 6px; 39 | } 40 | 41 | a, 42 | div, 43 | button, 44 | input, 45 | label { 46 | float: left; 47 | color: cs.$color-text-interactive-default; 48 | line-height: 16px; 49 | } 50 | 51 | #visual-refresh-toggle { 52 | margin-right: 5px; 53 | margin-top: 1px; 54 | } 55 | 56 | a, 57 | a:hover { 58 | cursor: pointer; 59 | text-decoration: none; 60 | } 61 | 62 | &.title { 63 | font-weight: bold; 64 | } 65 | } 66 | 67 | @media only screen and (max-width: 493px) { 68 | padding: 4px 20px; 69 | flex-wrap: wrap; 70 | height: fit-content; 71 | 72 | .title { 73 | flex: 1 1 100%; 74 | margin-bottom: 8px; 75 | } 76 | 77 | li { 78 | width: min-content; 79 | 80 | button, 81 | a { 82 | text-align: left; 83 | } 84 | 85 | a { 86 | padding: 0; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/styles/dashboard.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | @use './base'; 4 | 5 | .custom-dashboard-container { 6 | height: 100%; 7 | } 8 | -------------------------------------------------------------------------------- /src/styles/form.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | @use './base'; 5 | 6 | .custom-header { 7 | height: 100%; 8 | display: flex; 9 | align-items: flex-end; 10 | } 11 | 12 | .date-time-container > * { 13 | min-width: 18rem; 14 | } 15 | -------------------------------------------------------------------------------- /src/styles/onboarding.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | @use '~@cloudscape-design/design-tokens' as cs; 4 | @use './base'; 5 | 6 | .transcription-output-preview { 7 | background-color: cs.$color-background-dropdown-item-hover; 8 | } 9 | -------------------------------------------------------------------------------- /src/styles/product-page.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | @use '~@cloudscape-design/design-tokens' as cs; 4 | @use './base'; 5 | 6 | $viewport-breakpoint-s: 912px; 7 | 8 | body { 9 | // Note: This token will be themed (see the product page index.tsx) 10 | background: cs.$color-background-layout-main; 11 | } 12 | 13 | .product-page-content-grid { 14 | display: grid; 15 | grid-template-columns: 3fr 1fr; 16 | grid-template-rows: 0 auto 0; 17 | margin-block-start: cs.$space-static-xxl; 18 | } 19 | 20 | .on-this-page--mobile { 21 | grid-row: 1; 22 | grid-column: 1 / 3; 23 | display: none; 24 | margin-block-end: cs.$space-static-xxl; 25 | } 26 | 27 | .product-page-aside { 28 | grid-row: 2; 29 | grid-column: 2 / 3; 30 | padding-inline-start: calc(#{cs.$space-scaled-xxxl} /2); 31 | } 32 | .product-page-content { 33 | grid-row: 2; 34 | grid-column: 1 / 2; 35 | padding-inline-end: calc(#{cs.$space-scaled-xxxl} /2); 36 | } 37 | 38 | .product-page-mobile { 39 | grid-row: 3; 40 | grid-column: 1 / 3; 41 | display: none; 42 | } 43 | 44 | .product-page-aside-sticky { 45 | position: sticky; 46 | top: 40px; 47 | } 48 | 49 | @media only screen and (max-width: $viewport-breakpoint-s) { 50 | .product-page-content-grid { 51 | grid-template-columns: 100%; 52 | grid-template-rows: auto auto auto; 53 | } 54 | .on-this-page--mobile { 55 | display: block; 56 | } 57 | .product-page-mobile { 58 | display: block; 59 | } 60 | .product-page-aside { 61 | display: none; 62 | } 63 | } 64 | 65 | /* Simple separator */ 66 | hr { 67 | width: 100%; 68 | height: 0; 69 | border: none; 70 | border-block-start: 1px solid cs.$color-border-divider-default; 71 | } 72 | 73 | /* High-level sections of the main content area */ 74 | .page-section { 75 | padding-block-end: cs.$space-static-xxxl; 76 | margin-block-end: cs.$space-static-xxl; 77 | border-bottom: 1px solid cs.$color-border-divider-default; 78 | 79 | &:last-child { 80 | border: none; 81 | margin-block-end: 0; 82 | } 83 | } 84 | 85 | /* Product details list containing keys and values */ 86 | .product-details { 87 | display: grid; 88 | grid-template-columns: 60% 40%; 89 | margin: 0; 90 | padding: 0; 91 | 92 | dt { 93 | color: cs.$color-text-body-secondary; 94 | } 95 | 96 | dt, 97 | dd { 98 | margin: 0; 99 | padding: 0; 100 | padding-block: cs.$space-scaled-xs; 101 | border-block-end: 1px solid cs.$color-border-divider-default; 102 | } 103 | } 104 | 105 | /* List of product cards */ 106 | .product-cards-list { 107 | display: flex; 108 | flex-wrap: wrap; 109 | column-gap: cs.$space-scaled-l; 110 | row-gap: cs.$space-scaled-l; 111 | 112 | list-style-type: none; 113 | margin: 0; 114 | padding: 0; 115 | } 116 | 117 | .product-cards-list-item { 118 | flex: 1; 119 | flex-basis: 250px; 120 | max-width: 312px; 121 | 122 | list-style-type: none; 123 | margin: 0; 124 | padding: 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/styles/table-date-filter.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | @use '~@cloudscape-design/design-tokens' as cs; 4 | @use './base'; 5 | 6 | .date-form, 7 | .date-time-form { 8 | display: grid; 9 | grid-template-columns: auto; 10 | gap: cs.$space-scaled-s; 11 | } 12 | @media (min-width: 688px) { 13 | .date-time-form { 14 | grid-template-columns: min-content minmax(100px, max-content); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/styles/table-select.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | @use '~@cloudscape-design/design-tokens' as cs; 4 | @use './base'; 5 | 6 | .input-container { 7 | display: flex; 8 | flex-wrap: wrap; 9 | align-items: flex-end; 10 | order: 0; 11 | flex-grow: 10; 12 | margin-right: 0; 13 | margin-bottom: calc(-1 * #{cs.$space-scaled-m}); 14 | 15 | > *:not(:empty) { 16 | margin-right: cs.$space-scaled-m; 17 | margin-bottom: cs.$space-scaled-s; 18 | } 19 | } 20 | 21 | .input-filter { 22 | order: 0; 23 | flex-grow: 6; 24 | width: auto; 25 | max-width: 728px; 26 | } 27 | 28 | .select-filter { 29 | max-width: 130px; 30 | flex-grow: 2; 31 | width: auto; 32 | } 33 | 34 | .filtering-results { 35 | margin-left: 1rem; 36 | line-height: 3rem; 37 | color: cs.$color-text-form-default; 38 | } 39 | 40 | @media (max-width: 1152px) { 41 | .input-container { 42 | margin-right: calc(-1 * #{cs.$space-scaled-m}); 43 | } 44 | 45 | .select-filter { 46 | max-width: none; 47 | } 48 | 49 | .input-filter { 50 | width: 100%; 51 | max-width: none; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/styles/top-navigation.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | #h { 5 | z-index: 1002; 6 | } 7 | 8 | .menu-list { 9 | border-bottom: 1px solid #414750; // grey-650 10 | } 11 | -------------------------------------------------------------------------------- /src/styles/wizard.scss: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | @use './base'; 5 | 6 | .custom-input-small { 7 | max-width: 11rem; 8 | display: inline-block; 9 | } 10 | 11 | .flex-wrapper { 12 | display: flex; 13 | gap: 12px; 14 | align-items: flex-start; 15 | } 16 | 17 | .slider-wrapper { 18 | flex: 1; 19 | max-inline-size: 800px; 20 | } 21 | 22 | .input-wrapper { 23 | max-inline-size: 70px; 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "preserve" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------