📎 配套脚本附件正文未内嵌 · 共 19 个 · 点开可在线查看
📚 另需公共库(正文未重复内嵌,点名字直接看):_agent_cfg.py、_bootstrap.py、_config.py、_plugin_util.py
📂 查看全部 761 个脚本附件 →
⚠️ 脚本内含明文凭据(与本页正文一致),请勿在公网环境直接运行。
Dify · 专题一

在 Dify 集成入口添加自定义 Qwen3.6-27B

自定义模型、schema 派生、设为默认。步骤未改,代码块已换成完整程序。

Dify_专题一_集成Qwen3.6-27B_cursor.md

这是基于原文生成的 Cursor 版文档。

原文件未改动;本版只做三件事:统一阅读入口、修正已审出的明显问题、补充更适合快速排障的提示。

Cursor 版修订说明

阅读提示

运行环境:业务脚本均在 207 固定目录 /opt/workbuddy 执行,Python 为 /usr/bin/python3。 Windows 开发机仅保留 _probe_* 调试脚本;同步命令:python scripts_cursor/完整代码约定/00_部署_workbuddy到207.py。


原文主体(已纳入 Cursor 版纠错)

专题一:在 Dify 集成入口正确添加自定义 Qwen3.6-27B,使其在创建 Agent 时可被选择

专题目标:把 vLLM 上的 qwen3.6-27b 作为自定义大模型接入 Dify 的「模型供应商」入口,并确保后续创建 Agent 时能在模型下拉里正常选中它、且不再显示"不兼容"。

涉及两处插件代码 patch,本文直接给出可落地的完整代码。

阅读导航(何时打开 · 报错往哪查)

本文是单点修复/排障专题,配合《安装配置手册》使用;下文实战步骤与代码全部保留,此处只做索引。

现象优先看本文还可能是
只配了 Base、列表无 qwen§二 单独添加安装手册步骤 12
「不兼容」/ 无法选中§三~六 patch专题十
保存凭据 Connection error前置 vLLM专题五·第 2 层

0. 为什么"加了模型却选不到 / 显示不兼容"

在 Dify 里让一个自定义模型(尤其非 OpenAI 官方的,如 Qwen)在 Agent 创建时可被选中,要跨过两道门槛:

  1. 入口必须"单独添加":在 OpenAI 兼容供应商里,qwen3.6-27b 必须作为 customizable-model(自定义模型) 显式添加,光配 openai_api_base 不够——否则它根本不出现在模型列表。
  2. 插件必须能"派生出它的 schema":OpenAI 插件的 get_customizable_model_schema() 会按 base model 名去查预定义 schema;qwen3.6-27b 没有对应基座时会抛出 Base model qwen3.6-27b not found,前端就把它标成"不兼容",Agent 下拉里即便出现也无法选中/设默认。

本文按"入口添加 → 设为默认 → patch 插件让 schema 可派生 → 验证可选"的顺序讲清楚。


一、前置条件


二、在模型供应商入口添加自定义 Qwen3.6-27B

  1. 控制台 → 模型供应商 → 找到 OpenAI → 点击「设置 / 添加模型」。
  2. 填供应商级连接信息:
  1. 在 自定义模型(Customizable Model) 区域:
  1. 点击「保存」。此时该模型会出现在 OpenAI 供应商下的模型列表里。

注意:模型名称是大小写/字符敏感的,务必与 vLLM served-model-name 完全匹配。

本次实操也可以(推荐)走控制台 API。 模型供应商页虽然存在,但用 dify_api.py 更不容易填错字段。对应接口:

POST /console/api/workspaces/current/model-providers/langgenius/openai/openai/models/credentials

完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.py

运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.py

python
from dify_api import Dify, pp

d = Dify("38205036@qq.com", "Difyai123456")
d.login()

body = {
    "model": "qwen3.6-27b",
    "model_type": "llm",
    "credentials": {
        "openai_api_key": "123456",
        "openai_api_base": "http://116.62.10.183:8000/v1",
        "openai_organization": "",
        "api_protocol": "chat",
    },
    "name": "qwen3.6-27b",
}
r = d.post(
    "/workspaces/current/model-providers/langgenius/openai/openai/models/credentials",
    body,
)
pp("add qwen cred", r)   # 期望 200/201

