Compare commits

...

3 Commits

Author SHA1 Message Date
lorenzejay
e77d1c0d6e added 2026-04-24 13:53:44 -07:00
lorenzejay
c8716e7062 regen cassettes 2026-04-24 13:52:58 -07:00
lorenzejay
e48cf3604e refactor: implement _loop_response_model to manage response_model during tool loops
This change introduces the _loop_response_model method in BaseAgentExecutor to conditionally return the response_model based on the presence of tools. The CrewAgentExecutor and AgentExecutor classes have been updated to utilize this new method, ensuring that the response_model is not sent to the LLM during tool loops, which prevents issues with structured-output schemas. Additionally, tests have been added to verify the correct behavior of this implementation.
2026-04-24 12:20:32 -07:00
9 changed files with 443 additions and 93 deletions

View File

@@ -187,6 +187,8 @@ HEADERS_TO_FILTER = {
"anthropic-ratelimit-tokens-remaining": "ANTHROPIC-RATELIMIT-TOKENS-REMAINING-XXX",
"anthropic-ratelimit-tokens-reset": "ANTHROPIC-RATELIMIT-TOKENS-RESET-XXX",
"x-amz-date": "X-AMZ-DATE-XXX",
"x-amz-security-token": "X-AMZ-SECURITY-TOKEN-XXX",
"x-crewai-organization-id": "X-CREWAI-ORGANIZATION-ID-XXX",
"amz-sdk-invocation-id": "AMZ-SDK-INVOCATION-ID-XXX",
"accept-encoding": "ACCEPT-ENCODING-XXX",
"x-amzn-requestid": "X-AMZN-REQUESTID-XXX",

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from pydantic import BaseModel, Field, PrivateAttr
from pydantic import BaseModel as PydanticBaseModel, Field, PrivateAttr
from crewai.agents.parser import AgentFinish
from crewai.memory.utils import sanitize_scope_name
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
from crewai.task import Task
class BaseAgentExecutor(BaseModel):
class BaseAgentExecutor(PydanticBaseModel):
model_config = {"arbitrary_types_allowed": True}
executor_type: str = "base"
@@ -28,6 +28,26 @@ class BaseAgentExecutor(BaseModel):
messages: list[LLMMessage] = Field(default_factory=list)
_resuming: bool = PrivateAttr(default=False)
def _loop_response_model(self) -> type[PydanticBaseModel] | None:
"""Return the response_model to forward to the LLM during a tool loop.
When the executor has tools, returns ``None``. Sending a strict
structured-output schema alongside ``tools=`` makes many providers
(notably OpenAI's Responses API with ``text.format`` json_schema)
constrain the first response to the schema, so the model emits
placeholder JSON instead of calling tools. Schema conversion is
applied as post-processing in ``Task._export_output()`` (Crew path)
or via ``Converter`` in ``Agent._build_output_from_result()``
(standalone kickoff path).
Subclasses must define ``original_tools`` and ``response_model``
attributes; this base implementation reads them via ``getattr`` so
the helper works for any executor that exposes those names.
"""
if getattr(self, "original_tools", None):
return None
return getattr(self, "response_model", None)
def _save_to_memory(self, output: AgentFinish) -> None:
"""Save task result to unified memory (memory or crew._memory)."""
if self.agent is None:

View File

@@ -334,6 +334,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
enforce_rpm_limit(self.request_within_rpm_limit)
loop_response_model = self._loop_response_model()
answer = get_llm_response(
llm=cast("BaseLLM", self.llm),
messages=self.messages,
@@ -341,11 +342,11 @@ class CrewAgentExecutor(BaseAgentExecutor):
printer=PRINTER,
from_task=self.task,
from_agent=self.agent,
response_model=self.response_model,
response_model=loop_response_model,
executor_context=self,
verbose=self.agent.verbose,
)
if self.response_model is not None:
if loop_response_model is not None:
try:
if isinstance(answer, BaseModel):
output_json = answer.model_dump_json()
@@ -355,7 +356,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
text=output_json,
)
else:
self.response_model.model_validate_json(answer)
loop_response_model.model_validate_json(answer)
formatted_answer = AgentFinish(
thought="",
output=answer,
@@ -486,7 +487,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
available_functions=None,
from_task=self.task,
from_agent=self.agent,
response_model=self.response_model,
response_model=self._loop_response_model(),
executor_context=self,
verbose=self.agent.verbose,
)
@@ -1142,6 +1143,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
enforce_rpm_limit(self.request_within_rpm_limit)
loop_response_model = self._loop_response_model()
answer = await aget_llm_response(
llm=cast("BaseLLM", self.llm),
messages=self.messages,
@@ -1149,12 +1151,12 @@ class CrewAgentExecutor(BaseAgentExecutor):
printer=PRINTER,
from_task=self.task,
from_agent=self.agent,
response_model=self.response_model,
response_model=loop_response_model,
executor_context=self,
verbose=self.agent.verbose,
)
if self.response_model is not None:
if loop_response_model is not None:
try:
if isinstance(answer, BaseModel):
output_json = answer.model_dump_json()
@@ -1164,7 +1166,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
text=output_json,
)
else:
self.response_model.model_validate_json(answer)
loop_response_model.model_validate_json(answer)
formatted_answer = AgentFinish(
thought="",
output=answer,
@@ -1295,7 +1297,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
available_functions=None,
from_task=self.task,
from_agent=self.agent,
response_model=self.response_model,
response_model=self._loop_response_model(),
executor_context=self,
verbose=self.agent.verbose,
)

