Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ api_mode = "chat_completions"
# zh: 是否启用 reasoning.effort。
# en: Enable reasoning.effort.
reasoning_enabled = false
# zh: reasoning.effort 档位:none / minimal / low / medium / high / xhigh
# en: reasoning.effort level: none / minimal / low / medium / high / xhigh.
# zh: reasoning effort 档位。
# en: reasoning effort level.
reasoning_effort = "medium"
# zh: 是否启用 thinking(思维链)。
# en: Enable thinking (reasoning).
Expand All @@ -107,6 +107,9 @@ thinking_budget_tokens = 20000
# zh: 是否在请求中发送 budget_tokens(关闭后由提供商决定思维预算)。
# en: Whether to include budget_tokens in the request (if disabled, the provider decides the thinking budget).
thinking_include_budget = true
# zh: reasoning effort 传参风格:openai(reasoning.effort)/ anthropic(output_config.effort)。
# en: Reasoning effort wire format: openai (reasoning.effort) / anthropic (output_config.effort).
reasoning_effort_style = "openai"
# zh: 思维链工具调用兼容:启用后在多轮工具调用中回传 reasoning_content,避免部分模型返回 400。
# en: Thinking tool-call compatibility: pass back reasoning_content in multi-turn tool calls to avoid 400 errors from some models.
thinking_tool_call_compat = true
Expand Down Expand Up @@ -155,8 +158,8 @@ api_mode = "chat_completions"
# zh: 是否启用 reasoning.effort。
# en: Enable reasoning.effort.
reasoning_enabled = false
# zh: reasoning.effort 档位:none / minimal / low / medium / high / xhigh
# en: reasoning.effort level: none / minimal / low / medium / high / xhigh.
# zh: reasoning effort 档位。
# en: reasoning effort level.
reasoning_effort = "medium"
# zh: 是否启用 thinking(思维链)。
# en: Enable thinking (reasoning).
Expand All @@ -167,6 +170,9 @@ thinking_budget_tokens = 20000
# zh: 是否在请求中发送 budget_tokens(关闭后由提供商决定思维预算)。
# en: Whether to include budget_tokens in the request (if disabled, the provider decides the thinking budget).
thinking_include_budget = true
# zh: reasoning effort 传参风格:openai(reasoning.effort)/ anthropic(output_config.effort)。
# en: Reasoning effort wire format: openai (reasoning.effort) / anthropic (output_config.effort).
reasoning_effort_style = "openai"
# zh: 思维链工具调用兼容:启用后在多轮工具调用中回传 reasoning_content,避免部分模型返回 400。
# en: Thinking tool-call compatibility: pass back reasoning_content in multi-turn tool calls to avoid 400 errors from some models.
thinking_tool_call_compat = true
Expand Down Expand Up @@ -208,8 +214,8 @@ api_mode = "chat_completions"
# zh: 是否启用 reasoning.effort。
# en: Enable reasoning.effort.
reasoning_enabled = false
# zh: reasoning.effort 档位:none / minimal / low / medium / high / xhigh
# en: reasoning.effort level: none / minimal / low / medium / high / xhigh.
# zh: reasoning effort 档位。
# en: reasoning effort level.
reasoning_effort = "medium"
# zh: 是否启用 thinking(思维链)。
# en: Enable thinking (reasoning).
Expand All @@ -220,6 +226,9 @@ thinking_budget_tokens = 0
# zh: 是否在请求中发送 budget_tokens(关闭后由提供商决定思维预算)。
# en: Whether to include budget_tokens in the request (if disabled, the provider decides the thinking budget).
thinking_include_budget = true
# zh: reasoning effort 传参风格:openai(reasoning.effort)/ anthropic(output_config.effort)。
# en: Reasoning effort wire format: openai (reasoning.effort) / anthropic (output_config.effort).
reasoning_effort_style = "openai"
# zh: 思维链工具调用兼容:启用后在多轮工具调用中回传 reasoning_content,避免部分模型返回 400。
# en: Thinking tool-call compatibility: pass back reasoning_content in multi-turn tool calls to avoid 400 errors from some models.
thinking_tool_call_compat = true
Expand Down Expand Up @@ -258,8 +267,8 @@ api_mode = "chat_completions"
# zh: 是否启用 reasoning.effort。
# en: Enable reasoning.effort.
reasoning_enabled = false
# zh: reasoning.effort 档位:none / minimal / low / medium / high / xhigh
# en: reasoning.effort level: none / minimal / low / medium / high / xhigh.
# zh: reasoning effort 档位。
# en: reasoning effort level.
reasoning_effort = "medium"
# zh: 是否启用 thinking(思维链)。
# en: Enable thinking (reasoning).
Expand All @@ -270,6 +279,9 @@ thinking_budget_tokens = 0
# zh: 是否在请求中发送 budget_tokens(关闭后由提供商决定思维预算)。
# en: Whether to include budget_tokens in the request (if disabled, the provider decides the thinking budget).
thinking_include_budget = true
# zh: reasoning effort 传参风格:openai(reasoning.effort)/ anthropic(output_config.effort)。
# en: Reasoning effort wire format: openai (reasoning.effort) / anthropic (output_config.effort).
reasoning_effort_style = "openai"
# zh: 思维链工具调用兼容:启用后在多轮工具调用中回传 reasoning_content,避免部分模型返回 400。
# en: Thinking tool-call compatibility: pass back reasoning_content in multi-turn tool calls to avoid 400 errors from some models.
thinking_tool_call_compat = true
Expand Down Expand Up @@ -321,8 +333,8 @@ api_mode = "chat_completions"
# zh: 是否启用 reasoning.effort。
# en: Enable reasoning.effort.
reasoning_enabled = false
# zh: reasoning.effort 档位:none / minimal / low / medium / high / xhigh
# en: reasoning.effort level: none / minimal / low / medium / high / xhigh.
# zh: reasoning effort 档位。
# en: reasoning effort level.
reasoning_effort = "medium"
# zh: 是否启用 thinking(思维链)。
# en: Enable thinking (reasoning).
Expand All @@ -333,6 +345,9 @@ thinking_budget_tokens = 0
# zh: 是否在请求中发送 budget_tokens(关闭后由提供商决定思维预算)。
# en: Whether to include budget_tokens in the request (if disabled, the provider decides the thinking budget).
thinking_include_budget = true
# zh: reasoning effort 传参风格:openai(reasoning.effort)/ anthropic(output_config.effort)。
# en: Reasoning effort wire format: openai (reasoning.effort) / anthropic (output_config.effort).
reasoning_effort_style = "openai"
# zh: 思维链工具调用兼容:启用后在多轮工具调用中回传 reasoning_content,避免部分模型返回 400。
# en: Thinking tool-call compatibility: pass back reasoning_content in multi-turn tool calls to avoid 400 errors from some models.
thinking_tool_call_compat = true
Expand Down
78 changes: 76 additions & 2 deletions scripts/sync_config_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,49 @@ def build_parser() -> argparse.ArgumentParser:
action="store_true",
help="将同步后的完整 TOML 输出到标准输出。",
)
parser.add_argument(
"--prune",
action="store_true",
help="删除存在于 config.toml 但不存在于 config.toml.example 中的配置项(危险操作,需二次确认)。",
)
return parser