r = d.get("/workspaces/current/models/model-types/llm")
pp("llm list", r)
d.close()

已存在同一模型时改用 PUT 同一路径,并带上 GET 回来的 current_credential_id(见专题九 align_qwen_ctx.py)。验证:model-types/llm 里 qwen3.6-27b 的 status=active。


三、把它设为默认 LLM(否则默认指向插件自带的 gpt-5.6)

添加后还需把默认 LLM 改为 qwen3.6-27b,否则 Dify 默认还指向插件自带的 gpt-5.6,且 Agent 创建时默认的也是它。

方式 A · 控制台

方式 C · 控制台 API(和方式 A 同一接口)

完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py

运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py

python
from dify_api import Dify, pp
d = Dify("38205036@qq.com", "Difyai123456")
d.login()
r = d.post(
    "/workspaces/current/default-model",
    body={
        "model_settings": [{
            "model_type": "llm",
            "provider": "langgenius/openai/openai",
            "model": "qwen3.6-27b",
        }]
    },
)
pp("set default llm", r)
d.close()

若该接口在你的版本返回 404,用方式 B 改 tenant_default_models。

方式 B · 数据库(推荐,确保持久生效) 默认模型记录在 tenant_default_models 表,把 model_type='llm' 的记录从 gpt-5.6 改为 qwen3.6-27b:

完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.sh

bash
#!/usr/bin/env bash
set -euo pipefail

docker exec -it docker-db_postgres-1 psql -U postgres -d dify \
  -c "UPDATE tenant_default_models SET model='qwen3.6-27b' WHERE model_type='llm';"

修改后重启让配置生效: 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.sh

bash
#!/usr/bin/env bash
set -euo pipefail

cd /dify/dify-main/docker
docker compose restart api plugin_daemon

四、关键 Patch:让 qwen 有可派生的 model schema(消除"不兼容")

这一步是"能否在 Agent 里选中"的核心。OpenAI 插件的 get_customizable_model_schema() 找不到 qwen3.6-27b 的基座就会标"不兼容"。我们让它在找不到基座时 clone 一个通用的 chat schema 作为该模型的 schema。

升级注意(2026-08):plugin_daemon 卷里可能同时存在 openai-1.0.3@… 与 openai-1.0.4@…。运行时只加载当前启用版本;若只给旧目录打补丁,控制台会突然全面「不可用」。

排障专题:专题十七 · 插件升级后 schema 补丁丢失

推荐脚本(遍历所有 openai 版本):scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_04_改llm_schema_修改后.py

文件(容器内路径,host 上对应 plugin_daemon 卷;请对每个 openai-*@* 目录检查):

text
/app/storage/cwd/langgenius/openai-<版本>@<hash>/models/llm/llm.py
# 宿主机常见映射:
/dify/dify-main/docker/volumes/plugin_daemon/cwd/langgenius/openai-*@*/models/llm/llm.py

先备份:cp llm.py llm.py.bak_qwenfix 判定是否已补丁:文件中是否含 Fallback for custom / non-OpenAI base models。

改动位置:替换 get_customizable_model_schema 整个函数体。完整代码(已在本环境生效):

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.py

python
def get_customizable_model_schema(
        self, model: str, credentials: dict
    ) -> AIModelEntity:
        schema = {item.model: item for item in self.predefined_models()}.get(
            _base_model(model)
        )
        if schema is None:
            # Fallback for custom / non-OpenAI base models served via an
            # OpenAI-compatible endpoint (e.g. Qwen, DeepSeek, local vLLM):
            # clone a chat-capable predefined schema so the model is listable
            # and can be set as the default model.
            template = next(
                (m for m in self.predefined_models()
                 if m.model_properties.get("mode") == "chat"),
                None,
            )
            if template is None:
                template = self.predefined_models()[0]
            props = dict(template.model_properties)
            mp = (credentials or {}).get("model_properties") or {}
            # Align context_size to the actual vLLM --max-model-len (65536) so
            # Dify never lets prompts exceed what the served model can accept.
            # The credential-update API does NOT persist model_properties for
            # this provider, so we hardcode the safe ceiling as the default and
            # still honor an explicit override when one is supplied.
            VLLM_MAX_CTX = 64000
            try:
                props["context_size"] = int(mp["context_size"]) if "context_size" in mp else VLLM_MAX_CTX
            except (ValueError, TypeError):
                props["context_size"] = VLLM_MAX_CTX
            # NOTE: do NOT put max_tokens into model_properties. Dify API-side
            # ModelPropertyKey only allows {mode, context_size, ...} and does
            # NOT include MAX_TOKENS; adding it breaks Pydantic validation when
            # the API parses the schema from plugin_daemon. max_tokens is
            # already exposed as a parameter_rule by the cloned template.
            schema = template.model_copy(deep=True, update={"model_properties": props})
        return schema.model_copy(
            deep=True,
            update={
                "model": model,
                "label": I18nObject(zh_hans=model, en_us=model),
                "fetch_from": FetchFrom.CUSTOMIZABLE_MODEL,
            },
        )

