diff --git a/codellama/.circleci/config.yaml b/codellama/.circleci/config.yaml new file mode 100644 index 0000000..8e36760 --- /dev/null +++ b/codellama/.circleci/config.yaml @@ -0,0 +1,45 @@ +version: 2.1 + +jobs: + check_python_format: + docker: + - image: circleci/python:3.9 + steps: + - checkout + - run: + name: "Check format of .py with ufmt" + command: | + pip install black==22.12.0 + pip install usort==1.0.5 + pip install ufmt==2.0.1 + ufmt check . + check_type_annotation: + docker: + - image: circleci/python:3.9 + steps: + - checkout + - restore_cache: + keys: + - v1-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "dev-requirements.txt" }} + - run: + name: install dependencies + command: | + python3 -m venv venv + . venv/bin/activate + pip install -r requirements.txt + pip install -r dev-requirements.txt + - run: + name: "mypy" + command: | + . venv/bin/activate + mkdir .mypy_cache + mypy --install-types --non-interactive ./ --cache-dir=.mypy_cache/ + - save_cache: + paths: + - ./venv + key: v1-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "dev-requirements.txt" }} + +workflows: + frontend: + jobs: + - check_python_format \ No newline at end of file diff --git a/codellama/.gitignore b/codellama/.gitignore new file mode 100644 index 0000000..4a82d3d --- /dev/null +++ b/codellama/.gitignore @@ -0,0 +1,164 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Models + +models \ No newline at end of file diff --git a/codellama/CODE_OF_CONDUCT.md b/codellama/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..cf9dc24 --- /dev/null +++ b/codellama/CODE_OF_CONDUCT.md @@ -0,0 +1,80 @@ +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic +address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a +professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when there is a +reasonable belief that an individual's behavior may have a negative impact on +the project or its community. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq \ No newline at end of file diff --git a/codellama/CONTRIBUTING.md b/codellama/CONTRIBUTING.md new file mode 100644 index 0000000..b8c9e7d --- /dev/null +++ b/codellama/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing to Code Llama +We want to make contributing to this project as easy and transparent as +possible. + +## Pull Requests +We actively welcome your pull requests to address bugs or inconsistencies but +are not planning to add new features to the existing implementation. + +1. Fork the repo and create your branch from `main`. +2. If you've added code that should be tested, add tests. +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. +6. If you haven't already, complete the Contributor License Agreement ("CLA"). + +## Contributor License Agreement ("CLA") +In order to accept your pull request, we need you to submit a CLA. You only need +to do this once to work on any of Meta's open source projects. + +Complete your CLA here: + +## Issues +We use GitHub issues to track public bugs. Please ensure your description is +clear and has sufficient instructions to be able to reproduce the issue. + +Meta has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + +## License +By contributing to Code Llama, you agree that your contributions will be +licensed under the LICENSE file in the root directory of this source tree. diff --git a/codellama/LICENSE b/codellama/LICENSE new file mode 100644 index 0000000..2fa0dc8 --- /dev/null +++ b/codellama/LICENSE @@ -0,0 +1 @@ +Please refer to license: https://github.com/facebookresearch/llama/blob/main/LICENSE diff --git a/codellama/MODEL_CARD.md b/codellama/MODEL_CARD.md new file mode 100644 index 0000000..217ecd6 --- /dev/null +++ b/codellama/MODEL_CARD.md @@ -0,0 +1,52 @@ +# Code Llama + +## **Model Details** + +**Model Developers** Meta AI + +**Variations** Code Llama comes in four model sizes, and three variants: +1) Code Llama: our base models are designed for general code synthesis and understanding +2) Code Llama - Python: designed specifically for Python +3) Code Llama - Instruct: for instruction following and safer deployment + +All variants are available in sizes of 7B, 13B, 34B and 70B parameters. + +**Input** Models input text only. + +**Output** Models output text only. + +**Model Architecture** Code Llama and its variants are autoregressive language models using optimized transformer architectures. Code Llama 7B, 13B and 70B additionally support infilling text generation. All models but Code Llama - Python 70B and Code Llama - Instruct 70B were fine-tuned with up to 16K tokens, and support up to 100K tokens at inference time. + +**Model Dates** Code Llama and its variants have been trained between January 2023 and January 2024. + +**Status** This is a static model trained on an offline dataset. Future versions of Code Llama - Instruct will be released as we improve model safety with community feedback. + +**Licence** A custom commercial license is available at: [https://ai.meta.com/resources/models-and-libraries/llama-downloads/](https://ai.meta.com/resources/models-and-libraries/llama-downloads/). + +**Research Paper** More information can be found in the paper "[Code Llama: Open Foundation Models for Code](https://ai.meta.com/research/publications/code-llama-open-foundation-models-for-code/)". + +**Where to send comments** Instructions on how to provide feedback or comments on the model can be found in the model [README](README.md), or by opening an issue in the GitHub repository ([https://github.com/facebookresearch/codellama/](https://github.com/facebookresearch/codellama/)). + +## **Intended Use** +**Intended Use Cases** Code Llama and its variants are intended for commercial and research use in English and relevant programming languages. The base model Code Llama can be adapted for a variety of code synthesis and understanding tasks, Code Llama - Python is designed specifically to handle the Python programming language, and Code Llama - Instruct is intended to be safer to use for code assistance and generation applications. + +**Out-of-Scope Uses** Use in any manner that violates applicable laws or regulations (including trade compliance laws). Use in languages other than English. Use in any other way that is prohibited by the Acceptable Use Policy and Licensing Agreement for Code Llama and its variants. + +## **Hardware and Software** +**Training Factors** +We used custom training libraries. The training and fine-tuning of the released models have been performed by Meta’s Research Super Cluster. + +**Carbon Footprint** In aggregate, training all 12 Code Llama models required 1400K GPU hours of computation on hardware of type A100-80GB (TDP of 350-400W). Estimated total emissions were 228.55 tCO2eq, 100% of which were offset by Meta’s sustainability program. + +**Training data** +All experiments reported here and the released models have been trained and fine-tuned using the same data as Llama 2 with different weights (see Section 2 and Table 1 in the [research paper](https://ai.meta.com/research/publications/code-llama-open-foundation-models-for-code/) for details). +Code Llama - Instruct uses additional instruction fine-tuning data. + +**Evaluation Results** +See evaluations for the main models and detailed ablations in Section 3 and safety evaluations in Section 4 of the research paper. + +## **Ethical Considerations and Limitations** +Code Llama and its variants are a new technology that carries risks with use. Testing conducted to date has been in English, and has not covered, nor could it cover all scenarios. For these reasons, as with all LLMs, Code Llama’s potential outputs cannot be predicted in advance, and the model may in some instances produce inaccurate or objectionable responses to user prompts. Therefore, before deploying any applications of Code Llama, developers should perform safety testing and tuning tailored to their specific applications of the model. + +Please see the Responsible Use Guide available available at [https://ai.meta.com/llama/responsible-user-guide](https://ai.meta.com/llama/responsible-user-guide). + diff --git a/codellama/README.md b/codellama/README.md new file mode 100644 index 0000000..550f36d --- /dev/null +++ b/codellama/README.md @@ -0,0 +1,128 @@ +# Introducing Code Llama + +Code Llama is a family of large language models for code based on [Llama 2](https://github.com/facebookresearch/llama) providing state-of-the-art performance among open models, infilling capabilities, support for large input contexts, and zero-shot instruction following ability for programming tasks. We provide multiple flavors to cover a wide range of applications: foundation models (Code Llama), Python specializations (Code Llama - Python), and instruction-following models (Code Llama - Instruct) with 7B, 13B and 34B parameters each. All models are trained on sequences of 16k tokens and show improvements on inputs with up to 100k tokens. 7B and 13B Code Llama and Code Llama - Instruct variants support infilling based on surrounding content. Code Llama was developed by fine-tuning Llama 2 using a higher sampling of code. As with Llama 2, we applied considerable safety mitigations to the fine-tuned versions of the model. For detailed information on model training, architecture and parameters, evaluations, responsible AI and safety refer to our [research paper](https://ai.meta.com/research/publications/code-llama-open-foundation-models-for-code/). Output generated by code generation features of the Llama Materials, including Code Llama, may be subject to third party licenses, including, without limitation, open source licenses. + +We are unlocking the power of large language models and our latest version of Code Llama is now accessible to individuals, creators, researchers and businesses of all sizes so that they can experiment, innovate and scale their ideas responsibly. This release includes model weights and starting code for pretrained and fine-tuned Llama language models — ranging from 7B to 34B parameters. + +This repository is intended as a minimal example to load [Code Llama](https://ai.meta.com/research/publications/code-llama-open-foundation-models-for-code/) models and run inference. + + +[comment]: <> (Code Llama models are compatible with the scripts in llama-recipes) + + +## Download + +In order to download the model weights and tokenizers, please visit the [Meta website](https://ai.meta.com/resources/models-and-libraries/llama-downloads/) and accept our License. + +Once your request is approved, you will receive a signed URL over email. Then run the download.sh script, passing the URL provided when prompted to start the download. Make sure that you copy the URL text itself, **do not use the 'Copy link address' option** when you right click the URL. If the copied URL text starts with: https://download.llamameta.net, you copied it correctly. If the copied URL text starts with: https://l.facebook.com, you copied it the wrong way. + +Pre-requisites: make sure you have `wget` and `md5sum` installed. Then to run the script: `bash download.sh`. + +Keep in mind that the links expire after 24 hours and a certain amount of downloads. If you start seeing errors such as `403: Forbidden`, you can always re-request a link. + +### Model sizes + +| Model | Size | +|-------|----------| +| 7B | ~12.55GB | +| 13B | 24GB | +| 34B | 63GB | +| 70B | 131GB | + +[comment]: <> (Access on Hugging Face, We are also providing downloads on Hugging Face. You must first request a download from the Meta website using the same email address as your Hugging Face account. After doing so, you can request access to any of the models on Hugging Face and within 1-2 days your account will be granted access to all versions.) + +## Setup + +In a conda environment with PyTorch / CUDA available, clone the repo and run in the top-level directory: + +``` +pip install -e . +``` + +## Inference + +Different models require different model-parallel (MP) values: + +| Model | MP | +|-------|----| +| 7B | 1 | +| 13B | 2 | +| 34B | 4 | +| 70B | 8 | + +All models, except the 70B python and instruct versions, support sequence lengths up to 100,000 tokens, but we pre-allocate the cache according to `max_seq_len` and `max_batch_size` values. So set those according to your hardware and use-case. + +### Pretrained Code Models + +The Code Llama and Code Llama - Python models are not fine-tuned to follow instructions. They should be prompted so that the expected answer is the natural continuation of the prompt. + + +See `example_completion.py` for some examples. To illustrate, see command below to run it with the `CodeLlama-7b` model (`nproc_per_node` needs to be set to the `MP` value): + +``` +torchrun --nproc_per_node 1 example_completion.py \ + --ckpt_dir CodeLlama-7b/ \ + --tokenizer_path CodeLlama-7b/tokenizer.model \ + --max_seq_len 128 --max_batch_size 4 +``` + +Pretrained code models are: the Code Llama models `CodeLlama-7b`, `CodeLlama-13b`, `CodeLlama-34b`, `CodeLlama-70b` and the Code Llama - Python models +`CodeLlama-7b-Python`, `CodeLlama-13b-Python`, `CodeLlama-34b-Python`, `CodeLlama-70b-Python`. + +### Code Infilling + +Code Llama and Code Llama - Instruct 7B and 13B models are capable of filling in code given the surrounding context. + + +See `example_infilling.py` for some examples. The `CodeLlama-7b` model can be run for infilling with the command below (`nproc_per_node` needs to be set to the `MP` value): +``` +torchrun --nproc_per_node 1 example_infilling.py \ + --ckpt_dir CodeLlama-7b/ \ + --tokenizer_path CodeLlama-7b/tokenizer.model \ + --max_seq_len 192 --max_batch_size 4 +``` + +Pretrained infilling models are: the Code Llama models `CodeLlama-7b` and `CodeLlama-13b` and the Code Llama - Instruct models `CodeLlama-7b-Instruct`, `CodeLlama-13b-Instruct`. + +### Fine-tuned Instruction Models + +Code Llama - Instruct models are fine-tuned to follow instructions. To get the expected features and performance for the 7B, 13B and 34B variants, a specific formatting defined in [`chat_completion()`](https://github.com/facebookresearch/codellama/blob/main/llama/generation.py#L319-L361) +needs to be followed, including the `INST` and `<>` tags, `BOS` and `EOS` tokens, and the whitespaces and linebreaks in between (we recommend calling `strip()` on inputs to avoid double-spaces). +`CodeLlama-70b-Instruct` requires a separate turn-based prompt format defined in [`dialog_prompt_tokens()`](https://github.com/facebookresearch/codellama/blob/main/llama/generation.py#L506-L548). +You can use `chat_completion()` directly to generate answers with all instruct models; it will automatically perform the required formatting. + +You can also deploy additional classifiers for filtering out inputs and outputs that are deemed unsafe. See the llama-recipes repo for [an example](https://github.com/facebookresearch/llama-recipes/blob/main/src/llama_recipes/inference/safety_utils.py) of how to add a safety checker to the inputs and outputs of your inference code. + +Examples using `CodeLlama-7b-Instruct`: + +``` +torchrun --nproc_per_node 1 example_instructions.py \ + --ckpt_dir CodeLlama-7b-Instruct/ \ + --tokenizer_path CodeLlama-7b-Instruct/tokenizer.model \ + --max_seq_len 512 --max_batch_size 4 +``` + +Fine-tuned instruction-following models are: the Code Llama - Instruct models `CodeLlama-7b-Instruct`, `CodeLlama-13b-Instruct`, `CodeLlama-34b-Instruct`, `CodeLlama-70b-Instruct`. + +Code Llama is a new technology that carries potential risks with use. Testing conducted to date has not — and could not — cover all scenarios. +In order to help developers address these risks, we have created the [Responsible Use Guide](https://github.com/facebookresearch/llama/blob/main/Responsible-Use-Guide.pdf). More details can be found in our research papers as well. + +## Issues +Please report any software “bug”, or other problems with the models through one of the following means: +- Reporting issues with the model: [github.com/facebookresearch/codellama](http://github.com/facebookresearch/codellama) +- Reporting risky content generated by the model: [developers.facebook.com/llama_output_feedback](http://developers.facebook.com/llama_output_feedback) +- Reporting bugs and security concerns: [facebook.com/whitehat/info](http://facebook.com/whitehat/info) + +## Model Card +See [MODEL_CARD.md](MODEL_CARD.md) for the model card of Code Llama. + +## License + +Our model and weights are licensed for both researchers and commercial entities, upholding the principles of openness. Our mission is to empower individuals, and industry through this opportunity, while fostering an environment of discovery and ethical AI advancements. + +See the [LICENSE](https://github.com/facebookresearch/llama/blob/main/LICENSE) file, as well as our accompanying [Acceptable Use Policy](https://github.com/facebookresearch/llama/blob/main/USE_POLICY.md) + +## References + +1. [Code Llama Research Paper](https://ai.meta.com/research/publications/code-llama-open-foundation-models-for-code/) +2. [Code Llama Blog Post](https://ai.meta.com/blog/code-llama-large-language-model-coding/) diff --git a/codellama/USE_POLICY.md b/codellama/USE_POLICY.md new file mode 100644 index 0000000..efc3cf9 --- /dev/null +++ b/codellama/USE_POLICY.md @@ -0,0 +1 @@ +Please refer to acceptable use policy: https://github.com/facebookresearch/llama/blob/main/USE_POLICY.md diff --git a/codellama/dev-requirements.txt b/codellama/dev-requirements.txt new file mode 100644 index 0000000..cf7e40b --- /dev/null +++ b/codellama/dev-requirements.txt @@ -0,0 +1,4 @@ +black==22.12.0 +usort==1.0.5 +ufmt==2.0.1 +mypy==1.3.0 diff --git a/codellama/download.sh b/codellama/download.sh new file mode 100644 index 0000000..96b198b --- /dev/null +++ b/codellama/download.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Copyright (c) Meta Platforms, Inc. and affiliates. +# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. + +read -p "Enter the URL from email: " PRESIGNED_URL +echo "" +ALL_MODELS="7b,13b,34b,70b,7b-Python,13b-Python,34b-Python,70b-Python,7b-Instruct,13b-Instruct,34b-Instruct,70b-Instruct" +read -p "Enter the list of models to download without spaces ($ALL_MODELS), or press Enter for all: " MODEL_SIZE +TARGET_FOLDER="." # where all files should end up +mkdir -p ${TARGET_FOLDER} + +if [[ $MODEL_SIZE == "" ]]; then + MODEL_SIZE=$ALL_MODELS +fi + +echo "Downloading LICENSE and Acceptable Usage Policy" +wget --continue ${PRESIGNED_URL/'*'/"LICENSE"} -O ${TARGET_FOLDER}"/LICENSE" +wget --continue ${PRESIGNED_URL/'*'/"USE_POLICY.md"} -O ${TARGET_FOLDER}"/USE_POLICY.md" + +for m in ${MODEL_SIZE//,/ } +do + case $m in + 7b) + SHARD=0 ;; + 13b) + SHARD=1 ;; + 34b) + SHARD=3 ;; + 70b) + SHARD=7 ;; + 7b-Python) + SHARD=0 ;; + 13b-Python) + SHARD=1 ;; + 34b-Python) + SHARD=3 ;; + 70b-Python) + SHARD=7 ;; + 7b-Instruct) + SHARD=0 ;; + 13b-Instruct) + SHARD=1 ;; + 34b-Instruct) + SHARD=3 ;; + 70b-Instruct) + SHARD=7 ;; + *) + echo "Unknown model: $m" + exit 1 + esac + + MODEL_PATH="CodeLlama-$m" + echo "Downloading ${MODEL_PATH}" + mkdir -p ${TARGET_FOLDER}"/${MODEL_PATH}" + + for s in $(seq -f "0%g" 0 ${SHARD}) + do + wget --continue ${PRESIGNED_URL/'*'/"${MODEL_PATH}/consolidated.${s}.pth"} -O ${TARGET_FOLDER}"/${MODEL_PATH}/consolidated.${s}.pth" + done + + wget --continue ${PRESIGNED_URL/'*'/"${MODEL_PATH}/params.json"} -O ${TARGET_FOLDER}"/${MODEL_PATH}/params.json" + wget --continue ${PRESIGNED_URL/'*'/"${MODEL_PATH}/tokenizer.model"} -O ${TARGET_FOLDER}"/${MODEL_PATH}/tokenizer.model" + wget --continue ${PRESIGNED_URL/'*'/"${MODEL_PATH}/checklist.chk"} -O ${TARGET_FOLDER}"/${MODEL_PATH}/checklist.chk" + echo "Checking checksums" + (cd ${TARGET_FOLDER}"/${MODEL_PATH}" && md5sum -c checklist.chk) +done diff --git a/codellama/example_completion.py b/codellama/example_completion.py new file mode 100644 index 0000000..d8d7439 --- /dev/null +++ b/codellama/example_completion.py @@ -0,0 +1,54 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. + +from typing import Optional + +import fire + +from llama import Llama + + +def main( + ckpt_dir: str, + tokenizer_path: str, + temperature: float = 0.2, + top_p: float = 0.9, + max_seq_len: int = 256, + max_batch_size: int = 4, + max_gen_len: Optional[int] = None, +): + generator = Llama.build( + ckpt_dir=ckpt_dir, + tokenizer_path=tokenizer_path, + max_seq_len=max_seq_len, + max_batch_size=max_batch_size, + ) + + prompts = [ + # For these prompts, the expected answer is the natural continuation of the prompt + """\ +def fizzbuzz(n: int):""", + """\ +import argparse + +def main(string: str): + print(string) + print(string[::-1]) + +if __name__ == "__main__":""" + ] + results = generator.text_completion( + prompts, + max_gen_len=max_gen_len, + temperature=temperature, + top_p=top_p, + ) + for prompt, result in zip(prompts, results): + print(prompt) + print(f"> {result['generation']}") + print("\n==================================\n") + + +if __name__ == "__main__": + fire.Fire(main) + diff --git a/codellama/example_infilling.py b/codellama/example_infilling.py new file mode 100644 index 0000000..a11d80e --- /dev/null +++ b/codellama/example_infilling.py @@ -0,0 +1,79 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. + +import fire + +from llama import Llama + + +def main( + ckpt_dir: str, + tokenizer_path: str, + temperature: float = 0.0, + top_p: float = 0.9, + max_seq_len: int = 192, + max_gen_len: int = 128, + max_batch_size: int = 4, +): + generator = Llama.build( + ckpt_dir=ckpt_dir, + tokenizer_path=tokenizer_path, + max_seq_len=max_seq_len, + max_batch_size=max_batch_size, + ) + + prompts = [ + '''def remove_non_ascii(s: str) -> str: + """ + return result +''', + """# Installation instructions: + ```bash + + ``` +This downloads the LLaMA inference code and installs the repository as a local pip package. +""", + """class InterfaceManagerFactory(AbstractManagerFactory): + def __init__( +def main(): + factory = InterfaceManagerFactory(start=datetime.now()) + managers = [] + for i in range(10): + managers.append(factory.build(id=i)) +""", + """/-- A quasi-prefunctoid is 1-connected iff all its etalisations are 1-connected. -/ +theorem connected_iff_etalisation [C D : precategoroid] (P : quasi_prefunctoid C D) : + π₁ P = 0 ↔ = 0 := +begin + split, + { intros h f, + rw pi_1_etalisation at h, + simp [h], + refl + }, + { intro h, + have := @quasi_adjoint C D P, + simp [←pi_1_etalisation, this, h], + refl + } +end +""", + ] + prefixes = [p.split("")[0] for p in prompts] + suffixes = [p.split("")[1] for p in prompts] + results = generator.text_infilling( + prefixes=prefixes, + suffixes=suffixes, + max_gen_len=max_gen_len, + temperature=temperature, + top_p=top_p, + ) + for prompt, result in zip(prompts, results): + print("\n================= Prompt text =================\n") + print(prompt) + print("\n================= Filled text =================\n") + print(result["full_text"]) + + +if __name__ == "__main__": + fire.Fire(main) diff --git a/codellama/example_instructions.py b/codellama/example_instructions.py new file mode 100644 index 0000000..fa948f4 --- /dev/null +++ b/codellama/example_instructions.py @@ -0,0 +1,68 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. + +from typing import Optional + +import fire + +from llama import Llama + + +def main( + ckpt_dir: str, + tokenizer_path: str, + temperature: float = 0.2, + top_p: float = 0.95, + max_seq_len: int = 512, + max_batch_size: int = 8, + max_gen_len: Optional[int] = None, +): + generator = Llama.build( + ckpt_dir=ckpt_dir, + tokenizer_path=tokenizer_path, + max_seq_len=max_seq_len, + max_batch_size=max_batch_size, + ) + + instructions = [ + [ + { + "role": "user", + "content": "In Bash, how do I list all text files in the current directory (excluding subdirectories) that have been modified in the last month?", + } + ], + [ + { + "role": "user", + "content": "What is the difference between inorder and preorder traversal? Give an example in Python.", + } + ], + [ + { + "role": "system", + "content": "Provide answers in JavaScript", + }, + { + "role": "user", + "content": "Write a function that computes the set of sums of all contiguous sublists of a given list.", + } + ], + ] + results = generator.chat_completion( + instructions, # type: ignore + max_gen_len=max_gen_len, + temperature=temperature, + top_p=top_p, + ) + + for instruction, result in zip(instructions, results): + for msg in instruction: + print(f"{msg['role'].capitalize()}: {msg['content']}\n") + print( + f"> {result['generation']['role'].capitalize()}: {result['generation']['content']}" + ) + print("\n==================================\n") + + +if __name__ == "__main__": + fire.Fire(main) diff --git a/codellama/install.sh b/codellama/install.sh new file mode 100644 index 0000000..c1afd00 --- /dev/null +++ b/codellama/install.sh @@ -0,0 +1,7 @@ +conda create --name torchcuda python=3.9 pip + +conda activate torchcuda + +conda install pytorch pytorch-cuda=11.8 -c pytorch -c nvidia + +pip install -r requirements.txt \ No newline at end of file diff --git a/codellama/llama/__init__.py b/codellama/llama/__init__.py new file mode 100644 index 0000000..354342d --- /dev/null +++ b/codellama/llama/__init__.py @@ -0,0 +1,6 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. + +from .generation import Llama +from .model import ModelArgs, Transformer +from .tokenizer import Tokenizer diff --git a/codellama/llama/generation.py b/codellama/llama/generation.py new file mode 100644 index 0000000..ec77966 --- /dev/null +++ b/codellama/llama/generation.py @@ -0,0 +1,550 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. + +import json +import os +import sys +import time +from pathlib import Path +from typing import List, Literal, Optional, Tuple, TypedDict + +import torch +import torch.nn.functional as F +from fairscale.nn.model_parallel.initialize import ( + get_model_parallel_rank, + initialize_model_parallel, + model_parallel_is_initialized, +) + +from llama.model import ModelArgs, Transformer +from llama.tokenizer import Tokenizer + +if torch.cuda.is_available(): + device = "cuda" +elif torch.backends.mps.is_available(): + device = "mps" +else: + device = "cpu" + +print("Using device", device) + +Role = Literal["system", "user", "assistant"] + + +class Message(TypedDict): + role: Role + content: str + destination: str # required for model responses + + +class InfillingPrediction(TypedDict, total=False): + generation: str + full_text: str + tokens: List[str] # not required + logprobs: List[float] # not required + + +class CompletionPrediction(TypedDict, total=False): + generation: str + tokens: List[str] # not required + logprobs: List[float] # not required + + +class ChatPrediction(TypedDict, total=False): + generation: Message + tokens: List[str] # not required + logprobs: List[float] # not required + + +Dialog = List[Message] + +B_INST, E_INST = "[INST]", "[/INST]" +B_SYS, E_SYS = "<>\n", "\n<>\n\n" + +SPECIAL_TAGS = [B_INST, E_INST, "<>", "<>", ""] +UNSAFE_ERROR = "Error: special tags are not allowed as part of the prompt." + + +class Llama: + @staticmethod + def build( + ckpt_dir: str, + tokenizer_path: str, + max_seq_len: int, + max_batch_size: int, + model_parallel_size: Optional[int] = None, + ) -> "Llama": + if not torch.distributed.is_initialized(): + if device == "cuda": + torch.distributed.init_process_group("nccl") + else: + torch.distributed.init_process_group("gloo") + if not model_parallel_is_initialized(): + if model_parallel_size is None: + model_parallel_size = int(os.environ.get("WORLD_SIZE", 1)) + initialize_model_parallel(model_parallel_size) + + local_rank = int(os.environ.get("LOCAL_RANK", 0)) + if device == "cuda": + torch.cuda.set_device(local_rank) + + # seed must be the same in all processes + torch.manual_seed(1) + + if local_rank > 0: + sys.stdout = open(os.devnull, "w") + + start_time = time.time() + checkpoints = sorted(Path(ckpt_dir).glob("*.pth")) + assert len(checkpoints) > 0, f"no checkpoint files found in {ckpt_dir}" + assert model_parallel_size == len( + checkpoints + ), f"Loading a checkpoint for MP={len(checkpoints)} but world size is {model_parallel_size}" + ckpt_path = checkpoints[get_model_parallel_rank()] + checkpoint = torch.load(ckpt_path, map_location="cpu") + with open(Path(ckpt_dir) / "params.json", "r") as f: + params = json.loads(f.read()) + + model_args: ModelArgs = ModelArgs( + max_seq_len=max_seq_len, + max_batch_size=max_batch_size, + **params, + ) + tokenizer = Tokenizer(model_path=tokenizer_path) + model_args.vocab_size = tokenizer.n_words + # support for mac + if device == "cuda": + if torch.cuda.is_bf16_supported(): + torch.set_default_tensor_type(torch.cuda.BFloat16Tensor) + else: + torch.set_default_tensor_type(torch.cuda.HalfTensor) + else: + torch.set_default_tensor_type(torch.HalfTensor) + model = Transformer(model_args) + model.load_state_dict(checkpoint, strict=False) + model.to(device) + print(f"Loaded in {time.time() - start_time:.2f} seconds") + + return Llama(model, tokenizer) + + def __init__(self, model: Transformer, tokenizer: Tokenizer): + self.model = model + self.tokenizer = tokenizer + + @torch.inference_mode() + def generate( + self, + prompt_tokens: List[List[int]], + max_gen_len: int, + temperature: float = 0.6, + top_p: float = 0.9, + logprobs: bool = False, + echo: bool = False, + stop_token: Optional[int] = None, + ) -> Tuple[List[List[int]], Optional[List[List[float]]]]: + if stop_token is None: + stop_token = self.tokenizer.eos_id + params = self.model.params + bsz = len(prompt_tokens) + assert bsz <= params.max_batch_size, (bsz, params.max_batch_size) + + min_prompt_len = min(len(t) for t in prompt_tokens) + max_prompt_len = max(len(t) for t in prompt_tokens) + assert max_prompt_len <= params.max_seq_len + total_len = min(params.max_seq_len, max_gen_len + max_prompt_len) + + pad_id = self.tokenizer.pad_id + tokens = torch.full((bsz, total_len), pad_id, dtype=torch.long, device=device) + for k, t in enumerate(prompt_tokens): + tokens[k, : len(t)] = torch.tensor(t, dtype=torch.long, device=device) + if logprobs: + token_logprobs = torch.zeros_like(tokens, dtype=torch.float, device=device) + + prev_pos = 0 + stop_reached = torch.tensor([False] * bsz, device=device) + input_text_mask = tokens != pad_id + for cur_pos in range(min_prompt_len, total_len): + logits = self.model.forward(tokens[:, prev_pos:cur_pos], prev_pos) + if logprobs: + token_logprobs[:, prev_pos + 1 : cur_pos + 1] = -F.cross_entropy( + input=logits.transpose(1, 2), + target=tokens[:, prev_pos + 1 : cur_pos + 1], + reduction="none", + ignore_index=pad_id, + ) + if temperature > 0: + probs = torch.softmax(logits[:, -1] / temperature, dim=-1) + next_token = sample_top_p(probs, top_p) + else: + next_token = torch.argmax(logits[:, -1], dim=-1) + + next_token = next_token.reshape(-1) + # only replace token if prompt has already been generated + next_token = torch.where( + input_text_mask[:, cur_pos], tokens[:, cur_pos], next_token + ) + tokens[:, cur_pos] = next_token + stop_reached |= (~input_text_mask[:, cur_pos]) & (next_token == stop_token) + prev_pos = cur_pos + if all(stop_reached): + break + + if logprobs: + token_logprobs = token_logprobs.tolist() + out_tokens, out_logprobs = [], [] + for i, toks in enumerate(tokens.tolist()): + # cut to max gen len + start = 0 if echo else len(prompt_tokens[i]) + toks = toks[start : len(prompt_tokens[i]) + max_gen_len] + probs = None + if logprobs: + probs = token_logprobs[i][start : len(prompt_tokens[i]) + max_gen_len] + # cut to stop token if present + if stop_token in toks: + stop_idx = toks.index(stop_token) + toks = toks[:stop_idx] + probs = probs[:stop_idx] if logprobs else None + out_tokens.append(toks) + out_logprobs.append(probs) + return (out_tokens, out_logprobs if logprobs else None) + + def text_completion( + self, + prompts: List[str], + temperature: float = 0.6, + top_p: float = 0.9, + max_gen_len: Optional[int] = None, + logprobs: bool = False, + echo: bool = False, + ) -> List[CompletionPrediction]: + if max_gen_len is None: + max_gen_len = self.model.params.max_seq_len - 1 + prompt_tokens = [self.tokenizer.encode(x, bos=True, eos=False) for x in prompts] + generation_tokens, generation_logprobs = self.generate( + prompt_tokens=prompt_tokens, + max_gen_len=max_gen_len, + temperature=temperature, + top_p=top_p, + logprobs=logprobs, + echo=echo, + ) + if logprobs: + assert generation_logprobs is not None + return [ + { + "generation": self.tokenizer.decode(t), + "tokens": [self.tokenizer.token_piece(x) for x in t], + "logprobs": logprobs_i, + } + for t, logprobs_i in zip(generation_tokens, generation_logprobs) + ] + return [{"generation": self.tokenizer.decode(t)} for t in generation_tokens] + + def text_infilling( + self, + prefixes: List[str], + suffixes: List[str], + temperature: float = 0.6, + top_p: float = 0.9, + max_gen_len: Optional[int] = None, + logprobs: bool = False, + suffix_first: bool = False, + ) -> List[InfillingPrediction]: + assert self.tokenizer.eot_id is not None + if max_gen_len is None: + max_gen_len = self.model.params.max_seq_len - 1 + prompt_tokens = [ + infilling_prompt_tokens( + self.tokenizer, prefix, suffix, suffix_first=suffix_first + ) + for prefix, suffix in zip(prefixes, suffixes) + ] + generation_tokens, generation_logprobs = self.generate( + prompt_tokens=prompt_tokens, + max_gen_len=max_gen_len, + temperature=temperature, + top_p=top_p, + logprobs=logprobs, + echo=False, + stop_token=self.tokenizer.eot_id, + ) + + generations = [self.tokenizer.decode_infilling(t) for t in generation_tokens] + + if logprobs: + assert generation_logprobs is not None + return [ + { + "generation": generation, + "logprobs": logprobs_i, + "tokens": [self.tokenizer.token_piece(x) for x in t], + "full_text": prefix + generation + suffix, + } + for prefix, suffix, generation, t, logprobs_i in zip( + prefixes, + suffixes, + generations, + generation_tokens, + generation_logprobs, + ) + ] + else: + return [ + { + "generation": generation, + "full_text": prefix + generation + suffix, + } + for prefix, suffix, generation in zip(prefixes, suffixes, generations) + ] + + def chat_completion( + self, + dialogs: List[Dialog], + temperature: float = 0.6, + top_p: float = 0.9, + max_gen_len: Optional[int] = None, + logprobs: bool = False, + ) -> List[ChatPrediction]: + if self.tokenizer.step_id is not None: + return self._chat_completion_turns( + dialogs=dialogs, + temperature=temperature, + top_p=top_p, + max_gen_len=max_gen_len, + logprobs=logprobs, + ) + + if max_gen_len is None: + max_gen_len = self.model.params.max_seq_len - 1 + prompt_tokens = [] + unsafe_requests = [] + for dialog in dialogs: + unsafe_requests.append( + any([tag in msg["content"] for tag in SPECIAL_TAGS for msg in dialog]) + ) + if dialog[0]["role"] == "system": + dialog = [ # type: ignore + { + "role": dialog[1]["role"], + "content": B_SYS + + dialog[0]["content"] + + E_SYS + + dialog[1]["content"], + } + ] + dialog[2:] + assert all([msg["role"] == "user" for msg in dialog[::2]]) and all( + [msg["role"] == "assistant" for msg in dialog[1::2]] + ), ( + "model only supports 'system', 'user' and 'assistant' roles, " + "starting with 'system', then 'user' and alternating (u/a/u/a/u...)" + ) + dialog_tokens: List[int] = sum( + [ + self.tokenizer.encode( + f"{B_INST} {prompt['content'].strip()} {E_INST} {answer['content'].strip()} ", + bos=True, + eos=True, + ) + for prompt, answer in zip( + dialog[::2], + dialog[1::2], + ) + ], + [], + ) + assert ( + dialog[-1]["role"] == "user" + ), f"Last message must be from user, got {dialog[-1]['role']}" + dialog_tokens += self.tokenizer.encode( + f"{B_INST} {dialog[-1]['content'].strip()} {E_INST}", + bos=True, + eos=False, + ) + prompt_tokens.append(dialog_tokens) + + generation_tokens, generation_logprobs = self.generate( + prompt_tokens=prompt_tokens, + max_gen_len=max_gen_len, + temperature=temperature, + top_p=top_p, + logprobs=logprobs, + ) + if logprobs: + assert generation_logprobs is not None + return [ + { + "generation": { # type: ignore + "role": "assistant", + "content": self.tokenizer.decode(t) + if not unsafe + else UNSAFE_ERROR, + }, + "tokens": [self.tokenizer.token_piece(x) for x in t], + "logprobs": logprobs_i, + } + for t, logprobs_i, unsafe in zip( + generation_tokens, generation_logprobs, unsafe_requests + ) + ] + return [ + { + "generation": { # type: ignore + "role": "assistant", + "content": self.tokenizer.decode(t) if not unsafe else UNSAFE_ERROR, + } + } + for t, unsafe in zip(generation_tokens, unsafe_requests) + ] + + def _chat_completion_turns( + self, + dialogs: List[Dialog], + temperature: float = 0.6, + top_p: float = 0.9, + max_gen_len: Optional[int] = None, + logprobs: bool = False, + ) -> List[ChatPrediction]: + if self.tokenizer.step_id is None: + raise RuntimeError("Model not suitable for chat_completion_step()") + if max_gen_len is None: + max_gen_len = self.model.params.max_seq_len - 1 + + prompt_tokens = [] + unsafe_requests = [] + for dialog in dialogs: + unsafe_requests.append( + any([tag in msg["content"] for tag in SPECIAL_TAGS for msg in dialog]) + ) + + # Insert system message if not provided + if dialog[0]["role"] != "system": + dialog = [{"role": "system", "content": ""}] + dialog # type: ignore + + dialog_tokens = dialog_prompt_tokens(self.tokenizer, dialog) + prompt_tokens.append(dialog_tokens) + + generation_tokens, generation_logprobs = self.generate( + prompt_tokens=prompt_tokens, + max_gen_len=max_gen_len, + temperature=temperature, + top_p=top_p, + logprobs=logprobs, + stop_token=self.tokenizer.step_id, + ) + if logprobs: + assert generation_logprobs is not None + return [ + { + "generation": { + "role": "assistant", + "destination": "user", + "content": self.tokenizer.decode(t) + if not unsafe + else UNSAFE_ERROR, + }, + "tokens": [self.tokenizer.token_piece(x) for x in t], + "logprobs": logprobs_i, + } + for t, logprobs_i, unsafe in zip( + generation_tokens, generation_logprobs, unsafe_requests + ) + ] + return [ + { + "generation": { + "role": "assistant", + "destination": "user", + "content": self.tokenizer.decode(t) if not unsafe else UNSAFE_ERROR, + } + } + for t, unsafe in zip(generation_tokens, unsafe_requests) + ] + + + +def sample_top_p(probs, p): + probs_sort, probs_idx = torch.sort(probs, dim=-1, descending=True) + probs_sum = torch.cumsum(probs_sort, dim=-1) + mask = probs_sum - probs_sort > p + probs_sort[mask] = 0.0 + probs_sort.div_(probs_sort.sum(dim=-1, keepdim=True)) + next_token = torch.multinomial(probs_sort, num_samples=1) + next_token = torch.gather(probs_idx, -1, next_token) + return next_token + + +def infilling_prompt_tokens( + tokenizer: Tokenizer, + pre: str, + suf: str, + suffix_first: bool = False, +) -> List[int]: + """ + Format and encode an infilling problem. + If `suffix_first` is set, format in suffix-prefix-middle format. + """ + assert tokenizer.prefix_id is not None + assert tokenizer.middle_id is not None + assert tokenizer.suffix_id is not None + if suffix_first: + # format as "
 {suf}  {pre}"
