Skip to content

组件

Finetune

lazyllm.components.finetune.AlpacaloraFinetune

Bases: LazyLLMFinetuneBase

此类是 LazyLLMFinetuneBase 的子类,基于 alpaca-lora 项目提供的 LoRA 微调能力,用于对大语言模型进行 LoRA 微调。

Parameters:

  • base_model (str) –

    用于微调的基模型本地路径。

  • target_path (str) –

    微调后 LoRA 权重保存路径。

  • merge_path (Optional[str], default: None ) –

    合并 LoRA 权重后的模型保存路径,默认 None。 若未提供,则在 target_path 下创建 "lazyllm_lora" 与 "lazyllm_merge" 目录。

  • model_name (Optional[str], default: 'LLM' ) –

    模型名称,用于日志前缀,默认 LLM

  • cp_files (Optional[str], default: 'tokeniz*' ) –

    从基模型路径复制配置文件到 merge_path,默认 tokeniz*

  • launcher (launcher, default: remote(ngpus=1) ) –

    微调启动器,默认 launchers.remote(ngpus=1)

  • kw (dict, default: {} ) –

    用于更新默认训练参数的关键字参数,允许更新如下参数:

Other Parameters:

  • data_path (Optional[str]) –

    数据路径,默认 None

  • batch_size (Optional[int]) –

    批大小,默认 64。

  • micro_batch_size (Optional[int]) –

    微批大小,默认 4。

  • num_epochs (Optional[int]) –

    训练轮数,默认 2。

  • learning_rate (Optional[float]) –

    学习率,默认 5.e-4。

  • cutoff_len (Optional[int]) –

    截断长度,默认 1030。

  • filter_nums (Optional[int]) –

    过滤器数量,默认 1024。

  • val_set_size (Optional[int]) –

    验证集大小,默认 200。

  • lora_r (Optional[int]) –

    LoRA 秩,默认 8。

  • lora_alpha (Optional[int]) –

    LoRA 融合因子,默认 32。

  • lora_dropout (Optional[float]) –

    LoRA 丢弃率,默认 0.05。

  • lora_target_modules (Optional[str]) –

    LoRA 目标模块,默认 [wo,wqkv]

  • modules_to_save (Optional[str]) –

    全量微调模块,默认 [tok_embeddings,output]

  • deepspeed (Optional[str]) –

    DeepSpeed 配置路径,默认使用仓库预制 ds.json。

  • prompt_template_name (Optional[str]) –

    提示模板名称,默认 alpaca

  • train_on_inputs (Optional[bool]) –

    是否在输入上训练,默认 True

  • show_prompt (Optional[bool]) –

    是否显示提示,默认 False

  • nccl_port (Optional[int]) –

    NCCL 端口,默认随机在 19000-20500。

Examples:

>>> from lazyllm import finetune
>>> trainer = finetune.alpacalora('path/to/base/model', 'path/to/target')
Source code in lazyllm/components/finetune/alpacalora.py
class AlpacaloraFinetune(LazyLLMFinetuneBase):
    """此类是 ``LazyLLMFinetuneBase`` 的子类,基于 [alpaca-lora](https://github.com/tloen/alpaca-lora) 项目提供的 LoRA 微调能力,用于对大语言模型进行 LoRA 微调。

Args:
    base_model (str): 用于微调的基模型本地路径。
    target_path (str): 微调后 LoRA 权重保存路径。
    merge_path (Optional[str]): 合并 LoRA 权重后的模型保存路径,默认 ``None``。
        若未提供,则在 ``target_path`` 下创建 "lazyllm_lora" 与 "lazyllm_merge" 目录。
    model_name (Optional[str]): 模型名称,用于日志前缀,默认 ``LLM``。
    cp_files (Optional[str]): 从基模型路径复制配置文件到 ``merge_path``,默认 ``tokeniz*``。
    launcher (lazyllm.launcher): 微调启动器,默认 ``launchers.remote(ngpus=1)``。
    kw (dict): 用于更新默认训练参数的关键字参数,允许更新如下参数:

Keyword Args:
    data_path (Optional[str]): 数据路径,默认 ``None``。
    batch_size (Optional[int]): 批大小,默认 64。
    micro_batch_size (Optional[int]): 微批大小,默认 4。
    num_epochs (Optional[int]): 训练轮数,默认 2。
    learning_rate (Optional[float]): 学习率,默认 5.e-4。
    cutoff_len (Optional[int]): 截断长度,默认 1030。
    filter_nums (Optional[int]): 过滤器数量,默认 1024。
    val_set_size (Optional[int]): 验证集大小,默认 200。
    lora_r (Optional[int]): LoRA 秩,默认 8。
    lora_alpha (Optional[int]): LoRA 融合因子,默认 32。
    lora_dropout (Optional[float]): LoRA 丢弃率,默认 0.05。
    lora_target_modules (Optional[str]): LoRA 目标模块,默认 ``[wo,wqkv]``。
    modules_to_save (Optional[str]): 全量微调模块,默认 ``[tok_embeddings,output]``。
    deepspeed (Optional[str]): DeepSpeed 配置路径,默认使用仓库预制 ds.json。
    prompt_template_name (Optional[str]): 提示模板名称,默认 ``alpaca``。
    train_on_inputs (Optional[bool]): 是否在输入上训练,默认 ``True``。
    show_prompt (Optional[bool]): 是否显示提示,默认 ``False``。
    nccl_port (Optional[int]): NCCL 端口,默认随机在 19000-20500。


Examples:
    >>> from lazyllm import finetune
    >>> trainer = finetune.alpacalora('path/to/base/model', 'path/to/target')
    """
    defatult_kw = ArgsDict({
        'data_path': None,
        'batch_size': 64,
        'micro_batch_size': 4,
        'num_epochs': 2,
        'learning_rate': 5.e-4,
        'cutoff_len': 1030,
        'filter_nums': 1024,
        'val_set_size': 200,
        'lora_r': 8,
        'lora_alpha': 32,
        'lora_dropout': 0.05,
        'lora_target_modules': '[wo,wqkv]',
        'modules_to_save': '[tok_embeddings,output]',
        'deepspeed': '',
        'prompt_template_name': 'alpaca',
        'train_on_inputs': True,
        'show_prompt': False,
        'nccl_port': 19081,
    })
    auto_map = {'micro_batch_size': 'micro_batch_size'}

    def __init__(self,
                 base_model,
                 target_path,
                 merge_path=None,
                 model_name='LLM',
                 cp_files='tokeniz*',
                 launcher=launchers.remote(ngpus=1),  # noqa B008
                 **kw
                 ):
        if not merge_path:
            save_path = os.path.join(lazyllm.config['train_target_root'], target_path)
            target_path, merge_path = os.path.join(save_path, 'lazyllm_lora'), os.path.join(save_path, 'lazyllm_merge')
            os.makedirs(target_path, exist_ok=True)
            os.makedirs(merge_path, exist_ok=True)
        super().__init__(
            base_model,
            target_path,
            launcher=launcher,
        )
        self.folder_path = os.path.dirname(os.path.abspath(__file__))
        deepspeed_config_path = os.path.join(self.folder_path, 'alpaca-lora', 'ds.json')
        self.kw = copy.deepcopy(self.defatult_kw)
        self.kw['deepspeed'] = deepspeed_config_path
        self.kw['nccl_port'] = random.randint(19000, 20500)
        self.kw.check_and_update(kw)
        self.merge_path = merge_path
        self.cp_files = cp_files
        self.model_name = model_name

    def cmd(self, trainset, valset=None) -> str:
        """生成用于执行Alpaca-LoRA微调和模型合并的shell命令序列。

Args:
    trainset (str): 训练数据集路径,支持相对data_path配置的路径或绝对路径
    valset (str, optional): 验证数据集路径,未指定时将从训练集中自动划分

**Returns:**

- str or list: 当不需要合并模型时返回单个命令字符串,需要合并时返回包含微调命令、合并命令和文件拷贝命令的列表


Examples:
    >>> from lazyllm import finetune
    >>> trainer = finetune.alpacalora('path/to/base/model', 'path/to/target')
    >>> cmd = trainer.cmd("my_dataset.json")
    """
        thirdparty.check_packages(['datasets', 'deepspeed', 'fire', 'numpy', 'peft', 'torch', 'transformers'])
        if not os.path.exists(trainset):
            defatult_path = os.path.join(lazyllm.config['data_path'], trainset)
            if os.path.exists(defatult_path):
                trainset = defatult_path
        if not self.kw['data_path']:
            self.kw['data_path'] = trainset

        run_file_path = os.path.join(self.folder_path, 'alpaca-lora', 'finetune.py')
        cmd = (f'python {run_file_path} '
               f'--base_model={self.base_model} '
               f'--output_dir={self.target_path} '
            )
        cmd += self.kw.parse_kwargs()
        cmd += f' 2>&1 | tee {os.path.join(self.target_path, self.model_name)}_$(date +"%Y-%m-%d_%H-%M-%S").log'

        if self.merge_path:
            run_file_path = os.path.join(self.folder_path, 'alpaca-lora', 'utils', 'merge_weights.py')

            cmd = [cmd,
                   f'python {run_file_path} '
                   f'--base={self.base_model} '
                   f'--adapter={self.target_path} '
                   f'--save_path={self.merge_path} ',
                   f' cp {os.path.join(self.base_model, self.cp_files)} {self.merge_path} '
                ]

        # cmd = 'realpath .'
        return cmd

cmd(trainset, valset=None)

生成用于执行Alpaca-LoRA微调和模型合并的shell命令序列。

Parameters:

  • trainset (str) –

    训练数据集路径,支持相对data_path配置的路径或绝对路径

  • valset (str, default: None ) –

    验证数据集路径,未指定时将从训练集中自动划分

Returns:

  • str or list: 当不需要合并模型时返回单个命令字符串,需要合并时返回包含微调命令、合并命令和文件拷贝命令的列表

Examples:

>>> from lazyllm import finetune
>>> trainer = finetune.alpacalora('path/to/base/model', 'path/to/target')
>>> cmd = trainer.cmd("my_dataset.json")
Source code in lazyllm/components/finetune/alpacalora.py
    def cmd(self, trainset, valset=None) -> str:
        """生成用于执行Alpaca-LoRA微调和模型合并的shell命令序列。

Args:
    trainset (str): 训练数据集路径,支持相对data_path配置的路径或绝对路径
    valset (str, optional): 验证数据集路径,未指定时将从训练集中自动划分

**Returns:**

- str or list: 当不需要合并模型时返回单个命令字符串,需要合并时返回包含微调命令、合并命令和文件拷贝命令的列表


Examples:
    >>> from lazyllm import finetune
    >>> trainer = finetune.alpacalora('path/to/base/model', 'path/to/target')
    >>> cmd = trainer.cmd("my_dataset.json")
    """
        thirdparty.check_packages(['datasets', 'deepspeed', 'fire', 'numpy', 'peft', 'torch', 'transformers'])
        if not os.path.exists(trainset):
            defatult_path = os.path.join(lazyllm.config['data_path'], trainset)
            if os.path.exists(defatult_path):
                trainset = defatult_path
        if not self.kw['data_path']:
            self.kw['data_path'] = trainset

        run_file_path = os.path.join(self.folder_path, 'alpaca-lora', 'finetune.py')
        cmd = (f'python {run_file_path} '
               f'--base_model={self.base_model} '
               f'--output_dir={self.target_path} '
            )
        cmd += self.kw.parse_kwargs()
        cmd += f' 2>&1 | tee {os.path.join(self.target_path, self.model_name)}_$(date +"%Y-%m-%d_%H-%M-%S").log'

        if self.merge_path:
            run_file_path = os.path.join(self.folder_path, 'alpaca-lora', 'utils', 'merge_weights.py')

            cmd = [cmd,
                   f'python {run_file_path} '
                   f'--base={self.base_model} '
                   f'--adapter={self.target_path} '
                   f'--save_path={self.merge_path} ',
                   f' cp {os.path.join(self.base_model, self.cp_files)} {self.merge_path} '
                ]

        # cmd = 'realpath .'
        return cmd

lazyllm.components.finetune.CollieFinetune

Bases: LazyLLMFinetuneBase

此类是 LazyLLMFinetuneBase 的子类,基于 Collie 框架提供的 LoRA 微调能力,用于对大语言模型进行 LoRA 微调。

Parameters:

  • base_model (str) –

    用于微调的基模型路径。

  • target_path (str) –

    微调后 LoRA 权重保存路径。

  • merge_path (Optional[str], default: None ) –

    合并 LoRA 权重后的模型路径,默认 None。 若未提供,则在 target_path 下创建 "lazyllm_lora" 与 "lazyllm_merge" 目录。

  • model_name (Optional[str], default: 'LLM' ) –

    模型名称,用于日志前缀,默认 "LLM"。

  • cp_files (Optional[str], default: 'tokeniz*' ) –

    指定从基模型路径复制到 merge_path 的配置文件,默认 "tokeniz*"。

  • launcher (launcher, default: remote(ngpus=1) ) –

    微调启动器,默认 launchers.remote(ngpus=1)

  • kw (dict, default: {} ) –

    用于更新默认训练参数的关键字参数。仅允许更新如下参数:

Other Parameters:

  • data_path (Optional[str]) –

    数据路径,默认 None

  • batch_size (Optional[int]) –

    批大小,默认 64。

  • micro_batch_size (Optional[int]) –

    微批大小,默认 4。

  • num_epochs (Optional[int]) –

    训练轮数,默认 3。

  • learning_rate (Optional[float]) –

    学习率,默认 5.e-4。

  • dp_size (Optional[int]) –

    数据并行参数,默认 8。

  • pp_size (Optional[int]) –

    流水线并行参数,默认 1。

  • tp_size (Optional[int]) –

    张量并行参数,默认 1。

  • lora_r (Optional[int]) –

    LoRA 秩,默认 8。

  • lora_alpha (Optional[int]) –

    LoRA 融合因子,默认 16。

  • lora_dropout (Optional[float]) –

    LoRA 丢弃率,默认 0.05。

  • lora_target_modules (Optional[str]) –

    LoRA 目标模块,默认 [wo,wqkv]

  • modules_to_save (Optional[str]) –

    全量微调模块,默认 [tok_embeddings,output]

  • prompt_template_name (Optional[str]) –

    提示模板名称,默认 "alpaca"。

Examples:

>>> from lazyllm import finetune
>>> trainer = finetune.collie('path/to/base/model', 'path/to/target')
Source code in lazyllm/components/finetune/collie.py
class CollieFinetune(LazyLLMFinetuneBase):
    """此类是 ``LazyLLMFinetuneBase`` 的子类,基于 [Collie](https://github.com/OpenLMLab/collie) 框架提供的 LoRA 微调能力,用于对大语言模型进行 LoRA 微调。

Args:
    base_model (str): 用于微调的基模型路径。
    target_path (str): 微调后 LoRA 权重保存路径。
    merge_path (Optional[str]): 合并 LoRA 权重后的模型路径,默认 ``None``。
        若未提供,则在 ``target_path`` 下创建 "lazyllm_lora" 与 "lazyllm_merge" 目录。
    model_name (Optional[str]): 模型名称,用于日志前缀,默认 "LLM"。
    cp_files (Optional[str]): 指定从基模型路径复制到 ``merge_path`` 的配置文件,默认 "tokeniz*"。
    launcher (lazyllm.launcher): 微调启动器,默认 ``launchers.remote(ngpus=1)``。
    kw (dict): 用于更新默认训练参数的关键字参数。仅允许更新如下参数:

Keyword Args:
    data_path (Optional[str]): 数据路径,默认 ``None``。
    batch_size (Optional[int]): 批大小,默认 64。
    micro_batch_size (Optional[int]): 微批大小,默认 4。
    num_epochs (Optional[int]): 训练轮数,默认 3。
    learning_rate (Optional[float]): 学习率,默认 5.e-4。
    dp_size (Optional[int]): 数据并行参数,默认 8。
    pp_size (Optional[int]): 流水线并行参数,默认 1。
    tp_size (Optional[int]): 张量并行参数,默认 1。
    lora_r (Optional[int]): LoRA 秩,默认 8。
    lora_alpha (Optional[int]): LoRA 融合因子,默认 16。
    lora_dropout (Optional[float]): LoRA 丢弃率,默认 0.05。
    lora_target_modules (Optional[str]): LoRA 目标模块,默认 ``[wo,wqkv]``。
    modules_to_save (Optional[str]): 全量微调模块,默认 ``[tok_embeddings,output]``。
    prompt_template_name (Optional[str]): 提示模板名称,默认 "alpaca"。


Examples:
    >>> from lazyllm import finetune
    >>> trainer = finetune.collie('path/to/base/model', 'path/to/target')
    """
    defatult_kw = ArgsDict({
        'data_path': None,
        'batch_size': 64,
        'micro_batch_size': 4,
        'num_epochs': 3,
        'learning_rate': 5.e-4,
        'dp_size': 8,
        'pp_size': 1,
        'tp_size': 1,
        'lora_r': 8,
        'lora_alpha': 16,
        'lora_dropout': 0.05,
        'lora_target_modules': '[wo,wqkv]',
        'modules_to_save': '[tok_embeddings,output]',
        'prompt_template_name': 'alpaca',
    })
    auto_map = {
        'ddp': 'dp_size',
        'micro_batch_size': 'micro_batch_size',
        'tp': 'tp_size',
    }

    def __init__(self,
                 base_model,
                 target_path,
                 merge_path=None,
                 model_name='LLM',
                 cp_files='tokeniz*',
                 launcher=launchers.remote(ngpus=1),  # noqa B008
                 **kw
                 ):
        if not merge_path:
            save_path = os.path.join(lazyllm.config['train_target_root'], target_path)
            target_path, merge_path = os.path.join(save_path, 'lazyllm_lora'), os.path.join(save_path, 'lazyllm_merge')
            os.makedirs(target_path, exist_ok=True)
            os.makedirs(merge_path, exist_ok=True)
        super().__init__(
            base_model,
            target_path,
            launcher=launcher,
        )
        self.folder_path = os.path.dirname(os.path.abspath(__file__))
        self.kw = copy.deepcopy(self.defatult_kw)
        self.kw.check_and_update(kw)
        self.merge_path = merge_path
        self.cp_files = cp_files
        self.model_name = model_name

    def cmd(self, trainset, valset=None) -> str:
        thirdparty.check_packages(['numpy', 'peft', 'torch', 'transformers'])
        if not os.path.exists(trainset):
            defatult_path = os.path.join(lazyllm.config['data_path'], trainset)
            if os.path.exists(defatult_path):
                trainset = defatult_path
        if not self.kw['data_path']:
            self.kw['data_path'] = trainset

        run_file_path = os.path.join(self.folder_path, 'collie', 'finetune.py')
        cmd = (f'python {run_file_path} '
               f'--base_model={self.base_model} '
               f'--output_dir={self.target_path} '
            )
        cmd += self.kw.parse_kwargs()
        cmd += f' 2>&1 | tee {os.path.join(self.target_path, self.model_name)}_$(date +"%Y-%m-%d_%H-%M-%S").log'

        if self.merge_path:
            run_file_path = os.path.join(self.folder_path, 'alpaca-lora', 'utils', 'merge_weights.py')

            cmd = [cmd,
                   f'python {run_file_path} '
                   f'--base={self.base_model} '
                   f'--adapter={self.target_path} '
                   f'--save_path={self.merge_path} ',
                   f' cp {os.path.join(self.base_model, self.cp_files)} {self.merge_path} '
                ]

        return cmd

lazyllm.components.finetune.LlamafactoryFinetune

Bases: LazyLLMFinetuneBase

此类是 LazyLLMFinetuneBase 的子类,基于 LLaMA-Factory 框架提供的训练能力,用于对大语言模型(或视觉语言模型)进行训练。

Parameters:

  • base_model

    用于进行训练的基模型路径。支持本地路径,若路径不存在则尝试从配置的模型路径中查找。

  • target_path

    训练完成后,模型权重保存的目标路径。

  • merge_path (str, default: None ) –

    模型合并LoRA权重后的保存路径,默认为None。 如果未指定,将在 target_path 下自动创建两个目录: - "lazyllm_lora"(用于存放LoRA训练权重) - "lazyllm_merge"(用于存放合并后的模型权重)

  • config_path (str, default: None ) –

    训练配置的 YAML 文件路径,默认None。 如果未指定,则使用默认配置文件 llama_factory/sft.yaml。 配置文件支持覆盖默认训练参数。

  • export_config_path (str, default: None ) –

    LoRA权重合并导出配置的 YAML 文件路径,默认None。 如果未指定,则使用默认配置文件 llama_factory/lora_export.yaml

  • lora_r (int, default: None ) –

    LoRA的秩(rank),若提供则覆盖配置中的 lora_rank

  • modules_to_save (str, default: None ) –

    额外需要保存的模型模块名称列表,格式类似于Python列表字符串,如 "[module1,module2]"。

  • lora_target_modules (str, default: None ) –

    目标LoRA微调的模块名称列表,格式同上。

  • launcher (launcher, default: remote(ngpus=1, sync=True) ) –

    微调任务的启动器,默认为单卡同步远程启动器 launchers.remote(ngpus=1, sync=True)

  • **kw

    关键字参数,用于动态覆盖默认训练配置中的参数。

此类的关键字参数及其默认值如下:

Other Parameters:

  • stage (Literal['pt', 'sft', 'rm', 'ppo', 'dpo', 'kto']) –

    默认值是:sft。将在训练中执行的阶段。

  • do_train (bool) –

    默认值是:True。是否运行训练。

  • finetuning_type (Literal['lora', 'freeze', 'full']) –

    默认值是:lora。要使用的微调方法。

  • lora_target (str) –

    默认值是:all。要应用LoRA的目标模块的名称。使用逗号分隔多个模块。使用all指定所有线性模块。

  • template (Optional[str]) –

    默认值是:None。用于构建训练和推理提示的模板。

  • cutoff_len (int) –

    默认值是:1024。数据集中token化后输入的截止长度。

  • max_samples (Optional[int]) –

    默认值是:1000。出于调试目的,截断每个数据集的示例数量。

  • overwrite_cache (bool) –

    默认值是:True。覆盖缓存的训练和评估集。

  • preprocessing_num_workers (Optional[int]) –

    默认值是:16。用于预处理的进程数。

  • dataset_dir (str) –

    默认值是:lazyllm_temp_dir。包含数据集的文件夹的路径。如果没有明确指定,LazyLLM将在当前工作目录的 .temp 文件夹中生成一个 dataset_info.json 文件,供LLaMA-Factory使用。

  • logging_steps (float) –

    默认值是:10。每X个更新步骤记录一次日志。应该是整数或范围在 [0,1) 的浮点数。如果小于1,将被解释为总训练步骤的比例。

  • save_steps (float) –

    默认值是:500。每X个更新步骤保存一次检查点。应该是整数或范围在 [0,1) 的浮点数。如果小于1,将被解释为总训练步骤的比例。

  • plot_loss (bool) –

    默认值是:True。是否保存训练损失曲线。

  • overwrite_output_dir (bool) –

    默认值是:True。覆盖输出目录的内容。

  • per_device_train_batch_size (int) –

    默认值是:1。每个GPU/TPU/MPS/NPU核心/CPU的训练批次的大小。

  • gradient_accumulation_steps (int) –

    默认值是:8。在执行反向传播及参数更新前,要累积的更新步骤数。

  • learning_rate (float) –

    默认值是:1e-04。AdamW的初始学习率。

  • num_train_epochs (float) –

    默认值是:3.0。要执行的总训练周期数。

  • lr_scheduler_type (Union[SchedulerType, str]) –

    默认值是:cosine。要使用的调度器类型。

  • warmup_ratio (float) –

    默认值是:0.1。在总步骤的 warmup_ratio 分之一阶段内进行线性预热。

  • fp16 (bool) –

    默认值是:True。是否使用fp16(混合)精度,而不是32位。

  • ddp_timeout (Optional[int]) –

    默认值是:180000000。覆盖分布式训练的默认超时时间(值应以秒为单位给出)。

  • report_to (Union[NoneType, str, List[str]]) –

    默认值是:tensorboard。要将结果和日志报告到的集成列表。

  • val_size (float) –

    默认值是:0.1。验证集的大小,应该是整数或范围在[0,1)的浮点数。

  • per_device_eval_batch_size (int) –

    默认值是:1。每个GPU/TPU/MPS/NPU核心/CPU的验证集批次大小。

  • eval_strategy (Union[IntervalStrategy, str]) –

    默认值是:steps。要使用的验证评估策略。

  • eval_steps (Optional[float]) –

    默认值是:500。每X个步骤运行一次验证评估。应该是整数或范围在[0,1)的浮点数。如果小于1,将被解释为总训练步骤的比例。

Examples:

>>> from lazyllm import finetune
>>> trainer = finetune.llamafactory('internlm2-chat-7b', 'path/to/target')
<lazyllm.llm.finetune type=LlamafactoryFinetune>
Source code in lazyllm/components/finetune/llamafactory.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
class LlamafactoryFinetune(LazyLLMFinetuneBase):
    """此类是 ``LazyLLMFinetuneBase`` 的子类,基于 [LLaMA-Factory](https://github.com/hiyouga/LLaMA-Factory) 框架提供的训练能力,用于对大语言模型(或视觉语言模型)进行训练。

Args:
    base_model: 用于进行训练的基模型路径。支持本地路径,若路径不存在则尝试从配置的模型路径中查找。
    target_path: 训练完成后,模型权重保存的目标路径。
    merge_path (str, optional): 模型合并LoRA权重后的保存路径,默认为None。
        如果未指定,将在 ``target_path`` 下自动创建两个目录:
        - "lazyllm_lora"(用于存放LoRA训练权重)
        - "lazyllm_merge"(用于存放合并后的模型权重)
    config_path (str, optional): 训练配置的 YAML 文件路径,默认None。
        如果未指定,则使用默认配置文件 ``llama_factory/sft.yaml``。
        配置文件支持覆盖默认训练参数。
    export_config_path (str, optional): LoRA权重合并导出配置的 YAML 文件路径,默认None。
        如果未指定,则使用默认配置文件 ``llama_factory/lora_export.yaml``。
    lora_r (int, optional): LoRA的秩(rank),若提供则覆盖配置中的 ``lora_rank``。
    modules_to_save (str, optional): 额外需要保存的模型模块名称列表,格式类似于Python列表字符串,如 "[module1,module2]"。
    lora_target_modules (str, optional): 目标LoRA微调的模块名称列表,格式同上。
    launcher (lazyllm.launcher, optional): 微调任务的启动器,默认为单卡同步远程启动器 ``launchers.remote(ngpus=1, sync=True)``。
    **kw: 关键字参数,用于动态覆盖默认训练配置中的参数。

此类的关键字参数及其默认值如下:

Keyword Args:
    stage (typing.Literal['pt', 'sft', 'rm', 'ppo', 'dpo', 'kto']): 默认值是:``sft``。将在训练中执行的阶段。
    do_train (bool): 默认值是:``True``。是否运行训练。
    finetuning_type (typing.Literal['lora', 'freeze', 'full']): 默认值是:``lora``。要使用的微调方法。
    lora_target (str): 默认值是:``all``。要应用LoRA的目标模块的名称。使用逗号分隔多个模块。使用`all`指定所有线性模块。
    template (typing.Optional[str]): 默认值是:``None``。用于构建训练和推理提示的模板。
    cutoff_len (int): 默认值是:``1024``。数据集中token化后输入的截止长度。
    max_samples (typing.Optional[int]): 默认值是:``1000``。出于调试目的,截断每个数据集的示例数量。
    overwrite_cache (bool): 默认值是:``True``。覆盖缓存的训练和评估集。
    preprocessing_num_workers (typing.Optional[int]): 默认值是:``16``。用于预处理的进程数。
    dataset_dir (str): 默认值是:``lazyllm_temp_dir``。包含数据集的文件夹的路径。如果没有明确指定,LazyLLM将在当前工作目录的 ``.temp`` 文件夹中生成一个 ``dataset_info.json`` 文件,供LLaMA-Factory使用。
    logging_steps (float): 默认值是:``10``。每X个更新步骤记录一次日志。应该是整数或范围在 ``[0,1)`` 的浮点数。如果小于1,将被解释为总训练步骤的比例。
    save_steps (float): 默认值是:``500``。每X个更新步骤保存一次检查点。应该是整数或范围在 ``[0,1)`` 的浮点数。如果小于1,将被解释为总训练步骤的比例。
    plot_loss (bool): 默认值是:``True``。是否保存训练损失曲线。
    overwrite_output_dir (bool): 默认值是:``True``。覆盖输出目录的内容。
    per_device_train_batch_size (int): 默认值是:``1``。每个GPU/TPU/MPS/NPU核心/CPU的训练批次的大小。
    gradient_accumulation_steps (int): 默认值是:``8``。在执行反向传播及参数更新前,要累积的更新步骤数。
    learning_rate (float): 默认值是:``1e-04``。AdamW的初始学习率。
    num_train_epochs (float): 默认值是:``3.0``。要执行的总训练周期数。
    lr_scheduler_type (typing.Union[transformers.trainer_utils.SchedulerType, str]): 默认值是:``cosine``。要使用的调度器类型。
    warmup_ratio (float): 默认值是:``0.1``。在总步骤的 ``warmup_ratio`` 分之一阶段内进行线性预热。
    fp16 (bool): 默认值是:``True``。是否使用fp16(混合)精度,而不是32位。
    ddp_timeout (typing.Optional[int]): 默认值是:``180000000``。覆盖分布式训练的默认超时时间(值应以秒为单位给出)。
    report_to (typing.Union[NoneType, str, typing.List[str]]): 默认值是:``tensorboard``。要将结果和日志报告到的集成列表。
    val_size (float): 默认值是:``0.1``。验证集的大小,应该是整数或范围在`[0,1)`的浮点数。
    per_device_eval_batch_size (int): 默认值是:``1``。每个GPU/TPU/MPS/NPU核心/CPU的验证集批次大小。
    eval_strategy (typing.Union[transformers.trainer_utils.IntervalStrategy, str]): 默认值是:``steps``。要使用的验证评估策略。
    eval_steps (typing.Optional[float]): 默认值是:``500``。每X个步骤运行一次验证评估。应该是整数或范围在`[0,1)`的浮点数。如果小于1,将被解释为总训练步骤的比例。


Examples:
    >>> from lazyllm import finetune
    >>> trainer = finetune.llamafactory('internlm2-chat-7b', 'path/to/target')
    <lazyllm.llm.finetune type=LlamafactoryFinetune>
    """
    auto_map = {
        'gradient_step': 'gradient_accumulation_steps',
        'micro_batch_size': 'per_device_train_batch_size',
    }

    def __init__(self,
                 base_model,
                 target_path,
                 merge_path=None,
                 config_path=None,
                 export_config_path=None,
                 lora_r=None,
                 modules_to_save=None,
                 lora_target_modules=None,
                 launcher=launchers.remote(ngpus=1, sync=True),  # noqa B008
                 **kw
                 ):
        if not os.path.exists(base_model):
            default_path = os.path.join(lazyllm.config['model_path'], base_model)
            if os.path.exists(default_path):
                base_model = default_path
        if not merge_path:
            normalized_target = os.path.normpath(target_path)
            if normalized_target.endswith('lazyllm_lora'):
                merge_path = normalized_target.replace('lazyllm_lora', 'lazyllm_merge')
            else:
                save_path = os.path.join(lazyllm.config['train_target_root'], target_path)
                target_path = os.path.join(save_path, 'lazyllm_lora')
                merge_path = os.path.join(save_path, 'lazyllm_merge')
            os.makedirs(target_path, exist_ok=True)
            os.makedirs(merge_path, exist_ok=True)
        super().__init__(
            base_model,
            target_path,
            launcher=launcher,
        )
        self.merge_path = merge_path
        self.temp_yaml_file = None
        self.temp_export_yaml_file = None
        self.config_path = config_path
        self.export_config_path = export_config_path
        self.config_folder_path = os.path.dirname(os.path.abspath(__file__))

        default_config_path = os.path.join(self.config_folder_path, 'llama_factory', 'sft.yaml')
        self.template_dict = ArgsDict(self._load_yaml(default_config_path))

        if self.config_path:
            self.template_dict.update(self._load_yaml(self.config_path))

        if lora_r:
            self.template_dict['lora_rank'] = lora_r
        if modules_to_save:
            self.template_dict['additional_target'] = modules_to_save.strip('[]')
        if lora_target_modules:
            self.template_dict['lora_target'] = lora_target_modules.strip('[]')
        self.template_dict['model_name_or_path'] = base_model
        self.template_dict['output_dir'] = target_path
        self.template_dict['template'] = self._get_template_name(base_model)

        # Filter kw to only include keys that exist in template_dict
        # This ensures check_and_update won't fail due to unexpected keys
        # Keys not in template_dict will be silently ignored (they may be used elsewhere)
        ignored_keys = set(kw.keys()) - set(self.template_dict.keys())
        if ignored_keys:
            lazyllm.LOG.info(f'Ignored parameters not in template_dict: {sorted(ignored_keys)}')
        filtered_kw = {k: v for k, v in kw.items() if k in self.template_dict}

        self.template_dict.check_and_update(filtered_kw)

        default_export_config_path = os.path.join(self.config_folder_path, 'llama_factory', 'lora_export.yaml')
        self.export_dict = ArgsDict(self._load_yaml(default_export_config_path))

        if self.export_config_path:
            self.export_dict.update(self._load_yaml(self.export_config_path))

        self.export_dict['model_name_or_path'] = base_model
        self.export_dict['adapter_name_or_path'] = target_path
        self.export_dict['export_dir'] = merge_path
        self.export_dict['template'] = self.template_dict['template']

        self.temp_folder = os.path.join(lazyllm.config['temp_dir'], 'llamafactory_config', str(uuid.uuid4())[:10])
        if not os.path.exists(self.temp_folder):
            os.makedirs(self.temp_folder)
        self.log_file_path = None

    def _get_template_name(self, base_model):
        base_name = os.path.basename(base_model).lower()
        key_value = match_longest_prefix(base_name)
        if key_value:
            return key_value
        else:
            raise RuntimeError(f'Cannot find prfix of base_model({base_model}) '
                               f'in DEFAULT_TEMPLATE of LLaMA_Factory: {llamafactory_mapping_dict}')

    def _load_yaml(self, config_path):
        with open(config_path, 'r') as file:
            config_dict = yaml.safe_load(file)
        return config_dict

    def _build_temp_yaml(self, updated_template_str, prefix='train_'):
        fd, temp_yaml_file = tempfile.mkstemp(prefix=prefix, suffix='.yaml', dir=self.temp_folder)
        with os.fdopen(fd, 'w') as temp_file:
            temp_file.write(updated_template_str)
        return temp_yaml_file

    def _build_temp_dataset_info(self, datapaths, stage=None):  # noqa C901
        """
        Build dataset_info.json based on training stage and dataset format.
        """
        if isinstance(datapaths, str):
            datapaths = [datapaths]
        elif isinstance(datapaths, list) and all(isinstance(item, str) for item in datapaths):
            pass
        else:
            raise TypeError(f'datapaths({datapaths}) should be str or list of str.')

        if stage is None:
            stage = self.template_dict.get('stage', 'sft').lower()

        supported_stages = ['sft', 'pt', 'dpo']
        if stage not in supported_stages:
            raise ValueError(
                f'Unsupported training stage: {stage}. '
                f'Only supported stages are: {", ".join(supported_stages)}'
            )

        temp_dataset_dict = dict()
        for datapath in datapaths:
            datapath = os.path.join(lazyllm.config['data_path'], datapath)
            assert os.path.isfile(datapath)
            file_name, _ = os.path.splitext(os.path.basename(datapath))
            temp_dataset_dict[file_name] = {'file_name': datapath}

            formatting = None
            first_item = None

            if stage == 'pt':
                formatting = None
                try:
                    with open(datapath, 'r', encoding='utf-8') as file:
                        first_bytes = file.read(1024)
                        file.seek(0)

                        if not first_bytes.strip().startswith(('[', '{')):
                            lines = file.readlines()
                            if not lines:
                                raise ValueError(f'PT stage: Dataset file {datapath} is empty')

                            first_item = {'text': lines[0].strip() if lines else ''}
                            lazyllm.LOG.info(
                                f'PT stage: Dataset {file_name} detected as plain text format '
                                f'({len(lines)} lines). LLaMA-Factory will handle conversion.'
                            )
                        else:
                            try:
                                data = json.load(file)
                                if isinstance(data, list):
                                    if not data:
                                        raise ValueError(f'PT stage: Dataset file {datapath} is empty (empty list)')
                                    first_item = data[0]
                                elif isinstance(data, dict):
                                    first_item = data
                                else:
                                    raise ValueError(
                                        f'PT stage: Dataset file {datapath} has invalid JSON structure. '
                                        f'Expected list or dict, got {type(data).__name__}'
                                    )
                                lazyllm.LOG.info(
                                    f'PT stage: Dataset {file_name} detected as JSON format. '
                                    f'Looking for "text" field.'
                                )
                            except json.JSONDecodeError as json_err:
                                raise ValueError(
                                    f'PT stage: Dataset file {datapath} is neither valid plain text nor valid JSON. '
                                    f'JSON parse error: {str(json_err)}'
                                )

                    if not first_item:
                        raise ValueError(f'PT stage: Failed to extract first item from dataset {datapath}')

                    self._build_alpaca_dataset_info(
                        temp_dataset_dict, file_name, first_item, stage
                    )

                except Exception as e:
                    error_msg = (
                        f'PT stage: Failed to process dataset {file_name} from {datapath}. '
                        f'Error: {str(e)}. '
                        f'PT mode requires either: '
                        f'(1) Plain text format (one text per line), or '
                        f'(2) JSON format with "text" field in each object.'
                    )
                    lazyllm.LOG.error(error_msg)
                    raise ValueError(error_msg) from e
            else:
                formatting = 'alpaca'
                try:
                    with open(datapath, 'r', encoding='utf-8') as file:
                        data = json.load(file)
                    if not data:
                        raise ValueError(f'Dataset file {datapath} is empty')

                    first_item = data[0]

                    if 'messages' in first_item:
                        formatting = 'sharegpt'
                        self._build_sharegpt_dataset_info(
                            temp_dataset_dict, file_name, first_item, stage
                        )
                    else:
                        self._build_alpaca_dataset_info(
                            temp_dataset_dict, file_name, first_item, stage
                        )

                except Exception as e:
                    lazyllm.LOG.warning(
                        f'Failed to analyze dataset {datapath} for stage {stage}: {e}. '
                        f'Using default formatting.'
                    )

            if formatting is not None:
                temp_dataset_dict[file_name].update({'formatting': formatting})

        self.temp_dataset_info_path = os.path.join(self.temp_folder, 'dataset_info.json')
        with open(self.temp_dataset_info_path, 'w') as json_file:
            json.dump(temp_dataset_dict, json_file, indent=4)
        return self.temp_dataset_info_path, ','.join(temp_dataset_dict.keys())

    def _build_alpaca_dataset_info(self, dataset_dict, file_name, first_item, stage):  # noqa C901
        """
        Build dataset info for Alpaca format based on training stage.
        """
        columns = {}
        ranking = False

        media_types = []
        for media in ['images', 'videos', 'audios']:
            if media in first_item:
                media_types.append(media)

        if stage == 'pt':
            if 'text' in first_item:
                columns['prompt'] = 'text'
            else:
                if 'instruction' in first_item:
                    columns['prompt'] = 'instruction'
                elif 'output' in first_item:
                    columns['prompt'] = 'output'
                else:
                    lazyllm.LOG.warning(
                        f'PT stage: No "text" field found in dataset {file_name}, '
                        f'using "instruction" or "output" as fallback'
                    )
                    columns['prompt'] = 'instruction' if 'instruction' in first_item else 'output'

        elif stage == 'dpo':
            ranking = True
            if 'chosen' in first_item and 'rejected' in first_item:
                columns['prompt'] = 'instruction' if 'instruction' in first_item else None
                columns['query'] = 'input' if 'input' in first_item else None
                columns['chosen'] = 'chosen'
                columns['rejected'] = 'rejected'
                columns = {k: v for k, v in columns.items() if v is not None}
            else:
                raise ValueError(
                    f'DPO stage requires "chosen" and "rejected" fields in dataset, '
                    f'but found: {list(first_item.keys())}'
                )

        elif stage == 'sft':
            if 'instruction' in first_item:
                columns['prompt'] = 'instruction'
            if 'input' in first_item:
                columns['query'] = 'input'
            if 'output' in first_item:
                columns['response'] = 'output'
            if 'system' in first_item:
                columns['system'] = 'system'
            if 'history' in first_item:
                columns['history'] = 'history'
        else:
            raise ValueError(f'Unsupported stage: {stage}. Only sft, pt, dpo are supported.')

        if media_types:
            multimodal_columns = {item: item for item in media_types}
            multimodal_columns.update(columns)
            columns = multimodal_columns

        update_dict = {'columns': columns}
        if ranking:
            update_dict['ranking'] = True

        dataset_dict[file_name].update(update_dict)

    def _build_sharegpt_dataset_info(self, dataset_dict, file_name, first_item, stage):  # noqa C901
        """
        Build dataset info for ShareGPT format based on training stage.
        """
        columns = {}
        ranking = False

        media_types = []
        for media in ['images', 'videos', 'audios']:
            if media in first_item:
                media_types.append(media)

        if stage == 'dpo':
            ranking = True
            if 'chosen' in first_item and 'rejected' in first_item:
                columns['messages'] = 'conversations' if 'conversations' in first_item else 'messages'
                columns['chosen'] = 'chosen'
                columns['rejected'] = 'rejected'
            else:
                raise ValueError(
                    f'DPO stage requires "chosen" and "rejected" fields in dataset, '
                    f'but found: {list(first_item.keys())}'
                )
        elif stage == 'sft':
            columns['messages'] = 'conversations' if 'conversations' in first_item else 'messages'
            if 'system' in first_item:
                columns['system'] = 'system'
            if 'tools' in first_item:
                columns['tools'] = 'tools'

            if 'messages' in first_item and isinstance(first_item['messages'], list):
                if len(first_item['messages']) > 0:
                    msg = first_item['messages'][0]
                    if 'role' in msg and 'content' in msg:
                        dataset_dict[file_name].update({
                            'tags': {
                                'role_tag': 'role',
                                'content_tag': 'content',
                                'user_tag': 'user',
                                'assistant_tag': 'assistant',
                                'system_tag': 'system'
                            }
                        })
        else:
            raise ValueError(f'Unsupported stage: {stage}. Only sft, pt, dpo are supported.')

        if media_types:
            multimodal_columns = {item: item for item in media_types}
            multimodal_columns.update(columns)
            columns = multimodal_columns

        update_dict = {'columns': columns}
        if ranking:
            update_dict['ranking'] = True

        dataset_dict[file_name].update(update_dict)

    def _rm_temp_yaml(self):
        if self.temp_yaml_file:
            if os.path.exists(self.temp_yaml_file):
                os.remove(self.temp_yaml_file)
            self.temp_yaml_file = None

    def cmd(self, trainset, valset=None) -> str:
        """生成LLaMA-Factory微调命令序列,包括训练和模型合并命令。

Args:
    trainset (str): 训练数据集路径(支持相对lazyllm.config['data_path']的路径)
    valset (str, optional): 验证数据集路径(当前实现中未直接使用)

**Returns:**

- str: 完整的shell命令字符串,包含:
    - 训练命令(自动配置参数)
    - 日志重定向(保存到目标路径)
    - 可选的模型合并命令(当配置LoRA时)

注意事项:
    - 自动生成带时间戳的训练日志文件
    - 临时文件会在使用后自动清理
    - 支持多种数据格式(alpaca/sharegpt等)
    - 多模态数据(图像/视频/音频)会自动检测处理
"""
        thirdparty.check_packages(['datasets', 'deepspeed', 'numpy', 'peft', 'torch', 'transformers', 'trl'])
        if 'dataset_dir' in self.template_dict and self.template_dict['dataset_dir'] == 'lazyllm_temp_dir':
            stage = self.template_dict.get('stage', 'sft')
            _, datasets = self._build_temp_dataset_info(trainset, stage=stage)
            self.template_dict['dataset_dir'] = self.temp_folder
        else:
            datasets = trainset
        self.template_dict['dataset'] = datasets

        if self.template_dict['finetuning_type'] == 'lora':
            # For LoRA/QLoRA: use llamafactory-cli export to merge adapter with base model
            # For Full finetuning: model copy handling is done in cmds below
            updated_export_str = yaml.dump(dict(self.export_dict), default_flow_style=False)
            self.temp_export_yaml_file = self._build_temp_yaml(updated_export_str, prefix='merge_')

        updated_template_str = yaml.dump(dict(self.template_dict), default_flow_style=False)
        self.temp_yaml_file = self._build_temp_yaml(updated_template_str)

        formatted_date = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        random_value = random.randint(1000, 9999)
        self.log_file_path = f'{self.target_path}/train_log_{formatted_date}_{random_value}.log'

        # Use bash instead of sh to support pipefail
        # This ensures export only runs if training succeeds
        cmds = (f'export DISABLE_VERSION_CHECK=1 && bash -c "set -o pipefail && '
                f'llamafactory-cli train {self.temp_yaml_file} 2>&1 | '
                f'tee {self.log_file_path}"')
        if self.temp_export_yaml_file:
            # For LoRA/QLoRA: merge adapter with base model
            # Only run export if training succeeds (exit code 0)
            # With pipefail, tee will preserve the exit code from llamafactory-cli
            cmds += f' && llamafactory-cli export {self.temp_export_yaml_file}'
        elif self.template_dict['finetuning_type'] == 'full':
            # For Full finetuning: copy model from lazyllm_lora to lazyllm_merge
            # This maintains consistency with LoRA/QLoRA workflow
            # Only copy if training succeeds (exit code 0)
            # Only copy model files, exclude training process information (checkpoints, logs, etc.)
            # to save storage space since lazyllm_merge is only used for exporting to models directory
            exclude_patterns = [
                '--exclude=checkpoint-*',  # Exclude checkpoint directories
                '--exclude=train_log_*.log',  # Exclude training logs
                '--exclude=trainer_state.json',  # Exclude trainer state
                '--exclude=trainer_log.jsonl',  # Exclude trainer log
                '--exclude=train_results.json',  # Exclude training results
                '--exclude=all_results.json',  # Exclude all results
                '--exclude=eval_results.json',  # Exclude evaluation results
                '--exclude=training_loss.png',  # Exclude training loss plot
                '--exclude=runs/',  # Exclude tensorboard logs
                '--exclude=training_args.bin',  # Exclude training arguments
            ]
            exclude_str = ' '.join(exclude_patterns)
            cmds += f' && rsync -a {exclude_str} {self.target_path}/ {self.merge_path}/ 2>/dev/null || true'
        return cmds

cmd(trainset, valset=None)

生成LLaMA-Factory微调命令序列,包括训练和模型合并命令。

Parameters:

  • trainset (str) –

    训练数据集路径(支持相对lazyllm.config['data_path']的路径)

  • valset (str, default: None ) –

    验证数据集路径(当前实现中未直接使用)

Returns:

  • str: 完整的shell命令字符串,包含:
    • 训练命令(自动配置参数)
    • 日志重定向(保存到目标路径)
    • 可选的模型合并命令(当配置LoRA时)
注意事项
  • 自动生成带时间戳的训练日志文件
  • 临时文件会在使用后自动清理
  • 支持多种数据格式(alpaca/sharegpt等)
  • 多模态数据(图像/视频/音频)会自动检测处理
Source code in lazyllm/components/finetune/llamafactory.py
    def cmd(self, trainset, valset=None) -> str:
        """生成LLaMA-Factory微调命令序列,包括训练和模型合并命令。

Args:
    trainset (str): 训练数据集路径(支持相对lazyllm.config['data_path']的路径)
    valset (str, optional): 验证数据集路径(当前实现中未直接使用)

**Returns:**

- str: 完整的shell命令字符串,包含:
    - 训练命令(自动配置参数)
    - 日志重定向(保存到目标路径)
    - 可选的模型合并命令(当配置LoRA时)

注意事项:
    - 自动生成带时间戳的训练日志文件
    - 临时文件会在使用后自动清理
    - 支持多种数据格式(alpaca/sharegpt等)
    - 多模态数据(图像/视频/音频)会自动检测处理
"""
        thirdparty.check_packages(['datasets', 'deepspeed', 'numpy', 'peft', 'torch', 'transformers', 'trl'])
        if 'dataset_dir' in self.template_dict and self.template_dict['dataset_dir'] == 'lazyllm_temp_dir':
            stage = self.template_dict.get('stage', 'sft')
            _, datasets = self._build_temp_dataset_info(trainset, stage=stage)
            self.template_dict['dataset_dir'] = self.temp_folder
        else:
            datasets = trainset
        self.template_dict['dataset'] = datasets

        if self.template_dict['finetuning_type'] == 'lora':
            # For LoRA/QLoRA: use llamafactory-cli export to merge adapter with base model
            # For Full finetuning: model copy handling is done in cmds below
            updated_export_str = yaml.dump(dict(self.export_dict), default_flow_style=False)
            self.temp_export_yaml_file = self._build_temp_yaml(updated_export_str, prefix='merge_')

        updated_template_str = yaml.dump(dict(self.template_dict), default_flow_style=False)
        self.temp_yaml_file = self._build_temp_yaml(updated_template_str)

        formatted_date = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        random_value = random.randint(1000, 9999)
        self.log_file_path = f'{self.target_path}/train_log_{formatted_date}_{random_value}.log'

        # Use bash instead of sh to support pipefail
        # This ensures export only runs if training succeeds
        cmds = (f'export DISABLE_VERSION_CHECK=1 && bash -c "set -o pipefail && '
                f'llamafactory-cli train {self.temp_yaml_file} 2>&1 | '
                f'tee {self.log_file_path}"')
        if self.temp_export_yaml_file:
            # For LoRA/QLoRA: merge adapter with base model
            # Only run export if training succeeds (exit code 0)
            # With pipefail, tee will preserve the exit code from llamafactory-cli
            cmds += f' && llamafactory-cli export {self.temp_export_yaml_file}'
        elif self.template_dict['finetuning_type'] == 'full':
            # For Full finetuning: copy model from lazyllm_lora to lazyllm_merge
            # This maintains consistency with LoRA/QLoRA workflow
            # Only copy if training succeeds (exit code 0)
            # Only copy model files, exclude training process information (checkpoints, logs, etc.)
            # to save storage space since lazyllm_merge is only used for exporting to models directory
            exclude_patterns = [
                '--exclude=checkpoint-*',  # Exclude checkpoint directories
                '--exclude=train_log_*.log',  # Exclude training logs
                '--exclude=trainer_state.json',  # Exclude trainer state
                '--exclude=trainer_log.jsonl',  # Exclude trainer log
                '--exclude=train_results.json',  # Exclude training results
                '--exclude=all_results.json',  # Exclude all results
                '--exclude=eval_results.json',  # Exclude evaluation results
                '--exclude=training_loss.png',  # Exclude training loss plot
                '--exclude=runs/',  # Exclude tensorboard logs
                '--exclude=training_args.bin',  # Exclude training arguments
            ]
            exclude_str = ' '.join(exclude_patterns)
            cmds += f' && rsync -a {exclude_str} {self.target_path}/ {self.merge_path}/ 2>/dev/null || true'
        return cmds

lazyllm.components.deploy.LazyLLMDeployBase

Bases: ComponentBase

此类是 ComponentBase 的一个子类,提供了LazyLLM部署的基础功能。它支持多种媒体类型的编码转换,并提供了结果提取和流式处理的配置选项。

Parameters:

  • launcher (LauncherBase, default: remote() ) –

    用于部署的启动器实例,默认为远程启动器(launchers.remote())。

注意事项
  • 继承此类时需要实现具体的部署逻辑
  • 可以通过重写extract_result方法来自定义结果提取逻辑

Examples:

>>> import lazyllm
>>> from lazyllm.components.deploy.base import LazyLLMDeployBase
>>> class MyDeployer(LazyLLMDeployBase):
...     def __call__(self, inputs):
...         return processed_result
        def extract_result(output, inputs):
...         return output.json()['result']
>>> deployer = MyDeployer()
>>> result = deployer.extract_result(raw_output, input_data)
Source code in lazyllm/components/deploy/base.py
class LazyLLMDeployBase(ComponentBase):
    """此类是 ``ComponentBase`` 的一个子类,提供了LazyLLM部署的基础功能。它支持多种媒体类型的编码转换,并提供了结果提取和流式处理的配置选项。

Args:
    launcher (LauncherBase): 用于部署的启动器实例,默认为远程启动器(``launchers.remote()``)。

注意事项: 
    - 继承此类时需要实现具体的部署逻辑
    - 可以通过重写extract_result方法来自定义结果提取逻辑


Examples:
    >>> import lazyllm
    >>> from lazyllm.components.deploy.base import LazyLLMDeployBase
    >>> class MyDeployer(LazyLLMDeployBase):
    ...     def __call__(self, inputs):
    ...         return processed_result
            def extract_result(output, inputs):
    ...         return output.json()['result']
    >>> deployer = MyDeployer()
    >>> result = deployer.extract_result(raw_output, input_data)
    """
    keys_name_handle = None
    message_format = None
    default_headers = {'Content-Type': 'application/json'}
    stream_url_suffix = ''
    stream_parse_parameters = {}

    encoder_map = dict(image=_image_to_base64, audio=_audio_to_base64, ocr_files=ocr_to_base64)

    @staticmethod
    def extract_result(output, inputs):
        """从模型输出中提取最终结果,默认实现直接返回原始输出,子类可重写此方法实现自定义结果提取逻辑。

Args:
    output: 模型原始输出
    inputs: 原始输入数据,可用于结果后处理

**Returns:**

- 处理后的最终结果
"""
        return output

    def __init__(self, *, launcher=launchers.remote()):  # noqa B008
        super().__init__(launcher=launcher)

extract_result(output, inputs) staticmethod

从模型输出中提取最终结果,默认实现直接返回原始输出,子类可重写此方法实现自定义结果提取逻辑。

Parameters:

  • output

    模型原始输出

  • inputs

    原始输入数据,可用于结果后处理

Returns:

  • 处理后的最终结果
Source code in lazyllm/components/deploy/base.py
    @staticmethod
    def extract_result(output, inputs):
        """从模型输出中提取最终结果,默认实现直接返回原始输出,子类可重写此方法实现自定义结果提取逻辑。

Args:
    output: 模型原始输出
    inputs: 原始输入数据,可用于结果后处理

**Returns:**

- 处理后的最终结果
"""
        return output

lazyllm.components.deploy.LazyLLMDeployBase.extract_result(output, inputs) staticmethod

从模型输出中提取最终结果,默认实现直接返回原始输出,子类可重写此方法实现自定义结果提取逻辑。

Parameters:

  • output

    模型原始输出

  • inputs

    原始输入数据,可用于结果后处理

Returns:

  • 处理后的最终结果
Source code in lazyllm/components/deploy/base.py
    @staticmethod
    def extract_result(output, inputs):
        """从模型输出中提取最终结果,默认实现直接返回原始输出,子类可重写此方法实现自定义结果提取逻辑。

Args:
    output: 模型原始输出
    inputs: 原始输入数据,可用于结果后处理

**Returns:**

- 处理后的最终结果
"""
        return output

lazyllm.components.finetune.FlagembeddingFinetune

Bases: LazyLLMFinetuneBase

该类是 LazyLLMFinetuneBase 的子类,基于 FlagEmbedding 框架提供的训练能力,用于训练嵌入和重排模型。

Parameters:

  • base_model (str) –

    用于训练的基础模型。必须是基础模型的路径。

  • target_path (str) –

    训练后模型权重保存的路径。

  • launcher (launcher, default: remote(ngpus=1, sync=True) ) –

    微调的启动器,默认为 launchers.remote(ngpus=1, sync=True)

  • kw

    用于更新默认训练参数的关键字参数。

该类嵌入模型的关键字参数及其默认值如下:

Other Parameters:

  • train_group_size (int) –

    默认为:8。训练组的大小。用于控制每个训练集中的负样本数量。

  • query_max_len (int) –

    默认为:512。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。

  • passage_max_len (int) –

    默认为:512。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。

  • pad_to_multiple_of (int) –

    默认为:8。如果设置,将序列填充为提供值的倍数。

  • query_instruction_for_retrieval (str) –

    默认为:Represent this sentence for searching relevant passages:。查询query的指令。

  • query_instruction_format (str) –

    默认为:{}{}。查询指令格式。

  • learning_rate (float) –

    默认为:1e-5。学习率。

  • num_train_epochs (int) –

    默认为:1。要执行的总训练周期数。

  • per_device_train_batch_size (int) –

    默认为:2。训练批量大小。

  • gradient_accumulation_steps (int) –

    默认为:1。在执行反向/更新传递之前要累积的更新步骤数。

  • dataloader_drop_last (bool) –

    默认为:True。如果数据集大小不能被批量大小整除,则丢弃最后一个不完整的批量,即 DataLoader 只返回完整的批量。

  • warmup_ratio (float) –

    默认为:0.1。线性调度器的预热比率。

  • weight_decay (float) –

    默认为:0.01。AdamW 中的权重衰减。

  • deepspeed (str) –

    默认为:``。DeepSpeed 配置文件的路径,默认使用 LazyLLM 代码仓库中的预置文件:ds_stage0.json``。

  • logging_steps (int) –

    默认为:1。更新日志的频率。

  • save_steps (int) –

    默认为:1000。保存频率。

  • temperature (float) –

    默认为:0.02。用于相似度评分的温度。

  • sentence_pooling_method (str) –

    默认为:cls。池化方法。可用选项:'cls', 'mean', 'last_token'。

  • normalize_embeddings (bool) –

    默认为:True。是否归一化嵌入。

  • kd_loss_type (str) –

    默认为:kl_div。知识蒸馏的损失类型。可用选项:'kl_div', 'm3_kd_loss'。

  • overwrite_output_dir (bool) –

    默认为:True。用于允许程序覆盖现有的输出目录。

  • fp16 (bool) –

    默认为:True。是否使用 fp16(混合)精度而不是 32 位。

  • gradient_checkpointing (bool) –

    默认为:True。是否启用梯度检查点。

  • negatives_cross_device (bool) –

    默认为:True。是否在设备间共享负样本。

该类重排模型的关键字参数及其默认值如下:

Other Parameters:

  • train_group_size (int) –

    默认为:8。训练组的大小。用于控制每个训练集中的负样本数量。

  • query_max_len (int) –

    默认为:256。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。

  • passage_max_len (int) –

    默认为:256。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。

  • pad_to_multiple_of (int) –

    默认为:8。如果设置,将序列填充为提供值的倍数。

  • learning_rate (float) –

    默认为:6e-5。学习率。

  • num_train_epochs (int) –

    默认为:1。要执行的总训练周期数。

  • per_device_train_batch_size (int) –

    默认为:2。训练批量大小。

  • gradient_accumulation_steps (int) –

    默认为:1。在执行反向/更新传递之前要累积的更新步骤数。

  • dataloader_drop_last (bool) –

    默认为:True。如果数据集大小不能被批量大小整除,则丢弃最后一个不完整的批量,即 DataLoader 只返回完整的批量。

  • warmup_ratio (float) –

    默认为:0.1。线性调度器的预热比率。

  • weight_decay (float) –

    默认为:0.01。AdamW 中的权重衰减。

  • deepspeed (str) –

    默认为:``。DeepSpeed 配置文件的路径,默认使用 LazyLLM 代码仓库中的预置文件:ds_stage0.json``。

  • logging_steps (int) –

    默认为:1。更新日志的频率。

  • save_steps (int) –

    默认为:1000。保存频率。

  • overwrite_output_dir (bool) –

    默认为:True。用于允许程序覆盖现有的输出目录。

  • fp16 (bool) –

    默认为:True。是否使用 fp16(混合)精度而不是 32 位。

  • gradient_checkpointing (bool) –

    默认为:True。是否启用梯度检查点。

Examples:

>>> from lazyllm import finetune
>>> finetune.FlagembeddingFinetune('bge-m3', 'path/to/target')
<lazyllm.llm.finetune type=FlagembeddingFinetune>
Source code in lazyllm/components/finetune/flagembedding.py
class FlagembeddingFinetune(LazyLLMFinetuneBase):
    """该类是 ``LazyLLMFinetuneBase`` 的子类,基于 [FlagEmbedding](https://github.com/FlagOpen/FlagEmbedding) 框架提供的训练能力,用于训练嵌入和重排模型。

Args:
    base_model (str): 用于训练的基础模型。必须是基础模型的路径。
    target_path (str): 训练后模型权重保存的路径。
    launcher (lazyllm.launcher): 微调的启动器,默认为 ``launchers.remote(ngpus=1, sync=True)``。
    kw: 用于更新默认训练参数的关键字参数。

该类嵌入模型的关键字参数及其默认值如下:

Keyword Args:
    train_group_size (int): 默认为:``8``。训练组的大小。用于控制每个训练集中的负样本数量。
    query_max_len (int): 默认为:``512``。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。
    passage_max_len (int): 默认为:``512``。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。
    pad_to_multiple_of (int): 默认为:``8``。如果设置,将序列填充为提供值的倍数。
    query_instruction_for_retrieval (str): 默认为:``Represent this sentence for searching relevant passages: ``。查询query的指令。
    query_instruction_format (str): 默认为:``{}{}``。查询指令格式。
    learning_rate (float): 默认为:``1e-5``。学习率。
    num_train_epochs (int): 默认为:``1``。要执行的总训练周期数。
    per_device_train_batch_size (int): 默认为:``2``。训练批量大小。
    gradient_accumulation_steps (int): 默认为:``1``。在执行反向/更新传递之前要累积的更新步骤数。
    dataloader_drop_last (bool): 默认为:``True``。如果数据集大小不能被批量大小整除,则丢弃最后一个不完整的批量,即 DataLoader 只返回完整的批量。
    warmup_ratio (float): 默认为:``0.1``。线性调度器的预热比率。
    weight_decay (float): 默认为:``0.01``。AdamW 中的权重衰减。
    deepspeed (str): 默认为:````。DeepSpeed 配置文件的路径,默认使用 LazyLLM 代码仓库中的预置文件:``ds_stage0.json``。
    logging_steps (int): 默认为:``1``。更新日志的频率。
    save_steps (int): 默认为:``1000``。保存频率。
    temperature (float): 默认为:``0.02``。用于相似度评分的温度。
    sentence_pooling_method (str): 默认为:``cls``。池化方法。可用选项:'cls', 'mean', 'last_token'。
    normalize_embeddings (bool): 默认为:``True``。是否归一化嵌入。
    kd_loss_type (str): 默认为:``kl_div``。知识蒸馏的损失类型。可用选项:'kl_div', 'm3_kd_loss'。
    overwrite_output_dir (bool): 默认为:``True``。用于允许程序覆盖现有的输出目录。
    fp16 (bool): 默认为:``True``。是否使用 fp16(混合)精度而不是 32 位。
    gradient_checkpointing (bool): 默认为:``True``。是否启用梯度检查点。
    negatives_cross_device (bool): 默认为:``True``。是否在设备间共享负样本。

该类重排模型的关键字参数及其默认值如下:

Keyword Args:
    train_group_size (int): 默认为:``8``。训练组的大小。用于控制每个训练集中的负样本数量。
    query_max_len (int): 默认为:``256``。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。
    passage_max_len (int): 默认为:``256``。经过分词后,段落的最大总输入序列长度。超过此长度的序列将被截断,较短的序列将被填充。
    pad_to_multiple_of (int): 默认为:``8``。如果设置,将序列填充为提供值的倍数。
    learning_rate (float): 默认为:``6e-5``。学习率。
    num_train_epochs (int): 默认为:``1``。要执行的总训练周期数。
    per_device_train_batch_size (int): 默认为:``2``。训练批量大小。
    gradient_accumulation_steps (int): 默认为:``1``。在执行反向/更新传递之前要累积的更新步骤数。
    dataloader_drop_last (bool): 默认为:``True``。如果数据集大小不能被批量大小整除,则丢弃最后一个不完整的批量,即 DataLoader 只返回完整的批量。
    warmup_ratio (float): 默认为:``0.1``。线性调度器的预热比率。
    weight_decay (float): 默认为:``0.01``。AdamW 中的权重衰减。
    deepspeed (str): 默认为:````。DeepSpeed 配置文件的路径,默认使用 LazyLLM 代码仓库中的预置文件:``ds_stage0.json``。
    logging_steps (int): 默认为:``1``。更新日志的频率。
    save_steps (int): 默认为:``1000``。保存频率。
    overwrite_output_dir (bool): 默认为:``True``。用于允许程序覆盖现有的输出目录。
    fp16 (bool): 默认为:``True``。是否使用 fp16(混合)精度而不是 32 位。
    gradient_checkpointing (bool): 默认为:``True``。是否启用梯度检查点。


Examples:
    >>> from lazyllm import finetune
    >>> finetune.FlagembeddingFinetune('bge-m3', 'path/to/target')
    <lazyllm.llm.finetune type=FlagembeddingFinetune>
    """
    defatult_embed_kw = ArgsDict({
        'train_group_size': 8,
        'query_max_len': 512,
        'passage_max_len': 512,
        'pad_to_multiple_of': 8,
        'query_instruction_for_retrieval': 'Represent this sentence for searching relevant passages: ',
        'query_instruction_format': '{}{}',
        'learning_rate': 1e-5,
        'num_train_epochs': 1,
        'per_device_train_batch_size': 2,
        'gradient_accumulation_steps': 1,
        'dataloader_drop_last': True,
        'warmup_ratio': 0.1,
        'weight_decay': 0.01,
        'deepspeed': '',
        'logging_steps': 1,
        'save_steps': 1000,
        'temperature': 0.02,
        'sentence_pooling_method': 'cls',
        'normalize_embeddings': True,
        'kd_loss_type': 'kl_div',
        'overwrite_output_dir': True,
        'fp16': True,
        'gradient_checkpointing': True,
        'negatives_cross_device': True
    })
    defatult_rerank_kw = ArgsDict({
        'train_group_size': 8,
        'query_max_len': 256,
        'passage_max_len': 256,
        'pad_to_multiple_of': 8,
        'learning_rate': 6e-5,
        'num_train_epochs': 1,
        'per_device_train_batch_size': 2,
        'gradient_accumulation_steps': 1,
        'dataloader_drop_last': True,
        'warmup_ratio': 0.1,
        'weight_decay': 0.01,
        'deepspeed': '',
        'logging_steps': 1,
        'save_steps': 1000,
        'overwrite_output_dir': True,
        'fp16': True,
        'gradient_checkpointing': True
    })
    store_true_embed_kw = {'overwrite_output_dir', 'fp16', 'gradient_checkpointing', 'negatives_cross_device'}
    store_true_rerank_kw = {'overwrite_output_dir', 'fp16', 'gradient_checkpointing'}

    def __init__(
        self,
        base_model,
        target_path,
        launcher=launchers.remote(ngpus=1, sync=True),  # noqa B008
        **kw
    ):
        model_type = ModelManager.get_model_type(base_model.split('/')[-1])
        if model_type not in ('embed', 'rerank'):
            raise RuntimeError(f'Not supported {model_type} type to finetune.')
        if not os.path.exists(base_model):
            defatult_path = os.path.join(lazyllm.config['model_path'], base_model)
            if os.path.exists(defatult_path):
                base_model = defatult_path
        save_path = os.path.join(lazyllm.config['train_target_root'], target_path)
        target_path = os.path.join(save_path, model_type)
        os.system(f'mkdir -p {target_path}')
        super().__init__(
            base_model,
            target_path,
            launcher=launcher,
        )
        if model_type == 'rerank':
            self.kw = copy.deepcopy(self.defatult_rerank_kw)
            self.store_true_kw = copy.deepcopy(self.store_true_rerank_kw)
            self.module_run_path = 'FlagEmbedding.finetune.reranker.encoder_only.base'
        else:
            self.kw = copy.deepcopy(self.defatult_embed_kw)
            self.store_true_kw = copy.deepcopy(self.store_true_embed_kw)
            self.module_run_path = 'FlagEmbedding.finetune.embedder.encoder_only.base'
        self.kw.check_and_update(kw)
        if not self.kw['deepspeed']:
            folder_path = os.path.dirname(os.path.abspath(__file__))
            deepspeed_config_path = os.path.join(folder_path, 'flag_embedding', 'ds_stage0.json')
            self.kw['deepspeed'] = deepspeed_config_path
        self.nproc_per_node = launcher.ngpus

    def cmd(self, trainset, valset=None) -> str:
        thirdparty.check_packages(['flagembedding'])
        self.kw['train_data'] = trainset

        formatted_date = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        self.log_file_path = f'{self.target_path}/train_log_{formatted_date}_{random.randint(1000, 9999)}.log'
        cache_path = os.path.join(os.path.expanduser(lazyllm.config['home']), 'fintune', 'embeding')
        cache_model_path = os.path.join(cache_path, 'model')
        cache_data_path = os.path.join(cache_path, 'data')
        os.system(f'mkdir -p {cache_model_path} {cache_data_path}')

        cmds = (f'export WANDB_MODE=disabled && torchrun --nproc_per_node {self.nproc_per_node} '
                f'-m {self.module_run_path} '
                f'--model_name_or_path {self.base_model} '
                f'--output_dir {self.target_path} '
                f'--cache_dir {cache_model_path} '
                f'--cache_path {cache_data_path} '
            )
        for key in self.store_true_kw:
            cmds += f'--{key} ' if self.kw.pop(key) else ''
        cmds += self.kw.parse_kwargs()
        cmds += f' 2>&1 | tee {self.log_file_path}'
        return cmds

lazyllm.components.auto.AutoFinetune

Bases: LazyLLMFinetuneBase

此类是 LazyLLMFinetuneBase 的子类,可根据输入的参数自动选择合适的微调框架和参数,以对大语言模型进行微调。

具体而言,基于输入的:base_model 的模型参数、ctx_lenbatch_sizelora_rlauncher 中GPU的类型以及卡数,该类可以自动选择出合适的微调框架(如: AlpacaloraFinetuneCollieFinetune)及所需的参数。

Parameters:

  • base_model (str) –

    用于进行微调的基模型。要求是基模型的路径。

  • source (config[model_source]) –

    指定模型的下载源。可通过设置环境变量 LAZYLLM_MODEL_SOURCE 来配置,目前仅支持 huggingfacemodelscope 。若不设置,lazyllm不会启动自动模型下载。

  • target_path (str) –

    微调后模型保存LoRA权重的路径。

  • merge_path (str) –

    模型合并LoRA权重后的路径,默认为 None。如果未指定,则会在 target_path 下创建 "lazyllm_lora" 和 "lazyllm_merge" 目录,分别作为 target_pathmerge_path

  • ctx_len (int) –

    输入微调模型的token最大长度,默认为 1024

  • batch_size (int) –

    批处理大小,默认为 32

  • lora_r (int) –

    LoRA 的秩,默认为 8;该数值决定添加参数的量,数值越小参数量越小。

  • launcher (launcher, default: remote() ) –

    微调的启动器,默认为 launchers.remote(ngpus=1)

  • kw

    关键字参数,用于更新默认的训练参数。注意这里能够指定的关键字参数取决于 LazyLLM 推测出的框架,因此建议谨慎设置。

Examples:

>>> from lazyllm import finetune
>>> finetune.auto("internlm2-chat-7b", 'path/to/target')
<lazyllm.llm.finetune type=AlpacaloraFinetune>
Source code in lazyllm/components/auto/autofinetune.py
class AutoFinetune(LazyLLMFinetuneBase):
    """此类是 ``LazyLLMFinetuneBase`` 的子类,可根据输入的参数自动选择合适的微调框架和参数,以对大语言模型进行微调。

具体而言,基于输入的:``base_model`` 的模型参数、``ctx_len``、``batch_size``、``lora_r``、``launcher`` 中GPU的类型以及卡数,该类可以自动选择出合适的微调框架(如: ``AlpacaloraFinetune`` 或 ``CollieFinetune``)及所需的参数。

Args:
    base_model (str): 用于进行微调的基模型。要求是基模型的路径。
    source (lazyllm.config['model_source']): 指定模型的下载源。可通过设置环境变量 ``LAZYLLM_MODEL_SOURCE`` 来配置,目前仅支持 ``huggingface`` 或 ``modelscope`` 。若不设置,lazyllm不会启动自动模型下载。
    target_path (str): 微调后模型保存LoRA权重的路径。
    merge_path (str): 模型合并LoRA权重后的路径,默认为 ``None``。如果未指定,则会在 ``target_path`` 下创建 "lazyllm_lora" 和 "lazyllm_merge" 目录,分别作为 ``target_path`` 和  ``merge_path`` 。
    ctx_len (int): 输入微调模型的token最大长度,默认为 ``1024``。
    batch_size (int): 批处理大小,默认为 ``32``。
    lora_r (int): LoRA 的秩,默认为 ``8``;该数值决定添加参数的量,数值越小参数量越小。
    launcher (lazyllm.launcher): 微调的启动器,默认为 ``launchers.remote(ngpus=1)``。
    kw: 关键字参数,用于更新默认的训练参数。注意这里能够指定的关键字参数取决于 LazyLLM 推测出的框架,因此建议谨慎设置。


Examples:
    >>> from lazyllm import finetune
    >>> finetune.auto("internlm2-chat-7b", 'path/to/target')
    <lazyllm.llm.finetune type=AlpacaloraFinetune>
    """

    def __new__(cls, base_model: str, target_path: str, source: Optional[str] = None,
                merge_path: Optional[str] = None, batch_size: int = 32, lora_r: int = 8,
                model_type: Optional[str] = None, launcher: Optional[LazyLLMLaunchersBase] = None, **kw):
        base_model = ModelManager(source).download(base_model) or ''
        LOG.info(f'[AutoFinetune] Using base model from: {base_model}')
        model_name = get_model_name(base_model)
        if not model_type:
            model_type = ModelManager.get_model_type(model_name)
            LOG.info(f'[AutoFinetune] Infer type of model {model_name} is {model_type}')
        if model_type in ['tts', 'stt', 'sd', 'ocr', 'cross_modal_embed']:
            raise RuntimeError(f'Fine-tuning of the {model_type} model is not currently supported.')

        if model_type in ['embed', 'rerank']:
            LOG.info(f'[AutoFinetune] Finetune {model_name} with FlagEmbedding.')
            return finetune.flagembedding(base_model, target_path, **kw)

        params = {'gradient_step': 1, 'micro_batch_size': 32}
        if not launcher:
            match = re.search(r'(\d+)[bB]', model_name)
            model_size = int(match.group(1)) if match else 0
            gs, mbs, ngpus = estimate_finetune_plan(
                gpu_mem_gb=config['gpu_memory'], model_size_b=model_size, batch_size=batch_size, lora_r=lora_r)
            params.update({'gradient_step': gs, 'micro_batch_size': mbs})
            LOG.info(f'[AutoFinetune] Infer model_size: {model_size} B, '
                     f'gradient_step: {gs}, micro_batch_size: {mbs}, ngpus: {ngpus}')
            launcher = launchers.remote(ngpus=ngpus, sync=True)

        candidates = ['llamafactory', 'alpacalora']
        candidates = dict(llm=['llamafactory', 'alpacalora'], vlm=['llamafactory'])
        for finetune_cls_name in candidates[model_type]:
            if check_requirements(requirements[finetune_cls_name]):
                finetune_cls = getattr(finetune, finetune_cls_name)
                for key, value in finetune_cls.auto_map.items():
                    if value and value not in kw:
                        kw[value] = params[key]
                LOG.info(f'[AutoFinetune] Use {finetune_cls_name} to finetune.')
                if finetune_cls_name == 'llamafactory':
                    return finetune_cls(base_model, target_path, lora_r=lora_r, launcher=launcher, **kw)
                return finetune_cls(base_model, target_path, merge_path, cp_files='tokeniz*',
                                    batch_size=batch_size, lora_r=lora_r, launcher=launcher, **kw)
        raise RuntimeError('No valid framework found, candidates are '
                           f'{[c.framework.lower() for c in candidates[model_type]]}.')

lazyllm.components.finetune.base.DummyFinetune

Bases: LazyLLMFinetuneBase

DummyFinetune 是 LazyLLMFinetuneBase 的子类,用于占位实现微调逻辑。 此类主要用于演示或测试目的,因为它不执行任何实际的微调操作。

Parameters:

  • base_model

    字符串,指定基础模型的名称,默认为 'base'。

  • target_path

    字符串,指定微调输出的目标路径,默认为 'target'。

  • launcher

    启动器实例,用于执行命令。默认为 [launchers.remote()][lazyllm.launchers.remote]。

  • **kw

    其他关键字参数,这些参数会被保存以供后续使用。

Returns:

  • 一个字符串,表示一个占位命令。该字符串包括初始化时传递的参数。

Examples:

>>> from lazyllm.components import DummyFinetune
>>> from lazyllm import launchers
>>> # 创建一个 DummyFinetune 实例
>>> finetuner = DummyFinetune(base_model='example-base', target_path='example-target', launcher=launchers.local(), custom_arg='custom_value')
>>> # 调用 cmd 方法生成占位命令
>>> command = finetuner.cmd('--example-arg', key='value')
>>> print(command)
... echo 'dummy finetune!, and init-args is {'custom_arg': 'custom_value'}'
Source code in lazyllm/components/finetune/base.py
class DummyFinetune(LazyLLMFinetuneBase):
    """DummyFinetune 是 [LazyLLMFinetuneBase][lazyllm.components.LazyLLMFinetuneBase] 的子类,用于占位实现微调逻辑。
此类主要用于演示或测试目的,因为它不执行任何实际的微调操作。

Args:
    base_model: 字符串,指定基础模型的名称,默认为 'base'。
    target_path: 字符串,指定微调输出的目标路径,默认为 'target'。
    launcher: 启动器实例,用于执行命令。默认为 [launchers.remote()][lazyllm.launchers.remote]。
    **kw: 其他关键字参数,这些参数会被保存以供后续使用。

Returns:
    一个字符串,表示一个占位命令。该字符串包括初始化时传递的参数。


Examples:
    >>> from lazyllm.components import DummyFinetune
    >>> from lazyllm import launchers
    >>> # 创建一个 DummyFinetune 实例
    >>> finetuner = DummyFinetune(base_model='example-base', target_path='example-target', launcher=launchers.local(), custom_arg='custom_value')
    >>> # 调用 cmd 方法生成占位命令
    >>> command = finetuner.cmd('--example-arg', key='value')
    >>> print(command)
    ... echo 'dummy finetune!, and init-args is {'custom_arg': 'custom_value'}'
    """
    def __init__(self, base_model='base', target_path='target', *, launcher=launchers.remote(), **kw):  # noqa B008
        super().__init__(base_model, target_path, launcher=launchers.empty)
        self.kw = kw

    def cmd(self, *args, **kw) -> str:
        """`cmd` 方法生成一个用于微调的占位命令字符串。此方法主要用于测试或演示目的。

Args:
    *args: 要包含在命令中的位置参数(在本实现中未使用)。
    **kw: 要包含在命令中的关键字参数(在本实现中未使用)。

Returns:
    一个字符串,表示一个占位命令。该字符串包括初始化时传递的关键字参数 (`**kw`),存储在 `self.kw` 中。

Example:
    如果类初始化时使用 `custom_arg='value'`,调用 `cmd` 方法将返回:
    `"echo 'dummy finetune!, and init-args is {'custom_arg': 'value'}'"`


Examples:
    >>> from lazyllm.components import DummyFinetune
    >>> from lazyllm import launchers
    >>> # 创建一个 DummyFinetune 实例,并传递初始化参数
    >>> finetuner = DummyFinetune(base_model='example-base', target_path='example-target', launcher=launchers.local(), custom_arg='value')
    >>> # 调用 cmd 方法生成占位命令
    >>> command = finetuner.cmd()
    >>> # 打印生成的占位命令
    >>> print(command)
    ... echo 'dummy finetune!, and init-args is {'custom_arg': 'value'}'
    """
        return f'echo \'dummy finetune!, and init-args is {self.kw}\''

cmd(*args, **kw)

cmd 方法生成一个用于微调的占位命令字符串。此方法主要用于测试或演示目的。

Parameters:

  • *args

    要包含在命令中的位置参数(在本实现中未使用)。

  • **kw

    要包含在命令中的关键字参数(在本实现中未使用)。

Returns:

  • str

    一个字符串,表示一个占位命令。该字符串包括初始化时传递的关键字参数 (**kw),存储在 self.kw 中。

Example

如果类初始化时使用 custom_arg='value',调用 cmd 方法将返回: "echo 'dummy finetune!, and init-args is {'custom_arg': 'value'}'"

Examples:

>>> from lazyllm.components import DummyFinetune
>>> from lazyllm import launchers
>>> # 创建一个 DummyFinetune 实例,并传递初始化参数
>>> finetuner = DummyFinetune(base_model='example-base', target_path='example-target', launcher=launchers.local(), custom_arg='value')
>>> # 调用 cmd 方法生成占位命令
>>> command = finetuner.cmd()
>>> # 打印生成的占位命令
>>> print(command)
... echo 'dummy finetune!, and init-args is {'custom_arg': 'value'}'
Source code in lazyllm/components/finetune/base.py
    def cmd(self, *args, **kw) -> str:
        """`cmd` 方法生成一个用于微调的占位命令字符串。此方法主要用于测试或演示目的。

Args:
    *args: 要包含在命令中的位置参数(在本实现中未使用)。
    **kw: 要包含在命令中的关键字参数(在本实现中未使用)。

Returns:
    一个字符串,表示一个占位命令。该字符串包括初始化时传递的关键字参数 (`**kw`),存储在 `self.kw` 中。

Example:
    如果类初始化时使用 `custom_arg='value'`,调用 `cmd` 方法将返回:
    `"echo 'dummy finetune!, and init-args is {'custom_arg': 'value'}'"`


Examples:
    >>> from lazyllm.components import DummyFinetune
    >>> from lazyllm import launchers
    >>> # 创建一个 DummyFinetune 实例,并传递初始化参数
    >>> finetuner = DummyFinetune(base_model='example-base', target_path='example-target', launcher=launchers.local(), custom_arg='value')
    >>> # 调用 cmd 方法生成占位命令
    >>> command = finetuner.cmd()
    >>> # 打印生成的占位命令
    >>> print(command)
    ... echo 'dummy finetune!, and init-args is {'custom_arg': 'value'}'
    """
        return f'echo \'dummy finetune!, and init-args is {self.kw}\''

lazyllm.components.finetune.LazyLLMFinetuneBase

Bases: ComponentBase

LazyLLM微调基础组件类,继承自ComponentBase。

提供大语言模型微调的基础功能,支持远程启动器配置和模型路径管理。

Parameters:

  • base_model (str) –

    基础模型路径或标识

  • target_path (str) –

    微调后模型输出路径

  • launcher (Launcher, default: remote() ) –

    任务启动器,默认为远程启动器

Source code in lazyllm/components/finetune/base.py
class LazyLLMFinetuneBase(ComponentBase):
    """LazyLLM微调基础组件类,继承自ComponentBase。

提供大语言模型微调的基础功能,支持远程启动器配置和模型路径管理。

Args:
    base_model (str): 基础模型路径或标识
    target_path (str): 微调后模型输出路径
    launcher (Launcher, optional): 任务启动器,默认为远程启动器
"""
    __reg_overwrite__ = 'cmd'

    def __init__(self, base_model, target_path, *, launcher=launchers.remote()):  # noqa B008
        super().__init__(launcher=launcher)
        self.base_model = base_model
        self.target_path = target_path
        self.merge_path = None

    def __call__(self, *args, **kw):
        super().__call__(*args, **kw)
        if self.merge_path:
            return self.merge_path
        else:
            return self.target_path

Deploy

lazyllm.components.deploy.Lightllm

Bases: LazyLLMDeployBase

此类是 LazyLLMDeployBase 的子类,基于 LightLLM 框架提供的推理能力,用于对大语言模型进行推理。

Parameters:

  • trust_remote_code (bool, default: True ) –

    是否信任远程代码,默认为True

  • launcher (Launcher, default: remote(ngpus=1) ) –

    任务启动器,默认为单GPU远程启动器

  • log_path (str, default: None ) –

    日志文件路径,默认为None

  • **kw

    其他LightLLM服务器配置参数

此类的关键字参数及其默认值如下:

Other Parameters:

  • tp (int) –

    张量并行参数,默认为 1

  • max_total_token_num (int) –

    最大总token数,默认为 64000

  • eos_id (int) –

    结束符ID,默认为 2

  • port (int) –

    服务的端口号,默认为 None。此情况下LazyLLM会自动生成随机端口号。

  • host (str) –

    服务的IP地址,默认为 0.0.0.0

  • nccl_port (int) –

    NCCL 端口,默认为 None。此情况下LazyLLM会自动生成随机端口号。

  • tokenizer_mode (str) –

    tokenizer的加载模式,默认为 auto

  • running_max_req_size (int) –

    推理引擎最大的并行请求数, 默认为 256

  • data_type (str) –

    模型权重的数据类型,默认为 float16

  • max_req_total_len (int) –

    请求的最大总长度,默认为 64000

  • max_req_input_len (int) –

    输入的最大长度,默认为 4096

  • long_truncation_mode (str) –

    长文本的截断模式,默认为 head

Examples:

>>> from lazyllm import deploy
>>> infer = deploy.lightllm()
Source code in lazyllm/components/deploy/lightllm.py
class Lightllm(LazyLLMDeployBase):
    """此类是 ``LazyLLMDeployBase`` 的子类,基于 [LightLLM](https://github.com/ModelTC/lightllm) 框架提供的推理能力,用于对大语言模型进行推理。

Args:
    trust_remote_code (bool, optional): 是否信任远程代码,默认为True
    launcher (Launcher, optional): 任务启动器,默认为单GPU远程启动器
    log_path (str, optional): 日志文件路径,默认为None
    **kw: 其他LightLLM服务器配置参数
此类的关键字参数及其默认值如下:

Keyword Args: 
    tp (int): 张量并行参数,默认为 ``1``。
    max_total_token_num (int): 最大总token数,默认为 ``64000``。
    eos_id (int): 结束符ID,默认为 ``2``。
    port (int): 服务的端口号,默认为 ``None``。此情况下LazyLLM会自动生成随机端口号。
    host (str): 服务的IP地址,默认为 ``0.0.0.0``。
    nccl_port (int): NCCL 端口,默认为 ``None``。此情况下LazyLLM会自动生成随机端口号。
    tokenizer_mode (str): tokenizer的加载模式,默认为 ``auto``。
    running_max_req_size (int): 推理引擎最大的并行请求数, 默认为 ``256``。
    data_type (str): 模型权重的数据类型,默认为 ``float16``。
    max_req_total_len (int): 请求的最大总长度,默认为 ``64000``。
    max_req_input_len (int): 输入的最大长度,默认为 ``4096``。
    long_truncation_mode (str): 长文本的截断模式,默认为 ``head``。


Examples:
    >>> from lazyllm import deploy
    >>> infer = deploy.lightllm()
    """
    keys_name_handle = {
        'inputs': 'inputs',
        'stop': 'stop_sequences'
    }
    default_headers = {'Content-Type': 'application/json'}
    message_format = {
        'inputs': 'Who are you ?',
        'parameters': {
            'do_sample': False,
            'presence_penalty': 0.0,
            'frequency_penalty': 0.0,
            'repetition_penalty': 1.0,
            'temperature': 1.0,
            'top_p': 1,
            'top_k': -1,  # -1 is for all
            'ignore_eos': False,
            'max_new_tokens': 8192,
            'stop_sequences': None,
        }
    }
    auto_map = {}
    stream_url_suffix = '_stream'
    stream_parse_parameters = {'delimiter': b'\n\n'}

    def __init__(self, trust_remote_code=True, launcher=launchers.remote(ngpus=1), log_path=None, # noqa B008
                 openai_api: Optional[bool] = None, **kw):
        super().__init__(launcher=launcher)
        self.kw = ArgsDict({
            'tp': 1,
            'max_total_token_num': 64000,
            'eos_id': 2,
            'port': None,
            'host': '0.0.0.0',
            'nccl_port': None,
            'tokenizer_mode': 'auto',
            'running_max_req_size': 256,
            'data_type': 'float16',
            'max_req_total_len': 64000,
            'max_req_input_len': 4096,
            'long_truncation_mode': 'head',
        })
        self.options_keys = kw.pop('options_keys', [])
        if trust_remote_code and 'trust_remote_code' not in self.options_keys:
            self.options_keys.append('trust_remote_code')
        self.kw.check_and_update(kw)
        self.random_port = False if 'port' in kw and kw['port'] else True
        self.random_nccl_port = False if 'nccl_port' in kw and kw['nccl_port'] else True
        self.temp_folder = make_log_dir(log_path, 'lightllm') if log_path else None

    def cmd(self, finetuned_model=None, base_model=None):
        """该方法用于生成启动LightLLM服务的命令。

Args:
    finetuned_model (str): 微调后的模型路径。
    base_model (str): 基础模型路径,当finetuned_model无效时使用。

**Returns:**

- LazyLLMCMD: 一个包含启动命令的LazyLLMCMD对象。
"""
        if not os.path.exists(finetuned_model) or \
            not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                    for filename in os.listdir(finetuned_model)):
            if not finetuned_model:
                LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                            f'base_model({base_model}) will be used')
            finetuned_model = base_model

        def impl():
            if self.random_port:
                self.kw['port'] = random.randint(30000, 40000)
            if self.random_nccl_port:
                self.kw['nccl_port'] = random.randint(20000, 30000)
            cmd = f'python -m lightllm.server.api_server --model_dir {finetuned_model} '
            cmd += self.kw.parse_kwargs()
            cmd += ' ' + parse_options_keys(self.options_keys)
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl, checkf=verify_fastapi_func)

    def geturl(self, job=None):
        """获取LightLLM服务的URL地址。

Args:
    job (optional): 任务对象,默认为None,此时使用self.job。

**Returns:**

- str: 服务的URL地址,格式为"http://{ip}:{port}/generate"。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return 'http://{ip}:{port}/generate'
        else:
            return f'http://{job.get_jobip()}:{self.kw["port"]}/generate'

    @staticmethod
    def extract_result(x, inputs):
        """从服务响应中提取生成的文本结果。

Args:
    x (str): 服务返回的响应文本。
    inputs (str): 输入文本。

**Returns:**

- str: 提取出的生成文本。

异常:
    Exception: 当解析JSON响应失败时抛出异常。
"""
        try:
            if x.startswith('data:'): return json.loads(x[len('data:'):])['token']['text']
            else: return json.loads(x)['generated_text'][0]
        except Exception as e:
            LOG.warning(f'JSONDecodeError on load {x}')
            raise e

cmd(finetuned_model=None, base_model=None)

该方法用于生成启动LightLLM服务的命令。

Parameters:

  • finetuned_model (str, default: None ) –

    微调后的模型路径。

  • base_model (str, default: None ) –

    基础模型路径,当finetuned_model无效时使用。

Returns:

  • LazyLLMCMD: 一个包含启动命令的LazyLLMCMD对象。
Source code in lazyllm/components/deploy/lightllm.py
    def cmd(self, finetuned_model=None, base_model=None):
        """该方法用于生成启动LightLLM服务的命令。

Args:
    finetuned_model (str): 微调后的模型路径。
    base_model (str): 基础模型路径,当finetuned_model无效时使用。

**Returns:**

- LazyLLMCMD: 一个包含启动命令的LazyLLMCMD对象。
"""
        if not os.path.exists(finetuned_model) or \
            not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                    for filename in os.listdir(finetuned_model)):
            if not finetuned_model:
                LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                            f'base_model({base_model}) will be used')
            finetuned_model = base_model

        def impl():
            if self.random_port:
                self.kw['port'] = random.randint(30000, 40000)
            if self.random_nccl_port:
                self.kw['nccl_port'] = random.randint(20000, 30000)
            cmd = f'python -m lightllm.server.api_server --model_dir {finetuned_model} '
            cmd += self.kw.parse_kwargs()
            cmd += ' ' + parse_options_keys(self.options_keys)
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl, checkf=verify_fastapi_func)

geturl(job=None)

获取LightLLM服务的URL地址。

Parameters:

  • job (optional, default: None ) –

    任务对象,默认为None,此时使用self.job。

Returns:

  • str: 服务的URL地址,格式为"http://{ip}:{port}/generate"。
Source code in lazyllm/components/deploy/lightllm.py
    def geturl(self, job=None):
        """获取LightLLM服务的URL地址。

Args:
    job (optional): 任务对象,默认为None,此时使用self.job。

**Returns:**

- str: 服务的URL地址,格式为"http://{ip}:{port}/generate"。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return 'http://{ip}:{port}/generate'
        else:
            return f'http://{job.get_jobip()}:{self.kw["port"]}/generate'

extract_result(x, inputs) staticmethod

从服务响应中提取生成的文本结果。

Parameters:

  • x (str) –

    服务返回的响应文本。

  • inputs (str) –

    输入文本。

Returns:

  • str: 提取出的生成文本。
异常

Exception: 当解析JSON响应失败时抛出异常。

Source code in lazyllm/components/deploy/lightllm.py
    @staticmethod
    def extract_result(x, inputs):
        """从服务响应中提取生成的文本结果。

Args:
    x (str): 服务返回的响应文本。
    inputs (str): 输入文本。

**Returns:**

- str: 提取出的生成文本。

异常:
    Exception: 当解析JSON响应失败时抛出异常。
"""
        try:
            if x.startswith('data:'): return json.loads(x[len('data:'):])['token']['text']
            else: return json.loads(x)['generated_text'][0]
        except Exception as e:
            LOG.warning(f'JSONDecodeError on load {x}')
            raise e

lazyllm.components.deploy.Vllm

Bases: LazyLLMDeployBase

此类是 LazyLLMDeployBase 的子类,基于 VLLM 框架提供的推理能力,用于大语言模型的部署与推理。

Parameters:

  • trust_remote_code (bool, default: True ) –

    是否允许加载来自远程服务器的模型代码,默认为 True

  • launcher (launcher, default: remote(ngpus=1) ) –

    模型启动器,默认为 launchers.remote(ngpus=1)

  • log_path (str, default: None ) –

    日志保存路径,若为 None 则不保存日志。

  • openai_api (bool, default: None ) –

    是否使用 OpenAI API 接口启动 VLLM 服务,默认为 False

  • kw

    关键字参数,用于更新默认的部署参数。除支持的关键字参数外,不允许传入额外参数。

此类支持的关键字参数及其默认值如下:

Other Parameters:

  • tensor-parallel-size (int) –

    张量并行大小,默认为 1

  • dtype (str) –

    模型权重和激活值的数据类型,默认为 auto。可选:halffloat16bfloat16floatfloat32

  • kv-cache-dtype (str) –

    KV 缓存的数据类型,默认为 auto。可选:fp8fp8_e5m2fp8_e4m3

  • device (str) –

    VLLM 支持的硬件类型,默认为 auto。可选:cudaneuroncpu

  • block-size (int) –

    token 块大小,默认为 16

  • port (int | str) –

    服务端口号,默认为 auto,即随机分配。

  • host (str) –

    服务绑定的 IP 地址,默认为 0.0.0.0

  • seed (int) –

    随机数种子,默认为 0

  • tokenizer_mode (str) –

    Tokenizer 加载模式,默认为 auto

  • max-num-seqs (int) –

    推理引擎支持的最大并行请求数,默认为 256

  • pipeline-parallel-size (int) –

    流水线并行大小,默认为 1

  • max-num-batched-tokens (int) –

    最大批处理 token 数,默认为 64000

Examples:

>>> from lazyllm import deploy
>>> infer = deploy.vllm()
Source code in lazyllm/components/deploy/vllm.py
class Vllm(LazyLLMDeployBase, metaclass=_VllmStreamParseParametersMeta):
    """此类是 ``LazyLLMDeployBase`` 的子类,基于 [VLLM](https://github.com/vllm-project/vllm) 框架提供的推理能力,用于大语言模型的部署与推理。

Args:
    trust_remote_code (bool): 是否允许加载来自远程服务器的模型代码,默认为 ``True``。
    launcher (lazyllm.launcher): 模型启动器,默认为 ``launchers.remote(ngpus=1)``。
    log_path (str): 日志保存路径,若为 ``None`` 则不保存日志。
    openai_api (bool): 是否使用 OpenAI API 接口启动 VLLM 服务,默认为 ``False``。
    kw: 关键字参数,用于更新默认的部署参数。除支持的关键字参数外,不允许传入额外参数。

此类支持的关键字参数及其默认值如下:

Keyword Args: 
    tensor-parallel-size (int): 张量并行大小,默认为 ``1``。
    dtype (str): 模型权重和激活值的数据类型,默认为 ``auto``。可选:``half``、``float16``、``bfloat16``、``float``、``float32``。
    kv-cache-dtype (str): KV 缓存的数据类型,默认为 ``auto``。可选:``fp8``、``fp8_e5m2``、``fp8_e4m3``。
    device (str): VLLM 支持的硬件类型,默认为 ``auto``。可选:``cuda``、``neuron``、``cpu``。
    block-size (int): token 块大小,默认为 ``16``。
    port (int | str): 服务端口号,默认为 ``auto``,即随机分配。
    host (str): 服务绑定的 IP 地址,默认为 ``0.0.0.0``。
    seed (int): 随机数种子,默认为 ``0``。
    tokenizer_mode (str): Tokenizer 加载模式,默认为 ``auto``。
    max-num-seqs (int): 推理引擎支持的最大并行请求数,默认为 ``256``。
    pipeline-parallel-size (int): 流水线并行大小,默认为 ``1``。
    max-num-batched-tokens (int): 最大批处理 token 数,默认为 ``64000``。


Examples:
    >>> from lazyllm import deploy
    >>> infer = deploy.vllm()
    """
    # keys_name_handle/default_headers/message_format will lose efficacy when openai_api is True
    keys_name_handle = {'inputs': 'prompt', 'stop': 'stop'}
    default_headers = {'Content-Type': 'application/json'}
    message_format = {
        'prompt': 'Who are you ?',
        'stream': False,
        'stop': ['<|im_end|>', '<|im_start|>', '</s>', '<|assistant|>', '<|user|>', '<|system|>', '<eos>'],
        'skip_special_tokens': False,
        'temperature': 0.6,
        'top_p': 0.8,
        'max_tokens': 4096
    }
    auto_map = {
        'tp': 'tensor_parallel_size'
    }  # from cli to vllm
    optional_keys = set([
        'max_model_len',
        'gpu_memory_utilization',
        'task',
        'dtype',
        'kv_cache_dtype',
        'tokenizer_mode',
        'block_size',
        'max_num_seqs',
        'pipeline_parallel_size',
        'tensor_parallel_size',
        'seed',
        'port',
        'max_num_batched_tokens',
        'tool_call_parser',
        'swap_space',
        'mm_processor_kwargs',
        'limit_mm_per_prompt',
        'hf_overrides'])

    # TODO(wangzhihong): change default value for `openai_api` argument to True
    def __init__(self, trust_remote_code: bool = True, launcher: LazyLLMLaunchersBase = launchers.remote(ngpus=1),  # noqa B008
                 log_path: str = None, openai_api: Optional[bool] = None, **kw):
        self.launcher_list, launcher = reallocate_launcher(launcher)
        super().__init__(launcher=launcher)
        self.kw = ArgsDict({
            'host': '0.0.0.0',
            'max_model_len': 10240,
        })
        if openai_api is None: openai_api = lazyllm.config['openai_api']
        self._vllm_cmd = 'vllm.entrypoints.openai.api_server' if openai_api else 'vllm.entrypoints.api_server'
        self._openai_api = openai_api
        self.options_keys = kw.pop('options_keys', [])
        if trust_remote_code and 'trust_remote_code' not in self.options_keys:
            self.options_keys.append('trust_remote_code')
        self.kw.update(**{key: kw[key] for key in self.optional_keys if key in kw})
        self.kw.check_and_update(kw)
        self.random_port = False if 'port' in kw and kw['port'] and kw['port'] != 'auto' else True
        self.temp_folder = make_log_dir(log_path, 'vllm') if log_path else None
        if self.launcher_list:
            ray_launcher = [Distributed(launcher=launcher) for launcher in self.launcher_list]
            parall_launcher = [lazyllm.pipeline(sleep_moment, launcher) for launcher in ray_launcher[1:]]
            self._prepare_deploy = lazyllm.pipeline(
                ray_launcher[0], post_action=(lazyllm.parallel(*parall_launcher) if len(parall_launcher) else None))

    def cmd(self, finetuned_model=None, base_model=None, master_ip=None):
        """构造用于启动 vLLM 推理服务的命令。

该方法会自动检测模型路径是否有效,并根据当前配置参数动态生成可执行命令,支持多节点部署时自动加入 ray 启动命令。

Args:
    finetuned_model (str): 微调后的模型路径。
    base_model (str): 备用基础模型路径(当 finetuned_model 无效时启用)。
    master_ip (str): 分布式部署中的主节点 IP,仅在多节点时启用。

**Returns:**

- LazyLLMCMD: 可执行命令对象,包含启动指令、结果回调函数及健康检查方法。
"""
        if finetuned_model:
            LOG.info(f'Using finetuned model from {finetuned_model} to deploy.')
        if not finetuned_model:
            LOG.info(f'Using model {base_model} to deploy.')
            finetuned_model = base_model
        elif not os.path.exists(finetuned_model):
            LOG.warning(f'Warning! The finetuned_model path does not exist: {finetuned_model}. '
                        f'Using base_model({base_model}) instead.')
            finetuned_model = base_model
        elif not any(filename.endswith(('.bin', '.safetensors', '.pt'))
                     for filename in os.listdir(finetuned_model)):
            LOG.warning(f'Warning! No valid model files (.bin, .safetensors or .pt) found in: {finetuned_model}. '
                        f'Using base_model({base_model}) instead.')
            finetuned_model = base_model

        def impl():
            if self.random_port:
                self.kw['port'] = random.randint(30000, 40000)

            cmd = ''
            if self.launcher_list:
                cmd += f'ray start --address="{master_ip}" && '
            cmd += f'{sys.executable} -m {self._vllm_cmd} --model {finetuned_model} '
            if self._openai_api: cmd += '--served-model-name lazyllm '
            cmd += self.kw.parse_kwargs()
            cmd += ' ' + parse_options_keys(self.options_keys)
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl,
                          checkf=(verify_vllm_openai_func if self._openai_api else verify_fastapi_func))

    def geturl(self, job=None):
        """获取 vLLM 服务的推理地址。

根据运行模式(Display 模式或实际部署)返回相应的 URL,用于访问模型的生成接口。

Args:
    job (Job, optional): 部署任务对象。默认取当前模块绑定的 job。

**Returns:**

- str: 推理服务的 HTTP 地址。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return 'http://{ip}:{port}/generate'
        else:
            return f'http://{job.get_jobip()}:{self.kw["port"]}' + (
                '/v1/' if self._openai_api else '/generate')

    @staticmethod
    def extract_result(x, inputs):
        """从 VLLM 接口返回的 JSON 字符串中提取推理结果。

Args:
    x (str): VLLM 服务返回的原始 JSON 字符串。
    inputs (Any): 输入参数(此处未使用,保留接口一致性)。

**Returns:**

- str: 模型生成的文本结果。
"""
        return json.loads(x)['text'][0]

cmd(finetuned_model=None, base_model=None, master_ip=None)

构造用于启动 vLLM 推理服务的命令。

该方法会自动检测模型路径是否有效,并根据当前配置参数动态生成可执行命令,支持多节点部署时自动加入 ray 启动命令。

Parameters:

  • finetuned_model (str, default: None ) –

    微调后的模型路径。

  • base_model (str, default: None ) –

    备用基础模型路径(当 finetuned_model 无效时启用)。

  • master_ip (str, default: None ) –

    分布式部署中的主节点 IP,仅在多节点时启用。

Returns:

  • LazyLLMCMD: 可执行命令对象,包含启动指令、结果回调函数及健康检查方法。
Source code in lazyllm/components/deploy/vllm.py
    def cmd(self, finetuned_model=None, base_model=None, master_ip=None):
        """构造用于启动 vLLM 推理服务的命令。

该方法会自动检测模型路径是否有效,并根据当前配置参数动态生成可执行命令,支持多节点部署时自动加入 ray 启动命令。

Args:
    finetuned_model (str): 微调后的模型路径。
    base_model (str): 备用基础模型路径(当 finetuned_model 无效时启用)。
    master_ip (str): 分布式部署中的主节点 IP,仅在多节点时启用。

**Returns:**

- LazyLLMCMD: 可执行命令对象,包含启动指令、结果回调函数及健康检查方法。
"""
        if finetuned_model:
            LOG.info(f'Using finetuned model from {finetuned_model} to deploy.')
        if not finetuned_model:
            LOG.info(f'Using model {base_model} to deploy.')
            finetuned_model = base_model
        elif not os.path.exists(finetuned_model):
            LOG.warning(f'Warning! The finetuned_model path does not exist: {finetuned_model}. '
                        f'Using base_model({base_model}) instead.')
            finetuned_model = base_model
        elif not any(filename.endswith(('.bin', '.safetensors', '.pt'))
                     for filename in os.listdir(finetuned_model)):
            LOG.warning(f'Warning! No valid model files (.bin, .safetensors or .pt) found in: {finetuned_model}. '
                        f'Using base_model({base_model}) instead.')
            finetuned_model = base_model

        def impl():
            if self.random_port:
                self.kw['port'] = random.randint(30000, 40000)

            cmd = ''
            if self.launcher_list:
                cmd += f'ray start --address="{master_ip}" && '
            cmd += f'{sys.executable} -m {self._vllm_cmd} --model {finetuned_model} '
            if self._openai_api: cmd += '--served-model-name lazyllm '
            cmd += self.kw.parse_kwargs()
            cmd += ' ' + parse_options_keys(self.options_keys)
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl,
                          checkf=(verify_vllm_openai_func if self._openai_api else verify_fastapi_func))

geturl(job=None)

获取 vLLM 服务的推理地址。

根据运行模式(Display 模式或实际部署)返回相应的 URL,用于访问模型的生成接口。

Parameters:

  • job (Job, default: None ) –

    部署任务对象。默认取当前模块绑定的 job。

Returns:

  • str: 推理服务的 HTTP 地址。
Source code in lazyllm/components/deploy/vllm.py
    def geturl(self, job=None):
        """获取 vLLM 服务的推理地址。

根据运行模式(Display 模式或实际部署)返回相应的 URL,用于访问模型的生成接口。

Args:
    job (Job, optional): 部署任务对象。默认取当前模块绑定的 job。

**Returns:**

- str: 推理服务的 HTTP 地址。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return 'http://{ip}:{port}/generate'
        else:
            return f'http://{job.get_jobip()}:{self.kw["port"]}' + (
                '/v1/' if self._openai_api else '/generate')

extract_result(x, inputs) staticmethod

从 VLLM 接口返回的 JSON 字符串中提取推理结果。

Parameters:

  • x (str) –

    VLLM 服务返回的原始 JSON 字符串。

  • inputs (Any) –

    输入参数(此处未使用,保留接口一致性)。

Returns:

  • str: 模型生成的文本结果。
Source code in lazyllm/components/deploy/vllm.py
    @staticmethod
    def extract_result(x, inputs):
        """从 VLLM 接口返回的 JSON 字符串中提取推理结果。

Args:
    x (str): VLLM 服务返回的原始 JSON 字符串。
    inputs (Any): 输入参数(此处未使用,保留接口一致性)。

**Returns:**

- str: 模型生成的文本结果。
"""
        return json.loads(x)['text'][0]

lazyllm.components.deploy.LMDeploy

Bases: LazyLLMDeployBase

LMDeploy 类,继承自 LazyLLMDeployBase,基于 LMDeploy 框架,
用于启动并管理大语言模型的推理服务。

Parameters:

  • launcher (Optional[launcher], default: remote(ngpus=1) ) –

    服务启动器,默认使用 launchers.remote(ngpus=1)

  • trust_remote_code (bool, default: True ) –

    是否信任远程代码,默认为 True

  • log_path (Optional[str], default: None ) –

    日志输出路径,默认为 None

  • **kw

    关键字参数,用于更新默认的部署配置。除下列参数外,不允许传入额外参数。

Other Parameters:

  • tp (int) –

    张量并行参数,默认为 1

  • server-name (str) –

    服务监听的 IP 地址,默认为 0.0.0.0

  • server-port (Optional[int]) –

    服务端口号,默认为 None,此时会自动随机分配 30000–40000 区间的端口。

  • max-batch-size (int) –

    最大批处理大小,默认为 128

  • chat-template (Optional[str]) –

    对话模板文件路径。若模型不是视觉语言模型且未指定模板,将使用默认模板。

  • eager-mode (bool) –

    是否启用 eager 模式,受环境变量 LMDEPLOY_EAGER_MODE 控制,默认为 False

Examples:

>>> # Basic use:
>>> from lazyllm import deploy
>>> infer = deploy.LMDeploy()
>>>
>>> # MultiModal:
>>> import lazyllm
>>> from lazyllm import deploy, globals
>>> from lazyllm.components.formatter import encode_query_with_filepaths
>>> chat = lazyllm.TrainableModule('InternVL3_5-1B').deploy_method(deploy.LMDeploy)
>>> chat.update_server()
>>> inputs = encode_query_with_filepaths('What is it?', ['path/to/image'])
>>> res = chat(inputs)
Source code in lazyllm/components/deploy/lmdeploy.py
class LMDeploy(LazyLLMDeployBase):
    """``LMDeploy`` 类,继承自 ``LazyLLMDeployBase``,基于 [LMDeploy](https://github.com/InternLM/lmdeploy) 框架,  
用于启动并管理大语言模型的推理服务。

Args:
    launcher (Optional[lazyllm.launcher]): 服务启动器,默认使用 ``launchers.remote(ngpus=1)``。  
    trust_remote_code (bool): 是否信任远程代码,默认为 ``True``。  
    log_path (Optional[str]): 日志输出路径,默认为 ``None``。  
    **kw: 关键字参数,用于更新默认的部署配置。除下列参数外,不允许传入额外参数。  

Keyword Args:
    tp (int): 张量并行参数,默认为 ``1``。  
    server-name (str): 服务监听的 IP 地址,默认为 ``0.0.0.0``。  
    server-port (Optional[int]): 服务端口号,默认为 ``None``,此时会自动随机分配 30000–40000 区间的端口。  
    max-batch-size (int): 最大批处理大小,默认为 ``128``。  
    chat-template (Optional[str]): 对话模板文件路径。若模型不是视觉语言模型且未指定模板,将使用默认模板。  
    eager-mode (bool): 是否启用 eager 模式,受环境变量 ``LMDEPLOY_EAGER_MODE`` 控制,默认为 ``False``。  


Examples:
    >>> # Basic use:
    >>> from lazyllm import deploy
    >>> infer = deploy.LMDeploy()
    >>>
    >>> # MultiModal:
    >>> import lazyllm
    >>> from lazyllm import deploy, globals
    >>> from lazyllm.components.formatter import encode_query_with_filepaths
    >>> chat = lazyllm.TrainableModule('InternVL3_5-1B').deploy_method(deploy.LMDeploy)
    >>> chat.update_server()
    >>> inputs = encode_query_with_filepaths('What is it?', ['path/to/image'])
    >>> res = chat(inputs)
    """
    keys_name_handle = {
        'inputs': 'prompt',
        'stop': 'stop',
    }
    default_headers = {'Content-Type': 'application/json'}
    message_format = {
        'prompt': 'Who are you ?',
        'stream': False,
        'stop': None,
        'top_p': 0.8,
        'temperature': 0.8,
        'skip_special_tokens': True,
    }
    auto_map = {
        'port': 'server-port',
        'host': 'server-name',
        'max_batch_size': 'max-batch-size',
    }
    stream_parse_parameters = {'delimiter': b'\n'}

    def __init__(self, launcher=launchers.remote(ngpus=1), trust_remote_code=True, log_path=None, **kw):  # noqa B008
        super().__init__(launcher=launcher)
        self.kw = ArgsDict({
            'server-name': '0.0.0.0',
            'server-port': None,
            'tp': 1,
            'max-batch-size': 128,
        })
        self.options_keys = kw.pop('options_keys', [])
        self.kw.check_and_update(kw)
        self._trust_remote_code = trust_remote_code
        self.random_port = False if 'server-port' in kw and kw['server-port'] else True
        self.temp_folder = make_log_dir(log_path, 'lmdeploy') if log_path else None

    def cmd(self, finetuned_model=None, base_model=None):
        """该方法用于生成启动LMDeploy服务的命令。

Args:
    finetuned_model (str): 微调后的模型路径。
    base_model (str): 基础模型路径,当finetuned_model无效时使用。

**Returns:**

- LazyLLMCMD: 一个包含启动命令的LazyLLMCMD对象。
"""
        if not os.path.exists(finetuned_model) or \
            not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                    for filename in os.listdir(finetuned_model)):
            if not finetuned_model:
                LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                            f'base_model({base_model}) will be used')
            finetuned_model = base_model

        def impl():
            if self.random_port:
                self.kw['server-port'] = random.randint(30000, 40000)
            cmd = f'lmdeploy serve api_server {finetuned_model} --model-name lazyllm '

            if importlib.util.find_spec('torch_npu') is not None: cmd += '--device ascend '
            if config['lmdeploy_eager_mode']: cmd += '--eager-mode '
            cmd += self.kw.parse_kwargs()
            cmd += ' ' + parse_options_keys(self.options_keys)
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl, checkf=verify_fastapi_func)

    def geturl(self, job=None):
        """获取LMDeploy服务的URL地址。

Args:
    job (optional): 任务对象,默认为None,此时使用self.job。

**Returns:**

- str: 服务的URL地址,格式为"http://{ip}:{port}/v1/chat/interactive"。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return 'http://{ip}:{port}/v1/'
        else:
            return f'http://{job.get_jobip()}:{self.kw["server-port"]}/v1/'

    @staticmethod
    def extract_result(x, inputs):
        """解析模型推理结果,从返回的 JSON 字符串中提取文本输出。

Args:
    x (str): 模型返回的 JSON 格式字符串。  
    inputs (dict): 原始输入数据(此参数未被直接使用,保留作接口兼容)。  

**Returns:**

- str: 从响应中解析得到的文本结果。  
"""
        return json.loads(x)['text']

cmd(finetuned_model=None, base_model=None)

该方法用于生成启动LMDeploy服务的命令。

Parameters:

  • finetuned_model (str, default: None ) –

    微调后的模型路径。

  • base_model (str, default: None ) –

    基础模型路径,当finetuned_model无效时使用。

Returns:

  • LazyLLMCMD: 一个包含启动命令的LazyLLMCMD对象。
Source code in lazyllm/components/deploy/lmdeploy.py
    def cmd(self, finetuned_model=None, base_model=None):
        """该方法用于生成启动LMDeploy服务的命令。

Args:
    finetuned_model (str): 微调后的模型路径。
    base_model (str): 基础模型路径,当finetuned_model无效时使用。

**Returns:**

- LazyLLMCMD: 一个包含启动命令的LazyLLMCMD对象。
"""
        if not os.path.exists(finetuned_model) or \
            not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                    for filename in os.listdir(finetuned_model)):
            if not finetuned_model:
                LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                            f'base_model({base_model}) will be used')
            finetuned_model = base_model

        def impl():
            if self.random_port:
                self.kw['server-port'] = random.randint(30000, 40000)
            cmd = f'lmdeploy serve api_server {finetuned_model} --model-name lazyllm '

            if importlib.util.find_spec('torch_npu') is not None: cmd += '--device ascend '
            if config['lmdeploy_eager_mode']: cmd += '--eager-mode '
            cmd += self.kw.parse_kwargs()
            cmd += ' ' + parse_options_keys(self.options_keys)
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl, checkf=verify_fastapi_func)

geturl(job=None)

获取LMDeploy服务的URL地址。

Parameters:

  • job (optional, default: None ) –

    任务对象,默认为None,此时使用self.job。

Returns:

  • str: 服务的URL地址,格式为"http://{ip}:{port}/v1/chat/interactive"。
Source code in lazyllm/components/deploy/lmdeploy.py
    def geturl(self, job=None):
        """获取LMDeploy服务的URL地址。

Args:
    job (optional): 任务对象,默认为None,此时使用self.job。

**Returns:**

- str: 服务的URL地址,格式为"http://{ip}:{port}/v1/chat/interactive"。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return 'http://{ip}:{port}/v1/'
        else:
            return f'http://{job.get_jobip()}:{self.kw["server-port"]}/v1/'

extract_result(x, inputs) staticmethod

解析模型推理结果,从返回的 JSON 字符串中提取文本输出。

Parameters:

  • x (str) –

    模型返回的 JSON 格式字符串。

  • inputs (dict) –

    原始输入数据(此参数未被直接使用,保留作接口兼容)。

Returns:

  • str: 从响应中解析得到的文本结果。
Source code in lazyllm/components/deploy/lmdeploy.py
    @staticmethod
    def extract_result(x, inputs):
        """解析模型推理结果,从返回的 JSON 字符串中提取文本输出。

Args:
    x (str): 模型返回的 JSON 格式字符串。  
    inputs (dict): 原始输入数据(此参数未被直接使用,保留作接口兼容)。  

**Returns:**

- str: 从响应中解析得到的文本结果。  
"""
        return json.loads(x)['text']

lazyllm.components.deploy.base.DummyDeploy

Bases: LazyLLMDeployBase, Pipeline

DummyDeploy(launcher=launchers.remote(sync=False), , stream=False, *kw)

一个用于测试的模拟部署类,继承自 LazyLLMDeployBaseflows.Pipeline,实现了一个简单的流水线风格部署服务, 支持流式输出(可选)。

该类主要用于内部测试和示例用途。它接收符合 message_format 格式的输入,根据是否启用 stream 参数,返回 字符串或逐步输出的模拟响应。

Args: launcher: 部署器实例,默认值为 launchers.remote(sync=False)。 stream (bool): 是否以流式方式输出结果。 kw: 其他传递给父类的关键字参数。

Call Arguments

keys_name_handle (dict): 输入字段名的映射。

message_format (dict): 默认请求模板,包括输入内容与生成参数。

Source code in lazyllm/components/deploy/base.py
class DummyDeploy(LazyLLMDeployBase, flows.Pipeline):
    """DummyDeploy(launcher=launchers.remote(sync=False), *, stream=False, **kw)

一个用于测试的模拟部署类,继承自 `LazyLLMDeployBase` 和 `flows.Pipeline`,实现了一个简单的流水线风格部署服务,
支持流式输出(可选)。

该类主要用于内部测试和示例用途。它接收符合 `message_format` 格式的输入,根据是否启用 `stream` 参数,返回
字符串或逐步输出的模拟响应。

Args:
    launcher: 部署器实例,默认值为 `launchers.remote(sync=False)`。
    stream (bool): 是否以流式方式输出结果。
    kw: 其他传递给父类的关键字参数。

Call Arguments:
    keys_name_handle (dict): 输入字段名的映射。 

    message_format (dict): 默认请求模板,包括输入内容与生成参数。 

"""
    keys_name_handle = {'inputs': 'inputs'}
    message_format = {
        'inputs': '',
        'parameters': {
            'do_sample': False,
            'temperature': 0.1,
        }
    }

    def __init__(self, launcher=launchers.remote(sync=False), *, stream=False, **kw):  # noqa B008
        super().__init__(launcher=launcher)

        def func():

            def impl(x):
                LOG.info(f'input is {x["inputs"]}, parameters is {x["parameters"]}')
                return f'reply for {x["inputs"]}, and parameters is {x["parameters"]}'

            def impl_stream(x):
                for s in ['reply', ' for', f' {x["inputs"]}', ', and',
                          ' parameters', ' is', f' {x["parameters"]}']:
                    yield s
                    time.sleep(0.2)
            return impl_stream if stream else impl
        flows.Pipeline.__init__(self, func,
                                lazyllm.deploy.RelayServer(port=random.randint(30000, 40000), launcher=launcher))

    def __call__(self, *args):
        url = flows.Pipeline.__call__(self)
        LOG.info(f'dummy deploy url is : {url}')
        return url

    def __repr__(self):
        return flows.Pipeline.__repr__(self)

lazyllm.components.auto.AutoDeploy

Bases: LazyLLMDeployBase

此类是 LazyLLMDeployBase 的子类,可根据输入的参数自动选择合适的推理框架和参数,以对大语言模型进行推理。

具体而言,基于输入的:base_model 的模型参数、max_token_numlauncher 中GPU的类型以及卡数,该类可以自动选择出合适的推理框架(如: LightllmVllm)及所需的参数。

Parameters:

  • base_model (str) –

    用于进行微调的基模型,要求是基模型的路径或模型名。用于提供基模型信息。

  • source (config[model_source]) –

    指定模型的下载源。可通过设置环境变量 LAZYLLM_MODEL_SOURCE 来配置,目前仅支持 huggingfacemodelscope 。若不设置,lazyllm不会启动自动模型下载。

  • trust_remote_code (bool) –

    是否允许加载来自远程服务器的模型代码,默认为 True

  • launcher (launcher, default: remote() ) –

    微调的启动器,默认为 launchers.remote(ngpus=1)

  • stream (bool) –

    是否为流式响应,默认为 False

  • type (str) –

    类型参数,默认为 None,及llm类型,另外还支持embed类型。

  • max_token_num (int) –

    输入微调模型的token最大长度,默认为1024

  • launcher (launcher, default: remote() ) –

    微调的启动器,默认为 launchers.remote(ngpus=1)

  • kw

    关键字参数,用于更新默认的训练参数。注意这里能够指定的关键字参数取决于 LazyLLM 推测出的框架,因此建议谨慎设置。

Examples:

>>> from lazyllm import deploy
>>> deploy.auto('internlm2-chat-7b')
<lazyllm.llm.deploy type=Lightllm>
Source code in lazyllm/components/auto/autodeploy.py
class AutoDeploy(LazyLLMDeployBase):
    """此类是 ``LazyLLMDeployBase`` 的子类,可根据输入的参数自动选择合适的推理框架和参数,以对大语言模型进行推理。

具体而言,基于输入的:``base_model`` 的模型参数、``max_token_num``、``launcher`` 中GPU的类型以及卡数,该类可以自动选择出合适的推理框架(如: ``Lightllm`` 或 ``Vllm``)及所需的参数。

Args:
    base_model (str): 用于进行微调的基模型,要求是基模型的路径或模型名。用于提供基模型信息。
    source (lazyllm.config['model_source']): 指定模型的下载源。可通过设置环境变量 ``LAZYLLM_MODEL_SOURCE`` 来配置,目前仅支持 ``huggingface`` 或 ``modelscope`` 。若不设置,lazyllm不会启动自动模型下载。
    trust_remote_code (bool): 是否允许加载来自远程服务器的模型代码,默认为 ``True``。
    launcher (lazyllm.launcher): 微调的启动器,默认为 ``launchers.remote(ngpus=1)``。
    stream (bool): 是否为流式响应,默认为 ``False``。
    type (str): 类型参数,默认为 ``None``,及``llm``类型,另外还支持``embed``类型。
    max_token_num (int): 输入微调模型的token最大长度,默认为``1024``。
    launcher (lazyllm.launcher): 微调的启动器,默认为 ``launchers.remote(ngpus=1)``。
    kw: 关键字参数,用于更新默认的训练参数。注意这里能够指定的关键字参数取决于 LazyLLM 推测出的框架,因此建议谨慎设置。


Examples:
    >>> from lazyllm import deploy
    >>> deploy.auto('internlm2-chat-7b')
    <lazyllm.llm.deploy type=Lightllm> 
    """
    @staticmethod
    def _get_embed_deployer(launcher, type, kw):
        launcher = launcher or launchers.remote(ngpus=1)
        kw['model_type'] = type
        if lazyllm.config['default_embedding_engine'].lower() in ('transformers', 'flagembedding') \
            or kw.get('embed_type')=='sparse' or not check_requirements('infinity_emb'):
            return deploy.Rerank if type == 'rerank' else deploy.Embedding, launcher, kw
        else:
            return deploy.InfinityRerank if type == 'rerank' else deploy.Infinity, launcher, kw

    @classmethod
    def get_deployer(cls, base_model: str, source: Optional[str] = None, trust_remote_code: bool = True,
                     launcher: Optional[LazyLLMLaunchersBase] = None, type: Optional[str] = None,
                     log_path: Optional[str] = None, **kw):
        """根据模型类型获取对应的部署器类。

自动检测模型类型并返回最适合的部署器类、启动器和配置参数。

Args:
    base_model (str): 基础模型名称或路径。
    source (Optional[str], optional): 模型来源。
    trust_remote_code (bool, optional): 是否信任远程代码。
    launcher (Optional[LazyLLMLaunchersBase], optional): 启动器实例。
    type (Optional[str], optional): 模型类型。
    log_path (Optional[str], optional): 日志文件路径。
    **kw: 其他配置参数。
"""
        model_name = get_model_name(base_model)
        kw['log_path'], kw['trust_remote_code'] = log_path, trust_remote_code
        if not type:
            type = ModelManager.get_model_type(model_name)

        if type in ('embed', 'cross_modal_embed', 'rerank'):
            return AutoDeploy._get_embed_deployer(launcher, type, kw)
        elif type == 'sd':
            return StableDiffusionDeploy, launcher or launchers.remote(ngpus=1), kw
        elif type == 'stt':
            return SenseVoiceDeploy, launcher or launchers.remote(ngpus=1), kw
        elif type == 'tts':
            return TTSDeploy.get_deploy_cls(model_name), launcher or launchers.remote(ngpus=1), kw
        elif type == 'vlm':
            return deploy.LMDeploy, launcher or launchers.remote(ngpus=1), kw
        elif type == 'ocr':
            return OCRDeploy, launcher or launchers.remote(ngpus=1), kw

        if not launcher:
            match = re.search(r'(\d+)[bB]', model_name)
            size = int(match.group(1)) if match else 0
            size = (size * 2) if 'awq' not in model_name.lower() else (size / 1.5)
            ngpus = (1 << (math.ceil(size * 2 * 0.6 / config['gpu_memory']) - 1).bit_length())
            launcher = launchers.remote(ngpus = ngpus)

        for deploy_cls in ['vllm', 'lightllm', 'lmdeploy', 'mindie']:
            if check_cmd(deploy_cls) or check_requirements(requirements.get(deploy_cls)):
                deploy_cls = getattr(deploy, deploy_cls)
                return deploy_cls, launcher, kw
        return deploy.auto, launcher, kw

    def __new__(cls, base_model, source=lazyllm.config['model_source'], trust_remote_code=True,
                launcher=None, type=None, log_path=None, **kw):
        deploy_cls, launcher, kw = __class__.get_deployer(
            base_model=base_model, source=source, trust_remote_code=trust_remote_code,
            launcher=launcher, type=type, log_path=log_path, **kw)
        return deploy_cls(launcher=launcher, **kw)

get_deployer(base_model, source=None, trust_remote_code=True, launcher=None, type=None, log_path=None, **kw) classmethod

根据模型类型获取对应的部署器类。

自动检测模型类型并返回最适合的部署器类、启动器和配置参数。

Parameters:

  • base_model (str) –

    基础模型名称或路径。

  • source (Optional[str], default: None ) –

    模型来源。

  • trust_remote_code (bool, default: True ) –

    是否信任远程代码。

  • launcher (Optional[LazyLLMLaunchersBase], default: None ) –

    启动器实例。

  • type (Optional[str], default: None ) –

    模型类型。

  • log_path (Optional[str], default: None ) –

    日志文件路径。

  • **kw

    其他配置参数。

Source code in lazyllm/components/auto/autodeploy.py
    @classmethod
    def get_deployer(cls, base_model: str, source: Optional[str] = None, trust_remote_code: bool = True,
                     launcher: Optional[LazyLLMLaunchersBase] = None, type: Optional[str] = None,
                     log_path: Optional[str] = None, **kw):
        """根据模型类型获取对应的部署器类。

自动检测模型类型并返回最适合的部署器类、启动器和配置参数。

Args:
    base_model (str): 基础模型名称或路径。
    source (Optional[str], optional): 模型来源。
    trust_remote_code (bool, optional): 是否信任远程代码。
    launcher (Optional[LazyLLMLaunchersBase], optional): 启动器实例。
    type (Optional[str], optional): 模型类型。
    log_path (Optional[str], optional): 日志文件路径。
    **kw: 其他配置参数。
"""
        model_name = get_model_name(base_model)
        kw['log_path'], kw['trust_remote_code'] = log_path, trust_remote_code
        if not type:
            type = ModelManager.get_model_type(model_name)

        if type in ('embed', 'cross_modal_embed', 'rerank'):
            return AutoDeploy._get_embed_deployer(launcher, type, kw)
        elif type == 'sd':
            return StableDiffusionDeploy, launcher or launchers.remote(ngpus=1), kw
        elif type == 'stt':
            return SenseVoiceDeploy, launcher or launchers.remote(ngpus=1), kw
        elif type == 'tts':
            return TTSDeploy.get_deploy_cls(model_name), launcher or launchers.remote(ngpus=1), kw
        elif type == 'vlm':
            return deploy.LMDeploy, launcher or launchers.remote(ngpus=1), kw
        elif type == 'ocr':
            return OCRDeploy, launcher or launchers.remote(ngpus=1), kw

        if not launcher:
            match = re.search(r'(\d+)[bB]', model_name)
            size = int(match.group(1)) if match else 0
            size = (size * 2) if 'awq' not in model_name.lower() else (size / 1.5)
            ngpus = (1 << (math.ceil(size * 2 * 0.6 / config['gpu_memory']) - 1).bit_length())
            launcher = launchers.remote(ngpus = ngpus)

        for deploy_cls in ['vllm', 'lightllm', 'lmdeploy', 'mindie']:
            if check_cmd(deploy_cls) or check_requirements(requirements.get(deploy_cls)):
                deploy_cls = getattr(deploy, deploy_cls)
                return deploy_cls, launcher, kw
        return deploy.auto, launcher, kw

lazyllm.components.deploy.embed.AbstractEmbedding

Bases: ABC

抽象嵌入基类,为所有嵌入模型提供统一的接口和基础功能。此类定义了嵌入模型的标准接口,包括模型加载、调用和序列化等功能。

Parameters:

  • base_embed (str) –

    嵌入模型的基础路径或标识符,用于指定要加载的嵌入模型。

  • source (str, default: None ) –

    模型来源,默认为 None。如果未指定,将使用 LazyLLM 配置中的默认模型来源。

  • init (bool, default: False ) –

    是否在初始化时立即加载模型,默认为 False。如果为 True,将在对象创建时立即调用 load_embed() 方法。

Source code in lazyllm/components/deploy/embed.py
class AbstractEmbedding(ABC):
    """抽象嵌入基类,为所有嵌入模型提供统一的接口和基础功能。此类定义了嵌入模型的标准接口,包括模型加载、调用和序列化等功能。

Args:
    base_embed (str): 嵌入模型的基础路径或标识符,用于指定要加载的嵌入模型。
    source (str, optional): 模型来源,默认为 ``None``。如果未指定,将使用 LazyLLM 配置中的默认模型来源。
    init (bool): 是否在初始化时立即加载模型,默认为 ``False``。如果为 ``True``,将在对象创建时立即调用 ``load_embed()`` 方法。
"""
    def __init__(self, base_embed, source=None, init=False):
        from ..utils.downloader import ModelManager
        self._source = source or lazyllm.config['model_source']
        self._base_embed = ModelManager(self._source).download(base_embed) or ''
        self._embed = None
        self._init = lazyllm.once_flag()
        if init:
            lazyllm.call_once(self._init, self.load_embed)

    @abstractmethod
    def load_embed(self) -> None:
        """加载嵌入模型的抽象方法。此方法由子类实现,用于执行具体的模型加载逻辑。
"""
        pass

    @abstractmethod
    def _call(self, data: Dict[str, Union[str, List[str]]]) -> str:
        pass

    def __call__(self, data: Dict[str, Union[str, List[str]]]) -> str:
        lazyllm.call_once(self._init, self.load_embed)
        return self._call(data)

    def __reduce__(self):
        init = bool(os.getenv('LAZYLLM_ON_CLOUDPICKLE', None) == 'ON' or self._init)
        return self.__class__, (self._base_embed, self._source, init)

load_embed() abstractmethod

加载嵌入模型的抽象方法。此方法由子类实现,用于执行具体的模型加载逻辑。

Source code in lazyllm/components/deploy/embed.py
    @abstractmethod
    def load_embed(self) -> None:
        """加载嵌入模型的抽象方法。此方法由子类实现,用于执行具体的模型加载逻辑。
"""
        pass

lazyllm.components.deploy.EmbeddingDeploy

Bases: LazyLLMDeployBase

此类是 LazyLLMDeployBase 的子类,用于部署文本嵌入(Embedding)服务。支持稠密向量(dense)和稀疏向量(sparse)两种嵌入方式,可使用 HuggingFace 模型或 FlagEmbedding 模型。

Parameters:

  • launcher (Optional[launcher], default: None ) –

    启动器实例,默认为 None

  • model_type (Optional[str], default: 'embed' ) –

    模型类型,默认为 'embed'

  • log_path (Optional[str], default: None ) –

    日志文件路径,默认为 None

  • embed_type (Optional[str], default: 'dense' ) –

    嵌入类型,可选 'dense''sparse',默认为 'dense'

  • trust_remote_code (bool, default: True ) –

    是否信任远程代码,默认为 True

  • port (Optional[int], default: None ) –

    服务端口号,默认为 None,此情况下 LazyLLM 会自动生成随机端口号。

Call Arguments

finetuned_model (Optional[str]): 微调后的模型路径或名称。

base_model (Optional[str]): 基础模型路径或名称,当 finetuned_model 无效时会使用此模型。

Message Format

输入格式为包含 text(文本)和 images(图像列表)的字典。

  • text (str): 需要编码的文本内容

  • images (Union[str, List[str]]): 需要编码的图像列表,可选

Examples:

>>> from lazyllm import deploy
>>> embed_service = deploy.EmbeddingDeploy(embed_type='dense')
>>> embed_service('path/to/model')
Source code in lazyllm/components/deploy/embed.py
class EmbeddingDeploy(LazyLLMDeployBase):
    """此类是 ``LazyLLMDeployBase`` 的子类,用于部署文本嵌入(Embedding)服务。支持稠密向量(dense)和稀疏向量(sparse)两种嵌入方式,可使用 HuggingFace 模型或 FlagEmbedding 模型。

Args:
    launcher (Optional[lazyllm.launcher]): 启动器实例,默认为 ``None``。
    model_type (Optional[str]): 模型类型,默认为 ``'embed'``。
    log_path (Optional[str]): 日志文件路径,默认为 ``None``。
    embed_type (Optional[str]): 嵌入类型,可选 ``'dense'`` 或 ``'sparse'``,默认为 ``'dense'``。
    trust_remote_code (bool): 是否信任远程代码,默认为 ``True``。
    port (Optional[int]): 服务端口号,默认为 ``None``,此情况下 LazyLLM 会自动生成随机端口号。

Call Arguments:
    finetuned_model (Optional[str]): 微调后的模型路径或名称。\n
    base_model (Optional[str]): 基础模型路径或名称,当 finetuned_model 无效时会使用此模型。\n

Message Format:
    输入格式为包含 text(文本)和 images(图像列表)的字典。\n
    - text (str): 需要编码的文本内容 \n
    - images (Union[str, List[str]]): 需要编码的图像列表,可选 \n


Examples:
    >>> from lazyllm import deploy
    >>> embed_service = deploy.EmbeddingDeploy(embed_type='dense')
    >>> embed_service('path/to/model')
    """
    message_format = {
        'text': 'text',  # str,
        'images': []  # Union[str, List[str]]
    }
    keys_name_handle = {
        'inputs': 'text',
        'image': 'images'
    }
    default_headers = {'Content-Type': 'application/json'}

    def __init__(self, launcher: LazyLLMLaunchersBase = None, model_type: str = 'embed', log_path: Optional[str] = None,
                 embed_type: Optional[str] = 'dense', trust_remote_code: bool = True, port: Optional[int] = None, **kw):
        super().__init__(launcher=launcher)
        self._launcher = launcher
        self._port = port
        self._model_type = model_type
        self._log_path = log_path
        self._sparse_embed = True if embed_type == 'sparse' else False
        self._trust_remote_code = trust_remote_code
        self._port = port

    def _get_model_path(self, finetuned_model=None, base_model=None):
        if not os.path.exists(finetuned_model) or \
            not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                    for filename in os.listdir(finetuned_model)):
            if not finetuned_model:
                LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                            f'base_model({base_model}) will be used')
            finetuned_model = base_model
        return finetuned_model

    def __call__(self, finetuned_model=None, base_model=None):
        finetuned_model = self._get_model_path(finetuned_model, base_model)
        if self._sparse_embed or lazyllm.config['default_embedding_engine'] == 'flagEmbedding':
            return lazyllm.deploy.RelayServer(port=self._port, func=LazyFlagEmbedding(
                finetuned_model, sparse=self._sparse_embed),
                launcher=self._launcher, log_path=self._log_path, cls='embedding')()
        else:
            return lazyllm.deploy.RelayServer(port=self._port, func=HuggingFaceEmbedding(finetuned_model),
                                              launcher=self._launcher, log_path=self._log_path, cls='embedding')()

lazyllm.components.deploy.embed.RerankDeploy

Bases: EmbeddingDeploy

此类是 EmbeddingDeploy 的子类,用于部署重排序(Rerank)服务。支持使用HuggingFace模型进行文本重排序。

Parameters:

  • launcher (launcher, default: None ) –

    启动器,默认为 None

  • model_type (str, default: 'embed' ) –

    模型类型,默认为 'embed'

  • log_path (str, default: None ) –

    日志文件路径,默认为 None

  • trust_remote_code (bool, default: True ) –

    是否信任远程代码,默认为 True

  • port (int, default: None ) –

    服务端口号,默认为 None,此情况下LazyLLM会自动生成随机端口号。

Call Arguments

finetuned_model: 微调后的模型路径或模型名称。

base_model: 基础模型路径或模型名称,当finetuned_model无效时会使用此模型。

Message Format

输入格式为包含query(查询文本)、documents(候选文档列表)和top_n(返回的文档数量)的字典。

  • query: 查询文本

  • documents: 候选文档列表

  • top_n: 返回的文档数量,默认为1

Examples:

>>> from lazyllm import deploy
>>> rerank_service = deploy.embed.RerankDeploy()
>>> rerank_service('path/to/model')
>>> input_data = {
...     "query": "What is machine learning?",
...     "documents": [
...         "Machine learning is a branch of AI.",
...         "Machine learning uses data to improve.",
...         "Deep learning is a subset of machine learning."
...     ],
...     "top_n": 2
... }
>>> result = rerank_service(input_data)
Source code in lazyllm/components/deploy/embed.py
class RerankDeploy(EmbeddingDeploy):
    """此类是 ``EmbeddingDeploy`` 的子类,用于部署重排序(Rerank)服务。支持使用HuggingFace模型进行文本重排序。

Args:
    launcher (lazyllm.launcher): 启动器,默认为 ``None``。
    model_type (str): 模型类型,默认为 ``'embed'``。
    log_path (str): 日志文件路径,默认为 ``None``。
    trust_remote_code (bool): 是否信任远程代码,默认为 ``True``。
    port (int): 服务端口号,默认为 ``None``,此情况下LazyLLM会自动生成随机端口号。

Call Arguments:
    finetuned_model: 微调后的模型路径或模型名称。

    base_model: 基础模型路径或模型名称,当finetuned_model无效时会使用此模型。


Message Format:
    输入格式为包含query(查询文本)、documents(候选文档列表)和top_n(返回的文档数量)的字典。

    - query: 查询文本

    - documents: 候选文档列表

    - top_n: 返回的文档数量,默认为1



Examples:
    >>> from lazyllm import deploy
    >>> rerank_service = deploy.embed.RerankDeploy()
    >>> rerank_service('path/to/model')
    >>> input_data = {
    ...     "query": "What is machine learning?",
    ...     "documents": [
    ...         "Machine learning is a branch of AI.",
    ...         "Machine learning uses data to improve.",
    ...         "Deep learning is a subset of machine learning."
    ...     ],
    ...     "top_n": 2
    ... }
    >>> result = rerank_service(input_data)
    """
    message_format = {'query': 'query', 'documents': ['string'], 'top_n': 1}
    keys_name_handle = {'inputs': 'query', 'documents': 'documents', 'top_n': 'top_n'}
    default_headers = {'Content-Type': 'application/json'}

    def __call__(self, finetuned_model=None, base_model=None):
        finetuned_model = self._get_model_path(finetuned_model, base_model)
        return lazyllm.deploy.RelayServer(port=self._port, func=LazyHuggingFaceRerank(
            finetuned_model), launcher=self._launcher, log_path=self._log_path, cls='embedding')()

lazyllm.components.deploy.embed.LazyHuggingFaceRerank

Bases: object

基于 HuggingFace CrossEncoder 的重排序(Rerank)封装类。
用于根据查询与候选文档的相关性分数,对文档进行排序。
支持在初始化时下载并加载指定的重排序模型,并可选择延迟加载以提升启动性能。

Parameters:

  • base_rerank (str) –

    重排序模型名称或本地路径。支持 HuggingFace Hub 模型标识符或本地路径。

  • source (Optional[str], default: None ) –

    模型来源,支持 huggingfacemodelscope,默认为全局配置项 model_source

  • init (bool, default: False ) –

    是否在实例化时立即加载模型。若为 False,将在首次调用时延迟加载。

Source code in lazyllm/components/deploy/embed.py
class LazyHuggingFaceRerank(object):
    """基于 HuggingFace CrossEncoder 的重排序(Rerank)封装类。  
用于根据查询与候选文档的相关性分数,对文档进行排序。  
支持在初始化时下载并加载指定的重排序模型,并可选择延迟加载以提升启动性能。

Args:
    base_rerank (str): 重排序模型名称或本地路径。支持 HuggingFace Hub 模型标识符或本地路径。
    source (Optional[str]): 模型来源,支持 `huggingface` 和 `modelscope`,默认为全局配置项 `model_source`。
    init (bool): 是否在实例化时立即加载模型。若为 `False`,将在首次调用时延迟加载。
"""
    def __init__(self, base_rerank, source=None, init=False):
        from ..utils.downloader import ModelManager
        source = lazyllm.config['model_source'] if not source else source
        self.base_rerank = ModelManager(source).download(base_rerank) or ''
        self.reranker = None
        self.init_flag = lazyllm.once_flag()
        if init:
            lazyllm.call_once(self.init_flag, self.load_reranker)

    def load_reranker(self):
        """加载重排序模型。  

该方法会基于 `self.base_rerank` 初始化一个 `sentence_transformers.CrossEncoder` 实例,  
并赋值给类属性 `self.reranker`,用于后续的重排序任务。  
"""
        self.reranker = sentence_transformers.CrossEncoder(self.base_rerank)

    def __call__(self, inps):
        lazyllm.call_once(self.init_flag, self.load_reranker)
        query, documents, top_n = inps['query'], inps['documents'], inps['top_n']
        query_pairs = [(query, doc) for doc in documents]
        scores = self.reranker.predict(query_pairs)
        sorted_indices = [(index, scores[index]) for index in np.argsort(scores)[::-1]]
        if top_n > 0:
            sorted_indices = sorted_indices[:top_n]
        return sorted_indices

    @classmethod
    def rebuild(cls, base_rerank, init):
        """重建 `LazyHuggingFaceRerank` 实例的类方法。  
主要用于序列化(pickle/cloudpickle)时的反序列化过程,根据提供的参数重新实例化对象。

Args:
    base_rerank (str): 模型名称或路径。
    init (bool): 是否在重建时立即加载模型。

**Returns:**

- LazyHuggingFaceRerank: 重新构建的类实例。
"""
        return cls(base_rerank, init)

    def __reduce__(self):
        init = bool(os.getenv('LAZYLLM_ON_CLOUDPICKLE', None) == 'ON' or self.init_flag)
        return LazyHuggingFaceRerank.rebuild, (self.base_rerank, init)

load_reranker()

加载重排序模型。

该方法会基于 self.base_rerank 初始化一个 sentence_transformers.CrossEncoder 实例,
并赋值给类属性 self.reranker,用于后续的重排序任务。

Source code in lazyllm/components/deploy/embed.py
    def load_reranker(self):
        """加载重排序模型。  

该方法会基于 `self.base_rerank` 初始化一个 `sentence_transformers.CrossEncoder` 实例,  
并赋值给类属性 `self.reranker`,用于后续的重排序任务。  
"""
        self.reranker = sentence_transformers.CrossEncoder(self.base_rerank)

rebuild(base_rerank, init) classmethod

重建 LazyHuggingFaceRerank 实例的类方法。
主要用于序列化(pickle/cloudpickle)时的反序列化过程,根据提供的参数重新实例化对象。

Parameters:

  • base_rerank (str) –

    模型名称或路径。

  • init (bool) –

    是否在重建时立即加载模型。

Returns:

  • LazyHuggingFaceRerank: 重新构建的类实例。
Source code in lazyllm/components/deploy/embed.py
    @classmethod
    def rebuild(cls, base_rerank, init):
        """重建 `LazyHuggingFaceRerank` 实例的类方法。  
主要用于序列化(pickle/cloudpickle)时的反序列化过程,根据提供的参数重新实例化对象。

Args:
    base_rerank (str): 模型名称或路径。
    init (bool): 是否在重建时立即加载模型。

**Returns:**

- LazyHuggingFaceRerank: 重新构建的类实例。
"""
        return cls(base_rerank, init)

lazyllm.components.deploy.embed.HuggingFaceEmbedding

HuggingFace嵌入模型管理类,用于管理和注册不同的嵌入模型实现。

属性: _model_id_mapping (dict): 模型ID到具体实现类的映射字典。

Parameters:

  • base_embed (str) –

    基础嵌入模型的路径或名称。

  • source (Optional[str], default: None ) –

    模型来源,默认为None。

Source code in lazyllm/components/deploy/embed.py
class HuggingFaceEmbedding:
    """HuggingFace嵌入模型管理类,用于管理和注册不同的嵌入模型实现。

属性:
    _model_id_mapping (dict): 模型ID到具体实现类的映射字典。

Args:
    base_embed (str): 基础嵌入模型的路径或名称。
    source (Optional[str]): 模型来源,默认为None。
"""
    _model_id_mapping = {}

    @classmethod
    def get_emb_cls(cls, model_name: str):
        """获取模型对应的嵌入实现类。

Args:
    model_name (str): 模型名称或路径。

**Returns:**

- type: 返回对应的嵌入模型实现类,如果未找到则返回默认实现LazyHuggingFaceDefaultEmbedding。
"""
        model_id = model_name.split('/')[-1].lower()
        return cls._model_id_mapping.get(model_id, LazyHuggingFaceDefaultEmbedding)

    @classmethod
    def register(cls, model_ids: List[str]):
        """注册模型ID到特定实现类的装饰器。

Args:
    model_ids (List[str]): 要注册的模型ID列表。

**Returns:**

- Callable: 返回装饰器函数。
"""
        def decorator(target_class):
            for ele in model_ids:
                cls._model_id_mapping[ele.lower()] = target_class
            return target_class
        return decorator

    def __init__(self, base_embed, source=None):
        self._embed = self.__class__.get_emb_cls(base_embed)(base_embed, source)

    def load_embed(self):
        """加载嵌入模型。

该方法会调用内部嵌入实现类的load_embed方法来加载模型。
"""
        self._embed.load_embed()

    def __call__(self, *args, **kwargs):
        try:
            args[0]['images'] = [_base64_to_file(image) if _is_base64_with_mime(image) else image
                                 for image in args[0]['images']]
        except Exception as e:
            LOG.error(f'Error converting base64 to image: {e}')
        return self._embed(*args, **kwargs)

get_emb_cls(model_name) classmethod

获取模型对应的嵌入实现类。

Parameters:

  • model_name (str) –

    模型名称或路径。

Returns:

  • type: 返回对应的嵌入模型实现类,如果未找到则返回默认实现LazyHuggingFaceDefaultEmbedding。
Source code in lazyllm/components/deploy/embed.py
    @classmethod
    def get_emb_cls(cls, model_name: str):
        """获取模型对应的嵌入实现类。

Args:
    model_name (str): 模型名称或路径。

**Returns:**

- type: 返回对应的嵌入模型实现类,如果未找到则返回默认实现LazyHuggingFaceDefaultEmbedding。
"""
        model_id = model_name.split('/')[-1].lower()
        return cls._model_id_mapping.get(model_id, LazyHuggingFaceDefaultEmbedding)

register(model_ids) classmethod

注册模型ID到特定实现类的装饰器。

Parameters:

  • model_ids (List[str]) –

    要注册的模型ID列表。

Returns:

  • Callable: 返回装饰器函数。
Source code in lazyllm/components/deploy/embed.py
    @classmethod
    def register(cls, model_ids: List[str]):
        """注册模型ID到特定实现类的装饰器。

Args:
    model_ids (List[str]): 要注册的模型ID列表。

**Returns:**

- Callable: 返回装饰器函数。
"""
        def decorator(target_class):
            for ele in model_ids:
                cls._model_id_mapping[ele.lower()] = target_class
            return target_class
        return decorator

load_embed()

加载嵌入模型。

该方法会调用内部嵌入实现类的load_embed方法来加载模型。

Source code in lazyllm/components/deploy/embed.py
    def load_embed(self):
        """加载嵌入模型。

该方法会调用内部嵌入实现类的load_embed方法来加载模型。
"""
        self._embed.load_embed()

lazyllm.components.deploy.embed.LazyFlagEmbedding

Bases: object

支持懒加载的 FlagEmbedding 嵌入模块封装。

该类包装了 FlagEmbedding 的加载和调用逻辑,提供对稀疏和稠密嵌入的支持,并通过 lazyllm.once_flag() 机制实现懒加载。适用于嵌入模型的本地/远程下载、初始化与编码流程的封装,便于与 LazyLLM 系统集成。

Parameters:

  • base_embed (str) –

    嵌入模型名称或路径。

  • sparse (bool, default: False ) –

    是否使用稀疏嵌入模式,默认为 False。

  • source (str, default: None ) –

    模型下载源,若未提供则使用 lazyllm 全局配置。

  • init (bool, default: False ) –

    是否在初始化时立即加载模型,默认为 False。

Source code in lazyllm/components/deploy/embed.py
class LazyFlagEmbedding(object):
    """支持懒加载的 FlagEmbedding 嵌入模块封装。

该类包装了 FlagEmbedding 的加载和调用逻辑,提供对稀疏和稠密嵌入的支持,并通过 lazyllm.once_flag() 机制实现懒加载。适用于嵌入模型的本地/远程下载、初始化与编码流程的封装,便于与 LazyLLM 系统集成。

Args:
    base_embed (str): 嵌入模型名称或路径。
    sparse (bool): 是否使用稀疏嵌入模式,默认为 False。
    source (str, optional): 模型下载源,若未提供则使用 lazyllm 全局配置。
    init (bool): 是否在初始化时立即加载模型,默认为 False。
"""
    def __init__(self, base_embed, sparse=False, source=None, init=False):
        from ..utils.downloader import ModelManager
        source = lazyllm.config['model_source'] if not source else source
        self.base_embed = ModelManager(source).download(base_embed) or ''
        self.embed = None
        self.device = 'cpu'
        self.sparse = sparse
        self.init_flag = lazyllm.once_flag()
        if init:
            lazyllm.call_once(self.init_flag, self.load_embed)

    def load_embed(self):
        """加载嵌入模型并初始化到设备上。

该方法根据系统是否支持 CUDA 自动选择运行设备(GPU 或 CPU),并从本地或远程加载预训练的 FlagEmbedding 模型。
"""
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        self.embed = fe.FlagAutoModel.from_finetuned(self.base_embed, use_fp16=False, devices=[self.device])

    def __call__(self, data: Dict[str, Union[str, List[str]]]):
        lazyllm.call_once(self.init_flag, self.load_embed)
        string, _ = data['text'], data['images']
        with torch.no_grad():
            model_output = self.embed.encode(string, return_sparse=self.sparse)
        if self.sparse:
            embeddings = model_output['lexical_weights']
            if isinstance(string, list):
                res = [dict(embedding) for embedding in embeddings]
            else:
                res = dict(embeddings)
        else:
            res = model_output['dense_vecs'].tolist()

        if type(string) is list and type(res) is dict:
            return json.dumps([res], default=lambda x: float(x))
        else:
            return json.dumps(res, default=lambda x: float(x))

    @classmethod
    def rebuild(cls, base_embed, sparse, init):
        """重建 LazyFlagEmbedding 实例的方法。

该类方法用于在序列化或跨进程传递时,重新构造带有初始化配置的 LazyFlagEmbedding 实例。

Args:
    base_embed (str): 嵌入模型的路径或模型名称。
    sparse (bool): 是否启用稀疏嵌入。
    init (bool): 是否在构造时立即加载模型。

**Returns:**

- LazyFlagEmbedding: 一个新的 LazyFlagEmbedding 实例。
"""
        return cls(base_embed, sparse, init=init)

    def __reduce__(self):
        init = bool(os.getenv('LAZYLLM_ON_CLOUDPICKLE', None) == 'ON' or self.init_flag)
        return LazyFlagEmbedding.rebuild, (self.base_embed, self.sparse, init)

load_embed()

加载嵌入模型并初始化到设备上。

该方法根据系统是否支持 CUDA 自动选择运行设备(GPU 或 CPU),并从本地或远程加载预训练的 FlagEmbedding 模型。

Source code in lazyllm/components/deploy/embed.py
    def load_embed(self):
        """加载嵌入模型并初始化到设备上。

该方法根据系统是否支持 CUDA 自动选择运行设备(GPU 或 CPU),并从本地或远程加载预训练的 FlagEmbedding 模型。
"""
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        self.embed = fe.FlagAutoModel.from_finetuned(self.base_embed, use_fp16=False, devices=[self.device])

rebuild(base_embed, sparse, init) classmethod

重建 LazyFlagEmbedding 实例的方法。

该类方法用于在序列化或跨进程传递时,重新构造带有初始化配置的 LazyFlagEmbedding 实例。

Parameters:

  • base_embed (str) –

    嵌入模型的路径或模型名称。

  • sparse (bool) –

    是否启用稀疏嵌入。

  • init (bool) –

    是否在构造时立即加载模型。

Returns:

  • LazyFlagEmbedding: 一个新的 LazyFlagEmbedding 实例。
Source code in lazyllm/components/deploy/embed.py
    @classmethod
    def rebuild(cls, base_embed, sparse, init):
        """重建 LazyFlagEmbedding 实例的方法。

该类方法用于在序列化或跨进程传递时,重新构造带有初始化配置的 LazyFlagEmbedding 实例。

Args:
    base_embed (str): 嵌入模型的路径或模型名称。
    sparse (bool): 是否启用稀疏嵌入。
    init (bool): 是否在构造时立即加载模型。

**Returns:**

- LazyFlagEmbedding: 一个新的 LazyFlagEmbedding 实例。
"""
        return cls(base_embed, sparse, init=init)

lazyllm.components.deploy.Mindie

Bases: LazyLLMDeployBase

此类是 LazyLLMDeployBase 的一个子类, 用于部署和管理MindIE大模型推理服务。它封装了MindIE服务的配置生成、进程启动和API交互的全流程。

Parameters:

  • trust_remote_code (bool, default: True ) –

    是否信任远程代码(如HuggingFace模型)。默认为 True

  • launcher

    任务启动器实例,默认为 launchers.remote()

  • log_path (str, default: None ) –

    日志保存路径,若为 None 则不保存日志。

  • **kw

    其他配置参数

Other Parameters:

  • npuDeviceIds

    NPU设备ID列表(如 [[0,1]] 表示使用2张卡)

  • worldSize

    模型并行数量

  • port

    服务端口(设为 'auto' 时自动分配30000-40000的随机端口)

  • maxSeqLen

    最大序列长度

  • maxInputTokenLen

    单次输入最大token数

  • maxPrefillTokens

    预填充token上限

  • config

    自定义配置文件

Notes : 必须预先设置环境变量 LAZYLLM_MINDIE_HOME 指向MindIE安装目录, 若未指定 finetuned_model 或路径无效,会自动回退到 base_model

Examples:

>>> import lazyllm
>>> from lazyllm.components.deploy import Mindie            
>>> deployer = Mindie(
...     port=30000,
...     launcher=lazyllm.launchers.remote(),
...     max_seq_len=32000,
...     log_path="/path/to/logs"
... )
>>> cmd = deployer.cmd(
...     finetuned_model="/path/to/finetuned_model",
...     base_model="/path/to/base_model")
>>> print("Service URL:", cmd.geturl())
Source code in lazyllm/components/deploy/mindie.py
class Mindie(LazyLLMDeployBase):
    """此类是 ``LazyLLMDeployBase`` 的一个子类, 用于部署和管理MindIE大模型推理服务。它封装了MindIE服务的配置生成、进程启动和API交互的全流程。

Args:
    trust_remote_code (bool): 是否信任远程代码(如HuggingFace模型)。默认为 ``True``。
    launcher: 任务启动器实例,默认为 ``launchers.remote()``。
    log_path (str): 日志保存路径,若为 ``None`` 则不保存日志。
    **kw: 其他配置参数

Keyword Args: 
            npuDeviceIds: NPU设备ID列表(如 ``[[0,1]]`` 表示使用2张卡)
            worldSize: 模型并行数量
            port: 服务端口(设为 ``'auto'`` 时自动分配30000-40000的随机端口)
            maxSeqLen: 最大序列长度
            maxInputTokenLen: 单次输入最大token数
            maxPrefillTokens: 预填充token上限
            config: 自定义配置文件

Notes
                : 
   必须预先设置环境变量 ``LAZYLLM_MINDIE_HOME`` 指向MindIE安装目录, 若未指定 ``finetuned_model`` 或路径无效,会自动回退到 ``base_model``


Examples:
    >>> import lazyllm
    >>> from lazyllm.components.deploy import Mindie            
    >>> deployer = Mindie(
    ...     port=30000,
    ...     launcher=lazyllm.launchers.remote(),
    ...     max_seq_len=32000,
    ...     log_path="/path/to/logs"
    ... )
    >>> cmd = deployer.cmd(
    ...     finetuned_model="/path/to/finetuned_model",
    ...     base_model="/path/to/base_model")
    >>> print("Service URL:", cmd.geturl())

    """
    keys_name_handle = {
        'inputs': 'prompt',
    }
    default_headers = {'Content-Type': 'application/json'}
    message_format = {
        'prompt': 'Who are you ?',
        'stream': False,
        'max_tokens': 4096,
        'presence_penalty': 1.03,
        'frequency_penalty': 1.0,
        'temperature': 0.5,
        'top_p': 0.95
    }
    auto_map = {
        'port': int,
        'tp': ('worldSize', int),
        'max_input_token_len': ('maxInputTokenLen', int),
        'max_prefill_tokens': ('maxPrefillTokens', int),
        'max_seq_len': ('maxSeqLen', int)
    }

    def __init__(self, trust_remote_code=True, launcher=launchers.remote(), log_path=None, **kw):  # noqa B008
        super().__init__(launcher=launcher)
        assert lazyllm.config['mindie_home'], 'Ensure you have installed MindIE and \
                                  "export LAZYLLM_MINDIE_HOME=/path/to/mindie/latest"'
        self.mindie_home = lazyllm.config['mindie_home']
        self.mindie_config_path = os.path.join(self.mindie_home, 'mindie-service/conf/config.json')
        self.backup_path = self.mindie_config_path + '.backup'
        self.custom_config = kw.pop('config', None)
        self.kw = ArgsDict({
            'npuDeviceIds': [[0]],
            'worldSize': 1,
            'port': 'auto',
            'host': '0.0.0.0',
            'maxSeqLen': 64000,
            'maxInputTokenLen': 4096,
            'maxPrefillTokens': 8192,
        })
        self.trust_remote_code = trust_remote_code
        self.options_keys = kw.pop('options_keys', [])
        assert len(self.options_keys) == 0, 'options_keys is not supported'
        kw.update({'skip_check': False})
        self.kw.check_and_update(kw)
        self.kw['npuDeviceIds'] = [[i for i in range(self.kw.get('worldSize', 1))]]
        self.random_port = False if 'port' in kw and kw['port'] and kw['port'] != 'auto' else True
        self.temp_folder = make_log_dir(log_path, 'mindie') if log_path else None

        if self.custom_config:
            self.config_dict = (ArgsDict(self.load_config(self.custom_config))
                                if isinstance(self.custom_config, str) else ArgsDict(self.custom_config))
            self.kw['host'] = self.config_dict['ServerConfig']['ipAddress']
            self.kw['port'] = self.config_dict['ServerConfig']['port']
        else:
            default_config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mindie', 'config.json')
            self.config_dict = ArgsDict(self.load_config(default_config_path))

    def __del__(self):
        if hasattr(self, 'backup_path') and os.path.isfile(self.backup_path):
            shutil.copy2(self.backup_path, self.mindie_config_path)

    def load_config(self, config_path):
        """加载并解析MindIE配置文件。

Args:
    config_path (str): JSON配置文件的路径

**Returns:**

- dict: 解析后的配置字典

注意事项:
    - 处理默认和自定义配置文件
    - 使用JSON格式配置
    - 修改前会创建原始配置的备份
"""
        with open(config_path, 'r') as file:
            config_dict = json.load(file)
        return config_dict

    def save_config(self):
        """保存当前配置到文件。

注意事项:
    - 自动创建现有配置的备份
    - 写入到标准MindIE配置位置
    - 使用带缩进的JSON格式
    - 部署时自动调用
"""
        if os.path.isfile(self.mindie_config_path):
            shutil.copy2(self.mindie_config_path, self.backup_path)

        with open(self.mindie_config_path, 'w') as file:
            json.dump(self.config_dict, file)

    def update_config(self):
        """使用当前设置更新配置字典。

注意事项:
    - 处理多个配置部分:
        - 模型部署参数
        - 服务器设置
        - 调度参数
"""
        backend_config = self.config_dict['BackendConfig']
        backend_config['npuDeviceIds'] = self.kw['npuDeviceIds']
        model_config = {
            'modelName': self.finetuned_model.split('/')[-1],
            'modelWeightPath': self.finetuned_model,
            'worldSize': self.kw['worldSize'],
            'trust_remote_code': self.trust_remote_code
        }
        backend_config['ModelDeployConfig']['ModelConfig'][0].update(model_config)
        backend_config['ModelDeployConfig']['maxSeqLen'] = self.kw['maxSeqLen']
        backend_config['ModelDeployConfig']['maxInputTokenLen'] = self.kw['maxInputTokenLen']
        backend_config['ScheduleConfig']['maxPrefillTokens'] = self.kw['maxPrefillTokens']
        self.config_dict['BackendConfig'] = backend_config
        if self.kw['host'] != '0.0.0.0':
            self.config_dict['ServerConfig']['ipAddress'] = self.kw['host']
        self.config_dict['ServerConfig']['port'] = self.kw['port']

    def cmd(self, finetuned_model=None, base_model=None, master_ip=None):
        """生成启动MindIE服务的命令。

Args:
    finetuned_model (str): 微调模型路径
    base_model (str): 基础模型路径(当微调模型无效时作为后备)
    master_ip (str): 主节点IP地址(当前未使用)

**Returns:**

- LazyLLMCMD: 启动服务的命令对象

注意事项:
    - 自动处理模型路径验证
    - 启动服务前更新配置
    - 支持配置随机端口分配
"""
        if self.custom_config is None:
            self.finetuned_model = finetuned_model
            if finetuned_model or base_model:
                if not os.path.exists(finetuned_model) or \
                    not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                            for filename in os.listdir(finetuned_model)):
                    if not finetuned_model:
                        LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                                    f'base_model({base_model}) will be used')
                    self.finetuned_model = base_model

            if self.random_port:
                self.kw['port'] = random.randint(30000, 40000)

            self.update_config()

        self.save_config()

        def impl():
            cmd = f'{os.path.join(self.mindie_home, "mindie-service/bin/mindieservice_daemon")}'
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl, checkf=verify_fastapi_func)

    def geturl(self, job=None):
        """获取部署后的服务URL。

Args:
    job: 任务对象(可选,默认为self.job)

**Returns:**

- str: generate接口的URL

注意事项:
    - 根据显示模式返回不同格式
    - 包含配置中的端口号
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return f'http://{job.get_jobip()}:{self.kw["port"]}/generate'
        else:
            LOG.info(f'MindIE Server running on http://{job.get_jobip()}:{self.kw["port"]}')
            return f'http://{job.get_jobip()}:{self.kw["port"]}/generate'

    @staticmethod
    def extract_result(x, inputs):
        """从API响应中提取生成的文本。

Args:
    x: 原始API响应
    inputs: 原始输入(未使用)

**Returns:**

- str: 生成的文本

注意事项:
    - 解析JSON响应
    - 返回响应中的第一个文本条目
"""
        return json.loads(x)['text'][0]

cmd(finetuned_model=None, base_model=None, master_ip=None)

生成启动MindIE服务的命令。

Parameters:

  • finetuned_model (str, default: None ) –

    微调模型路径

  • base_model (str, default: None ) –

    基础模型路径(当微调模型无效时作为后备)

  • master_ip (str, default: None ) –

    主节点IP地址(当前未使用)

Returns:

  • LazyLLMCMD: 启动服务的命令对象
注意事项
  • 自动处理模型路径验证
  • 启动服务前更新配置
  • 支持配置随机端口分配
Source code in lazyllm/components/deploy/mindie.py
    def cmd(self, finetuned_model=None, base_model=None, master_ip=None):
        """生成启动MindIE服务的命令。

Args:
    finetuned_model (str): 微调模型路径
    base_model (str): 基础模型路径(当微调模型无效时作为后备)
    master_ip (str): 主节点IP地址(当前未使用)

**Returns:**

- LazyLLMCMD: 启动服务的命令对象

注意事项:
    - 自动处理模型路径验证
    - 启动服务前更新配置
    - 支持配置随机端口分配
"""
        if self.custom_config is None:
            self.finetuned_model = finetuned_model
            if finetuned_model or base_model:
                if not os.path.exists(finetuned_model) or \
                    not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                            for filename in os.listdir(finetuned_model)):
                    if not finetuned_model:
                        LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                                    f'base_model({base_model}) will be used')
                    self.finetuned_model = base_model

            if self.random_port:
                self.kw['port'] = random.randint(30000, 40000)

            self.update_config()

        self.save_config()

        def impl():
            cmd = f'{os.path.join(self.mindie_home, "mindie-service/bin/mindieservice_daemon")}'
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl, checkf=verify_fastapi_func)

extract_result(x, inputs) staticmethod

从API响应中提取生成的文本。

Parameters:

  • x

    原始API响应

  • inputs

    原始输入(未使用)

Returns:

  • str: 生成的文本
注意事项
  • 解析JSON响应
  • 返回响应中的第一个文本条目
Source code in lazyllm/components/deploy/mindie.py
    @staticmethod
    def extract_result(x, inputs):
        """从API响应中提取生成的文本。

Args:
    x: 原始API响应
    inputs: 原始输入(未使用)

**Returns:**

- str: 生成的文本

注意事项:
    - 解析JSON响应
    - 返回响应中的第一个文本条目
"""
        return json.loads(x)['text'][0]

geturl(job=None)

获取部署后的服务URL。

Parameters:

  • job

    任务对象(可选,默认为self.job)

Returns:

  • str: generate接口的URL
注意事项
  • 根据显示模式返回不同格式
  • 包含配置中的端口号
Source code in lazyllm/components/deploy/mindie.py
    def geturl(self, job=None):
        """获取部署后的服务URL。

Args:
    job: 任务对象(可选,默认为self.job)

**Returns:**

- str: generate接口的URL

注意事项:
    - 根据显示模式返回不同格式
    - 包含配置中的端口号
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return f'http://{job.get_jobip()}:{self.kw["port"]}/generate'
        else:
            LOG.info(f'MindIE Server running on http://{job.get_jobip()}:{self.kw["port"]}')
            return f'http://{job.get_jobip()}:{self.kw["port"]}/generate'

load_config(config_path)

加载并解析MindIE配置文件。

Parameters:

  • config_path (str) –

    JSON配置文件的路径

Returns:

  • dict: 解析后的配置字典
注意事项
  • 处理默认和自定义配置文件
  • 使用JSON格式配置
  • 修改前会创建原始配置的备份
Source code in lazyllm/components/deploy/mindie.py
    def load_config(self, config_path):
        """加载并解析MindIE配置文件。

Args:
    config_path (str): JSON配置文件的路径

**Returns:**

- dict: 解析后的配置字典

注意事项:
    - 处理默认和自定义配置文件
    - 使用JSON格式配置
    - 修改前会创建原始配置的备份
"""
        with open(config_path, 'r') as file:
            config_dict = json.load(file)
        return config_dict

save_config()

保存当前配置到文件。

注意事项
  • 自动创建现有配置的备份
  • 写入到标准MindIE配置位置
  • 使用带缩进的JSON格式
  • 部署时自动调用
Source code in lazyllm/components/deploy/mindie.py
    def save_config(self):
        """保存当前配置到文件。

注意事项:
    - 自动创建现有配置的备份
    - 写入到标准MindIE配置位置
    - 使用带缩进的JSON格式
    - 部署时自动调用
"""
        if os.path.isfile(self.mindie_config_path):
            shutil.copy2(self.mindie_config_path, self.backup_path)

        with open(self.mindie_config_path, 'w') as file:
            json.dump(self.config_dict, file)

update_config()

使用当前设置更新配置字典。

注意事项
  • 处理多个配置部分:
    • 模型部署参数
    • 服务器设置
    • 调度参数
Source code in lazyllm/components/deploy/mindie.py
    def update_config(self):
        """使用当前设置更新配置字典。

注意事项:
    - 处理多个配置部分:
        - 模型部署参数
        - 服务器设置
        - 调度参数
"""
        backend_config = self.config_dict['BackendConfig']
        backend_config['npuDeviceIds'] = self.kw['npuDeviceIds']
        model_config = {
            'modelName': self.finetuned_model.split('/')[-1],
            'modelWeightPath': self.finetuned_model,
            'worldSize': self.kw['worldSize'],
            'trust_remote_code': self.trust_remote_code
        }
        backend_config['ModelDeployConfig']['ModelConfig'][0].update(model_config)
        backend_config['ModelDeployConfig']['maxSeqLen'] = self.kw['maxSeqLen']
        backend_config['ModelDeployConfig']['maxInputTokenLen'] = self.kw['maxInputTokenLen']
        backend_config['ScheduleConfig']['maxPrefillTokens'] = self.kw['maxPrefillTokens']
        self.config_dict['BackendConfig'] = backend_config
        if self.kw['host'] != '0.0.0.0':
            self.config_dict['ServerConfig']['ipAddress'] = self.kw['host']
        self.config_dict['ServerConfig']['port'] = self.kw['port']

lazyllm.components.deploy.OCRDeploy

Bases: LazyLLMDeployBase

OCRDeploy 是 LazyLLMDeployBase 的子类,用于部署 OCR(光学字符识别)模型。 此类支持额外的配置,例如日志记录、远程代码信任以及端口自定义。

属性:

keys_name_handle: 一个字典,用于将输入键映射到相应的处理键。例如:
    - "inputs": 处理一般输入。
    - "ocr_files": 同样映射到 "inputs"。
message_format: 一个字典,指定模型期望的消息格式。例如:
    - {"inputs": "/path/to/pdf"} 表示模型需要一个 PDF 文件路径作为输入。
default_headers: 一个字典,指定 API 请求的默认头部。默认为:
    - {"Content-Type": "application/json"}

Parameters:

  • launcher

    启动器实例,用于部署模型。默认为 None

  • log_path

    字符串,指定日志保存的路径。默认为 None

  • trust_remote_code

    布尔值,指示是否信任远程代码执行。默认为 True

  • port

    整数,指定部署服务器的端口号。默认为 None

Returns:

  • OCRDeploy实例,可通过调用方式启动服务

Examples:

>>> from lazyllm.components import OCRDeploy
>>> from lazyllm import launchers
>>> # 创建一个 OCRDeploy 实例
>>> deployer = OCRDeploy(launcher=launchers.local(), log_path='./logs', port=8080)
>>> # 使用微调的 OCR 模型部署服务器
>>> server = deployer(finetuned_model='ocr-model')
>>> # 打印部署服务器信息
>>> print(server)
... <RelayServer instance ready to handle OCR requests>
Source code in lazyllm/components/deploy/ocr/pp_ocr.py
class OCRDeploy(LazyLLMDeployBase):
    """OCRDeploy 是 [LazyLLMDeployBase][lazyllm.components.LazyLLMDeployBase] 的子类,用于部署 OCR(光学字符识别)模型。
此类支持额外的配置,例如日志记录、远程代码信任以及端口自定义。

属性:

    keys_name_handle: 一个字典,用于将输入键映射到相应的处理键。例如:
        - "inputs": 处理一般输入。
        - "ocr_files": 同样映射到 "inputs"。
    message_format: 一个字典,指定模型期望的消息格式。例如:
        - {"inputs": "/path/to/pdf"} 表示模型需要一个 PDF 文件路径作为输入。
    default_headers: 一个字典,指定 API 请求的默认头部。默认为:
        - {"Content-Type": "application/json"}

Args:
    launcher: 启动器实例,用于部署模型。默认为 `None`。
    log_path: 字符串,指定日志保存的路径。默认为 `None`。
    trust_remote_code: 布尔值,指示是否信任远程代码执行。默认为 `True`。
    port: 整数,指定部署服务器的端口号。默认为 `None`。

Returns:
    OCRDeploy实例,可通过调用方式启动服务


Examples:
    >>> from lazyllm.components import OCRDeploy
    >>> from lazyllm import launchers
    >>> # 创建一个 OCRDeploy 实例
    >>> deployer = OCRDeploy(launcher=launchers.local(), log_path='./logs', port=8080)
    >>> # 使用微调的 OCR 模型部署服务器
    >>> server = deployer(finetuned_model='ocr-model')
    >>> # 打印部署服务器信息
    >>> print(server)
    ... <RelayServer instance ready to handle OCR requests>
    """
    keys_name_handle = {
        'inputs': 'inputs',
        'ocr_files': 'inputs',
    }
    message_format = {'inputs': '/path/to/pdf'}
    default_headers = {'Content-Type': 'application/json'}

    def __init__(self, launcher=None, log_path=None, trust_remote_code=True, port=None, **kw):
        super().__init__(launcher=launcher)
        self._log_path = log_path
        self._trust_remote_code = trust_remote_code
        self._port = port

    def __call__(self, finetuned_model=None, base_model=None):
        if not finetuned_model:
            finetuned_model = base_model
        return lazyllm.deploy.RelayServer(
            port=self._port, func=_OCR(finetuned_model), launcher=self._launcher, log_path=self._log_path, cls='ocr')()

lazyllm.components.deploy.Infinity

Bases: LazyLLMDeployBase

此类是 LazyLLMDeployBase 的子类,基于 Infinity 框架提供的高性能文本嵌入、重排序和CLIP等能力。

Parameters:

  • launcher (launcher, default: remote(ngpus=1) ) –

    Infinity 的启动器,默认为 launchers.remote(ngpus=1)

  • kw

    关键字参数,用于更新默认的训练参数。请注意,除了以下列出的关键字参数外,这里不能传入额外的关键字参数。

此类的关键字参数及其默认值如下:

Other Parameters:

  • launcher (Launcher) –

    启动器配置,默认为remote(ngpus=1)。

  • model_type (str) –

    模型类型,默认为'embed'。

  • log_path (str) –

    日志文件路径,默认为None。

  • **kw

    额外的配置参数,包括host、port、batch-size等。

Examples:

>>> import lazyllm
>>> from lazyllm import deploy
>>> deploy.Infinity()
<lazyllm.llm.deploy type=Infinity>
Source code in lazyllm/components/deploy/infinity.py
class Infinity(LazyLLMDeployBase):
    """此类是 ``LazyLLMDeployBase`` 的子类,基于 [Infinity](https://github.com/michaelfeil/infinity) 框架提供的高性能文本嵌入、重排序和CLIP等能力。

Args:
    launcher (lazyllm.launcher): Infinity 的启动器,默认为 ``launchers.remote(ngpus=1)``。
    kw: 关键字参数,用于更新默认的训练参数。请注意,除了以下列出的关键字参数外,这里不能传入额外的关键字参数。

此类的关键字参数及其默认值如下:

Keyword Args: 
    launcher (Launcher, optional): 启动器配置,默认为remote(ngpus=1)。
    model_type (str, optional): 模型类型,默认为'embed'。
    log_path (str, optional): 日志文件路径,默认为None。
    **kw: 额外的配置参数,包括host、port、batch-size等。



Examples:
    >>> import lazyllm
    >>> from lazyllm import deploy
    >>> deploy.Infinity()
    <lazyllm.llm.deploy type=Infinity>
    """
    keys_name_handle = {
        'inputs': 'input',
    }
    message_format = {
        'input': 'who are you ?',
    }
    default_headers = {'Content-Type': 'application/json'}
    target_name = 'embeddings'

    def __init__(self, launcher=launchers.remote(ngpus=1), model_type='embed', log_path=None, **kw):  # noqa B008
        super().__init__(launcher=launcher)
        self.kw = ArgsDict({
            'host': '0.0.0.0',
            'port': None,
            'batch-size': 256,
        })
        self._model_type = model_type
        kw.pop('stream', '')
        # Infinity (embedding model) doesn't support tensor parallel, ignore 'tp' parameter
        kw.pop('tp', None)
        self.options_keys = kw.pop('options_keys', [])
        self.kw.check_and_update(kw)
        self.random_port = False if 'port' in kw and kw['port'] else True
        self.temp_folder = make_log_dir(log_path, 'lmdeploy') if log_path else None

    def cmd(self, finetuned_model=None, base_model=None):
        if not os.path.exists(finetuned_model) or \
            not any(filename.endswith('.bin') or filename.endswith('.safetensors')
                    for filename in os.listdir(finetuned_model)):
            if not finetuned_model:
                LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                            f'base_model({base_model}) will be used')
            finetuned_model = base_model

        def impl():
            if self.random_port:
                self.kw['port'] = random.randint(30000, 40000)
            cmd = f'infinity_emb v2 --model-id {finetuned_model} --no-bettertransformer '
            if isinstance(self._launcher, launchers.EmptyLauncher) and self._launcher.ngpus:
                available_gpus = self._launcher._get_idle_gpus()
                required_count = self._launcher.ngpus
                if required_count <= len(available_gpus):
                    try:
                        use_cuda_visible = lazyllm.config['cuda_visible']
                    except (KeyError, AttributeError):
                        use_cuda_visible = False
                    if use_cuda_visible:
                        # Use logical GPU IDs (0, 1, 2...) when CUDA_VISIBLE_DEVICES is set
                        gpu_ids = ','.join(map(str, range(required_count)))
                    else:
                        # Use physical GPU IDs when CUDA_VISIBLE_DEVICES is not set
                        gpu_ids = ','.join(map(str, available_gpus[:required_count]))
                    cmd += f'--device-id={gpu_ids} '
                else:
                    raise RuntimeError(
                        f'Insufficient GPUs available (required: {required_count}, '
                        f'available: {len(available_gpus)})'
                    )
            cmd += self.kw.parse_kwargs()
            cmd += ' ' + parse_options_keys(self.options_keys)
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl, checkf=verify_fastapi_func)

    def geturl(self, job=None):
        """获取Infinity服务的URL地址。根据部署模式和作业状态,返回对应的API访问URL地址。

Args:
    job (Optional[Any]): 作业对象,如果为None则使用当前实例的job属性。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return f'http://<ip>:<port>/{self.target_name}'
        else:
            return f'http://{job.get_jobip()}:{self.kw["port"]}/{self.target_name}'

    @staticmethod
    def extract_result(x, inputs):
        """从Infinity API响应中提取结果数据。
解析Infinity服务的JSON响应,根据返回的对象类型提取嵌入向量或重排序结果。

Args:
    x (str): API返回的JSON字符串响应。
    inputs (Dict): 原始输入数据,用于确定返回结果的格式。
"""
        try:
            res_object = json.loads(x)
        except Exception as e:
            LOG.warning(f'JSONDecodeError on load {x}')
            raise e
        assert 'object' in res_object
        object_type = res_object['object']
        if object_type == 'list':  # for infinity >= 0.0.64
            object_type = res_object['data'][0]['object']
        if object_type == 'embedding':
            res_list = [item['embedding'] for item in res_object['data']]
            if len(res_list) == 1 and type(inputs['input']) is str:
                res_list = res_list[0]
            return json.dumps(res_list)
        elif object_type == 'rerank':
            return [(x['index'], x['relevance_score']) for x in res_object['results']]

extract_result(x, inputs) staticmethod

从Infinity API响应中提取结果数据。 解析Infinity服务的JSON响应,根据返回的对象类型提取嵌入向量或重排序结果。

Parameters:

  • x (str) –

    API返回的JSON字符串响应。

  • inputs (Dict) –

    原始输入数据,用于确定返回结果的格式。

Source code in lazyllm/components/deploy/infinity.py
    @staticmethod
    def extract_result(x, inputs):
        """从Infinity API响应中提取结果数据。
解析Infinity服务的JSON响应,根据返回的对象类型提取嵌入向量或重排序结果。

Args:
    x (str): API返回的JSON字符串响应。
    inputs (Dict): 原始输入数据,用于确定返回结果的格式。
"""
        try:
            res_object = json.loads(x)
        except Exception as e:
            LOG.warning(f'JSONDecodeError on load {x}')
            raise e
        assert 'object' in res_object
        object_type = res_object['object']
        if object_type == 'list':  # for infinity >= 0.0.64
            object_type = res_object['data'][0]['object']
        if object_type == 'embedding':
            res_list = [item['embedding'] for item in res_object['data']]
            if len(res_list) == 1 and type(inputs['input']) is str:
                res_list = res_list[0]
            return json.dumps(res_list)
        elif object_type == 'rerank':
            return [(x['index'], x['relevance_score']) for x in res_object['results']]

geturl(job=None)

获取Infinity服务的URL地址。根据部署模式和作业状态,返回对应的API访问URL地址。

Parameters:

  • job (Optional[Any], default: None ) –

    作业对象,如果为None则使用当前实例的job属性。

Source code in lazyllm/components/deploy/infinity.py
    def geturl(self, job=None):
        """获取Infinity服务的URL地址。根据部署模式和作业状态,返回对应的API访问URL地址。

Args:
    job (Optional[Any]): 作业对象,如果为None则使用当前实例的job属性。
"""
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return f'http://<ip>:<port>/{self.target_name}'
        else:
            return f'http://{job.get_jobip()}:{self.kw["port"]}/{self.target_name}'

lazyllm.components.deploy.relay.base.RelayServer

Bases: LazyLLMDeployBase

Source code in lazyllm/components/deploy/relay/base.py
class RelayServer(LazyLLMDeployBase):
    keys_name_handle = None
    default_headers = {'Content-Type': 'application/json'}
    message_format = None

    def __init__(self, port=None, *, func=None, pre_func=None, post_func=None, pythonpath=None,
                 log_path=None, cls=None, launcher=launchers.remote(sync=False), num_replicas: int = 1,  # noqa B008
                 security_key: Optional[str] = None, defined_pos: Optional[str] = None):
        # func must dump in __call__ to wait for dependancies.
        self._func = func
        self._pre = dump_obj(pre_func)
        self._post = dump_obj(post_func)
        self._port, self._real_port = port, None
        self._pythonpath = pythonpath
        self._num_replicas = num_replicas
        self._security_key = security_key
        self._defined_pos = defined_pos
        super().__init__(launcher=launcher)
        self.temp_folder = make_log_dir(log_path, cls or 'relay') if log_path else None

    def cmd(self, func=None):
        FastapiApp.update()
        self._func = dump_obj(func or self._func)
        folder_path = os.path.dirname(os.path.abspath(__file__))
        run_file_path = os.path.join(folder_path, 'server.py')

        def impl():
            self._real_port = self._port if self._port else random.randint(30000, 40000)
            cmd = f'{sys.executable} {run_file_path} --open_port={self._real_port} --function="{self._func}" '
            if self._pre:
                cmd += f'--before_function="{self._pre}" '
            if self._post:
                cmd += f'--after_function="{self._post}" '
            if self._pythonpath:
                cmd += f'--pythonpath="{self._pythonpath}" '
            if self._num_replicas > 1 and config['use_ray']:
                cmd += f'--num_replicas={self._num_replicas}'
            if self._security_key:
                cmd += f'--security_key="{self._security_key}" '
            if self._defined_pos:
                cmd += '--defined_pos="{}" '.format(dump_obj(self._defined_pos.replace('"', r'\"')))
            if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
            return cmd

        return LazyLLMCMD(cmd=impl, return_value=self.geturl,
                          checkf=verify_ray_func if config['use_ray'] else verify_fastapi_func,
                          no_displays=['function', 'before_function', 'after_function', 'security_key', 'defined_pos'])

    def geturl(self, job=None):
        if job is None:
            job = self.job
        return f'http://{job.get_jobip()}:{self._real_port}/generate'

lazyllm.components.deploy.OCRDeploy

Bases: LazyLLMDeployBase

OCRDeploy 是 LazyLLMDeployBase 的子类,用于部署 OCR(光学字符识别)模型。 此类支持额外的配置,例如日志记录、远程代码信任以及端口自定义。

属性:

keys_name_handle: 一个字典,用于将输入键映射到相应的处理键。例如:
    - "inputs": 处理一般输入。
    - "ocr_files": 同样映射到 "inputs"。
message_format: 一个字典,指定模型期望的消息格式。例如:
    - {"inputs": "/path/to/pdf"} 表示模型需要一个 PDF 文件路径作为输入。
default_headers: 一个字典,指定 API 请求的默认头部。默认为:
    - {"Content-Type": "application/json"}

Parameters:

  • launcher

    启动器实例,用于部署模型。默认为 None

  • log_path

    字符串,指定日志保存的路径。默认为 None

  • trust_remote_code

    布尔值,指示是否信任远程代码执行。默认为 True

  • port

    整数,指定部署服务器的端口号。默认为 None

Returns:

  • OCRDeploy实例,可通过调用方式启动服务

Examples:

>>> from lazyllm.components import OCRDeploy
>>> from lazyllm import launchers
>>> # 创建一个 OCRDeploy 实例
>>> deployer = OCRDeploy(launcher=launchers.local(), log_path='./logs', port=8080)
>>> # 使用微调的 OCR 模型部署服务器
>>> server = deployer(finetuned_model='ocr-model')
>>> # 打印部署服务器信息
>>> print(server)
... <RelayServer instance ready to handle OCR requests>
Source code in lazyllm/components/deploy/ocr/pp_ocr.py
class OCRDeploy(LazyLLMDeployBase):
    """OCRDeploy 是 [LazyLLMDeployBase][lazyllm.components.LazyLLMDeployBase] 的子类,用于部署 OCR(光学字符识别)模型。
此类支持额外的配置,例如日志记录、远程代码信任以及端口自定义。

属性:

    keys_name_handle: 一个字典,用于将输入键映射到相应的处理键。例如:
        - "inputs": 处理一般输入。
        - "ocr_files": 同样映射到 "inputs"。
    message_format: 一个字典,指定模型期望的消息格式。例如:
        - {"inputs": "/path/to/pdf"} 表示模型需要一个 PDF 文件路径作为输入。
    default_headers: 一个字典,指定 API 请求的默认头部。默认为:
        - {"Content-Type": "application/json"}

Args:
    launcher: 启动器实例,用于部署模型。默认为 `None`。
    log_path: 字符串,指定日志保存的路径。默认为 `None`。
    trust_remote_code: 布尔值,指示是否信任远程代码执行。默认为 `True`。
    port: 整数,指定部署服务器的端口号。默认为 `None`。

Returns:
    OCRDeploy实例,可通过调用方式启动服务


Examples:
    >>> from lazyllm.components import OCRDeploy
    >>> from lazyllm import launchers
    >>> # 创建一个 OCRDeploy 实例
    >>> deployer = OCRDeploy(launcher=launchers.local(), log_path='./logs', port=8080)
    >>> # 使用微调的 OCR 模型部署服务器
    >>> server = deployer(finetuned_model='ocr-model')
    >>> # 打印部署服务器信息
    >>> print(server)
    ... <RelayServer instance ready to handle OCR requests>
    """
    keys_name_handle = {
        'inputs': 'inputs',
        'ocr_files': 'inputs',
    }
    message_format = {'inputs': '/path/to/pdf'}
    default_headers = {'Content-Type': 'application/json'}

    def __init__(self, launcher=None, log_path=None, trust_remote_code=True, port=None, **kw):
        super().__init__(launcher=launcher)
        self._log_path = log_path
        self._trust_remote_code = trust_remote_code
        self._port = port

    def __call__(self, finetuned_model=None, base_model=None):
        if not finetuned_model:
            finetuned_model = base_model
        return lazyllm.deploy.RelayServer(
            port=self._port, func=_OCR(finetuned_model), launcher=self._launcher, log_path=self._log_path, cls='ocr')()

lazyllm.components.deploy.text_to_speech.utils.TTSBase

Bases: LazyLLMDeployBase

TTS(文本转语音)服务的基类。

提供文本转语音服务的部署基础框架,支持模型加载和RelayServer部署。

Parameters:

  • launcher (LazyLLMLaunchersBase, default: None ) –

    任务启动器

  • log_path (str, default: None ) –

    日志文件路径

  • port (int, default: None ) –

    服务端口号

Source code in lazyllm/components/deploy/text_to_speech/utils.py
class TTSBase(LazyLLMDeployBase):
    """TTS(文本转语音)服务的基类。

提供文本转语音服务的部署基础框架,支持模型加载和RelayServer部署。

Args:
    launcher (LazyLLMLaunchersBase, optional): 任务启动器
    log_path (str, optional): 日志文件路径
    port (int, optional): 服务端口号
"""
    func = None

    def __init__(self, launcher: LazyLLMLaunchersBase = None,
                 log_path: Optional[str] = None, port: Optional[int] = None, **kw):
        super().__init__(launcher=launcher)
        self._log_path = log_path
        self._port = port

    def __call__(self, finetuned_model=None, base_model=None):
        if not finetuned_model:
            finetuned_model = base_model
        elif not os.path.exists(finetuned_model) or \
            not any(file.endswith(('.bin', '.safetensors'))
                    for _, _, filenames in os.walk(finetuned_model) for file in filenames):
            LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                        f'base_model({base_model}) will be used')
            finetuned_model = base_model
        return lazyllm.deploy.RelayServer(port=self._port, func=self.__class__.func(finetuned_model),
                                          launcher=self._launcher, log_path=self._log_path, cls='tts')()

lazyllm.components.deploy.relay.base.FastapiApp

Bases: object

Source code in lazyllm/components/deploy/relay/base.py
class FastapiApp(object):
    __relay_services__ = []

    @staticmethod
    def _server(method, path, **kw):
        def impl(f):
            FastapiApp.__relay_services__.append([f, method, path, kw])
            return f
        return impl

    @staticmethod
    def get(path, **kw):
        return FastapiApp._server('get', path, **kw)

    @staticmethod
    def post(path, **kw):
        return FastapiApp._server('post', path, **kw)

    @staticmethod
    def list(path, **kw):
        return FastapiApp._server('list', path, **kw)

    @staticmethod
    def delete(path, **kw):
        return FastapiApp._server('delete', path, **kw)

    @staticmethod
    def update():
        for f, method, path, kw in FastapiApp.__relay_services__:
            cls = inspect._findclass(f)
            if '__relay_services__' not in cls.__dict__:
                cls.__relay_services__ = dict()
            cls.__relay_services__[method, path] = ([f.__name__, kw])
        FastapiApp.__relay_services__.clear()

Prompter

lazyllm.components.prompter.LazyLLMPrompterBase

LazyLLM提示词基类,用于管理和生成模型提示词。

Parameters:

  • show (bool, default: False ) –

    是否显示生成的提示词,默认为False。

  • tools (Optional[List], default: None ) –

    可用工具列表,默认为None。

  • history (Optional[List], default: None ) –

    对话历史记录,默认为None。

Attributes:

  • ISA (str) –

    指令分隔符起始标记 "<!lazyllm-spliter!>"。

  • ISE (str) –

    指令分隔符结束标记 "</!lazyllm-spliter!>"。

Configuration Items
  • system: 系统角色设定

  • sos/eos: 会话开始/结束标记

  • soh/eoh: 人类输入开始/结束标记

  • soa/eoa: AI回复开始/结束标记

  • soe/eoe: 工具执行结果开始/结束标记

  • tool_start_token/tool_end_token: 工具调用开始/结束标记

  • tool_args_token: 工具参数标记

Source code in lazyllm/components/prompter/builtinPrompt.py
class LazyLLMPrompterBase(metaclass=LazyLLMRegisterMetaClass):
    """LazyLLM提示词基类,用于管理和生成模型提示词。

Args:
    show (bool): 是否显示生成的提示词,默认为False。
    tools (Optional[List]): 可用工具列表,默认为None。
    history (Optional[List]): 对话历史记录,默认为None。

Attributes:
    ISA (str): 指令分隔符起始标记 "<!lazyllm-spliter!>"。

    ISE (str): 指令分隔符结束标记 "</!lazyllm-spliter!>"。


Configuration Items:
    - system: 系统角色设定

    - sos/eos: 会话开始/结束标记

    - soh/eoh: 人类输入开始/结束标记

    - soa/eoa: AI回复开始/结束标记

    - soe/eoe: 工具执行结果开始/结束标记

    - tool_start_token/tool_end_token: 工具调用开始/结束标记

    - tool_args_token: 工具参数标记

"""
    ISA = '<!lazyllm-spliter!>'
    ISE = '</!lazyllm-spliter!>'

    def __init__(self, show=False, tools=None, history=None, *, enable_system: bool = True):
        self._set_model_configs(system='You are an AI-Agent developed by LazyLLM.', sos='',
                                soh='', soa='', eos='', eoh='', eoa='')
        self._show = show
        self._tools = tools
        self._pre_hook = None
        self._history = history or []
        self._enable_system = enable_system

    def _init_prompt(self, template: str, instruction_template: str, split: Union[None, str] = None):
        self._template = template
        self._instruction_template = instruction_template
        if split:
            assert not hasattr(self, '_split')
            self._split = split

    @staticmethod
    def _get_extro_key_template(extra_keys, prefix='Here are some extra messages you can referred to:\n\n'):
        if extra_keys:
            if isinstance(extra_keys, str): extra_keys = [extra_keys]
            assert isinstance(extra_keys, (tuple, list)), 'Only str, tuple[str], list[str] are supported'
            return prefix + ''.join([f'### {k}:\n{{{k}}}\n\n' for k in extra_keys])
        return ''

    def _handle_tool_call_instruction(self, instruction, tools):
        tool_dict = {}
        for key in ['tool_start_token', 'tool_args_token', 'tool_end_token']:
            if getattr(self, f'_{key}', None) and key in instruction:
                tool_dict[key] = getattr(self, f'_{key}')
        if 'tool_names' in instruction: tool_dict['tool_names'] = self._get_tools_name(tools)
        return reduce(lambda s, kv: s.replace(f'{{{kv[0]}}}', kv[1]), tool_dict.items(), instruction)

    def _set_model_configs(self, system: str = None, sos: Union[None, str] = None, soh: Union[None, str] = None,
                           soa: Union[None, str] = None, eos: Union[None, str] = None,
                           eoh: Union[None, str] = None, eoa: Union[None, str] = None,
                           soe: Union[None, str] = None, eoe: Union[None, str] = None,
                           separator: Union[None, str] = None, plugin: Union[None, str] = None,
                           interpreter: Union[None, str] = None, stop_words: Union[None, List[str]] = None,
                           tool_start_token: Union[None, str] = None, tool_end_token: Union[None, str] = None,
                           tool_args_token: Union[None, str] = None):

        local = locals()
        for name in ['system', 'sos', 'soh', 'soa', 'eos', 'eoh', 'eoa', 'soe', 'eoe', 'tool_start_token',
                     'tool_end_token', 'tool_args_token']:
            if local[name] is not None: setattr(self, f'_{name}', local[name])

    def _get_tools(self, tools, *, return_dict):
        return tools if return_dict else '### Function-call Tools. \n\n' +\
            f'{json.dumps(tools, ensure_ascii=False)}\n\n' if tools else ''

    def _get_tools_name(self, tools):
        return json.dumps([t['function']['name'] for t in tools], ensure_ascii=False) if tools else ''

    def _get_histories(self, history, *, return_dict):  # noqa: C901
        if not self._history and not history: return ''
        if return_dict:
            content = []
            for item in self._history + (history or []):
                if isinstance(item, list):
                    assert len(item) <= 2, 'history item length cannot be greater than 2'
                    if len(item) > 0: content.append({'role': 'user', 'content': item[0]})
                    if len(item) > 1: content.append({'role': 'assistant', 'content': item[1]})
                elif isinstance(item, dict):
                    content.append(item)
                else:
                    LOG.error(f'history: {history}')
                    raise ValueError('history must be a list of list or dict')
            return content
        else:
            ret = ''.join([f'{self._soh}{h}{self._eoh}{self._soa}{a}{self._eoa}' for h, a in self._history])
            if not history: return ret
            if isinstance(history[0], list):
                return ret + ''.join([f'{self._soh}{h}{self._eoh}{self._soa}{a}{self._eoa}' for h, a in history])
            elif isinstance(history[0], dict):
                for item in history:
                    if item['role'] == 'user':
                        ret += f'{self._soh}{item["content"]}{self._eoh}'
                    elif item['role'] == 'assistant':
                        ret += f'{self._soa}'
                        ret += f'{item.get("content", "")}'
                        for idx in range(len(item.get('tool_calls', []))):
                            tool = item['tool_calls'][idx]['function']
                            if getattr(self, '_tool_args_token', None):
                                tool = tool['name'] + self._tool_args_token + \
                                    json.dumps(tool['arguments'], ensure_ascii=False)
                            ret += (f'{getattr(self, "_tool_start_token", "")}' + '\n'
                                    f'{tool}'
                                    f'{getattr(self, "_tool_end_token", "")}' + '\n')
                        ret += f'{self._eoa}'
                    elif item['role'] == 'tool':
                        try:
                            content = json.loads(item['content'].strip())
                        except Exception:
                            content = item['content']
                        ret += f'{getattr(self, "_soe", "")}{content}{getattr(self, "_eoe", "")}'

                return ret
            else:
                raise NotImplementedError('Cannot transform json history to {type(history[0])} now')

    def _get_instruction_and_input(self, input, *, return_dict=False, tools=None):
        instruction = self._instruction_template
        fc_prompt = '' if return_dict or not tools else FC_PROMPT
        if fc_prompt and FC_PROMPT_PLACEHOLDER not in instruction:
            instruction = f'{instruction}\n\n{fc_prompt}'
        instruction = instruction.replace(FC_PROMPT_PLACEHOLDER, fc_prompt)
        instruction = self._handle_tool_call_instruction(instruction, tools)
        prompt_keys = list(set(re.findall(r'\{(\w+)\}', instruction)))
        if isinstance(input, (str, int)):
            if len(prompt_keys) == 1:
                return instruction.format(**{prompt_keys[0]: input}), ''
            else:
                assert len(prompt_keys) == 0
                return instruction, input
        assert isinstance(input, dict), f'expected types are str, int and dict, bug get {type(input)}(`{input})`'
        kwargs = {k: input.pop(k) for k in prompt_keys}
        assert len(input) <= 1, f'Unexpected keys found in input: {list(input.keys())}'
        return (reduce(lambda s, kv: s.replace(f'{{{kv[0]}}}', kv[1]),
                       kwargs.items(),
                       instruction)
                if len(kwargs) > 0 else instruction,
                list(input.values())[0] if input else '')

    def _check_values(self, instruction, input, history, tools): pass

    # Used for TrainableModule(local deployed)
    def _generate_prompt_impl(self, instruction, input, user, history, tools, label):
        is_tool = False
        if isinstance(input, dict):
            input = input.get('content', '')
            is_tool = input.get('role') == 'tool'
        elif isinstance(input, list):
            is_tool = any(item.get('role') == 'tool' for item in input)
            input = '\n'.join([item.get('content', '') for item in input])
        params = dict(system=self._system, instruction=instruction, input=input, user=user, history=history, tools=tools,
                      sos=self._sos, eos=self._eos, soh=self._soh, eoh=self._eoh, soa=self._soa, eoa=self._eoa)
        if is_tool:
            params['soh'] = getattr(self, '_soe', self._soh)
            params['eoh'] = getattr(self, '_eoe', self._eoh)
        return self._template.format(**params) + (label if label else '')

    # Used for OnlineChatModule
    def _generate_prompt_dict_impl(self, instruction, input, user, history, tools, label):
        if not history: history = []
        if isinstance(input, str):
            history.append({'role': 'user', 'content': input})
        elif isinstance(input, dict):
            history.append(input)
        elif isinstance(input, list) and all(isinstance(ele, dict) for ele in input):
            history.extend(input)
        elif isinstance(input, tuple) and len(input) == 1:
            # Note tuple size 1 with one single string is not expected
            history.append({'role': 'user', 'content': input[0]})
        else:
            raise TypeError('input must be a string or a dict')

        if user:
            history[-1]['content'] = user + history[-1]['content']

        if self._enable_system:
            history.insert(0, {'role': 'system',
                               'content': self._system + '\n' + instruction if instruction else self._system})
        return dict(messages=history, tools=tools) if tools else dict(messages=history)

    def pre_hook(self, func: Optional[Callable] = None):
        """设置预处理钩子函数,供外部在生成提示词前对输入数据进行自定义处理。

Args:
    func (Optional[Callable]): 一个可调用对象,作为预处理钩子函数,接收并处理输入数据。

**Returns:**

- LazyLLMPrompterBase: 返回自身实例,方便链式调用。
"""
        self._pre_hook = func
        return self

    def _split_instruction(self, instruction: str):
        system_instruction = instruction
        user_instruction = ''
        if LazyLLMPrompterBase.ISA in instruction and LazyLLMPrompterBase.ISE in instruction:
            # The instruction includes system prompts and/or user prompts
            pattern = re.compile(r'%s(.*)%s' % (LazyLLMPrompterBase.ISA, LazyLLMPrompterBase.ISE), re.DOTALL)
            ret = re.split(pattern, instruction)
            system_instruction = ret[0]
            user_instruction = ret[1]

        return system_instruction, user_instruction

    def generate_prompt(self, input: Union[str, List, Dict[str, str], None] = None,
                        history: List[Union[List[str], Dict[str, Any]]] = None,
                        tools: Union[List[Dict[str, Any]], None] = None,
                        label: Union[str, None] = None,
                        *, show: bool = False, return_dict: bool = False) -> Union[str, Dict]:
        """根据用户输入,生成对应的Prompt.

Args:
    input (Option[str | Dict]):  Prompter的输入,如果是dict,会填充到instruction的槽位中;如果是str,则会作为输入。
    history (Option[List[List | Dict]]): 历史对话,可以为 ``[[u, s], [u, s]]`` 或 openai的history格式,默认为None。
    tools (Option[List[Dict]]: 可以使用的工具合集,大模型用作FunctionCall时使用,默认为None
    label (Option[str]): 标签,训练或微调时使用,默认为None
    show (bool): 标志是否打印生成的Prompt,默认为False
    return_dict (bool): 标志是否返回dict,一般情况下使用 ``OnlineChatModule`` 时会设置为True。如果返回dict,则仅填充 ``instruction``。默认为False
"""
        input = copy.deepcopy(input)
        if self._pre_hook:
            input, history, tools, label = self._pre_hook(input, history, tools, label)
        tools = tools or self._tools
        instruction, input = self._get_instruction_and_input(input, return_dict=return_dict, tools=tools)
        history = self._get_histories(history, return_dict=return_dict)
        tools = self._get_tools(tools, return_dict=return_dict)
        self._check_values(instruction, input, history, tools)
        instruction, user_instruction = self._split_instruction(instruction)
        func = self._generate_prompt_dict_impl if return_dict else self._generate_prompt_impl
        result = func(instruction, input, user_instruction, history, tools, label)
        if self._show or show: LOG.info(result)
        return result

    def get_response(self, output: str, input: Union[str, None] = None) -> str:
        """用作对Prompt的截断,只保留有价值的输出

Args:
     output (str): 大模型的输出
     input (Option[[str]): 大模型的输入,若指定此参数,会将输出中包含输入的部分全部截断,默认为None
"""
        if input and output.startswith(input):
            return output[len(input):]
        return output if getattr(self, '_split', None) is None else output.split(self._split)[-1]

generate_prompt(input=None, history=None, tools=None, label=None, *, show=False, return_dict=False)

根据用户输入,生成对应的Prompt.

Parameters:

  • input (Option[str | Dict], default: None ) –

    Prompter的输入,如果是dict,会填充到instruction的槽位中;如果是str,则会作为输入。

  • history (Option[List[List | Dict]], default: None ) –

    历史对话,可以为 [[u, s], [u, s]] 或 openai的history格式,默认为None。

  • tools (Option[List[Dict]]

    可以使用的工具合集,大模型用作FunctionCall时使用,默认为None

  • label (Option[str], default: None ) –

    标签,训练或微调时使用,默认为None

  • show (bool, default: False ) –

    标志是否打印生成的Prompt,默认为False

  • return_dict (bool, default: False ) –

    标志是否返回dict,一般情况下使用 OnlineChatModule 时会设置为True。如果返回dict,则仅填充 instruction。默认为False

Source code in lazyllm/components/prompter/builtinPrompt.py
    def generate_prompt(self, input: Union[str, List, Dict[str, str], None] = None,
                        history: List[Union[List[str], Dict[str, Any]]] = None,
                        tools: Union[List[Dict[str, Any]], None] = None,
                        label: Union[str, None] = None,
                        *, show: bool = False, return_dict: bool = False) -> Union[str, Dict]:
        """根据用户输入,生成对应的Prompt.

Args:
    input (Option[str | Dict]):  Prompter的输入,如果是dict,会填充到instruction的槽位中;如果是str,则会作为输入。
    history (Option[List[List | Dict]]): 历史对话,可以为 ``[[u, s], [u, s]]`` 或 openai的history格式,默认为None。
    tools (Option[List[Dict]]: 可以使用的工具合集,大模型用作FunctionCall时使用,默认为None
    label (Option[str]): 标签,训练或微调时使用,默认为None
    show (bool): 标志是否打印生成的Prompt,默认为False
    return_dict (bool): 标志是否返回dict,一般情况下使用 ``OnlineChatModule`` 时会设置为True。如果返回dict,则仅填充 ``instruction``。默认为False
"""
        input = copy.deepcopy(input)
        if self._pre_hook:
            input, history, tools, label = self._pre_hook(input, history, tools, label)
        tools = tools or self._tools
        instruction, input = self._get_instruction_and_input(input, return_dict=return_dict, tools=tools)
        history = self._get_histories(history, return_dict=return_dict)
        tools = self._get_tools(tools, return_dict=return_dict)
        self._check_values(instruction, input, history, tools)
        instruction, user_instruction = self._split_instruction(instruction)
        func = self._generate_prompt_dict_impl if return_dict else self._generate_prompt_impl
        result = func(instruction, input, user_instruction, history, tools, label)
        if self._show or show: LOG.info(result)
        return result

get_response(output, input=None)

用作对Prompt的截断,只保留有价值的输出

Parameters:

  • output (str) –

    大模型的输出

  • input (Option[[str], default: None ) –

    大模型的输入,若指定此参数,会将输出中包含输入的部分全部截断,默认为None

Source code in lazyllm/components/prompter/builtinPrompt.py
    def get_response(self, output: str, input: Union[str, None] = None) -> str:
        """用作对Prompt的截断,只保留有价值的输出

Args:
     output (str): 大模型的输出
     input (Option[[str]): 大模型的输入,若指定此参数,会将输出中包含输入的部分全部截断,默认为None
"""
        if input and output.startswith(input):
            return output[len(input):]
        return output if getattr(self, '_split', None) is None else output.split(self._split)[-1]

pre_hook(func=None)

设置预处理钩子函数,供外部在生成提示词前对输入数据进行自定义处理。

Parameters:

  • func (Optional[Callable], default: None ) –

    一个可调用对象,作为预处理钩子函数,接收并处理输入数据。

Returns:

  • LazyLLMPrompterBase: 返回自身实例,方便链式调用。
Source code in lazyllm/components/prompter/builtinPrompt.py
    def pre_hook(self, func: Optional[Callable] = None):
        """设置预处理钩子函数,供外部在生成提示词前对输入数据进行自定义处理。

Args:
    func (Optional[Callable]): 一个可调用对象,作为预处理钩子函数,接收并处理输入数据。

**Returns:**

- LazyLLMPrompterBase: 返回自身实例,方便链式调用。
"""
        self._pre_hook = func
        return self

lazyllm.components.prompter.EmptyPrompter

Bases: LazyLLMPrompterBase

继承自 LazyLLMPrompterBase 的空提示生成器,用于直接返回原始输入。

该类不会对输入进行任何处理,适用于无需格式化的调试、测试或占位场景。

Examples:

>>> from lazyllm.components.prompter import EmptyPrompter
>>> prompter = EmptyPrompter()
>>> prompter.generate_prompt("Hello LazyLLM")
'Hello LazyLLM'
>>> prompter.generate_prompt({"query": "Tell me a joke"})
{'query': 'Tell me a joke'}
>>> # Even with additional parameters, the input is returned unchanged
>>> prompter.generate_prompt("No-op", history=[["Hi", "Hello"]], tools=[{"name": "search"}], label="debug")
'No-op'
Source code in lazyllm/components/prompter/builtinPrompt.py
class EmptyPrompter(LazyLLMPrompterBase):
    """继承自 `LazyLLMPrompterBase` 的空提示生成器,用于直接返回原始输入。

该类不会对输入进行任何处理,适用于无需格式化的调试、测试或占位场景。


Examples:
    >>> from lazyllm.components.prompter import EmptyPrompter
    >>> prompter = EmptyPrompter()
    >>> prompter.generate_prompt("Hello LazyLLM")
    'Hello LazyLLM'
    >>> prompter.generate_prompt({"query": "Tell me a joke"})
    {'query': 'Tell me a joke'}
    >>> # Even with additional parameters, the input is returned unchanged
    >>> prompter.generate_prompt("No-op", history=[["Hi", "Hello"]], tools=[{"name": "search"}], label="debug")
    'No-op'
    """

    def generate_prompt(self, input, history=None, tools=None, label=None, show=False, return_dict=False):
        """直接返回输入的Prompt实现,继承自 `LazyLLMPrompterBase`。

该方法不会对输入做任何格式化操作,适用于调试、测试或占位场景。

Args:
    input (Any): 任意输入,作为Prompt返回。
    history (Option[List[List | Dict]]): 历史对话,可忽略,默认None。
    tools (Option[List[Dict]]): 工具参数,可忽略,默认None。
    label (Option[str]): 标签,可忽略,默认None。
    show (bool): 是否打印返回内容,默认为False。
"""
        if return_dict:
            return {'messages': [{'role': 'user', 'content': input}]}
        if self._show or show: LOG.info(input)
        return input

generate_prompt(input, history=None, tools=None, label=None, show=False, return_dict=False)

直接返回输入的Prompt实现,继承自 LazyLLMPrompterBase

该方法不会对输入做任何格式化操作,适用于调试、测试或占位场景。

Parameters:

  • input (Any) –

    任意输入,作为Prompt返回。

  • history (Option[List[List | Dict]], default: None ) –

    历史对话,可忽略,默认None。

  • tools (Option[List[Dict]], default: None ) –

    工具参数,可忽略,默认None。

  • label (Option[str], default: None ) –

    标签,可忽略,默认None。

  • show (bool, default: False ) –

    是否打印返回内容,默认为False。

Source code in lazyllm/components/prompter/builtinPrompt.py
    def generate_prompt(self, input, history=None, tools=None, label=None, show=False, return_dict=False):
        """直接返回输入的Prompt实现,继承自 `LazyLLMPrompterBase`。

该方法不会对输入做任何格式化操作,适用于调试、测试或占位场景。

Args:
    input (Any): 任意输入,作为Prompt返回。
    history (Option[List[List | Dict]]): 历史对话,可忽略,默认None。
    tools (Option[List[Dict]]): 工具参数,可忽略,默认None。
    label (Option[str]): 标签,可忽略,默认None。
    show (bool): 是否打印返回内容,默认为False。
"""
        if return_dict:
            return {'messages': [{'role': 'user', 'content': input}]}
        if self._show or show: LOG.info(input)
        return input

lazyllm.components.Prompter

Bases: object

用于生成模型输入的Prompt类,支持模板、历史对话拼接与响应抽取。

该类支持从字典、模板名称或文件中加载prompt配置,支持历史对话结构拼接(用于Chat类任务), 可灵活处理有/无history结构的prompt输入,适配非字典类型输入。

Parameters:

  • prompt (Optional[str], default: None ) –

    模板Prompt字符串,支持格式化字段。

  • response_split (Optional[str], default: None ) –

    对模型响应进行切分的分隔符,仅用于抽取模型回答。

  • chat_prompt (Optional[str], default: None ) –

    多轮对话使用的Prompt模板,必须包含history字段。

  • history_symbol (str, default: 'llm_chat_history' ) –

    表示历史对话字段的名称,默认为'llm_chat_history'。

  • eoa (Optional[str], default: None ) –

    对话中 assistant/user 分隔符。

  • eoh (Optional[str], default: None ) –

    多轮history中 user-assistant 分隔符。

  • show (bool, default: False ) –

    是否打印最终生成的Prompt,默认False。

Examples:

>>> from lazyllm import Prompter
>>> p = Prompter(prompt="Answer the following: {question}")
>>> p.generate_prompt("What is AI?")
'Answer the following: What is AI?'
>>> p.generate_prompt({"question": "Define machine learning"})
'Answer the following: Define machine learning'
>>> p = Prompter(
...     prompt="Instruction: {instruction}",
...     chat_prompt="Instruction: {instruction}\nHistory:\n{llm_chat_history}",
...     history_symbol="llm_chat_history",
...     eoa="</s>",
...     eoh="|"
... )
>>> p.generate_prompt(
...     input={"instruction": "Translate this."},
...     history=[["hello", "你好"], ["how are you", "你好吗"]]
... )
'Instruction: Translate this.\nHistory:\nhello|你好</s>how are you|你好吗'
>>> prompt_conf = {
...     "prompt": "Task: {task}",
...     "response_split": "---"
... }
>>> p = Prompter.from_dict(prompt_conf)
>>> p.generate_prompt("Summarize this article.")
'Task: Summarize this article.'
>>> full_output = "Task: Summarize this article.---This is the summary."
>>> p.get_response(full_output)
'This is the summary.'
Source code in lazyllm/components/prompter/prompter.py
class Prompter(object):
    """用于生成模型输入的Prompt类,支持模板、历史对话拼接与响应抽取。

该类支持从字典、模板名称或文件中加载prompt配置,支持历史对话结构拼接(用于Chat类任务),
可灵活处理有/无history结构的prompt输入,适配非字典类型输入。

Args:
    prompt (Optional[str]): 模板Prompt字符串,支持格式化字段。
    response_split (Optional[str]): 对模型响应进行切分的分隔符,仅用于抽取模型回答。
    chat_prompt (Optional[str]): 多轮对话使用的Prompt模板,必须包含history字段。
    history_symbol (str): 表示历史对话字段的名称,默认为'llm_chat_history'。
    eoa (Optional[str]): 对话中 assistant/user 分隔符。
    eoh (Optional[str]): 多轮history中 user-assistant 分隔符。
    show (bool): 是否打印最终生成的Prompt,默认False。


Examples:
    >>> from lazyllm import Prompter

    >>> p = Prompter(prompt="Answer the following: {question}")
    >>> p.generate_prompt("What is AI?")
    'Answer the following: What is AI?'

    >>> p.generate_prompt({"question": "Define machine learning"})
    'Answer the following: Define machine learning'

    >>> p = Prompter(
    ...     prompt="Instruction: {instruction}",
    ...     chat_prompt="Instruction: {instruction}\\nHistory:\\n{llm_chat_history}",
    ...     history_symbol="llm_chat_history",
    ...     eoa="</s>",
    ...     eoh="|"
    ... )
    >>> p.generate_prompt(
    ...     input={"instruction": "Translate this."},
    ...     history=[["hello", "你好"], ["how are you", "你好吗"]]
    ... )
    'Instruction: Translate this.\\nHistory:\\nhello|你好</s>how are you|你好吗'

    >>> prompt_conf = {
    ...     "prompt": "Task: {task}",
    ...     "response_split": "---"
    ... }
    >>> p = Prompter.from_dict(prompt_conf)
    >>> p.generate_prompt("Summarize this article.")
    'Task: Summarize this article.'

    >>> full_output = "Task: Summarize this article.---This is the summary."
    >>> p.get_response(full_output)
    'This is the summary.'
    """
    def __init__(self, prompt=None, response_split=None, *, chat_prompt=None,
                 history_symbol='llm_chat_history', eoa=None, eoh=None, show=False):
        self._prompt, self._response_split = prompt, response_split
        self._chat_prompt = chat_prompt
        self._history_symbol, self._eoa, self._eoh = history_symbol, eoa, eoh
        self._show = show
        self._prompt_keys = list(set(re.findall(r'\{(\w+)\}', self._prompt))) if prompt else []
        if chat_prompt is not None:
            chat_keys = set(re.findall(r'\{(\w+)\}', self._chat_prompt))
            assert set(self._prompt_keys).issubset(chat_keys)
            assert chat_keys - set(self._prompt_keys) == set([self._history_symbol])
            self.use_history = True
        else:
            self.use_history = history_symbol in self._prompt_keys
            if self.use_history:
                self._prompt_keys.pop(self._prompt_keys.index(history_symbol))
                self._chat_prompt = self._prompt

    @classmethod
    def from_dict(cls, prompt, *, show=False):
        """通过字典配置初始化一个 Prompter 实例。

Args:
    prompt (Dict): 包含 prompt 相关字段的配置字典,需包含 `prompt` 键,其他为可选。
    show (bool): 是否显示生成的 prompt,默认为 False。

**Returns:**

- Prompter: 返回一个初始化的 Prompter 实例。
"""
        assert isinstance(prompt, dict)
        return cls(**prompt, show=show)

    @classmethod
    def from_template(cls, template_name, *, show=False):
        """根据模板名称加载 prompt 配置并初始化 Prompter 实例。

Args:
    template_name (str): 模板名称,必须在 `templates` 中存在。
    show (bool): 是否显示生成的 prompt,默认为 False。

**Returns:**

- Prompter: 返回一个初始化的 Prompter 实例。
"""
        return cls.from_dict(templates[template_name], show=show)

    @classmethod
    def from_file(cls, fname, *, show=False):
        """从 JSON 文件中读取配置并初始化 Prompter 实例。

Args:
    fname (str): JSON 配置文件路径。
    show (bool): 是否显示生成的 prompt,默认为 False。

Returns:
    Prompter: 返回一个初始化的 Prompter 实例。
"""
        with open(fname) as fp:
            return cls.from_dict(json.load(fp), show=show)

    @classmethod
    def empty(cls):
        """创建一个空的 Prompter 实例。

Returns:
    Prompter: 返回一个无 prompt 配置的 Prompter 实例。
"""
        return cls()

    def _is_empty(self):
        return self._prompt is None

    def generate_prompt(self, input, history=None, tools=None, label=None, show=False):
        """根据输入和可选的历史记录生成最终 Prompt。

Args:
    input (Union[str, Dict]): 用户输入。可以是字符串或包含多字段的字典。
    history (Optional[List[List[str]]]): 多轮对话历史,例如 [['u1', 'a1'], ['u2', 'a2']]。
    tools (Optional[Any]): 目前未支持工具调用,此字段必须为 None。
    label (Optional[str]): 附加在 prompt 末尾的标签,通常用于训练。
    show (bool): 是否显示生成的 prompt,默认 False。

Returns:
    str: 格式化后的 prompt 字符串。
"""
        if not self._is_empty():
            assert tools is None
            # datasets.formatting.formatting.LazyDict is used in transformers
            if not isinstance(input, collections.abc.Mapping):
                assert len(self._prompt_keys) == 1, (
                    f'invalid prompt `{self._prompt}` for <{type(input)}> input `{input}`')
                input = {self._prompt_keys[0]: input}
            try:
                if self.use_history and isinstance(history, list) and len(history) > 0:
                    assert isinstance(history[0], list), 'history must be list of list'
                    input[self._history_symbol] = self._eoa.join([self._eoh.join(h) for h in history])
                    input = self._chat_prompt.format(**input)
                else:
                    if self.use_history: input[self._history_symbol] = ''
                    input = self._prompt.format(**input)
            except Exception:
                raise RuntimeError(f'Generate prompt failed, and prompt is {self._prompt}; chat-prompt'
                                   f' is {self._chat_prompt}; input is {input}; history is {history}')
            if label: input += label
        if self._show or show: LOG.info(input)
        return input

    def get_response(self, response, input=None):
        """从 LLM 返回结果中提取模型的回答内容。

Args:
    response (str): 模型完整响应文本。
    input (Optional[str]): 如果模型输出以输入开头,将会自动去除输入部分。

Returns:
    str: 提取后的模型响应内容。
"""
        if input and response.startswith(input):
            return response[len(input):]
        return response if self._response_split is None else response.split(self._response_split)[-1]

from_dict(prompt, *, show=False) classmethod

通过字典配置初始化一个 Prompter 实例。

Parameters:

  • prompt (Dict) –

    包含 prompt 相关字段的配置字典,需包含 prompt 键,其他为可选。

  • show (bool, default: False ) –

    是否显示生成的 prompt,默认为 False。

Returns:

  • Prompter: 返回一个初始化的 Prompter 实例。
Source code in lazyllm/components/prompter/prompter.py
    @classmethod
    def from_dict(cls, prompt, *, show=False):
        """通过字典配置初始化一个 Prompter 实例。

Args:
    prompt (Dict): 包含 prompt 相关字段的配置字典,需包含 `prompt` 键,其他为可选。
    show (bool): 是否显示生成的 prompt,默认为 False。

**Returns:**

- Prompter: 返回一个初始化的 Prompter 实例。
"""
        assert isinstance(prompt, dict)
        return cls(**prompt, show=show)

from_template(template_name, *, show=False) classmethod

根据模板名称加载 prompt 配置并初始化 Prompter 实例。

Parameters:

  • template_name (str) –

    模板名称,必须在 templates 中存在。

  • show (bool, default: False ) –

    是否显示生成的 prompt,默认为 False。

Returns:

  • Prompter: 返回一个初始化的 Prompter 实例。
Source code in lazyllm/components/prompter/prompter.py
    @classmethod
    def from_template(cls, template_name, *, show=False):
        """根据模板名称加载 prompt 配置并初始化 Prompter 实例。

Args:
    template_name (str): 模板名称,必须在 `templates` 中存在。
    show (bool): 是否显示生成的 prompt,默认为 False。

**Returns:**

- Prompter: 返回一个初始化的 Prompter 实例。
"""
        return cls.from_dict(templates[template_name], show=show)

from_file(fname, *, show=False) classmethod

从 JSON 文件中读取配置并初始化 Prompter 实例。

Parameters:

  • fname (str) –

    JSON 配置文件路径。

  • show (bool, default: False ) –

    是否显示生成的 prompt,默认为 False。

Returns:

  • Prompter

    返回一个初始化的 Prompter 实例。

Source code in lazyllm/components/prompter/prompter.py
    @classmethod
    def from_file(cls, fname, *, show=False):
        """从 JSON 文件中读取配置并初始化 Prompter 实例。

Args:
    fname (str): JSON 配置文件路径。
    show (bool): 是否显示生成的 prompt,默认为 False。

Returns:
    Prompter: 返回一个初始化的 Prompter 实例。
"""
        with open(fname) as fp:
            return cls.from_dict(json.load(fp), show=show)

empty() classmethod

创建一个空的 Prompter 实例。

Returns:

  • Prompter

    返回一个无 prompt 配置的 Prompter 实例。

Source code in lazyllm/components/prompter/prompter.py
    @classmethod
    def empty(cls):
        """创建一个空的 Prompter 实例。

Returns:
    Prompter: 返回一个无 prompt 配置的 Prompter 实例。
"""
        return cls()

generate_prompt(input, history=None, tools=None, label=None, show=False)

根据输入和可选的历史记录生成最终 Prompt。

Parameters:

  • input (Union[str, Dict]) –

    用户输入。可以是字符串或包含多字段的字典。

  • history (Optional[List[List[str]]], default: None ) –

    多轮对话历史,例如 [['u1', 'a1'], ['u2', 'a2']]。

  • tools (Optional[Any], default: None ) –

    目前未支持工具调用,此字段必须为 None。

  • label (Optional[str], default: None ) –

    附加在 prompt 末尾的标签,通常用于训练。

  • show (bool, default: False ) –

    是否显示生成的 prompt,默认 False。

Returns:

  • str

    格式化后的 prompt 字符串。

Source code in lazyllm/components/prompter/prompter.py
    def generate_prompt(self, input, history=None, tools=None, label=None, show=False):
        """根据输入和可选的历史记录生成最终 Prompt。

Args:
    input (Union[str, Dict]): 用户输入。可以是字符串或包含多字段的字典。
    history (Optional[List[List[str]]]): 多轮对话历史,例如 [['u1', 'a1'], ['u2', 'a2']]。
    tools (Optional[Any]): 目前未支持工具调用,此字段必须为 None。
    label (Optional[str]): 附加在 prompt 末尾的标签,通常用于训练。
    show (bool): 是否显示生成的 prompt,默认 False。

Returns:
    str: 格式化后的 prompt 字符串。
"""
        if not self._is_empty():
            assert tools is None
            # datasets.formatting.formatting.LazyDict is used in transformers
            if not isinstance(input, collections.abc.Mapping):
                assert len(self._prompt_keys) == 1, (
                    f'invalid prompt `{self._prompt}` for <{type(input)}> input `{input}`')
                input = {self._prompt_keys[0]: input}
            try:
                if self.use_history and isinstance(history, list) and len(history) > 0:
                    assert isinstance(history[0], list), 'history must be list of list'
                    input[self._history_symbol] = self._eoa.join([self._eoh.join(h) for h in history])
                    input = self._chat_prompt.format(**input)
                else:
                    if self.use_history: input[self._history_symbol] = ''
                    input = self._prompt.format(**input)
            except Exception:
                raise RuntimeError(f'Generate prompt failed, and prompt is {self._prompt}; chat-prompt'
                                   f' is {self._chat_prompt}; input is {input}; history is {history}')
            if label: input += label
        if self._show or show: LOG.info(input)
        return input

get_response(response, input=None)

从 LLM 返回结果中提取模型的回答内容。

Parameters:

  • response (str) –

    模型完整响应文本。

  • input (Optional[str], default: None ) –

    如果模型输出以输入开头,将会自动去除输入部分。

Returns:

  • str

    提取后的模型响应内容。

Source code in lazyllm/components/prompter/prompter.py
    def get_response(self, response, input=None):
        """从 LLM 返回结果中提取模型的回答内容。

Args:
    response (str): 模型完整响应文本。
    input (Optional[str]): 如果模型输出以输入开头,将会自动去除输入部分。

Returns:
    str: 提取后的模型响应内容。
"""
        if input and response.startswith(input):
            return response[len(input):]
        return response if self._response_split is None else response.split(self._response_split)[-1]

lazyllm.components.AlpacaPrompter

Bases: LazyLLMPrompterBase

Alpaca格式的Prompter,支持工具调用,不支持历史对话。

Parameters:

  • instruction (Option[str], default: None ) –

    大模型的任务指令,至少带一个可填充的槽位(如 {instruction})。或者使用字典指定 systemuser 的指令。

  • extra_keys (Option[List], default: None ) –

    额外的字段,用户的输入会填充这些字段。

  • show (bool, default: False ) –

    标志是否打印生成的Prompt,默认为False

  • tools (Option[list], default: None ) –

    大模型可以使用的工具集合,默认为None

Examples:

>>> from lazyllm import AlpacaPrompter
>>> p = AlpacaPrompter('hello world {instruction}')
>>> p.generate_prompt('this is my input')
'You are an AI-Agent developed by LazyLLM.\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\n\n ### Instruction:\nhello world this is my input\n\n\n### Response:\n'
>>> p.generate_prompt('this is my input', return_dict=True)
{'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\n\n ### Instruction:\nhello world this is my input\n\n'}, {'role': 'user', 'content': ''}]}
>>>
>>> p = AlpacaPrompter('hello world {instruction}, {input}', extra_keys=['knowledge'])
>>> p.generate_prompt(dict(instruction='hello world', input='my input', knowledge='lazyllm'))
'You are an AI-Agent developed by LazyLLM.\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\n\n ### Instruction:\nhello world hello world, my input\n\nHere are some extra messages you can referred to:\n\n### knowledge:\nlazyllm\n\n\n### Response:\n'
>>> p.generate_prompt(dict(instruction='hello world', input='my input', knowledge='lazyllm'), return_dict=True)
{'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\n\n ### Instruction:\nhello world hello world, my input\n\nHere are some extra messages you can referred to:\n\n### knowledge:\nlazyllm\n\n'}, {'role': 'user', 'content': ''}]}
>>>
>>> p = AlpacaPrompter(dict(system="hello world", user="this is user instruction {input}"))
>>> p.generate_prompt(dict(input="my input"))
'You are an AI-Agent developed by LazyLLM.\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\n\n ### Instruction:\nhello word\n\n\n\nthis is user instruction my input### Response:\n'
>>> p.generate_prompt(dict(input="my input"), return_dict=True)
{'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\n\n ### Instruction:\nhello world'}, {'role': 'user', 'content': 'this is user instruction my input'}]}
Source code in lazyllm/components/prompter/alpacaPrompter.py
class AlpacaPrompter(LazyLLMPrompterBase):
    """Alpaca格式的Prompter,支持工具调用,不支持历史对话。

Args:
    instruction (Option[str]): 大模型的任务指令,至少带一个可填充的槽位(如 ``{instruction}``)。或者使用字典指定 ``system`` 和 ``user`` 的指令。
    extra_keys (Option[List]): 额外的字段,用户的输入会填充这些字段。
    show (bool): 标志是否打印生成的Prompt,默认为False
    tools (Option[list]): 大模型可以使用的工具集合,默认为None


Examples:
    >>> from lazyllm import AlpacaPrompter
    >>> p = AlpacaPrompter('hello world {instruction}')
    >>> p.generate_prompt('this is my input')
    'You are an AI-Agent developed by LazyLLM.\\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\\n\\n ### Instruction:\\nhello world this is my input\\n\\n\\n### Response:\\n'
    >>> p.generate_prompt('this is my input', return_dict=True)
    {'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\\n\\n ### Instruction:\\nhello world this is my input\\n\\n'}, {'role': 'user', 'content': ''}]}
    >>>
    >>> p = AlpacaPrompter('hello world {instruction}, {input}', extra_keys=['knowledge'])
    >>> p.generate_prompt(dict(instruction='hello world', input='my input', knowledge='lazyllm'))
    'You are an AI-Agent developed by LazyLLM.\\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\\n\\n ### Instruction:\\nhello world hello world, my input\\n\\nHere are some extra messages you can referred to:\\n\\n### knowledge:\\nlazyllm\\n\\n\\n### Response:\\n'
    >>> p.generate_prompt(dict(instruction='hello world', input='my input', knowledge='lazyllm'), return_dict=True)
    {'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\\n\\n ### Instruction:\\nhello world hello world, my input\\n\\nHere are some extra messages you can referred to:\\n\\n### knowledge:\\nlazyllm\\n\\n'}, {'role': 'user', 'content': ''}]}
    >>>
    >>> p = AlpacaPrompter(dict(system="hello world", user="this is user instruction {input}"))
    >>> p.generate_prompt(dict(input="my input"))
    'You are an AI-Agent developed by LazyLLM.\\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\\n\\n ### Instruction:\\nhello word\\n\\n\\n\\nthis is user instruction my input### Response:\\n'
    >>> p.generate_prompt(dict(input="my input"), return_dict=True)
    {'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\\nBelow is an instruction that describes a task, paired with extra messages such as input that provides further context if possible. Write a response that appropriately completes the request.\\n\\n ### Instruction:\\nhello world'}, {'role': 'user', 'content': 'this is user instruction my input'}]}
    """
    def __init__(self, instruction: Union[None, str, Dict[str, str]] = None, extra_keys: Union[None, List[str]] = None,
                 show: bool = False, tools: Optional[List] = None):
        super(__class__, self).__init__(show, tools=tools)
        if isinstance(instruction, dict):
            splice_struction = instruction.get('system', '') + \
                AlpacaPrompter.ISA + instruction.get('user', '') + AlpacaPrompter.ISE
            instruction = splice_struction
        instruction_template = ('Below is an instruction that describes a task, paired with extra messages such as '
                                'input that provides further context if possible. Write a response that appropriately '
                                f'completes the request.\n\n### Instruction:\n{instruction if instruction else ""}'
                                '\n\n' + LazyLLMPrompterBase._get_extro_key_template(extra_keys))
        self._init_prompt('{system}\n{instruction}\n{tools}\n{user}### Response:\n',
                          instruction_template,
                          '### Response:\n')

    def _check_values(self, instruction, input, history, tools):
        assert not history, f'Chat history is not supported in {__class__}.'
        assert not input, 'All keys should in instruction or extra-keys'

generate_prompt(input=None, history=None, tools=None, label=None, *, show=False, return_dict=False)

根据用户输入,生成对应的Prompt.

Parameters:

  • input (Option[str | Dict], default: None ) –

    Prompter的输入,如果是dict,会填充到instruction的槽位中;如果是str,则会作为输入。

  • history (Option[List[List | Dict]], default: None ) –

    历史对话,可以为 [[u, s], [u, s]] 或 openai的history格式,默认为None。

  • tools (Option[List[Dict]]

    可以使用的工具合集,大模型用作FunctionCall时使用,默认为None

  • label (Option[str], default: None ) –

    标签,训练或微调时使用,默认为None

  • show (bool, default: False ) –

    标志是否打印生成的Prompt,默认为False

  • return_dict (bool, default: False ) –

    标志是否返回dict,一般情况下使用 OnlineChatModule 时会设置为True。如果返回dict,则仅填充 instruction。默认为False

Source code in lazyllm/components/prompter/builtinPrompt.py
    def generate_prompt(self, input: Union[str, List, Dict[str, str], None] = None,
                        history: List[Union[List[str], Dict[str, Any]]] = None,
                        tools: Union[List[Dict[str, Any]], None] = None,
                        label: Union[str, None] = None,
                        *, show: bool = False, return_dict: bool = False) -> Union[str, Dict]:
        """根据用户输入,生成对应的Prompt.

Args:
    input (Option[str | Dict]):  Prompter的输入,如果是dict,会填充到instruction的槽位中;如果是str,则会作为输入。
    history (Option[List[List | Dict]]): 历史对话,可以为 ``[[u, s], [u, s]]`` 或 openai的history格式,默认为None。
    tools (Option[List[Dict]]: 可以使用的工具合集,大模型用作FunctionCall时使用,默认为None
    label (Option[str]): 标签,训练或微调时使用,默认为None
    show (bool): 标志是否打印生成的Prompt,默认为False
    return_dict (bool): 标志是否返回dict,一般情况下使用 ``OnlineChatModule`` 时会设置为True。如果返回dict,则仅填充 ``instruction``。默认为False
"""
        input = copy.deepcopy(input)
        if self._pre_hook:
            input, history, tools, label = self._pre_hook(input, history, tools, label)
        tools = tools or self._tools
        instruction, input = self._get_instruction_and_input(input, return_dict=return_dict, tools=tools)
        history = self._get_histories(history, return_dict=return_dict)
        tools = self._get_tools(tools, return_dict=return_dict)
        self._check_values(instruction, input, history, tools)
        instruction, user_instruction = self._split_instruction(instruction)
        func = self._generate_prompt_dict_impl if return_dict else self._generate_prompt_impl
        result = func(instruction, input, user_instruction, history, tools, label)
        if self._show or show: LOG.info(result)
        return result

get_response(output, input=None)

用作对Prompt的截断,只保留有价值的输出

Parameters:

  • output (str) –

    大模型的输出

  • input (Option[[str], default: None ) –

    大模型的输入,若指定此参数,会将输出中包含输入的部分全部截断,默认为None

Source code in lazyllm/components/prompter/builtinPrompt.py
    def get_response(self, output: str, input: Union[str, None] = None) -> str:
        """用作对Prompt的截断,只保留有价值的输出

Args:
     output (str): 大模型的输出
     input (Option[[str]): 大模型的输入,若指定此参数,会将输出中包含输入的部分全部截断,默认为None
"""
        if input and output.startswith(input):
            return output[len(input):]
        return output if getattr(self, '_split', None) is None else output.split(self._split)[-1]

lazyllm.components.ChatPrompter

Bases: LazyLLMPrompterBase

用于多轮对话的大模型Prompt构造器,继承自 LazyLLMPrompterBase

支持工具调用、历史对话与自定义指令模版。支持传入 system/user 拆分的指令结构,自动合并为统一模板。支持额外字段注入和打印提示信息。

Parameters:

  • instruction (Option[str | Dict[str, str]], default: None ) –

    Prompt模板指令,可为字符串或包含 systemuser 的字典。若为字典,将自动拼接并注入特殊标记分隔符。

  • extra_keys (Option[List[str]], default: None ) –

    额外的字段列表,用户输入中的内容会被插入对应槽位,用于丰富上下文。

  • show (bool, default: False ) –

    是否打印生成的Prompt,默认False。

  • tools (Option[List], default: None ) –

    可选的工具列表,用于FunctionCall任务,默认None。

  • history (Option[List[List[str]]], default: None ) –

    可选的历史对话,用于对话记忆,格式为[[user, assistant], ...],默认None。

Examples:

>>> from lazyllm import ChatPrompter
  • Simple instruction string
>>> p = ChatPrompter('hello world')
>>> p.generate_prompt('this is my input')
'You are an AI-Agent developed by LazyLLM.hello world\nthis is my input\n'
>>> p.generate_prompt('this is my input', return_dict=True)
{'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\nhello world'}, {'role': 'user', 'content': 'this is my input'}]}
  • Using extra_keys
>>> p = ChatPrompter('hello world {instruction}', extra_keys=['knowledge'])
>>> p.generate_prompt({
...     'instruction': 'this is my ins',
...     'input': 'this is my inp',
...     'knowledge': 'LazyLLM-Knowledge'
... })
'You are an AI-Agent developed by LazyLLM.hello world this is my ins\nHere are some extra messages you can referred to:\n\n### knowledge:\nLazyLLM-Knowledge\nthis is my inp\n'
  • With conversation history
>>> p.generate_prompt({
...     'instruction': 'this is my ins',
...     'input': 'this is my inp',
...     'knowledge': 'LazyLLM-Knowledge'
... }, history=[['s1', 'e1'], ['s2', 'e2']])
'You are an AI-Agent developed by LazyLLM.hello world this is my ins\nHere are some extra messages you can referred to:\n\n### knowledge:\nLazyLLM-Knowledge\ns1|e1\ns2|e2\nthis is my inp\n'
  • Using dict format for system/user instructions
>>> p = ChatPrompter(dict(system="hello world", user="this is user instruction {input}"))
>>> p.generate_prompt({'input': "my input", 'query': "this is user query"})
'You are an AI-Agent developed by LazyLLM.hello world\nthis is user instruction my input this is user query\n'
>>> p.generate_prompt({'input': "my input", 'query': "this is user query"}, return_dict=True)
{'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\nhello world'}, {'role': 'user', 'content': 'this is user instruction my input this is user query'}]}
Source code in lazyllm/components/prompter/chatPrompter.py
class ChatPrompter(LazyLLMPrompterBase):
    """用于多轮对话的大模型Prompt构造器,继承自 `LazyLLMPrompterBase`。

支持工具调用、历史对话与自定义指令模版。支持传入 system/user 拆分的指令结构,自动合并为统一模板。支持额外字段注入和打印提示信息。

Args:
    instruction (Option[str | Dict[str, str]]): Prompt模板指令,可为字符串或包含 `system` 和 `user` 的字典。若为字典,将自动拼接并注入特殊标记分隔符。
    extra_keys (Option[List[str]]): 额外的字段列表,用户输入中的内容会被插入对应槽位,用于丰富上下文。
    show (bool): 是否打印生成的Prompt,默认False。
    tools (Option[List]): 可选的工具列表,用于FunctionCall任务,默认None。
    history (Option[List[List[str]]]): 可选的历史对话,用于对话记忆,格式为[[user, assistant], ...],默认None。


Examples:
    >>> from lazyllm import ChatPrompter

    - Simple instruction string
    >>> p = ChatPrompter('hello world')
    >>> p.generate_prompt('this is my input')
    'You are an AI-Agent developed by LazyLLM.hello world\\nthis is my input\\n'

    >>> p.generate_prompt('this is my input', return_dict=True)
    {'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\\nhello world'}, {'role': 'user', 'content': 'this is my input'}]}

    - Using extra_keys
    >>> p = ChatPrompter('hello world {instruction}', extra_keys=['knowledge'])
    >>> p.generate_prompt({
    ...     'instruction': 'this is my ins',
    ...     'input': 'this is my inp',
    ...     'knowledge': 'LazyLLM-Knowledge'
    ... })
    'You are an AI-Agent developed by LazyLLM.hello world this is my ins\\nHere are some extra messages you can referred to:\\n\\n### knowledge:\\nLazyLLM-Knowledge\\nthis is my inp\\n'

    - With conversation history
    >>> p.generate_prompt({
    ...     'instruction': 'this is my ins',
    ...     'input': 'this is my inp',
    ...     'knowledge': 'LazyLLM-Knowledge'
    ... }, history=[['s1', 'e1'], ['s2', 'e2']])
    'You are an AI-Agent developed by LazyLLM.hello world this is my ins\\nHere are some extra messages you can referred to:\\n\\n### knowledge:\\nLazyLLM-Knowledge\\ns1|e1\\ns2|e2\\nthis is my inp\\n'

    - Using dict format for system/user instructions
    >>> p = ChatPrompter(dict(system="hello world", user="this is user instruction {input}"))
    >>> p.generate_prompt({'input': "my input", 'query': "this is user query"})
    'You are an AI-Agent developed by LazyLLM.hello world\\nthis is user instruction my input this is user query\\n'

    >>> p.generate_prompt({'input': "my input", 'query': "this is user query"}, return_dict=True)
    {'messages': [{'role': 'system', 'content': 'You are an AI-Agent developed by LazyLLM.\\nhello world'}, {'role': 'user', 'content': 'this is user instruction my input this is user query'}]}
    """
    def __init__(self, instruction: Union[None, str, Dict[str, str]] = None, extra_keys: Union[None, List[str]] = None,
                 show: bool = False, tools: Optional[List] = None, history: Optional[List[List[str]]] = None,
                 *, enable_system: bool = True):
        super(__class__, self).__init__(show, tools=tools, history=history, enable_system=enable_system)
        if isinstance(instruction, dict):
            splice_instruction = instruction.get('system', '') + \
                ChatPrompter.ISA + instruction.get('user', '') + ChatPrompter.ISE
            instruction = splice_instruction
        instruction_template = f'{instruction}\n{{extra_keys}}\n'.replace(
            '{extra_keys}', LazyLLMPrompterBase._get_extro_key_template(extra_keys)) if instruction else ''
        self._init_prompt('{sos}{system}{instruction}{tools}{eos}\n\n{history}\n{soh}\n{user}{input}\n{eoh}{soa}\n',
                          instruction_template)

    @property
    def _split(self): return f'{self._soa}\n' if self._soa else None

generate_prompt(input=None, history=None, tools=None, label=None, *, show=False, return_dict=False)

根据用户输入,生成对应的Prompt.

Parameters:

  • input (Option[str | Dict], default: None ) –

    Prompter的输入,如果是dict,会填充到instruction的槽位中;如果是str,则会作为输入。

  • history (Option[List[List | Dict]], default: None ) –

    历史对话,可以为 [[u, s], [u, s]] 或 openai的history格式,默认为None。

  • tools (Option[List[Dict]]

    可以使用的工具合集,大模型用作FunctionCall时使用,默认为None

  • label (Option[str], default: None ) –

    标签,训练或微调时使用,默认为None

  • show (bool, default: False ) –

    标志是否打印生成的Prompt,默认为False

  • return_dict (bool, default: False ) –

    标志是否返回dict,一般情况下使用 OnlineChatModule 时会设置为True。如果返回dict,则仅填充 instruction。默认为False

Source code in lazyllm/components/prompter/builtinPrompt.py
    def generate_prompt(self, input: Union[str, List, Dict[str, str], None] = None,
                        history: List[Union[List[str], Dict[str, Any]]] = None,
                        tools: Union[List[Dict[str, Any]], None] = None,
                        label: Union[str, None] = None,
                        *, show: bool = False, return_dict: bool = False) -> Union[str, Dict]:
        """根据用户输入,生成对应的Prompt.

Args:
    input (Option[str | Dict]):  Prompter的输入,如果是dict,会填充到instruction的槽位中;如果是str,则会作为输入。
    history (Option[List[List | Dict]]): 历史对话,可以为 ``[[u, s], [u, s]]`` 或 openai的history格式,默认为None。
    tools (Option[List[Dict]]: 可以使用的工具合集,大模型用作FunctionCall时使用,默认为None
    label (Option[str]): 标签,训练或微调时使用,默认为None
    show (bool): 标志是否打印生成的Prompt,默认为False
    return_dict (bool): 标志是否返回dict,一般情况下使用 ``OnlineChatModule`` 时会设置为True。如果返回dict,则仅填充 ``instruction``。默认为False
"""
        input = copy.deepcopy(input)
        if self._pre_hook:
            input, history, tools, label = self._pre_hook(input, history, tools, label)
        tools = tools or self._tools
        instruction, input = self._get_instruction_and_input(input, return_dict=return_dict, tools=tools)
        history = self._get_histories(history, return_dict=return_dict)
        tools = self._get_tools(tools, return_dict=return_dict)
        self._check_values(instruction, input, history, tools)
        instruction, user_instruction = self._split_instruction(instruction)
        func = self._generate_prompt_dict_impl if return_dict else self._generate_prompt_impl
        result = func(instruction, input, user_instruction, history, tools, label)
        if self._show or show: LOG.info(result)
        return result

get_response(output, input=None)

用作对Prompt的截断,只保留有价值的输出

Parameters:

  • output (str) –

    大模型的输出

  • input (Option[[str], default: None ) –

    大模型的输入,若指定此参数,会将输出中包含输入的部分全部截断,默认为None

Source code in lazyllm/components/prompter/builtinPrompt.py
    def get_response(self, output: str, input: Union[str, None] = None) -> str:
        """用作对Prompt的截断,只保留有价值的输出

Args:
     output (str): 大模型的输出
     input (Option[[str]): 大模型的输入,若指定此参数,会将输出中包含输入的部分全部截断,默认为None
"""
        if input and output.startswith(input):
            return output[len(input):]
        return output if getattr(self, '_split', None) is None else output.split(self._split)[-1]

MultiModal

Text to Image

lazyllm.components.StableDiffusionDeploy

Bases: LazyLLMDeployBase

Stable Diffusion 模型部署类。该类用于将SD模型部署到指定服务器上,以便可以通过网络进行调用。

Parameters:

  • launcher (Optional[LazyLLMLaunchersBase], default: None ) –

    启动器实例。默认为 None

  • log_path (Optional[str], default: None ) –

    日志文件路径。默认为 None

  • trust_remote_code (bool, default: True ) –

    是否信任远程代码。默认为 True

  • port (Optional[int], default: None ) –

    服务端口号。默认为 None

Examples:

>>> from lazyllm import launchers, UrlModule
>>> from lazyllm.components import StableDiffusionDeploy
>>> deployer = StableDiffusionDeploy(launchers.remote())
>>> url = deployer(base_model='stable-diffusion-3-medium')
>>> model = UrlModule(url=url)
>>> res = model('a tiny cat.')
>>> print(res)
... <lazyllm-query>{"query": "", "files": ["path/to/sd3/image_xxx.png"]}
Source code in lazyllm/components/deploy/stable_diffusion/stable_diffusion3.py
class StableDiffusionDeploy(LazyLLMDeployBase):
    """Stable Diffusion 模型部署类。该类用于将SD模型部署到指定服务器上,以便可以通过网络进行调用。

Args:
    launcher (Optional[LazyLLMLaunchersBase], optional): 启动器实例。默认为 ``None``
    log_path (Optional[str], optional): 日志文件路径。默认为 ``None``
    trust_remote_code (bool, optional): 是否信任远程代码。默认为 ``True``
    port (Optional[int], optional): 服务端口号。默认为 ``None``



Examples:
    >>> from lazyllm import launchers, UrlModule
    >>> from lazyllm.components import StableDiffusionDeploy
    >>> deployer = StableDiffusionDeploy(launchers.remote())
    >>> url = deployer(base_model='stable-diffusion-3-medium')
    >>> model = UrlModule(url=url)
    >>> res = model('a tiny cat.')
    >>> print(res)
    ... <lazyllm-query>{"query": "", "files": ["path/to/sd3/image_xxx.png"]}
    """
    message_format = None
    keys_name_handle = None
    default_headers = {'Content-Type': 'application/json'}

    def __init__(self, launcher: Optional[LazyLLMLaunchersBase] = None,
                 log_path: Optional[str] = None, trust_remote_code: bool = True, port: Optional[int] = None, **kw):
        super().__init__(launcher=launcher)
        self._log_path = log_path
        self._trust_remote_code = trust_remote_code
        self._port = port

    def __call__(self, finetuned_model=None, base_model=None):
        if not finetuned_model:
            finetuned_model = base_model
        elif not os.path.exists(finetuned_model) or \
            not any(file.endswith(('.bin', '.safetensors'))
                    for _, _, filenames in os.walk(finetuned_model) for file in filenames):
            LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                        f'base_model({base_model}) will be used')
            finetuned_model = base_model
        return lazyllm.deploy.RelayServer(port=self._port, func=_StableDiffusion3(finetuned_model),
                                          launcher=self._launcher, log_path=self._log_path, cls='stable_diffusion')()

Visual Question Answering

Reference LMDeploy, which supports the Visual Question Answering model.

Text to Sound

lazyllm.components.TTSDeploy

Source code in lazyllm/components/deploy/text_to_speech/__init__.py
class TTSDeploy:

    def __new__(cls, name, **kwarg):
        return cls.get_deploy_cls(name)(**kwarg)

    @classmethod
    def get_deploy_cls(cls, name):
        name = name.lower()
        if name == 'bark':
            return BarkDeploy
        elif name in ('chattts', 'chattts-new'):
            raise RuntimeError('ChatTTS is deprecated and no longer supported.')
            return ChatTTSDeploy
        elif name.startswith('musicgen'):
            return MusicGenDeploy
        else:
            raise RuntimeError(f'Not support model: {name}')

lazyllm.components.ChatTTSDeploy

Bases: TTSBase

ChatTTS 模型部署类。

Other Parameters:

  • keys_name_handle (dict) –

    键名映射字典,用于处理内部和外部API接口之间的参数名称转换。 默认为 {'inputs': 'inputs'}

  • message_format (dict) –

    请求负载结构,包含三个主要部分:

    • inputs (str): 要合成为语音的原始文本内容。

    • refinetext (dict): 文本细化和风格化参数,控制语音表达:

      • prompt (str): 语音风格控制标签,例如:"[oral_2][laugh_0][break_6]"

      • top_P (float): 核采样参数,用于解码策略(默认值:0.7)

      • top_K (int): Top-K 采样参数(默认值:20)

      • temperature (float): 采样温度,控制随机性(默认值:0.7)

      • repetition_penalty (float): 重复惩罚,避免冗余生成(默认值:1.0)

      • max_new_token (int): 最大生成token数(默认值:384)

      • min_new_token (int): 最小生成token数(默认值:0)

      • show_tqdm (bool): 是否在生成过程中显示进度条(默认值:True)

      • ensure_non_empty (bool): 确保生成非空结果(默认值:True)

    • infercode (dict): 推理和编码参数,影响音频质量:

      • prompt (str): 语速控制标签,例如:"[speed_5]"

      • spk_emb (可选): 说话人嵌入向量,用于指定音色特征(默认值:None)

      • temperature (float): 音频生成的采样温度(默认值:0.3)

      • repetition_penalty (float): 重复惩罚系数(默认值:1.05)

      • max_new_token (int): 音频生成的最大token数(默认值:2048)

Examples:

>>> from lazyllm import launchers, UrlModule
>>> from lazyllm.components import ChatTTSDeploy
>>> deployer = ChatTTSDeploy(launchers.remote())
>>> url = deployer(base_model='ChatTTS')
>>> model = UrlModule(url=url)
>>> res = model('Hello World!')
>>> print(res)
... <lazyllm-query>{"query": "", "files": ["path/to/chattts/sound_xxx.wav"]}
Source code in lazyllm/components/deploy/text_to_speech/chattts.py
class ChatTTSDeploy(TTSBase):
    """ChatTTS 模型部署类。

Keyword Args: 
    keys_name_handle (dict): 键名映射字典,用于处理内部和外部API接口之间的参数名称转换。
                            默认为 `{'inputs': 'inputs'}`。

    message_format (dict): 请求负载结构,包含三个主要部分:

        - `inputs` (str): 要合成为语音的原始文本内容。

        - `refinetext` (dict): 文本细化和风格化参数,控制语音表达:

            * `prompt` (str): 语音风格控制标签,例如:"[oral_2][laugh_0][break_6]"

            * `top_P` (float): 核采样参数,用于解码策略(默认值:0.7)

            * `top_K` (int): Top-K 采样参数(默认值:20)

            * `temperature` (float): 采样温度,控制随机性(默认值:0.7)

            * `repetition_penalty` (float): 重复惩罚,避免冗余生成(默认值:1.0)

            * `max_new_token` (int): 最大生成token数(默认值:384)

            * `min_new_token` (int): 最小生成token数(默认值:0)

            * `show_tqdm` (bool): 是否在生成过程中显示进度条(默认值:True)

            * `ensure_non_empty` (bool): 确保生成非空结果(默认值:True)

        - `infercode` (dict): 推理和编码参数,影响音频质量:

            * `prompt` (str): 语速控制标签,例如:"[speed_5]"

            * `spk_emb` (可选): 说话人嵌入向量,用于指定音色特征(默认值:None)

            * `temperature` (float): 音频生成的采样温度(默认值:0.3)

            * `repetition_penalty` (float): 重复惩罚系数(默认值:1.05)

            * `max_new_token` (int): 音频生成的最大token数(默认值:2048)



Examples:
    >>> from lazyllm import launchers, UrlModule
    >>> from lazyllm.components import ChatTTSDeploy
    >>> deployer = ChatTTSDeploy(launchers.remote())
    >>> url = deployer(base_model='ChatTTS')
    >>> model = UrlModule(url=url)
    >>> res = model('Hello World!')
    >>> print(res)
    ... <lazyllm-query>{"query": "", "files": ["path/to/chattts/sound_xxx.wav"]}
    """
    keys_name_handle = {
        'inputs': 'inputs',
    }
    message_format = {
        'inputs': 'Who are you ?',
        'refinetext': {
            'prompt': '[oral_2][laugh_0][break_6]',
            'top_P': 0.7,
            'top_K': 20,
            'temperature': 0.7,
            'repetition_penalty': 1.0,
            'max_new_token': 384,
            'min_new_token': 0,
            'show_tqdm': True,
            'ensure_non_empty': True,
        },
        'infercode': {
            'prompt': '[speed_5]',
            'spk_emb': None,
            'temperature': 0.3,
            'repetition_penalty': 1.05,
            'max_new_token': 2048,
        }

    }
    default_headers = {'Content-Type': 'application/json'}
    func = _ChatTTSModule

lazyllm.components.BarkDeploy

Bases: TTSBase

Bark 模型部署类。该类用于将Bark模型部署到指定服务器上,以便可以通过网络进行调用。

__init__(self, launcher=None) 构造函数,初始化部署类。

Parameters:

  • launcher (launcher, default: None ) –

    用于启动远程服务的启动器实例。

__call__(self, finetuned_model=None, base_model=None) 部署模型,并返回远程服务地址。

Parameters:

  • finetuned_model (str) –

    如果提供,则使用该模型进行部署;如果未提供或路径无效,则使用 base_model

  • base_model (str) –

    默认模型,如果 finetuned_model 无效,则使用该模型进行部署。

  • 返回值 (str) –

    远程服务的URL地址。

Notes
  • 推理的输入:字符串。待生成音频的对应文字。
  • 推理的返回值:从生成的文件路径编码的字符串, 编码标志以 ""开头,后面跟序列化后的字典, 字典中 files键存放了一个列表,元素是生成的音频文件路径。
  • 支持的模型为:bark

Examples:

>>> from lazyllm import launchers, UrlModule
>>> from lazyllm.components import BarkDeploy
>>> deployer = BarkDeploy(launchers.remote())
>>> url = deployer(base_model='bark')
>>> model = UrlModule(url=url)
>>> res = model('Hello World!')
>>> print(res)
... <lazyllm-query>{"query": "", "files": ["path/to/bark/sound_xxx.wav"]}
Source code in lazyllm/components/deploy/text_to_speech/bark.py
class BarkDeploy(TTSBase):
    """Bark 模型部署类。该类用于将Bark模型部署到指定服务器上,以便可以通过网络进行调用。

`__init__(self, launcher=None)`
构造函数,初始化部署类。

Args:
    launcher(lazyllm.launcher): 用于启动远程服务的启动器实例。

`__call__(self, finetuned_model=None, base_model=None)`
部署模型,并返回远程服务地址。

Args: 
    finetuned_model (str): 如果提供,则使用该模型进行部署;如果未提供或路径无效,则使用 `base_model`。
    base_model (str): 默认模型,如果 `finetuned_model` 无效,则使用该模型进行部署。
    返回值 (str): 远程服务的URL地址。

Notes:
    - 推理的输入:字符串。待生成音频的对应文字。
    - 推理的返回值:从生成的文件路径编码的字符串, 编码标志以 "<lazyllm-query>"开头,后面跟序列化后的字典, 字典中 `files`键存放了一个列表,元素是生成的音频文件路径。
    - 支持的模型为:[bark](https://huggingface.co/suno/bark)


Examples:
    >>> from lazyllm import launchers, UrlModule
    >>> from lazyllm.components import BarkDeploy
    >>> deployer = BarkDeploy(launchers.remote())
    >>> url = deployer(base_model='bark')
    >>> model = UrlModule(url=url)
    >>> res = model('Hello World!')
    >>> print(res)
    ... <lazyllm-query>{"query": "", "files": ["path/to/bark/sound_xxx.wav"]}
    """
    keys_name_handle = {
        'inputs': 'inputs',
    }
    message_format = {
        'inputs': 'Who are you ?',
        'voice_preset': None,
    }
    default_headers = {'Content-Type': 'application/json'}

    func = _Bark

lazyllm.components.MusicGenDeploy

Bases: TTSBase

MusicGen 模型部署类。该类用于将MusicGen模型部署到指定服务器上,以便可以通过网络进行调用。

__init__(self, launcher=None) 构造函数,初始化部署类。

Parameters:

  • launcher (launcher, default: None ) –

    用于启动远程服务的启动器实例。

__call__(self, finetuned_model=None, base_model=None) 部署模型,并返回远程服务地址。

Parameters:

  • finetuned_model (str) –

    如果提供,则使用该模型进行部署;如果未提供或路径无效,则使用 base_model

  • base_model (str) –

    默认模型,如果 finetuned_model 无效,则使用该模型进行部署。

  • 返回值 (str) –

    远程服务的URL地址。

Notes
  • 推理的输入:字符串。待生成音频的对应文字。
  • 推理的返回值:从生成的文件路径编码的字符串, 编码标志以 ""开头,后面跟序列化后的字典, 字典中 files键存放了一个列表,元素是生成的音频文件路径。
  • 支持的模型为:musicgen-small

Examples:

>>> from lazyllm import launchers, UrlModule
>>> from lazyllm.components import MusicGenDeploy
>>> deployer = MusicGenDeploy(launchers.remote())
>>> url = deployer(base_model='musicgen-small')
>>> model = UrlModule(url=url)
>>> model('Symphony with flute as the main melody')
... <lazyllm-query>{"query": "", "files": ["path/to/musicgen/sound_xxx.wav"]}
Source code in lazyllm/components/deploy/text_to_speech/musicgen.py
class MusicGenDeploy(TTSBase):
    """MusicGen 模型部署类。该类用于将MusicGen模型部署到指定服务器上,以便可以通过网络进行调用。

`__init__(self, launcher=None)`
构造函数,初始化部署类。

Args:
    launcher(lazyllm.launcher): 用于启动远程服务的启动器实例。

`__call__(self, finetuned_model=None, base_model=None)`
部署模型,并返回远程服务地址。

Args: 
    finetuned_model (str): 如果提供,则使用该模型进行部署;如果未提供或路径无效,则使用 `base_model`。
    base_model (str): 默认模型,如果 `finetuned_model` 无效,则使用该模型进行部署。
    返回值 (str): 远程服务的URL地址。

Notes:
    - 推理的输入:字符串。待生成音频的对应文字。
    - 推理的返回值:从生成的文件路径编码的字符串, 编码标志以 "<lazyllm-query>"开头,后面跟序列化后的字典, 字典中 `files`键存放了一个列表,元素是生成的音频文件路径。
    - 支持的模型为:[musicgen-small](https://huggingface.co/facebook/musicgen-small)


Examples:
    >>> from lazyllm import launchers, UrlModule
    >>> from lazyllm.components import MusicGenDeploy
    >>> deployer = MusicGenDeploy(launchers.remote())
    >>> url = deployer(base_model='musicgen-small')
    >>> model = UrlModule(url=url)
    >>> model('Symphony with flute as the main melody')
    ... <lazyllm-query>{"query": "", "files": ["path/to/musicgen/sound_xxx.wav"]}
    """
    message_format = None
    keys_name_handle = None
    default_headers = {'Content-Type': 'application/json'}
    func = _MusicGen

Speech to Text

lazyllm.components.SenseVoiceDeploy

Bases: LazyLLMDeployBase

SenseVoice 模型部署类。该类用于将SenseVoice模型部署到指定服务器上,以便可以通过网络进行调用。

__init__(self, launcher=None) 构造函数,初始化部署类。

Parameters:

  • launcher (Optional[LazyLLMLaunchersBase], default: None ) –

    Launcher instance, defaults to None.

  • log_path (Optional[str], default: None ) –

    Log file path, defaults to None.

  • trust_remote_code (bool, default: True ) –

    Whether to trust remote code, defaults to True.

  • port (Optional[int], default: None ) –

    Service port number, defaults to None.

Notes
  • 推理的输入:字符串。音频路径或者链接。
  • 推理的返回值:字符串。识别出的内容。
  • 支持的模型为:SenseVoiceSmall

Examples:

>>> import os
>>> import lazyllm
>>> from lazyllm import launchers, UrlModule
>>> from lazyllm.components import SenseVoiceDeploy
>>> deployer = SenseVoiceDeploy(launchers.remote())
>>> url = deployer(base_model='SenseVoiceSmall')
>>> model = UrlModule(url=url)
>>> model('path/to/audio') # support format: .mp3, .wav
... xxxxxxxxxxxxxxxx
Source code in lazyllm/components/deploy/speech_to_text/sense_voice.py
class SenseVoiceDeploy(LazyLLMDeployBase):
    """SenseVoice 模型部署类。该类用于将SenseVoice模型部署到指定服务器上,以便可以通过网络进行调用。

`__init__(self, launcher=None)`
构造函数,初始化部署类。

Args:
    launcher (Optional[LazyLLMLaunchersBase]): Launcher instance, defaults to None.
    log_path (Optional[str]): Log file path, defaults to None.
    trust_remote_code (bool): Whether to trust remote code, defaults to True.
    port (Optional[int]): Service port number, defaults to None.

Notes:
    - 推理的输入:字符串。音频路径或者链接。
    - 推理的返回值:字符串。识别出的内容。
    - 支持的模型为:[SenseVoiceSmall](https://huggingface.co/FunAudioLLM/SenseVoiceSmall)


Examples:
    >>> import os
    >>> import lazyllm
    >>> from lazyllm import launchers, UrlModule
    >>> from lazyllm.components import SenseVoiceDeploy
    >>> deployer = SenseVoiceDeploy(launchers.remote())
    >>> url = deployer(base_model='SenseVoiceSmall')
    >>> model = UrlModule(url=url)
    >>> model('path/to/audio') # support format: .mp3, .wav
    ... xxxxxxxxxxxxxxxx
    """
    keys_name_handle = {
        'inputs': 'inputs',
        'audio': 'audio',
    }
    message_format = {
        'inputs': 'Who are you ?',
        'audio': None,
    }
    default_headers = {'Content-Type': 'application/json'}

    def __init__(self, launcher: Optional[LazyLLMLaunchersBase] = None,
                 log_path: Optional[str] = None, trust_remote_code: bool = True, port: Optional[int] = None, **kw):
        super().__init__(launcher=launcher)
        self._log_path = log_path
        self._trust_remote_code = trust_remote_code
        self._port = port

    def __call__(self, finetuned_model=None, base_model=None):
        if not finetuned_model:
            finetuned_model = base_model
        elif not os.path.exists(finetuned_model) or \
            not any(file.endswith(('.pt', '.bin', '.safetensors'))
                    for _, _, filenames in os.walk(finetuned_model) for file in filenames):
            LOG.warning(f'Note! That finetuned_model({finetuned_model}) is an invalid path, '
                        f'base_model({base_model}) will be used')
            finetuned_model = base_model
        return lazyllm.deploy.RelayServer(port=self._port, func=SenseVoice(finetuned_model), launcher=self._launcher,
                                          log_path=self._log_path, cls='sensevoice')()

lazyllm.components.deploy.speech_to_text.sense_voice.SenseVoice

Bases: object

SenseVoice 类,封装了基于 FunASR 的语音转文本模型加载与调用逻辑。
支持懒加载、自动模型下载,输入可为字符串路径、URL 或包含音频的字典。

Parameters:

  • base_path (str) –

    模型路径或标识符,将通过 ModelManager 下载到本地。

  • source (Optional[str], default: None ) –

    模型来源,若未指定则使用 lazyllm.config['model_source']

  • init (bool, default: False ) –

    是否在初始化时立即加载模型,默认为 False

Attributes:

  • base_path (str) –

    下载或解析后的模型路径。

  • model (Optional[AutoModel]) –

    FunASR 语音识别模型实例,初始化后可用。

  • init_flag

    用于懒加载的标志,确保模型只加载一次。

Source code in lazyllm/components/deploy/speech_to_text/sense_voice.py
class SenseVoice(object):
    """SenseVoice 类,封装了基于 FunASR 的语音转文本模型加载与调用逻辑。  
支持懒加载、自动模型下载,输入可为字符串路径、URL 或包含音频的字典。  

Args:
    base_path (str): 模型路径或标识符,将通过 ModelManager 下载到本地。  
    source (Optional[str]): 模型来源,若未指定则使用 ``lazyllm.config['model_source']``。  
    init (bool): 是否在初始化时立即加载模型,默认为 ``False``。  

Attributes:
    base_path (str): 下载或解析后的模型路径。  
    model (Optional[funasr.AutoModel]): FunASR 语音识别模型实例,初始化后可用。  
    init_flag: 用于懒加载的标志,确保模型只加载一次。  
"""
    def __init__(self, base_path, source=None, init=False):
        source = lazyllm.config['model_source'] if not source else source
        self.base_path = ModelManager(source).download(base_path) or ''
        self.model = None
        self.init_flag = lazyllm.once_flag()
        if init:
            lazyllm.call_once(self.init_flag, self.load_stt)

    def load_stt(self):
        """初始化并加载 FunASR 语音转文本模型,如果存在 `torch_npu` 则支持华为 NPU 加速。

使用 `fsmn-vad` 进行语音活动检测(VAD),支持长语音段。
单段语音最大持续时间为 30 秒。
默认推理设备为 `cuda:0`(GPU)。

加载的模型将保存在 `self.model` 中,用于后续音频转写。

注意:
- 如果当前环境中存在 `torch_npu`,函数会导入以支持昇腾 NPU 加速。
"""
        if importlib.util.find_spec('torch_npu') is not None:
            import torch_npu  # noqa F401
            from torch_npu.contrib import transfer_to_npu  # noqa F401

        self.model = funasr.AutoModel(
            model=self.base_path,
            trust_remote_code=False,
            vad_model='fsmn-vad',
            vad_kwargs={'max_single_segment_time': 30000},
            device='cuda:0',
        )

    def __call__(self, string):
        lazyllm.call_once(self.init_flag, self.load_stt)
        if isinstance(string, dict):
            if string['audio']:
                string = string['audio'][-1] if isinstance(string['audio'], list) else string['audio']
            else:
                string = string['inputs']
        assert isinstance(string, str)
        string = string.strip()
        try:
            string = _base64_to_file(string) if _is_base64_with_mime(string) else string
        except Exception as e:
            LOG.error(f'Error processing base64 encoding: {e}')
            return f'Error processing base64 encoding {e}'
        if not string.endswith(supported_formats):
            return f'Only {", ".join(supported_formats)} formats in the form of file paths or URLs are supported.'
        if not is_valid_path(string) and not is_valid_url(string):
            return f'This {string} is not a valid URL or file path. Please check.'
        res = self.model.generate(
            input=string,
            cache={},
            language='auto',  # 'zn', 'en', 'yue', 'ja', 'ko', 'nospeech'
            use_itn=True,
            batch_size_s=60,
            merge_vad=True,
            merge_length_s=15,
        )
        text = funasr.utils.postprocess_utils.rich_transcription_postprocess(res[0]['text'])
        return text

    @classmethod
    def rebuild(cls, base_path, init):
        """类方法,用于在反序列化过程中重新构建 `SenseVoice` 实例(例如使用 `cloudpickle`)。  

Args:
    base_path (str): 语音识别模型路径。  
    init (bool): 实例化时是否立即初始化并加载模型。

**Returns:**

- SenseVoice: 返回一个新的 `SenseVoice` 实例,用于支持序列化/多进程兼容。
"""
        return cls(base_path, init=init)

    def __reduce__(self):
        init = bool(os.getenv('LAZYLLM_ON_CLOUDPICKLE', None) == 'ON' or self.init_flag)
        return SenseVoice.rebuild, (self.base_path, init)
load_stt()

初始化并加载 FunASR 语音转文本模型,如果存在 torch_npu 则支持华为 NPU 加速。

使用 fsmn-vad 进行语音活动检测(VAD),支持长语音段。 单段语音最大持续时间为 30 秒。 默认推理设备为 cuda:0(GPU)。

加载的模型将保存在 self.model 中,用于后续音频转写。

注意: - 如果当前环境中存在 torch_npu,函数会导入以支持昇腾 NPU 加速。

Source code in lazyllm/components/deploy/speech_to_text/sense_voice.py
    def load_stt(self):
        """初始化并加载 FunASR 语音转文本模型,如果存在 `torch_npu` 则支持华为 NPU 加速。

使用 `fsmn-vad` 进行语音活动检测(VAD),支持长语音段。
单段语音最大持续时间为 30 秒。
默认推理设备为 `cuda:0`(GPU)。

加载的模型将保存在 `self.model` 中,用于后续音频转写。

注意:
- 如果当前环境中存在 `torch_npu`,函数会导入以支持昇腾 NPU 加速。
"""
        if importlib.util.find_spec('torch_npu') is not None:
            import torch_npu  # noqa F401
            from torch_npu.contrib import transfer_to_npu  # noqa F401

        self.model = funasr.AutoModel(
            model=self.base_path,
            trust_remote_code=False,
            vad_model='fsmn-vad',
            vad_kwargs={'max_single_segment_time': 30000},
            device='cuda:0',
        )
rebuild(base_path, init) classmethod

类方法,用于在反序列化过程中重新构建 SenseVoice 实例(例如使用 cloudpickle)。

Parameters:

  • base_path (str) –

    语音识别模型路径。

  • init (bool) –

    实例化时是否立即初始化并加载模型。

Returns:

  • SenseVoice: 返回一个新的 SenseVoice 实例,用于支持序列化/多进程兼容。
Source code in lazyllm/components/deploy/speech_to_text/sense_voice.py
    @classmethod
    def rebuild(cls, base_path, init):
        """类方法,用于在反序列化过程中重新构建 `SenseVoice` 实例(例如使用 `cloudpickle`)。  

Args:
    base_path (str): 语音识别模型路径。  
    init (bool): 实例化时是否立即初始化并加载模型。

**Returns:**

- SenseVoice: 返回一个新的 `SenseVoice` 实例,用于支持序列化/多进程兼容。
"""
        return cls(base_path, init=init)

ModelManager

lazyllm.components.ModelManager

ModelManager 是 LazyLLM 提供的模型管理与下载工具类,支持本地搜索和 Huggingface/Modelscope 下载。

Parameters:

  • model_source (Optional[str]) –

    模型下载源,仅支持 huggingfacemodelscope。 未提供时使用 LAZYLLM_MODEL_SOURCE,若未设置则默认 modelscope

  • token (Optional[str], default: config['model_source_token'] ) –

    下载私有模型的访问令牌。未提供时使用 LAZYLLM_MODEL_SOURCE_TOKEN。

  • model_path (Optional[str], default: config['model_path'] ) –

    冒号分隔的本地绝对路径列表,用于下载前搜索模型。未提供时使用 LAZYLLM_MODEL_PATH。

  • cache_dir (Optional[str], default: config['model_cache_dir'] ) –

    本地缓存目录,用于存放下载的模型。未提供时使用 LAZYLLM_MODEL_CACHE_DIR,默认 ~/.lazyllm/model

Static Methods

get_model_type(model: str) -> str 返回指定模型类型,如 llmchat,未识别返回 llm。 get_model_prompt_keys(model: str) -> dict 返回模型的 prompt key 映射字典。 validate_model_path(model_path: str) -> bool 检查目录下是否存在有效模型文件(扩展名: .pt, .bin, .safetensors)。

Instance Methods

download(model: Optional[str] = '', call_back: Optional[Callable] = None) -> str | bool 下载指定模型。流程: 1. 在 model_path 列出的本地目录搜索; 2. 未找到则在 cache_dir 下搜索; 3. 仍未找到则从 model_source 下载并存放 cache_dir。

Args:
    model (Optional[str]): 目标模型名称,可使用简略名称或下载源完整名称。
    call_back (Optional[Callable]): 下载进度回调函数,可选。

Examples:

>>> from lazyllm.components import ModelManager
>>> downloader = ModelManager(model_source='modelscope')
>>> downloader.download('chatglm3-6b')
Source code in lazyllm/components/utils/downloader/model_downloader.py
class ModelManager():
    """ModelManager 是 LazyLLM 提供的模型管理与下载工具类,支持本地搜索和 Huggingface/Modelscope 下载。  

Args:
    model_source (Optional[str]): 模型下载源,仅支持 ``huggingface`` 或 ``modelscope``。
        未提供时使用 LAZYLLM_MODEL_SOURCE,若未设置则默认 ``modelscope``。
    token (Optional[str]): 下载私有模型的访问令牌。未提供时使用 LAZYLLM_MODEL_SOURCE_TOKEN。
    model_path (Optional[str]): 冒号分隔的本地绝对路径列表,用于下载前搜索模型。未提供时使用 LAZYLLM_MODEL_PATH。
    cache_dir (Optional[str]): 本地缓存目录,用于存放下载的模型。未提供时使用 LAZYLLM_MODEL_CACHE_DIR,默认 ``~/.lazyllm/model``。

Static Methods:
    get_model_type(model: str) -> str
        返回指定模型类型,如 ``llm``、``chat``,未识别返回 ``llm``。
    get_model_prompt_keys(model: str) -> dict
        返回模型的 prompt key 映射字典。
    validate_model_path(model_path: str) -> bool
        检查目录下是否存在有效模型文件(扩展名: ``.pt``, ``.bin``, ``.safetensors``)。

Instance Methods:
    download(model: Optional[str] = '', call_back: Optional[Callable] = None) -> str | bool
        下载指定模型。流程:
        1. 在 model_path 列出的本地目录搜索;
        2. 未找到则在 cache_dir 下搜索;
        3. 仍未找到则从 model_source 下载并存放 cache_dir。

        Args:
            model (Optional[str]): 目标模型名称,可使用简略名称或下载源完整名称。
            call_back (Optional[Callable]): 下载进度回调函数,可选。


Examples:
    >>> from lazyllm.components import ModelManager
    >>> downloader = ModelManager(model_source='modelscope')
    >>> downloader.download('chatglm3-6b')
    """
    def __init__(self, model_source, token=lazyllm.config['model_source_token'],
                 cache_dir=lazyllm.config['model_cache_dir'], model_path=lazyllm.config['model_path']):
        self.model_source = model_source or lazyllm.config['model_source']
        self.token = token or None
        self.cache_dir = cache_dir
        self.model_paths = model_path.split(':') if len(model_path) > 0 else []
        if self.model_source == 'huggingface':
            self.hub_downloader = _HuggingfaceDownloader(token=self.token)
        else:
            self.hub_downloader = _ModelscopeDownloader(token=self.token)
            if self.model_source != 'modelscope':
                lazyllm.LOG.error('Only support Huggingface and Modelscope currently. '
                                  f'Unsupported model source: {self.model_source}. Forcing use of Modelscope.')

    @staticmethod
    @functools.lru_cache
    def get_model_type(model) -> str:
        """根据模型名称获取模型类型(如 LLM、VLM 等)。

Args:
    model (str): 模型名称或路径,必须为非空字符串。

**Returns:**

- str: 模型类型,如果无法匹配则返回 ``llm``。
"""
        assert isinstance(model, str) and len(model) > 0, f'model name should be a non-empty string, get {model}'
        __class__._try_add_mapping(model)
        for name, info in model_name_mapping.items():
            if 'type' not in info: continue

            model_name_set = {name.casefold()}
            for source in info['source']:
                model_name_set.add(info['source'][source].split('/')[-1].casefold())

            if model.split(os.sep)[-1].casefold() in model_name_set:
                return info['type']
        return infer_model_type(model)

    @staticmethod
    @functools.lru_cache
    def _get_model_name(model) -> str:
        search_string = os.path.basename(model)
        __class__._try_add_mapping(search_string)
        for model_name, sources in model_name_mapping.items():
            if model_name.lower() == search_string.lower() or any(
                    os.path.basename(source_file).lower() == search_string.lower()
                    for source_file in sources['source'].values()):
                return model_name
        return ''

    @staticmethod
    @functools.lru_cache
    def get_model_prompt_keys(model) -> dict:
        """获取指定模型的 prompt key 映射字典,用于推理时构建输入。  

Args:
    model (str): 模型名称或路径。

**Returns:**

- dict: 模型对应的 prompt key 映射,如果不存在则返回空字典
"""
        model_name = __class__._get_model_name(model)
        __class__._try_add_mapping(model_name)
        if model_name and 'prompt_keys' in model_name_mapping[model_name.lower()]:
            return model_name_mapping[model_name.lower()]['prompt_keys']
        else:
            return dict()

    @staticmethod
    def validate_model_path(model_path):
        """检查指定路径下是否存在有效的模型文件(.pt, .bin, .safetensors)。  

Args:
    model_path (str): 模型目录路径。

**Returns:**

- bool: 如果目录中存在模型文件返回 True,否则返回 False
"""
        extensions = {'.pt', '.bin', '.safetensors'}
        for _, _, files in os.walk(model_path):
            for file in files:
                if any(file.endswith(ext) for ext in extensions):
                    return True
        return False

    @staticmethod
    def _try_add_mapping(model):
        model_base = os.path.basename(model)
        model = model_base.lower()
        if model in model_name_mapping.keys():
            return
        matched_model_prefix = next((key for key in model_provider if model.startswith(key)), None)
        if matched_model_prefix:
            matching_keys = [key for key in model_groups.keys() if key in model]
            if matching_keys:
                matched_groups = max(matching_keys, key=len)
                model_name_mapping[model] = {
                    'prompt_keys': model_groups[matched_groups]['prompt_keys'],
                    'source': {k: v + '/' + model_base for k, v in model_provider[matched_model_prefix].items()}
                }

    def download(self, model='', call_back=None):
        """下载指定名称的模型,如果本地已有则直接返回本地路径;  
支持 Huggingface 和 Modelscope 平台的自动下载,并会在缓存目录创建符号链接以便统一管理。  

Args:
    model (str, optional): 模型名称或路径,默认为空字符串,表示不下载。
    call_back (Optional[Callable], optional): 下载进度回调函数,接受当前下载状态等参数。  

**Returns:**

- str | bool: 模型在本地的完整路径,如果下载失败返回 False
"""
        assert isinstance(model, str), 'model name should be a string.'
        if len(model) == 0 or model[0] in (os.sep, '.', '~') or os.path.isabs(model): return model
        if (model_at_path := self._model_exists_at_path(model)): return model_at_path
        if self.model_source == '' or self.model_source not in ('huggingface', 'modelscope'):
            lazyllm.LOG.error('model automatic downloads only support Huggingface and Modelscope currently.')
            return model

        self._try_add_mapping(model)
        if model_name_mapping.get(model.lower(), {}).get('download_by_other'): return model

        if model.lower() in model_name_mapping.keys() and \
                self.model_source in model_name_mapping[model.lower()]['source'].keys():
            full_model_dir = os.path.join(self.cache_dir, model)

            mapped_model_name = model_name_mapping[model.lower()]['source'][self.model_source]
            model_save_dir = self._do_download(mapped_model_name, call_back)
            if model_save_dir:
                # The code safely creates a symbolic link by removing any existing target.
                if os.path.exists(full_model_dir):
                    os.remove(full_model_dir)
                if os.path.islink(full_model_dir):
                    os.unlink(full_model_dir)
                os.symlink(model_save_dir, full_model_dir, target_is_directory=True)
                return full_model_dir
            return model_save_dir  # return False
        else:
            model_name_for_download = model

            if '/' not in model_name_for_download:
                # Try to figure out a possible model provider
                matched_model_prefix = next((key for key in model_provider if model.lower().startswith(key)), None)
                if matched_model_prefix and self.model_source in model_provider[matched_model_prefix]:
                    model_name_for_download = model_provider[matched_model_prefix][self.model_source] + '/' + model

            model_save_dir = self._do_download(model_name_for_download, call_back)
            return model_save_dir

    def _validate_token(self):
        return self.hub_downloader.verify_hub_token()

    def _validate_model_id(self, model_id):
        return self.hub_downloader._verify_model_id(model_id)

    def _model_exists_at_path(self, model_name):
        if len(self.model_paths) == 0:
            return None
        model_dirs = []

        # For short model name, get all possible names from the mapping.
        if model_name.lower() in model_name_mapping.keys():
            for source in ('huggingface', 'modelscope'):
                if source in model_name_mapping[model_name.lower()]['source'].keys():
                    model_dirs.append(model_name_mapping[model_name.lower()]['source'][source].replace('/', os.sep))
        model_dirs.append(model_name.replace('/', os.sep))

        for model_path in self.model_paths:
            if len(model_path) == 0: continue
            if model_path[0] != os.sep:
                lazyllm.LOG.warning(f'skipping path {model_path} as only absolute paths is accepted.')
                continue
            for model_dir in model_dirs:
                full_model_dir = os.path.join(model_path, model_dir)
                if self._is_model_valid(full_model_dir):
                    return full_model_dir
        return None

    def _is_model_valid(self, model_dir):
        if not os.path.isdir(model_dir):
            return False
        return any((True for _ in os.scandir(model_dir)))

    def _do_download(self, model='', call_back=None):
        model_dir = model.replace('/', os.sep)
        full_model_dir = os.path.join(self.cache_dir, self.model_source, model_dir)

        try:
            return self.hub_downloader.download(model, full_model_dir, call_back)
        # Use `BaseException` to capture `KeyboardInterrupt` and normal `Exceptioin`.
        except BaseException as e:  # noqa B036
            lazyllm.LOG.warning(f'Download encountered an error: {e}')
            if '401' in str(e) or 'Client Error' in str(e):
                raise RuntimeError('Authentication failed (401 Error). Please check your access token and '
                                   'permissions.  And set the token with the environment variable '
                                   'LAZYLLM_MODEL_SOURCE_TOKEN.')
            if not self.token and 'Permission denied' not in str(e):
                lazyllm.LOG.warning('Token is empty, which may prevent private models from being downloaded, '
                                    'as indicated by "the model does not exist." Please set the token with the '
                                    'environment variable LAZYLLM_MODEL_SOURCE_TOKEN to download private models.')
            raise RuntimeError(f'Model download failed for model: {model}, with error: {e}')
        return False

get_model_type(model) cached staticmethod

根据模型名称获取模型类型(如 LLM、VLM 等)。

Parameters:

  • model (str) –

    模型名称或路径,必须为非空字符串。

Returns:

  • str: 模型类型,如果无法匹配则返回 llm
Source code in lazyllm/components/utils/downloader/model_downloader.py
    @staticmethod
    @functools.lru_cache
    def get_model_type(model) -> str:
        """根据模型名称获取模型类型(如 LLM、VLM 等)。

Args:
    model (str): 模型名称或路径,必须为非空字符串。

**Returns:**

- str: 模型类型,如果无法匹配则返回 ``llm``。
"""
        assert isinstance(model, str) and len(model) > 0, f'model name should be a non-empty string, get {model}'
        __class__._try_add_mapping(model)
        for name, info in model_name_mapping.items():
            if 'type' not in info: continue

            model_name_set = {name.casefold()}
            for source in info['source']:
                model_name_set.add(info['source'][source].split('/')[-1].casefold())

            if model.split(os.sep)[-1].casefold() in model_name_set:
                return info['type']
        return infer_model_type(model)

get_model_prompt_keys(model) cached staticmethod

获取指定模型的 prompt key 映射字典,用于推理时构建输入。

Parameters:

  • model (str) –

    模型名称或路径。

Returns:

  • dict: 模型对应的 prompt key 映射,如果不存在则返回空字典
Source code in lazyllm/components/utils/downloader/model_downloader.py
    @staticmethod
    @functools.lru_cache
    def get_model_prompt_keys(model) -> dict:
        """获取指定模型的 prompt key 映射字典,用于推理时构建输入。  

Args:
    model (str): 模型名称或路径。

**Returns:**

- dict: 模型对应的 prompt key 映射,如果不存在则返回空字典
"""
        model_name = __class__._get_model_name(model)
        __class__._try_add_mapping(model_name)
        if model_name and 'prompt_keys' in model_name_mapping[model_name.lower()]:
            return model_name_mapping[model_name.lower()]['prompt_keys']
        else:
            return dict()

validate_model_path(model_path) staticmethod

检查指定路径下是否存在有效的模型文件(.pt, .bin, .safetensors)。

Parameters:

  • model_path (str) –

    模型目录路径。

Returns:

  • bool: 如果目录中存在模型文件返回 True,否则返回 False
Source code in lazyllm/components/utils/downloader/model_downloader.py
    @staticmethod
    def validate_model_path(model_path):
        """检查指定路径下是否存在有效的模型文件(.pt, .bin, .safetensors)。  

Args:
    model_path (str): 模型目录路径。

**Returns:**

- bool: 如果目录中存在模型文件返回 True,否则返回 False
"""
        extensions = {'.pt', '.bin', '.safetensors'}
        for _, _, files in os.walk(model_path):
            for file in files:
                if any(file.endswith(ext) for ext in extensions):
                    return True
        return False

download(model='', call_back=None)

下载指定名称的模型,如果本地已有则直接返回本地路径;
支持 Huggingface 和 Modelscope 平台的自动下载,并会在缓存目录创建符号链接以便统一管理。

Parameters:

  • model (str, default: '' ) –

    模型名称或路径,默认为空字符串,表示不下载。

  • call_back (Optional[Callable], default: None ) –

    下载进度回调函数,接受当前下载状态等参数。

Returns:

  • str | bool: 模型在本地的完整路径,如果下载失败返回 False
Source code in lazyllm/components/utils/downloader/model_downloader.py
    def download(self, model='', call_back=None):
        """下载指定名称的模型,如果本地已有则直接返回本地路径;  
支持 Huggingface 和 Modelscope 平台的自动下载,并会在缓存目录创建符号链接以便统一管理。  

Args:
    model (str, optional): 模型名称或路径,默认为空字符串,表示不下载。
    call_back (Optional[Callable], optional): 下载进度回调函数,接受当前下载状态等参数。  

**Returns:**

- str | bool: 模型在本地的完整路径,如果下载失败返回 False
"""
        assert isinstance(model, str), 'model name should be a string.'
        if len(model) == 0 or model[0] in (os.sep, '.', '~') or os.path.isabs(model): return model
        if (model_at_path := self._model_exists_at_path(model)): return model_at_path
        if self.model_source == '' or self.model_source not in ('huggingface', 'modelscope'):
            lazyllm.LOG.error('model automatic downloads only support Huggingface and Modelscope currently.')
            return model

        self._try_add_mapping(model)
        if model_name_mapping.get(model.lower(), {}).get('download_by_other'): return model

        if model.lower() in model_name_mapping.keys() and \
                self.model_source in model_name_mapping[model.lower()]['source'].keys():
            full_model_dir = os.path.join(self.cache_dir, model)

            mapped_model_name = model_name_mapping[model.lower()]['source'][self.model_source]
            model_save_dir = self._do_download(mapped_model_name, call_back)
            if model_save_dir:
                # The code safely creates a symbolic link by removing any existing target.
                if os.path.exists(full_model_dir):
                    os.remove(full_model_dir)
                if os.path.islink(full_model_dir):
                    os.unlink(full_model_dir)
                os.symlink(model_save_dir, full_model_dir, target_is_directory=True)
                return full_model_dir
            return model_save_dir  # return False
        else:
            model_name_for_download = model

            if '/' not in model_name_for_download:
                # Try to figure out a possible model provider
                matched_model_prefix = next((key for key in model_provider if model.lower().startswith(key)), None)
                if matched_model_prefix and self.model_source in model_provider[matched_model_prefix]:
                    model_name_for_download = model_provider[matched_model_prefix][self.model_source] + '/' + model

            model_save_dir = self._do_download(model_name_for_download, call_back)
            return model_save_dir

Formatter

lazyllm.components.formatter.LazyLLMFormatterBase

此类是格式化器的基类,格式化器是模型输出结果的格式化器,用户可以自定义格式化器,也可以使用LazyLLM提供的格式化器。

Examples:

>>> from lazyllm.components.formatter import LazyLLMFormatterBase
>>> class MyFormatter(LazyLLMFormatterBase):
...     def __init__(self, formatter: str = None):
...         self._formatter = formatter
...         if self._formatter:
...             self._parse_formatter()
...         else:
...             self._slices = None
...     def _parse_formatter(self):
...         slice_str = self._formatter.strip()[1:-1]
...         slices = []
...         parts = slice_str.split(":")
...         start = int(parts[0]) if parts[0] else None
...         end = int(parts[1]) if len(parts) > 1 and parts[1] else None
...         step = int(parts[2]) if len(parts) > 2 and parts[2] else None
...         slices.append(slice(start, end, step))
...         self._slices = slices
...     def _load(self, data):
...         return [int(x) for x in data.strip('[]').split(',')]
...     def _parse_py_data_by_formatter(self, data):
...         if self._slices is not None:
...             result = []
...             for s in self._slices:
...                 if isinstance(s, slice):
...                     result.extend(data[s])
...                 else:
...                     result.append(data[int(s)])
...             return result
...         else:
...             return data
...
>>> fmt = MyFormatter("[1:3]")
>>> res = fmt.format("[1,2,3,4,5]")
>>> print(res)
[2, 3]
Source code in lazyllm/components/formatter/formatterbase.py
class LazyLLMFormatterBase(metaclass=LazyLLMRegisterMetaClass):
    """此类是格式化器的基类,格式化器是模型输出结果的格式化器,用户可以自定义格式化器,也可以使用LazyLLM提供的格式化器。


Examples:
    >>> from lazyllm.components.formatter import LazyLLMFormatterBase
    >>> class MyFormatter(LazyLLMFormatterBase):
    ...     def __init__(self, formatter: str = None):
    ...         self._formatter = formatter
    ...         if self._formatter:
    ...             self._parse_formatter()
    ...         else:
    ...             self._slices = None
    ...     def _parse_formatter(self):
    ...         slice_str = self._formatter.strip()[1:-1]
    ...         slices = []
    ...         parts = slice_str.split(":")
    ...         start = int(parts[0]) if parts[0] else None
    ...         end = int(parts[1]) if len(parts) > 1 and parts[1] else None
    ...         step = int(parts[2]) if len(parts) > 2 and parts[2] else None
    ...         slices.append(slice(start, end, step))
    ...         self._slices = slices
    ...     def _load(self, data):
    ...         return [int(x) for x in data.strip('[]').split(',')]
    ...     def _parse_py_data_by_formatter(self, data):
    ...         if self._slices is not None:
    ...             result = []
    ...             for s in self._slices:
    ...                 if isinstance(s, slice):
    ...                     result.extend(data[s])
    ...                 else:
    ...                     result.append(data[int(s)])
    ...             return result
    ...         else:
    ...             return data
    ...
    >>> fmt = MyFormatter("[1:3]")
    >>> res = fmt.format("[1,2,3,4,5]")
    >>> print(res)
    [2, 3]
    """
    def _load(self, msg: str):
        return msg

    def _parse_py_data_by_formatter(self, py_data):
        raise NotImplementedError('This data parse function is not implemented.')

    def format(self, msg):
        """格式化输入消息。

Args:
    msg: 输入消息,可以是字符串或其他格式

**Returns:**

- 格式化后的数据,具体类型由子类实现决定
"""
        if _is_chat_message(msg): msg = msg['content']
        if isinstance(msg, str): msg = self._load(msg)
        return self._parse_py_data_by_formatter(msg)

    def __call__(self, *msg):
        return self.format(msg[0] if len(msg) == 1 else package(msg))

    def __or__(self, other):
        if not isinstance(other, LazyLLMFormatterBase):
            return NotImplemented
        return PipelineFormatter(other.__ror__(self))

    def __ror__(self, f: Callable) -> Pipeline:
        if isinstance(f, Pipeline):
            if not f._capture:
                _ = Finalizer(lambda: setattr(f, '_capture', True), lambda: setattr(f, '_capture', False))
            f._add(str(uuid.uuid4().hex) if len(f._item_names) else None, self)
            return f
        return Pipeline(f, self)

format(msg)

格式化输入消息。

Parameters:

  • msg

    输入消息,可以是字符串或其他格式

Returns:

  • 格式化后的数据,具体类型由子类实现决定
Source code in lazyllm/components/formatter/formatterbase.py
    def format(self, msg):
        """格式化输入消息。

Args:
    msg: 输入消息,可以是字符串或其他格式

**Returns:**

- 格式化后的数据,具体类型由子类实现决定
"""
        if _is_chat_message(msg): msg = msg['content']
        if isinstance(msg, str): msg = self._load(msg)
        return self._parse_py_data_by_formatter(msg)

lazyllm.components.formatter.formatterbase.JsonLikeFormatter

Bases: LazyLLMFormatterBase

该类用于以类 JSON 的格式提取嵌套结构(如 dict、list、tuple)中的子字段内容。

其功能通过格式化字符串 formatter 来控制,格式类似于数组/字典的索引切片表达式。例如:

  • [0] 表示取第 0 个元素
  • [0][{key}] 表示取第 0 个元素并获取其中 key 字段
  • [0,1][{a,b}] 表示同时提取第 0 和第 1 个对象的 a 和 b 字段
  • [::2] 表示步长为 2 的切片
  • *[0][{x}] 表示以包装格式返回处理后的数据(用于进一步结构化)

Parameters:

  • formatter (str, default: None ) –

    控制提取规则的格式字符串。若为 None,则返回原始数据。

Examples:

>>> from lazyllm.components.formatter.formatterbase import JsonLikeFormatter
>>> formatter = JsonLikeFormatter("[{a,b}]")
Source code in lazyllm/components/formatter/formatterbase.py
class JsonLikeFormatter(LazyLLMFormatterBase):
    """该类用于以类 JSON 的格式提取嵌套结构(如 dict、list、tuple)中的子字段内容。

其功能通过格式化字符串 `formatter` 来控制,格式类似于数组/字典的索引切片表达式。例如:

- `[0]` 表示取第 0 个元素
- `[0][{key}]` 表示取第 0 个元素并获取其中 key 字段
- `[0,1][{a,b}]` 表示同时提取第 0 和第 1 个对象的 a 和 b 字段
- `[::2]` 表示步长为 2 的切片
- `*[0][{x}]` 表示以包装格式返回处理后的数据(用于进一步结构化)

Args:
    formatter (str, optional): 控制提取规则的格式字符串。若为 None,则返回原始数据。


Examples:
    >>> from lazyllm.components.formatter.formatterbase import JsonLikeFormatter
    >>> formatter = JsonLikeFormatter("[{a,b}]")
    """
    class _ListIdxes(tuple): pass
    class _DictKeys(tuple): pass

    def __init__(self, formatter: Optional[str] = None):
        if formatter and formatter.startswith('*['):
            self._return_package = True
            self._formatter = formatter.strip('*')
        else:
            self._return_package = False
            self._formatter = formatter

        if self._formatter:
            assert '*' not in self._formatter, '`*` can only be used before `[` in the beginning'
            self._formatter = self._formatter.strip().replace('{', '[{').replace('}', '}]')
            self._parse_formatter()
        else:
            self._slices = None

    def _parse_formatter(self):
        # Remove the surrounding brackets
        assert self._formatter.startswith('[') and self._formatter.endswith(']')
        slice_str = self._formatter.strip()[1:-1]
        dimensions = slice_str.split('][')
        slices = []

        for dim in dimensions:
            if '{' in dim:
                slices.append(__class__._DictKeys(d.strip() for d in dim[1:-1].split(',') if d.strip()))
            elif ':' in dim:
                assert ',' not in dim, '[a, b:c] is not supported'
                parts = dim.split(':')
                start = int(parts[0]) if _is_number(parts[0]) else None
                end = int(parts[1]) if len(parts) > 1 and _is_number(parts[1]) else None
                step = int(parts[2]) if len(parts) > 2 and _is_number(parts[2]) else None
                slices.append(slice(start, end, step))
            elif ',' in dim:
                slices.append(__class__._ListIdxes(d.strip() for d in dim.split(',') if d.strip()))
            else:
                slices.append(dim.strip())
        self._slices = slices

    def _parse_py_data_by_formatter(self, data, *, slices=None):  # noqa C901
        def _impl(data, slice):
            if isinstance(data, (tuple, list)) and isinstance(slice, str):
                return data[int(slice)]
            if isinstance(slice, __class__._ListIdxes):
                if isinstance(data, dict): return [data[k] for k in slice]
                elif isinstance(data, (tuple, list)): return type(data)(data[int(k)] for k in slice)
                else: raise RuntimeError('Only tuple/list/dict is supported for [a,b,c]')
            if isinstance(slice, __class__._DictKeys):
                assert isinstance(data, dict)
                if len(slice) == 1 and slice[0] == ':': return data
                return {k: data[k] for k in slice}
            return data[slice]

        if slices is None: slices = self._slices
        if not slices: return data
        curr_slice = slices[0]
        if isinstance(curr_slice, slice):
            if isinstance(data, dict):
                assert curr_slice.start is None and curr_slice.stop is None and curr_slice.step is None, (
                    'Only {:} and [:] is supported in dict slice')
                curr_slice = __class__._ListIdxes(data.keys())
            elif isinstance(data, (tuple, list)):
                return type(data)(self._parse_py_data_by_formatter(d, slices=slices[1:])
                                  for d in _impl(data, curr_slice))
        if isinstance(curr_slice, __class__._DictKeys):
            return {k: self._parse_py_data_by_formatter(v, slices=slices[1:])
                    for k, v in _impl(data, curr_slice).items()}
        elif isinstance(curr_slice, __class__._ListIdxes):
            tp = package if self._return_package else list if isinstance(data, dict) else type(data)
            return tp(self._parse_py_data_by_formatter(r, slices=slices[1:]) for r in _impl(data, curr_slice))
        else: return self._parse_py_data_by_formatter(_impl(data, curr_slice), slices=slices[1:])

lazyllm.components.formatter.formatterbase.PythonFormatter

Bases: JsonLikeFormatter

预留格式化器类,用于支持 Python 风格的数据提取语法,待开发。

当前继承自 JsonLikeFormatter,无额外功能。

Source code in lazyllm/components/formatter/formatterbase.py
class PythonFormatter(JsonLikeFormatter):
    """预留格式化器类,用于支持 Python 风格的数据提取语法,待开发。

当前继承自 JsonLikeFormatter,无额外功能。
"""
    pass

lazyllm.components.formatter.FileFormatter

Bases: LazyLLMFormatterBase

用于处理带文档上下文的查询字符串格式转换的格式化器。

支持三种模式:

  • "decode":将结构化查询字符串解码为包含 query 和 files 的字典。
  • "encode":将包含 query 和 files 的字典编码为结构化查询字符串。
  • "merge":将多个结构化查询字符串合并为一个整体查询。

Parameters:

  • formatter (str, default: 'decode' ) –

    指定操作模式,可为 "decode"、"encode" 或 "merge"(默认为 "decode")。

Examples:

>>> from lazyllm.components.formatter import FileFormatter
>>> # Decode mode
>>> fmt = FileFormatter('decode')
Source code in lazyllm/components/formatter/formatterbase.py
class FileFormatter(LazyLLMFormatterBase):
    """用于处理带文档上下文的查询字符串格式转换的格式化器。

支持三种模式:

- "decode":将结构化查询字符串解码为包含 query 和 files 的字典。
- "encode":将包含 query 和 files 的字典编码为结构化查询字符串。
- "merge":将多个结构化查询字符串合并为一个整体查询。

Args:
    formatter (str): 指定操作模式,可为 "decode"、"encode" 或 "merge"(默认为 "decode")。


Examples:
    >>> from lazyllm.components.formatter import FileFormatter

    >>> # Decode mode
    >>> fmt = FileFormatter('decode')
    """

    def __init__(self, formatter: str = 'decode'):
        self._mode = formatter.strip().lower()
        assert self._mode in ('decode', 'encode', 'merge')

    def _parse_py_data_by_formatter(self, py_data):
        if self._mode == 'merge':
            if isinstance(py_data, str):
                return py_data
            assert isinstance(py_data, package)
            return lazyllm_merge_query(*py_data)

        if isinstance(py_data, package):
            res = []
            for i_data in py_data:
                res.append(self._parse_py_data_by_formatter(i_data))
            return package(res)
        elif isinstance(py_data, (str, dict)):
            return self._decode_one_data(py_data)
        else:
            return py_data

    def _decode_one_data(self, py_data):
        if self._mode == 'decode':
            if isinstance(py_data, str):
                return decode_query_with_filepaths(py_data)
            else:
                return py_data
        else:
            if isinstance(py_data, dict) and 'query' in py_data and 'files' in py_data:
                return encode_query_with_filepaths(**py_data)
            else:
                return py_data

lazyllm.components.formatter.YamlFormatter

Bases: JsonLikeFormatter

用于从 YAML 格式的字符串中提取结构化信息的格式化器。

继承自 JsonLikeFormatter,通过内部方法将字符串解析为 Python 对象后使用类 JSON 的方式提取字段。

适合用于处理包含嵌套结构的 YAML 文本,并结合格式化表达式获取目标数据。

Examples:

>>> from lazyllm.components.formatter import YamlFormatter
>>> formatter = YamlFormatter("{name,age}")
>>> msg = """ 
... name: Alice
... age: 30
... city: London
... """
>>> formatter(msg)
{'name': 'Alice', 'age': 30}
Source code in lazyllm/components/formatter/yamlformatter.py
class YamlFormatter(JsonLikeFormatter):
    """用于从 YAML 格式的字符串中提取结构化信息的格式化器。

继承自 JsonLikeFormatter,通过内部方法将字符串解析为 Python 对象后使用类 JSON 的方式提取字段。

适合用于处理包含嵌套结构的 YAML 文本,并结合格式化表达式获取目标数据。



Examples:
    >>> from lazyllm.components.formatter import YamlFormatter
    >>> formatter = YamlFormatter("{name,age}")
    >>> msg = \"\"\" 
    ... name: Alice
    ... age: 30
    ... city: London
    ... \"\"\"
    >>> formatter(msg)
    {'name': 'Alice', 'age': 30}
    """
    def _load(self, msg: str):
        try:
            return yaml.load(msg, Loader=yaml.SafeLoader)
        except Exception as e:
            lazyllm.LOG.info(f'Error: {e}')
            return ''

lazyllm.components.formatter.encode_query_with_filepaths(query=None, files=None)

将查询文本和文件路径编码为带有文档上下文的结构化字符串格式。

当指定文件路径时,该函数会将查询内容与文件路径打包成 JSON 格式,并在前缀 __lazyllm_docs__ 的基础上编码返回。否则仅返回原始查询文本。

Parameters:

  • query (str, default: None ) –

    用户查询字符串,默认为空字符串。

  • files (str or List[str], default: None ) –

    与查询相关的文档路径,可为单个字符串或字符串列表。

Returns:

  • str: 编码后的结构化查询字符串,或原始查询。

Raises:

  • AssertionError

    如果 files 不是字符串或字符串列表,或列表中元素类型错误。

Examples:

>>> from lazyllm.components.formatter import encode_query_with_filepaths
>>> # Encode a query along with associated documentation files
>>> encode_query_with_filepaths("Generate questions based on the document", files=["a.md"])
'<lazyllm-query>{"query": "Generate questions based on the document", "files": ["a.md"]}'
Source code in lazyllm/components/formatter/formatterbase.py
def encode_query_with_filepaths(query: str = None, files: Union[str, List[str]] = None) -> str:
    """将查询文本和文件路径编码为带有文档上下文的结构化字符串格式。

当指定文件路径时,该函数会将查询内容与文件路径打包成 JSON 格式,并在前缀 ``__lazyllm_docs__`` 的基础上编码返回。否则仅返回原始查询文本。

Args:
    query (str): 用户查询字符串,默认为空字符串。
    files (str or List[str]): 与查询相关的文档路径,可为单个字符串或字符串列表。

**Returns:**

- str: 编码后的结构化查询字符串,或原始查询。

Raises:
    AssertionError: 如果 `files` 不是字符串或字符串列表,或列表中元素类型错误。


Examples:
    >>> from lazyllm.components.formatter import encode_query_with_filepaths

    >>> # Encode a query along with associated documentation files
    >>> encode_query_with_filepaths("Generate questions based on the document", files=["a.md"])
    '<lazyllm-query>{"query": "Generate questions based on the document", "files": ["a.md"]}'
    """
    query = query if query else ''
    if files:
        if isinstance(files, str): files = [files]
        assert isinstance(files, list), 'files must be a list.'
        assert all(isinstance(item, str) for item in files), 'All items in files must be strings'
        return LAZYLLM_QUERY_PREFIX + json.dumps({'query': query, 'files': files})
    else:
        return query

lazyllm.components.formatter.decode_query_with_filepaths(query_files)

将结构化查询字符串解析为包含原始查询和文件路径的字典格式。

当输入字符串以特殊前缀 __lazyllm_docs__ 开头时,函数会尝试从中提取 JSON 格式的查询信息;否则将原样返回字符串内容。

Parameters:

  • query_files (str) –

    编码后的查询字符串,可能包含文档路径和查询内容。

Returns:

  • Union[dict, str]: 若为结构化格式则返回包含 'query' 和 'files' 的字典,否则返回原始查询字符串。

Raises:

  • AssertionError

    如果输入参数不是字符串类型。

  • ValueError

    如果字符串为结构化格式但解析 JSON 失败。

Examples:

>>> from lazyllm.components.formatter import decode_query_with_filepaths
>>> # Decode a structured query with files
>>> decode_query_with_filepaths('<lazyllm-query>{"query": "Summarize the content", "files": ["doc.md"]}')
{'query': 'Summarize the content', 'files': ['doc.md']}
>>> # Decode a plain string without files
>>> decode_query_with_filepaths("This is just a simple question")
'This is just a simple question'
Source code in lazyllm/components/formatter/formatterbase.py
def decode_query_with_filepaths(query_files: str) -> Union[dict, str]:
    """将结构化查询字符串解析为包含原始查询和文件路径的字典格式。

当输入字符串以特殊前缀 ``__lazyllm_docs__`` 开头时,函数会尝试从中提取 JSON 格式的查询信息;否则将原样返回字符串内容。

Args:
    query_files (str): 编码后的查询字符串,可能包含文档路径和查询内容。

**Returns:**

- Union[dict, str]: 若为结构化格式则返回包含 'query' 和 'files' 的字典,否则返回原始查询字符串。

Raises:
    AssertionError: 如果输入参数不是字符串类型。
    ValueError: 如果字符串为结构化格式但解析 JSON 失败。


Examples:
    >>> from lazyllm.components.formatter import decode_query_with_filepaths

    >>> # Decode a structured query with files
    >>> decode_query_with_filepaths('<lazyllm-query>{"query": "Summarize the content", "files": ["doc.md"]}')
    {'query': 'Summarize the content', 'files': ['doc.md']}

    >>> # Decode a plain string without files
    >>> decode_query_with_filepaths("This is just a simple question")
    'This is just a simple question'
    """
    assert isinstance(query_files, str), 'query_files must be a str.'
    query_files = query_files.strip()
    if query_files.startswith(LAZYLLM_QUERY_PREFIX):
        try:
            obj = json.loads(query_files[len(LAZYLLM_QUERY_PREFIX):])
            return obj
        except json.JSONDecodeError as e:
            raise ValueError(f'JSON parsing failed: {e}')
    else:
        return query_files

lazyllm.components.formatter.lazyllm_merge_query(*args)

将多个查询字符串(可能包含文档路径)合并为一个统一的结构化查询字符串。

每个输入参数可以是普通查询字符串或由 encode_query_with_filepaths 编码后的结构化字符串。函数会自动解码、拼接查询文本,并合并所有涉及的文档路径,最终重新编码为统一的查询格式。

Parameters:

  • *args (str, default: () ) –

    多个查询字符串。每个字符串可以是普通文本或已编码的带文件路径的结构化查询。

Returns:

  • str: 合并后的结构化查询字符串,包含统一的查询内容与文件路径。

Examples:

>>> from lazyllm.components.formatter import encode_query_with_filepaths, lazyllm_merge_query
>>> # Merge two structured queries with English content and associated files
>>> q1 = encode_query_with_filepaths("Please summarize document one", files=["doc1.md"])
>>> q2 = encode_query_with_filepaths("Add details from document two", files=["doc2.md"])
>>> lazyllm_merge_query(q1, q2)
'<lazyllm-query>{"query": "Please summarize document oneAdd details from document two", "files": ["doc1.md", "doc2.md"]}'
>>> # Merge plain English text queries without documents
>>> lazyllm_merge_query("What is AI?", "Explain deep learning.")
'What is AI?Explain deep learning.'
Source code in lazyllm/components/formatter/formatterbase.py
def lazyllm_merge_query(*args: str) -> str:
    """将多个查询字符串(可能包含文档路径)合并为一个统一的结构化查询字符串。

每个输入参数可以是普通查询字符串或由 ``encode_query_with_filepaths`` 编码后的结构化字符串。函数会自动解码、拼接查询文本,并合并所有涉及的文档路径,最终重新编码为统一的查询格式。

Args:
    *args (str): 多个查询字符串。每个字符串可以是普通文本或已编码的带文件路径的结构化查询。

**Returns:**

- str: 合并后的结构化查询字符串,包含统一的查询内容与文件路径。


Examples:
    >>> from lazyllm.components.formatter import encode_query_with_filepaths, lazyllm_merge_query

    >>> # Merge two structured queries with English content and associated files
    >>> q1 = encode_query_with_filepaths("Please summarize document one", files=["doc1.md"])
    >>> q2 = encode_query_with_filepaths("Add details from document two", files=["doc2.md"])
    >>> lazyllm_merge_query(q1, q2)
    '<lazyllm-query>{"query": "Please summarize document oneAdd details from document two", "files": ["doc1.md", "doc2.md"]}'

    >>> # Merge plain English text queries without documents
    >>> lazyllm_merge_query("What is AI?", "Explain deep learning.")
    'What is AI?Explain deep learning.'
    """
    if len(args) == 1:
        return args[0]
    for item in args:
        assert isinstance(item, str), 'Merge object must be str!'
    querys = ''
    files = []
    for item in args:
        decode = decode_query_with_filepaths(item)
        if isinstance(decode, dict):
            querys += decode['query']
            files.extend(decode['files'])
        else:
            querys += decode
    return encode_query_with_filepaths(querys, files)

lazyllm.components.JsonFormatter

Bases: JsonLikeFormatter

此类是JSON格式化器,即用户希望模型输出的内容格式为JSON,还可以通过索引方式对输出内容中的某个字段进行选择。

Examples:

>>> import lazyllm
>>> from lazyllm.components import JsonFormatter
>>> toc_prompt='''
... You are now an intelligent assistant. Your task is to understand the user's input and convert the outline into a list of nested dictionaries. Each dictionary contains a `title` and a `describe`, where the `title` should clearly indicate the level using Markdown format, and the `describe` is a description and writing guide for that section.
... 
... Please generate the corresponding list of nested dictionaries based on the following user input:
... 
... Example output:
... [
...     {
...         "title": "# Level 1 Title",
...         "describe": "Please provide a detailed description of the content under this title, offering background information and core viewpoints."
...     },
...     {
...         "title": "## Level 2 Title",
...         "describe": "Please provide a detailed description of the content under this title, giving specific details and examples to support the viewpoints of the Level 1 title."
...     },
...     {
...         "title": "### Level 3 Title",
...         "describe": "Please provide a detailed description of the content under this title, deeply analyzing and providing more details and data support."
...     }
... ]
... User input is as follows:
... '''
>>> query = "Please help me write an article about the application of artificial intelligence in the medical field."
>>> m = lazyllm.TrainableModule("internlm2-chat-20b").prompt(toc_prompt).start()
>>> ret = m(query, max_new_tokens=2048)
>>> print(f"ret: {ret!r}")  # the model output without specifying a formatter
'Based on your user input, here is the corresponding list of nested dictionaries:
[
    {
        "title": "# Application of Artificial Intelligence in the Medical Field",
        "describe": "Please provide a detailed description of the application of artificial intelligence in the medical field, including its benefits, challenges, and future prospects."
    },
    {
        "title": "## AI in Medical Diagnosis",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical diagnosis, including specific examples of AI-based diagnostic tools and their impact on patient outcomes."
    },
    {
        "title": "### AI in Medical Imaging",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical imaging, including the advantages of AI-based image analysis and its applications in various medical specialties."
    },
    {
        "title": "### AI in Drug Discovery and Development",
        "describe": "Please provide a detailed description of how artificial intelligence is used in drug discovery and development, including the role of AI in identifying potential drug candidates and streamlining the drug development process."
    },
    {
        "title": "## AI in Medical Research",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical research, including its applications in genomics, epidemiology, and clinical trials."
    },
    {
        "title": "### AI in Genomics and Precision Medicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in genomics and precision medicine, including the role of AI in analyzing large-scale genomic data and tailoring treatments to individual patients."
    },
    {
        "title": "### AI in Epidemiology and Public Health",
        "describe": "Please provide a detailed description of how artificial intelligence is used in epidemiology and public health, including its applications in disease surveillance, outbreak prediction, and resource allocation."
    },
    {
        "title": "### AI in Clinical Trials",
        "describe": "Please provide a detailed description of how artificial intelligence is used in clinical trials, including its role in patient recruitment, trial design, and data analysis."
    },
    {
        "title": "## AI in Medical Practice",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical practice, including its applications in patient monitoring, personalized medicine, and telemedicine."
    },
    {
        "title": "### AI in Patient Monitoring",
        "describe": "Please provide a detailed description of how artificial intelligence is used in patient monitoring, including its role in real-time monitoring of vital signs and early detection of health issues."
    },
    {
        "title": "### AI in Personalized Medicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in personalized medicine, including its role in analyzing patient data to tailor treatments and predict outcomes."
    },
    {
        "title": "### AI in Telemedicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in telemedicine, including its applications in remote consultations, virtual diagnoses, and digital health records."
    },
    {
        "title": "## AI in Medical Ethics and Policy",
        "describe": "Please provide a detailed description of the ethical and policy considerations surrounding the use of artificial intelligence in the medical field, including issues related to data privacy, bias, and accountability."
    }
]'
>>> m = lazyllm.TrainableModule("internlm2-chat-20b").formatter(JsonFormatter("[:][title]")).prompt(toc_prompt).start()
>>> ret = m(query, max_new_tokens=2048)
>>> print(f"ret: {ret}")  # the model output of the specified formaater
['# Application of Artificial Intelligence in the Medical Field', '## AI in Medical Diagnosis', '### AI in Medical Imaging', '### AI in Drug Discovery and Development', '## AI in Medical Research', '### AI in Genomics and Precision Medicine', '### AI in Epidemiology and Public Health', '### AI in Clinical Trials', '## AI in Medical Practice', '### AI in Patient Monitoring', '### AI in Personalized Medicine', '### AI in Telemedicine', '## AI in Medical Ethics and Policy']
Source code in lazyllm/components/formatter/jsonformatter.py
class JsonFormatter(JsonLikeFormatter):
    """此类是JSON格式化器,即用户希望模型输出的内容格式为JSON,还可以通过索引方式对输出内容中的某个字段进行选择。


Examples:
    >>> import lazyllm
    >>> from lazyllm.components import JsonFormatter
    >>> toc_prompt='''
    ... You are now an intelligent assistant. Your task is to understand the user's input and convert the outline into a list of nested dictionaries. Each dictionary contains a `title` and a `describe`, where the `title` should clearly indicate the level using Markdown format, and the `describe` is a description and writing guide for that section.
    ... 
    ... Please generate the corresponding list of nested dictionaries based on the following user input:
    ... 
    ... Example output:
    ... [
    ...     {
    ...         "title": "# Level 1 Title",
    ...         "describe": "Please provide a detailed description of the content under this title, offering background information and core viewpoints."
    ...     },
    ...     {
    ...         "title": "## Level 2 Title",
    ...         "describe": "Please provide a detailed description of the content under this title, giving specific details and examples to support the viewpoints of the Level 1 title."
    ...     },
    ...     {
    ...         "title": "### Level 3 Title",
    ...         "describe": "Please provide a detailed description of the content under this title, deeply analyzing and providing more details and data support."
    ...     }
    ... ]
    ... User input is as follows:
    ... '''
    >>> query = "Please help me write an article about the application of artificial intelligence in the medical field."
    >>> m = lazyllm.TrainableModule("internlm2-chat-20b").prompt(toc_prompt).start()
    >>> ret = m(query, max_new_tokens=2048)
    >>> print(f"ret: {ret!r}")  # the model output without specifying a formatter
    'Based on your user input, here is the corresponding list of nested dictionaries:
    [
        {
            "title": "# Application of Artificial Intelligence in the Medical Field",
            "describe": "Please provide a detailed description of the application of artificial intelligence in the medical field, including its benefits, challenges, and future prospects."
        },
        {
            "title": "## AI in Medical Diagnosis",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical diagnosis, including specific examples of AI-based diagnostic tools and their impact on patient outcomes."
        },
        {
            "title": "### AI in Medical Imaging",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical imaging, including the advantages of AI-based image analysis and its applications in various medical specialties."
        },
        {
            "title": "### AI in Drug Discovery and Development",
            "describe": "Please provide a detailed description of how artificial intelligence is used in drug discovery and development, including the role of AI in identifying potential drug candidates and streamlining the drug development process."
        },
        {
            "title": "## AI in Medical Research",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical research, including its applications in genomics, epidemiology, and clinical trials."
        },
        {
            "title": "### AI in Genomics and Precision Medicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in genomics and precision medicine, including the role of AI in analyzing large-scale genomic data and tailoring treatments to individual patients."
        },
        {
            "title": "### AI in Epidemiology and Public Health",
            "describe": "Please provide a detailed description of how artificial intelligence is used in epidemiology and public health, including its applications in disease surveillance, outbreak prediction, and resource allocation."
        },
        {
            "title": "### AI in Clinical Trials",
            "describe": "Please provide a detailed description of how artificial intelligence is used in clinical trials, including its role in patient recruitment, trial design, and data analysis."
        },
        {
            "title": "## AI in Medical Practice",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical practice, including its applications in patient monitoring, personalized medicine, and telemedicine."
        },
        {
            "title": "### AI in Patient Monitoring",
            "describe": "Please provide a detailed description of how artificial intelligence is used in patient monitoring, including its role in real-time monitoring of vital signs and early detection of health issues."
        },
        {
            "title": "### AI in Personalized Medicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in personalized medicine, including its role in analyzing patient data to tailor treatments and predict outcomes."
        },
        {
            "title": "### AI in Telemedicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in telemedicine, including its applications in remote consultations, virtual diagnoses, and digital health records."
        },
        {
            "title": "## AI in Medical Ethics and Policy",
            "describe": "Please provide a detailed description of the ethical and policy considerations surrounding the use of artificial intelligence in the medical field, including issues related to data privacy, bias, and accountability."
        }
    ]'
    >>> m = lazyllm.TrainableModule("internlm2-chat-20b").formatter(JsonFormatter("[:][title]")).prompt(toc_prompt).start()
    >>> ret = m(query, max_new_tokens=2048)
    >>> print(f"ret: {ret}")  # the model output of the specified formaater
    ['# Application of Artificial Intelligence in the Medical Field', '## AI in Medical Diagnosis', '### AI in Medical Imaging', '### AI in Drug Discovery and Development', '## AI in Medical Research', '### AI in Genomics and Precision Medicine', '### AI in Epidemiology and Public Health', '### AI in Clinical Trials', '## AI in Medical Practice', '### AI in Patient Monitoring', '### AI in Personalized Medicine', '### AI in Telemedicine', '## AI in Medical Ethics and Policy']
    """
    def _extract_json_from_string(self, mixed_str: str):  # noqa: C901
        json_objects = []
        brace_level = 0
        current_json = ''
        in_string = False

        for char in mixed_str:
            if char == '"' and (len(current_json) == 0 or current_json[-1] != '\\'):
                in_string = not in_string

            if not in_string:
                if char in '{[':
                    if brace_level == 0:
                        current_json = ''
                    brace_level += 1
                elif char in '}]':
                    brace_level -= 1

            if brace_level > 0 or (brace_level == 0 and char in '}]'):
                current_json += char

            if brace_level == 0 and current_json:
                try:
                    json_objects.append(json.loads(current_json))
                    current_json = ''
                except json.JSONDecodeError:
                    try:
                        repaired_obj = json_repair.loads(current_json)
                        json_objects.append(repaired_obj)
                        current_json = ''
                    except (json.JSONDecodeError, ValueError):
                        continue

        return json_objects

    def _load(self, msg: str):
        # Convert str to json format
        assert msg.count('{') == msg.count('}'), f'{msg} is not a valid json string.'
        try:
            json_objects = self._extract_json_from_string(msg)
            if len(json_objects) == 0:
                raise TypeError(f'{msg} is not a valid json string.')
            return json_objects if len(json_objects) > 1 else json_objects[0]
        except Exception as e:
            lazyllm.LOG.info(f'Error: {e}')
            return ''

lazyllm.components.EmptyFormatter

Bases: LazyLLMFormatterBase

此类是空的格式化器,即用户希望对模型的输出不做格式化,用户可以对模型指定该格式化器,也可以不指定(模型默认的格式化器就是空格式化器)

Examples:

>>> import lazyllm
>>> from lazyllm.components import EmptyFormatter
>>> toc_prompt='''
... You are now an intelligent assistant. Your task is to understand the user's input and convert the outline into a list of nested dictionaries. Each dictionary contains a `title` and a `describe`, where the `title` should clearly indicate the level using Markdown format, and the `describe` is a description and writing guide for that section.
... 
... Please generate the corresponding list of nested dictionaries based on the following user input:
... 
... Example output:
... [
...     {
...         "title": "# Level 1 Title",
...         "describe": "Please provide a detailed description of the content under this title, offering background information and core viewpoints."
...     },
...     {
...         "title": "## Level 2 Title",
...         "describe": "Please provide a detailed description of the content under this title, giving specific details and examples to support the viewpoints of the Level 1 title."
...     },
...     {
...         "title": "### Level 3 Title",
...         "describe": "Please provide a detailed description of the content under this title, deeply analyzing and providing more details and data support."
...     }
... ]
... User input is as follows:
... '''
>>> query = "Please help me write an article about the application of artificial intelligence in the medical field."
>>> m = lazyllm.TrainableModule("internlm2-chat-20b").prompt(toc_prompt).start()  # the model output without specifying a formatter
>>> ret = m(query, max_new_tokens=2048)
>>> print(f"ret: {ret!r}")
'Based on your user input, here is the corresponding list of nested dictionaries:
[
    {
        "title": "# Application of Artificial Intelligence in the Medical Field",
        "describe": "Please provide a detailed description of the application of artificial intelligence in the medical field, including its benefits, challenges, and future prospects."
    },
    {
        "title": "## AI in Medical Diagnosis",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical diagnosis, including specific examples of AI-based diagnostic tools and their impact on patient outcomes."
    },
    {
        "title": "### AI in Medical Imaging",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical imaging, including the advantages of AI-based image analysis and its applications in various medical specialties."
    },
    {
        "title": "### AI in Drug Discovery and Development",
        "describe": "Please provide a detailed description of how artificial intelligence is used in drug discovery and development, including the role of AI in identifying potential drug candidates and streamlining the drug development process."
    },
    {
        "title": "## AI in Medical Research",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical research, including its applications in genomics, epidemiology, and clinical trials."
    },
    {
        "title": "### AI in Genomics and Precision Medicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in genomics and precision medicine, including the role of AI in analyzing large-scale genomic data and tailoring treatments to individual patients."
    },
    {
        "title": "### AI in Epidemiology and Public Health",
        "describe": "Please provide a detailed description of how artificial intelligence is used in epidemiology and public health, including its applications in disease surveillance, outbreak prediction, and resource allocation."
    },
    {
        "title": "### AI in Clinical Trials",
        "describe": "Please provide a detailed description of how artificial intelligence is used in clinical trials, including its role in patient recruitment, trial design, and data analysis."
    },
    {
        "title": "## AI in Medical Practice",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical practice, including its applications in patient monitoring, personalized medicine, and telemedicine."
    },
    {
        "title": "### AI in Patient Monitoring",
        "describe": "Please provide a detailed description of how artificial intelligence is used in patient monitoring, including its role in real-time monitoring of vital signs and early detection of health issues."
    },
    {
        "title": "### AI in Personalized Medicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in personalized medicine, including its role in analyzing patient data to tailor treatments and predict outcomes."
    },
    {
        "title": "### AI in Telemedicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in telemedicine, including its applications in remote consultations, virtual diagnoses, and digital health records."
    },
    {
        "title": "## AI in Medical Ethics and Policy",
        "describe": "Please provide a detailed description of the ethical and policy considerations surrounding the use of artificial intelligence in the medical field, including issues related to data privacy, bias, and accountability."
    }
]'
>>> m = lazyllm.TrainableModule("internlm2-chat-20b").formatter(EmptyFormatter()).prompt(toc_prompt).start()  # the model output of the specified formatter
>>> ret = m(query, max_new_tokens=2048)
>>> print(f"ret: {ret!r}")
'Based on your user input, here is the corresponding list of nested dictionaries:
[
    {
        "title": "# Application of Artificial Intelligence in the Medical Field",
        "describe": "Please provide a detailed description of the application of artificial intelligence in the medical field, including its benefits, challenges, and future prospects."
    },
    {
        "title": "## AI in Medical Diagnosis",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical diagnosis, including specific examples of AI-based diagnostic tools and their impact on patient outcomes."
    },
    {
        "title": "### AI in Medical Imaging",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical imaging, including the advantages of AI-based image analysis and its applications in various medical specialties."
    },
    {
        "title": "### AI in Drug Discovery and Development",
        "describe": "Please provide a detailed description of how artificial intelligence is used in drug discovery and development, including the role of AI in identifying potential drug candidates and streamlining the drug development process."
    },
    {
        "title": "## AI in Medical Research",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical research, including its applications in genomics, epidemiology, and clinical trials."
    },
    {
        "title": "### AI in Genomics and Precision Medicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in genomics and precision medicine, including the role of AI in analyzing large-scale genomic data and tailoring treatments to individual patients."
    },
    {
        "title": "### AI in Epidemiology and Public Health",
        "describe": "Please provide a detailed description of how artificial intelligence is used in epidemiology and public health, including its applications in disease surveillance, outbreak prediction, and resource allocation."
    },
    {
        "title": "### AI in Clinical Trials",
        "describe": "Please provide a detailed description of how artificial intelligence is used in clinical trials, including its role in patient recruitment, trial design, and data analysis."
    },
    {
        "title": "## AI in Medical Practice",
        "describe": "Please provide a detailed description of how artificial intelligence is used in medical practice, including its applications in patient monitoring, personalized medicine, and telemedicine."
    },
    {
        "title": "### AI in Patient Monitoring",
        "describe": "Please provide a detailed description of how artificial intelligence is used in patient monitoring, including its role in real-time monitoring of vital signs and early detection of health issues."
    },
    {
        "title": "### AI in Personalized Medicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in personalized medicine, including its role in analyzing patient data to tailor treatments and predict outcomes."
    },
    {
        "title": "### AI in Telemedicine",
        "describe": "Please provide a detailed description of how artificial intelligence is used in telemedicine, including its applications in remote consultations, virtual diagnoses, and digital health records."
    },
    {
        "title": "## AI in Medical Ethics and Policy",
        "describe": "Please provide a detailed description of the ethical and policy considerations surrounding the use of artificial intelligence in the medical field, including issues related to data privacy, bias, and accountability."
    }
]'
Source code in lazyllm/components/formatter/formatterbase.py
class EmptyFormatter(LazyLLMFormatterBase):
    """此类是空的格式化器,即用户希望对模型的输出不做格式化,用户可以对模型指定该格式化器,也可以不指定(模型默认的格式化器就是空格式化器)


Examples:
    >>> import lazyllm
    >>> from lazyllm.components import EmptyFormatter
    >>> toc_prompt='''
    ... You are now an intelligent assistant. Your task is to understand the user's input and convert the outline into a list of nested dictionaries. Each dictionary contains a `title` and a `describe`, where the `title` should clearly indicate the level using Markdown format, and the `describe` is a description and writing guide for that section.
    ... 
    ... Please generate the corresponding list of nested dictionaries based on the following user input:
    ... 
    ... Example output:
    ... [
    ...     {
    ...         "title": "# Level 1 Title",
    ...         "describe": "Please provide a detailed description of the content under this title, offering background information and core viewpoints."
    ...     },
    ...     {
    ...         "title": "## Level 2 Title",
    ...         "describe": "Please provide a detailed description of the content under this title, giving specific details and examples to support the viewpoints of the Level 1 title."
    ...     },
    ...     {
    ...         "title": "### Level 3 Title",
    ...         "describe": "Please provide a detailed description of the content under this title, deeply analyzing and providing more details and data support."
    ...     }
    ... ]
    ... User input is as follows:
    ... '''
    >>> query = "Please help me write an article about the application of artificial intelligence in the medical field."
    >>> m = lazyllm.TrainableModule("internlm2-chat-20b").prompt(toc_prompt).start()  # the model output without specifying a formatter
    >>> ret = m(query, max_new_tokens=2048)
    >>> print(f"ret: {ret!r}")
    'Based on your user input, here is the corresponding list of nested dictionaries:
    [
        {
            "title": "# Application of Artificial Intelligence in the Medical Field",
            "describe": "Please provide a detailed description of the application of artificial intelligence in the medical field, including its benefits, challenges, and future prospects."
        },
        {
            "title": "## AI in Medical Diagnosis",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical diagnosis, including specific examples of AI-based diagnostic tools and their impact on patient outcomes."
        },
        {
            "title": "### AI in Medical Imaging",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical imaging, including the advantages of AI-based image analysis and its applications in various medical specialties."
        },
        {
            "title": "### AI in Drug Discovery and Development",
            "describe": "Please provide a detailed description of how artificial intelligence is used in drug discovery and development, including the role of AI in identifying potential drug candidates and streamlining the drug development process."
        },
        {
            "title": "## AI in Medical Research",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical research, including its applications in genomics, epidemiology, and clinical trials."
        },
        {
            "title": "### AI in Genomics and Precision Medicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in genomics and precision medicine, including the role of AI in analyzing large-scale genomic data and tailoring treatments to individual patients."
        },
        {
            "title": "### AI in Epidemiology and Public Health",
            "describe": "Please provide a detailed description of how artificial intelligence is used in epidemiology and public health, including its applications in disease surveillance, outbreak prediction, and resource allocation."
        },
        {
            "title": "### AI in Clinical Trials",
            "describe": "Please provide a detailed description of how artificial intelligence is used in clinical trials, including its role in patient recruitment, trial design, and data analysis."
        },
        {
            "title": "## AI in Medical Practice",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical practice, including its applications in patient monitoring, personalized medicine, and telemedicine."
        },
        {
            "title": "### AI in Patient Monitoring",
            "describe": "Please provide a detailed description of how artificial intelligence is used in patient monitoring, including its role in real-time monitoring of vital signs and early detection of health issues."
        },
        {
            "title": "### AI in Personalized Medicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in personalized medicine, including its role in analyzing patient data to tailor treatments and predict outcomes."
        },
        {
            "title": "### AI in Telemedicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in telemedicine, including its applications in remote consultations, virtual diagnoses, and digital health records."
        },
        {
            "title": "## AI in Medical Ethics and Policy",
            "describe": "Please provide a detailed description of the ethical and policy considerations surrounding the use of artificial intelligence in the medical field, including issues related to data privacy, bias, and accountability."
        }
    ]'
    >>> m = lazyllm.TrainableModule("internlm2-chat-20b").formatter(EmptyFormatter()).prompt(toc_prompt).start()  # the model output of the specified formatter
    >>> ret = m(query, max_new_tokens=2048)
    >>> print(f"ret: {ret!r}")
    'Based on your user input, here is the corresponding list of nested dictionaries:
    [
        {
            "title": "# Application of Artificial Intelligence in the Medical Field",
            "describe": "Please provide a detailed description of the application of artificial intelligence in the medical field, including its benefits, challenges, and future prospects."
        },
        {
            "title": "## AI in Medical Diagnosis",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical diagnosis, including specific examples of AI-based diagnostic tools and their impact on patient outcomes."
        },
        {
            "title": "### AI in Medical Imaging",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical imaging, including the advantages of AI-based image analysis and its applications in various medical specialties."
        },
        {
            "title": "### AI in Drug Discovery and Development",
            "describe": "Please provide a detailed description of how artificial intelligence is used in drug discovery and development, including the role of AI in identifying potential drug candidates and streamlining the drug development process."
        },
        {
            "title": "## AI in Medical Research",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical research, including its applications in genomics, epidemiology, and clinical trials."
        },
        {
            "title": "### AI in Genomics and Precision Medicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in genomics and precision medicine, including the role of AI in analyzing large-scale genomic data and tailoring treatments to individual patients."
        },
        {
            "title": "### AI in Epidemiology and Public Health",
            "describe": "Please provide a detailed description of how artificial intelligence is used in epidemiology and public health, including its applications in disease surveillance, outbreak prediction, and resource allocation."
        },
        {
            "title": "### AI in Clinical Trials",
            "describe": "Please provide a detailed description of how artificial intelligence is used in clinical trials, including its role in patient recruitment, trial design, and data analysis."
        },
        {
            "title": "## AI in Medical Practice",
            "describe": "Please provide a detailed description of how artificial intelligence is used in medical practice, including its applications in patient monitoring, personalized medicine, and telemedicine."
        },
        {
            "title": "### AI in Patient Monitoring",
            "describe": "Please provide a detailed description of how artificial intelligence is used in patient monitoring, including its role in real-time monitoring of vital signs and early detection of health issues."
        },
        {
            "title": "### AI in Personalized Medicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in personalized medicine, including its role in analyzing patient data to tailor treatments and predict outcomes."
        },
        {
            "title": "### AI in Telemedicine",
            "describe": "Please provide a detailed description of how artificial intelligence is used in telemedicine, including its applications in remote consultations, virtual diagnoses, and digital health records."
        },
        {
            "title": "## AI in Medical Ethics and Policy",
            "describe": "Please provide a detailed description of the ethical and policy considerations surrounding the use of artificial intelligence in the medical field, including issues related to data privacy, bias, and accountability."
        }
    ]'
    """
    def _parse_py_data_by_formatter(self, msg: str):
        return msg

lazyllm.components.FunctionCallFormatter

Bases: LazyLLMFormatterBase

函数调用格式化器,用于处理包含函数调用信息的消息字典。

该格式化器专门用于处理函数调用场景下的模型输出,只提取字典中的 'role'、'content' 和 'tool_calls' 字段,过滤掉其他不需要的字段。

主要用于 FunctionCall 等工具调用相关的功能模块。

注意
  • 输入必须是字典类型,否则会抛出断言错误
  • 只保留字典中存在的 'role'、'content'、'tool_calls' 字段

Examples:

>>> from lazyllm.components.formatter.formatterbase import FunctionCallFormatter
>>> formatter = FunctionCallFormatter()
>>> 
>>> # 处理包含函数调用的消息
>>> msg = {
...     'role': 'assistant',
...     'content': 'I will call a function to get the weather.',
...     'tool_calls': [
...         {
...             'id': 'call_123',
...             'type': 'function',
...             'function': {
...                 'name': 'get_weather',
...                 'arguments': '{"location": "Beijing"}'
...             }
...         }
...     ],
...     'other_field': 'will be filtered'
... }
>>> result = formatter.format(msg)
>>> print(result)
{'role': 'assistant', 'content': 'I will call a function to get the weather.', 'tool_calls': [{'id': 'call_123', 'type': 'function', 'function': {'name': 'get_weather', 'arguments': '{"location": "Beijing"}'}}]}
>>> 
>>> # 处理只包含部分字段的消息
>>> msg2 = {
...     'role': 'assistant',
...     'content': 'Hello, how can I help you?'
... }
>>> result2 = formatter.format(msg2)
>>> print(result2)
{'role': 'assistant', 'content': 'Hello, how can I help you?'}
Source code in lazyllm/components/formatter/formatterbase.py
class FunctionCallFormatter(LazyLLMFormatterBase):
    """函数调用格式化器,用于处理包含函数调用信息的消息字典。

该格式化器专门用于处理函数调用场景下的模型输出,只提取字典中的 'role'、'content' 和 'tool_calls' 字段,过滤掉其他不需要的字段。

主要用于 FunctionCall 等工具调用相关的功能模块。

Args:
    无参数,直接实例化使用。

注意:
    - 输入必须是字典类型,否则会抛出断言错误
    - 只保留字典中存在的 'role'、'content'、'tool_calls' 字段


Examples:
    >>> from lazyllm.components.formatter.formatterbase import FunctionCallFormatter
    >>> formatter = FunctionCallFormatter()
    >>> 
    >>> # 处理包含函数调用的消息
    >>> msg = {
    ...     'role': 'assistant',
    ...     'content': 'I will call a function to get the weather.',
    ...     'tool_calls': [
    ...         {
    ...             'id': 'call_123',
    ...             'type': 'function',
    ...             'function': {
    ...                 'name': 'get_weather',
    ...                 'arguments': '{"location": "Beijing"}'
    ...             }
    ...         }
    ...     ],
    ...     'other_field': 'will be filtered'
    ... }
    >>> result = formatter.format(msg)
    >>> print(result)
    {'role': 'assistant', 'content': 'I will call a function to get the weather.', 'tool_calls': [{'id': 'call_123', 'type': 'function', 'function': {'name': 'get_weather', 'arguments': '{"location": "Beijing"}'}}]}
    >>> 
    >>> # 处理只包含部分字段的消息
    >>> msg2 = {
    ...     'role': 'assistant',
    ...     'content': 'Hello, how can I help you?'
    ... }
    >>> result2 = formatter.format(msg2)
    >>> print(result2)
    {'role': 'assistant', 'content': 'Hello, how can I help you?'}
    """
    def format(self, msg):
        assert isinstance(msg, dict), 'FunctionCallFormatter only supports dict input.'
        return {k: msg[k] for k in ('role', 'content', 'tool_calls') if k in msg}

lazyllm.components.formatter.formatterbase.PipelineFormatter

Bases: LazyLLMFormatterBase

流水线格式化器,用于将数据处理流水线封装为格式化器。

该类将Pipeline实例包装为格式化器,支持通过管道操作符组合多个格式化器。

Parameters:

  • formatter (Pipeline) –

    要封装的流水线实例

Source code in lazyllm/components/formatter/formatterbase.py
class PipelineFormatter(LazyLLMFormatterBase):
    """流水线格式化器,用于将数据处理流水线封装为格式化器。

该类将Pipeline实例包装为格式化器,支持通过管道操作符组合多个格式化器。

Args:
    formatter (Pipeline): 要封装的流水线实例
"""
    def __init__(self, formatter: Pipeline):
        self._formatter = formatter

    def _parse_py_data_by_formatter(self, py_data):
        return self._formatter(py_data)

    def __or__(self, other):
        if isinstance(other, LazyLLMFormatterBase):
            if isinstance(other, PipelineFormatter): other = other._formatter
            return PipelineFormatter(self._formatter | other)
        return NotImplemented

ComponentBase

lazyllm.components.core.ComponentBase

Bases: object

组件基类,提供统一的接口与基础实现,便于创建不同类型的组件。
组件通过指定的 Launcher 来执行任务,支持自定义任务执行逻辑。

Parameters:

  • launcher (LazyLLMLaunchersBase or type, default: empty() ) –

    组件使用的启动器实例或启动器类,默认为空启动器(empty)。

Examples:

>>> from lazyllm.components.core import ComponentBase
>>> class MyComponent(ComponentBase):
...     def apply(self, x):
...         return x * 2
>>> comp = MyComponent()
>>> comp.name = "ExampleComponent"
>>> print(comp.name)
ExampleComponent
>>> result = comp(10)
>>> print(result)
20
>>> print(comp.apply(5))
10
Source code in lazyllm/components/core.py
class ComponentBase(object, metaclass=LazyLLMRegisterMetaClass):
    """组件基类,提供统一的接口与基础实现,便于创建不同类型的组件。  
组件通过指定的 Launcher 来执行任务,支持自定义任务执行逻辑。

Args:
    launcher (LazyLLMLaunchersBase or type, optional): 组件使用的启动器实例或启动器类,默认为空启动器(empty)。


Examples:
    >>> from lazyllm.components.core import ComponentBase
    >>> class MyComponent(ComponentBase):
    ...     def apply(self, x):
    ...         return x * 2
    >>> comp = MyComponent()
    >>> comp.name = "ExampleComponent"
    >>> print(comp.name)
    ExampleComponent
    >>> result = comp(10)
    >>> print(result)
    20
    >>> print(comp.apply(5))
    10
    """
    def __init__(self, *, launcher=launchers.empty()):  # noqa B008
        self._llm_name = None
        self.job = ReadOnlyWrapper()
        if isinstance(launcher, LazyLLMLaunchersBase):
            self._launcher = launcher
        elif isinstance(launcher, type) and issubclass(launcher, LazyLLMLaunchersBase):
            self._launcher = launcher()
        else:
            raise RuntimeError('Invalid launcher given:', launcher)

    def apply():
        """组件执行的核心方法,需由子类实现。  
定义组件的具体业务逻辑或任务执行步骤。  

**注意:**  
调用组件时,如果子类重写了此方法,则会调用此方法执行任务。  
"""
        raise NotImplementedError('please implement function \'apply\'')

    def cmd(self, *args, **kw) -> Union[str, tuple, list]:
        """生成组件的执行命令,需由子类实现。  
返回的命令可以是字符串、元组或列表,表示具体执行任务的指令。  

**注意:**  
调用组件时,如果未重写 `apply` 方法,将通过此命令生成任务并由启动器执行。  
"""
        raise NotImplementedError('please implement function \'cmd\'')

    @property
    def name(self): return self._llm_name
    @name.setter
    def name(self, name): self._llm_name = name

    @property
    def launcher(self): return self._launcher

    def _get_job_with_cmd(self, *args, **kw):
        cmd = self.cmd(*args, **kw)
        cmd = cmd if isinstance(cmd, LazyLLMCMD) else LazyLLMCMD(cmd)
        return self._launcher.makejob(cmd=cmd)

    def _overwrote(self, f):
        return getattr(self.__class__, f) is not getattr(__class__, f) or \
            getattr(self.__class__, '__reg_overwrite__', None) == f

    def __call__(self, *args, **kw):
        if self._overwrote('apply'):
            assert not self._overwrote('cmd'), (
                'Cannot overwrite \'cmd\' and \'apply\' in the same class')
            assert isinstance(self._launcher, launchers.Empty), 'Please use EmptyLauncher instead.'
            return self._launcher.launch(self.apply, *args, **kw)
        else:
            job = self._get_job_with_cmd(*args, **kw)
            self.job.set(job)
            return self._launcher.launch(job)

    def __repr__(self):
        return lazyllm.make_repr('lazyllm.llm.' + self.__class__._lazy_llm_group,
                                 self.__class__.__name__, name=self.name)

apply()

组件执行的核心方法,需由子类实现。
定义组件的具体业务逻辑或任务执行步骤。

注意:
调用组件时,如果子类重写了此方法,则会调用此方法执行任务。

Source code in lazyllm/components/core.py
    def apply():
        """组件执行的核心方法,需由子类实现。  
定义组件的具体业务逻辑或任务执行步骤。  

**注意:**  
调用组件时,如果子类重写了此方法,则会调用此方法执行任务。  
"""
        raise NotImplementedError('please implement function \'apply\'')

cmd(*args, **kw)

生成组件的执行命令,需由子类实现。
返回的命令可以是字符串、元组或列表,表示具体执行任务的指令。

注意:
调用组件时,如果未重写 apply 方法,将通过此命令生成任务并由启动器执行。

Source code in lazyllm/components/core.py
    def cmd(self, *args, **kw) -> Union[str, tuple, list]:
        """生成组件的执行命令,需由子类实现。  
返回的命令可以是字符串、元组或列表,表示具体执行任务的指令。  

**注意:**  
调用组件时,如果未重写 `apply` 方法,将通过此命令生成任务并由启动器执行。  
"""
        raise NotImplementedError('please implement function \'cmd\'')

lazyllm.components.deploy.ray.Distributed

Bases: LazyLLMDeployBase

分布式部署类,继承自LazyLLMDeployBase。

提供基于Ray框架的分布式模型部署功能,支持多节点集群部署。

Parameters:

  • launcher

    启动器配置,默认为远程启动器(ngpus=1)

  • port (int, default: None ) –

    服务端口号,默认为随机端口(30000-40000)

Attributes:

  • finetuned_model

    微调后的模型路径

  • base_model

    基础模型路径

  • master_ip

    主节点IP地址

Methods:

  • cmd

    生成部署命令

  • geturl

    获取部署服务的URL地址

Source code in lazyllm/components/deploy/ray.py
class Distributed(LazyLLMDeployBase):
    """分布式部署类,继承自LazyLLMDeployBase。

提供基于Ray框架的分布式模型部署功能,支持多节点集群部署。

Args:
    launcher: 启动器配置,默认为远程启动器(ngpus=1)
    port (int, optional): 服务端口号,默认为随机端口(30000-40000)

Attributes:
    finetuned_model: 微调后的模型路径
    base_model: 基础模型路径
    master_ip: 主节点IP地址

Methods:
    cmd(finetuned_model, base_model, master_ip): 生成部署命令
    geturl(job): 获取部署服务的URL地址
"""

    def __init__(self, launcher=launchers.remote(ngpus=1), port=None):  # noqa B008
        super().__init__(launcher=launcher)
        self.port = port or random.randint(30000, 40000)
        self.finetuned_model = None
        self.base_model = None
        self.master_ip = None

    def cmd(self, finetuned_model=None, base_model=None, master_ip=None):
        """生成Ray分布式部署命令。

根据是否为主节点生成相应的Ray启动命令,支持头节点和工作节点两种模式。

Args:
    finetuned_model: 微调后的模型路径
    base_model: 基础模型路径
    master_ip: 主节点IP地址,如果为空则作为头节点启动

Returns:
    LazyLLMCMD: 包含部署命令的对象
"""
        self.finetuned_model = finetuned_model
        self.base_model = base_model
        self.master_ip = master_ip
        if not self.master_ip:
            cmd = f'ray start --block --head --port={self.port} && sleep 365d'
        else:
            cmd = f'ray start --address={self.master_ip} && sleep 365d'
        return LazyLLMCMD(cmd=cmd, return_value=self.geturl)

    def geturl(self, job=None):
        """获取分布式部署服务的URL地址。

根据部署模式返回相应的服务地址信息,支持显示模式和实际部署模式。

Args:
    job: 任务对象,默认为当前任务

Returns:
    Package: 包含模型路径和服务地址的打包对象
"""
        time.sleep(5)
        if job is None:
            job = self.job
        if lazyllm.config['mode'] == lazyllm.Mode.Display:
            return lazyllm.package(self.finetuned_model, self.base_model, None)
        else:
            if self.master_ip:
                return lazyllm.package(self.finetuned_model, self.base_model, self.master_ip)
            return lazyllm.package(self.finetuned_model, self.base_model, f'{job.get_jobip()}:{self.port}')

cmd(finetuned_model=None, base_model=None, master_ip=None)

生成Ray分布式部署命令。

根据是否为主节点生成相应的Ray启动命令,支持头节点和工作节点两种模式。

Parameters:

  • finetuned_model

    微调后的模型路径

  • base_model

    基础模型路径

  • master_ip

    主节点IP地址,如果为空则作为头节点启动

Returns:

  • LazyLLMCMD

    包含部署命令的对象

Source code in lazyllm/components/deploy/ray.py
    def cmd(self, finetuned_model=None, base_model=None, master_ip=None):
        """生成Ray分布式部署命令。

根据是否为主节点生成相应的Ray启动命令,支持头节点和工作节点两种模式。

Args:
    finetuned_model: 微调后的模型路径
    base_model: 基础模型路径
    master_ip: 主节点IP地址,如果为空则作为头节点启动

Returns:
    LazyLLMCMD: 包含部署命令的对象
"""
        self.finetuned_model = finetuned_model
        self.base_model = base_model
        self.master_ip = master_ip
        if not self.master_ip:
            cmd = f'ray start --block --head --port={self.port} && sleep 365d'
        else:
            cmd = f'ray start --address={self.master_ip} && sleep 365d'
        return LazyLLMCMD(cmd=cmd, return_value=self.geturl)