View File

@@ -1229,7 +1229,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor
printer=PRINTER,
from_task=self.task,
from_agent=self.agent,
response_model=self.response_model,
response_model=self._loop_response_model(),
executor_context=self,
verbose=self.agent.verbose,
)
@@ -1317,7 +1317,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor
available_functions=None,
from_task=self.task,
from_agent=self.agent,
response_model=self.response_model,
response_model=self._loop_response_model(),
executor_context=self,
verbose=self.agent.verbose,
)

View File

@@ -2044,3 +2044,103 @@ class TestVisionImageFormatContract:
assert hasattr(AnthropicCompletion, "_convert_image_blocks"), (
"Anthropic provider must have _convert_image_blocks for auto-conversion"
)
class TestLoopResponseModelGuard:
"""Regression: response_model must not be sent to the LLM during a tool loop.
Bug: when output_pydantic + tools are both set, OpenAI's Responses API
(and several other providers) constrain the first response to the
structured-output schema, so the model emits placeholder JSON instead of
calling tools. Schema conversion is post-processed, so the LLM call must
omit response_model whenever tools are present.
"""
def _make_pydantic_model(self):
from pydantic import BaseModel as _BaseModel
class _Out(_BaseModel):
answer: str
return _Out
def _crew_executor(self, *, with_tools: bool, response_model):
from crewai.agents.crew_agent_executor import CrewAgentExecutor
tool = Mock()
tool.name = "search"
tool.description = "search"
executor = CrewAgentExecutor.model_construct(
response_model=response_model,
original_tools=[tool] if with_tools else [],
)
return executor
def test_crew_helper_strips_when_tools_present(self):
Model = self._make_pydantic_model()
ex = self._crew_executor(with_tools=True, response_model=Model)
assert ex._loop_response_model() is None
def test_crew_helper_preserves_when_no_tools(self):
Model = self._make_pydantic_model()
ex = self._crew_executor(with_tools=False, response_model=Model)
assert ex._loop_response_model() is Model
def test_crew_helper_returns_none_when_no_response_model(self):
ex = self._crew_executor(with_tools=False, response_model=None)
assert ex._loop_response_model() is None
def test_experimental_helper_strips_when_tools_present(self):
Model = self._make_pydantic_model()
llm = Mock()
llm.supports_stop_words.return_value = False
tool = Mock()
tool.name = "search"
ex = _build_executor(llm=llm, response_model=Model, original_tools=[tool])
assert ex._loop_response_model() is None
def test_experimental_helper_preserves_when_no_tools(self):
Model = self._make_pydantic_model()
llm = Mock()
llm.supports_stop_words.return_value = False
ex = _build_executor(llm=llm, response_model=Model, original_tools=[])
assert ex._loop_response_model() is Model
def test_loop_call_sites_route_through_helper(self):
"""Source-level guard: every in-loop LLM call must use the helper.
This catches regressions where someone reintroduces
`response_model=self.response_model` inside a tool loop.
"""
import inspect
from crewai.agents import crew_agent_executor as _crew_mod
from crewai.experimental import agent_executor as _exp_mod
for mod, names in (
(
_crew_mod,
(
"_invoke_loop_react",
"_invoke_loop_native_tools",
"_ainvoke_loop_react",
"_ainvoke_loop_native_tools",
),
),
(
_exp_mod,
("call_llm_and_parse", "call_llm_native_tools"),
),
):
cls = (
_crew_mod.CrewAgentExecutor
if mod is _crew_mod
else _exp_mod.AgentExecutor
)
for name in names:
src = inspect.getsource(getattr(cls, name))
assert "response_model=self.response_model" not in src, (
f"{cls.__name__}.{name} forwards self.response_model directly. "
"Use self._loop_response_model() instead — see "
"TestLoopResponseModelGuard for the bug it prevents."
)

View File