+        return (
+            [tokenizer.bos_id, tokenizer.prefix_id, tokenizer.suffix_id]
+            + tokenizer.encode_infilling(suf)
+            + [tokenizer.middle_id]
+            + tokenizer.encode(pre, bos=False, eos=False)
+        )
+    else:
+        # format as "
 {pre} {suf} "
+        return (
+            [tokenizer.bos_id, tokenizer.prefix_id]
+            + tokenizer.encode(pre, bos=False, eos=False)
+            + [tokenizer.suffix_id]
+            + tokenizer.encode_infilling(suf)
+            + [tokenizer.middle_id]
+        )
+
+
+def dialog_prompt_tokens(tokenizer: Tokenizer, dialog: Dialog) -> List[int]:
+    """
+    Prompt formatting for multi-turn dialogs.
+    The dialog is expected to start with a system message and then alternate
+    between user and assistant messages.
+    """
+    assert tokenizer.step_id is not None
+    assert all([msg["role"] == "user" for msg in dialog[1::2]]) and all(
+        [msg["role"] == "assistant" for msg in dialog[2::2]]
+    ), (
+        "model only supports 'system', 'user' and 'assistant' roles, "
+        "starting with 'system', then 'user' and alternating (u/a/u/a/u...)"
+    )
+    assert (
+        dialog[-1]["role"] == "user"
+    ), f"Last message must be from user, got {dialog[-1]['role']}"
+
+    # Format context
+    dialog_tokens: List[int] = [tokenizer.bos_id]
+    headers: List[str] = []
+    for message in dialog:
+        headers.clear()
+        headers.append(f"Source: {message['role'].strip()}")
+        if message.get("destination") is not None:
+            headers.append(f"Destination: {message['destination'].strip()}")
+        header = " " + "\n".join(headers)
+        dialog_tokens += tokenizer.encode(header, bos=False, eos=False)
+
+        if message["content"]:
+            body = "\n\n " + message["content"].strip()
+            dialog_tokens += tokenizer.encode(body, bos=False, eos=False)
+
+        dialog_tokens += [tokenizer.step_id]
+
+    # Start of reply
+    headers.clear()
+    headers.append("Source: assistant")
+    headers.append("Destination: user")
+    header = " " + "\n".join(headers)
+    dialog_tokens += tokenizer.encode(header, bos=False, eos=False)
+    dialog_tokens += tokenizer.encode("\n\n ", bos=False, eos=False)
+
+    return dialog_tokens
diff --git a/codellama/llama/model.py b/codellama/llama/model.py
new file mode 100644
index 0000000..90b535b
--- /dev/null
+++ b/codellama/llama/model.py
@@ -0,0 +1,301 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement.
+
+import math
+from dataclasses import dataclass
+from typing import Any, Optional, Tuple
+
+import fairscale.nn.model_parallel.initialize as fs_init
+import torch
+import torch.nn.functional as F
+from fairscale.nn.model_parallel.layers import (
+    ColumnParallelLinear,
+    ParallelEmbedding,
+    RowParallelLinear,
+)
+from torch import nn
+
+if torch.cuda.is_available():
+    device = "cuda"
+elif torch.backends.mps.is_available():
+    device = "mps"
+else:
+    device = "cpu"
+
+@dataclass
+class ModelArgs:
+    dim: int = 4096
+    n_layers: int = 32
+    n_heads: int = 32
+    n_kv_heads: Optional[int] = None
+    vocab_size: int = -1  # defined later by tokenizer
+    multiple_of: int = 256  # make SwiGLU hidden layer size multiple of large power of 2
+    ffn_dim_multiplier: Optional[float] = None
+    norm_eps: float = 1e-5
+    rope_theta: float = 10000
+
+    max_batch_size: int = 32
+    max_seq_len: int = 2048
+
+
+class RMSNorm(torch.nn.Module):
+    def __init__(self, dim: int, eps: float = 1e-6):
+        super().__init__()
+        self.eps = eps
+        self.weight = nn.Parameter(torch.ones(dim))
+
+    def _norm(self, x):
+        return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
+
+    def forward(self, x):
+        output = self._norm(x.float()).type_as(x)
+        return output * self.weight
+
+
+def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0):
+    freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
+
+    t = torch.arange(end, device=freqs.device, dtype=torch.float32)  # type: ignore
+    freqs = torch.outer(t, freqs)  # type: ignore
+    freqs_cis = torch.polar(torch.ones_like(freqs), freqs)  # complex64
+    return freqs_cis
+
+
+def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor):
+    ndim = x.ndim
+    assert 0 <= 1 < ndim
+    assert freqs_cis.shape == (x.shape[1], x.shape[-1])
+    shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
+    return freqs_cis.view(*shape)
+
+
+def apply_rotary_emb(
+    xq: torch.Tensor,
+    xk: torch.Tensor,
+    freqs_cis: torch.Tensor,
+) -> Tuple[torch.Tensor, torch.Tensor]:
+    if not torch.cuda.is_available():
+        xq = xq.to('cpu')
+        xk = xk.to('cpu')
+    xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
+    xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
+    freqs_cis = reshape_for_broadcast(freqs_cis, xq_)
+    xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(3)
+    xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(3)
+    return xq_out.type_as(xq).to(device), xk_out.type_as(xk).to(device)
+
+
+def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor:
+    """torch.repeat_interleave(x, dim=2, repeats=n_rep)"""
+    bs, slen, n_kv_heads, head_dim = x.shape
+    if n_rep == 1:
+        return x
+    return (
+        x[:, :, :, None, :]
+        .expand(bs, slen, n_kv_heads, n_rep, head_dim)
+        .reshape(bs, slen, n_kv_heads * n_rep, head_dim)
+    )
+
+
+class Attention(nn.Module):
+    def __init__(self, args: ModelArgs):
+        super().__init__()
+        self.n_kv_heads = args.n_heads if args.n_kv_heads is None else args.n_kv_heads
+        model_parallel_size = fs_init.get_model_parallel_world_size()
+        self.n_local_heads = args.n_heads // model_parallel_size
+        self.n_local_kv_heads = self.n_kv_heads // model_parallel_size
+        self.n_rep = self.n_local_heads // self.n_local_kv_heads
+        self.head_dim = args.dim // args.n_heads
+
+        self.wq = ColumnParallelLinear(
+            args.dim,
+            args.n_heads * self.head_dim,
+            bias=False,
+            gather_output=False,
+            init_method=lambda x: x,
+        )
+        self.wk = ColumnParallelLinear(
+            args.dim,
+            self.n_kv_heads * self.head_dim,
+            bias=False,
+            gather_output=False,
+            init_method=lambda x: x,
+        )
+        self.wv = ColumnParallelLinear(
+            args.dim,
+            self.n_kv_heads * self.head_dim,
+            bias=False,
+            gather_output=False,
+            init_method=lambda x: x,
+        )
+        self.wo = RowParallelLinear(
+            args.n_heads * self.head_dim,
+            args.dim,
+            bias=False,
+            input_is_parallel=True,
+            init_method=lambda x: x,
+        )
+
+        self.cache_k = torch.zeros(
+            (
+                args.max_batch_size,
+                args.max_seq_len,
+                self.n_local_kv_heads,
+                self.head_dim,
+            )
+        ).to(device)
+        self.cache_v = torch.zeros(
+            (
+                args.max_batch_size,
+                args.max_seq_len,
+                self.n_local_kv_heads,
+                self.head_dim,
+            )
+        ).to(device)
+
+    def forward(
+        self,
+        x: torch.Tensor,
+        start_pos: int,
+        freqs_cis: torch.Tensor,
+        mask: Optional[torch.Tensor],
+    ):
+        bsz, seqlen, _ = x.shape
+        xq, xk, xv = self.wq(x), self.wk(x), self.wv(x)
+
+        xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)
+        xk = xk.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim)
+        xv = xv.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim)
+
+        xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis)
+
+        self.cache_k = self.cache_k.to(xq)
+        self.cache_v = self.cache_v.to(xq)
+
+        self.cache_k[:bsz, start_pos : start_pos + seqlen] = xk
+        self.cache_v[:bsz, start_pos : start_pos + seqlen] = xv
+
+        keys = self.cache_k[:bsz, : start_pos + seqlen]
+        values = self.cache_v[:bsz, : start_pos + seqlen]
+
+        # repeat k/v heads if n_kv_heads < n_heads
+        keys = repeat_kv(keys, self.n_rep)  # (bs, seqlen, n_local_heads, head_dim)
+        values = repeat_kv(values, self.n_rep)  # (bs, seqlen, n_local_heads, head_dim)
+
+        xq = xq.transpose(1, 2)  # (bs, n_local_heads, seqlen, head_dim)
+        keys = keys.transpose(1, 2)
+        values = values.transpose(1, 2)
+        scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)
+        if mask is not None:
+            scores = scores + mask  # (bs, n_local_heads, seqlen, cache_len + seqlen)
+        scores = F.softmax(scores.float(), dim=-1).type_as(xq)
+        output = torch.matmul(scores, values)  # (bs, n_local_heads, seqlen, head_dim)
+        output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)
+        return self.wo(output)
+
+
+class FeedForward(nn.Module):
+    def __init__(
+        self,
+        dim: int,
+        hidden_dim: int,
+        multiple_of: int,
+        ffn_dim_multiplier: Optional[float],
+    ):
+        super().__init__()
+        hidden_dim = int(2 * hidden_dim / 3)
+        # custom dim factor multiplier
+        if ffn_dim_multiplier is not None:
+            hidden_dim = int(ffn_dim_multiplier * hidden_dim)
+        hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of)
+
+        self.w1 = ColumnParallelLinear(
+            dim, hidden_dim, bias=False, gather_output=False, init_method=lambda x: x
+        )
+        self.w2 = RowParallelLinear(
+            hidden_dim, dim, bias=False, input_is_parallel=True, init_method=lambda x: x
+        )
+        self.w3 = ColumnParallelLinear(
+            dim, hidden_dim, bias=False, gather_output=False, init_method=lambda x: x
+        )
+
+    def forward(self, x):
+        return self.w2(F.silu(self.w1(x)) * self.w3(x))
+
+
+class TransformerBlock(nn.Module):
+    def __init__(self, layer_id: int, args: ModelArgs):
+        super().__init__()
+        self.n_heads = args.n_heads
+        self.dim = args.dim
+        self.head_dim = args.dim // args.n_heads
+        self.attention = Attention(args)
+        self.feed_forward = FeedForward(
+            dim=args.dim,
+            hidden_dim=4 * args.dim,
+            multiple_of=args.multiple_of,
+            ffn_dim_multiplier=args.ffn_dim_multiplier,
+        )
+        self.layer_id = layer_id
+        self.attention_norm = RMSNorm(args.dim, eps=args.norm_eps)
+        self.ffn_norm = RMSNorm(args.dim, eps=args.norm_eps)
+
+    def forward(
+        self,
+        x: torch.Tensor,
+        start_pos: int,
+        freqs_cis: torch.Tensor,
+        mask: Optional[torch.Tensor],
+    ):
+        h = x + self.attention.forward(
+            self.attention_norm(x), start_pos, freqs_cis, mask
+        )
+        out = h + self.feed_forward.forward(self.ffn_norm(h))
+        return out
+
+
+class Transformer(nn.Module):
+    def __init__(self, params: ModelArgs):
+        super().__init__()
+        self.params = params
+        self.vocab_size = params.vocab_size
+        self.n_layers = params.n_layers
+
+        self.tok_embeddings = ParallelEmbedding(
+            params.vocab_size, params.dim, init_method=lambda x: x,
+        )
+
+        self.layers = torch.nn.ModuleList()
+        for layer_id in range(params.n_layers):
+            self.layers.append(TransformerBlock(layer_id, params))
+
+        self.norm = RMSNorm(params.dim, eps=params.norm_eps)
+        self.output = ColumnParallelLinear(
+            params.dim, params.vocab_size, bias=False, init_method=lambda x: x
+        )
+
+        self.freqs_cis = precompute_freqs_cis(
+            self.params.dim // self.params.n_heads,
+            self.params.max_seq_len * 2,
+            params.rope_theta,
+        )
+
+    @torch.inference_mode()
+    def forward(self, tokens: torch.Tensor, start_pos: int):
+        _bsz, seqlen = tokens.shape
+        h = self.tok_embeddings(tokens)
+        self.freqs_cis = self.freqs_cis.to("cuda" if device == "cuda" else "cpu")
+        freqs_cis = self.freqs_cis[start_pos : start_pos + seqlen]
+
+        mask = None
+        if seqlen > 1:
+            mask = torch.full(
+                (1, 1, seqlen, seqlen), float("-inf"), device=torch.device('cpu')
+            )
+            mask = mask.to(torch.float32).triu(diagonal=start_pos+1).type_as(h)
+
+        for layer in self.layers:
+            h = layer(h, start_pos, freqs_cis, (mask.to(device) if mask is not None else mask))
+        h = self.norm(h)
+        output = self.output(h).float()
+        return output
diff --git a/codellama/llama/tokenizer.py b/codellama/llama/tokenizer.py
new file mode 100644
index 0000000..6716970
--- /dev/null
+++ b/codellama/llama/tokenizer.py
@@ -0,0 +1,63 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement.
+
+import os
+from logging import getLogger
+from typing import List, Optional
+
+from sentencepiece import SentencePieceProcessor
+
+
+logger = getLogger()
+
+
+class Tokenizer:
+    def __init__(self, model_path: str):
+        # reload tokenizer
+        assert os.path.isfile(model_path), model_path
+        self.sp_model = SentencePieceProcessor(model_file=model_path)
+        logger.info(f"Reloaded SentencePiece model from {model_path}")
+
+        # BOS / EOS token IDs
+        self.n_words: int = self.sp_model.vocab_size()
+        self.bos_id: int = self.sp_model.bos_id()
+        self.eos_id: int = self.sp_model.eos_id()
+        self.pad_id: int = self.sp_model.pad_id()
+
+        # token IDs for special infilling tokens
+        self.prefix_id: Optional[int] = self.sp_model.piece_to_id("▁
") or None
+        self.middle_id: Optional[int] = self.sp_model.piece_to_id("▁") or None
+        self.suffix_id: Optional[int] = self.sp_model.piece_to_id("▁") or None
+        self.eot_id: Optional[int] = self.sp_model.piece_to_id("▁") or None
+
+        # marker for turn-based step format
+        self.step_id: Optional[int] = self.sp_model.piece_to_id("") or None
+
+        logger.info(
+            f"#words: {self.n_words} - BOS ID: {self.bos_id} - EOS ID: {self.eos_id} "
+            f"- PRE ID: {self.prefix_id} - MID ID: {self.middle_id} - SUF ID: {self.suffix_id} - EOT ID: {self.eot_id} - STEP ID: {self.step_id}"
+        )
+        assert self.sp_model.vocab_size() == self.sp_model.get_piece_size()
+
+    def encode(self, s: str, bos: bool, eos: bool) -> List[int]:
+        assert type(s) is str
+        t = self.sp_model.encode(s)
+        if bos:
+            t = [self.bos_id] + t
+        if eos:
+            t = t + [self.eos_id]
+        return t
+
+    def decode(self, t: List[int]) -> str:
+        return self.sp_model.decode(list(filter(lambda tk: tk != -1, t)))
+
+    def token_piece(self, t: int) -> str:
+        return self.sp_model.id_to_piece(t)
+
+    def encode_infilling(self, s: str) -> List[int]:
+        """Encode a string without an implicit leading space."""
+        return self.sp_model.encode("☺" + s)[2:]
+
+    def decode_infilling(self, t: List[int]) -> str:
+        """Decode a string without an implicit leading space."""
+        return self.sp_model.decode([self.sp_model.piece_to_id("☺")] + t)[1:]
diff --git a/codellama/requirements.txt b/codellama/requirements.txt
new file mode 100644
index 0000000..f642d3e
--- /dev/null
+++ b/codellama/requirements.txt
@@ -0,0 +1,3 @@
+fairscale
+fire
+sentencepiece
diff --git a/codellama/setup.py b/codellama/setup.py
new file mode 100644
index 0000000..a67e374
--- /dev/null
+++ b/codellama/setup.py
@@ -0,0 +1,16 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement.
+
+from setuptools import find_packages, setup
+
+
+def get_requirements(path: str):
+    return [l.strip() for l in open(path)]
+
+
+setup(
+    name="codellama",
+    version="0.0.1",
+    packages=find_packages(),
+    install_requires=get_requirements("requirements.txt"),
+)