mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-04 08:28:36 +00:00
fix: allow custom provider to configure thinking style (#4429)
This commit is contained in:
parent
34f776b48b
commit
ceae6d7b61
@ -182,6 +182,7 @@ class ProviderConfig(Base):
|
|||||||
extra_headers: dict[str, str] | None = None # Custom headers (e.g. APP-Code for AiHubMix)
|
extra_headers: dict[str, str] | None = None # Custom headers (e.g. APP-Code for AiHubMix)
|
||||||
extra_body: dict[str, Any] | None = None # Extra provider request fields; shape depends on provider/API surface
|
extra_body: dict[str, Any] | None = None # Extra provider request fields; shape depends on provider/API surface
|
||||||
extra_query: dict[str, str] | None = None # Extra query params (e.g. api-version for Azure-style gateways)
|
extra_query: dict[str, str] | None = None # Extra query params (e.g. api-version for Azure-style gateways)
|
||||||
|
thinking_style: str = "" # Thinking injection style for custom providers: "thinking_type", "enable_thinking", "reasoning_split"
|
||||||
|
|
||||||
|
|
||||||
class BedrockProviderConfig(ProviderConfig):
|
class BedrockProviderConfig(ProviderConfig):
|
||||||
|
|||||||
@ -54,7 +54,7 @@ def _make_provider_core(
|
|||||||
if provider_name and not spec and p:
|
if provider_name and not spec and p:
|
||||||
if not p.api_base:
|
if not p.api_base:
|
||||||
raise ValueError(f"Provider '{provider_name}' requires api_base in config.")
|
raise ValueError(f"Provider '{provider_name}' requires api_base in config.")
|
||||||
spec = create_dynamic_spec(provider_name)
|
spec = create_dynamic_spec(provider_name, thinking_style=p.thinking_style if p else "")
|
||||||
if spec and spec.is_transcription_only:
|
if spec and spec.is_transcription_only:
|
||||||
raise ValueError(f"Provider '{provider_name}' only supports transcription.")
|
raise ValueError(f"Provider '{provider_name}' only supports transcription.")
|
||||||
backend = spec.backend if spec else "openai_compat"
|
backend = spec.backend if spec else "openai_compat"
|
||||||
|
|||||||
@ -628,7 +628,7 @@ def find_by_name(name: str) -> ProviderSpec | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def create_dynamic_spec(name: str) -> ProviderSpec:
|
def create_dynamic_spec(name: str, *, thinking_style: str = "") -> ProviderSpec:
|
||||||
"""Create a dynamic ProviderSpec for custom user-defined providers."""
|
"""Create a dynamic ProviderSpec for custom user-defined providers."""
|
||||||
normalized = to_snake(name.replace("-", "_"))
|
normalized = to_snake(name.replace("-", "_"))
|
||||||
strip_prefixes = tuple(dict.fromkeys((name, normalized)))
|
strip_prefixes = tuple(dict.fromkeys((name, normalized)))
|
||||||
@ -640,4 +640,5 @@ def create_dynamic_spec(name: str) -> ProviderSpec:
|
|||||||
backend="openai_compat",
|
backend="openai_compat",
|
||||||
is_direct=True,
|
is_direct=True,
|
||||||
strip_model_prefixes=strip_prefixes,
|
strip_model_prefixes=strip_prefixes,
|
||||||
|
thinking_style=thinking_style,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -359,7 +359,7 @@ def _resolve_settings_provider(
|
|||||||
normalized = provider_name.replace("-", "_")
|
normalized = provider_name.replace("-", "_")
|
||||||
for extra_name, provider_config in _dynamic_provider_items(config):
|
for extra_name, provider_config in _dynamic_provider_items(config):
|
||||||
if provider_name == extra_name or normalized == extra_name.replace("-", "_"):
|
if provider_name == extra_name or normalized == extra_name.replace("-", "_"):
|
||||||
return create_dynamic_spec(extra_name), extra_name, provider_config
|
return create_dynamic_spec(extra_name, thinking_style=provider_config.thinking_style), extra_name, provider_config
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -739,7 +739,7 @@ def settings_payload(
|
|||||||
providers.append(
|
providers.append(
|
||||||
_provider_settings_row(
|
_provider_settings_row(
|
||||||
provider_key,
|
provider_key,
|
||||||
create_dynamic_spec(provider_key),
|
create_dynamic_spec(provider_key, thinking_style=provider_config.thinking_style),
|
||||||
provider_config,
|
provider_config,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
47
tests/providers/test_custom_thinking_style.py
Normal file
47
tests/providers/test_custom_thinking_style.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""Tests for custom provider thinking_style config passthrough."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from nanobot.config.schema import ProviderConfig, ProvidersConfig
|
||||||
|
from nanobot.providers.registry import create_dynamic_spec
|
||||||
|
|
||||||
|
|
||||||
|
class TestCustomProviderThinkingStyle:
|
||||||
|
"""Verify that thinking_style flows from config to ProviderSpec."""
|
||||||
|
|
||||||
|
def test_default_thinking_style_is_empty(self) -> None:
|
||||||
|
cfg = ProviderConfig()
|
||||||
|
assert cfg.thinking_style == ""
|
||||||
|
|
||||||
|
def test_create_dynamic_spec_default(self) -> None:
|
||||||
|
spec = create_dynamic_spec("custom")
|
||||||
|
assert spec.thinking_style == ""
|
||||||
|
|
||||||
|
def test_create_dynamic_spec_with_thinking_type(self) -> None:
|
||||||
|
spec = create_dynamic_spec("custom", thinking_style="thinking_type")
|
||||||
|
assert spec.thinking_style == "thinking_type"
|
||||||
|
|
||||||
|
def test_create_dynamic_spec_with_enable_thinking(self) -> None:
|
||||||
|
spec = create_dynamic_spec("custom", thinking_style="enable_thinking")
|
||||||
|
assert spec.thinking_style == "enable_thinking"
|
||||||
|
|
||||||
|
def test_create_dynamic_spec_with_reasoning_split(self) -> None:
|
||||||
|
spec = create_dynamic_spec("custom", thinking_style="reasoning_split")
|
||||||
|
assert spec.thinking_style == "reasoning_split"
|
||||||
|
|
||||||
|
def test_provider_config_accepts_camel_case(self) -> None:
|
||||||
|
"""Config JSON uses camelCase: thinkingStyle."""
|
||||||
|
cfg = ProviderConfig.model_validate({"thinkingStyle": "thinking_type"})
|
||||||
|
assert cfg.thinking_style == "thinking_type"
|
||||||
|
|
||||||
|
def test_providers_config_custom_has_thinking_style(self) -> None:
|
||||||
|
"""Full providers config round-trip."""
|
||||||
|
data = {
|
||||||
|
"custom": {
|
||||||
|
"apiKey": "sk-test",
|
||||||
|
"apiBase": "https://example.com/v1",
|
||||||
|
"thinkingStyle": "enable_thinking",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pc = ProvidersConfig.model_validate(data)
|
||||||
|
assert pc.custom.thinking_style == "enable_thinking"
|
||||||
Loading…
x
Reference in New Issue
Block a user