@@ -1,14 +1,10 @@
interactions:
- request:
body: '{"max_tokens":4096,"messages":[{"role":"user","content":"\nCurrent Task:
Calculate 15 + 27 using your add_numbers tool. Report the result."}],"model":"claude-3-5-haiku-20241022","stop_sequences":["\nObservation:"],"stream":false,"system":"You
Calculate 15 + 27 using your add_numbers tool. Report the result."}],"model":"claude-sonnet-4-6","stop_sequences":["\nObservation:"],"stream":false,"system":"You
are Calculator. You are a calculator assistant that uses tools to compute results.\nYour
personal goal is: Perform calculations using available tools","tool_choice":{"type":"tool","name":"structured_output"},"tools":[{"name":"structured_output","description":"Output
the structured response","input_schema":{"type":"object","description":"Structured
output for calculation results.","title":"CalculationResult","properties":{"operation":{"type":"string","description":"The
mathematical operation performed","title":"Operation"},"result":{"type":"integer","description":"The
result of the calculation","title":"Result"},"explanation":{"type":"string","description":"Brief
explanation of the calculation","title":"Explanation"}},"additionalProperties":false,"required":["operation","result","explanation"]}}]}'
personal goal is: Perform calculations using available tools","tools":[{"name":"add_numbers","description":"Add
two numbers together and return the sum.","input_schema":{"properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"],"type":"object","additionalProperties":false},"strict":true}]}'
headers:
User-Agent:
- X-USER-AGENT-XXX
@@ -21,7 +17,7 @@ interactions:
connection:
- keep-alive
content-length:
- '1050'
- '638'
content-type:
- application/json
host:
@@ -37,7 +33,7 @@ interactions:
x-stainless-os:
- X-STAINLESS-OS-XXX
x-stainless-package-version:
- 0.73.0
- 0.96.0
x-stainless-retry-count:
- '0'
x-stainless-runtime:
@@ -50,8 +46,8 @@ interactions:
uri: https://api.anthropic.com/v1/messages
response:
body:
string: '{"model":"claude-3-5-haiku-20241022","id":"msg_01MsoNSVoPuoMYGCcJLvfXS6","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01TtAsqddWjE7C4GYmCKavdg","name":"structured_output","input":{"operation":"Addition","result":42,"explanation":"Added
15 and 27 together"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":573,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":75,"service_tier":"standard","inference_geo":"not_available"}}'
string: '{"model":"claude-sonnet-4-6","id":"msg_016CHZY3K6G73PFc4iAJVC5C","type":"message","role":"assistant","content":[{"type":"text","text":"I''ll
calculate 15 + 27 right away using the `add_numbers` tool!"},{"type":"tool_use","id":"toolu_011UPTzkFNsgYshddoTX2A8X","name":"add_numbers","input":{"a":15,"b":27},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":633,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":92,"service_tier":"standard","inference_geo":"global"}}'
headers:
CF-RAY:
- CF-RAY-XXX
@@ -62,7 +58,7 @@ interactions:
Content-Type:
- application/json
Date:
- Thu, 12 Feb 2026 22:11:20 GMT
- Fri, 24 Apr 2026 20:48:28 GMT
Server:
- cloudflare
Transfer-Encoding:
@@ -84,11 +80,119 @@ interactions:
anthropic-ratelimit-output-tokens-reset:
- ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-RESET-XXX
anthropic-ratelimit-requests-limit:
- '4000'
- '20000'
anthropic-ratelimit-requests-remaining:
- '3999'
- '19999'
anthropic-ratelimit-requests-reset:
- '2026-02-12T22:11:18Z'
- '2026-04-24T20:48:25Z'
anthropic-ratelimit-tokens-limit:
- ANTHROPIC-RATELIMIT-TOKENS-LIMIT-XXX
anthropic-ratelimit-tokens-remaining:
- ANTHROPIC-RATELIMIT-TOKENS-REMAINING-XXX
anthropic-ratelimit-tokens-reset:
- ANTHROPIC-RATELIMIT-TOKENS-RESET-XXX
cf-cache-status:
- DYNAMIC
request-id:
- REQUEST-ID-XXX
set-cookie:
- SET-COOKIE-XXX
strict-transport-security:
- STS-XXX
x-envoy-upstream-service-time:
- '2056'
status:
code: 200
message: OK
- request:
body: '{"max_tokens":4096,"messages":[{"role":"user","content":"\nCurrent Task:
Calculate 15 + 27 using your add_numbers tool. Report the result."},{"role":"assistant","content":[{"type":"tool_use","id":"toolu_011UPTzkFNsgYshddoTX2A8X","name":"add_numbers","input":{"a":15,"b":27}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_011UPTzkFNsgYshddoTX2A8X","content":"42"}]}],"model":"claude-sonnet-4-6","stop_sequences":["\nObservation:"],"stream":false,"system":"You
are Calculator. You are a calculator assistant that uses tools to compute results.\nYour
personal goal is: Perform calculations using available tools","tools":[{"name":"add_numbers","description":"Add
two numbers together and return the sum.","input_schema":{"properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"],"type":"object","additionalProperties":false},"strict":true}]}'
headers:
User-Agent:
- X-USER-AGENT-XXX
accept:
- application/json
accept-encoding:
- ACCEPT-ENCODING-XXX
anthropic-version:
- '2023-06-01'
connection:
- keep-alive
content-length:
- '887'
content-type:
- application/json
cookie:
- COOKIE-XXX
host:
- api.anthropic.com
x-api-key:
- X-API-KEY-XXX
x-stainless-arch:
- X-STAINLESS-ARCH-XXX
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- X-STAINLESS-OS-XXX
x-stainless-package-version:
- 0.96.0
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.13.3
x-stainless-timeout:
- NOT_GIVEN
method: POST
uri: https://api.anthropic.com/v1/messages
response:
body:
string: "{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01V13FHBygrTHV6pnhocqvd9\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"The
result of **15 + 27 = 42**! \U0001F389\\n\\nUsing the `add_numbers` tool,
I computed the sum of 15 and 27, and the result is **42**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":717,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":51,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}}"
headers:
CF-RAY:
- CF-RAY-XXX
Connection:
- keep-alive
Content-Security-Policy:
- CSP-FILTERED
Content-Type:
- application/json
Date:
- Fri, 24 Apr 2026 20:48:32 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Robots-Tag:
- none
anthropic-organization-id:
- ANTHROPIC-ORGANIZATION-ID-XXX
anthropic-ratelimit-input-tokens-limit:
- ANTHROPIC-RATELIMIT-INPUT-TOKENS-LIMIT-XXX
anthropic-ratelimit-input-tokens-remaining:
- ANTHROPIC-RATELIMIT-INPUT-TOKENS-REMAINING-XXX
anthropic-ratelimit-input-tokens-reset:
- ANTHROPIC-RATELIMIT-INPUT-TOKENS-RESET-XXX
anthropic-ratelimit-output-tokens-limit:
- ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-LIMIT-XXX
anthropic-ratelimit-output-tokens-remaining:
- ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-REMAINING-XXX
anthropic-ratelimit-output-tokens-reset:
- ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-RESET-XXX
anthropic-ratelimit-requests-limit:
- '20000'
anthropic-ratelimit-requests-remaining:
- '19999'
anthropic-ratelimit-requests-reset:
- '2026-04-24T20:48:28Z'
anthropic-ratelimit-tokens-limit:
- ANTHROPIC-RATELIMIT-TOKENS-LIMIT-XXX
anthropic-ratelimit-tokens-remaining:
@@ -102,7 +206,139 @@ interactions:
strict-transport-security:
- STS-XXX
x-envoy-upstream-service-time:
- '1234'
- '4700'
status:
code: 200
message: OK
- request:
body: "{\"max_tokens\":4096,\"messages\":[{\"role\":\"user\",\"content\":\"The
result of **15 + 27 = 42**! \U0001F389\\n\\nUsing the `add_numbers` tool, I
computed the sum of 15 and 27, and the result is **42**.\"}],\"model\":\"claude-sonnet-4-6\",\"stop_sequences\":[\"\\nObservation:\"],\"stream\":false,\"system\":\"Format
your final answer according to the following OpenAPI schema: {\\n \\\"type\\\":
\\\"json_schema\\\",\\n \\\"json_schema\\\": {\\n \\\"name\\\": \\\"CalculationResult\\\",\\n
\ \\\"strict\\\": true,\\n \\\"schema\\\": {\\n \\\"description\\\":
\\\"Structured output for calculation results.\\\",\\n \\\"properties\\\":
{\\n \\\"operation\\\": {\\n \\\"description\\\": \\\"The mathematical
operation performed\\\",\\n \\\"title\\\": \\\"Operation\\\",\\n \\\"type\\\":
\\\"string\\\"\\n },\\n \\\"result\\\": {\\n \\\"description\\\":
\\\"The result of the calculation\\\",\\n \\\"title\\\": \\\"Result\\\",\\n
\ \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"explanation\\\":
{\\n \\\"description\\\": \\\"Brief explanation of the calculation\\\",\\n
\ \\\"title\\\": \\\"Explanation\\\",\\n \\\"type\\\": \\\"string\\\"\\n
\ }\\n },\\n \\\"required\\\": [\\n \\\"operation\\\",\\n
\ \\\"result\\\",\\n \\\"explanation\\\"\\n ],\\n \\\"title\\\":
\\\"CalculationResult\\\",\\n \\\"type\\\": \\\"object\\\",\\n \\\"additionalProperties\\\":
false\\n }\\n }\\n}\\n\\nIMPORTANT: Preserve the original content exactly
as-is. Do NOT rewrite, paraphrase, or modify the meaning of the content. Only
structure it to match the schema format.\\n\\nDo not include the OpenAPI schema
in the final output. Ensure the final output does not include any code block
markers like ```json or ```python.\",\"tool_choice\":{\"type\":\"tool\",\"name\":\"structured_output\"},\"tools\":[{\"name\":\"structured_output\",\"description\":\"Output
the structured response\",\"input_schema\":{\"type\":\"object\",\"description\":\"Structured
output for calculation results.\",\"title\":\"CalculationResult\",\"properties\":{\"operation\":{\"type\":\"string\",\"description\":\"The
mathematical operation performed\",\"title\":\"Operation\"},\"result\":{\"type\":\"integer\",\"description\":\"The
result of the calculation\",\"title\":\"Result\"},\"explanation\":{\"type\":\"string\",\"description\":\"Brief
explanation of the calculation\",\"title\":\"Explanation\"}},\"additionalProperties\":false,\"required\":[\"operation\",\"result\",\"explanation\"]}}]}"
headers:
User-Agent:
- X-USER-AGENT-XXX
accept:
- application/json
accept-encoding:
- ACCEPT-ENCODING-XXX
anthropic-version:
- '2023-06-01'
connection:
- keep-alive
content-length:
- '2313'
content-type:
- application/json
cookie:
- COOKIE-XXX
host:
- api.anthropic.com
x-api-key:
- X-API-KEY-XXX
x-stainless-arch:
- X-STAINLESS-ARCH-XXX
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- X-STAINLESS-OS-XXX
x-stainless-package-version:
- 0.96.0
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.13.3
x-stainless-timeout:
- NOT_GIVEN
method: POST
uri: https://api.anthropic.com/v1/messages
response:
body:
string: "{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01Csx2k6XmGyjfWkdLrXzLyA\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01DzJ3ZUUnvPPJgzioZnxjKM\",\"name\":\"structured_output\",\"input\":{\"operation\":\"add_numbers\",\"result\":42,\"explanation\":\"The
result of **15 + 27 = 42**! \U0001F389\\n\\nUsing the `add_numbers` tool,
I computed the sum of 15 and 27, and the result is **42**.\"},\"caller\":{\"type\":\"direct\"}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1122,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":116,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}}"
headers:
CF-RAY:
- CF-RAY-XXX
Connection:
- keep-alive
Content-Security-Policy:
- CSP-FILTERED
Content-Type:
- application/json
Date:
- Fri, 24 Apr 2026 20:48:34 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Robots-Tag:
- none
anthropic-organization-id:
- ANTHROPIC-ORGANIZATION-ID-XXX
anthropic-ratelimit-input-tokens-limit:
- ANTHROPIC-RATELIMIT-INPUT-TOKENS-LIMIT-XXX
anthropic-ratelimit-input-tokens-remaining:
- ANTHROPIC-RATELIMIT-INPUT-TOKENS-REMAINING-XXX
anthropic-ratelimit-input-tokens-reset:
- ANTHROPIC-RATELIMIT-INPUT-TOKENS-RESET-XXX
anthropic-ratelimit-output-tokens-limit:
- ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-LIMIT-XXX
anthropic-ratelimit-output-tokens-remaining:
- ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-REMAINING-XXX
anthropic-ratelimit-output-tokens-reset:
- ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-RESET-XXX
anthropic-ratelimit-requests-limit:
- '20000'
anthropic-ratelimit-requests-remaining:
- '19999'
anthropic-ratelimit-requests-reset:
- '2026-04-24T20:48:32Z'
anthropic-ratelimit-tokens-limit:
- ANTHROPIC-RATELIMIT-TOKENS-LIMIT-XXX
anthropic-ratelimit-tokens-remaining:
- ANTHROPIC-RATELIMIT-TOKENS-REMAINING-XXX
anthropic-ratelimit-tokens-reset:
- ANTHROPIC-RATELIMIT-TOKENS-RESET-XXX
cf-cache-status:
- DYNAMIC
request-id:
- REQUEST-ID-XXX
server-timing:
- x-originResponse;dur=1594
strict-transport-security:
- STS-XXX
vary:
- Accept-Encoding
x-envoy-upstream-service-time:
- '1588'
status:
code: 200
message: OK