要点:


五、验证:创建 Agent 时已可选、不再"不兼容"

  1. 控制台 → 创建应用 → Agent(或聊天助手/工作流,凡是选模型的地方)。
  2. 模型下拉里应能看到 qwen3.6-27b,且不再标"不兼容",可以选中并设为该应用的模型。
  3. 用 Agent API 实跑一条(必须 streaming):

完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.sh

bash
#!/usr/bin/env bash
set -euo pipefail

curl -s -N -X POST 'http://192.168.110.207/v1/chat-messages' \
  -H 'Authorization: Bearer app-v9yYVTQTrUoSFHNqAhufSGmh' \
  -H 'Content-Type: application/json' \
  -d '{"inputs":{},"query":"你好,介绍一下你自己","response_mode":"streaming","user":"debug"}'

返回正常文本、ERROR EVENT 为空,即"选得到且能用"。


六、补充 Patch:Agent 实际调用时合并多条 system(否则会报 400)

上一步只解决"选得到"。但 Agent 真正对话时,Dify 会把多条 system/developer 提示(知识库助手 + Agent 工具 + 空 system + 配置 + 环境变量)堆在前面;而 Qwen3/qwen3_5 的 chat template 只接受一条 system 且必须在最前,否则回 400 System message must be at the beginning。需再 patch chat.py 把多条 system 合并成一条。

文件:

text
.../openai-1.0.3@<hash>/models/llm/chat.py

先备份:cp chat.py chat.py.bak_systemfix

改动 1 · 在模块顶部(from __future__ import annotations 之后)新增 helper:

完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py

运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py

python
def _ensure_system_first(messages: list[PromptMessage]) -> list[PromptMessage]:
    """Qwen3/vLLM chat templates accept ONLY one system message, at the start.

    Dify (esp. Agent apps) stacks several system/developer prompts: KB assistant
    prompt + agent tool prompt + an empty system + config manifest... Merging them
    into a single system message (joined by blank lines, empties dropped) keeps it
    at the front; every other message keeps its original relative order.
    """
    system_parts: list[str] = []
    rest: list[PromptMessage] = []
    for m in messages:
        if isinstance(m, (SystemPromptMessage, DeveloperPromptMessage)):
            text = _text_content(m)
            if text.strip():
                system_parts.append(text)
        else:
            rest.append(m)
    if system_parts:
        rest.insert(0, SystemPromptMessage(content="\n\n".join(system_parts)))
    return rest

注意:_text_content()、SystemPromptMessage、DeveloperPromptMessage 均为该模块已有定义/导入,无需额外添加。_ensure_system_first 必须放在 from __future__ import annotations 之后,否则整文件 SyntaxError、插件直接加载失败。

改动 2 · 在 client.chat.completions.create(...) 的 messages= 处调用它:

原调用: 完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py

运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py

python
    response = client.chat.completions.create(
        model=model,
        messages=cast(Any, [message(item) for item in prompt_messages]),
        stream=stream,
        **params
    )

改为(只改 messages= 一行): 完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py

运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py

python
    response = client.chat.completions.create(
        model=model,
        messages=cast(Any, [message(item) for item in _ensure_system_first(prompt_messages)]),
        stream=stream,
        **params
    )

改完 docker compose restart plugin_daemon。现在 Agent 既能"选到"、也能"正常调通"。


七、落地清单

