Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/scripts/check_doc_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""Fail when a Sphinx warning reports a broken internal documentation link."""

import argparse
from pathlib import Path


BLOCKING_WARNING_MARKERS = (
"[myst.xref_missing]",
"[myst.iref_missing]",
"[myst.xref_ambiguous]",
"[myst.iref_ambiguous]",
"[ref.",
"[toc.",
)

BLOCKING_WARNING_MESSAGES = (
"cross-reference target not found",
"reference target not found",
"undefined label:",
"failed to create a cross reference",
"toctree contains reference to",
"document isn't included in any toctree",
"duplicated entry found in toctree",
)


def find_blocking_warnings(warning_log: str) -> list[str]:
"""Return warnings related to internal references and navigation."""
blocking_warnings = []
for line in warning_log.splitlines():
normalized_line = line.lower()
if any(marker in normalized_line for marker in BLOCKING_WARNING_MARKERS) or any(
message in normalized_line for message in BLOCKING_WARNING_MESSAGES
):
blocking_warnings.append(line)
return blocking_warnings


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("warning_log", type=Path, help="Sphinx warning log generated with -w")
args = parser.parse_args()

blocking_warnings = find_blocking_warnings(args.warning_log.read_text(encoding="utf-8"))
if not blocking_warnings:
print("Documentation internal-link check passed.")
return 0

print("Documentation internal-link check failed:")
for warning in blocking_warnings:
print(warning)
return 1


if __name__ == "__main__":
raise SystemExit(main())
5 changes: 5 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ jobs:
cache-dependency-path: |
requirements.txt
setup.py
- name: Install CPU-only PyTorch
run: >-
python -m pip install
--index-url https://download.pytorch.org/whl/cpu
torch==2.8.0+cpu
- name: Install LMFlow and test dependencies
run: python -m pip install -e ".[develop]"
- name: Run offline CPU tests
Expand Down
48 changes: 32 additions & 16 deletions .github/workflows/documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ concurrency:
jobs:
build:
name: Build documentation
if: github.repository == 'OptimalScale/LMFlow'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
Expand All @@ -31,29 +32,44 @@ jobs:
- name: Install dependencies
run: python -m pip install -r docs/requirements.txt
- name: Build documentation
run: sphinx-build -b html docs/source _build/html
- name: Configure GitHub Pages
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
- name: Upload Pages artifact
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
run: sphinx-build -b html -w _build/sphinx-warnings.log docs/source _build/html
- name: Check documentation links
run: python .github/scripts/check_doc_links.py _build/sphinx-warnings.log
- name: Upload documentation artifact
if: >-
github.repository == 'OptimalScale/LMFlow' &&
github.event_name != 'pull_request' &&
github.ref == 'refs/heads/main'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: documentation-html
path: _build/html
if-no-files-found: error
retention-days: 1

deploy:
name: Deploy documentation
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
if: >-
github.repository == 'OptimalScale/LMFlow' &&
github.event_name != 'pull_request' &&
github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
contents: write
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
- name: Check out repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2
- name: Download documentation artifact
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: documentation-html
path: _build/html
- name: Deploy to gh-pages branch
uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3.9.3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_branch: gh-pages
publish_dir: _build/html
force_orphan: true
1 change: 0 additions & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ sphinx_design
myst-parser
sphinx-autoapi
matplotlib
numpydoc
31 changes: 30 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"matplotlib.sphinxext.plot_directive",
# "myst_nb",
# "nbsphinx", # Uncomment and comment-out MyST-NB for local testing purposes.
"numpydoc",
"sphinx.ext.napoleon",
# "sphinx_togglebutton",
# "sphinx_favicon",
]
Expand All @@ -46,6 +46,35 @@

autoapi_type = "python"
autoapi_dirs = ["../../src"]
autoapi_options = [
"members",
"undoc-members",
"show-inheritance",
"show-module-summary",
"special-members",
]

myst_heading_anchors = 4
show_warning_types = True

_autoapi_internal_modules = (
"lmflow.pipeline.utils",
"lmflow.utils.deprecated",
"lmflow.utils.protocol",
)


def _skip_internal_autoapi_modules(app, what, name, obj, skip, options):
if what in {"module", "package"} and any(
name == module_name or name.startswith(f"{module_name}.")
for module_name in _autoapi_internal_modules
):
return True
return skip


def setup(app):
app.connect("autoapi-skip-member", _skip_internal_autoapi_modules)

source_suffix = {
".rst": "restructuredtext",
Expand Down
8 changes: 4 additions & 4 deletions docs/source/examples/DATASETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ Conversations should be formatted before feeding into the model. As of now, we'v

| Template Name | Filled Example | Detailed Template |
| ------------- | -------------- | ----------------- |
| `chatglm3` | `[gMASK]sop<\|system\|>`<br>` You are a chatbot developed by LMFlow team.<\|user\|>`<br>` Who are you?<\|assistant\|>`<br>` I am a chatbot developed by LMFlow team.<\|user\|>`<br>` How old are you?<\|assistant\|>`<br>` I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.` | [Link](./supported_conversation_template.md#chatglm3) |
| `chatglm3` | `[gMASK]sop<\|system\|>`<br>` You are a chatbot developed by LMFlow team.<\|user\|>`<br>` Who are you?<\|assistant\|>`<br>` I am a chatbot developed by LMFlow team.<\|user\|>`<br>` How old are you?<\|assistant\|>`<br>` I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.` | [Link](./supported_conversation_template.md#chatglm-3) |
| `chatml` | `<\|im_start\|>system`<br>`You are a chatbot developed by LMFlow team.<\|im_end\|>`<br>`<\|im_start\|>user`<br>`Who are you?<\|im_end\|>`<br>`<\|im_start\|>assistant`<br>`I am a chatbot developed by LMFlow team.<\|im_end\|>`<br>`<\|im_start\|>user`<br>`How old are you?<\|im_end\|>`<br>`<\|im_start\|>assistant`<br>`I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.<\|im_end\|>`<br> | [Link](./supported_conversation_template.md#chatml) |
| `deepseek_v2` | `<|begin▁of▁sentence|>You are a chatbot developed by LMFlow team.`<br><br>`User: Who are you?`<br><br>`Assistant: I am a chatbot developed by LMFlow team.<|end▁of▁sentence|>User: How old are you?`<br><br>`Assistant: I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.<|end▁of▁sentence|>` | [Link](./supported_conversation_template.md#deepseek) |
| `deepseek_v2` | `<|begin▁of▁sentence|>You are a chatbot developed by LMFlow team.`<br><br>`User: Who are you?`<br><br>`Assistant: I am a chatbot developed by LMFlow team.<|end▁of▁sentence|>User: How old are you?`<br><br>`Assistant: I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.<|end▁of▁sentence|>` | [Link](./supported_conversation_template.md#deepseek-v2) |
| `deepseek_v3` | -- | [Link](./supported_conversation_template.md#deepseek-v3) |
| `deepseek_r1` | -- | [Link](./supported_conversation_template.md#deepseek-r1-zero) |
| `deepseek_r1_distill` | -- | [Link](./supported_conversation_template.md#deepseek-r1-distill-llamaqwenl) |
| `deepseek_r1_distill` | -- | [Link](./supported_conversation_template.md#deepseek-r1-distill-llamaqwen) |
| `gemma` | `<bos>You are a chatbot developed by LMFlow team.<start_of_turn>user`<br>`Who are you?<end_of_turn>`<br>`<start_of_turn>model`<br>`I am a chatbot developed by LMFlow team.<end_of_turn>`<br>`<start_of_turn>user`<br>`How old are you?<end_of_turn>`<br>`<start_of_turn>model`<br>`I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.<end_of_turn>`<br> | [Link](./supported_conversation_template.md#gemma) |
| `hymba` | `<extra_id_0>System`<br>`You are a chatbot developed by LMFlow team.`<br>`<tool> {"name": "generate_qrcode", "description": "Generate a QR code for a given text", "parameters": {"type": "object", "properties": {"text": {"type": "string", "description": "The text to encode in the QR code"}}, "required": ["text"]}} </tool>`<br><br>`<extra_id_1>User`<br>`Who are you?`<br>`<extra_id_1>Assistant`<br>`I am a chatbot developed by LMFlow team.`<br>`<extra_id_1>User`<br>`How old are you?`<br>`<extra_id_1>Assistant`<br>`I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.</s>` | [Link](./supported_conversation_template.md#hymba) |
| `internlm2` | `<s><\|im_start\|>system`<br>`You are a chatbot developed by LMFlow team.<\|im_end\|>`<br>`<\|im_start\|>user`<br>`Who are you?<\|im_end\|>`<br>`<\|im_start\|>assistant`<br>`I am a chatbot developed by LMFlow team.<\|im_end\|>`<br>`<\|im_start\|>user`<br>`How old are you?<\|im_end\|>`<br>`<\|im_start\|>assistant`<br>`I don't age like humans do. I exist as a piece of software, so I don't have a concept of age in the traditional sense.<\|im_end\|>`<br> | [Link](./supported_conversation_template.md#internlm2) |
Expand Down Expand Up @@ -389,4 +389,4 @@ please refer to [conversation data](#conversation).
]
}
```
````
````
12 changes: 6 additions & 6 deletions docs/source/examples/customize_conversation_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

We provide the flexibility to customize the conversation template. You can customize your own conversation template by following the steps below:

### 1. Decompose your conversations
## 1. Decompose your conversations
Say you want to make the conversations between user and assistant look like:

```
Expand All @@ -32,7 +32,7 @@ It is easy to abstract the format for each message:

Also, we have a bos token at the beginning of the conversation session.

### 2. Choose proper `Formatter`
## 2. Choose proper `Formatter`
Recall the requirements for a conversation dataset:
> - `system`: `Optional[string]`.
> - `tools`: `Optional[list[string]]`.
Expand All @@ -42,7 +42,7 @@ Recall the requirements for a conversation dataset:

System message, user message, and assistant message are strings thus we can use `StringFormatter` for them.

### 3. Build the template
## 3. Build the template
All preset templates are located at `src/lmflow/utils/conversation_template`.

Within the template file, define your own template like:
Expand Down Expand Up @@ -89,7 +89,7 @@ YOUR_TEMPLATE = ConversationTemplate(

Feel free to create your own template by inheriting the `ConversationTemplate` class. Llama-2 v.s. llama-3 would be a good examples to refer to.

### 4. Register your template
## 4. Register your template
After defining your own template, you need to register it in the `src/lmflow/utils/conversation_template/__init__.py` file.

```python
Expand All @@ -103,7 +103,7 @@ PRESET_TEMPLATES = {
}
```

### 5. Use your template
## 5. Use your template
You are all set! Specify the template name in, for example, your finetune script:

```bash
Expand All @@ -112,4 +112,4 @@ You are all set! Specify the template name in, for example, your finetune script
--dataset_path your_conversation_dataset \
--conversation_template your_template_name \
--output_model_path output_models/your_model
```
```
4 changes: 3 additions & 1 deletion docs/source/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ We provide several examples to show how to use our package in your problem.
:maxdepth: 3
DATASETS
supported_conversation_template
customize_conversation_template
```

```{toctree}
Expand All @@ -24,6 +26,7 @@ For SFT,
:maxdepth: 3
finetuning
medical_finetune
```


Expand Down Expand Up @@ -54,4 +57,3 @@ Refer to [examples](https://github.com/OptimalScale/LMFlow/blob/main/examples).
TASK_GUIDE
```


2 changes: 2 additions & 0 deletions src/lmflow/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,8 @@ class InferencerArguments:
Define a class InferencerArguments using the dataclass decorator. The class contains several optional
parameters that can be used to configure a inferencer.

Parameters
----------
local_rank : str
For distributed training: local_rank
random_seed : int, default = 1
Expand Down
57 changes: 28 additions & 29 deletions src/lmflow/datasets/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,43 +148,40 @@ def _check_hf_json_format(self, data_files: list[str]):
)

def from_dict(self, dict_obj: dict, *args, **kwargs):
r"""
Create a Dataset object from a dictionary.
"""Populate this dataset from an LMFlow dataset dictionary.

The expected dictionary shape is::

Return a Dataset given a dict with format:
{
"type": TYPE,
"instances": [
{
"key_1": VALUE_1.1,
"key_2": VALUE_1.2,
"key_1": VALUE_1_1,
"key_2": VALUE_1_2,
...
},
{
"key_1": VALUE_2.1,
"key_2": VALUE_2.2,
"key_1": VALUE_2_1,
"key_2": VALUE_2_2,
...
},
...
]
}

Parameters
-----------

dict_obj : dict.
A dictionary containing the dataset information.

args : Optional.
Positional arguments.

kwargs : Optional.
Keyword arguments.
----------
dict_obj : dict
Dataset data containing ``type`` and ``instances`` keys.
*args
Positional arguments passed to the selected dataset backend.
**kwargs
Keyword arguments passed to the selected dataset backend.

Returns
---------

self : Dataset object.
-------
Dataset
This dataset instance.
"""
if self.backend == "huggingface":
if KEY_TYPE not in dict_obj:
Expand Down Expand Up @@ -247,29 +244,31 @@ def create_from_dict(cls, dict_obj, *args, **kwargs):
return dataset.from_dict(dict_obj)

def to_dict(self):
r"""
Returns
---------
"""Convert this dataset to the LMFlow dictionary format.

The returned dictionary has the following shape::

Return a dict represents the dataset:
{
"type": TYPE,
"instances": [
{
"key_1": VALUE_1.1,
"key_2": VALUE_1.2,
"key_1": VALUE_1_1,
"key_2": VALUE_1_2,
...
},
{
"key_1": VALUE_2.1,
"key_2": VALUE_2.2,
"key_1": VALUE_2_1,
"key_2": VALUE_2_2,
...
},
...
]
}

A python dict object represents the content of this dataset.
Returns
-------
dict
Dataset data containing ``type`` and ``instances`` keys.
"""
if self.backend == "huggingface":
dict_obj = {}
Expand Down
Loading
Loading