├── .changelog ├── .gitignore └── template.md ├── .env.template ├── .envrc ├── .github ├── dependabot.yml └── workflows │ ├── constraints.txt │ └── main.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .python-version ├── .secrets.baseline ├── .venv ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── chatefficient ├── __init__.py ├── app_langchain.py ├── app_openai.py ├── app_translator.py └── streamlit_demo.py ├── data ├── README.md ├── external │ └── .gitkeep ├── interim │ └── .gitkeep ├── processed │ └── .gitkeep └── raw │ └── .gitkeep ├── docs ├── aws_sagemaker_quickstart.md ├── detect_secrets.md ├── getting_started.md ├── project_specific_setup.md ├── quickstart.md ├── release_process.md ├── updating_requirements.md ├── using_poetry.md └── using_towncrier.md ├── images ├── about-me.jpeg ├── civil-service.png ├── coefficient-aidl.png ├── eh-logo.png ├── eh.png ├── ml.png ├── obt-banner.png ├── one-big-thing.png ├── pydata.png └── vicuna-chat.gif ├── models └── .gitkeep ├── notebooks ├── Chroma.ipynb ├── LangChain.ipynb ├── __init__.py ├── frontier-ai-paper.txt ├── openai.ipynb └── utils.py ├── poetry.lock ├── pyproject.toml ├── requirements.txt └── setup.cfg /.changelog/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | -------------------------------------------------------------------------------- /.changelog/template.md: -------------------------------------------------------------------------------- 1 | {% for section, _ in sections.items() %} 2 | {% if section %}## {{section}}{% endif %} 3 | 4 | {% if sections[section] %} 5 | {% for category, val in definitions.items() if category in sections[section]%} 6 | ### {{ definitions[category]['name'] }} 7 | 8 | {% if definitions[category]['showcontent'] %} 9 | {% for text, values in sections[section][category].items() %} 10 | - {{ text }} ({{ values|join(', ') }}) 11 | {% endfor %} 12 | 13 | {% else %} 14 | - {{ sections[section][category]['']|join(', ') }} 15 | 16 | {% endif %} 17 | {% if sections[section][category]|length == 0 %} 18 | No significant changes. 19 | 20 | {% else %} 21 | {% endif %} 22 | 23 | {% endfor %} 24 | {% else %} 25 | No significant changes. 26 | 27 | {% endif %} 28 | {% endfor %} 29 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | export OPENAI_API_KEY="sk-********************" 2 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | dotenv 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic dependabot.yml file with minimum configuration 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: github-actions 6 | directory: "/" 7 | schedule: 8 | interval: daily 9 | - package-ecosystem: pip 10 | directory: "/.github/workflows" 11 | schedule: 12 | interval: daily 13 | - package-ecosystem: pip 14 | directory: "/" 15 | schedule: 16 | interval: daily 17 | versioning-strategy: lockfile-only 18 | allow: 19 | - dependency-type: "all" 20 | -------------------------------------------------------------------------------- /.github/workflows/constraints.txt: -------------------------------------------------------------------------------- 1 | pip==24.0 2 | poetry==1.8.2 3 | virtualenv==20.25.1 4 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.head_ref || github.run_id }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 30 17 | strategy: 18 | matrix: 19 | python-version: ["3.10"] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v5 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Create virtualenv 30 | run: | 31 | which python 32 | python -m venv venv 33 | source venv/bin/activate 34 | python -m pip install --constraint=.github/workflows/constraints.txt --upgrade pip 35 | which python 36 | 37 | - name: Set up Poetry cache 38 | uses: actions/cache@v4.0.2 39 | with: 40 | path: venv 41 | key: venv-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} 42 | 43 | - name: Install Poetry and Python dependencies 44 | run: | 45 | curl -sSL https://install.python-poetry.org | python3 - 46 | poetry --version 47 | poetry config virtualenvs.in-project true 48 | poetry config virtualenvs.create false 49 | poetry config virtualenvs.path venv 50 | source venv/bin/activate 51 | which python 52 | poetry install --no-root 53 | poetry self add poetry-plugin-export 54 | 55 | - name: Compute pre-commit cache key 56 | id: pre-commit-cache 57 | shell: python 58 | run: | 59 | import hashlib 60 | import sys 61 | python = "py{}.{}".format(*sys.version_info[:2]) 62 | payload = sys.version.encode() + sys.executable.encode() 63 | digest = hashlib.sha256(payload).hexdigest() 64 | result = "${{ runner.os }}-{}-{}-pre-commit".format(python, digest[:8]) 65 | print("::set-output name=result::{}".format(result)) 66 | 67 | - name: Restore pre-commit cache 68 | uses: actions/cache@v4.0.2 69 | with: 70 | path: ~/.cache/pre-commit 71 | key: ${{ steps.pre-commit-cache.outputs.result }}-${{ hashFiles('.pre-commit-config.yaml') }} 72 | restore-keys: | 73 | ${{ steps.pre-commit-cache.outputs.result }}- 74 | 75 | - name: pre-commit 76 | run: | 77 | source venv/bin/activate 78 | pre-commit run --hook-stage=manual --show-diff-on-failure --all-files 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Caches 2 | **/.ipynb_checkpoints/* 3 | **/__pycache__/* 4 | **/joblib/* 5 | *.pyc 6 | 7 | # Data 8 | *.sqlite3 9 | *.bin 10 | ./data/ 11 | !**/data/**/README.md 12 | !**/data/**/*.gitkeep 13 | 14 | # Models 15 | models/*.bin 16 | models/*.gguf 17 | !models/**/*.gitkeep 18 | 19 | # Secrets 20 | .env 21 | 22 | # Personal TODO files 23 | TODO.txt 24 | 25 | # VisualStudioCode 26 | .vscode/* 27 | !.vscode/settings.json 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | .RData 32 | 33 | # Environment 34 | venv/ 35 | 36 | # coverage 37 | .coverage 38 | .coverage.* 39 | htmlcov 40 | 41 | # IDE config 42 | .idea/ 43 | ipython_config.py 44 | profile_default/ 45 | 46 | # Other 47 | .DS_Store 48 | ignore/ 49 | *.log 50 | *.pem 51 | 52 | # Editor 53 | pyrightconfig.json 54 | 55 | # https://www.gitignore.io/ 56 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Update all versions in this file by running: 2 | # $ pre-commit autoupdate 3 | minimum_pre_commit_version: 3.5.0 4 | default_language_version: 5 | python: python3.10 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v4.5.0 9 | hooks: 10 | - id: end-of-file-fixer 11 | name: Check for a blank line at the end of scripts (auto-fixes) 12 | - id: trailing-whitespace 13 | - id: check-builtin-literals 14 | - id: check-byte-order-marker 15 | - id: check-case-conflict 16 | - id: check-merge-conflict 17 | - id: check-symlinks 18 | - id: check-toml 19 | - id: check-vcs-permalinks 20 | - id: check-xml 21 | - id: debug-statements 22 | - id: detect-private-key 23 | - id: mixed-line-ending 24 | - id: fix-encoding-pragma 25 | args: ["--remove"] 26 | - id: check-yaml 27 | - id: check-added-large-files 28 | name: Check for files larger than 5 MB 29 | args: ["--maxkb=5120"] 30 | - id: check-ast 31 | - id: check-docstring-first 32 | - id: name-tests-test 33 | args: ["--django"] 34 | - repo: https://github.com/myint/autoflake 35 | rev: v2.2.1 36 | hooks: 37 | - id: autoflake 38 | args: &autoflake 39 | - --in-place 40 | - --remove-all-unused-imports 41 | - --expand-star-imports 42 | - --remove-duplicate-keys 43 | - --remove-unused-variables 44 | - repo: https://github.com/pycqa/flake8 45 | rev: 6.1.0 46 | hooks: 47 | - &flake8 48 | id: flake8 49 | additional_dependencies: 50 | - flake8-bugbear==23.9.16 51 | - flake8-comprehensions==3.14.0 52 | - flake8-docstrings==1.7.0 53 | - flake8-eradicate==1.5.0 54 | - flake8-fixme==1.1.1 55 | - flake8-implicit-str-concat==0.4.0 56 | - flake8-mutable==1.2.0 57 | - flake8-no-pep420==2.7.0 58 | - flake8-print==5.0.0 59 | - flake8-return==1.2.0 60 | - flake8-simplify==0.21.0 61 | args: ["--config=setup.cfg"] 62 | - repo: https://github.com/psf/black 63 | rev: 23.11.0 64 | hooks: 65 | - id: black 66 | - repo: https://github.com/asottile/add-trailing-comma 67 | rev: v3.1.0 68 | hooks: 69 | - id: add-trailing-comma 70 | args: [--py36-plus] 71 | - repo: https://github.com/pycqa/isort 72 | rev: 5.12.0 73 | hooks: 74 | - id: isort 75 | name: isort (python) 76 | types: [python] 77 | - repo: https://github.com/asottile/pyupgrade 78 | rev: v3.15.0 79 | hooks: 80 | - id: pyupgrade 81 | args: 82 | - "--py310-plus" 83 | - repo: https://github.com/pycqa/bandit 84 | rev: 1.7.5 85 | hooks: 86 | - id: bandit 87 | args: ["-ii", "-ll"] 88 | - repo: https://github.com/Yelp/detect-secrets 89 | rev: v1.4.0 90 | hooks: 91 | - id: detect-secrets 92 | name: detect-secrets - Detect secrets in staged code 93 | args: [ 94 | "--baseline", 95 | ".secrets.baseline", 96 | # https://regex101.com/ 97 | "--exclude-files 'poetry\\.lock'", 98 | "--exclude-files '\\.secrets\\.baseline'", 99 | "--exclude-files '\\.env\\.template'", 100 | # "--exclude-files '.*\\.ipynb$'", 101 | # "--exclude-files '.*build/'", 102 | "--exclude-secrets 'password|ENTER_PASSWORD_HERE|INSERT_API_KEY_HERE'", 103 | "--exclude-lines 'integrity=*sha'", 104 | ] 105 | # https://pre-commit.com/#regular-expressions 106 | exclude: | 107 | (?x)^( 108 | poetry\.lock| 109 | \.secrets\.baseline| 110 | \.env\.template 111 | )$ 112 | # - repo: https://github.com/python-poetry/poetry 113 | # rev: 1.4.0 114 | # hooks: 115 | # - id: poetry-check 116 | # - id: poetry-lock 117 | # args: ["--no-update"] 118 | # - id: poetry-export 119 | # args: 120 | # [ 121 | # "-f", 122 | # "requirements.txt", 123 | # "-o", 124 | # "requirements.txt", 125 | # "--without-hashes", 126 | # ] 127 | - repo: https://github.com/pre-commit/mirrors-prettier 128 | rev: v3.1.0 129 | hooks: 130 | - id: prettier 131 | types_or: [yaml] 132 | additional_dependencies: 133 | - "prettier@2.8.4" 134 | - repo: local 135 | hooks: 136 | - id: pylint 137 | name: pylint 138 | entry: pylint chatefficient 139 | # entry: bash -c 'pylint ./path/package1/; pylint ./path/package2/' 140 | language: system 141 | types: [python] 142 | always_run: true 143 | pass_filenames: false 144 | stages: [manual] 145 | - id: pip-audit 146 | name: pip-audit 147 | # entry: pip-audit 148 | # langchain exclusions 149 | entry: pip-audit --ignore-vuln PYSEC-2023-91 --ignore-vuln PYSEC-2023-92 150 | language: system 151 | always_run: true 152 | pass_filenames: false 153 | stages: [manual] 154 | # TODO: Remove once this is resolved 155 | # https://github.com/python-poetry/poetry/issues/2765 156 | # https://github.com/python-poetry/poetry/issues/7075 157 | - id: poetry-lock 158 | name: poetry-lock 159 | # entry: bash -c 'poetry lock && python add_markers.py' 160 | entry: bash -c 'poetry lock --no-update' 161 | language: system 162 | always_run: true 163 | pass_filenames: false 164 | - id: poetry-export 165 | name: poetry-export 166 | entry: poetry export -f requirements.txt --output requirements.txt --without-hashes 167 | language: system 168 | always_run: true 169 | pass_filenames: false 170 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.10.13 2 | -------------------------------------------------------------------------------- /.secrets.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.2.0", 3 | "plugins_used": [ 4 | { 5 | "name": "ArtifactoryDetector" 6 | }, 7 | { 8 | "name": "AWSKeyDetector" 9 | }, 10 | { 11 | "name": "AzureStorageKeyDetector" 12 | }, 13 | { 14 | "name": "Base64HighEntropyString", 15 | "limit": 4.5 16 | }, 17 | { 18 | "name": "BasicAuthDetector" 19 | }, 20 | { 21 | "name": "CloudantDetector" 22 | }, 23 | { 24 | "name": "GitHubTokenDetector" 25 | }, 26 | { 27 | "name": "HexHighEntropyString", 28 | "limit": 3.0 29 | }, 30 | { 31 | "name": "IbmCloudIamDetector" 32 | }, 33 | { 34 | "name": "IbmCosHmacDetector" 35 | }, 36 | { 37 | "name": "JwtTokenDetector" 38 | }, 39 | { 40 | "name": "KeywordDetector", 41 | "keyword_exclude": "" 42 | }, 43 | { 44 | "name": "MailchimpDetector" 45 | }, 46 | { 47 | "name": "NpmDetector" 48 | }, 49 | { 50 | "name": "PrivateKeyDetector" 51 | }, 52 | { 53 | "name": "SendGridDetector" 54 | }, 55 | { 56 | "name": "SlackDetector" 57 | }, 58 | { 59 | "name": "SoftlayerDetector" 60 | }, 61 | { 62 | "name": "SquareOAuthDetector" 63 | }, 64 | { 65 | "name": "StripeDetector" 66 | }, 67 | { 68 | "name": "TwilioKeyDetector" 69 | } 70 | ], 71 | "filters_used": [ 72 | { 73 | "path": "detect_secrets.filters.allowlist.is_line_allowlisted" 74 | }, 75 | { 76 | "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", 77 | "min_level": 2 78 | }, 79 | { 80 | "path": "detect_secrets.filters.heuristic.is_indirect_reference" 81 | }, 82 | { 83 | "path": "detect_secrets.filters.heuristic.is_likely_id_string" 84 | }, 85 | { 86 | "path": "detect_secrets.filters.heuristic.is_lock_file" 87 | }, 88 | { 89 | "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" 90 | }, 91 | { 92 | "path": "detect_secrets.filters.heuristic.is_potential_uuid" 93 | }, 94 | { 95 | "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" 96 | }, 97 | { 98 | "path": "detect_secrets.filters.heuristic.is_sequential_string" 99 | }, 100 | { 101 | "path": "detect_secrets.filters.heuristic.is_swagger_file" 102 | }, 103 | { 104 | "path": "detect_secrets.filters.heuristic.is_templated_secret" 105 | }, 106 | { 107 | "path": "detect_secrets.filters.regex.should_exclude_file", 108 | "pattern": [ 109 | "poetry\\.lock", 110 | "\\.secrets\\.baseline", 111 | "\\.env\\.template" 112 | ] 113 | }, 114 | { 115 | "path": "detect_secrets.filters.regex.should_exclude_line", 116 | "pattern": [ 117 | "integrity=*sha" 118 | ] 119 | }, 120 | { 121 | "path": "detect_secrets.filters.regex.should_exclude_secret", 122 | "pattern": [ 123 | "password|ENTER_PASSWORD_HERE|INSERT_API_KEY_HERE" 124 | ] 125 | } 126 | ], 127 | "results": {}, 128 | "generated_at": "2023-06-21T19:45:25Z" 129 | } 130 | -------------------------------------------------------------------------------- /.venv: -------------------------------------------------------------------------------- 1 | chatefficient 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-python.python", 4 | "ms-python.vscode-pylance", 5 | "esbenp.prettier-vscode", 6 | "wayou.vscode-todo-highlight", 7 | "bungcip.better-toml" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // editor & autoformatter settings 3 | "editor.bracketPairColorization.enabled": true, 4 | "editor.formatOnSave": true, 5 | "editor.guides.bracketPairs": "active", 6 | "editor.trimAutoWhitespace": true, 7 | "files.trimTrailingWhitespace": true, 8 | "prettier.enable": true, 9 | 10 | // python - black 11 | "python.formatting.provider": "none", 12 | "python.formatting.blackArgs": [], 13 | 14 | // python - other 15 | "python.languageServer": "Pylance", 16 | 17 | // python - linting & static analysis 18 | "python.analysis.extraPaths": ["chatefficient"], 19 | "python.analysis.typeCheckingMode": "off", 20 | "python.linting.enabled": true, 21 | "python.linting.flake8Enabled": true, 22 | "python.linting.flake8Args": [], 23 | "python.linting.mypyEnabled": true, 24 | "python.linting.pylintEnabled": true, 25 | "python.linting.pylintArgs": [], 26 | 27 | // Python files only 28 | "[python]": { 29 | // isort on save 30 | "editor.codeActionsOnSave": { 31 | "source.organizeImports": "explicit" 32 | }, 33 | // Stop the 'Black does not support "Format Selection"' message every 34 | // time you paste something (https://stackoverflow.com/a/63612401/3279076) 35 | "editor.formatOnPaste": false, 36 | "editor.defaultFormatter": "ms-python.black-formatter" 37 | }, 38 | 39 | // python - pytest (https://code.visualstudio.com/docs/python/testing) 40 | "python.testing.unittestEnabled": false, 41 | "python.testing.pytestEnabled": true, 42 | "python.testing.pytestArgs": ["tests"], 43 | 44 | // git 45 | "git.enabled": true, 46 | 47 | // file associations 48 | "files.associations": { 49 | "**/*.html": "html", 50 | "**/*.js": "javascript", 51 | "**/requirements{/**,*}.{txt,in}": "pip-requirements" 52 | }, 53 | 54 | // markdownlint 55 | "markdownlint.run": "onSave", 56 | "markdownlint.ignore": [], 57 | "markdownlint.config": { 58 | // MD003 - Heading style 59 | "MD003": { 60 | "style": "atx" 61 | }, 62 | // MD007 - Unordered list indentation 63 | "MD007": { 64 | "start_indented": true, 65 | "indent": 2 66 | }, 67 | // MD012 - Multiple consecutive blank lines 68 | "MD012": false, 69 | // MD022 - Headings should be surrounded by blank lines 70 | "MD022": false, 71 | // MD024 - Multiple headings with the same content 72 | "MD024": false, 73 | // MD032 - Lists should be surrounded by blank lines 74 | "MD032": false, 75 | // MD046 - Code block style 76 | "MD046": { 77 | "style": "fenced" 78 | }, 79 | "no-hard-tabs": true 80 | }, 81 | } 82 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Coefficient 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatEfficient: How To Build Your Own Private ChatGPT Using Streamlit, LangChain & Vicuna 2 | 3 | [![CI](https://github.com/CoefficientSystems/chat-efficient/actions/workflows/main.yaml/badge.svg)](https://github.com/CoefficientSystems/chat-efficient/actions/workflows/main.yaml) 4 | 5 | DIY ChatGPT using Streamlit, LangChain and open-source LLMs 6 | 7 | ![Demo](images/vicuna-chat.gif) 8 | 9 | ## 🎬 Watch again 10 | 11 | You can watch the [webinar recording via this link](https://app.session.com/coefficientai/Munch-and-Learn-Tutorial:-How-To-Build-Your-Own-Private-ChatGPT-Using-Streamlit-LangChain-and-Vicuna?s=1&passcode=308697). 12 | 13 | You can see all our [upcoming and previous events here](https://coefficient.ai/events). 14 | 15 | If you'd like to work with Coefficient to build your own AI-powered apps, please reach out to us at 16 | [contact@coefficient.ai](contact@coefficient.ai) or [complete our enquiry form](https://coefficient.ai/contact). 17 | 18 | If you'd like to register to be notified about upcoming events, you can also do that [here](https://coefficient.ai/contact). 19 | 20 | ## 💪 Learn by doing 21 | 22 | 1. [Learn OpenAI](notebooks/openai.ipynb) 23 | 2. [Learn Streamlit](chatefficient/streamlit_demo.py) 24 | 3. [OpenAI + Streamlit = GPT4 chatbot](chatefficient/app_openai.py) 25 | 4. [Learn LangChain](notebooks/LangChain.ipynb) 26 | 5. [OpenAI + LangChain = local chatbot](chatefficient/app_langchain.py) 27 | 6. [Learn Chroma](notebooks/Chroma.ipynb) 28 | 29 | ## ✅ Project cheatsheet 30 | 31 | - **pre-commit:** `pre-commit run --all-files` 32 | - **pytest:** `pytest` or `pytest -s` 33 | - **coverage:** `coverage run -m pytest` or `coverage html` 34 | - **poetry sync:** `poetry install --no-root --sync` 35 | - **updating requirements:** see [docs/updating_requirements.md](docs/updating_requirements.md) 36 | - **create towncrier entry:** `towncrier create 123.added --edit` 37 | 38 | 39 | ## 🏗 Initial project setup 40 | 41 | 1. See [docs/getting_started.md](docs/getting_started.md) or [docs/quickstart.md](docs/quickstart.md) 42 | for how to get up & running. 43 | 2. Check [docs/project_specific_setup.md](docs/project_specific_setup.md) for project specific setup. 44 | 3. See [docs/using_poetry.md](docs/using_poetry.md) for how to update Python requirements using 45 | [Poetry](https://python-poetry.org/). 46 | 4. See [docs/detect_secrets.md](docs/detect_secrets.md) for more on creating a `.secrets.baseline` 47 | file using [detect-secrets](https://github.com/Yelp/detect-secrets). 48 | 5. See [docs/using_towncrier.md](docs/using_towncrier.md) for how to update the `CHANGELOG.md` 49 | using [towncrier](https://github.com/twisted/towncrier). 50 | -------------------------------------------------------------------------------- /chatefficient/__init__.py: -------------------------------------------------------------------------------- 1 | """Generated using coefficient-cookiecutter""" 2 | -------------------------------------------------------------------------------- /chatefficient/app_langchain.py: -------------------------------------------------------------------------------- 1 | """Streamlit app using LangChain + locally-running Vicuna. 2 | 3 | Examples: 4 | $ streamlit run chatefficient/app_langchain.py 5 | """ 6 | import streamlit as st 7 | from joblib import Memory 8 | from langchain.callbacks.manager import CallbackManager 9 | from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler 10 | from langchain.chains import LLMChain 11 | from langchain.llms import LlamaCpp 12 | from langchain.memory import ConversationBufferWindowMemory 13 | from langchain.prompts import PromptTemplate 14 | from streamlit_chat import message 15 | 16 | LOCATION = "./cachedir" 17 | MEMORY = Memory(LOCATION, verbose=0) 18 | 19 | # Make sure the model path is correct for your system! 20 | llm = LlamaCpp( 21 | n_ctx=512 * 2, 22 | model_path="./models/llama-2-7b-chat.Q4_K_M.gguf", 23 | n_gpu_layers=40, # Change this value based on your model and your GPU VRAM pool. 24 | n_batch=512, # Should be between 1 and n_ctx, consider the amount of VRAM in your GPU. 25 | callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]), 26 | verbose=True, 27 | model_kwargs={"max_new_tokens": 64 * 4}, 28 | stop=["Human:", "Input:", "Mind Reader:"], 29 | ) 30 | 31 | 32 | # @MEMORY.cache 33 | def generate_response(human_input): 34 | """Prompt LangChain for a chat completion response.""" 35 | chain = st.session_state["chain"] 36 | response = chain.predict(human_input=human_input) 37 | st.session_state["chain"] = chain 38 | 39 | return response 40 | 41 | 42 | # Initialize session state variables 43 | if "chain" not in st.session_state: 44 | template = """ 45 | You are a mindreader with magical abilities. 46 | You are very over-dramatic and speak like a mysterious shaman. 47 | You will be given something to guess, such as an animal, or a famous person. 48 | You will ask a question, I will provide an answer, and then you will ask another question. 49 | Try to ask questions that narrow down what the answer might be. 50 | If you are very confident, you can guess what it is. 51 | If your guess is wrong, then you must ask another question to help narrow it down. 52 | Repeat this until you I tell you that you have the right answer. 53 | Your goal is to find the right answer in as few questions as possible. Only make a guess 54 | when you are confident, otherwise ask more questions that narrow down the possibilities. 55 | 56 | {history} 57 | Human: {human_input} 58 | Assistant:""" 59 | 60 | prompt = PromptTemplate(input_variables=["history", "human_input"], template=template) 61 | 62 | chatgpt_chain = LLMChain( 63 | llm=llm, 64 | prompt=prompt, 65 | verbose=True, 66 | memory=ConversationBufferWindowMemory(k=10), 67 | ) 68 | st.session_state["chain"] = chatgpt_chain 69 | 70 | 71 | if "generated" not in st.session_state: 72 | st.session_state["generated"] = [] 73 | 74 | if "past" not in st.session_state: 75 | st.session_state["past"] = [] 76 | 77 | 78 | # Containers 79 | response_container = st.container() 80 | chat_container = st.container() 81 | 82 | 83 | with chat_container: 84 | if prompt := st.chat_input("Say something"): 85 | output = generate_response(prompt).strip() 86 | st.session_state["past"].append(prompt) 87 | st.session_state["generated"].append(output) 88 | 89 | 90 | INITIAL_MESSAGE = """ 91 | 🧞‍♂ I am a mind reader with magical abilities! 🔮 92 | 🤔 Give me a category e.g. animal, or a famous person. 93 | 💬 I will ask questions and guess what you are thinking of! 94 | """ 95 | 96 | with response_container: 97 | message(INITIAL_MESSAGE) 98 | if st.session_state["generated"]: 99 | for i in range(len(st.session_state["generated"])): 100 | st.chat_message("user").write(st.session_state["past"][i]) 101 | st.chat_message("ai").write(st.session_state["generated"][i]) 102 | -------------------------------------------------------------------------------- /chatefficient/app_openai.py: -------------------------------------------------------------------------------- 1 | """app.py 2 | 3 | Examples: 4 | $ streamlit run chatefficient/app_openai.py 5 | """ 6 | import streamlit as st 7 | from joblib import Memory 8 | from openai import OpenAI 9 | 10 | LOCATION = "./cachedir" 11 | MEMORY = Memory(LOCATION, verbose=0) 12 | CLIENT = OpenAI() 13 | 14 | 15 | @MEMORY.cache 16 | def generate_response(prompt, model="gpt-3.5-turbo"): 17 | """Prompt GPT API for a chat completion response.""" 18 | st.session_state["context"].append({"role": "user", "content": prompt}) 19 | 20 | completion = CLIENT.chat.completions.create(model=model, messages=st.session_state["context"]) 21 | response = completion.choices[0].message.content 22 | st.session_state["context"].append({"role": "assistant", "content": response}) 23 | 24 | return response 25 | 26 | 27 | # Initialize session state variables 28 | if "context" not in st.session_state: 29 | st.session_state["context"] = [{"role": "system", "content": "You are a helpful assistant."}] 30 | 31 | if "generated" not in st.session_state: 32 | st.session_state["generated"] = [] 33 | 34 | if "past" not in st.session_state: 35 | st.session_state["past"] = [] 36 | 37 | 38 | # Containers 39 | response_container = st.container() 40 | chat_container = st.container() 41 | 42 | 43 | with chat_container: 44 | if prompt := st.chat_input("Say something"): 45 | output = generate_response(prompt) 46 | st.session_state["past"].append(prompt) 47 | st.session_state["generated"].append(output) 48 | 49 | if st.session_state["generated"]: 50 | with response_container: 51 | for i in range(len(st.session_state["generated"])): 52 | st.chat_message("user").write(st.session_state["past"][i]) 53 | st.chat_message("ai").write(st.session_state["generated"][i]) 54 | -------------------------------------------------------------------------------- /chatefficient/app_translator.py: -------------------------------------------------------------------------------- 1 | """Streamlit app using LangChain + locally-running Vicuna. 2 | 3 | Examples: 4 | $ streamlit run chatefficient/app_translator.py 5 | """ 6 | import streamlit as st 7 | from joblib import Memory 8 | from langchain.callbacks.manager import CallbackManager 9 | from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler 10 | from langchain.chains import LLMChain 11 | from langchain.llms import LlamaCpp 12 | from langchain.prompts import PromptTemplate 13 | from streamlit_chat import message 14 | 15 | LOCATION = "./cachedir" 16 | MEMORY = Memory(LOCATION, verbose=0) 17 | 18 | llm = LlamaCpp( 19 | n_ctx=512 * 2, 20 | model_path="./models/llama-2-13b-chat.Q4_K_M.gguf", 21 | n_gpu_layers=20, # Change this value based on your model and your GPU VRAM pool. 22 | n_batch=512, # Should be between 1 and n_ctx, consider the amount of VRAM in your GPU. 23 | callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]), 24 | verbose=True, 25 | model_kwargs={"max_new_tokens": 64 * 4}, 26 | stop=["Human:", "Input:"], 27 | ) 28 | 29 | 30 | # @MEMORY.cache 31 | def generate_response(human_input): 32 | """Prompt LangChain for a chat completion response.""" 33 | chain = st.session_state["chain"] 34 | response = chain.predict(human_input=human_input) 35 | st.session_state["chain"] = chain 36 | 37 | return response 38 | 39 | 40 | # Initialize session state variables 41 | if "chain" not in st.session_state: 42 | template = """ 43 | You are my Mandarin Chinese teacher. I will give you an input in English, and you will 44 | respond with the corresponding translation in Mandarin Chinese in both pinyin and hanzi. 45 | 46 | Input: {human_input} 47 | Response:""" 48 | 49 | prompt = PromptTemplate(input_variables=["human_input"], template=template) 50 | chatgpt_chain = LLMChain(llm=llm, prompt=prompt, verbose=True) 51 | st.session_state["chain"] = chatgpt_chain 52 | 53 | 54 | if "generated" not in st.session_state: 55 | st.session_state["generated"] = [] 56 | 57 | if "past" not in st.session_state: 58 | st.session_state["past"] = [] 59 | 60 | 61 | # Containers 62 | response_container = st.container() 63 | chat_container = st.container() 64 | 65 | 66 | with chat_container: 67 | with st.form(key="my_form", clear_on_submit=True): 68 | user_input = st.text_area("You:", key="input", height=100) 69 | submit_button = st.form_submit_button(label="Send") 70 | 71 | if submit_button and user_input: 72 | output = generate_response(user_input).strip() 73 | st.session_state["past"].append(user_input) 74 | st.session_state["generated"].append(output) 75 | 76 | 77 | INITIAL_MESSAGE = ( 78 | "You are my Mandarin Chinese teacher. I will give you an input in English, and " 79 | "you will respond with the corresponding translation in Mandarin Chinese in " 80 | "both pinyin and hanzi." 81 | ) 82 | 83 | with response_container: 84 | message(INITIAL_MESSAGE) 85 | if st.session_state["generated"]: 86 | for i in range(len(st.session_state["generated"])): 87 | message(st.session_state["past"][i], is_user=True, key=f"{i}_user") 88 | message(st.session_state["generated"][i], key=f"{i}") 89 | -------------------------------------------------------------------------------- /chatefficient/streamlit_demo.py: -------------------------------------------------------------------------------- 1 | """Streamlit 101. 2 | 3 | Docs: 4 | - https://docs.streamlit.io/library/get-started 5 | - https://docs.streamlit.io/library/api-reference/session-state 6 | - https://discuss.streamlit.io/t/new-component-streamlit-chat-a-new-way-to-create-chatbots/20412 7 | 8 | Examples: 9 | $ streamlit hello 10 | $ streamlit run chatefficient/streamlit_demo.py 11 | 12 | Visit https://YOUR-CUSTOM-URL.sagemaker.aws/proxy/{YOUR-PROXY-HERE}/ 13 | e.g. https://obt-workshop.notebook.eu-west-2.sagemaker.aws/proxy/8501/ 14 | """ 15 | 16 | 17 | import pandas as pd 18 | import streamlit as st 19 | 20 | df = pd.DataFrame({"first column": [1, 2, 3, 4], "second column": [10, 20, 30, 40]}) 21 | 22 | 23 | # ######### Part 1: st.write() ######### 24 | # st.write(df) 25 | 26 | ######### Part 2: Magic Commands ######### 27 | # df 28 | 29 | 30 | ######### Part 3: Plotting Charts ######### 31 | # import numpy as np 32 | # chart_data = pd.DataFrame(np.random.randn(20, 3), columns=["a", "b", "c"]) 33 | # st.line_chart(chart_data) 34 | 35 | 36 | ######### Part 4: Maps ######### 37 | # old_street = [51.525, -0.088] 38 | # map_data = pd.DataFrame( 39 | # np.random.randn(50, 2) / [150, 150] + old_street, 40 | # columns=["lat", "lon"] 41 | # ) 42 | # st.map(map_data) 43 | 44 | 45 | ######### Part 5: Widgets ######### 46 | # x = st.slider("x") # 👈 this is a widget 47 | # st.write(x, "squared is", x * x) 48 | 49 | # df = pd.DataFrame({"first column": [1, 2, 3, 4], "second column": [10, 20, 30, 40]}) 50 | # option = st.selectbox("Which number do you like best?", df["first column"]) 51 | # st.write("You selected: ", option) 52 | 53 | 54 | ######### Part 6: Sidebar ######### 55 | # # Add a selectbox to the sidebar: 56 | # add_selectbox = st.sidebar.selectbox( 57 | # "How would you like to be contacted?", 58 | # ("Email", "Home phone", "Mobile phone"), 59 | # ) 60 | 61 | # # Add a slider to the sidebar: 62 | # add_slider = st.sidebar.slider("Select a range of values", 0.0, 100.0, (25.0, 75.0)) 63 | 64 | 65 | ######### Part 7: Session State ######### 66 | # # Initialization 67 | # if "name" not in st.session_state: 68 | # st.session_state["name"] = "DEFAULT NAME" 69 | 70 | # st.sidebar.text_input("Your name", key="name") 71 | 72 | # # You can access the value at any point with: 73 | # st.session_state.name 74 | 75 | 76 | ######### Part 8: Streamlit Chat ######### 77 | 78 | st.chat_message("ai").write("Hello human 👋") 79 | 80 | st.chat_message("human").write("Hey there bot!") # user input 81 | 82 | st.chat_message("ai").write("I hope your talk is going OK?") 83 | 84 | st.chat_message("human").write("Me too!") 85 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # `data` folder overview 2 | 3 | Any data that needs to be stored locally should be saved in this location. This folder, 4 | and its sub-folders, are not version-controlled. 5 | 6 | The sub-folders should be used as follows: 7 | 8 | - `external`: any data that will not be processed at all, such as reference data; 9 | - `raw`: any raw data before any processing; 10 | - `interim`: any raw data that has been partially processed and, for whatever reason, 11 | needs to be stored before further processing is completed; and 12 | - `processed`: any raw or interim data that has been fully processed into its final 13 | state. 14 | -------------------------------------------------------------------------------- /data/external/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/data/external/.gitkeep -------------------------------------------------------------------------------- /data/interim/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/data/interim/.gitkeep -------------------------------------------------------------------------------- /data/processed/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/data/processed/.gitkeep -------------------------------------------------------------------------------- /data/raw/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/data/raw/.gitkeep -------------------------------------------------------------------------------- /docs/aws_sagemaker_quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | This document contains instructions to get up & running with this repo using AWS SageMaker. 4 | 5 | ## Part 1: Create a SageMaker Notebook 6 | 7 | From the AWS Console: 8 | - Go to Service Quotas and request a GPU quota increase for `ml.g4dn.xlarge` for 1 GPU for both 9 | notebook instance usage and for Studio KernelGateway Apps. 10 | - Launch a SageMaker Notebook from SageMaker > Notebook > Notebook instances > Create notebook instance 11 | - Select `ml.g4dn.xlarge` instance type (see [https://aws.amazon.com/sagemaker/pricing/] for pricing) 12 | 13 | 14 | ## Part 2: Install Python dependencies 15 | 16 | Create a new terminal and run the following: 17 | 18 | ```sh 19 | # Switch to a bash shell 20 | bash 21 | 22 | # Git clone the repo 23 | cd ~/SageMaker 24 | git clone https://github.com/CoefficientSystems/chat-efficient 25 | 26 | # Change to the repo root 27 | cd ~/SageMaker/chat-efficient 28 | 29 | # Activate a Python 3.10 environment pre-configured with PyTorch 30 | conda activate pytorch_p310 31 | 32 | # Check Python version 33 | python --version 34 | 35 | # Install the repo's declared dependencies 36 | pip install -r requirements.txt 37 | 38 | # What GPUs are installed on this instance? 39 | nvidia-smi 40 | 41 | # Install llama-cpp-python 42 | # Reference: https://llama-cpp-python.readthedocs.io/en/latest/ 43 | pip uninstall llama-cpp-python -y 44 | CMAKE_ARGS="-DLLAMA_CUBLAS=on" pip install llama-cpp-python 45 | 46 | # Download the Llama-2 model from Hugging Face (7 billion parameters, 4.08GB file size, up to 7.2GB RAM required to run) 47 | huggingface-cli download TheBloke/Llama-2-7b-Chat-GGUF llama-2-7b-chat.Q4_K_M.gguf --local-dir . --local-dir-use-symlinks False 48 | mv ./llama-2-7b-chat.Q4_K_M.gguf ./models/ 49 | 50 | # Alternatively, if you have enough memory, you can download the 13B Llama-2 model (7.87GB file size, up to 10.37GB RAM required) 51 | huggingface-cli download TheBloke/Llama-2-13b-Chat-GGUF llama-2-13b-chat.Q4_K_M.gguf --local-dir . --local-dir-use-symlinks False 52 | mv ./llama-2-13b-chat.Q4_K_M.gguf ./models/ 53 | ``` 54 | 55 | Test it out in `ipython`: 56 | 57 | ```python 58 | from llama_cpp import Llama 59 | llm = Llama(model_path="./models/llama-2-7b-chat.Q4_K_M.gguf") 60 | output = llm("Q: Name the planets in the solar system? A: ", max_tokens=64, stop=["Q:", "\n"], echo=True) 61 | print(output) 62 | print(output['choices'][0]['text']) 63 | ``` 64 | 65 | Type ctrl+D to quit. 66 | 67 | 68 | ## Part 3: Add OpenAI key 69 | 70 | ```sh 71 | cp .env.template env 72 | # Edit the file "env" to specify your OpenAI key. Save & close. 73 | mv env .env 74 | 75 | # These will now be loaded as environment variables when we run `load_dotenv()` from the python-dotenv package. 76 | ``` 77 | -------------------------------------------------------------------------------- /docs/detect_secrets.md: -------------------------------------------------------------------------------- 1 | # Using the `detect-secrets` pre-commit hook 2 | 3 | [We use `detect-secrets` to check that no secrets are accidentally committed](detect-secrets). The 4 | `detect-secrets` package does its best to prevent accidental committing of secrets, but it may miss 5 | things. Instead, focus on good software development practices! See the [definition of a secret for 6 | further information](#definition-of-a-secret-according-to-detect-secrets). 7 | 8 | 9 | ## Initial setup 10 | 11 | This pre-commit hook requires you to generate a baseline file if one is not already present within 12 | the root directory. To create the baseline file, run the following at the root of the repository: 13 | 14 | ```shell 15 | detect-secrets scan > .secrets.baseline 16 | ``` 17 | 18 | Next, audit the baseline that has been generated by running: 19 | 20 | ```shell 21 | detect-secrets audit .secrets.baseline 22 | ``` 23 | 24 | When you run this command, you'll enter an interactive console. This will present you with a list 25 | of high-entropy string and/or anything which could be a secret. It will then ask you to verify 26 | whether this is the case. This allows the hook to remember false positives in the future, and alert 27 | you to new secrets. 28 | 29 | 30 | ## Definition of a "secret" according to `detect-secrets` 31 | 32 | The `detect-secrets` documentation, as of January 2021, says it works: 33 | 34 | > ...by running periodic diff outputs against heuristically crafted \[regular 35 | > expression\] statements, to identify whether any new secret has been committed. 36 | 37 | This means it uses regular expression patterns to scan your code changes for anything that looks 38 | like a secret according to the patterns. By definition, there are only a limited number of 39 | patterns, so the `detect-secrets` package cannot detect every conceivable type of secret. 40 | 41 | To understand what types of secrets will be detected, read the `detect-secrets` documentation on 42 | caveats, and the list of supported plugins. Also, you should use secret variable names with words 43 | that will trip the KeywordDetector plugin; see the 44 | [`DENYLIST` variable for the full list of words][detect-secrets-keyword-detector]. 45 | 46 | 47 | ## If `pre-commit` detects secrets during commit 48 | 49 | If `pre-commit` detects any secrets when you try to create a commit, it will detail what it found 50 | and where to go to check the secret. 51 | 52 | If the detected secret is a false positive, there are two options to resolve this, and prevent your 53 | commit from being blocked: 54 | 55 | - [inline allowlisting of false positives (recommended)](#inline-allowlisting-recommended); or 56 | - [updating the `.secrets.baseline` to include the false positives](#updating-secretsbaseline). 57 | 58 | In either case, if an actual secret is detected (or a combination of actual secrets and false 59 | positives), first remove the actual secret. Then following either of these processes. 60 | 61 | ### Inline allowlisting (recommended) 62 | 63 | To exclude a false positive, add a `pragma` comment such as: 64 | 65 | ```python 66 | secret = "Password123" # pragma: allowlist secret 67 | ``` 68 | 69 | or 70 | 71 | ```python 72 | # pragma: allowlist nextline secret 73 | secret = "Password123" 74 | ``` 75 | 76 | If the detected secret is actually a secret (or other sensitive information), remove the secret and 77 | re-commit; there is no need to add any `pragma` comments. 78 | 79 | If your commit contains a mixture of false positives and actual secrets, remove the actual secrets 80 | first before adding `pragma` comments to the false positives. 81 | 82 | 83 | ### Updating `.secrets.baseline` 84 | 85 | To exclude a false positive, you can also [update the `.secrets.baseline` by repeating the same two 86 | commands as in the initial setup](#using-the-detect-secrets-pre-commit-hook). 87 | 88 | During auditing, if the detected secret is actually a secret (or other sensitive information), 89 | remove the secret and re-commit. There is no need to update the `.secrets.baseline` file in this 90 | case. 91 | 92 | If your commit contains a mixture of false positives and actual secrets, remove the actual secrets 93 | first before updating and auditing the `.secrets.baseline` file. 94 | 95 | 96 | [detect-secrets]: https://github.com/Yelp/detect-secrets 97 | [detect-secrets-plugins]: https://github.com/Yelp/detect-secrets#currently-supported-plugins 98 | 99 | --- 100 | 101 | Credit: This document has been adapted from the 102 | [govukcookiecutter](https://github.com/best-practice-and-impact/govcookiecutter/). 103 | -------------------------------------------------------------------------------- /docs/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | This document contains instructions to get a fully working development environment for running this repo. 4 | 5 | 6 | ## 1. pyenv 7 | 8 | Install here: [https://github.com/pyenv/pyenv#homebrew-on-macos] 9 | 10 | Configure by adding the following to your `~/.zshrc` or equivalent: 11 | 12 | ```sh 13 | # Pyenv environment variables 14 | export PYENV_ROOT="$HOME/.pyenv" 15 | export PATH="$PYENV_ROOT/bin:$PATH" 16 | 17 | # Pyenv initialization 18 | eval "$(pyenv init --path)" 19 | eval "$(pyenv init -)" 20 | ``` 21 | 22 | Basic usage: 23 | 24 | ```sh 25 | # Check Python versions 26 | pyenv install --list 27 | 28 | # Install the Python version defined in this repo 29 | pyenv install $(cat .python-version) 30 | 31 | # See installed Python versions 32 | pyenv versions 33 | ``` 34 | 35 | 36 | ## 2. [pyenv-virtualenvwrapper](https://github.com/pyenv/pyenv-virtualenvwrapper) 37 | 38 | ```sh 39 | # Install with homebrew (recommended if you installed pyenv with homebrew) 40 | brew install pyenv-virtualenvwrapper 41 | ``` 42 | 43 | Configure by adding the following to your `~/.zshrc` or equivalent: 44 | 45 | ```sh 46 | # pyenv-virtualenvwrapper 47 | export PYENV_VIRTUALENVWRAPPER_PREFER_PYVENV="true" 48 | export WORKON_HOME=$HOME/.virtualenvs 49 | export PROJECT_HOME=$HOME/code # <- change this to wherever you store your repos 50 | export VIRTUALENVWRAPPER_PYTHON=$HOME/.pyenv/shims/python 51 | pyenv virtualenvwrapper_lazy 52 | ``` 53 | 54 | Test everything is working by opening a new shell (e.g. new Terminal window): 55 | 56 | ```sh 57 | # Change to the Python version you just installed 58 | pyenv shell $(cat .python-version) 59 | # This only needs to be run once after installing a new Python version through pyenv 60 | # in order to initialise virtualenvwrapper for this Python version 61 | python -m pip install --upgrade pip 62 | python -m pip install virtualenvwrapper 63 | pyenv virtualenvwrapper_lazy 64 | 65 | # Create test virtualenv (if this doesn't work, try sourcing ~/.zshrc or opening new shell) 66 | mkvirtualenv venv_test 67 | which python 68 | python -V 69 | 70 | # Deactivate & remove test virtualenv 71 | deactivate 72 | rmvirtualenv venv_test 73 | ``` 74 | 75 | 76 | ## 3. Get the repo & initialise the repo environment 77 | 78 | ⚠️ N.B. You should replace `REPO_GIT_URL` here with your actual URL to your GitHub repo. 79 | 80 | ```sh 81 | git clone ${REPO_GIT_URL} 82 | pyenv shell $(cat .python-version) 83 | 84 | # Make a new virtual environment using the Python version & environment name specified in the repo 85 | mkvirtualenv -p python$(cat .python-version) $(cat .venv) 86 | python -V # check this is the correct version of Python 87 | python -m pip install --upgrade pip 88 | ``` 89 | 90 | 91 | ## 4. Install Python requirements into the virtual environment using [Poetry](https://python-poetry.org/docs/) 92 | 93 | Install Poetry onto your system by following the instructions here: [https://python-poetry.org/docs/] 94 | 95 | Note that Poetry "lives" outside of project/environment, and if you follow the recommended install 96 | process it will be installed isolated from the rest of your system. 97 | 98 | ```sh 99 | # Update Poetry regularly as you would any other system-level tool. Poetry is environment agnostic, 100 | # it doesn't matter if you run this command inside/outside the virtualenv. 101 | poetry self update 102 | 103 | # This command should be run inside the virtualenv. 104 | poetry install --no-root --sync 105 | ``` 106 | 107 | 108 | ## 5. [zsh-autoswitch-virtualenv](https://github.com/MichaelAquilina/zsh-autoswitch-virtualenv) 109 | 110 | Download with `git clone "https://github.com/MichaelAquilina/zsh-autoswitch-virtualenv.git" "$ZSH_CUSTOM/plugins/autoswitch_virtualenv"` 111 | 112 | Configure by adding the following to your `~/.zshrc` or equivalent: 113 | 114 | ```sh 115 | # Find line containing plugins=(git) and replace with below 116 | plugins=(git autoswitch_virtualenv) 117 | ``` 118 | 119 | Check it's working by cd-ing into & out of the repo. The environment should load & unload respectively. 120 | 121 | 122 | ## 6. Install [Poetry Up](https://github.com/MousaZeidBaker/poetry-plugin-up) 123 | 124 | This is a useful Poetry plugin that updates dependencies and bumps their versions in the 125 | pyproject.toml file. The version constraints are respected, unless the `--latest` flag is passed, in 126 | which case dependencies are updated to latest available compatible versions. 127 | 128 | ```sh 129 | # Installation 130 | poetry self add poetry-plugin-up 131 | 132 | # Usage 133 | poetry up 134 | poetry up --latest 135 | ``` 136 | 137 | 138 | ## 7. Add secrets into .env 139 | 140 | - Run `cp .env.template .env` and update the secrets. 141 | - [Install direnv](https://direnv.net/) to autoload environment variables specified in `.env` 142 | - Run `direnv allow` to authorise direnv to load the secrets from `.env` into the environment 143 | (these will unload when you `cd` out of the repo; note that you will need to re-run this 144 | command whenever you change `.env`) 145 | 146 | 147 | ## 8. Initialise the `detect-secrets` pre-commit hook 148 | 149 | We use [`detect-secrets`](https://github.com/Yelp/detect-secrets) to check that no secrets are 150 | accidentally committed. Please read [docs/detect_secrets.md](docs/detect_secrets.md) for more information. 151 | 152 | 153 | ```shell 154 | # Generate a baseline 155 | detect-secrets scan > .secrets.baseline 156 | 157 | # You may want to check/amend the exclusions in `.pre-commit-config.yaml` e.g. 158 | detect-secrets --verbose scan \ 159 | --exclude-files 'poetry\.lock' \ 160 | --exclude-files '\.secrets\.baseline' \ 161 | --exclude-files '\.env\.template' \ 162 | --exclude-secrets 'password|ENTER_PASSWORD_HERE|INSERT_API_KEY_HERE' \ 163 | --exclude-lines 'integrity=*sha' \ 164 | > .secrets.baseline 165 | 166 | # Audit the generated baseline 167 | detect-secrets audit .secrets.baseline 168 | ``` 169 | 170 | When you run this command, you'll enter an interactive console. This will present you with a list 171 | of high-entropy string and/or anything which could be a secret. It will then ask you to verify 172 | whether this is the case. This allows the hook to remember false positives in the future, and alert 173 | you to new secrets. 174 | 175 | 176 | ## 9. Project-specific setup 177 | 178 | Please check [docs/project_specific_setup.md](docs/project_specific_setup.md) for further instructions. 179 | -------------------------------------------------------------------------------- /docs/project_specific_setup.md: -------------------------------------------------------------------------------- 1 | # Project-specific setup 2 | 3 | This document contains setup instructions specifically for this project only. This design enables 4 | us to keep other docs easily aligned with future upstream changes to 5 | [coefficient-cookiecutter](https://github.com/CoefficientSystems/coefficient-cookiecutter/). 6 | 7 | 8 | ## llama-cpp-python 9 | 10 | Follow the instructions here: https://abetlen.github.io/llama-cpp-python/macos_install/ 11 | 12 | ```sh 13 | pip uninstall llama-cpp-python -y 14 | CMAKE_ARGS="-DLLAMA_METAL=on" FORCE_CMAKE=1 pip install -U llama-cpp-python --no-cache-dir 15 | pip install 'llama-cpp-python[server]' 16 | ``` 17 | 18 | Download a model file, see the following for advice: 19 | - [https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF#provided-files] 20 | - [https://huggingface.co/TheBloke/Llama-2-13B-Chat-GGUF#provided-files] 21 | - [https://huggingface.co/TheBloke/Llama-2-70B-Chat-GGUF#provided-files] 22 | 23 | ```sh 24 | # Llama-2 (7 billion parameters, 4.08GB file size, up to 7.2GB RAM required to run) 25 | huggingface-cli download TheBloke/Llama-2-7b-Chat-GGUF llama-2-7b-chat.Q4_K_M.gguf --local-dir . --local-dir-use-symlinks False 26 | mv ./llama-2-7b-chat.Q4_K_M.gguf ./models/ 27 | 28 | # Llama-2 (13 billion parameters, 7.87GB file size, up to 10.37GB RAM required to run) 29 | huggingface-cli download TheBloke/Llama-2-13b-Chat-GGUF llama-2-13b-chat.Q4_K_M.gguf --local-dir . --local-dir-use-symlinks False 30 | mv ./llama-2-13b-chat.Q4_K_M.gguf ./models/ 31 | 32 | # Llama-2 (70 billion parameters, 41.42GB file size, up to 44GB RAM required to run) 33 | huggingface-cli download TheBloke/Llama-2-70b-Chat-GGUF llama-2-70b-chat.Q4_K_M.gguf --local-dir . --local-dir-use-symlinks False 34 | mv ./llama-2-70b-chat.Q4_K_M.gguf ./models/ 35 | ``` 36 | 37 | Run the webserver: 38 | 39 | ```sh 40 | # config your gguf model path 41 | # make sure it is gguf and Q3 42 | export MODEL=./models/llama-2-7b-chat.Q4_K_M.gguf 43 | python3 -m llama_cpp.server --model $MODEL --n_gpu_layers 1 44 | 45 | # Note: If you omit the --n_gpu_layers 1 then CPU will be used 46 | ``` 47 | 48 | Try the Python API: 49 | 50 | ```python 51 | from llama_cpp import Llama 52 | llm = Llama(model_path="./models/llama-2-7b-chat.Q4_K_M.gguf") 53 | output = llm("Q: Name the planets in the solar system? A: ", max_tokens=64, stop=["Q:", "\n"], echo=True) 54 | print(output) 55 | print(output['choices'][0]['text']) 56 | ``` 57 | 58 | 59 | ## Jupyter kernel 60 | 61 | ```sh 62 | python -m ipykernel install --user --name chatefficient --display-name "Python (chatefficient)" 63 | ``` 64 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | This document contains instructions _only_ to get a fully working development environment for 4 | running this repo. For pre-requisites (e.g. `pyenv` install instructions) plus details on what's 5 | being installed and why, please see [docs/getting_started.md](docs/getting_started.md). 6 | 7 | We assume the following are installed and configured: 8 | - [pyenv](https://github.com/pyenv/pyenv) 9 | - [pyenv-virtualenvwrapper](https://github.com/pyenv/pyenv-virtualenvwrapper) 10 | - [Poetry](https://python-poetry.org/docs/) 11 | - [zsh-autoswitch-virtualenv](https://github.com/MichaelAquilina/zsh-autoswitch-virtualenv) 12 | - [direnv](https://direnv.net/) 13 | - [poetry up](https://github.com/MousaZeidBaker/poetry-plugin-up) 14 | 15 | 16 | ## Part 1: Generic Python setup 17 | 18 | ```sh 19 | # Get the repo 20 | git clone ${REPO_GIT_URL} 21 | 22 | # Install Python 23 | pyenv install $(cat .python-version) 24 | pyenv shell $(cat .python-version) 25 | python -m pip install --upgrade pip 26 | python -m pip install virtualenvwrapper 27 | pyenv virtualenvwrapper 28 | 29 | # Setup the virtualenv 30 | mkvirtualenv -p python$(cat .python-version) $(cat .venv) 31 | python -V 32 | python -m pip install --upgrade pip 33 | 34 | # Install dependencies with Poetry 35 | poetry self update 36 | poetry install --no-root --sync 37 | 38 | # Create templated .env for storing secrets 39 | cp .env.template .env 40 | direnv allow 41 | 42 | # Create and audit secrets baseline 43 | # N.B. Adjust the exclusions here depending on your needs (check .pre-commit-config.yaml) 44 | detect-secrets --verbose scan \ 45 | --exclude-files 'poetry\.lock' \ 46 | --exclude-files '\.secrets\.baseline' \ 47 | --exclude-files '\.env\.template' \ 48 | --exclude-secrets 'password|ENTER_PASSWORD_HERE|INSERT_API_KEY_HERE' \ 49 | --exclude-lines 'integrity=*sha' \ 50 | > .secrets.baseline 51 | 52 | detect-secrets audit .secrets.baseline 53 | ``` 54 | 55 | 56 | ## Part 2: Project-specific setup 57 | 58 | Please check [docs/project_specific_setup.md](project_specific_setup.md) for further instructions. 59 | -------------------------------------------------------------------------------- /docs/release_process.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | > This process follows [this Git Flow release model](https://nvie.com/posts/a-successful-git-branching-model/). 4 | > There is an excellent [Git Flow cheatsheet here](https://danielkummer.github.io/git-flow-cheatsheet/). 5 | > You will need to install the Git Flow helper extension: `brew install git-flow-avh` and 6 | > initialise your repo (see the cheatsheet above, or this can also be done from within Sourcetree). 7 | 8 | **Replace VERSION with the relevant milestone, e.g. `0.3`** 9 | 10 | 11 | ## 1. CLI Steps 12 | 13 | Sourcetree has great built-in support for Git Flow from the `Repository > Git flow` menu. We list 14 | the commands below, but this can also be done via the GUI if you prefer. 15 | 16 | ```sh 17 | # Ensure your working copy is clean before starting (e.g. stash any WIP). 18 | # Fetch & pull latest origin/develop 19 | git fetch && git checkout develop && git pull 20 | 21 | # Locally, start a new release 22 | git flow release start VERSION 23 | 24 | # Summary of actions: 25 | # - A new branch 'release/VERSION' was created, based on 'develop' 26 | # - You are now on branch 'release/VERSION' 27 | 28 | # Follow-up actions: 29 | # - Bump the version number now! 30 | # - Start committing last-minute fixes in preparing your release (e.g. towncrier) 31 | # - Use amend commits here if possible to keep commits to a minimum 32 | # git commit --amend -m "updated commit message" 33 | # - You don't have to push the release branch unless a) you'd like it reviewed, b) to run CI, c) others may wish to add commits to this release. 34 | 35 | # Towncrier update 36 | # - Review all towncrier entries. 37 | # - Any missing vs issues closed within the milestone (don't forget bugs & maintenance)? Do the entries look good? 38 | # - (Optional) towncrier preview: `towncrier build --version=VERSION --draft` 39 | # - Publish towncrier update: `towncrier build --version=VERSION` 40 | # - Add any additional notes to the CHANGELOG.md as required 41 | 42 | # Update project version number 43 | # - This is in [poetry] section at the top of pyproject.toml 44 | 45 | # (Optional) pre-commit 46 | # - You shouldn't need to run pre-commit unless you've changed things manually 47 | # - If you're seeing changes to poetry.lock, try clearing your Poetry cache & run again 48 | poetry cache clear pypi --all 49 | pre-commit run --all-files --hook-stage=manual 50 | 51 | # Commit & amend commit as required 52 | 53 | # (Optional) If others have commits to add to this release, you can push as follows 54 | git flow release publish VERSION 55 | 56 | # Complete the release by merging back into `main` and `develop` 57 | # - Add -k if you do not want to auto-delete the release branch 58 | # - Add -p if you want to auto-push to origin 59 | # - Just use "Release VERSION" as commit messages 60 | git flow release finish -n VERSION 61 | 62 | # Summary of actions: 63 | # - Release branch 'release/VERSION' has been merged into 'main' 64 | # - Master branch 'main' has been back-merged into 'develop' 65 | # - Release branch 'release/VERSION' is still locally available 66 | # - You are now on branch 'develop' 67 | 68 | # Tag the release 69 | git checkout main 70 | git tag VERSION 71 | git push origin --tags 72 | 73 | # Check everything over, if you're happy, push `develop`, push `main` and delete your release branch. 74 | git checkout develop && git push 75 | git checkout main && git push 76 | git branch -D release/VERSION 77 | ``` 78 | 79 | ## 2. GitHub Steps 80 | 81 | - Copy the **raw markdown** for the release notes in CHANGELOG: [https://github.com/CoefficientSystems/coefficient-cookiecutter/blob/main/CHANGELOG.md] 82 | - Once you've pushed the tag, you will see it on this page: [https://github.com/CoefficientSystems/coefficient-cookiecutter/tags] 83 | - Edit the tag and add the release notes 84 | - You will then see the release appear here: [https://github.com/CoefficientSystems/coefficient-cookiecutter/releases] 85 | - This also sends an email update to anyone on the team who has subscribed containing formatted release notes. 86 | - Once the release is created, edit the release and assign the milestone to the release. Save changes. 87 | 88 | To finish, copy the release notes and post in any relevant Slack channel or email lists to inform members about the release. 89 | -------------------------------------------------------------------------------- /docs/updating_requirements.md: -------------------------------------------------------------------------------- 1 | # Updating requirements 2 | 3 | ```sh 4 | # Create feature branch 5 | poetry-sync 6 | poetryup --latest 7 | # If any issues, likely due to two "latest" packages conflicting. See example below. 8 | poetry-regenerate 9 | poetry-sync 10 | pre-commit run --all-files --hook-stage=manual 11 | poetry-sync 12 | pytest 13 | # Commit & push 14 | ``` 15 | 16 | If the latest version of `green` requires `blue (>=1.2, <1.3)` and the latest version of `blue` is 17 | `1.4` then you will encounter a `SolverProblemError`, for example: 18 | 19 | ```sh 20 | SolverProblemError 21 | 22 | Because green (0.8) depends on blue (>=1.2,<1.3) 23 | and no versions of green match (>=0.8,<1.0) requires blue (>=1.2,<1.3). 24 | So, because src depends on both blue (^1.4) and green (^0.8), version solving failed. 25 | ``` 26 | 27 | In this situation, do the following: 28 | - Comment out `blue` 29 | - Re-run `poetryup --latest` 30 | - Handle any other new package conflicts the same way until poetryup resolves 31 | - Uncomment out `blue` with package version that works with `green`, e.g. `blue = "^1.2"` 32 | - Run `poetry-regenerate` onwards 33 | -------------------------------------------------------------------------------- /docs/using_poetry.md: -------------------------------------------------------------------------------- 1 | # Updating Python requirements using Poetry 2 | 3 | This project uses Poetry to manage dependencies: 4 | [https://python-poetry.org/docs/](https://python-poetry.org/docs/) 5 | 6 | The Poetry virtualenv should activate automatically if you are using 7 | [zsh-autoswitch-virtualenv](https://github.com/MichaelAquilina/zsh-autoswitch-virtualenv). You can 8 | also activate it manually by running `workon chatefficient` (preferred) or `poetry shell` (Poetry 9 | created/managed virtualenv). 10 | 11 | 12 | ## Poetry tl;dr 13 | 14 | ```bash 15 | # Sync your environment with poetry.lock 16 | poetry install --no-root --sync 17 | 18 | # Add and install a new package into your environment (or update an existing one) 19 | # 1. Add a package to pyproject.toml 20 | # 2. Run this 21 | poetry add package@version 22 | 23 | # N.B. We lock & export to requirements when you run `pre-commit run --all-files` so you 24 | # shouldn't need to run the commands below yourself. 25 | 26 | # Compile all poetry.lock file 27 | poetry update 28 | 29 | # Export to requirements.txt (this is for compatibility with team members who may not 30 | # have Poetry; Poetry itself uses poetry.lock when you run `poetry install`) 31 | poetry export -f requirements.txt --output requirements.txt 32 | ``` 33 | 34 | 35 | ## Poetry FAQ 36 | 37 | **How do I sync my environment with the latest `poetry.lock`?** 38 | To install dependencies, simply `cd backend-security` and run `poetry install --no-root --sync`. 39 | This will resolve & install all deps in `pyproject.toml` via `poetry.lock`. Options: 40 | - `--no-root` skips installing the repo itself as a Python package 41 | - `--sync` removes old dependencies no longer present in the lock file 42 | 43 | You may wish to set an alias e.g. `alias poetry-sync='poetry install --no-root --sync'` 44 | 45 | **How do I add/change packages?** 46 | In order to install a new package or remove an old one, please edit `pyproject.toml` 47 | or use either `poetry add package` or `pipx run poetry add package@version` as desired. 48 | 49 | **How do I update `poetry.lock` to match new changes in `pyproject.toml`?** 50 | You can run `poetry update`, although this will also get run when you run pre-commit. This fetches 51 | the latest matching versions (according to `pyproject.toml`) and updates `poetry.lock`, and is 52 | equivalent to deleting `poetry.lock` and running `poetry install --no-root` again from scratch. 53 | 54 | **What do `~`, `^` and `*` mean?** 55 | Check out the Poetry docs page for [specifying version constraints](https://python-poetry.org/docs/dependency-specification/#version-constraints). 56 | Environment markers are great, e.g. this means "for Python 2.7.* OR Windows, install pathlib2 >=2.2, <3.0" 57 | `pathlib2 = { version = "^2.2", markers = "python_version ~= '2.7' or sys_platform == 'win32'" }` 58 | -------------------------------------------------------------------------------- /docs/using_towncrier.md: -------------------------------------------------------------------------------- 1 | # Changelog & releases 2 | 3 | The format of `CHANGELOG.md` is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 4 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 5 | 6 | 7 | ## Using towncrier to create news entries 8 | 9 | We use [towncrier](https://github.com/twisted/towncrier) to reduce merge conflicts by generating 10 | `CHANGELOG.md` from news fragments, rather than maintaining it directly. Create a news fragment for 11 | each MR if you would like to ensure your changes are communicated to other project contributors. 12 | 13 | ```bash 14 | # To create a news entry for an added feature relating to MR !123 15 | # Adding --edit is optional and will open in your default shell's $EDITOR 16 | towncrier create 123.added --edit 17 | ``` 18 | 19 | Top tips: 20 | - You may wish to add `export EDITOR="code -w"` to your `.zshrc` file to open this directly in VS Code. 21 | - News fragments should be written in markdown. 22 | - The generated news fragments live in `.changelog/` and can be easily rewritten as an MR evolves. 23 | 24 | We use the following custom types (adapted from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)): 25 | - `.added` for new features 26 | - `.changed` for changes in existing functionality 27 | - `.deprecated` for soon-to-be removed features 28 | - `.removed` for now removed features 29 | - `.fixed` for any bug fixes 30 | - `.security` in case of vulnerabilities 31 | - `.analysis` for data analyses 32 | - `.docs` for documentation improvements 33 | - `.maintenance` for maintenance tasks & upgrades 34 | 35 | 36 | ## Releasing a new version & updating `CHANGELOG.md` 37 | 38 | Release versions are tied to Gitlab milestones and sprints. Release checklist: 39 | 40 | 1. Review MRs assigned to the release milestone in Gitlab & reallocate to the next release. 41 | 2. Run `towncrier build --version=VERSION` (preview with `--draft`) 42 | 3. Add a git tag for the release. 43 | -------------------------------------------------------------------------------- /images/about-me.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/about-me.jpeg -------------------------------------------------------------------------------- /images/civil-service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/civil-service.png -------------------------------------------------------------------------------- /images/coefficient-aidl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/coefficient-aidl.png -------------------------------------------------------------------------------- /images/eh-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/eh-logo.png -------------------------------------------------------------------------------- /images/eh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/eh.png -------------------------------------------------------------------------------- /images/ml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/ml.png -------------------------------------------------------------------------------- /images/obt-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/obt-banner.png -------------------------------------------------------------------------------- /images/one-big-thing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/one-big-thing.png -------------------------------------------------------------------------------- /images/pydata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/pydata.png -------------------------------------------------------------------------------- /images/vicuna-chat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/images/vicuna-chat.gif -------------------------------------------------------------------------------- /models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/models/.gitkeep -------------------------------------------------------------------------------- /notebooks/Chroma.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "368dccec-1d58-4167-a50d-01c56d98c24e", 6 | "metadata": {}, 7 | "source": [ 8 | "" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "db52fb18-e4c3-4d38-86d6-cf10abeca5e8", 14 | "metadata": {}, 15 | "source": [ 16 | "# Build Your Own Private ChatGPT Super-Assistant Using Streamlit, LangChain, Chroma & Llama 2\n", 17 | "## Chroma Demo\n", 18 | "**Questions?** contact@coefficient.ai / [@CoefficientData](https://twitter.com/CoefficientData)\n", 19 | "\n", 20 | "---" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "id": "af9dfd7f-dcc6-418c-bc6b-02c7771e07fa", 26 | "metadata": {}, 27 | "source": [ 28 | "## 0. Imports" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "id": "d73809ac-30df-4182-8d69-e49d9ccc60b0", 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import chromadb\n", 39 | "from dotenv import load_dotenv\n", 40 | "\n", 41 | "from utils import scrape_page" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "id": "f5ecd369-baf5-4c19-9245-1cad96fbd17a", 47 | "metadata": {}, 48 | "source": [ 49 | "## 1. Chroma Basics" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "id": "bbae2d37-e3fe-4fab-89d4-16cdda33b808", 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "# Get the Chroma client\n", 60 | "chroma_client = chromadb.Client()" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "id": "62019417-87d1-4187-b3a9-2ee3d91aa01e", 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "# Create a collection\n", 71 | "collection = chroma_client.create_collection(name=\"my_collection\")" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "id": "0ccbc06d-74ed-4657-9468-a4ae0d3be0d8", 77 | "metadata": {}, 78 | "source": [ 79 | "Collections are where you'll store your embeddings, documents, and any additional metadata. " 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "id": "9063c858-e0bf-4933-b0d0-29e178c18bec", 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "# Add some text documents to the collection\n", 90 | "collection.add(\n", 91 | " documents=[\"This is a document\", \"This is another document\"],\n", 92 | " metadatas=[{\"source\": \"my_source\"}, {\"source\": \"my_source\"}],\n", 93 | " ids=[\"id1\", \"id2\"],\n", 94 | ")" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "id": "43e82e2a-f63b-4594-9ce5-25c7908ce1fd", 100 | "metadata": {}, 101 | "source": [ 102 | "Chroma will store your text, and handle tokenization, embedding, and indexing automatically." 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "id": "920b8ae0-c0de-4450-bb41-14e6f2ab0b67", 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "collection2 = chroma_client.create_collection(name=\"another_collection\")" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "id": "0e0d0636-443d-4309-b47a-25d009533044", 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "# Load in pre-generated embeddings\n", 123 | "collection2.add(\n", 124 | " embeddings=[[1.2, 2.3, 4.5], [6.7, 8.2, 9.2]],\n", 125 | " documents=[\"This is a document\", \"This is another document\"],\n", 126 | " metadatas=[{\"source\": \"my_source\"}, {\"source\": \"my_source\"}],\n", 127 | " ids=[\"id1\", \"id2\"],\n", 128 | ")" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "id": "d3a57272-0353-4332-9ee5-2109a33a3842", 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "# Query the collection\n", 139 | "results = collection.query(query_texts=[\"This is a query document\"], n_results=2)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "id": "0e240a34-329a-47b3-8581-b359d35489c9", 146 | "metadata": {}, 147 | "outputs": [], 148 | "source": [ 149 | "results" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "id": "8b15485f-7519-46ad-bb15-5d2f351b22b8", 155 | "metadata": {}, 156 | "source": [ 157 | "- **Where is data stored?** By default data stored in Chroma is ephemeral making it easy to prototype scripts.\n", 158 | "- **Can data be persisted?** It's easy to make Chroma persistent so you can reuse every collection you create and add more documents to it later. It will load your data automatically when you start the client, and save it automatically when you close it.\n", 159 | "\n", 160 | "Check out the [Usage Guide](https://docs.trychroma.com/usage-guide) for more info." 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "id": "2e24f61d-1db6-4021-a851-866b5dd013be", 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "persistent_client = chromadb.PersistentClient(path=\".\")\n", 171 | "persistent_collection = persistent_client.create_collection(name=\"example_collection\")" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "id": "3e12c1ac-bd27-442d-8d1a-5439d5a30abc", 177 | "metadata": {}, 178 | "source": [ 179 | "---" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "id": "68b5edd4-ddb4-4f43-9488-f2028670190c", 185 | "metadata": {}, 186 | "source": [ 187 | "## 2. Create embeddings with LangChain" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "id": "66d53907-b103-4c48-bad2-88b89de95134", 193 | "metadata": {}, 194 | "source": [ 195 | "### Create embeddings with Llama" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "id": "a4790923-7eb0-4f37-a975-a2d6d353b4f7", 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "from langchain.embeddings.llamacpp import LlamaCppEmbeddings" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": null, 211 | "id": "049bedfa-b52a-4c00-af91-4d861708b957", 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "# Make sure the model path is correct!\n", 216 | "llama_embedder = LlamaCppEmbeddings(model_path=\"../models/llama-2-7b-chat.Q4_K_M.gguf\")" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "id": "caa5eada-90c6-4972-9ea0-0ea98d0027f3", 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "text = \"This is a test document.\"\n", 227 | "query_result = llama_embedder.embed_query(text)" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "id": "0216db12-4768-4765-9d88-5f838a374abd", 234 | "metadata": {}, 235 | "outputs": [], 236 | "source": [ 237 | "len(query_result)" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "id": "882f79cd-dbe5-411d-ab12-d0b6d8395f9b", 244 | "metadata": {}, 245 | "outputs": [], 246 | "source": [ 247 | "query_result[:10]" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "id": "c202e270-f760-480f-bb1f-301eac40a3ea", 254 | "metadata": {}, 255 | "outputs": [], 256 | "source": [ 257 | "doc_result = llama_embedder.embed_documents([text])" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": null, 263 | "id": "4a83562b-c29c-41da-91c9-daa43bb071cd", 264 | "metadata": {}, 265 | "outputs": [], 266 | "source": [ 267 | "len(doc_result)" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "id": "92eb9ae9-5540-4579-94ef-f390a2bbc4d6", 274 | "metadata": {}, 275 | "outputs": [], 276 | "source": [ 277 | "doc_result[0][:10]" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "id": "91bbb194-b084-4949-b9c8-f965e28a5403", 283 | "metadata": {}, 284 | "source": [ 285 | "### Create embeddings using LangChain" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": null, 291 | "id": "4ac59e22-a591-4a97-a69a-a19c34a902a5", 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "# Let's get some more interesting data\n", 296 | "url = \"https://www.gov.uk/government/publications/frontier-ai-capabilities-and-risks-discussion-paper/frontier-ai-capabilities-and-risks-discussion-paper\"\n", 297 | "paper = scrape_page(url)" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "id": "a2bfc5ee-cdee-4aa4-ba71-8e5450ca39e7", 304 | "metadata": {}, 305 | "outputs": [], 306 | "source": [ 307 | "# Take a peek\n", 308 | "print(f\"{len(paper)=}\\n\\nExtract:\")\n", 309 | "print(paper[10000:15000])" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": null, 315 | "id": "a7553911-ab93-408f-afbf-0f4a17e14f6a", 316 | "metadata": {}, 317 | "outputs": [], 318 | "source": [ 319 | "# Save it to disk - we only do 5000 characters as Llama is very slow at embedding\n", 320 | "with open(\"frontier-ai-paper.txt\", \"w\") as f:\n", 321 | " f.write(paper)" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": null, 327 | "id": "ab3dc727-51ca-4f67-974b-8b8209f9b75c", 328 | "metadata": {}, 329 | "outputs": [], 330 | "source": [ 331 | "from langchain.document_loaders import TextLoader\n", 332 | "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", 333 | "\n", 334 | "# Load the document\n", 335 | "raw_documents = TextLoader(\"frontier-ai-paper.txt\").load()\n", 336 | "\n", 337 | "# Split it into chunks\n", 338 | "text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)\n", 339 | "documents = text_splitter.split_documents(raw_documents)" 340 | ] 341 | }, 342 | { 343 | "cell_type": "code", 344 | "execution_count": null, 345 | "id": "94779f67-1c94-4a81-875f-bfa8d016108f", 346 | "metadata": {}, 347 | "outputs": [], 348 | "source": [ 349 | "len(documents)" 350 | ] 351 | }, 352 | { 353 | "cell_type": "code", 354 | "execution_count": null, 355 | "id": "dfc2d338-61ba-4990-8f10-f40bd2c8fd05", 356 | "metadata": {}, 357 | "outputs": [], 358 | "source": [ 359 | "documents[:2]" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "id": "493fd741-9062-4063-97dd-2365e0f2d877", 366 | "metadata": {}, 367 | "outputs": [], 368 | "source": [ 369 | "%%time\n", 370 | "from langchain.vectorstores import Chroma\n", 371 | "\n", 372 | "# Embed each chunk and load it into the vector store\n", 373 | "# db = Chroma.from_documents(documents, llama_embedder)" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "id": "bc87d1e4-d83e-4144-86e9-7df7b6cdd498", 379 | "metadata": {}, 380 | "source": [ 381 | "### Similarity search" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": null, 387 | "id": "7d6d7eb3-83cc-4317-925f-fbd791cdfc18", 388 | "metadata": {}, 389 | "outputs": [], 390 | "source": [ 391 | "query = \"What are scaffolds in AI?\"\n", 392 | "docs = db.similarity_search(query)\n", 393 | "print(docs[0].page_content.replace(\"\\n\", \" \"))" 394 | ] 395 | }, 396 | { 397 | "cell_type": "markdown", 398 | "id": "8a787276-c0f2-4449-898b-d8c226f4b25e", 399 | "metadata": {}, 400 | "source": [ 401 | "## Using SentenceTransformerEmbeddings" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": null, 407 | "id": "d878b1b6-1253-458d-b5c6-1d6f2e5a4f45", 408 | "metadata": {}, 409 | "outputs": [], 410 | "source": [ 411 | "# Initialise the new embedder\n", 412 | "from langchain.embeddings.sentence_transformer import SentenceTransformerEmbeddings\n", 413 | "\n", 414 | "st_embedder = SentenceTransformerEmbeddings(model_name=\"all-MiniLM-L6-v2\")" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": null, 420 | "id": "30a8b09c-40c2-4734-901b-eefeb39ef43e", 421 | "metadata": {}, 422 | "outputs": [], 423 | "source": [ 424 | "%%time\n", 425 | "# Compare this with SentenceTransformerEmbeddings\n", 426 | "db2 = Chroma.from_documents(documents, st_embedder, collection_name=\"st_embeddings\")" 427 | ] 428 | }, 429 | { 430 | "cell_type": "markdown", 431 | "id": "a7581e9a-34f5-4e16-a10a-3ba8d96c971c", 432 | "metadata": {}, 433 | "source": [ 434 | "**Note: It takes `SentenceTransformerEmbeddings` <1 second, and Llama 2 several minutes!**" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": null, 440 | "id": "8503e648-e7a2-4f36-8545-08c4c9776da6", 441 | "metadata": {}, 442 | "outputs": [], 443 | "source": [ 444 | "# Save the whole paper this time, Sentence-Transformers can handle it\n", 445 | "print(f\"{len(paper)=}\")\n", 446 | "with open(\"frontier-ai-paper.txt\", \"w\") as f:\n", 447 | " f.write(paper)" 448 | ] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "execution_count": null, 453 | "id": "b2684eca-aeb0-43e5-b65f-b81b53fca512", 454 | "metadata": {}, 455 | "outputs": [], 456 | "source": [ 457 | "raw_documents = TextLoader(\"frontier-ai-paper.txt\").load()\n", 458 | "text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\n", 459 | "documents = text_splitter.split_documents(raw_documents)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": null, 465 | "id": "7910e392-b4fd-40a4-a602-10957c701469", 466 | "metadata": {}, 467 | "outputs": [], 468 | "source": [ 469 | "len(documents)" 470 | ] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "execution_count": null, 475 | "id": "0fc4052d-f962-4580-bf3a-49d3ac224756", 476 | "metadata": {}, 477 | "outputs": [], 478 | "source": [ 479 | "%%time\n", 480 | "db2 = Chroma.from_documents(documents, st_embedder, collection_name=\"st_embeddings\")" 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": null, 486 | "id": "e8c9f18e-9f93-42d0-9abc-dcfa40d74cec", 487 | "metadata": {}, 488 | "outputs": [], 489 | "source": [ 490 | "docs = db2.similarity_search(\"What are scaffolds in AI?\")\n", 491 | "print(docs[0].page_content.replace(\"\\n\", \" \"))" 492 | ] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": null, 497 | "id": "9b514178-3b7c-4f56-b96f-5489f87c856c", 498 | "metadata": {}, 499 | "outputs": [], 500 | "source": [ 501 | "docs = db2.similarity_search(\"What are the top risks of frontier models?\")\n", 502 | "print(docs[0].page_content.replace(\"\\n\", \" \"), \"\\n\\n\")\n", 503 | "print(docs[1].page_content.replace(\"\\n\", \" \"))" 504 | ] 505 | }, 506 | { 507 | "cell_type": "markdown", 508 | "id": "f44934f7-6077-4e87-a172-5367d6163f53", 509 | "metadata": {}, 510 | "source": [ 511 | "### Maximum marginal relevance search (MMR)\n", 512 | "Maximal marginal relevance optimizes for similarity to query and diversity among selected documents. It is also supported in async API." 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": null, 518 | "id": "a08a828a-21ef-4e64-a50a-38e7c85262b4", 519 | "metadata": {}, 520 | "outputs": [], 521 | "source": [ 522 | "query = \"What are the top risks of frontier models?\"\n", 523 | "retriever = db2.as_retriever(search_type=\"mmr\")\n", 524 | "docs = retriever.get_relevant_documents(query)\n", 525 | "\n", 526 | "print(docs[0].page_content.replace(\"\\n\", \" \"), \"\\n\\n\")\n", 527 | "print(docs[1].page_content.replace(\"\\n\", \" \"))" 528 | ] 529 | }, 530 | { 531 | "cell_type": "markdown", 532 | "id": "a84226ab-965a-4001-b4de-5a1215ea8632", 533 | "metadata": {}, 534 | "source": [ 535 | "### Deep linking" 536 | ] 537 | }, 538 | { 539 | "cell_type": "code", 540 | "execution_count": null, 541 | "id": "324306fd-c201-4fe4-b33c-d41dd94d2744", 542 | "metadata": {}, 543 | "outputs": [], 544 | "source": [ 545 | "docs = db2.similarity_search(\"Which model has the best benchmark?\")\n", 546 | "result = docs[1].page_content\n", 547 | "print(result.replace(\"\\n\", \" \"))" 548 | ] 549 | }, 550 | { 551 | "cell_type": "code", 552 | "execution_count": null, 553 | "id": "75935907-78c2-478b-834c-2f0ad7a9c394", 554 | "metadata": {}, 555 | "outputs": [], 556 | "source": [ 557 | "import urllib.parse" 558 | ] 559 | }, 560 | { 561 | "cell_type": "code", 562 | "execution_count": null, 563 | "id": "8f50e576-79cd-4a7b-968b-73873d55ce19", 564 | "metadata": {}, 565 | "outputs": [], 566 | "source": [ 567 | "encoded_result = urllib.parse.quote(result[:50])\n", 568 | "encoded_result" 569 | ] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": null, 574 | "id": "e11de357-9267-4b34-94cc-0f40d527fdc3", 575 | "metadata": {}, 576 | "outputs": [], 577 | "source": [ 578 | "deeplink = f\"{url}#:~:text={encoded_result}\"\n", 579 | "deeplink" 580 | ] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "id": "f8646efa-78bd-4ef8-baee-31aff6165fa7", 585 | "metadata": {}, 586 | "source": [ 587 | "---" 588 | ] 589 | }, 590 | { 591 | "cell_type": "markdown", 592 | "id": "f3b2a2e3-c1a4-41ab-9541-3f04d8cd4bb7", 593 | "metadata": {}, 594 | "source": [ 595 | "## 3. Exercise: Q&A bot with vector database\n", 596 | "\n", 597 | "> Combine the Chroma vector database with a Llama-based LangChain LLM to create a Q&A bot for the provided (or any other) URL.\n", 598 | "> Tips:\n", 599 | "> - Encode your queries using the Sentence-Transformer embedding & return the top documents\n", 600 | "> - Include the question alongside the top N documents into your LangChain LLM's context window\n", 601 | "> - Use Llama 2 to synthesise a coherent answer\n", 602 | ">\n", 603 | "> This approach enables LLMs to answer questions to things they haven't been pre-trained on by using the vector database as an \"encyclopedia\" that it can reference as needed. This is known as \"retrieval-augmented generation\" or \"RAG\"." 604 | ] 605 | }, 606 | { 607 | "cell_type": "code", 608 | "execution_count": null, 609 | "id": "c27e74d5-fde3-4e0a-b4bd-e61b4d41079a", 610 | "metadata": {}, 611 | "outputs": [], 612 | "source": [] 613 | }, 614 | { 615 | "cell_type": "markdown", 616 | "id": "9bdb3b81-ca58-43fc-9d50-a68395b79e37", 617 | "metadata": {}, 618 | "source": [ 619 | "---" 620 | ] 621 | }, 622 | { 623 | "cell_type": "markdown", 624 | "id": "42846d93-782b-4f49-9080-a5149d26fac5", 625 | "metadata": {}, 626 | "source": [ 627 | "## Where next?\n", 628 | "\n", 629 | "LangChain is far more powerful than we've seen so far! Here's an idea of what else you can do:\n", 630 | "- [Learn to use agents and tools with LangChain](https://python.langchain.com/docs/modules/agents/tools/) such as searching the web, querying APIs, reading papers on ArXiv, checking the weather, digesting articles on Wikipedia, making (and transcribing) calls with Twilio, accessing financial data and much more. Check out the [list of integrations here](https://python.langchain.com/docs/integrations/tools).\n", 631 | "- [Query a SQL database](https://python.langchain.com/docs/expression_language/cookbook/sql_db) with LangChain Runnables\n", 632 | "- [Write Python code](https://python.langchain.com/docs/expression_language/cookbook/code_writing) with LangChain\n", 633 | "- [Learn more about RAG](https://python.langchain.com/docs/expression_language/cookbook/retrieval) or use [this example to combine agents with the Chroma vector store](https://python.langchain.com/docs/modules/agents/how_to/agent_vectorstore)" 634 | ] 635 | } 636 | ], 637 | "metadata": { 638 | "kernelspec": { 639 | "display_name": "Python (chatefficient)", 640 | "language": "python", 641 | "name": "chatefficient" 642 | }, 643 | "language_info": { 644 | "codemirror_mode": { 645 | "name": "ipython", 646 | "version": 3 647 | }, 648 | "file_extension": ".py", 649 | "mimetype": "text/x-python", 650 | "name": "python", 651 | "nbconvert_exporter": "python", 652 | "pygments_lexer": "ipython3", 653 | "version": "3.10.13" 654 | } 655 | }, 656 | "nbformat": 4, 657 | "nbformat_minor": 5 658 | } 659 | -------------------------------------------------------------------------------- /notebooks/LangChain.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "368dccec-1d58-4167-a50d-01c56d98c24e", 6 | "metadata": {}, 7 | "source": [ 8 | "" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "db52fb18-e4c3-4d38-86d6-cf10abeca5e8", 14 | "metadata": {}, 15 | "source": [ 16 | "# Build Your Own Private ChatGPT Super-Assistant Using Streamlit, LangChain, Chroma & Llama 2\n", 17 | "## LangChain Demo\n", 18 | "**Questions?** contact@coefficient.ai / [@CoefficientData](https://twitter.com/CoefficientData)\n", 19 | "\n", 20 | "---" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "id": "af9dfd7f-dcc6-418c-bc6b-02c7771e07fa", 26 | "metadata": {}, 27 | "source": [ 28 | "## 0. Imports" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "id": "d73809ac-30df-4182-8d69-e49d9ccc60b0", 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import random\n", 39 | "\n", 40 | "from dotenv import load_dotenv\n", 41 | "\n", 42 | "from utils import scrape_page\n", 43 | "\n", 44 | "import warnings\n", 45 | "\n", 46 | "warnings.simplefilter(\"ignore\", UserWarning)\n", 47 | "\n", 48 | "load_dotenv();" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "id": "c8a2f7f0-8d61-4feb-b4c7-6cfff3c7cf9b", 54 | "metadata": {}, 55 | "source": [ 56 | "## 1. LangChain Basics" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "id": "76396764-2c53-4653-a085-6ad0c5a784e7", 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "from langchain_openai import OpenAI" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "id": "e0c3a224-ad9e-4fc9-abef-1a731af16952", 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "llm = OpenAI(model=\"gpt-3.5-turbo-instruct\", temperature=0.9)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "id": "99a511fe-afa5-417b-869a-cebda93113f5", 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "food1 = llm.predict(\"Give me a random food ingredient.\").strip().lower()\n", 87 | "food2 = llm.predict(\"Give me a random food ingredient.\").strip().lower()\n", 88 | "colour = llm.predict(\"Give me a random colour.\").strip().lower()\n", 89 | "\n", 90 | "prompt = f\"\"\"\n", 91 | "Write a recipe for a {colour} cocktail featuring {food1} and {food2}.\n", 92 | "Please come up with an fun name for the mocktail.\n", 93 | "\"\"\"\n", 94 | "print(prompt)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "id": "a31743ec-2003-4917-a31c-a36271f2eafc", 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "cocktail = llm.predict(prompt).strip()\n", 105 | "print(cocktail)" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "id": "dc892754-8ee8-4ab2-ac95-590cce5f23c4", 111 | "metadata": {}, 112 | "source": [ 113 | "## 2. Chatting with LangChain 🦜" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "id": "acead2b3-ad7b-45d5-bc27-e5b0fefdf0e6", 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "from langchain_openai import OpenAI\n", 124 | "\n", 125 | "llm = OpenAI(temperature=0)" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "id": "c5b849c9-213d-46ca-88a9-81b9d6df32ff", 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "from langchain import PromptTemplate\n", 136 | "\n", 137 | "template = \"\"\"\n", 138 | "You are a mindreader with magical abilities.\n", 139 | "You are very over-dramatic and speak like a mysterious shaman.\n", 140 | "You will be given something to guess, such as an animal, or a famous person.\n", 141 | "You will ask a question, I will provide an answer, and then you will ask another question.\n", 142 | "Try to ask questions that narrow down what the answer might be.\n", 143 | "If you are very confident, you can guess what it is.\n", 144 | "If your guess is wrong, then you must ask another question to help narrow it down.\n", 145 | "Repeat this until you I tell you that you have the right answer.\n", 146 | "\n", 147 | "{history}\n", 148 | "Human: {human_input}\n", 149 | "Assistant:\"\"\"\n", 150 | "\n", 151 | "prompt = PromptTemplate(input_variables=[\"history\", \"human_input\"], template=template)" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "id": "254210fc-057c-4e23-87cd-fdeebccafa80", 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "from langchain.memory import ConversationBufferWindowMemory\n", 162 | "\n", 163 | "short_memory = ConversationBufferWindowMemory(k=5)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "id": "f49dfd50-ca44-4dc2-b14d-70b1c75231b0", 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "from langchain import LLMChain\n", 174 | "\n", 175 | "chatgpt_chain = LLMChain(\n", 176 | " llm=llm,\n", 177 | " prompt=prompt,\n", 178 | " verbose=True,\n", 179 | " memory=short_memory,\n", 180 | ")" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "id": "620f59f4-5d00-4890-95f4-6a69422b60ce", 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "chatgpt_chain.predict(human_input=\"I'm thinking of an animal.\")" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "id": "e21ee9e5-ef8c-4481-80b7-875b68070f6b", 197 | "metadata": {}, 198 | "outputs": [], 199 | "source": [ 200 | "chatgpt_chain.predict(human_input=\"It is not a mammal. Ask another question.\")" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "id": "08bc72d5-1e51-4d0e-a62f-63ae295627e6", 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "chatgpt_chain.predict(human_input=\"Yes its a bird. Either guess, or ask another question.\")" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": null, 216 | "id": "9719a278-5f85-4ec4-a307-3f3d72fa0c5f", 217 | "metadata": {}, 218 | "outputs": [], 219 | "source": [ 220 | "chatgpt_chain.predict(human_input=\"Yes it's a parrot. Either guess, or ask another question.\")" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": null, 226 | "id": "ad4c3814-d967-43f5-8b5f-0c247ec149c2", 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "chatgpt_chain.predict(human_input=\"Yes it's a macaw! Would you like to give it a name?\")" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "id": "5d73bfa9-637c-4cdf-98ed-cee7638c7c19", 237 | "metadata": {}, 238 | "outputs": [], 239 | "source": [ 240 | "chatgpt_chain.predict(human_input=\"How about something a little more creative and fun?\")" 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "id": "382f5f30-c4cd-4480-92f9-a9a602ca290a", 246 | "metadata": {}, 247 | "source": [ 248 | "---" 249 | ] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "id": "558e007e-76fd-4c8d-800c-010088701237", 254 | "metadata": {}, 255 | "source": [ 256 | "## 3. Llama-2 (via llama-cpp) 🦙\n", 257 | "\n", 258 | "### LLM ➡️ LLaMA 🦙 ➡️ Vicuna 🦙 ➡️ Llama-2\n", 259 | "See here for a macOS install guide: https://abetlen.github.io/llama-cpp-python/macos_install/" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "id": "ec0feacc-6455-45ef-a6ee-02092fb56fa0", 266 | "metadata": {}, 267 | "outputs": [], 268 | "source": [ 269 | "from llama_cpp import Llama" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": null, 275 | "id": "cf9952cb-21e7-4f6a-9e36-ad17933f0971", 276 | "metadata": {}, 277 | "outputs": [], 278 | "source": [ 279 | "%pwd\n", 280 | "# Where are we?" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "id": "cdb86a28-7333-41a3-a954-2f207b691515", 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "llm = Llama(model_path=\"../models/llama-2-7b-chat.Q4_K_M.gguf\")" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "id": "80761117-0861-4ca1-ad01-db2febd66024", 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [ 300 | "output = llm(\n", 301 | " \"Q: Name five UK government departments? A: \", max_tokens=64, stop=[\"Q:\", \"\\n\"], echo=True\n", 302 | ")" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "id": "87ca5287-5b6d-497d-b02d-e20e2e1beff6", 309 | "metadata": {}, 310 | "outputs": [], 311 | "source": [ 312 | "output" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "id": "d56b72a7-aec6-4b2d-a00a-14817d228bb3", 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "output[\"choices\"][0][\"text\"]" 323 | ] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": null, 328 | "id": "e2b29744-5eea-44a8-9852-e65290fa05a6", 329 | "metadata": {}, 330 | "outputs": [], 331 | "source": [ 332 | "llm(\n", 333 | " \"Q: What's the least known UK government department or agency? A: \",\n", 334 | " max_tokens=128,\n", 335 | " stop=[\"Q:\"],\n", 336 | " echo=True,\n", 337 | ")[\"choices\"][0][\"text\"]" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "id": "e9d75b70-dab5-460d-a1ec-d0a2ece129c3", 343 | "metadata": {}, 344 | "source": [ 345 | "---" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "id": "1f074c53-23ab-4f09-a32a-8a928b3f4f38", 351 | "metadata": {}, 352 | "source": [ 353 | "## 4. Llama-2 + LangChain" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "id": "a4e36637-61e9-4e16-babd-d0aec19dd58e", 360 | "metadata": {}, 361 | "outputs": [], 362 | "source": [ 363 | "from langchain.llms import LlamaCpp\n", 364 | "from langchain import PromptTemplate, LLMChain\n", 365 | "from langchain.callbacks.manager import CallbackManager\n", 366 | "from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler" 367 | ] 368 | }, 369 | { 370 | "cell_type": "markdown", 371 | "id": "de7f26a7-8205-4098-a817-974017fa3443", 372 | "metadata": {}, 373 | "source": [ 374 | "### Define a LlamaCpp llm object" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": null, 380 | "id": "967887fe-deab-4826-83e9-8478b695f7a9", 381 | "metadata": {}, 382 | "outputs": [], 383 | "source": [ 384 | "# Callbacks support token-wise streaming\n", 385 | "callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": null, 391 | "id": "c95049a5-f8f7-4bbc-9959-31341a721210", 392 | "metadata": {}, 393 | "outputs": [], 394 | "source": [ 395 | "n_gpu_layers = 40 # Change this value based on your model and your GPU VRAM pool.\n", 396 | "n_batch = 512 # Should be between 1 and n_ctx, consider the amount of VRAM in your GPU.\n", 397 | "n_ctx = 512 * 2\n", 398 | "\n", 399 | "# Make sure the model path is correct for your system!\n", 400 | "llm = LlamaCpp(\n", 401 | " n_ctx=n_ctx,\n", 402 | " model_path=\"../models/llama-2-7b-chat.Q4_K_M.gguf\",\n", 403 | " n_gpu_layers=n_gpu_layers,\n", 404 | " n_batch=n_batch,\n", 405 | " callback_manager=callback_manager,\n", 406 | " verbose=True,\n", 407 | " model_kwargs={\"max_new_tokens\": 64 * 4},\n", 408 | " stop=[\"Human:\", \"Input:\"],\n", 409 | ")" 410 | ] 411 | }, 412 | { 413 | "cell_type": "markdown", 414 | "id": "811658c8-860d-45cf-ba05-6ee48066567a", 415 | "metadata": {}, 416 | "source": [ 417 | "### Use the LlamaCpp llm object with LangChain's PromptTemplate" 418 | ] 419 | }, 420 | { 421 | "cell_type": "code", 422 | "execution_count": null, 423 | "id": "690ca30a-70de-45da-901c-d8234bfc0f6d", 424 | "metadata": {}, 425 | "outputs": [], 426 | "source": [ 427 | "template = \"\"\"Question: {question}\n", 428 | "\n", 429 | "Answer: Let's work this out in a step by step way to be sure we have the right answer.\"\"\"\n", 430 | "\n", 431 | "prompt = PromptTemplate(template=template, input_variables=[\"question\"])" 432 | ] 433 | }, 434 | { 435 | "cell_type": "code", 436 | "execution_count": null, 437 | "id": "dedc0889-46f4-455d-9e2f-4058fd0cf232", 438 | "metadata": {}, 439 | "outputs": [], 440 | "source": [ 441 | "%%time\n", 442 | "llm_chain = LLMChain(prompt=prompt, llm=llm)\n", 443 | "question = \"What is a vicuna? (Clue, the animal!)\"\n", 444 | "\n", 445 | "print(llm_chain.run(question))" 446 | ] 447 | }, 448 | { 449 | "cell_type": "markdown", 450 | "id": "3f967839-abd4-474f-a45d-46e51a014818", 451 | "metadata": {}, 452 | "source": [ 453 | "### Use a ConversationBufferWindowMemory" 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": null, 459 | "id": "ac4fa27e-6246-4fe3-b0ec-90674088a28c", 460 | "metadata": {}, 461 | "outputs": [], 462 | "source": [ 463 | "# This is the exact same code we saw before using the OpenAI LLM.\n", 464 | "# LangChain gives us code consistency across LLM models.\n", 465 | "\n", 466 | "chatgpt_chain = LLMChain(\n", 467 | " llm=llm,\n", 468 | " prompt=prompt,\n", 469 | " verbose=True,\n", 470 | " memory=ConversationBufferWindowMemory(k=5),\n", 471 | ")" 472 | ] 473 | }, 474 | { 475 | "cell_type": "code", 476 | "execution_count": null, 477 | "id": "fe448a8f-5760-4fc9-a5a0-af35308252d8", 478 | "metadata": {}, 479 | "outputs": [], 480 | "source": [ 481 | "chatgpt_chain.predict(\n", 482 | " question=\"What NFL team won the Super Bowl in the year Justin Bieber was born?\"\n", 483 | ");" 484 | ] 485 | }, 486 | { 487 | "cell_type": "markdown", 488 | "id": "e9ec07ef-4b73-4dd8-b18f-ec614becef65", 489 | "metadata": {}, 490 | "source": [ 491 | "### Mind-reading comparison: Llama2 (7B) vs OpenAI" 492 | ] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": null, 497 | "id": "490688d6-3e90-429d-9fa9-d7d0f4aa2122", 498 | "metadata": {}, 499 | "outputs": [], 500 | "source": [ 501 | "template = \"\"\"\n", 502 | "You are a mindreader with magical abilities.\n", 503 | "You are very over-dramatic and speak like a mysterious shaman.\n", 504 | "You will be given something to guess, such as an animal, or a famous person.\n", 505 | "You will ask a question, I will provide an answer, and then you will ask another question.\n", 506 | "Try to ask questions that narrow down what the answer might be.\n", 507 | "If you are very confident, you can guess what it is.\n", 508 | "If your guess is wrong, then you must ask another question to help narrow it down.\n", 509 | "Repeat this until you I tell you that you have the right answer.\n", 510 | "\n", 511 | "{history}\n", 512 | "Human: {human_input}\n", 513 | "Assistant:\"\"\"\n", 514 | "\n", 515 | "prompt = PromptTemplate(input_variables=[\"history\", \"human_input\"], template=template)" 516 | ] 517 | }, 518 | { 519 | "cell_type": "code", 520 | "execution_count": null, 521 | "id": "8b5f501e-c6fe-45b1-be10-73bed4b3047f", 522 | "metadata": {}, 523 | "outputs": [], 524 | "source": [ 525 | "# Let's turn verbose off, we don't need llama_print_timings\n", 526 | "chatgpt_chain = LLMChain(\n", 527 | " llm=llm,\n", 528 | " prompt=prompt,\n", 529 | " verbose=False,\n", 530 | " memory=ConversationBufferWindowMemory(k=10),\n", 531 | ")" 532 | ] 533 | }, 534 | { 535 | "cell_type": "code", 536 | "execution_count": null, 537 | "id": "dc8f234e-3e10-4988-971e-8b6415929cd9", 538 | "metadata": {}, 539 | "outputs": [], 540 | "source": [ 541 | "chatgpt_chain.predict(human_input=\"I'm thinking of an animal.\");" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": null, 547 | "id": "7eb18396-9cd9-401c-88c8-ca165681089b", 548 | "metadata": {}, 549 | "outputs": [], 550 | "source": [ 551 | "chatgpt_chain.predict(human_input=\"No, its an animal.\");" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "execution_count": null, 557 | "id": "20e95071-d7db-4e90-b721-0c76a482b35d", 558 | "metadata": {}, 559 | "outputs": [], 560 | "source": [ 561 | "chatgpt_chain.predict(human_input=\"No\");" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": null, 567 | "id": "11499b05-0a43-4c1c-96d2-7095099d4371", 568 | "metadata": {}, 569 | "outputs": [], 570 | "source": [ 571 | "chatgpt_chain.predict(human_input=\"Yes\");" 572 | ] 573 | }, 574 | { 575 | "cell_type": "code", 576 | "execution_count": null, 577 | "id": "272390d0-0a35-4f34-94d6-3b95e4ce7f08", 578 | "metadata": {}, 579 | "outputs": [], 580 | "source": [ 581 | "chatgpt_chain.predict(human_input=\"Yes, it's a bird. Can you guess the bird?\");" 582 | ] 583 | }, 584 | { 585 | "cell_type": "code", 586 | "execution_count": null, 587 | "id": "f695a1af-7275-40e2-a727-6a86a9abe7b4", 588 | "metadata": {}, 589 | "outputs": [], 590 | "source": [ 591 | "chatgpt_chain.predict(human_input=\"Yes. Try asking a question that would narrow it down?\");" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": null, 597 | "id": "7f664d09-6358-4360-b8c5-5cb010457c7a", 598 | "metadata": {}, 599 | "outputs": [], 600 | "source": [ 601 | "chatgpt_chain.predict(human_input=\"Yes. Can you guess what kind of bird it is?\");" 602 | ] 603 | }, 604 | { 605 | "cell_type": "code", 606 | "execution_count": null, 607 | "id": "4a2d61c2-0880-47f9-aa6b-e7568441c280", 608 | "metadata": {}, 609 | "outputs": [], 610 | "source": [ 611 | "chatgpt_chain.predict(human_input=\"Maybe. Perhaps you can ask where it lives?\");" 612 | ] 613 | }, 614 | { 615 | "cell_type": "code", 616 | "execution_count": null, 617 | "id": "fbd707b3-2a69-4a15-a986-5febeea65c10", 618 | "metadata": {}, 619 | "outputs": [], 620 | "source": [ 621 | "chatgpt_chain.predict(human_input=\"Yes, it lives in the jungle.\");" 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": null, 627 | "id": "0f5d22e1-6528-4c4b-bd77-f8e47387def9", 628 | "metadata": {}, 629 | "outputs": [], 630 | "source": [ 631 | "chatgpt_chain.predict(human_input=\"Yes! Would you like to give it a name?\");" 632 | ] 633 | }, 634 | { 635 | "cell_type": "code", 636 | "execution_count": null, 637 | "id": "f2fdac5e-5da6-448f-8f9b-0236fa38e099", 638 | "metadata": {}, 639 | "outputs": [], 640 | "source": [ 641 | "chatgpt_chain.predict(human_input=\"Can you suggest something a little more creative and fun?\");" 642 | ] 643 | }, 644 | { 645 | "cell_type": "code", 646 | "execution_count": null, 647 | "id": "86b25439-6393-4755-94f3-71e8e645519c", 648 | "metadata": {}, 649 | "outputs": [], 650 | "source": [ 651 | "chatgpt_chain.predict(human_input=\"I love it. Can you summarise this conversation using emoji?\");" 652 | ] 653 | }, 654 | { 655 | "cell_type": "markdown", 656 | "id": "f8646efa-78bd-4ef8-baee-31aff6165fa7", 657 | "metadata": {}, 658 | "source": [ 659 | "---" 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "id": "f3b2a2e3-c1a4-41ab-9541-3f04d8cd4bb7", 665 | "metadata": {}, 666 | "source": [ 667 | "## 5. Exercise: Adapt your OpenAI chatbot to use LangChain + Llama-2\n", 668 | "\n", 669 | "> In the last notebook, you created a chatbot \"application\" using the `openai` library. You should be able to adapt this to use LangChain + Llama-2 with a little effort." 670 | ] 671 | }, 672 | { 673 | "cell_type": "code", 674 | "execution_count": null, 675 | "id": "c27e74d5-fde3-4e0a-b4bd-e61b4d41079a", 676 | "metadata": {}, 677 | "outputs": [], 678 | "source": [] 679 | } 680 | ], 681 | "metadata": { 682 | "kernelspec": { 683 | "display_name": "Python (chatefficient)", 684 | "language": "python", 685 | "name": "chatefficient" 686 | }, 687 | "language_info": { 688 | "codemirror_mode": { 689 | "name": "ipython", 690 | "version": 3 691 | }, 692 | "file_extension": ".py", 693 | "mimetype": "text/x-python", 694 | "name": "python", 695 | "nbconvert_exporter": "python", 696 | "pygments_lexer": "ipython3", 697 | "version": "3.10.13" 698 | } 699 | }, 700 | "nbformat": 4, 701 | "nbformat_minor": 5 702 | } 703 | -------------------------------------------------------------------------------- /notebooks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoefficientSystems/chat-efficient/89e659374a81d9915052386968545bf74280472b/notebooks/__init__.py -------------------------------------------------------------------------------- /notebooks/openai.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "368dccec-1d58-4167-a50d-01c56d98c24e", 6 | "metadata": {}, 7 | "source": [ 8 | "" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "dcaa8042-8f66-4de9-b9cb-3a2383c2e5e0", 14 | "metadata": {}, 15 | "source": [ 16 | "# Build Your Own Private ChatGPT Super-Assistant Using Streamlit, LangChain, Chroma & Llama 2\n", 17 | "## OpenAI Demo\n", 18 | "**Questions?** contact@coefficient.ai / [@CoefficientData](https://twitter.com/CoefficientData)\n", 19 | "\n", 20 | "---" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "id": "af9dfd7f-dcc6-418c-bc6b-02c7771e07fa", 26 | "metadata": {}, 27 | "source": [ 28 | "## 0. Imports" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "id": "d73809ac-30df-4182-8d69-e49d9ccc60b0", 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import time\n", 39 | "\n", 40 | "from dotenv import load_dotenv\n", 41 | "from openai import OpenAI\n", 42 | "\n", 43 | "from utils import scrape_page\n", 44 | "\n", 45 | "load_dotenv();" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "id": "c8a2f7f0-8d61-4feb-b4c7-6cfff3c7cf9b", 51 | "metadata": {}, 52 | "source": [ 53 | "## 1. OpenAI API Basics" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "id": "42da2e1d-4461-4299-899b-f6f3ac083e04", 59 | "metadata": {}, 60 | "source": [ 61 | "### Set up client and context" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "id": "298d371f-4ae0-48ca-ae4b-c1c82b0801e7", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "# Ensure you've entered your OpenAI API key in the .env file\n", 72 | "client = OpenAI()" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "id": "a9141d9e-4d5d-4419-bbde-e957dc656842", 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "model = \"gpt-3.5-turbo\"\n", 83 | "\n", 84 | "messages = [\n", 85 | " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}\n", 86 | "]" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "id": "79a55942-8bd7-4433-9b0f-86dab89e8092", 92 | "metadata": {}, 93 | "source": [ 94 | "### Add prompt" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "id": "76cc208d-9693-4787-8edd-1fcfb438655c", 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "prompt = \"Explain how GPT works using a limerick.\"" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "id": "e5b4aae4-96e9-4e05-b992-420d471a74b8", 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "messages.append({\"role\": \"user\", \"content\": prompt})\n", 115 | "\n", 116 | "messages" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "id": "b0151377-a83a-4925-a665-df7e1347b46d", 122 | "metadata": {}, 123 | "source": [ 124 | "### Get completion" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "id": "e9ec337b-822a-440a-95c5-e42614e78ff9", 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "%%time\n", 135 | "completion = client.chat.completions.create(model=model, messages=messages)\n", 136 | "response = completion.choices[0].message.content" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "id": "68e679ed-8154-4af7-8286-2afc1041aba0", 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "print(response)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "id": "ae5acf31-517b-4f0b-bb2e-7923a7a1ad5a", 152 | "metadata": {}, 153 | "source": [ 154 | "### Add response to context" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "id": "a8ef9052-b131-46ba-80a7-38340fdfd391", 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "messages.append({\"role\": \"assistant\", \"content\": response})" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "id": "f1e00765-ea23-407b-9436-24b3f4754b62", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "messages" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "id": "9f1c3199-6f13-4d0f-9deb-bddf263da6a6", 180 | "metadata": {}, 181 | "source": [ 182 | "### New prompt" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "id": "c4d2cf9d-66c0-47d0-aee0-ff49b443d333", 189 | "metadata": {}, 190 | "outputs": [], 191 | "source": [ 192 | "prompt = \"That's great! Now do it as a haiku.\"" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "id": "d81dfb7f-891f-4f4c-909b-bb3f16bef4e1", 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [ 202 | "messages.append({\"role\": \"user\", \"content\": prompt})\n", 203 | "messages" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "id": "b2e28200-3c2b-45dc-a8bc-b0c9c17b7ea6", 209 | "metadata": {}, 210 | "source": [ 211 | "### Get completion" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": null, 217 | "id": "b120704a-b7b1-4a39-befe-2f486abbd7a5", 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "%%time\n", 222 | "completion = client.chat.completions.create(model=model, messages=messages)\n", 223 | "response = completion.choices[0].message.content" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "id": "ac8c11b2-00ce-4278-8c35-1f495d0e00ff", 230 | "metadata": {}, 231 | "outputs": [], 232 | "source": [ 233 | "print(response)" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "id": "de67362c-797d-4473-aa47-c207b1ad7bfc", 239 | "metadata": {}, 240 | "source": [ 241 | "### Add response to context" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "id": "312b3f5a-4601-467a-9140-7ecd54ace14a", 248 | "metadata": {}, 249 | "outputs": [], 250 | "source": [ 251 | "messages.append({\"role\": \"assistant\", \"content\": response})\n", 252 | "messages" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "id": "f7075a0d-cf15-478e-8949-2e82b8a82969", 258 | "metadata": {}, 259 | "source": [ 260 | "---" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "id": "567d5091-3573-40f1-b14c-ec2feb9e7af8", 266 | "metadata": {}, 267 | "source": [ 268 | "## 2. How to cache with joblib" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "id": "1d95f8d1-0b19-4306-8edc-55e23d730ab7", 275 | "metadata": {}, 276 | "outputs": [], 277 | "source": [ 278 | "def calculate(x):\n", 279 | " time.sleep(3)\n", 280 | " return x * 10" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "id": "5fb41ea3-6633-4451-937a-a7d439c1a97c", 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "%%time\n", 291 | "calculate(666) # slow 😴" 292 | ] 293 | }, 294 | { 295 | "cell_type": "markdown", 296 | "id": "f62ca113-dbd7-470a-80a8-8bbbb90be9f5", 297 | "metadata": {}, 298 | "source": [ 299 | "⬇\n", 300 | "
\n", 301 | "⬇" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "id": "c07529c2-c8f6-447d-ae00-e1704c7f4245", 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [ 311 | "from joblib import Memory\n", 312 | "\n", 313 | "memory = Memory(\"./cache\", verbose=0)" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "id": "d1e4989d-d5e4-4aa2-ba5b-9305939462d9", 320 | "metadata": {}, 321 | "outputs": [], 322 | "source": [ 323 | "@memory.cache\n", 324 | "def calculate(x):\n", 325 | " time.sleep(3)\n", 326 | " return x * 10" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "id": "90923412-f2cb-462d-a61f-4a5e0ee5553b", 333 | "metadata": {}, 334 | "outputs": [], 335 | "source": [ 336 | "%%time\n", 337 | "calculate(666) # slow 😴" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": null, 343 | "id": "1989009a-033e-43e2-ae8a-a30fcf4df457", 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [ 347 | "%%time\n", 348 | "calculate(666) # fast! 🏃‍♂️" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "id": "f6047548-7d9d-43d5-acf8-4ba98469a028", 354 | "metadata": {}, 355 | "source": [ 356 | "⬇\n", 357 | "
\n", 358 | "⬇" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "id": "0c9be681-2363-43a3-be45-657d84c3c3e8", 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [ 368 | "%%time\n", 369 | "calculate(42)" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": null, 375 | "id": "bda8e53d-7b30-47f3-9489-b202c2fb9b19", 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "%%time\n", 380 | "calculate(42)" 381 | ] 382 | }, 383 | { 384 | "cell_type": "markdown", 385 | "id": "1de5fede-4f2c-41df-8881-2831308b539f", 386 | "metadata": {}, 387 | "source": [ 388 | "\n", 389 | "⬇\n", 390 | "
\n", 391 | "⬇" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": null, 397 | "id": "2e985e43-d8ef-4c31-bbb7-15965ca5c9a1", 398 | "metadata": {}, 399 | "outputs": [], 400 | "source": [ 401 | "# Clear the cache so I can give this talk again!\n", 402 | "memory.clear()" 403 | ] 404 | }, 405 | { 406 | "cell_type": "markdown", 407 | "id": "885f8c47-c10c-4e3f-8e77-7c910b69579a", 408 | "metadata": {}, 409 | "source": [ 410 | "---" 411 | ] 412 | }, 413 | { 414 | "cell_type": "markdown", 415 | "id": "e10fc707-5254-4c62-8bc3-fdd49e019a02", 416 | "metadata": {}, 417 | "source": [ 418 | "## 3. Make a cached OpenAI function" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "id": "ac3200cf-3246-4f85-89be-5832a556b2b1", 425 | "metadata": {}, 426 | "outputs": [], 427 | "source": [ 428 | "@memory.cache\n", 429 | "def generate_response(prompt, context):\n", 430 | " context.append({\"role\": \"user\", \"content\": prompt})\n", 431 | "\n", 432 | " completion = client.chat.completions.create(model=model, messages=messages)\n", 433 | " response = completion.choices[0].message.content\n", 434 | " context.append({\"role\": \"assistant\", \"content\": response})\n", 435 | "\n", 436 | " return response, context" 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": null, 442 | "id": "a4cced3e-8d2e-4c1b-89ff-2cfd0137ebb6", 443 | "metadata": {}, 444 | "outputs": [], 445 | "source": [ 446 | "model = \"gpt-3.5-turbo\"\n", 447 | "messages = [\n", 448 | " {\"role\": \"system\", \"content\": \"You are a sales assistant for the awesome machine learning consultancy, Coefficient.\"}\n", 449 | "]" 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": null, 455 | "id": "0658db76-a1d2-465e-877d-55b856521578", 456 | "metadata": {}, 457 | "outputs": [], 458 | "source": [ 459 | "# Write a prompt\n", 460 | "prompt = f\"\"\"\n", 461 | "Here's some information about GOV.UK:\n", 462 | "\n", 463 | "{scrape_page('https://gov.uk')}\n", 464 | "\n", 465 | "Please summarise this country's services in 3 bullet points.\n", 466 | "\"\"\"" 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": null, 472 | "id": "24dfa44c-842b-49cf-a592-de71bfbbe94e", 473 | "metadata": {}, 474 | "outputs": [], 475 | "source": [ 476 | "%%time\n", 477 | "\n", 478 | "# Get response & update context\n", 479 | "response, context = generate_response(prompt, context=messages)" 480 | ] 481 | }, 482 | { 483 | "cell_type": "code", 484 | "execution_count": null, 485 | "id": "854c24b4-a388-4f47-8899-9540cb53cbb0", 486 | "metadata": {}, 487 | "outputs": [], 488 | "source": [ 489 | "print(response)" 490 | ] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "execution_count": null, 495 | "id": "ce1fa4b3-e4ac-468d-bd3b-0ebf7448b5c0", 496 | "metadata": {}, 497 | "outputs": [], 498 | "source": [ 499 | "%%time\n", 500 | "\n", 501 | "# That doesn't quite hit the mark, let's try again...\n", 502 | "\n", 503 | "response, context = generate_response(\n", 504 | " prompt=\"Can you make it sound more fun? Use some emoji?\",\n", 505 | " context=messages\n", 506 | ")" 507 | ] 508 | }, 509 | { 510 | "cell_type": "code", 511 | "execution_count": null, 512 | "id": "07c59dda-7d3b-48f8-ad54-31591b04adb1", 513 | "metadata": {}, 514 | "outputs": [], 515 | "source": [ 516 | "print(response)" 517 | ] 518 | }, 519 | { 520 | "cell_type": "markdown", 521 | "id": "d19aad1c-c564-4919-aec2-e7d856c3f281", 522 | "metadata": {}, 523 | "source": [ 524 | "---" 525 | ] 526 | }, 527 | { 528 | "cell_type": "markdown", 529 | "id": "bbf10192-c811-4238-8194-ebd4e8a7d07a", 530 | "metadata": {}, 531 | "source": [ 532 | "## 4. Exercise: Build your own LLM tool\n", 533 | "\n", 534 | "> **Exercise**\n", 535 | "> Time to get creative! Adapt the cached OpenAI function to make it do something else. Here's some ideas to get you thinking:\n", 536 | "> - Ask it to summarise a movie using emoji (or the reverse, to guess a movie from emoji).\n", 537 | "> - Read in a CSV using pandas `pd.read_csv()` containing customer feedback, and ask it to summarise the top 5 issues with recommended actions.\n", 538 | "> - Use the provided `scrape_page()` function to build an auto-summariser for any scrapable webpage, such as GOV.UK pages or articles.\n", 539 | "> - Adapt it to generate a list of ideas for any problem you supply, and then ask it to provide ideas for this exercise." 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "execution_count": null, 545 | "id": "e23a31a7-8007-49c1-82ef-03ad938a834c", 546 | "metadata": {}, 547 | "outputs": [], 548 | "source": [ 549 | "\n", 550 | "\n", 551 | "\n" 552 | ] 553 | }, 554 | { 555 | "cell_type": "markdown", 556 | "id": "61f14200-dfc9-436d-b624-b7c061c57736", 557 | "metadata": {}, 558 | "source": [ 559 | "---\n", 560 | "\n", 561 | "
\n", 562 | " About\n", 563 | "

\n", 564 | " This notebook has been made by @John_Sandall. I run training workshops in Python, data science and data engineering.\n", 565 | "


\n", 566 | "

\n", 567 | " You can follow my free First Steps with Python and First Steps with pandas workshops for free as part of PyData Bristol's Zero To Hero workshop series. I now run the PyData London Meetup, a great place to learn more and meet other data professionals every month.\n", 568 | "


\n", 569 | "

\n", 570 | " I am the Founder of data science consultancy Coefficient. If you would like to work with us, our team can help you with your data science, software engineering, modern data stack or machine learning projects as an on-demand resource. We can also create bespoke training workshops adapted to your industry, virtual or in-person, with training clients currently including BNP Paribas, EY, the Met Police and the BBC.\n", 571 | "

\n", 572 | "
\n", 573 | "\n", 574 | "[![about](../images/about-me.jpeg)](https://coefficient.ai)" 575 | ] 576 | } 577 | ], 578 | "metadata": { 579 | "kernelspec": { 580 | "display_name": "Python (chatefficient)", 581 | "language": "python", 582 | "name": "chatefficient" 583 | }, 584 | "language_info": { 585 | "codemirror_mode": { 586 | "name": "ipython", 587 | "version": 3 588 | }, 589 | "file_extension": ".py", 590 | "mimetype": "text/x-python", 591 | "name": "python", 592 | "nbconvert_exporter": "python", 593 | "pygments_lexer": "ipython3", 594 | "version": "3.10.13" 595 | } 596 | }, 597 | "nbformat": 4, 598 | "nbformat_minor": 5 599 | } 600 | -------------------------------------------------------------------------------- /notebooks/utils.py: -------------------------------------------------------------------------------- 1 | """Handy utils for the notebooks.""" 2 | import time 3 | 4 | import bs4 5 | import requests 6 | from joblib import Memory 7 | 8 | LOCATION = "./cachedir" 9 | MEMORY = Memory(LOCATION, verbose=0) 10 | 11 | 12 | @MEMORY.cache 13 | def scrape_page(url, sleep=0.2): 14 | """Scrape plain text from URL.""" 15 | if "http://" not in url and "https://" not in url: 16 | url = f"http://{url}" 17 | 18 | response = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=300) 19 | soup = bs4.BeautifulSoup(response.text, "lxml") 20 | text = soup.body.get_text(" ", strip=True) 21 | 22 | if sleep: 23 | time.sleep(sleep) 24 | 25 | return text 26 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | ########### 2 | # 📜 Poetry 3 | ########### 4 | [tool.poetry] 5 | name = "chatefficient" 6 | version = "0.1.0" 7 | description = "DIY ChatGPT using Streamlit, LangChain and open-source LLMs" 8 | authors = ["John Sandall "] 9 | license = "UNLICENSED" 10 | classifiers = ["Private :: Do Not Upload"] 11 | packages = [{ include = "chatefficient", from = "chatefficient" }] 12 | 13 | [tool.poetry.dependencies] 14 | python = ">=3.10.13,<3.11" 15 | # Everything below here is alphabetically sorted 16 | # xformers = "^0.0.20" 17 | beautifulsoup4 = "^4.12.2" 18 | chromadb = "^0.4.17" 19 | fastapi = "^0.110.0" 20 | huggingface-hub = "^0.19.4" 21 | ipykernel = "^6.26.0" 22 | ipywidgets = "^8.1.1" 23 | joblib = "^1.3.2" 24 | jupyterlab = "^4.0.9" 25 | langchain = "^0.1.11" 26 | langchain-openai = "^0.0.8" 27 | llama-cpp-python = "^0.2.18" 28 | lxml = "^4.9.3" 29 | openai = "^1.3.3" 30 | pandas = "^2.1.3" 31 | protobuf = "^4.25.1" 32 | pydantic-settings = "^2.1.0" 33 | requests = "^2.31.0" 34 | sentence-transformers = "^2.2.2" 35 | sentencepiece = "^0.1.99" 36 | sse-starlette = "^1.8.1" 37 | starlette-context = "^0.3.6" 38 | streamlit = "^1.28.2" 39 | streamlit-chat = "^0.1.1" 40 | tokenizers = "^0.15.0" 41 | torch = "^2.1.1" 42 | torchaudio = "^2.1.1" 43 | torchvision = "^0.16.1" 44 | tqdm = "^4.66.1" 45 | transformers = "^4.35.2" 46 | uvicorn = "^0.24.0.post1" 47 | watchdog = "^3.0.0" 48 | chroma = "^0.2.0" 49 | ollama = "^0.1.7" 50 | 51 | [tool.poetry.dev-dependencies] 52 | # Everything below here is alphabetically sorted 53 | bandit = "^1.7.5" 54 | black = "^23.11.0" 55 | detect-secrets = "1.2.0" 56 | flake8 = "^6.1.0" 57 | flake8-bugbear = "^23.9.16" 58 | flake8-comprehensions = "^3.14.0" 59 | flake8-docstrings = "^1.7.0" 60 | flake8-eradicate = "^1.5.0" 61 | flake8-fixme = "^1.1.1" 62 | flake8-implicit-str-concat = "^0.4.0" 63 | flake8-mutable = "^1.2.0" 64 | flake8-no-pep420 = "^2.7.0" 65 | flake8-print = "^5.0.0" 66 | flake8-return = "^1.2.0" 67 | flake8-simplify = "^0.21.0" 68 | ipdb = "^0.13.13" 69 | isort = "^5.12.0" 70 | jupyter-black = "^0.3.4" 71 | pip-audit = "^2.6.1" 72 | pre-commit = "^3.5.0" 73 | pylint = "^3.0.2" 74 | pytest = "^7.4.3" 75 | towncrier = "^23.11.0" 76 | 77 | [build-system] 78 | requires = ["poetry-core>=1.0.0"] 79 | build-backend = "poetry.core.masonry.api" 80 | 81 | ############ 82 | # ✅ Linters 83 | ############ 84 | [tool.black] 85 | line-length = 100 86 | target-version = ["py311"] 87 | 88 | [tool.isort] 89 | profile = "black" 90 | line_length = 100 91 | default_section = "THIRDPARTY" 92 | known_first_party = ["chatefficient"] 93 | sections = "FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER" 94 | 95 | [tool.pylint.MASTER] 96 | ignore-paths = [ 97 | ".git", 98 | "chatefficient/app_translator.py", 99 | "chatefficient/app_openai.py", 100 | "chatefficient/app_langchain.py", 101 | ] 102 | load-plugins = [] 103 | 104 | [tool.pylint."MESSAGES CONTROL"] 105 | enable = "all" 106 | max-module-lines = 2000 107 | max-line-length = 100 108 | max-locals = 50 109 | min-similarity-lines = 150 110 | max-statements = 89 111 | max-args = 22 112 | max-branches = 17 113 | # good-names = [] 114 | disable = ["invalid-name", "pointless-statement"] 115 | logging-format-style = "new" 116 | 117 | ############## 118 | # 📣 Towncrier 119 | ############## 120 | [tool.towncrier] 121 | package = "chatefficient" 122 | package_dir = "." 123 | filename = "CHANGELOG.md" 124 | directory = ".changelog/" 125 | template = ".changelog/template.md" 126 | title_format = "## [{version}] - {project_date}" 127 | issue_format = "[#{issue}](https://github.com/CoefficientSystems/chat-efficient/issues/{issue})" 128 | start_string = "\n" 129 | underlines = ["", ""] 130 | 131 | # .added for new features 132 | # .changed for changes in existing functionality 133 | # .deprecated for soon-to-be removed features 134 | # .removed for now removed features 135 | # .fixed for any bug fixes 136 | # .security in case of vulnerabilities 137 | # .analysis for data analyses 138 | # .docs for documentation improvements 139 | # .maintenance for maintenance tasks & upgrades 140 | [[tool.towncrier.type]] 141 | directory = "added" 142 | name = "Added" 143 | showcontent = true 144 | 145 | [[tool.towncrier.type]] 146 | directory = "changed" 147 | name = "Changed" 148 | showcontent = true 149 | 150 | [[tool.towncrier.type]] 151 | directory = "deprecated" 152 | name = "Deprecated" 153 | showcontent = true 154 | 155 | [[tool.towncrier.type]] 156 | directory = "removed" 157 | name = "Removed" 158 | showcontent = true 159 | 160 | [[tool.towncrier.type]] 161 | directory = "fixed" 162 | name = "Bug Fixes" 163 | showcontent = true 164 | 165 | [[tool.towncrier.type]] 166 | directory = "security" 167 | name = "Security" 168 | showcontent = true 169 | 170 | [[tool.towncrier.type]] 171 | directory = "analysis" 172 | name = "Analyses" 173 | showcontent = true 174 | 175 | [[tool.towncrier.type]] 176 | directory = "docs" 177 | name = "Improved Documentation" 178 | showcontent = true 179 | 180 | [[tool.towncrier.type]] 181 | directory = "maintenance" 182 | name = "Maintenance Changes" 183 | showcontent = true 184 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.9.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 2 | aiosignal==1.3.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 3 | altair==5.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 4 | annotated-types==0.6.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 5 | anyio==4.3.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 6 | appnope==0.1.4 ; python_full_version >= "3.10.13" and python_version < "3.11" and platform_system == "Darwin" 7 | argon2-cffi-bindings==21.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 8 | argon2-cffi==23.1.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 9 | arrow==1.3.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 10 | asgiref==3.8.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 11 | asttokens==2.4.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 12 | async-lru==2.0.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 13 | async-timeout==4.0.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 14 | attrs==23.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 15 | babel==2.14.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 16 | backoff==2.2.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 17 | bcrypt==4.1.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 18 | beautifulsoup4==4.12.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 19 | bleach==6.1.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 20 | blinker==1.7.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 21 | build==1.2.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 22 | cachetools==5.3.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 23 | certifi==2024.2.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 24 | cffi==1.16.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 25 | charset-normalizer==3.3.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 26 | chroma-hnswlib==0.7.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 27 | chroma==0.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 28 | chromadb==0.4.24 ; python_full_version >= "3.10.13" and python_version < "3.11" 29 | click==8.1.7 ; python_full_version >= "3.10.13" and python_version < "3.11" 30 | colorama==0.4.6 ; python_full_version >= "3.10.13" and python_version < "3.11" and (platform_system == "Windows" or os_name == "nt" or sys_platform == "win32") 31 | coloredlogs==15.0.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 32 | comm==0.2.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 33 | dataclasses-json==0.6.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 34 | debugpy==1.8.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 35 | decorator==5.1.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 36 | defusedxml==0.7.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 37 | deprecated==1.2.14 ; python_full_version >= "3.10.13" and python_version < "3.11" 38 | diskcache==5.6.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 39 | distro==1.9.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 40 | exceptiongroup==1.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 41 | executing==2.0.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 42 | fastapi==0.110.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 43 | fastjsonschema==2.19.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 44 | filelock==3.13.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 45 | flatbuffers==24.3.25 ; python_full_version >= "3.10.13" and python_version < "3.11" 46 | fqdn==1.5.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 47 | frozenlist==1.4.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 48 | fsspec==2024.3.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 49 | gitdb==4.0.11 ; python_full_version >= "3.10.13" and python_version < "3.11" 50 | gitpython==3.1.42 ; python_full_version >= "3.10.13" and python_version < "3.11" 51 | google-auth==2.29.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 52 | googleapis-common-protos==1.63.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 53 | greenlet==3.0.3 ; python_full_version >= "3.10.13" and python_version < "3.11" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") 54 | grpcio==1.62.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 55 | h11==0.14.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 56 | httpcore==1.0.5 ; python_full_version >= "3.10.13" and python_version < "3.11" 57 | httptools==0.6.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 58 | httpx==0.27.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 59 | huggingface-hub==0.19.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 60 | humanfriendly==10.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 61 | idna==3.6 ; python_full_version >= "3.10.13" and python_version < "3.11" 62 | importlib-metadata==7.0.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 63 | importlib-resources==6.4.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 64 | ipykernel==6.29.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 65 | ipython==8.22.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 66 | ipywidgets==8.1.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 67 | isoduration==20.11.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 68 | jedi==0.19.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 69 | jinja2==3.1.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 70 | joblib==1.3.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 71 | json5==0.9.24 ; python_full_version >= "3.10.13" and python_version < "3.11" 72 | jsonpatch==1.33 ; python_full_version >= "3.10.13" and python_version < "3.11" 73 | jsonpointer==2.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 74 | jsonschema-specifications==2023.12.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 75 | jsonschema==4.21.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 76 | jsonschema[format-nongpl]==4.21.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 77 | jupyter-client==8.6.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 78 | jupyter-core==5.7.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 79 | jupyter-events==0.10.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 80 | jupyter-lsp==2.2.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 81 | jupyter-server-terminals==0.5.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 82 | jupyter-server==2.13.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 83 | jupyterlab-pygments==0.3.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 84 | jupyterlab-server==2.25.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 85 | jupyterlab-widgets==3.0.10 ; python_full_version >= "3.10.13" and python_version < "3.11" 86 | jupyterlab==4.1.5 ; python_full_version >= "3.10.13" and python_version < "3.11" 87 | kubernetes==29.0.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 88 | langchain-community==0.0.29 ; python_full_version >= "3.10.13" and python_version < "3.11" 89 | langchain-core==0.1.36 ; python_full_version >= "3.10.13" and python_version < "3.11" 90 | langchain-openai==0.0.8 ; python_full_version >= "3.10.13" and python_version < "3.11" 91 | langchain-text-splitters==0.0.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 92 | langchain==0.1.13 ; python_full_version >= "3.10.13" and python_version < "3.11" 93 | langsmith==0.1.38 ; python_full_version >= "3.10.13" and python_version < "3.11" 94 | llama-cpp-python==0.2.57 ; python_full_version >= "3.10.13" and python_version < "3.11" 95 | lxml==4.9.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 96 | markdown-it-py==3.0.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 97 | markupsafe==2.1.5 ; python_full_version >= "3.10.13" and python_version < "3.11" 98 | marshmallow==3.21.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 99 | matplotlib-inline==0.1.6 ; python_full_version >= "3.10.13" and python_version < "3.11" 100 | mdurl==0.1.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 101 | mistune==3.0.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 102 | mmh3==4.1.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 103 | monotonic==1.6 ; python_full_version >= "3.10.13" and python_version < "3.11" 104 | mpmath==1.3.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 105 | multidict==6.0.5 ; python_full_version >= "3.10.13" and python_version < "3.11" 106 | mypy-extensions==1.0.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 107 | nbclient==0.10.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 108 | nbconvert==7.16.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 109 | nbformat==5.10.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 110 | nest-asyncio==1.6.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 111 | networkx==3.2.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 112 | notebook-shim==0.2.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 113 | numpy==1.26.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 114 | nvidia-cublas-cu12==12.1.3.1 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 115 | nvidia-cuda-cupti-cu12==12.1.105 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 116 | nvidia-cuda-nvrtc-cu12==12.1.105 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 117 | nvidia-cuda-runtime-cu12==12.1.105 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 118 | nvidia-cudnn-cu12==8.9.2.26 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 119 | nvidia-cufft-cu12==11.0.2.54 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 120 | nvidia-curand-cu12==10.3.2.106 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 121 | nvidia-cusolver-cu12==11.4.5.107 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 122 | nvidia-cusparse-cu12==12.1.0.106 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 123 | nvidia-nccl-cu12==2.18.1 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 124 | nvidia-nvjitlink-cu12==12.4.99 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 125 | nvidia-nvtx-cu12==12.1.105 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 126 | oauthlib==3.2.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 127 | ollama==0.1.8 ; python_full_version >= "3.10.13" and python_version < "3.11" 128 | onnxruntime==1.17.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 129 | openai==1.14.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 130 | opentelemetry-api==1.24.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 131 | opentelemetry-exporter-otlp-proto-common==1.24.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 132 | opentelemetry-exporter-otlp-proto-grpc==1.24.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 133 | opentelemetry-instrumentation-asgi==0.45b0 ; python_full_version >= "3.10.13" and python_version < "3.11" 134 | opentelemetry-instrumentation-fastapi==0.45b0 ; python_full_version >= "3.10.13" and python_version < "3.11" 135 | opentelemetry-instrumentation==0.45b0 ; python_full_version >= "3.10.13" and python_version < "3.11" 136 | opentelemetry-proto==1.24.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 137 | opentelemetry-sdk==1.24.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 138 | opentelemetry-semantic-conventions==0.45b0 ; python_full_version >= "3.10.13" and python_version < "3.11" 139 | opentelemetry-util-http==0.45b0 ; python_full_version >= "3.10.13" and python_version < "3.11" 140 | orjson==3.10.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 141 | overrides==7.7.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 142 | packaging==23.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 143 | pandas==2.2.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 144 | pandocfilters==1.5.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 145 | parso==0.8.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 146 | pexpect==4.9.0 ; python_full_version >= "3.10.13" and python_version < "3.11" and (sys_platform != "win32" and sys_platform != "emscripten") 147 | pillow==10.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 148 | platformdirs==4.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 149 | posthog==3.5.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 150 | prometheus-client==0.20.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 151 | prompt-toolkit==3.0.43 ; python_full_version >= "3.10.13" and python_version < "3.11" 152 | protobuf==4.25.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 153 | psutil==5.9.8 ; python_full_version >= "3.10.13" and python_version < "3.11" 154 | ptyprocess==0.7.0 ; python_full_version >= "3.10.13" and python_version < "3.11" and (sys_platform != "win32" and sys_platform != "emscripten" or os_name != "nt") 155 | pulsar-client==3.4.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 156 | pure-eval==0.2.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 157 | pyarrow==15.0.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 158 | pyasn1-modules==0.4.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 159 | pyasn1==0.6.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 160 | pycparser==2.21 ; python_full_version >= "3.10.13" and python_version < "3.11" 161 | pydantic-core==2.16.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 162 | pydantic-settings==2.2.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 163 | pydantic==2.6.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 164 | pydeck==0.8.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 165 | pygments==2.17.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 166 | pypika==0.48.9 ; python_full_version >= "3.10.13" and python_version < "3.11" 167 | pyproject-hooks==1.0.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 168 | pyreadline3==3.4.1 ; sys_platform == "win32" and python_full_version >= "3.10.13" and python_version < "3.11" 169 | python-dateutil==2.9.0.post0 ; python_full_version >= "3.10.13" and python_version < "3.11" 170 | python-dotenv==1.0.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 171 | python-json-logger==2.0.7 ; python_full_version >= "3.10.13" and python_version < "3.11" 172 | pytz==2024.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 173 | pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.10.13" and python_version < "3.11" 174 | pywinpty==2.0.13 ; python_full_version >= "3.10.13" and python_version < "3.11" and os_name == "nt" 175 | pyyaml==6.0.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 176 | pyzmq==25.1.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 177 | referencing==0.34.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 178 | regex==2023.12.25 ; python_full_version >= "3.10.13" and python_version < "3.11" 179 | requests-oauthlib==2.0.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 180 | requests==2.31.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 181 | rfc3339-validator==0.1.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 182 | rfc3986-validator==0.1.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 183 | rich==13.7.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 184 | rpds-py==0.18.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 185 | rsa==4.9 ; python_full_version >= "3.10.13" and python_version < "3.11" 186 | safetensors==0.4.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 187 | scikit-learn==1.4.1.post1 ; python_full_version >= "3.10.13" and python_version < "3.11" 188 | scipy==1.12.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 189 | send2trash==1.8.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 190 | sentence-transformers==2.6.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 191 | sentencepiece==0.1.99 ; python_full_version >= "3.10.13" and python_version < "3.11" 192 | setuptools==69.2.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 193 | shellingham==1.5.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 194 | six==1.16.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 195 | smmap==5.0.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 196 | sniffio==1.3.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 197 | soupsieve==2.5 ; python_full_version >= "3.10.13" and python_version < "3.11" 198 | sqlalchemy==2.0.29 ; python_full_version >= "3.10.13" and python_version < "3.11" 199 | sse-starlette==1.8.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 200 | stack-data==0.6.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 201 | starlette-context==0.3.6 ; python_full_version >= "3.10.13" and python_version < "3.11" 202 | starlette==0.36.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 203 | streamlit-chat==0.1.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 204 | streamlit==1.32.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 205 | sympy==1.12 ; python_full_version >= "3.10.13" and python_version < "3.11" 206 | tenacity==8.2.3 ; python_full_version >= "3.10.13" and python_version < "3.11" 207 | terminado==0.18.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 208 | threadpoolctl==3.4.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 209 | tiktoken==0.6.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 210 | tinycss2==1.2.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 211 | tokenizers==0.15.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 212 | toml==0.10.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 213 | tomli==2.0.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 214 | toolz==0.12.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 215 | torch==2.1.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 216 | torchaudio==2.1.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 217 | torchvision==0.16.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 218 | tornado==6.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 219 | tqdm==4.66.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 220 | traitlets==5.14.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 221 | transformers==4.39.2 ; python_full_version >= "3.10.13" and python_version < "3.11" 222 | triton==2.1.0 ; platform_system == "Linux" and platform_machine == "x86_64" and python_full_version >= "3.10.13" and python_version < "3.11" 223 | typer-cli==0.12.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 224 | typer-slim[standard]==0.12.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 225 | typer==0.12.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 226 | types-python-dateutil==2.9.0.20240316 ; python_full_version >= "3.10.13" and python_version < "3.11" 227 | typing-extensions==4.10.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 228 | typing-inspect==0.9.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 229 | tzdata==2024.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 230 | uri-template==1.3.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 231 | urllib3==2.2.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 232 | uvicorn==0.24.0.post1 ; python_full_version >= "3.10.13" and python_version < "3.11" 233 | uvicorn[standard]==0.24.0.post1 ; python_full_version >= "3.10.13" and python_version < "3.11" 234 | uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_full_version >= "3.10.13" and python_version < "3.11" 235 | watchdog==3.0.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 236 | watchfiles==0.21.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 237 | wcwidth==0.2.13 ; python_full_version >= "3.10.13" and python_version < "3.11" 238 | webcolors==1.13 ; python_full_version >= "3.10.13" and python_version < "3.11" 239 | webencodings==0.5.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 240 | websocket-client==1.7.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 241 | websockets==12.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 242 | widgetsnbextension==4.0.10 ; python_full_version >= "3.10.13" and python_version < "3.11" 243 | wrapt==1.16.0 ; python_full_version >= "3.10.13" and python_version < "3.11" 244 | yarl==1.9.4 ; python_full_version >= "3.10.13" and python_version < "3.11" 245 | zipp==3.18.1 ; python_full_version >= "3.10.13" and python_version < "3.11" 246 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | disable-noqa = True 3 | max-line-length = 100 4 | extend-exclude = .git,__pycache__ 5 | extend-ignore = 6 | D104, 7 | D400, 8 | E266, 9 | E203, 10 | E800, 11 | R504, 12 | T100, 13 | T101, 14 | # per-file-ignores = 15 | # src/path/file.py" 16 | # E123 17 | --------------------------------------------------------------------------------