风险提示:llm.py / chat.py 两处补丁位于插件卷内,升级/重装 openai 插件时会被覆盖,需重打(备份 llm.py.bak_qwenfix、chat.py.bak_systemfix 已留在卷内)。 已根治(2026-09-24 起):model 类插件的自动升级已关闭、并把 langgenius/openai 加入排除名单,补丁不会再被夜间自动升级冲掉;改为「想升级时手动升 → 升完立刻重跑 专题一_04」。做法与复核命令见 专题十七 · §5 根治。 另注:chat.py 那处合并补丁在 openai 插件 1.0.5 已由上游原生实现(_single_leading_system),新版本上通常不必再打,只需盯 llm.py。


八、实战记录(本次部署真实遇到的情况)

本次真实卡住的两处,都和"接入口加模型"直接相关。下面按统一维度拆解——现象(当时看到什么)→ 定位(怎么确认根因)→ 改了什么(文件/命令逐条)→ 怎么重启服务 → 怎么看日志 → 怎么判断正常,方便照着复现与自查。

8.1 模型显示"不兼容"、Agent 选不到

现象:保存 qwen3.6-27b 后,OpenAI 供应商下它被标成"不兼容";创建 Agent 时 LLM 下拉里选不到、悬停提示模型不可用。

定位:

改了什么(文件:.../openai-1.0.3@<hash>/models/llm/llm.py,完整代码见本文第四节):

  1. 先备份:cp llm.py llm.py.bak_qwenfix
  2. 把 get_customizable_model_schema 改为:找不到基座时 clone 一个通用 chat schema;仅允许凭据 model_properties 覆盖 context_size(默认 64000),不要把 max_tokens 写进 model_properties。

怎么重启服务: 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.sh

bash
#!/usr/bin/env bash
set -euo pipefail

cd /dify/dify-main/docker && docker compose restart plugin_daemon
sleep 8 && docker ps --filter name=plugin_daemon --format '{{.Names}} {{.Status}}'

怎么看日志(确认插件加载成功): 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.sh

bash
#!/usr/bin/env bash
set -euo pipefail

docker logs docker-plugin_daemon-1 2>&1 | grep -iE 'error|syntax|traceback|openai' | tail -20

无 SyntaxError/Traceback、能看到 openai 插件 register 日志 → 加载正常。

怎么判断正常:回到控制台 → 模型供应商 → OpenAI,qwen3.6-27b 状态从"不兼容"变正常;创建 Agent 时 LLM 下拉能选中它。

8.2 新建应用默认仍是 gpt-5.6,发消息报 The model gpt-5.6 does not exist

现象:即使 8.1 修好,新建 Agent 默认 LLM 仍指向不存在的 gpt-5.6,发消息报 The model gpt-5.6 does not exist。

定位:数据库 tenant_default_models 的 llm 行还是 gpt-5.6(插件自带基座),没切到 qwen。

改了什么(见本文第三节 / 专题五第 5 层): 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.sh

bash
#!/usr/bin/env bash
set -euo pipefail

docker exec -it docker-db_postgres-1 psql -U postgres -d dify \
  -c "UPDATE tenant_default_models SET model='qwen3.6-27b' WHERE model_type='llm';"

怎么重启服务: 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.sh

bash
#!/usr/bin/env bash
set -euo pipefail

cd /dify/dify-main/docker && docker compose restart api worker plugin_daemon

怎么看日志(确认 api 不再报找不到模型): 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.sh

bash
#!/usr/bin/env bash
set -euo pipefail

docker logs docker-api-1 2>&1 | grep -iE 'gpt-5.6|does not exist|error' | tail -20

应无 The model gpt-5.6 does not exist。

怎么判断正常:新建 Agent 默认 LLM 自动是 qwen3.6-27b;用 Agent API streaming 调一次(见本文第五节),返回正常答案、ERROR EVENT 为空。

一句话对照:专题一前面是"怎么加",本节是"本次真正卡住的两处"——不兼容 + 默认模型错,两处都修好 Agent 才选得到 qwen3.6-27b。


本专题 Python 程序一览

