fix(llm): unify JSON output handling across providers and fix tool+json_output compatibility
- Remove redundant `response_format` injection for VOLCANO provider since it's unsupported; rely on system prompt injection instead - Extend system prompt JSON injection logic to cover VOLCANO and tool-enabled cases universally - Simplify model parameter construction by removing redundant `params["model_kwargs"] = model_kwargs` assignments - Refactor `CompatibleChatOpenAI._get_request_payload` to strip `response_format` when tools are present, avoiding strict validation errors in langchain_openai - Fix timestamp calculation order in `datetime_tool.py` to avoid integer truncation before multiplication
This commit is contained in:
@@ -101,12 +101,10 @@ class RedBearModelFactory:
|
||||
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
|
||||
|
||||
if provider in [ModelProvider.OPENAI, ModelProvider.XINFERENCE, ModelProvider.GPUSTACK, ModelProvider.OLLAMA, ModelProvider.VOLCANO]:
|
||||
@@ -148,11 +146,12 @@ class RedBearModelFactory:
|
||||
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"}
|
||||
model_kwargs = params.setdefault("model_kwargs", {})
|
||||
# VOLCANO 模型不支持 response_format,JSON 输出由 system prompt 注入实现
|
||||
if provider != ModelProvider.VOLCANO:
|
||||
model_kwargs["response_format"] = {"type": "json_object"}
|
||||
return params
|
||||
elif provider == ModelProvider.DASHSCOPE:
|
||||
params = {
|
||||
@@ -172,11 +171,9 @@ class RedBearModelFactory:
|
||||
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:
|
||||
# Bedrock 使用 AWS 凭证
|
||||
@@ -225,8 +222,8 @@ class RedBearModelFactory:
|
||||
}
|
||||
# JSON 输出模式
|
||||
if config.json_output:
|
||||
params.setdefault("model_kwargs", {})
|
||||
params["model_kwargs"]["response_format"] = {"type": "json_object"}
|
||||
model_kwargs = params.setdefault("model_kwargs", {})
|
||||
model_kwargs["response_format"] = {"type": "json_object"}
|
||||
return params
|
||||
else:
|
||||
raise BusinessException(f"不支持的提供商: {provider}", code=BizCode.PROVIDER_NOT_SUPPORTED)
|
||||
@@ -261,12 +258,13 @@ def get_provider_llm_class(config: RedBearModelConfig, type: ModelType = ModelTy
|
||||
if provider == ModelProvider.VOLCANO:
|
||||
return CompatibleChatOpenAI
|
||||
if provider in [ModelProvider.OPENAI, ModelProvider.XINFERENCE, ModelProvider.GPUSTACK]:
|
||||
if type == ModelType.LLM:
|
||||
return OpenAI
|
||||
elif type == ModelType.CHAT:
|
||||
return ChatOpenAI
|
||||
else:
|
||||
raise BusinessException(f"不支持的模型提供商及类型: {provider}-{type}", code=BizCode.PROVIDER_NOT_SUPPORTED)
|
||||
return CompatibleChatOpenAI
|
||||
# if type == ModelType.LLM:
|
||||
# return OpenAI
|
||||
# elif type == ModelType.CHAT:
|
||||
# return CompatibleChatOpenAI
|
||||
# else:
|
||||
# raise BusinessException(f"不支持的模型提供商及类型: {provider}-{type}", code=BizCode.PROVIDER_NOT_SUPPORTED)
|
||||
elif provider == ModelProvider.DASHSCOPE:
|
||||
return ChatTongyi
|
||||
elif provider == ModelProvider.OLLAMA:
|
||||
|
||||
@@ -8,12 +8,33 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.outputs import ChatGenerationChunk, ChatResult
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
|
||||
class CompatibleChatOpenAI(ChatOpenAI):
|
||||
"""火山和千问的omni兼容模型,支持深度思考内容(reasoning_content)的流式和非流式透传。"""
|
||||
"""火山和千问的omni兼容模型,支持深度思考内容(reasoning_content)的流式和非流式透传。
|
||||
|
||||
同时修复 json_output + tools 同时使用时 langchain_openai 强制走 .parse()/.stream()
|
||||
导致 strict 校验报错的问题:有工具时从 payload 中移除 response_format,
|
||||
让父类走普通 .create()/.astream() 路径,JSON 输出由 system prompt 指令保证。
|
||||
"""
|
||||
|
||||
def _get_request_payload(
|
||||
self,
|
||||
input_: list[BaseMessage],
|
||||
*,
|
||||
stop: list[str] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> dict:
|
||||
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
|
||||
# 有工具时 langchain_openai 检测到 response_format 会切换到 .parse()/.stream()
|
||||
# 接口,OpenAI SDK 要求此时所有工具必须 strict=True,动态生成的工具不满足。
|
||||
# 移除 response_format,让父类走普通路径,JSON 输出由 system prompt 指令保证。
|
||||
if payload.get("tools") and "response_format" in payload:
|
||||
payload.pop("response_format")
|
||||
return payload
|
||||
|
||||
def _create_chat_result(self, response: Union[dict, Any], generation_info: Optional[dict] = None) -> ChatResult:
|
||||
result = super()._create_chat_result(response, generation_info)
|
||||
|
||||
Reference in New Issue
Block a user