Merge remote-tracking branch 'upstream/develop' into feat/quota-check-decorator

This commit is contained in:
wxy
2026-04-17 10:38:24 +08:00
34 changed files with 405 additions and 156 deletions

View File

@@ -121,6 +121,8 @@ jobs:
AUTHOR: ${{ github.event.pull_request.user.login }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
SOURCERY_FOUND: ${{ steps.sourcery.outputs.found }}
SOURCERY_SUMMARY: ${{ steps.sourcery.outputs.summary }}
QWEN_SUMMARY: ${{ steps.qwen.outputs.summary }}
@@ -135,11 +137,16 @@ jobs:
label = "AI变更摘要"
summary = os.environ.get("QWEN_SUMMARY", "AI 摘要生成失败")
pr_number = os.environ.get("PR_NUMBER", "")
short_sha = os.environ.get("MERGE_SHA", "")[:7]
content = (
"## 🚀 Release 发布通知\n"
"> 📦 **分支**: " + os.environ["BRANCH"] + "\n"
"> <EFBFBD> **分支**: " + os.environ["BRANCH"] + "\n"
"> 👤 **提交人**: " + os.environ["AUTHOR"] + "\n"
"> 📝 **标题**: " + os.environ["PR_TITLE"] + "\n\n"
"> 📝 **标题**: " + os.environ["PR_TITLE"] + "\n"
"> 🔢 **PR编号**: #" + pr_number + "\n"
"> 🔖 **Commit**: " + short_sha + "\n\n"
"### 🧠 " + label + "\n" +
summary + "\n\n"
"---\n"

View File

@@ -443,10 +443,10 @@ async def retrieve_chunks(
match retrieve_data.retrieve_type:
case chunk_schema.RetrieveType.PARTICIPLE:
rs = vector_service.search_by_full_text(query=retrieve_data.query, top_k=retrieve_data.top_k, indices=indices, score_threshold=retrieve_data.similarity_threshold, file_names_filter=retrieve_data.file_names_filter)
return success(data=rs, msg="retrieval successful")
return success(data=jsonable_encoder(rs), msg="retrieval successful")
case chunk_schema.RetrieveType.SEMANTIC:
rs = vector_service.search_by_vector(query=retrieve_data.query, top_k=retrieve_data.top_k, indices=indices, score_threshold=retrieve_data.vector_similarity_weight, file_names_filter=retrieve_data.file_names_filter)
return success(data=rs, msg="retrieval successful")
return success(data=jsonable_encoder(rs), msg="retrieval successful")
case _:
rs1 = vector_service.search_by_vector(query=retrieve_data.query, top_k=retrieve_data.top_k, indices=indices, score_threshold=retrieve_data.vector_similarity_weight, file_names_filter=retrieve_data.file_names_filter)
rs2 = vector_service.search_by_full_text(query=retrieve_data.query, top_k=retrieve_data.top_k, indices=indices, score_threshold=retrieve_data.similarity_threshold, file_names_filter=retrieve_data.file_names_filter)

View File

@@ -165,7 +165,7 @@ def _get_ontology_service(
api_key=api_key_config.api_key,
base_url=api_key_config.api_base,
is_omni=api_key_config.is_omni,
support_thinking="thinking" in (api_key_config.capability or []),
capability=api_key_config.capability,
max_retries=3,
timeout=60.0
)

View File

@@ -41,6 +41,7 @@ class LangChainAgent:
max_tool_consecutive_calls: int = 3, # 单个工具最大连续调用次数
deep_thinking: bool = False, # 是否启用深度思考模式
thinking_budget_tokens: Optional[int] = None, # 深度思考 token 预算
json_output: bool = False, # 是否强制 JSON 输出
capability: Optional[List[str]] = None # 模型能力列表,用于校验是否支持深度思考
):
"""初始化 LangChain Agent
@@ -64,7 +65,6 @@ class LangChainAgent:
self.streaming = streaming
self.is_omni = is_omni
self.max_tool_consecutive_calls = max_tool_consecutive_calls
self.deep_thinking = deep_thinking and ("thinking" in (capability or []))
# 工具调用计数器:记录每个工具的连续调用次数
self.tool_call_counter: Dict[str, int] = {}
@@ -80,6 +80,12 @@ class LangChainAgent:
self.system_prompt = system_prompt or "你是一个专业的AI助手"
# ChatTongyi 要求 messages 含 'json' 字样才能使用 response_format
# 在 system prompt 中注入 JSON 要求
from app.models.models_model import ModelProvider
if json_output and provider.lower() == ModelProvider.DASHSCOPE and not is_omni:
self.system_prompt += "\n请以JSON格式输出。"
logger.debug(
f"Agent 迭代次数配置: max_iterations={self.max_iterations}, "
f"tool_count={len(self.tools)}, "
@@ -87,23 +93,17 @@ class LangChainAgent:
f"auto_calculated={max_iterations is None}"
)
# 根据 capability 校验是否真正支持深度思考
actual_deep_thinking = self.deep_thinking
if deep_thinking and not actual_deep_thinking:
logger.warning(
f"模型 {model_name} 不支持深度思考capability 中无 'thinking'),已自动关闭 deep_thinking"
)
# 创建 RedBearLLM支持多提供商
# 创建 RedBearLLMcapability 校验由 RedBearModelConfig 统一处理
model_config = RedBearModelConfig(
model_name=model_name,
provider=provider,
api_key=api_key,
base_url=api_base,
is_omni=is_omni,
deep_thinking=actual_deep_thinking,
thinking_budget_tokens=thinking_budget_tokens if actual_deep_thinking else None,
support_thinking="thinking" in (capability or []),
capability=capability,
deep_thinking=deep_thinking,
thinking_budget_tokens=thinking_budget_tokens,
json_output=json_output,
extra_params={
"temperature": temperature,
"max_tokens": max_tokens,
@@ -112,6 +112,9 @@ class LangChainAgent:
)
self.llm = RedBearLLM(model_config, type=ModelType.CHAT)
# 从经过校验的 config 读取实际生效的能力开关
self.deep_thinking = model_config.deep_thinking
self.json_output = model_config.json_output
# 获取底层模型用于真正的流式调用
self._underlying_llm = self.llm._model if hasattr(self.llm, '_model') else self.llm

View File

@@ -1,7 +1,7 @@
from __future__ import annotations
import os
from typing import Any, Dict, Optional, TypeVar
from typing import Any, Dict, List, Optional, TypeVar
from langchain_aws import ChatBedrock
from langchain_community.chat_models import ChatTongyi
@@ -9,12 +9,12 @@ from langchain_core.embeddings import Embeddings
from langchain_core.language_models import BaseLLM
from langchain_ollama import OllamaLLM
from langchain_openai import ChatOpenAI, OpenAI
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, model_validator
from app.core.error_codes import BizCode
from app.core.exceptions import BusinessException
from app.models.models_model import ModelProvider, ModelType
from app.core.models.volcano_chat import VolcanoChatOpenAI
from app.core.models.compatible_chat import CompatibleChatOpenAI
T = TypeVar("T")
@@ -25,10 +25,11 @@ class RedBearModelConfig(BaseModel):
provider: str
api_key: str
base_url: Optional[str] = None
capability: List[str] = Field(default_factory=list) # 模型能力列表,驱动所有能力开关
is_omni: bool = False # 是否为 Omni 模型
deep_thinking: bool = False # 是否启用深度思考模式
thinking_budget_tokens: Optional[int] = None # 深度思考 token 预算
support_thinking: bool = False # 模型是否支持 enable_thinking 参数capability 含 thinking
json_output: bool = False # 是否强制 JSON 输出
# 请求超时时间(秒)- 默认120秒以支持复杂的LLM调用可通过环境变量 LLM_TIMEOUT 配置
timeout: float = Field(default_factory=lambda: float(os.getenv("LLM_TIMEOUT", "120.0")))
# 最大重试次数 - 默认2次以避免过长等待可通过环境变量 LLM_MAX_RETRIES 配置
@@ -36,6 +37,23 @@ class RedBearModelConfig(BaseModel):
concurrency: int = 5 # 并发限流
extra_params: Dict[str, Any] = {}
@model_validator(mode="after")
def _resolve_capabilities(self) -> "RedBearModelConfig":
from app.core.logging_config import get_business_logger
logger = get_business_logger()
if self.deep_thinking and "thinking" not in self.capability:
logger.warning(
f"模型 {self.model_name} 不支持深度思考capability 中无 'thinking'),已自动关闭 deep_thinking"
)
self.deep_thinking = False
self.thinking_budget_tokens = None
if self.json_output and "json_output" not in self.capability:
logger.warning(
f"模型 {self.model_name} 不支持 JSON 输出capability 中无 'json_output'),已自动关闭 json_output"
)
self.json_output = False
return self
class RedBearModelFactory:
"""模型工厂类"""
@@ -74,17 +92,20 @@ class RedBearModelFactory:
is_streaming = bool(config.extra_params.get("streaming"))
if is_streaming:
params["stream_usage"] = True
# 只有支持 thinking 的模型传 enable_thinking
if config.support_thinking:
model_kwargs: Dict[str, Any] = config.extra_params.get("model_kwargs", {})
if is_streaming:
model_kwargs["enable_thinking"] = config.deep_thinking
if config.deep_thinking:
model_kwargs["incremental_output"] = True
if config.thinking_budget_tokens:
model_kwargs["thinking_budget"] = config.thinking_budget_tokens
else:
model_kwargs["enable_thinking"] = False
# 支持 thinking 的模型始终传 enable_thinking,关闭时显式传 False 避免模型默认开启思考
if "thinking" in config.capability:
extra_body = params.setdefault("extra_body", {})
if config.deep_thinking:
extra_body["enable_thinking"] = False
if is_streaming:
extra_body["enable_thinking"] = True
if config.thinking_budget_tokens:
extra_body["thinking_budget"] = config.thinking_budget_tokens
params["extra_body"] = extra_body
# JSON 输出模式
if config.json_output:
model_kwargs = params.setdefault("model_kwargs", {})
model_kwargs["response_format"] = {"type": "json_object"}
params["model_kwargs"] = model_kwargs
return params
@@ -108,27 +129,30 @@ class RedBearModelFactory:
**config.extra_params
}
# 流式模式下启用 stream_usage 以获取 token 统计
if config.extra_params.get("streaming"):
params["stream_usage"] = True
# 深度思考模式
is_streaming = bool(config.extra_params.get("streaming"))
if config.support_thinking:
if is_streaming and not config.is_omni:
if provider == ModelProvider.VOLCANO:
# 火山引擎深度思考仅流式调用支持,非流式时不传 thinking 参数
thinking_config: Dict[str, Any] = {
"type": "enabled" if config.deep_thinking else "disabled"
}
if config.deep_thinking and config.thinking_budget_tokens:
thinking_config["budget_tokens"] = config.thinking_budget_tokens
params["extra_body"] = {"thinking": thinking_config}
else:
# 始终显式传递 enable_thinking不支持该参数的模型如 DeepSeek-R1会直接忽略
model_kwargs: Dict[str, Any] = config.extra_params.get("model_kwargs", {})
model_kwargs["enable_thinking"] = config.deep_thinking
if config.deep_thinking and config.thinking_budget_tokens:
model_kwargs["thinking_budget"] = config.thinking_budget_tokens
params["model_kwargs"] = model_kwargs
if is_streaming:
params["stream_usage"] = True
# 支持 thinking 的模型始终传 enable_thinking关闭时显式传 False 避免模型默认开启思考
if "thinking" in config.capability:
# VOLCANO 深度思考仅流式支持
if provider == ModelProvider.VOLCANO:
thinking_config: Dict[str, Any] = {"type": "enabled" if config.deep_thinking else "disabled"}
if config.deep_thinking and config.thinking_budget_tokens:
thinking_config["budget_tokens"] = config.thinking_budget_tokens
params["extra_body"] = {"thinking": thinking_config}
else:
extra_body = params.setdefault("extra_body", {})
if config.deep_thinking:
extra_body["enable_thinking"] = False
if is_streaming:
extra_body["enable_thinking"] = True
if config.thinking_budget_tokens:
extra_body["thinking_budget"] = config.thinking_budget_tokens
params["extra_body"] = extra_body
# JSON 输出模式
if config.json_output:
params.setdefault("model_kwargs", {})
params["model_kwargs"]["response_format"] = {"type": "json_object"}
return params
elif provider == ModelProvider.DASHSCOPE:
params = {
@@ -137,18 +161,21 @@ class RedBearModelFactory:
"max_retries": config.max_retries,
**config.extra_params
}
# 只有支持 thinking 的模型传 enable_thinking
if config.support_thinking:
# 支持 thinking 的模型始终传 enable_thinking,关闭时显式传 False 避免模型默认开启思考
if "thinking" in config.capability:
is_streaming = bool(config.extra_params.get("streaming"))
model_kwargs: Dict[str, Any] = config.extra_params.get("model_kwargs", {})
if is_streaming:
model_kwargs["enable_thinking"] = config.deep_thinking
if config.deep_thinking:
model_kwargs["incremental_output"] = True
if config.thinking_budget_tokens:
model_kwargs["thinking_budget"] = config.thinking_budget_tokens
else:
model_kwargs = params.setdefault("model_kwargs", {})
if config.deep_thinking:
model_kwargs["enable_thinking"] = False
if is_streaming:
model_kwargs["enable_thinking"] = True
model_kwargs["incremental_output"] = True
if config.thinking_budget_tokens:
model_kwargs["thinking_budget"] = config.thinking_budget_tokens
params["model_kwargs"] = model_kwargs
if config.json_output:
model_kwargs = params.setdefault("model_kwargs", {})
model_kwargs["response_format"] = {"type": "json_object"}
params["model_kwargs"] = model_kwargs
return params
elif provider == ModelProvider.BEDROCK:
@@ -196,6 +223,10 @@ class RedBearModelFactory:
params["additional_model_request_fields"] = {
"thinking": {"type": "enabled", "budget_tokens": budget}
}
# JSON 输出模式
if config.json_output:
params.setdefault("model_kwargs", {})
params["model_kwargs"]["response_format"] = {"type": "json_object"}
return params
else:
raise BusinessException(f"不支持的提供商: {provider}", code=BizCode.PROVIDER_NOT_SUPPORTED)
@@ -224,11 +255,11 @@ def get_provider_llm_class(config: RedBearModelConfig, type: ModelType = ModelTy
"""根据模型提供商获取对应的模型类"""
provider = config.provider.lower()
# dashscopeomni 模型使用 OpenAI 兼容模式
# dashscopeomni模型 和 volcano模型使用
if provider == ModelProvider.DASHSCOPE and config.is_omni:
return ChatOpenAI
return CompatibleChatOpenAI
if provider == ModelProvider.VOLCANO:
return VolcanoChatOpenAI
return CompatibleChatOpenAI
if provider in [ModelProvider.OPENAI, ModelProvider.XINFERENCE, ModelProvider.GPUSTACK]:
if type == ModelType.LLM:
return OpenAI

View File

@@ -12,8 +12,8 @@ from langchain_core.outputs import ChatGenerationChunk, ChatResult
from langchain_openai import ChatOpenAI
class VolcanoChatOpenAI(ChatOpenAI):
"""火山引擎 Chat 模型支持深度思考内容reasoning_content的流式和非流式透传。"""
class CompatibleChatOpenAI(ChatOpenAI):
"""火山和千问的omni兼容模型支持深度思考内容reasoning_content的流式和非流式透传。"""
def _create_chat_result(self, response: Union[dict, Any], generation_info: Optional[dict] = None) -> ChatResult:
result = super()._create_chat_result(response, generation_info)

View File

@@ -6,7 +6,8 @@ models:
description: AI21 Labs大语言模型completion生成模式256000上下文窗口
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -20,6 +21,7 @@ models:
is_official: true
capability:
- vision
- json_output
is_omni: false
tags:
- 大语言模型
@@ -38,6 +40,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -54,7 +57,8 @@ models:
description: Cohere大语言模型支持智能体思考、工具调用、流式工具调用128000上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -72,6 +76,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -87,7 +92,8 @@ models:
description: Meta Llama大语言模型支持智能体思考、工具调用128000上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -101,7 +107,8 @@ models:
description: Mistral AI大语言模型支持智能体思考、工具调用32000上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -115,7 +122,8 @@ models:
description: OpenAI大语言模型支持智能体思考、工具调用、流式工具调用32768上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -130,7 +138,8 @@ models:
description: Qwen大语言模型支持智能体思考、工具调用、流式工具调用32768上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型

View File

@@ -8,6 +8,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -22,6 +23,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -36,6 +38,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -48,7 +51,8 @@ models:
description: DeepSeek-V3.1大语言模型支持智能体思考131072超大上下文窗口对话模式支持丰富生成参数调节
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -61,7 +65,8 @@ models:
description: DeepSeek-V3.2-exp实验版大语言模型支持智能体思考131072超大上下文窗口对话模式支持丰富生成参数调节
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -74,7 +79,8 @@ models:
description: DeepSeek-V3.2大语言模型支持智能体思考131072超大上下文窗口对话模式支持丰富生成参数调节
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -87,7 +93,8 @@ models:
description: DeepSeek-V3大语言模型支持智能体思考64000上下文窗口对话模式支持文本与JSON格式输出
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -100,7 +107,8 @@ models:
description: farui-plus大语言模型支持多工具调用、智能体思考、流式工具调用12288上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -115,7 +123,8 @@ models:
description: GLM-4.7大语言模型支持多工具调用、智能体思考、流式工具调用202752超大上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -133,6 +142,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -150,6 +160,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -180,6 +191,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -210,7 +222,7 @@ models:
is_deprecated: false
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -376,6 +388,7 @@ models:
capability:
- vision
- video
- json_output
is_omni: false
tags:
- 大语言模型
@@ -448,6 +461,7 @@ models:
capability:
- vision
- video
- json_output
is_omni: false
tags:
- 大语言模型
@@ -466,6 +480,7 @@ models:
capability:
- vision
- video
- json_output
is_omni: false
tags:
- 大语言模型
@@ -481,7 +496,8 @@ models:
description: qwen2.5-0.5b-instruct大语言模型支持多工具调用、智能体思考、流式工具调用32768上下文窗口对话模式未废弃
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -498,6 +514,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -513,7 +530,7 @@ models:
is_deprecated: false
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -530,6 +547,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -546,6 +564,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -561,7 +580,7 @@ models:
is_deprecated: false
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -578,6 +597,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -594,6 +614,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -610,6 +631,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -626,6 +648,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -641,7 +664,7 @@ models:
is_deprecated: false
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -656,7 +679,7 @@ models:
is_deprecated: false
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -672,6 +695,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -687,6 +711,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -702,6 +727,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -719,6 +745,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -736,6 +763,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -752,6 +780,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -768,7 +797,7 @@ models:
is_deprecated: false
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -785,6 +814,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -803,6 +833,8 @@ models:
- vision
- video
- audio
- thinking
- json_output
is_omni: true
tags:
- 大语言模型
@@ -822,7 +854,7 @@ models:
capability:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -844,6 +876,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -864,7 +897,7 @@ models:
capability:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -886,6 +919,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -907,6 +941,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -928,6 +963,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -947,6 +983,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -964,6 +1001,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -979,6 +1017,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -994,6 +1033,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型

View File

@@ -10,6 +10,7 @@ models:
- vision
- audio
- video
- json_output
is_omni: true
tags:
- 大语言模型
@@ -27,7 +28,8 @@ models:
description: gpt-3.5-turbo-0125大语言模型支持多工具调用、智能体思考、流式工具调用16385上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -42,7 +44,8 @@ models:
description: gpt-3.5-turbo-1106大语言模型支持多工具调用、智能体思考、流式工具调用16385上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -57,7 +60,8 @@ models:
description: gpt-3.5-turbo-16k大语言模型支持多工具调用、智能体思考、流式工具调用16385上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -84,7 +88,8 @@ models:
description: gpt-3.5-turbo大语言模型支持多工具调用、智能体思考、流式工具调用16385上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -99,7 +104,8 @@ models:
description: gpt-4-0125-preview大语言模型支持多工具调用、智能体思考、流式工具调用128000上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -114,7 +120,8 @@ models:
description: gpt-4-1106-preview大语言模型支持多工具调用、智能体思考、流式工具调用128000上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -131,6 +138,7 @@ models:
is_official: true
capability:
- vision
- json_output
is_omni: false
tags:
- 大语言模型
@@ -146,7 +154,8 @@ models:
description: gpt-4-turbo-preview大语言模型支持多工具调用、智能体思考、流式工具调用128000上下文窗口对话模式
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -163,6 +172,7 @@ models:
is_official: true
capability:
- vision
- json_output
is_omni: false
tags:
- 大语言模型
@@ -194,6 +204,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -213,6 +224,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -231,6 +243,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -248,6 +261,7 @@ models:
is_official: true
capability:
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -266,6 +280,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -284,6 +299,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -302,6 +318,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -321,6 +338,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -340,6 +358,7 @@ models:
capability:
- vision
- thinking
- json_output
is_omni: false
tags:
- 大语言模型

View File

@@ -11,6 +11,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -26,6 +27,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -41,6 +43,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -56,6 +59,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -72,6 +76,7 @@ models:
capability:
- vision
- video
- json_output
is_omni: false
tags:
- 大语言模型
@@ -87,6 +92,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -102,6 +108,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -117,6 +124,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -132,6 +140,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -148,6 +157,7 @@ models:
- vision
- video
- thinking
- json_output
is_omni: false
tags:
- 大语言模型
@@ -175,7 +185,8 @@ models:
description: 全新一代主力模型,性能全面升级,在知识、代码、推理等方面表现卓越。最大支持 128k 上下文窗口,输出长度支持最大 12k tokens。
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型
@@ -187,7 +198,8 @@ models:
description: 全新一代轻量版模型,极致响应速度,效果与时延均达到全球一流水平。支持 32k 上下文窗口,输出长度支持最大 12k tokens。
is_deprecated: false
is_official: true
capability: []
capability:
- json_output
is_omni: false
tags:
- 大语言模型

View File

@@ -116,6 +116,11 @@ class LLMNodeConfig(BaseNodeConfig):
description="Top-p 采样参数"
)
json_output: bool = Field(
default=False,
description="是否以 JSON 格式输出"
)
frequency_penalty: float | None = Field(
default=None,
ge=-2.0,

View File

@@ -22,6 +22,7 @@ from app.db import get_db_context
from app.models import ModelType
from app.schemas.model_schema import ModelInfo
from app.services.model_service import ModelConfigService
from app.models.models_model import ModelProvider
logger = logging.getLogger(__name__)
@@ -126,7 +127,11 @@ class LLMNode(BaseNode):
# 4. 创建 LLM 实例(使用已提取的数据)
# 注意:对于流式输出,需要在模型初始化时设置 streaming=True
extra_params = {"streaming": stream} if stream else {}
extra_params: dict[str, Any] = {"streaming": stream} if stream else {}
if self.typed_config.temperature is not None:
extra_params["temperature"] = self.typed_config.temperature
if self.typed_config.max_tokens is not None:
extra_params["max_tokens"] = self.typed_config.max_tokens
llm = RedBearLLM(
RedBearModelConfig(
@@ -135,7 +140,9 @@ class LLMNode(BaseNode):
api_key=model_info.api_key,
base_url=model_info.api_base,
extra_params=extra_params,
is_omni=model_info.is_omni
is_omni=model_info.is_omni,
capability=model_info.capability,
json_output=self.typed_config.json_output,
),
type=model_info.model_type
)
@@ -218,6 +225,16 @@ class LLMNode(BaseNode):
rendered = self._render_template(prompt_template, variable_pool)
self.messages = [{"role": "user", "content": rendered}]
# ChatTongyi 要求 messages 含 'json' 字样才能使用 response_format在 system prompt 中注入
if (self.typed_config.json_output
and model_info.provider.lower() == ModelProvider.DASHSCOPE
and not model_info.is_omni):
system_msg = next((m for m in self.messages if m["role"] == "system"), None)
if system_msg:
system_msg["content"] += "\n请以JSON格式输出。"
else:
self.messages.insert(0, {"role": "system", "content": "请以JSON格式输出。"})
return llm
async def execute(self, state: WorkflowState, variable_pool: VariablePool) -> AIMessage:

View File

@@ -245,6 +245,7 @@ class ModelParameters(BaseModel):
stop: Optional[List[str]] = Field(default=None, description="停止序列")
deep_thinking: bool = Field(default=False, description="是否启用深度思考模式(需模型支持,如 DeepSeek-R1、QwQ 等)")
thinking_budget_tokens: Optional[int] = Field(default=None, ge=1024, le=131072, description="深度思考 token 预算(仅部分模型支持)")
json_output: bool = Field(default=False, description="是否强制 JSON 格式输出(需模型支持 json_output 能力)")
class VariableDefinition(BaseModel):

View File

@@ -120,6 +120,7 @@ class AppChatService:
tools=tools,
deep_thinking=model_parameters.get("deep_thinking", False),
thinking_budget_tokens=model_parameters.get("thinking_budget_tokens"),
json_output=model_parameters.get("json_output", False),
capability=api_key_obj.capability or [],
)
@@ -392,6 +393,7 @@ class AppChatService:
streaming=True,
deep_thinking=model_parameters.get("deep_thinking", False),
thinking_budget_tokens=model_parameters.get("thinking_budget_tokens"),
json_output=model_parameters.get("json_output", False),
capability=api_key_obj.capability or [],
)

View File

@@ -544,7 +544,7 @@ class ConversationService:
api_key=api_key,
base_url=api_base,
is_omni=is_omni,
support_thinking="thinking" in (capability or []),
capability=capability,
),
type=ModelType(model_type)
)

View File

@@ -597,6 +597,7 @@ class AgentRunService:
tools=tools,
deep_thinking=effective_params.get("deep_thinking", False),
thinking_budget_tokens=effective_params.get("thinking_budget_tokens"),
json_output=effective_params.get("json_output", False),
capability=api_key_config.get("capability", []),
)
@@ -853,6 +854,7 @@ class AgentRunService:
streaming=True,
deep_thinking=effective_params.get("deep_thinking", False),
thinking_budget_tokens=effective_params.get("thinking_budget_tokens"),
json_output=effective_params.get("json_output", False),
capability=api_key_config.get("capability", []),
)

View File

@@ -415,9 +415,11 @@ class LLMRouter:
api_key=api_key_config.api_key,
base_url=api_key_config.api_base,
is_omni=api_key_config.is_omni,
support_thinking="thinking" in (api_key_config.capability or []),
temperature=0.3,
max_tokens=500
capability=api_key_config.capability,
extra_params={
"temperature": 0.3,
"max_tokens": 500
}
)
logger.debug(f"创建 LLM 实例 - Provider: {api_key_config.provider}, Model: {api_key_config.model_name}")

View File

@@ -393,7 +393,7 @@ class MasterAgentRouter:
api_key=api_key_config.api_key,
base_url=api_key_config.api_base,
is_omni=api_key_config.is_omni,
support_thinking="thinking" in (api_key_config.capability or []),
capability=api_key_config.capability,
extra_params = extra_params
)

View File

@@ -233,7 +233,7 @@ class MemoryPerceptualService:
api_key=model_config.api_key,
base_url=model_config.api_base,
is_omni=model_config.is_omni,
support_thinking="thinking" in (model_config.capability or []),
capability=model_config.capability,
)
)
return llm, model_config

View File

@@ -47,7 +47,8 @@ class ModelParameterMerger:
"n": 1,
"stop": None,
"deep_thinking": False,
"thinking_budget_tokens": None
"thinking_budget_tokens": None,
"json_output": False
}
# 合并参数:默认值 -> 模型配置 -> Agent 配置

View File

@@ -125,9 +125,11 @@ class ModelConfigService:
api_key=api_key,
base_url=api_base,
is_omni=is_omni,
support_thinking="thinking" in (capability or []),
temperature=0.7,
max_tokens=100
capability=capability,
extra_params={
"temperature": 0.7,
"max_tokens": 100
}
)
# 根据模型类型选择不同的验证方式

View File

@@ -2616,9 +2616,11 @@ class MultiAgentOrchestrator:
api_key=api_key_config.api_key,
base_url=api_key_config.api_base,
is_omni=api_key_config.is_omni,
support_thinking="thinking" in (api_key_config.capability or []),
temperature=0.7, # 整合任务使用中等温度
max_tokens=2000
capability=api_key_config.capability,
extra_params={
"temperature": 0.7, # 整合任务使用中等温度
"max_tokens": 2000
}
)
# 创建 LLM 实例
@@ -2795,10 +2797,12 @@ class MultiAgentOrchestrator:
api_key=api_key_config.api_key,
base_url=api_key_config.api_base,
is_omni=api_key_config.is_omni,
support_thinking="thinking" in (api_key_config.capability or []),
temperature=0.7,
max_tokens=2000,
extra_params={"streaming": True} # 启用流式输出
capability=api_key_config.capability,
extra_params={
"temperature": 0.7,
"max_tokens": 2000,
"streaming": True # 启用流式输出
}
)
# 创建 LLM 实例

View File

@@ -186,7 +186,7 @@ class PromptOptimizerService:
api_key=api_config.api_key,
base_url=api_config.api_base,
is_omni=api_config.is_omni,
support_thinking="thinking" in (api_config.capability or []),
capability=api_config.capability,
), type=ModelType(model_config.type))
try:
prompt_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prompt')

View File

@@ -250,7 +250,8 @@ class SharedChatService:
tools=tools,
deep_thinking=model_parameters.get("deep_thinking", False),
thinking_budget_tokens=model_parameters.get("thinking_budget_tokens"),
capability=api_key_obj.capability or [],
json_output=model_parameters.get("json_output", False),
capability=api_key_obj.capability,
)
# 加载历史消息
@@ -455,6 +456,7 @@ class SharedChatService:
streaming=True,
deep_thinking=model_parameters.get("deep_thinking", False),
thinking_budget_tokens=model_parameters.get("thinking_budget_tokens"),
json_output=model_parameters.get("json_output", False),
capability=api_key_obj.capability or [],
)

View File

@@ -1,4 +1,40 @@
{
"v0.3.0": {
"introduction": {
"codeName": "破晓",
"releaseDate": "2026-4-15",
"upgradePosition": "🐻 全面升级应用工作流、记忆智能与系统稳健性引入版本化API、多模态记忆感知及大量工作流增强打造更可靠、精准的 MemoryBear",
"coreUpgrades": [
"1. 应用与API增强<br>* 版本化API调用支持对外服务API支持指定版本调用<br>* 工作流检查清单:新增结构化验证步骤<br>* 深度思考参数精准控制:仅向支持深度推理的模型发送思考参数<br>* 提示器模型返回优化:优化提示器模型响应处理",
"2. 记忆智能 🧠<br>* 多模态记忆感知Agent支持多模态记忆读取与写入<br>* OpenClaw内置工具新增内置工具扩展Agent工具集",
"3. 用户体验 🎨<br>* 流式渲染稳定性优化解决LLM流式输出页面抖动问题<br>* 记忆中枢更名:「记忆相关」更名为「记忆中枢」",
"4. 工作流改进 ⚙️<br>* 三级变量模板转换:支持三级变量解析<br>* VL模型Token统计修复模型组合中VL模型Token未统计问题<br>* 导入工作流功能特性同步:正确同步开场白、引用等属性<br>* 会话变量名称唯一性校验:防止变量名冲突<br>* 文件类型提取修复正确提取file.type信息<br>* 条件分支显示修复值为0或会话变量时正确渲染<br>* Object/Array校验规则防止JSON序列化错误<br>* HTTP请求Body字段修正body字段从name改为key",
"5. 知识库 📚<br>* Embedding Token截断安全边界统一添加8000 token截断优化Excel独立chunk处理",
"6. 稳健性与缺陷修复 🔧<br>* 原子性更新与批量访问失败修复<br>* 对话别名提取错误修复<br>* 工作流别名提取修正区分用户和AI回复<br>* RAG记忆分页数据修复<br>* 隐式记忆详情显示修复<br>* 向量查询驱动关闭异常修复<br>* 用户管理启停异常修复<br>* 模型列表筛选不一致修复",
"<br>",
"v0.3.0 标志着 MemoryBear 向生产成熟度迈出坚实一步。后续版本将持续深化工作流表达力、记忆检索精度和跨模态理解能力强化复杂Agent编排支持稳固大规模生产部署基础。",
"<br>",
"MemoryBear — 破晓 🐻✨"
]
},
"introduction_en": {
"codeName": "PoXiao",
"releaseDate": "2026-4-15",
"upgradePosition": "🐻 Comprehensive upgrades across application workflows, memory intelligence, and system robustness — introducing versioned APIs, multimodal memory perception, and extensive workflow enhancements for a more reliable MemoryBear",
"coreUpgrades": [
"1. Application & API Enhancements<br>* Versioned API Support: External APIs now support version-specific calls<br>* Workflow Checklist: Structured validation steps before deployment<br>* Deep Thinking Parameter Control: Only send thinking params to supported models<br>* Prompt Optimizer Return Optimization: Improved prompt optimizer response handling",
"2. Memory Intelligence 🧠<br>* Multimodal Memory Perception Agent: Read/write multimodal memory<br>* OpenClaw Built-in Tool: New built-in tool for agent operations",
"3. User Experience 🎨<br>* Streaming Render Stabilization: Eliminated page jitter during LLM output<br>* Memory Hub Renaming: Renamed to better reflect central memory role",
"4. Workflow Improvements ⚙️<br>* Three-Level Variable Template Conversion: Support for three-level variable resolution<br>* VL Model Token Tracking: Fixed token tracking for VL models in model groups<br>* Imported Workflow Feature Sync: Properly sync opening messages, citations, etc.<br>* Session Variable Name Uniqueness: Prevent variable name conflicts<br>* File Type Extraction Fix: Correctly extract file.type information<br>* Condition Branch Display Fix: Correct rendering for value 0 or session variables<br>* Object/Array Validation Rules: Prevent JSON serialization save errors<br>* HTTP Request Body Key Fix: Body field uses key instead of name",
"5. Knowledge Base 📚<br>* Embedding Token Truncation Safety: Unified 8000-token boundary, optimized Excel chunk processing",
"6. Robustness & Bug Fixes 🔧<br>* Atomic update & batch access failure fixes<br>* Conversation alias extraction fix<br>* Workflow alias extraction correction (user vs AI distinction)<br>* RAG memory pagination fix<br>* Implicit memory detail display fix<br>* Vector query driver closed exception fix<br>* User management enable/disable fix<br>* Model list filter inconsistency fix",
"<br>",
"v0.3.0 marks a meaningful step toward production maturity for MemoryBear. Upcoming releases will deepen workflow expressiveness, memory retrieval precision, and cross-modal understanding while strengthening complex agent orchestration and large-scale deployment foundations.",
"<br>",
"MemoryBear — Daybreak 🐻✨"
]
}
},
"v0.2.10": {
"introduction": {
"codeName": "炼剑",

View File

@@ -2,9 +2,9 @@
* @Author: ZhaoYing
* @Date: 2026-03-07 16:49:59
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:21:59
* @Last Modified time: 2026-04-17 10:11:54
*/
import { useEffect, useState, type FC } from 'react';
import { type FC, useEffect, useState } from 'react';
import { Select, Flex, Space } from 'antd';
import type { SelectProps } from 'antd/es/select';
import { useTranslation } from 'react-i18next';
@@ -22,16 +22,10 @@ interface ModelSelectProps extends SelectProps {
fontClassName?: string;
isAutoFetch?: boolean;
initialData?: Model[];
updateOptions?: (options: Model[]) => void;
}
const ModelSelect: FC<ModelSelectProps> = ({
params,
placeholder,
fontClassName,
isAutoFetch = true,
initialData = [],
...props
}) => {
const ModelSelect: FC<ModelSelectProps> = ({ params, placeholder, fontClassName, isAutoFetch = true, initialData = [], updateOptions, ...props }) => {
const { t } = useTranslation();
const [options, setOptions] = useState<Model[]>([]);
@@ -60,6 +54,10 @@ const ModelSelect: FC<ModelSelectProps> = ({
);
};
useEffect(() => {
if (updateOptions) updateOptions([...options, ...initialData]);
}, [options, initialData])
return (
<Select
placeholder={placeholder ?? t('common.pleaseSelect')}

View File

@@ -629,6 +629,7 @@ export const en = {
video: 'Video',
thinking: 'Deep Thinking',
is_thinking: 'Deep Thinking Support',
json_output: 'Support JSON formatted output',
},
knowledgeBase: {
home: 'Home',
@@ -1524,6 +1525,7 @@ export const en = {
}`,
uploadCover: 'Import and Overwrite',
refresh: 'Refresh Current Page',
json_output: 'Support JSON formatted output',
},
userMemory: {
userMemory: 'User Memory',
@@ -2287,6 +2289,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
messagesPlaceholder: 'Write prompts here, type "{" to insert variables, type "insert" to insert',
vision: 'Vision',
parameterSettings: 'Parameter Settings',
json_output: 'Support JSON formatted output',
},
start: {
variables: 'Input Fields',

View File

@@ -859,6 +859,7 @@ export const zh = {
}`,
uploadCover: '导入并覆盖',
refresh: '刷新当前页',
json_output: '支持JSON格式化输出',
},
table: {
totalRecords: '共 {{total}} 条记录'
@@ -1307,6 +1308,7 @@ export const zh = {
video: '视频',
thinking: '深度思考',
is_thinking: '支持深度思考',
json_output: '支持JSON格式化输出',
},
timezones: {
'Asia/Shanghai': '中国标准时间 (UTC+8)',
@@ -2248,6 +2250,7 @@ export const zh = {
messagesPlaceholder: '在此处编写提示,输入“{”插入变量输入“insert”插入',
vision: '视觉',
parameterSettings: '参数设置',
json_output: '支持JSON格式化输出',
},
start: {
variables: '输入字段',

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:28:07
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-31 16:56:57
* @Last Modified time: 2026-04-16 18:51:01
*/
/**
* Model Configuration Modal
@@ -102,14 +102,15 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
}
/** Handle model selection change */
const handleChange: SelectProps['onChange'] = (_value, option) => {
if (source === 'chat') {
form.setFieldValue('label', (option as Model).name)
}
form.setFieldsValue({
const newValues: ModelConfig = {
capability: (option as Model).capability,
deep_thinking: false,
})
json_output: false
}
if (source === 'chat') {
newValues.label = (option as Model).name
}
form.setFieldsValue(newValues)
}
/** Expose methods to parent component */
@@ -119,12 +120,10 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
}));
useEffect(() => {
const { deep_thinking: _, ...rest } = data?.model_parameters || {}
const { deep_thinking: _, json_output: __, ...rest } = data?.model_parameters || {}
form.setFieldsValue(rest)
}, [values?.default_model_config_id])
console.log('handleChange values', values)
return (
<RbModal
title={t('application.modelConfig')}
@@ -159,6 +158,9 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
<FormItem name="deep_thinking" valuePropName="checked" hidden={!['model', 'chat'].includes(source) || !(values?.deep_thinking || values?.capability?.includes('thinking'))}>
<Checkbox>{t('application.deep_thinking')}</Checkbox>
</FormItem>
<FormItem name="json_output" valuePropName="checked" hidden={!(values?.capability?.includes('json_output'))}>
<Checkbox>{t('application.json_output')}</Checkbox>
</FormItem>
{source === 'chat' && <FormItem name="label" hidden />}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:49
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-04-07 15:46:19
* @Last Modified time: 2026-04-16 18:20:14
*/
import type { KnowledgeConfig } from './components/Knowledge/types'
import type { Variable } from './components/VariableList/types'
@@ -24,20 +24,21 @@ export interface ModelConfig {
default_model_config_id?: string;
capability?: Capability[];
/** Temperature for response randomness (0-2) */
temperature: number;
temperature?: number;
/** Maximum tokens in response */
max_tokens: number;
max_tokens?: number;
/** Top-p sampling parameter */
top_p: number;
top_p?: number;
/** Frequency penalty */
frequency_penalty: number;
frequency_penalty?: number;
/** Presence penalty */
presence_penalty: number;
presence_penalty?: number;
/** Number of completions to generate */
n: number;
n?: number;
/** Stop sequences */
stop?: string;
deep_thinking?: boolean;
json_output?: boolean;
}
/**

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:49:28
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-31 13:56:18
* @Last Modified time: 2026-04-16 18:03:53
*/
/**
* Custom Model Modal
@@ -14,7 +14,7 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { Form, Input, App, Checkbox, Button, Row, Col } from 'antd';
import { useTranslation } from 'react-i18next';
import type { CustomModelForm, ModelListItem, CustomModelModalRef, CustomModelModalProps } from '../types';
import type { CustomModelForm, ModelListItem, CustomModelModalRef, CustomModelModalProps, Capability } from '../types';
import RbModal from '@/components/RbModal'
import CustomSelect from '@/components/CustomSelect'
import UploadImages from '@/components/Upload/UploadImages'
@@ -73,6 +73,7 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
is_video: capability?.includes('video') || false,
is_audio: capability?.includes('audio') || false,
is_thinking: capability?.includes('thinking') || false,
json_output: capability?.includes('json_output') || false,
});
} else {
setIsEdit(false);
@@ -102,13 +103,13 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
form
.validateFields()
.then((values) => {
const { logo, type, is_vision, is_video, is_audio, is_omni, is_thinking, ...rest } = values;
const { logo, type, is_vision, is_video, is_audio, is_omni, is_thinking, json_output, ...rest } = values;
const formData: CustomModelForm = {
...rest,
type,
}
if (!['embedding', 'rerank'].includes(type as string)) {
let capability = is_omni ? ["vision", "audio", 'video'] : []
let capability: Capability[] = is_omni ? ["vision", "audio", 'video'] : []
if (!is_omni) {
if (is_vision) {
@@ -124,6 +125,9 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
if (is_thinking) {
capability.push('thinking')
}
if (json_output) {
capability.push('json_output')
}
formData.capability = capability
formData.is_omni = is_omni
@@ -269,6 +273,11 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
<Checkbox>{t('modelNew.is_thinking')}</Checkbox>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item name="json_output" valuePropName="checked" className="rb:mb-0!">
<Checkbox>{t('modelNew.json_output')}</Checkbox>
</Form.Item>
</Col>
</Row>
}
</Form>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:50:18
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-31 15:48:02
* @Last Modified time: 2026-04-16 18:04:46
*/
/**
* Type definitions for Model Management
@@ -296,6 +296,7 @@ export interface CustomModelForm {
is_audio?: boolean;
is_omni?: boolean;
is_thinking?: boolean;
json_output?: boolean;
capability?: Capability[];
}
@@ -325,7 +326,7 @@ export interface BaseRef {
modelListDetailRefresh?: () => void;
}
export type Capability = 'vision' | 'audio' | 'video' | 'thinking';
export type Capability = 'vision' | 'audio' | 'video' | 'thinking' | 'json_output';
export interface Model {
name: string;
type: string;

View File

@@ -1,16 +1,38 @@
import { type FC } from "react";
/*
* @Author: ZhaoYing
* @Date: 2026-03-07 14:55:04
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-04-17 10:05:32
*/
import { type FC, useEffect, useState } from "react";
import { useTranslation } from 'react-i18next'
import { Form } from 'antd'
import { Form, Switch } from 'antd'
import RbSlider from '@/components/RbSlider'
import RbCard from '@/components/RbCard/Card'
import ModelSelect from '@/components/ModelSelect'
import type { Model } from '@/views/ModelManagement/types';
const ModelConfig: FC = () => {
const { t } = useTranslation()
const form = Form.useFormInstance()
const model_id = Form.useWatch(['model_id'], form)
console.log('ModelConfig', model_id)
const [selectedModel, setSelectedModel] = useState<Model | null>(null)
const [options, setOptions] = useState<Model[]>([])
const updateOptions = (options: Model[]) => {
setOptions(options)
}
useEffect(() => {
if (model_id && options) {
const model = options.find(item => item.id === model_id)
setSelectedModel(model || null)
form.setFieldValue('json_output', false)
} else {
setSelectedModel(null)
}
}, [model_id, options])
return (
<>
@@ -25,6 +47,7 @@ const ModelConfig: FC = () => {
params={{ type: 'llm,chat' }}
className="rb:w-full!"
size="small"
updateOptions={updateOptions}
/>
</Form.Item>
{model_id && (
@@ -52,7 +75,7 @@ const ModelConfig: FC = () => {
<Form.Item
name="max_tokens"
label={t('workflow.config.llm.max_tokens')}
className="rb:mb-0!"
className="rb:mb-1.5!"
>
<RbSlider
min={256}
@@ -63,6 +86,16 @@ const ModelConfig: FC = () => {
className="rb:-mt-2!"
/>
</Form.Item>
<Form.Item
name="json_output"
valuePropName="checked"
label={t('workflow.config.llm.json_output')}
layout="horizontal"
className="rb:mb-0!"
hidden={!(selectedModel?.capability?.includes('json_output'))}
>
<Switch />
</Form.Item>
</RbCard>
)}
</>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 15:06:18
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-04-07 19:56:56
* @Last Modified time: 2026-04-16 17:52:30
*/
import LoopNode from './components/Nodes/LoopNode';
import NormalNode from './components/Nodes/NormalNode';
@@ -101,6 +101,10 @@ export const nodeLibrary: NodeLibrary[] = [
step: 1,
defaultValue: 2000
},
json_output: {
type: 'define',
defaultValue: false
},
context: {
type: 'variableList',
placeholder: 'workflow.config.llm.contextPlaceholder'