feat: add LLM quota deduction functionality and enhance model configuration handling in llm_utils.py; update test cases for LLM node context handling

This commit is contained in:
Novice
2026-03-24 08:45:11 +08:00
parent ed1bd338f1
commit dcd614ca77
4 changed files with 57 additions and 19 deletions

View File

@ -22,6 +22,7 @@ from dify_graph.model_runtime.entities import (
TextPromptMessageContent,
ToolPromptMessage,
)
from dify_graph.model_runtime.entities.llm_entities import LLMUsage
from dify_graph.model_runtime.entities.message_entities import (
AssistantPromptMessage,
PromptMessageContentUnionTypes,
@ -37,7 +38,7 @@ from dify_graph.runtime import VariablePool
from dify_graph.variables import ArrayFileSegment, FileSegment
from dify_graph.variables.segments import ArrayAnySegment, NoneSegment, StringSegment
from .entities import LLMNodeChatModelMessage, LLMNodeCompletionModelPromptTemplate
from .entities import LLMNodeChatModelMessage, LLMNodeCompletionModelPromptTemplate, ModelConfig
from .exc import (
InvalidVariableTypeError,
MemoryRolePrefixRequiredError,
@ -47,9 +48,7 @@ from .exc import (
from .protocols import TemplateRenderer
def fetch_model_config(
*, tenant_id: str, node_data_model: ModelConfig
) -> tuple[ModelInstance, Any]:
def fetch_model_config(*, tenant_id: str, node_data_model: ModelConfig) -> tuple[ModelInstance, Any]:
from core.app.llm.model_access import build_dify_model_access
from core.app.llm.model_access import fetch_model_config as _fetch
@ -61,6 +60,12 @@ def fetch_model_config(
)
def deduct_llm_quota(*, tenant_id: str, model_instance: ModelInstance, usage: LLMUsage) -> None:
from core.app.llm.quota import deduct_llm_quota as _deduct
_deduct(tenant_id=tenant_id, model_instance=model_instance, usage=usage)
def fetch_model_schema(*, model_instance: ModelInstance) -> AIModelEntity:
model_schema = cast(LargeLanguageModel, model_instance.model_type_instance).get_model_schema(
model_instance.model_name,

View File

@ -35,7 +35,14 @@ def patch_deduct_llm_quota(monkeypatch):
def _make_llm_node(reasoning_format: str) -> LLMNode:
node = LLMNode.__new__(LLMNode)
object.__setattr__(node, "_node_data", types.SimpleNamespace(reasoning_format=reasoning_format, tools=[]))
object.__setattr__(node, "tenant_id", "tenant")
object.__setattr__(
node,
"_run_context",
{"_dify": types.SimpleNamespace(
tenant_id="tenant", app_id="app", user_id="user",
user_from="account", invoke_from="debugger",
)},
)
return node

View File

@ -799,7 +799,8 @@
"nodes.llm.computerUse.title": "Computer Use",
"nodes.llm.computerUse.tooltip": "Enable the model to interact with a computer desktop environment",
"nodes.llm.context": "context",
"nodes.llm.contextMissing": "Context is missing",
"nodes.llm.contextBlock": "Context Block",
"nodes.llm.contextMissing": "Context missing: {{nodeName}}",
"nodes.llm.contextTooltip": "You can import Knowledge as context",
"nodes.llm.contextUnknownNode": "Unknown node",
"nodes.llm.files": "Files",
@ -943,17 +944,22 @@
"nodes.templateTransform.codeSupportTip": "Only supports Jinja2",
"nodes.templateTransform.inputVars": "Input Variables",
"nodes.templateTransform.outputVars.output": "Transformed content",
"nodes.tool.agentPlaceholder": "Select an agent...",
"nodes.tool.agentPlaceholder": "Enter value for {{paramKey}}...",
"nodes.tool.agentPopupHeader": "Select Agent",
"nodes.tool.assembleVariables": "Assemble Variables",
"nodes.tool.authorizationRequired": "Authorization required",
"nodes.tool.authorize": "Authorize",
"nodes.tool.contextGenerate.apply": "Apply",
"nodes.tool.contextGenerate.code": "Code",
"nodes.tool.contextGenerate.codeBlock": "Code Block",
"nodes.tool.contextGenerate.codeLanguage.javascript": "JavaScript",
"nodes.tool.contextGenerate.codeLanguage.python3": "Python 3",
"nodes.tool.contextGenerate.defaultAssistantMessage": "I'll help you generate the context code.",
"nodes.tool.contextGenerate.generatedCode": "Generated Code",
"nodes.tool.contextGenerate.generating": "Generating...",
"nodes.tool.contextGenerate.initPlaceholder": "Describe what you want to generate...",
"nodes.tool.contextGenerate.inputPlaceholder": "Type your message...",
"nodes.tool.contextGenerate.instruction": "Instruction",
"nodes.tool.contextGenerate.output": "Output",
"nodes.tool.contextGenerate.resizeHandle": "Resize",
"nodes.tool.contextGenerate.rightSidePlaceholder": "Generated content will appear here",
@ -1173,6 +1179,7 @@
"panel.scrollToSelectedNode": "Scroll to selected node",
"panel.selectNextStep": "Select Next Step",
"panel.startNode": "Start Node",
"panel.ungroup": "Ungroup",
"panel.userInputField": "User Input Field",
"publishLimit.startNodeDesc": "Youve reached the limit of 2 triggers per workflow for this plan. Upgrade to publish this workflow.",
"publishLimit.startNodeTitlePrefix": "Upgrade to",
@ -1193,7 +1200,7 @@
"singleRun.reRun": "Re-run",
"singleRun.running": "Running",
"singleRun.startRun": "Start Run",
"singleRun.subgraph.nullOutputError": "Subgraph returned null output",
"singleRun.subgraph.nullOutputError": "Sub-graph returned null for output: {{output}}",
"singleRun.testRun": "Test Run",
"singleRun.testRunIteration": "Test Run Iteration",
"singleRun.testRunLoop": "Test Run Loop",
@ -1238,7 +1245,7 @@
"skillEditor.previewUnavailable": "Preview unavailable",
"skillEditor.referenceFiles": "Reference Files",
"skillEditor.toolMissing": "Tool Missing",
"skillEditor.toolMissingDesc": "The referenced tool is missing or has been removed",
"skillEditor.toolMissingDesc": "The referenced tool is missing. Go to <Plugins>Plugins</Plugins> to install it.",
"skillEditor.unsupportedPreview": "Unsupported file type for preview",
"skillEditor.uploadFiles": "Upload Files",
"skillEditor.uploadIn": "Upload in",
@ -1306,16 +1313,22 @@
"skillSidebar.uploadSuccess": "Upload successful",
"skillSidebar.uploadSuccessDetail": "{{count}} files uploaded",
"skillSidebar.uploadingItems": "Uploading {{count}} items...",
"subGraphModal.canvasPlaceholder": "Select a node to view its details",
"subGraphModal.defaultValueHint": "Default value will be used when output is null",
"subGraphModal.internalStructure": "Internal Structure",
"subGraphModal.internalStructureDesc": "View the internal workflow structure of this sub-graph",
"subGraphModal.internalStructureDesc": "View the internal workflow structure of {{name}}",
"subGraphModal.lastRun": "Last Run",
"subGraphModal.noRunHistory": "No run history",
"subGraphModal.outputVariables": "Output Variables",
"subGraphModal.settings": "Settings",
"subGraphModal.sourceNode": "Source Node",
"subGraphModal.title": "Sub-Graph Details",
"subGraphModal.whenOutputIsNone": "When Output is None",
"subGraphModal.whenOutputNone.default": "Use Default",
"subGraphModal.whenOutputNone.defaultDesc": "Use a default value when sub-graph output is null",
"subGraphModal.whenOutputNone.error": "Throw Error",
"subGraphModal.whenOutputNone.errorDesc": "Throw an error when sub-graph output is null",
"subGraphModal.whenOutputNone.skip": "Skip",
"tabs.-": "Default",
"tabs.addAll": "Add all",
"tabs.agent": "Agent Strategy",
@ -1350,7 +1363,7 @@
"tabs.usePlugin": "Select tool",
"tabs.utilities": "Utilities",
"tabs.workflowTool": "Workflow",
"toolGroup.actionsEnabled": "{{count}} actions enabled",
"toolGroup.actionsEnabled": "{{num}} actions enabled",
"toolGroup.byAuthor": "By {{author}}",
"tracing.stopBy": "Stop by {{user}}",
"triggerStatus.disabled": "TRIGGER • DISABLED",
@ -1364,7 +1377,7 @@
"versionHistory.action.deleteFailure": "Failed to delete version",
"versionHistory.action.deleteSuccess": "Version deleted",
"versionHistory.action.restoreFailure": "Failed to restore version",
"versionHistory.action.restoreInProgress": "Restoring...",
"versionHistory.action.restoreInProgress": "{{userName}} is restoring version {{versionName}}...",
"versionHistory.action.restoreSuccess": "Version restored",
"versionHistory.action.updateFailure": "Failed to update version",
"versionHistory.action.updateSuccess": "Version updated",

View File

@ -799,7 +799,8 @@
"nodes.llm.computerUse.title": "计算机操作",
"nodes.llm.computerUse.tooltip": "允许模型与计算机桌面环境交互",
"nodes.llm.context": "上下文",
"nodes.llm.contextMissing": "缺少上下文",
"nodes.llm.contextBlock": "上下文",
"nodes.llm.contextMissing": "缺少上下文:{{nodeName}}",
"nodes.llm.contextTooltip": "您可以导入知识库作为上下文",
"nodes.llm.contextUnknownNode": "未知节点",
"nodes.llm.files": "文件",
@ -943,17 +944,22 @@
"nodes.templateTransform.codeSupportTip": "只支持 Jinja2",
"nodes.templateTransform.inputVars": "输入变量",
"nodes.templateTransform.outputVars.output": "转换后内容",
"nodes.tool.agentPlaceholder": "选择 Agent...",
"nodes.tool.agentPlaceholder": "输入 {{paramKey}} 的值...",
"nodes.tool.agentPopupHeader": "选择 Agent",
"nodes.tool.assembleVariables": "组装变量",
"nodes.tool.authorizationRequired": "需要授权",
"nodes.tool.authorize": "授权",
"nodes.tool.contextGenerate.apply": "应用",
"nodes.tool.contextGenerate.code": "代码",
"nodes.tool.contextGenerate.codeBlock": "代码块",
"nodes.tool.contextGenerate.codeLanguage.javascript": "JavaScript",
"nodes.tool.contextGenerate.codeLanguage.python3": "Python 3",
"nodes.tool.contextGenerate.defaultAssistantMessage": "我将帮你生成上下文代码。",
"nodes.tool.contextGenerate.generatedCode": "生成的代码",
"nodes.tool.contextGenerate.generating": "生成中...",
"nodes.tool.contextGenerate.initPlaceholder": "描述你想要生成的内容...",
"nodes.tool.contextGenerate.inputPlaceholder": "输入消息...",
"nodes.tool.contextGenerate.instruction": "指令",
"nodes.tool.contextGenerate.output": "输出",
"nodes.tool.contextGenerate.resizeHandle": "调整大小",
"nodes.tool.contextGenerate.rightSidePlaceholder": "生成的内容将显示在这里",
@ -1173,6 +1179,7 @@
"panel.scrollToSelectedNode": "滚动至选中节点",
"panel.selectNextStep": "选择下一个节点",
"panel.startNode": "开始节点",
"panel.ungroup": "取消分组",
"panel.userInputField": "用户输入字段",
"publishLimit.startNodeDesc": "您已达到此计划上每个工作流最多 2 个触发器的限制。请升级后再发布此工作流。",
"publishLimit.startNodeTitlePrefix": "升级以",
@ -1193,7 +1200,7 @@
"singleRun.reRun": "重新运行",
"singleRun.running": "运行中",
"singleRun.startRun": "开始运行",
"singleRun.subgraph.nullOutputError": "子图返回了空输出",
"singleRun.subgraph.nullOutputError": "子图的输出 {{output}} 返回了空",
"singleRun.testRun": "测试运行",
"singleRun.testRunIteration": "测试运行迭代",
"singleRun.testRunLoop": "测试运行循环",
@ -1238,7 +1245,7 @@
"skillEditor.previewUnavailable": "预览不可用",
"skillEditor.referenceFiles": "引用文件",
"skillEditor.toolMissing": "工具缺失",
"skillEditor.toolMissingDesc": "引用的工具缺失或已被移除",
"skillEditor.toolMissingDesc": "引用的工具缺失或已被移除。前往<Plugins>插件</Plugins>安装。",
"skillEditor.unsupportedPreview": "不支持预览的文件类型",
"skillEditor.uploadFiles": "上传文件",
"skillEditor.uploadIn": "上传至",
@ -1306,16 +1313,22 @@
"skillSidebar.uploadSuccess": "上传成功",
"skillSidebar.uploadSuccessDetail": "已上传 {{count}} 个文件",
"skillSidebar.uploadingItems": "正在上传 {{count}} 个项目...",
"subGraphModal.canvasPlaceholder": "选择一个节点以查看其详情",
"subGraphModal.defaultValueHint": "输出为空时将使用默认值",
"subGraphModal.internalStructure": "内部结构",
"subGraphModal.internalStructureDesc": "查看此子图的内部工作流结构",
"subGraphModal.internalStructureDesc": "查看 {{name}} 的内部工作流结构",
"subGraphModal.lastRun": "上次运行",
"subGraphModal.noRunHistory": "暂无运行历史",
"subGraphModal.outputVariables": "输出变量",
"subGraphModal.settings": "设置",
"subGraphModal.sourceNode": "源节点",
"subGraphModal.title": "子图详情",
"subGraphModal.whenOutputIsNone": "当输出为空时",
"subGraphModal.whenOutputNone.default": "使用默认值",
"subGraphModal.whenOutputNone.defaultDesc": "当子图输出为空时使用默认值",
"subGraphModal.whenOutputNone.error": "抛出错误",
"subGraphModal.whenOutputNone.errorDesc": "当子图输出为空时抛出错误",
"subGraphModal.whenOutputNone.skip": "跳过",
"tabs.-": "默认",
"tabs.addAll": "添加全部",
"tabs.agent": "Agent 策略",
@ -1350,7 +1363,7 @@
"tabs.usePlugin": "选择工具",
"tabs.utilities": "工具",
"tabs.workflowTool": "工作流",
"toolGroup.actionsEnabled": "已启用 {{count}} 个操作",
"toolGroup.actionsEnabled": "已启用 {{num}} 个操作",
"toolGroup.byAuthor": "作者 {{author}}",
"tracing.stopBy": "由{{user}}终止",
"triggerStatus.disabled": "触发器 • 已禁用",
@ -1364,7 +1377,7 @@
"versionHistory.action.deleteFailure": "删除失败",
"versionHistory.action.deleteSuccess": "版本已删除",
"versionHistory.action.restoreFailure": "回滚失败",
"versionHistory.action.restoreInProgress": "恢复中...",
"versionHistory.action.restoreInProgress": "{{userName}} 正在恢复版本 {{versionName}}...",
"versionHistory.action.restoreSuccess": "回滚成功",
"versionHistory.action.updateFailure": "更新失败",
"versionHistory.action.updateSuccess": "版本信息已更新",