| 文件 | 用途 | |---|---|| 专题一_01_添加Qwen凭据.py | 专题一 · 步骤 2:控制台 API 添加自定义 Qwen3.6-27B 凭据。 | | 专题一_02_设默认LLM.py | 专题一 · 步骤 3:设默认 LLM(API + tenant_default_models)。 | | 专题一_03_验证vLLM与LLM列表.py | 专题一 · 步骤 5:验证 vLLM 可达且 Dify LLM 列表中 qwen active。 | | 专题一_04_改llm_schema_修改前.py | 专题一 · 步骤 4 · 修改前:确认 llm.py 尚未打 Qwen schema 补丁。 | | 专题一_04_改llm_schema_修改后.py | 专题一 · 步骤 4 · 修改后:patch llm.py 克隆 chat schema,重启 plugin_daemon。 | | 专题一_05_改chat_system_修改前.py | 专题一 · 步骤 6 · 修改前:确认 chat.py 尚未合并多条 system。 | | 专题一_05_改chat_system_修改后.py | 专题一 · 步骤 6 · 修改后:patch chat.py 合并 system,重启 plugin_daemon。 |

专题一_01_添加Qwen凭据.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_01_添加Qwen凭据.py

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 2:控制台 API 添加自定义 Qwen3.6-27B 凭据。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
from dify_api import Dify, pp


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        body = {
            "model": C.QWEN_MODEL,
            "model_type": "llm",
            "credentials": {
                "openai_api_key": C.VLLM_KEY,
                "openai_api_base": C.VLLM_BASE,
                "openai_organization": "",
                "api_protocol": "chat",
            },
            "name": C.QWEN_MODEL,
        }
        path = "/workspaces/current/model-providers/%s/models/credentials" % C.OPENAI_PROVIDER
        r = d.get(
            "/workspaces/current/model-providers/%s/models/credentials?model_type=llm&model=%s"
            % (C.OPENAI_PROVIDER, C.QWEN_MODEL)
        )
        cid = (r.get("json") or {}).get("current_credential_id")
        if cid:
            body["credential_id"] = cid
            r = d.req("PUT", path, body=body)
            pp("update qwen cred", r)
        else:
            r = d.post(path, body=body)
            pp("add qwen cred", r)
        r2 = d.get("/workspaces/current/models/model-types/llm")
        pp("llm list", r2)
        return 0 if r.get("code") in ("200", "201") else 1
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())

专题一_02_设默认LLM.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_02_设默认LLM.py

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 3:设默认 LLM(API + tenant_default_models)。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
from dify_api import Dify, pp


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        r = d.post(
            "/workspaces/current/default-model",
            body={
                "model_settings": [{
                    "model_type": "llm",
                    "provider": C.OPENAI_PROVIDER,
                    "model": C.QWEN_MODEL,
                }]
            },
        )
        pp("set default llm (API)", r)

        sql = (
            "UPDATE tenant_default_models SET model='%s' WHERE model_type='llm';"
            % C.QWEN_MODEL
        )
        out, err = d.sh(
            "docker exec %s psql -U postgres -d dify -c \"%s\""
            % (C.PG_CONTAINER, sql)
        )
        print("tenant_default_models:\n", out or err)

        out2, err2 = d.sh(
            "cd %s && docker compose restart api plugin_daemon" % C.DIFY_DOCKER_DIR,
            t=120,
        )
        print(out2 or err2)
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())