View File

@@ -8,20 +8,10 @@ interactions:
[{"toolSpec": {"name": "add_numbers", "description": "Add two numbers together
and return the sum.", "inputSchema": {"json": {"properties": {"a": {"title":
"A", "type": "integer"}, "b": {"title": "B", "type": "integer"}}, "required":
["a", "b"], "type": "object", "additionalProperties": false}}}}, {"toolSpec":
{"name": "structured_output", "description": "Use this tool to provide your
final structured response. Call this tool when you have gathered all necessary
information and are ready to provide the final answer in the required format.",
"inputSchema": {"json": {"description": "Structured output for calculation results.",
"properties": {"operation": {"description": "The mathematical operation performed",
"title": "Operation", "type": "string"}, "result": {"description": "The result
of the calculation", "title": "Result", "type": "integer"}, "explanation": {"description":
"Brief explanation of the calculation", "title": "Explanation", "type": "string"}},
"required": ["operation", "result", "explanation"], "title": "CalculationResult",
"type": "object", "additionalProperties": false}}}}]}}'
["a", "b"], "type": "object", "additionalProperties": false}}}}]}}'
headers:
Content-Length:
- '1509'
- '702'
Content-Type:
- !!binary |
YXBwbGljYXRpb24vanNvbg==
@@ -36,21 +26,23 @@ interactions:
- AUTHORIZATION-XXX
x-amz-date:
- X-AMZ-DATE-XXX
x-amz-security-token:
- X-AMZ-SECURITY-TOKEN-XXX
method: POST
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-sonnet-20240229-v1%3A0/converse
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/us.anthropic.claude-sonnet-4-6/converse
response:
body:
string: '{"metrics":{"latencyMs":1968},"output":{"message":{"content":[{"text":"Okay,
let''s calculate 15 + 27 using the add_numbers tool:"},{"toolUse":{"input":{"a":15,"b":27},"name":"add_numbers","toolUseId":"tooluse_pSseOamVELzpL3kQG5VukN"}}],"role":"assistant"}},"stopReason":"tool_use","usage":{"inputTokens":488,"outputTokens":91,"serverToolUsage":{},"totalTokens":579}}'
string: '{"metrics":{"latencyMs":1570},"output":{"message":{"content":[{"text":"I''ll
calculate 15 + 27 right away using the `add_numbers` tool!"},{"toolUse":{"input":{"a":15,"b":27},"name":"add_numbers","toolUseId":"tooluse_lYs0ulbVGQaYseRBvZ7tdX","type":"tool_use"}}],"role":"assistant"}},"stopReason":"tool_use","usage":{"cacheReadInputTokenCount":0,"cacheReadInputTokens":0,"cacheWriteInputTokenCount":0,"cacheWriteInputTokens":0,"inputTokens":645,"outputTokens":92,"serverToolUsage":{},"totalTokens":737}}'
headers:
Connection:
- keep-alive
Content-Length:
- '366'
- '500'
Content-Type:
- application/json
Date:
- Thu, 12 Feb 2026 22:01:04 GMT
- Fri, 24 Apr 2026 20:49:36 GMT
x-amzn-RequestId:
- X-AMZN-REQUESTID-XXX
status:
@@ -59,29 +51,19 @@ interactions:
- request:
body: '{"messages": [{"role": "user", "content": [{"text": "\nCurrent Task: Calculate
15 + 27 using your add_numbers tool. Report the result."}]}, {"role": "assistant",
"content": [{"toolUse": {"toolUseId": "tooluse_pSseOamVELzpL3kQG5VukN", "name":
"content": [{"toolUse": {"toolUseId": "tooluse_lYs0ulbVGQaYseRBvZ7tdX", "name":
"add_numbers", "input": {"a": 15, "b": 27}}}]}, {"role": "user", "content":
[{"toolResult": {"toolUseId": "tooluse_pSseOamVELzpL3kQG5VukN", "content": [{"text":
[{"toolResult": {"toolUseId": "tooluse_lYs0ulbVGQaYseRBvZ7tdX", "content": [{"text":
"42"}]}}]}], "inferenceConfig": {"stopSequences": ["\nObservation:"]}, "system":
[{"text": "You are Calculator. You are a calculator assistant that uses tools
to compute results.\nYour personal goal is: Perform calculations using available
tools"}], "toolConfig": {"tools": [{"toolSpec": {"name": "add_numbers", "description":
"Add two numbers together and return the sum.", "inputSchema": {"json": {"properties":
{"a": {"title": "A", "type": "integer"}, "b": {"title": "B", "type": "integer"}},
"required": ["a", "b"], "type": "object", "additionalProperties": false}}}},
{"toolSpec": {"name": "structured_output", "description": "Use this tool to
provide your final structured response. Call this tool when you have gathered
all necessary information and are ready to provide the final answer in the required
format.", "inputSchema": {"json": {"description": "Structured output for calculation
results.", "properties": {"operation": {"description": "The mathematical operation
performed", "title": "Operation", "type": "string"}, "result": {"description":
"The result of the calculation", "title": "Result", "type": "integer"}, "explanation":
{"description": "Brief explanation of the calculation", "title": "Explanation",
"type": "string"}}, "required": ["operation", "result", "explanation"], "title":
"CalculationResult", "type": "object", "additionalProperties": false}}}}]}}'
"required": ["a", "b"], "type": "object", "additionalProperties": false}}}}]}}'
headers:
Content-Length:
- '1784'
- '977'
Content-Type:
- !!binary |
YXBwbGljYXRpb24vanNvbg==
@@ -96,41 +78,50 @@ interactions:
- AUTHORIZATION-XXX
x-amz-date:
- X-AMZ-DATE-XXX
x-amz-security-token:
- X-AMZ-SECURITY-TOKEN-XXX
method: POST
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-sonnet-20240229-v1%3A0/converse
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/us.anthropic.claude-sonnet-4-6/converse
response:
body:
string: '{"metrics":{"latencyMs":7598},"output":{"message":{"content":[{"toolUse":{"input":{"operation":"Addition","result":42,"explanation":"I
added 15 and 27 using the add_numbers tool."},"name":"structured_output","toolUseId":"tooluse_RT8uSPaM37Q8CVuo3rJLtI"}}],"role":"assistant"}},"stopReason":"tool_use","usage":{"inputTokens":571,"outputTokens":102,"serverToolUsage":{},"totalTokens":673}}'
string: "{\"metrics\":{\"latencyMs\":1456},\"output\":{\"message\":{\"content\":[{\"text\":\"The
result of **15 + 27 = 42**! \U0001F9EE\\n\\nUsing the `add_numbers` tool,
I computed the sum of 15 and 27, and the result is **42**.\"}],\"role\":\"assistant\"}},\"stopReason\":\"end_turn\",\"usage\":{\"cacheReadInputTokenCount\":0,\"cacheReadInputTokens\":0,\"cacheWriteInputTokenCount\":0,\"cacheWriteInputTokens\":0,\"inputTokens\":729,\"outputTokens\":51,\"serverToolUsage\":{},\"totalTokens\":780}}"
headers:
Connection:
- keep-alive
Content-Length:
- '387'
- '443'
Content-Type:
- application/json
Date:
- Thu, 12 Feb 2026 22:01:12 GMT
- Fri, 24 Apr 2026 20:49:37 GMT
x-amzn-RequestId:
- X-AMZN-REQUESTID-XXX
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "user", "content": [{"text": "\nCurrent Task: Calculate
15 + 27 using your add_numbers tool. Report the result."}]}, {"role": "assistant",
"content": [{"toolUse": {"toolUseId": "tooluse_pSseOamVELzpL3kQG5VukN", "name":
"add_numbers", "input": {"a": 15, "b": 27}}}]}, {"role": "user", "content":
[{"toolResult": {"toolUseId": "tooluse_pSseOamVELzpL3kQG5VukN", "content": [{"text":
"42"}]}}]}, {"role": "assistant", "content": [{"text": "{\"operation\":\"Addition\",\"result\":42,\"explanation\":\"I
added 15 and 27 using the add_numbers tool.\"}"}]}], "inferenceConfig": {"stopSequences":
["\nObservation:"]}, "system": [{"text": "You are Calculator. You are a calculator
assistant that uses tools to compute results.\nYour personal goal is: Perform
calculations using available tools"}], "toolConfig": {"tools": [{"toolSpec":
{"name": "add_numbers", "description": "Add two numbers together and return
the sum.", "inputSchema": {"json": {"properties": {"a": {"title": "A", "type":
"integer"}, "b": {"title": "B", "type": "integer"}}, "required": ["a", "b"],
"type": "object", "additionalProperties": false}}}}, {"toolSpec": {"name": "structured_output",
body: '{"messages": [{"role": "user", "content": [{"text": "The result of **15
+ 27 = 42**! \ud83e\uddee\n\nUsing the `add_numbers` tool, I computed the sum
of 15 and 27, and the result is **42**."}]}], "inferenceConfig": {"stopSequences":
["\nObservation:"]}, "system": [{"text": "Format your final answer according
to the following OpenAPI schema: {\n \"type\": \"json_schema\",\n \"json_schema\":
{\n \"name\": \"CalculationResult\",\n \"strict\": true,\n \"schema\":
{\n \"description\": \"Structured output for calculation results.\",\n \"properties\":
{\n \"operation\": {\n \"description\": \"The mathematical operation
performed\",\n \"title\": \"Operation\",\n \"type\": \"string\"\n },\n \"result\":
{\n \"description\": \"The result of the calculation\",\n \"title\":
\"Result\",\n \"type\": \"integer\"\n },\n \"explanation\":
{\n \"description\": \"Brief explanation of the calculation\",\n \"title\":
\"Explanation\",\n \"type\": \"string\"\n }\n },\n \"required\":
[\n \"operation\",\n \"result\",\n \"explanation\"\n ],\n \"title\":
\"CalculationResult\",\n \"type\": \"object\",\n \"additionalProperties\":
false\n }\n }\n}\n\nIMPORTANT: Preserve the original content exactly as-is.
Do NOT rewrite, paraphrase, or modify the meaning of the content. Only structure
it to match the schema format.\n\nDo not include the OpenAPI schema in the final
output. Ensure the final output does not include any code block markers like
```json or ```python."}], "toolConfig": {"tools": [{"toolSpec": {"name": "structured_output",
"description": "Use this tool to provide your final structured response. Call
this tool when you have gathered all necessary information and are ready to
provide the final answer in the required format.", "inputSchema": {"json": {"description":
@@ -140,10 +131,10 @@ interactions:
"type": "integer"}, "explanation": {"description": "Brief explanation of the
calculation", "title": "Explanation", "type": "string"}}, "required": ["operation",
"result", "explanation"], "title": "CalculationResult", "type": "object", "additionalProperties":
false}}}}]}}'
false}}}}], "toolChoice": {"tool": {"name": "structured_output"}}}}'
headers:
Content-Length:
- '1942'
- '2545'
Content-Type:
- !!binary |
YXBwbGljYXRpb24vanNvbg==
@@ -158,28 +149,27 @@ interactions:
- AUTHORIZATION-XXX
x-amz-date:
- X-AMZ-DATE-XXX
x-amz-security-token:
- X-AMZ-SECURITY-TOKEN-XXX
method: POST
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-sonnet-20240229-v1%3A0/converse
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/us.anthropic.claude-sonnet-4-6/converse
response:
body:
string: '{"message":"The model returned the following errors: Your API request
included an `assistant` message in the final position, which would pre-fill
the `assistant` response. When using tools, pre-filling the `assistant` response
is not supported."}'
string: "{\"metrics\":{\"latencyMs\":1729},\"output\":{\"message\":{\"content\":[{\"toolUse\":{\"input\":{\"operation\":\"add_numbers\",\"result\":42,\"explanation\":\"The
result of **15 + 27 = 42**! \U0001F9EE\\n\\nUsing the `add_numbers` tool,
I computed the sum of 15 and 27, and the result is **42**.\"},\"name\":\"structured_output\",\"toolUseId\":\"tooluse_FavcTtQCbzFrqURMiduSla\",\"type\":\"tool_use\"}}],\"role\":\"assistant\"}},\"stopReason\":\"tool_use\",\"usage\":{\"cacheReadInputTokenCount\":0,\"cacheReadInputTokens\":0,\"cacheWriteInputTokenCount\":0,\"cacheWriteInputTokens\":0,\"inputTokens\":1150,\"outputTokens\":116,\"serverToolUsage\":{},\"totalTokens\":1266}}"
headers:
Connection:
- keep-alive
Content-Length:
- '246'
- '603'
Content-Type:
- application/json
Date:
- Thu, 12 Feb 2026 22:01:12 GMT
x-amzn-ErrorType:
- ValidationException:http://internal.amazon.com/coral/com.amazon.bedrock/
- Fri, 24 Apr 2026 20:49:39 GMT
x-amzn-RequestId:
- X-AMZN-REQUESTID-XXX
status:
code: 400
message: Bad Request
code: 200
message: OK
version: 1

View File

@@ -975,7 +975,7 @@ def test_anthropic_agent_kickoff_structured_output_with_tools():
role="Calculator",
goal="Perform calculations using available tools",
backstory="You are a calculator assistant that uses tools to compute results.",
llm=LLM(model="anthropic/claude-3-5-haiku-20241022"),
llm=LLM(model="anthropic/claude-sonnet-4-6"),
tools=[add_numbers],
verbose=True,
)

View File

@@ -952,7 +952,7 @@ def test_bedrock_agent_kickoff_structured_output_with_tools():
role="Calculator",
goal="Perform calculations using available tools",
backstory="You are a calculator assistant that uses tools to compute results.",
llm=LLM(model="bedrock/anthropic.claude-3-sonnet-20240229-v1:0"),
llm=LLM(model="bedrock/us.anthropic.claude-sonnet-4-6"),
tools=[add_numbers],
verbose=True,
)