def _confirm_prune(removed_paths: list[str]) -> bool:
"""显示即将删除的路径并请求用户二次确认。"""
print(
"\n\033[1;31m[sync-config] ⚠ 危险操作:以下配置项不存在于模板中,将被永久删除:\033[0m"
)
for path in removed_paths:
print(f" \033[31m- {path}\033[0m")
print()
try:
answer = input(
"\033[1;33m确认删除以上配置项?此操作不可撤销。输入 yes 确认: \033[0m"
)
except (EOFError, KeyboardInterrupt):
print()
return False
return answer.strip().lower() == "yes"


def _initial_action_label(*, dry_run: bool, prune: bool) -> str:
if dry_run:
return "预览完成"
if prune:
return "分析完成"
return "同步完成"


def main() -> int:
args = build_parser().parse_args()

# 第一轮:不带 prune 的常规同步(或 dry-run 预览)
try:
result = sync_config_file(
config_path=args.config,
example_path=args.example,
write=not args.dry_run,
write=not args.dry_run and not args.prune,
)
except FileNotFoundError as exc:
print(f"[sync-config] 未找到示例配置:{exc}", file=sys.stderr)
Expand All @@ -57,12 +90,53 @@ def main() -> int:
print(f"[sync-config] 配置解析失败:{exc}", file=sys.stderr)
return 1

action = "预览完成" if args.dry_run else "同步完成"
action = _initial_action_label(dry_run=args.dry_run, prune=args.prune)
print(f"[sync-config] {action}: {args.config}")
print(f"[sync-config] 新增路径数量: {len(result.added_paths)}")
for path in result.added_paths:
print(f" + {path}")

if result.removed_paths:
print(f"[sync-config] 多余路径数量: {len(result.removed_paths)}")
for path in result.removed_paths:
print(f" - {path}")

# --prune 流程:确认后带 prune 重新同步
if args.prune and result.removed_paths:
if args.dry_run:
print("\n[sync-config] --dry-run 模式,跳过删除。")
elif _confirm_prune(result.removed_paths):
result = sync_config_file(
config_path=args.config,
example_path=args.example,
write=True,
prune=True,
)
print(
f"\033[1;32m[sync-config] 已删除 {len(result.removed_paths)} 个多余配置项并写回文件。\033[0m"
)
else:
# 用户取消 prune,仍执行不带 prune 的常规同步
sync_config_file(
config_path=args.config,
example_path=args.example,
write=True,
prune=False,
)
print("[sync-config] 已取消删除,仅执行常规同步。")
elif args.prune and not result.removed_paths:
if not args.dry_run:
# 无多余项但仍需写入常规同步结果
sync_config_file(
config_path=args.config,
example_path=args.example,
write=True,
prune=False,
)
print("[sync-config] 无多余配置项需要删除,已完成常规同步。")
else:
print("[sync-config] 无多余配置项需要删除。")