专题一_03_验证vLLM与LLM列表.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_03_验证vLLM与LLM列表.py

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 5:验证 vLLM 可达且 Dify LLM 列表中 qwen active。"""
from __future__ import annotations

import json
import sys

import _bootstrap  # noqa: F401
import _config as C
from dify_api import Dify, pp


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        print("=== 1/2 从 207 curl vLLM /v1/models ===")
        out, err = d.sh(
            "curl -s -m 10 -H 'Authorization: Bearer %s' %s/models"
            % (C.VLLM_KEY, C.VLLM_BASE)
        )
        print(out or err)
        try:
            data = json.loads(out)
            ids = [m.get("id") for m in data.get("data", [])]
            print("model ids:", ids)
            if C.QWEN_MODEL not in ids:
                print("[WARN] vLLM 列表无", C.QWEN_MODEL)
        except Exception:
            pass

        print("\n=== 2/2 Dify model-types/llm ===")
        d.login()
        r = d.get("/workspaces/current/models/model-types/llm")
        pp("llm list", r)
        found = False
        for p in (r.get("json") or {}).get("data") or []:
            if p.get("provider") != C.OPENAI_PROVIDER:
                continue
            for m in p.get("models") or []:
                if m.get("model") == C.QWEN_MODEL:
                    found = True
                    print("qwen status:", m.get("status"), "props:", m.get("model_properties"))
        if not found:
            print("[WARN] Dify LLM 列表未找到", C.QWEN_MODEL)
            return 1
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())

专题一_04_改llm_schema_修改前.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_04_改llm_schema_修改前.py

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 4 · 修改前:确认 llm.py 尚未打 Qwen schema 补丁。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify, pp


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        path = PU.find_plugin_file(d, "models/llm/llm.py")
        print("llm.py:", path)
        content = PU.container_read(d, C.PLUGIN_CONTAINER, path)
        if PU.LLM_PATCH_MARKER in content:
            print("[INFO] 已存在 Qwen schema fallback 补丁,无需再改")
        else:
            print("[WARN] 尚未打补丁 — 运行 专题一_04_改llm_schema_修改后.py")
            snippet = PU.grep_in_container(d, C.PLUGIN_CONTAINER, path, "get_customizable_model_schema")
            print(snippet[:1200])

        r = d.get("/workspaces/current/models/model-types/llm")
        pp("llm list(不兼容时 status 非 active)", r)
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())

专题一_04_改llm_schema_修改后.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_04_改llm_schema_修改后.py

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 4 · 修改后:patch llm.py 克隆 chat schema,重启 plugin_daemon。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        path = PU.find_plugin_file(d, "models/llm/llm.py")
        print("target:", path)
        content = PU.container_read(d, C.PLUGIN_CONTAINER, path)
        if PU.LLM_PATCH_MARKER in content:
            print("already patched")
            return 0
        d.sh(
            "docker exec %s cp %s %s.bak_qwenfix"
            % (C.PLUGIN_CONTAINER, path, path)
        )
        new_content = PU.patch_llm_schema(content)
        PU.container_write(d, C.PLUGIN_CONTAINER, path, new_content)
        out, err = d.sh(
            "docker exec %s python -m py_compile %s" % (C.PLUGIN_CONTAINER, path)
        )
        print("py_compile:", out or err or "ok")
        print("restarting plugin_daemon ...")
        PU.restart_plugin_daemon(d)
        print("done — 回控制台确认 qwen 不再「不兼容」")
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())

专题一_05_改chat_system_修改前.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_05_改chat_system_修改前.py

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 6 · 修改前:确认 chat.py 尚未合并多条 system。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        path = PU.find_plugin_file(d, "models/llm/chat.py")
        print("chat.py:", path)
        if PU.CHAT_PATCH_MARKER in PU.container_read(d, C.PLUGIN_CONTAINER, path):
            print("[INFO] 已有 _ensure_system_first 补丁")
        else:
            print("[WARN] 尚未打补丁 — Agent 问答可能 400 System message must be at the beginning")
            print(PU.grep_in_container(d, C.PLUGIN_CONTAINER, path, "chat.completions.create")[:800])
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())

专题一_05_改chat_system_修改后.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_05_改chat_system_修改后.py

完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.py

运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 6 · 修改后:patch chat.py 合并 system,重启 plugin_daemon。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        path = PU.find_plugin_file(d, "models/llm/chat.py")
        print("target:", path)
        content = PU.container_read(d, C.PLUGIN_CONTAINER, path)
        if PU.CHAT_PATCH_MARKER in content:
            print("already patched")
            return 0
        d.sh(
            "docker exec %s cp %s %s.bak_systemfix"
            % (C.PLUGIN_CONTAINER, path, path)
        )
        new_content = PU.patch_chat_system(content)
        PU.container_write(d, C.PLUGIN_CONTAINER, path, new_content)
        out, err = d.sh(
            "docker exec %s python -m py_compile %s" % (C.PLUGIN_CONTAINER, path)
        )
        print("py_compile:", out or err or "ok")
        print("restarting plugin_daemon ...")
        PU.restart_plugin_daemon(d)
        print("done — 新开 Agent 对话验证不再 400")
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())