Add enable_tools, enabled_tools, session_id to /v1/messages endpoint

Support the same shorthand as /v1/chat/completions: enable_tools=true
with an optional enabled_tools list uses built-in server tools without
requiring full Anthropic tool definitions. session_id is passed through
for sandbox isolation. max_tokens is now optional.
This commit is contained in:
Roland Tannous 2026-04-11 21:02:16 +04:00
commit bb1ac77c34
3 changed files with 48 additions and 8 deletions

View file

@ -621,7 +621,7 @@ class AnthropicTool(BaseModel):
class AnthropicMessagesRequest(BaseModel):
model: str = "default"
max_tokens: int
max_tokens: Optional[int] = None
messages: list[AnthropicMessage]
system: Optional[Union[str, list]] = None
tools: Optional[list[AnthropicTool]] = None
@ -632,6 +632,10 @@ class AnthropicMessagesRequest(BaseModel):
top_k: Optional[int] = None
stop_sequences: Optional[list[str]] = None
metadata: Optional[dict] = None
# [x-unsloth] extensions — mirror the OpenAI endpoint convenience fields
enable_tools: Optional[bool] = None
enabled_tools: Optional[list[str]] = None
session_id: Optional[str] = None
model_config = {"extra": "allow"}

View file

@ -2255,16 +2255,26 @@ async def anthropic_messages(
cancel_event = threading.Event()
# ── Tool-calling path ─────────────────────────────────────
use_tools = (
payload.tools
and len(payload.tools) > 0
and llama_backend.supports_tools
# Two ways to enable tools:
# 1. Anthropic-style: send full tool definitions in payload.tools
# 2. Unsloth shorthand: enable_tools=true + optional enabled_tools list
use_tools = llama_backend.supports_tools and (
(payload.tools and len(payload.tools) > 0)
or payload.enable_tools
)
if use_tools:
from core.inference.tools import ALL_TOOLS
openai_tools = anthropic_tools_to_openai(payload.tools)
if payload.tools and len(payload.tools) > 0:
openai_tools = anthropic_tools_to_openai(payload.tools)
elif payload.enabled_tools is not None:
openai_tools = [
t for t in ALL_TOOLS
if t["function"]["name"] in payload.enabled_tools
]
else:
openai_tools = ALL_TOOLS
# Build tool-use system prompt nudge (same logic as /chat/completions)
_tool_names = {t["function"]["name"] for t in openai_tools}
@ -2339,6 +2349,7 @@ async def anthropic_messages(
max_tool_iterations = 25,
auto_heal_tool_calls = True,
tool_call_timeout = 300,
session_id = payload.session_id,
)
if payload.stream:

View file

@ -41,13 +41,19 @@ from core.inference.anthropic_compat import (
class TestAnthropicModels:
def test_minimal_request(self):
req = AnthropicMessagesRequest(
messages = [{"role": "user", "content": "Hi"}],
)
assert req.max_tokens is None
assert req.model == "default"
assert req.stream is False
def test_max_tokens_optional(self):
req = AnthropicMessagesRequest(
max_tokens = 100,
messages = [{"role": "user", "content": "Hi"}],
)
assert req.max_tokens == 100
assert req.model == "default"
assert req.stream is False
def test_system_as_string(self):
req = AnthropicMessagesRequest(
@ -81,6 +87,25 @@ class TestAnthropicModels:
)
assert req.stream is False
def test_enable_tools_shorthand(self):
req = AnthropicMessagesRequest(
messages = [{"role": "user", "content": "Hi"}],
enable_tools = True,
enabled_tools = ["web_search", "python"],
session_id = "my-session",
)
assert req.enable_tools is True
assert req.enabled_tools == ["web_search", "python"]
assert req.session_id == "my-session"
def test_extension_fields_default_none(self):
req = AnthropicMessagesRequest(
messages = [{"role": "user", "content": "Hi"}],
)
assert req.enable_tools is None
assert req.enabled_tools is None
assert req.session_id is None
def test_response_model_defaults(self):
resp = AnthropicMessagesResponse()
assert resp.type == "message"