if args.stdout:
print("\n--- merged config.toml ---\n")
print(result.content, end="")
Expand Down
54 changes: 33 additions & 21 deletions src/Undefined/ai/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
API_MODE_RESPONSES,
build_responses_request_body,
get_api_mode,
get_reasoning_payload,
get_effort_payload,
get_effort_style,
get_thinking_payload,
normalize_responses_result,
)
from Undefined.ai.retrieval import RetrievalRequester
Expand Down Expand Up @@ -117,8 +119,10 @@
"tool_choice",
"stream",
"stream_options",
"thinking",
"reasoning",
"reasoning_effort",
"output_config",
}
)
| _SDK_REQUEST_OPTION_FIELDS
Expand All @@ -139,6 +143,7 @@
"thinking",
"reasoning",
"reasoning_effort",
"output_config",
}
)
| _SDK_REQUEST_OPTION_FIELDS
Expand Down Expand Up @@ -870,6 +875,8 @@ def _build_effective_request_kwargs(
getattr(model_config, "request_params", {}),
overrides,
)
thinking_override = overrides["thinking"] if "thinking" in overrides else None
has_thinking_override = "thinking" in overrides
reserved_fields = (
_RESPONSES_RESERVED_FIELDS
if get_api_mode(model_config) == API_MODE_RESPONSES
Expand All @@ -879,11 +886,15 @@ def _build_effective_request_kwargs(
merged,
reserved_fields,
)
if has_thinking_override:
ignored.pop("thinking", None)
_warn_ignored_request_params(
call_type=call_type,
model_name=model_config.model_name,
ignored=ignored,
)
if has_thinking_override:
allowed["thinking"] = thinking_override
return allowed


Expand Down Expand Up @@ -1447,12 +1458,20 @@ def build_request_body(
"""构建 API 请求体。"""
api_mode = get_api_mode(model_config)
extra_kwargs: dict[str, Any] = dict(kwargs)
reasoning_payload = get_reasoning_payload(model_config)

if "thinking" in extra_kwargs:
normalized = _normalize_thinking_override(
extra_kwargs.get("thinking"), model_config
)
if normalized is None:
extra_kwargs.pop("thinking", None)
else:
extra_kwargs["thinking"] = normalized

if api_mode == API_MODE_RESPONSES:
extra_kwargs.pop("thinking", None)
extra_kwargs.pop("reasoning", None)
extra_kwargs.pop("reasoning_effort", None)
extra_kwargs.pop("output_config", None)
return build_responses_request_body(
model_config,
messages,
Expand All @@ -1470,28 +1489,21 @@ def build_request_body(
"max_tokens": max_tokens,
}

if "thinking" in extra_kwargs:
normalized = _normalize_thinking_override(
extra_kwargs.get("thinking"), model_config
)
if normalized is None:
extra_kwargs.pop("thinking", None)
else:
extra_kwargs["thinking"] = normalized

extra_kwargs.pop("reasoning", None)
extra_kwargs.pop("reasoning_effort", None)
extra_kwargs.pop("output_config", None)

if getattr(model_config, "thinking_enabled", False):
thinking_param: dict[str, Any] = {"type": "enabled"}
if getattr(model_config, "thinking_include_budget", True):
thinking_param["budget_tokens"] = getattr(
model_config, "thinking_budget_tokens", 0
)
body["thinking"] = thinking_param
thinking = get_thinking_payload(model_config)
if thinking is not None:
body["thinking"] = thinking

if reasoning_payload is not None:
body["reasoning"] = reasoning_payload
effort_payload = get_effort_payload(model_config)
if effort_payload is not None:
style = get_effort_style(model_config)
if style == "anthropic":
body["output_config"] = effort_payload
else:
body["reasoning"] = effort_payload

if tools:
body["tools"] = tools
Expand Down
6 changes: 6 additions & 0 deletions src/Undefined/ai/transports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
API_MODE_RESPONSES,
build_responses_request_body,
get_api_mode,
get_effort_payload,
get_effort_style,
get_reasoning_payload,
get_thinking_payload,
normalize_api_mode,
normalize_reasoning_effort,
normalize_responses_result,
Expand All @@ -16,7 +19,10 @@
"API_MODE_RESPONSES",
"build_responses_request_body",
"get_api_mode",
"get_effort_payload",
"get_effort_style",
"get_reasoning_payload",
"get_thinking_payload",
"normalize_api_mode",
"normalize_reasoning_effort",
"normalize_responses_result",
Expand Down
Loading
Loading