mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-30 23:02:50 +00:00
Merge branch 'main' into lorenze/imp/docs-improvements
This commit is contained in:
@@ -7,6 +7,7 @@ and memory management.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Any, Literal, cast
|
from typing import TYPE_CHECKING, Any, Literal, cast
|
||||||
|
|
||||||
@@ -685,30 +686,138 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
AgentFinish if tool has result_as_answer=True, None otherwise.
|
AgentFinish if tool has result_as_answer=True, None otherwise.
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
|
||||||
import json
|
|
||||||
|
|
||||||
from crewai.events import crewai_event_bus
|
|
||||||
from crewai.events.types.tool_usage_events import (
|
|
||||||
ToolUsageErrorEvent,
|
|
||||||
ToolUsageFinishedEvent,
|
|
||||||
ToolUsageStartedEvent,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not tool_calls:
|
if not tool_calls:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Only process the FIRST tool call for sequential execution with reflection
|
parsed_calls = [
|
||||||
tool_call = tool_calls[0]
|
parsed
|
||||||
|
for tool_call in tool_calls
|
||||||
|
if (parsed := self._parse_native_tool_call(tool_call)) is not None
|
||||||
|
]
|
||||||
|
if not parsed_calls:
|
||||||
|
return None
|
||||||
|
|
||||||
# Extract tool call info - handle OpenAI-style, Anthropic-style, and Gemini-style
|
original_tools_by_name: dict[str, Any] = {}
|
||||||
|
for tool in self.original_tools or []:
|
||||||
|
original_tools_by_name[sanitize_tool_name(tool.name)] = tool
|
||||||
|
|
||||||
|
if len(parsed_calls) > 1:
|
||||||
|
has_result_as_answer_in_batch = any(
|
||||||
|
bool(
|
||||||
|
original_tools_by_name.get(func_name)
|
||||||
|
and getattr(
|
||||||
|
original_tools_by_name.get(func_name), "result_as_answer", False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for _, func_name, _ in parsed_calls
|
||||||
|
)
|
||||||
|
has_max_usage_count_in_batch = any(
|
||||||
|
bool(
|
||||||
|
original_tools_by_name.get(func_name)
|
||||||
|
and getattr(
|
||||||
|
original_tools_by_name.get(func_name),
|
||||||
|
"max_usage_count",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
is not None
|
||||||
|
)
|
||||||
|
for _, func_name, _ in parsed_calls
|
||||||
|
)
|
||||||
|
|
||||||
|
# Preserve historical sequential behavior for result_as_answer batches.
|
||||||
|
# Also avoid threading around usage counters for max_usage_count tools.
|
||||||
|
if has_result_as_answer_in_batch or has_max_usage_count_in_batch:
|
||||||
|
logger.debug(
|
||||||
|
"Skipping parallel native execution because batch includes result_as_answer or max_usage_count tool"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
execution_plan: list[
|
||||||
|
tuple[str, str, str | dict[str, Any], Any | None]
|
||||||
|
] = []
|
||||||
|
for call_id, func_name, func_args in parsed_calls:
|
||||||
|
original_tool = original_tools_by_name.get(func_name)
|
||||||
|
execution_plan.append((call_id, func_name, func_args, original_tool))
|
||||||
|
|
||||||
|
self._append_assistant_tool_calls_message(
|
||||||
|
[
|
||||||
|
(call_id, func_name, func_args)
|
||||||
|
for call_id, func_name, func_args, _ in execution_plan
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
max_workers = min(8, len(execution_plan))
|
||||||
|
ordered_results: list[dict[str, Any] | None] = [None] * len(execution_plan)
|
||||||
|
with ThreadPoolExecutor(max_workers=max_workers) as pool:
|
||||||
|
futures = {
|
||||||
|
pool.submit(
|
||||||
|
self._execute_single_native_tool_call,
|
||||||
|
call_id=call_id,
|
||||||
|
func_name=func_name,
|
||||||
|
func_args=func_args,
|
||||||
|
available_functions=available_functions,
|
||||||
|
original_tool=original_tool,
|
||||||
|
should_execute=True,
|
||||||
|
): idx
|
||||||
|
for idx, (
|
||||||
|
call_id,
|
||||||
|
func_name,
|
||||||
|
func_args,
|
||||||
|
original_tool,
|
||||||
|
) in enumerate(execution_plan)
|
||||||
|
}
|
||||||
|
for future in as_completed(futures):
|
||||||
|
idx = futures[future]
|
||||||
|
ordered_results[idx] = future.result()
|
||||||
|
|
||||||
|
for execution_result in ordered_results:
|
||||||
|
if not execution_result:
|
||||||
|
continue
|
||||||
|
tool_finish = self._append_tool_result_and_check_finality(
|
||||||
|
execution_result
|
||||||
|
)
|
||||||
|
if tool_finish:
|
||||||
|
return tool_finish
|
||||||
|
|
||||||
|
reasoning_prompt = self._i18n.slice("post_tool_reasoning")
|
||||||
|
reasoning_message: LLMMessage = {
|
||||||
|
"role": "user",
|
||||||
|
"content": reasoning_prompt,
|
||||||
|
}
|
||||||
|
self.messages.append(reasoning_message)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Sequential behavior: process only first tool call, then force reflection.
|
||||||
|
call_id, func_name, func_args = parsed_calls[0]
|
||||||
|
self._append_assistant_tool_calls_message([(call_id, func_name, func_args)])
|
||||||
|
|
||||||
|
execution_result = self._execute_single_native_tool_call(
|
||||||
|
call_id=call_id,
|
||||||
|
func_name=func_name,
|
||||||
|
func_args=func_args,
|
||||||
|
available_functions=available_functions,
|
||||||
|
original_tool=original_tools_by_name.get(func_name),
|
||||||
|
should_execute=True,
|
||||||
|
)
|
||||||
|
tool_finish = self._append_tool_result_and_check_finality(execution_result)
|
||||||
|
if tool_finish:
|
||||||
|
return tool_finish
|
||||||
|
|
||||||
|
reasoning_prompt = self._i18n.slice("post_tool_reasoning")
|
||||||
|
reasoning_message: LLMMessage = {
|
||||||
|
"role": "user",
|
||||||
|
"content": reasoning_prompt,
|
||||||
|
}
|
||||||
|
self.messages.append(reasoning_message)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_native_tool_call(
|
||||||
|
self, tool_call: Any
|
||||||
|
) -> tuple[str, str, str | dict[str, Any]] | None:
|
||||||
if hasattr(tool_call, "function"):
|
if hasattr(tool_call, "function"):
|
||||||
# OpenAI-style: has .function.name and .function.arguments
|
|
||||||
call_id = getattr(tool_call, "id", f"call_{id(tool_call)}")
|
call_id = getattr(tool_call, "id", f"call_{id(tool_call)}")
|
||||||
func_name = sanitize_tool_name(tool_call.function.name)
|
func_name = sanitize_tool_name(tool_call.function.name)
|
||||||
func_args = tool_call.function.arguments
|
return call_id, func_name, tool_call.function.arguments
|
||||||
elif hasattr(tool_call, "function_call") and tool_call.function_call:
|
if hasattr(tool_call, "function_call") and tool_call.function_call:
|
||||||
# Gemini-style: has .function_call.name and .function_call.args
|
|
||||||
call_id = f"call_{id(tool_call)}"
|
call_id = f"call_{id(tool_call)}"
|
||||||
func_name = sanitize_tool_name(tool_call.function_call.name)
|
func_name = sanitize_tool_name(tool_call.function_call.name)
|
||||||
func_args = (
|
func_args = (
|
||||||
@@ -716,13 +825,12 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
if tool_call.function_call.args
|
if tool_call.function_call.args
|
||||||
else {}
|
else {}
|
||||||
)
|
)
|
||||||
elif hasattr(tool_call, "name") and hasattr(tool_call, "input"):
|
return call_id, func_name, func_args
|
||||||
# Anthropic format: has .name and .input (ToolUseBlock)
|
if hasattr(tool_call, "name") and hasattr(tool_call, "input"):
|
||||||
call_id = getattr(tool_call, "id", f"call_{id(tool_call)}")
|
call_id = getattr(tool_call, "id", f"call_{id(tool_call)}")
|
||||||
func_name = sanitize_tool_name(tool_call.name)
|
func_name = sanitize_tool_name(tool_call.name)
|
||||||
func_args = tool_call.input # Already a dict in Anthropic
|
return call_id, func_name, tool_call.input
|
||||||
elif isinstance(tool_call, dict):
|
if isinstance(tool_call, dict):
|
||||||
# Support OpenAI "id", Bedrock "toolUseId", or generate one
|
|
||||||
call_id = (
|
call_id = (
|
||||||
tool_call.get("id")
|
tool_call.get("id")
|
||||||
or tool_call.get("toolUseId")
|
or tool_call.get("toolUseId")
|
||||||
@@ -733,10 +841,15 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
func_info.get("name", "") or tool_call.get("name", "")
|
func_info.get("name", "") or tool_call.get("name", "")
|
||||||
)
|
)
|
||||||
func_args = func_info.get("arguments", "{}") or tool_call.get("input", {})
|
func_args = func_info.get("arguments", "{}") or tool_call.get("input", {})
|
||||||
else:
|
return call_id, func_name, func_args
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _append_assistant_tool_calls_message(
|
||||||
|
self,
|
||||||
|
parsed_calls: list[tuple[str, str, str | dict[str, Any]]],
|
||||||
|
) -> None:
|
||||||
|
import json
|
||||||
|
|
||||||
# Append assistant message with single tool call
|
|
||||||
assistant_message: LLMMessage = {
|
assistant_message: LLMMessage = {
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": None,
|
"content": None,
|
||||||
@@ -751,12 +864,30 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
else json.dumps(func_args),
|
else json.dumps(func_args),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
for call_id, func_name, func_args in parsed_calls
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
self.messages.append(assistant_message)
|
self.messages.append(assistant_message)
|
||||||
|
|
||||||
# Parse arguments for the single tool call
|
def _execute_single_native_tool_call(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
call_id: str,
|
||||||
|
func_name: str,
|
||||||
|
func_args: str | dict[str, Any],
|
||||||
|
available_functions: dict[str, Callable[..., Any]],
|
||||||
|
original_tool: Any | None = None,
|
||||||
|
should_execute: bool = True,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
|
from crewai.events.types.tool_usage_events import (
|
||||||
|
ToolUsageErrorEvent,
|
||||||
|
ToolUsageFinishedEvent,
|
||||||
|
ToolUsageStartedEvent,
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(func_args, str):
|
if isinstance(func_args, str):
|
||||||
try:
|
try:
|
||||||
args_dict = json.loads(func_args)
|
args_dict = json.loads(func_args)
|
||||||
@@ -765,28 +896,26 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
else:
|
else:
|
||||||
args_dict = func_args
|
args_dict = func_args
|
||||||
|
|
||||||
agent_key = getattr(self.agent, "key", "unknown") if self.agent else "unknown"
|
if original_tool is None:
|
||||||
|
for tool in self.original_tools or []:
|
||||||
|
if sanitize_tool_name(tool.name) == func_name:
|
||||||
|
original_tool = tool
|
||||||
|
break
|
||||||
|
|
||||||
# Find original tool by matching sanitized name (needed for cache_function and result_as_answer)
|
|
||||||
|
|
||||||
original_tool = None
|
|
||||||
for tool in self.original_tools or []:
|
|
||||||
if sanitize_tool_name(tool.name) == func_name:
|
|
||||||
original_tool = tool
|
|
||||||
break
|
|
||||||
|
|
||||||
# Check if tool has reached max usage count
|
|
||||||
max_usage_reached = False
|
max_usage_reached = False
|
||||||
if original_tool:
|
if not should_execute and original_tool:
|
||||||
if (
|
max_usage_reached = True
|
||||||
hasattr(original_tool, "max_usage_count")
|
elif (
|
||||||
and original_tool.max_usage_count is not None
|
should_execute
|
||||||
and original_tool.current_usage_count >= original_tool.max_usage_count
|
and original_tool
|
||||||
):
|
and getattr(original_tool, "max_usage_count", None) is not None
|
||||||
max_usage_reached = True
|
and getattr(original_tool, "current_usage_count", 0)
|
||||||
|
>= original_tool.max_usage_count
|
||||||
|
):
|
||||||
|
max_usage_reached = True
|
||||||
|
|
||||||
# Check cache before executing
|
|
||||||
from_cache = False
|
from_cache = False
|
||||||
|
result: str = "Tool not found"
|
||||||
input_str = json.dumps(args_dict) if args_dict else ""
|
input_str = json.dumps(args_dict) if args_dict else ""
|
||||||
if self.tools_handler and self.tools_handler.cache:
|
if self.tools_handler and self.tools_handler.cache:
|
||||||
cached_result = self.tools_handler.cache.read(
|
cached_result = self.tools_handler.cache.read(
|
||||||
@@ -800,7 +929,7 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
)
|
)
|
||||||
from_cache = True
|
from_cache = True
|
||||||
|
|
||||||
# Emit tool usage started event
|
agent_key = getattr(self.agent, "key", "unknown") if self.agent else "unknown"
|
||||||
started_at = datetime.now()
|
started_at = datetime.now()
|
||||||
crewai_event_bus.emit(
|
crewai_event_bus.emit(
|
||||||
self,
|
self,
|
||||||
@@ -816,14 +945,12 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
|
|
||||||
track_delegation_if_needed(func_name, args_dict, self.task)
|
track_delegation_if_needed(func_name, args_dict, self.task)
|
||||||
|
|
||||||
# Find the structured tool for hook context
|
|
||||||
structured_tool: CrewStructuredTool | None = None
|
structured_tool: CrewStructuredTool | None = None
|
||||||
for structured in self.tools or []:
|
for structured in self.tools or []:
|
||||||
if sanitize_tool_name(structured.name) == func_name:
|
if sanitize_tool_name(structured.name) == func_name:
|
||||||
structured_tool = structured
|
structured_tool = structured
|
||||||
break
|
break
|
||||||
|
|
||||||
# Execute before_tool_call hooks
|
|
||||||
hook_blocked = False
|
hook_blocked = False
|
||||||
before_hook_context = ToolCallHookContext(
|
before_hook_context = ToolCallHookContext(
|
||||||
tool_name=func_name,
|
tool_name=func_name,
|
||||||
@@ -847,58 +974,44 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
color="red",
|
color="red",
|
||||||
)
|
)
|
||||||
|
|
||||||
# If hook blocked execution, set result and skip tool execution
|
|
||||||
if hook_blocked:
|
if hook_blocked:
|
||||||
result = f"Tool execution blocked by hook. Tool: {func_name}"
|
result = f"Tool execution blocked by hook. Tool: {func_name}"
|
||||||
# Execute the tool (only if not cached, not at max usage, and not blocked by hook)
|
|
||||||
elif not from_cache and not max_usage_reached:
|
|
||||||
result = "Tool not found"
|
|
||||||
if func_name in available_functions:
|
|
||||||
try:
|
|
||||||
tool_func = available_functions[func_name]
|
|
||||||
raw_result = tool_func(**args_dict)
|
|
||||||
|
|
||||||
# Add to cache after successful execution (before string conversion)
|
|
||||||
if self.tools_handler and self.tools_handler.cache:
|
|
||||||
should_cache = True
|
|
||||||
if (
|
|
||||||
original_tool
|
|
||||||
and hasattr(original_tool, "cache_function")
|
|
||||||
and callable(original_tool.cache_function)
|
|
||||||
):
|
|
||||||
should_cache = original_tool.cache_function(
|
|
||||||
args_dict, raw_result
|
|
||||||
)
|
|
||||||
if should_cache:
|
|
||||||
self.tools_handler.cache.add(
|
|
||||||
tool=func_name, input=input_str, output=raw_result
|
|
||||||
)
|
|
||||||
|
|
||||||
# Convert to string for message
|
|
||||||
result = (
|
|
||||||
str(raw_result)
|
|
||||||
if not isinstance(raw_result, str)
|
|
||||||
else raw_result
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
result = f"Error executing tool: {e}"
|
|
||||||
if self.task:
|
|
||||||
self.task.increment_tools_errors()
|
|
||||||
crewai_event_bus.emit(
|
|
||||||
self,
|
|
||||||
event=ToolUsageErrorEvent(
|
|
||||||
tool_name=func_name,
|
|
||||||
tool_args=args_dict,
|
|
||||||
from_agent=self.agent,
|
|
||||||
from_task=self.task,
|
|
||||||
agent_key=agent_key,
|
|
||||||
error=e,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
error_event_emitted = True
|
|
||||||
elif max_usage_reached and original_tool:
|
elif max_usage_reached and original_tool:
|
||||||
# Return error message when max usage limit is reached
|
|
||||||
result = f"Tool '{func_name}' has reached its usage limit of {original_tool.max_usage_count} times and cannot be used anymore."
|
result = f"Tool '{func_name}' has reached its usage limit of {original_tool.max_usage_count} times and cannot be used anymore."
|
||||||
|
elif not from_cache and func_name in available_functions:
|
||||||
|
try:
|
||||||
|
raw_result = available_functions[func_name](**args_dict)
|
||||||
|
|
||||||
|
if self.tools_handler and self.tools_handler.cache:
|
||||||
|
should_cache = True
|
||||||
|
if (
|
||||||
|
original_tool
|
||||||
|
and hasattr(original_tool, "cache_function")
|
||||||
|
and callable(original_tool.cache_function)
|
||||||
|
):
|
||||||
|
should_cache = original_tool.cache_function(args_dict, raw_result)
|
||||||
|
if should_cache:
|
||||||
|
self.tools_handler.cache.add(
|
||||||
|
tool=func_name, input=input_str, output=raw_result
|
||||||
|
)
|
||||||
|
|
||||||
|
result = str(raw_result) if not isinstance(raw_result, str) else raw_result
|
||||||
|
except Exception as e:
|
||||||
|
result = f"Error executing tool: {e}"
|
||||||
|
if self.task:
|
||||||
|
self.task.increment_tools_errors()
|
||||||
|
crewai_event_bus.emit(
|
||||||
|
self,
|
||||||
|
event=ToolUsageErrorEvent(
|
||||||
|
tool_name=func_name,
|
||||||
|
tool_args=args_dict,
|
||||||
|
from_agent=self.agent,
|
||||||
|
from_task=self.task,
|
||||||
|
agent_key=agent_key,
|
||||||
|
error=e,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
error_event_emitted = True
|
||||||
|
|
||||||
after_hook_context = ToolCallHookContext(
|
after_hook_context = ToolCallHookContext(
|
||||||
tool_name=func_name,
|
tool_name=func_name,
|
||||||
@@ -938,7 +1051,23 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Append tool result message
|
return {
|
||||||
|
"call_id": call_id,
|
||||||
|
"func_name": func_name,
|
||||||
|
"result": result,
|
||||||
|
"from_cache": from_cache,
|
||||||
|
"original_tool": original_tool,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _append_tool_result_and_check_finality(
|
||||||
|
self, execution_result: dict[str, Any]
|
||||||
|
) -> AgentFinish | None:
|
||||||
|
call_id = cast(str, execution_result["call_id"])
|
||||||
|
func_name = cast(str, execution_result["func_name"])
|
||||||
|
result = cast(str, execution_result["result"])
|
||||||
|
from_cache = cast(bool, execution_result["from_cache"])
|
||||||
|
original_tool = execution_result["original_tool"]
|
||||||
|
|
||||||
tool_message: LLMMessage = {
|
tool_message: LLMMessage = {
|
||||||
"role": "tool",
|
"role": "tool",
|
||||||
"tool_call_id": call_id,
|
"tool_call_id": call_id,
|
||||||
@@ -947,7 +1076,6 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
}
|
}
|
||||||
self.messages.append(tool_message)
|
self.messages.append(tool_message)
|
||||||
|
|
||||||
# Log the tool execution
|
|
||||||
if self.agent and self.agent.verbose:
|
if self.agent and self.agent.verbose:
|
||||||
cache_info = " (from cache)" if from_cache else ""
|
cache_info = " (from cache)" if from_cache else ""
|
||||||
self._printer.print(
|
self._printer.print(
|
||||||
@@ -960,20 +1088,11 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
and hasattr(original_tool, "result_as_answer")
|
and hasattr(original_tool, "result_as_answer")
|
||||||
and original_tool.result_as_answer
|
and original_tool.result_as_answer
|
||||||
):
|
):
|
||||||
# Return immediately with tool result as final answer
|
|
||||||
return AgentFinish(
|
return AgentFinish(
|
||||||
thought="Tool result is the final answer",
|
thought="Tool result is the final answer",
|
||||||
output=result,
|
output=result,
|
||||||
text=result,
|
text=result,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Inject post-tool reasoning prompt to enforce analysis
|
|
||||||
reasoning_prompt = self._i18n.slice("post_tool_reasoning")
|
|
||||||
reasoning_message: LLMMessage = {
|
|
||||||
"role": "user",
|
|
||||||
"content": reasoning_prompt,
|
|
||||||
}
|
|
||||||
self.messages.append(reasoning_message)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def ainvoke(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
async def ainvoke(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
@@ -668,9 +669,12 @@ class AgentExecutor(Flow[AgentReActState], CrewAgentExecutorMixin):
|
|||||||
if not self.state.pending_tool_calls:
|
if not self.state.pending_tool_calls:
|
||||||
return "native_tool_completed"
|
return "native_tool_completed"
|
||||||
|
|
||||||
|
pending_tool_calls = list(self.state.pending_tool_calls)
|
||||||
|
self.state.pending_tool_calls.clear()
|
||||||
|
|
||||||
# Group all tool calls into a single assistant message
|
# Group all tool calls into a single assistant message
|
||||||
tool_calls_to_report = []
|
tool_calls_to_report = []
|
||||||
for tool_call in self.state.pending_tool_calls:
|
for tool_call in pending_tool_calls:
|
||||||
info = extract_tool_call_info(tool_call)
|
info = extract_tool_call_info(tool_call)
|
||||||
if not info:
|
if not info:
|
||||||
continue
|
continue
|
||||||
@@ -695,201 +699,85 @@ class AgentExecutor(Flow[AgentReActState], CrewAgentExecutorMixin):
|
|||||||
"content": None,
|
"content": None,
|
||||||
"tool_calls": tool_calls_to_report,
|
"tool_calls": tool_calls_to_report,
|
||||||
}
|
}
|
||||||
if all(
|
if all(type(tc).__qualname__ == "Part" for tc in pending_tool_calls):
|
||||||
type(tc).__qualname__ == "Part" for tc in self.state.pending_tool_calls
|
assistant_message["raw_tool_call_parts"] = list(pending_tool_calls)
|
||||||
):
|
|
||||||
assistant_message["raw_tool_call_parts"] = list(
|
|
||||||
self.state.pending_tool_calls
|
|
||||||
)
|
|
||||||
self.state.messages.append(assistant_message)
|
self.state.messages.append(assistant_message)
|
||||||
|
|
||||||
# Now execute each tool
|
runnable_tool_calls = [
|
||||||
while self.state.pending_tool_calls:
|
tool_call
|
||||||
tool_call = self.state.pending_tool_calls.pop(0)
|
for tool_call in pending_tool_calls
|
||||||
info = extract_tool_call_info(tool_call)
|
if extract_tool_call_info(tool_call) is not None
|
||||||
if not info:
|
]
|
||||||
continue
|
should_parallelize = self._should_parallelize_native_tool_calls(
|
||||||
|
runnable_tool_calls
|
||||||
|
)
|
||||||
|
|
||||||
call_id, func_name, func_args = info
|
execution_results: list[dict[str, Any]] = []
|
||||||
|
if should_parallelize:
|
||||||
# Parse arguments
|
max_workers = min(8, len(runnable_tool_calls))
|
||||||
if isinstance(func_args, str):
|
with ThreadPoolExecutor(max_workers=max_workers) as pool:
|
||||||
try:
|
future_to_idx = {
|
||||||
args_dict = json.loads(func_args)
|
pool.submit(self._execute_single_native_tool_call, tool_call): idx
|
||||||
except json.JSONDecodeError:
|
for idx, tool_call in enumerate(runnable_tool_calls)
|
||||||
args_dict = {}
|
}
|
||||||
else:
|
ordered_results: list[dict[str, Any] | None] = [None] * len(
|
||||||
args_dict = func_args
|
runnable_tool_calls
|
||||||
|
|
||||||
# Get agent_key for event tracking
|
|
||||||
agent_key = (
|
|
||||||
getattr(self.agent, "key", "unknown") if self.agent else "unknown"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Find original tool by matching sanitized name (needed for cache_function and result_as_answer)
|
|
||||||
original_tool = None
|
|
||||||
for tool in self.original_tools or []:
|
|
||||||
if sanitize_tool_name(tool.name) == func_name:
|
|
||||||
original_tool = tool
|
|
||||||
break
|
|
||||||
|
|
||||||
# Check if tool has reached max usage count
|
|
||||||
max_usage_reached = False
|
|
||||||
if (
|
|
||||||
original_tool
|
|
||||||
and original_tool.max_usage_count is not None
|
|
||||||
and original_tool.current_usage_count >= original_tool.max_usage_count
|
|
||||||
):
|
|
||||||
max_usage_reached = True
|
|
||||||
|
|
||||||
# Check cache before executing
|
|
||||||
from_cache = False
|
|
||||||
input_str = json.dumps(args_dict) if args_dict else ""
|
|
||||||
if self.tools_handler and self.tools_handler.cache:
|
|
||||||
cached_result = self.tools_handler.cache.read(
|
|
||||||
tool=func_name, input=input_str
|
|
||||||
)
|
)
|
||||||
if cached_result is not None:
|
for future in as_completed(future_to_idx):
|
||||||
result = (
|
idx = future_to_idx[future]
|
||||||
str(cached_result)
|
ordered_results[idx] = future.result()
|
||||||
if not isinstance(cached_result, str)
|
execution_results = [
|
||||||
else cached_result
|
result for result in ordered_results if result is not None
|
||||||
)
|
]
|
||||||
from_cache = True
|
else:
|
||||||
|
# Execute sequentially so result_as_answer tools can short-circuit
|
||||||
|
# immediately without running remaining calls.
|
||||||
|
for tool_call in runnable_tool_calls:
|
||||||
|
execution_result = self._execute_single_native_tool_call(tool_call)
|
||||||
|
call_id = cast(str, execution_result["call_id"])
|
||||||
|
func_name = cast(str, execution_result["func_name"])
|
||||||
|
result = cast(str, execution_result["result"])
|
||||||
|
from_cache = cast(bool, execution_result["from_cache"])
|
||||||
|
original_tool = execution_result["original_tool"]
|
||||||
|
|
||||||
# Emit tool usage started event
|
tool_message: LLMMessage = {
|
||||||
started_at = datetime.now()
|
"role": "tool",
|
||||||
crewai_event_bus.emit(
|
"tool_call_id": call_id,
|
||||||
self,
|
"name": func_name,
|
||||||
event=ToolUsageStartedEvent(
|
"content": result,
|
||||||
tool_name=func_name,
|
}
|
||||||
tool_args=args_dict,
|
self.state.messages.append(tool_message)
|
||||||
from_agent=self.agent,
|
|
||||||
from_task=self.task,
|
|
||||||
agent_key=agent_key,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
error_event_emitted = False
|
|
||||||
|
|
||||||
track_delegation_if_needed(func_name, args_dict, self.task)
|
# Log the tool execution
|
||||||
|
if self.agent and self.agent.verbose:
|
||||||
structured_tool: CrewStructuredTool | None = None
|
cache_info = " (from cache)" if from_cache else ""
|
||||||
for structured in self.tools or []:
|
|
||||||
if sanitize_tool_name(structured.name) == func_name:
|
|
||||||
structured_tool = structured
|
|
||||||
break
|
|
||||||
|
|
||||||
hook_blocked = False
|
|
||||||
before_hook_context = ToolCallHookContext(
|
|
||||||
tool_name=func_name,
|
|
||||||
tool_input=args_dict,
|
|
||||||
tool=structured_tool, # type: ignore[arg-type]
|
|
||||||
agent=self.agent,
|
|
||||||
task=self.task,
|
|
||||||
crew=self.crew,
|
|
||||||
)
|
|
||||||
before_hooks = get_before_tool_call_hooks()
|
|
||||||
try:
|
|
||||||
for hook in before_hooks:
|
|
||||||
hook_result = hook(before_hook_context)
|
|
||||||
if hook_result is False:
|
|
||||||
hook_blocked = True
|
|
||||||
break
|
|
||||||
except Exception as hook_error:
|
|
||||||
if self.agent.verbose:
|
|
||||||
self._printer.print(
|
self._printer.print(
|
||||||
content=f"Error in before_tool_call hook: {hook_error}",
|
content=f"Tool {func_name} executed with result{cache_info}: {result[:200]}...",
|
||||||
color="red",
|
color="green",
|
||||||
)
|
)
|
||||||
|
|
||||||
if hook_blocked:
|
if (
|
||||||
result = f"Tool execution blocked by hook. Tool: {func_name}"
|
original_tool
|
||||||
elif not from_cache and not max_usage_reached:
|
and hasattr(original_tool, "result_as_answer")
|
||||||
result = "Tool not found"
|
and original_tool.result_as_answer
|
||||||
if func_name in self._available_functions:
|
):
|
||||||
try:
|
self.state.current_answer = AgentFinish(
|
||||||
tool_func = self._available_functions[func_name]
|
thought="Tool result is the final answer",
|
||||||
raw_result = tool_func(**args_dict)
|
|
||||||
|
|
||||||
# Add to cache after successful execution (before string conversion)
|
|
||||||
if self.tools_handler and self.tools_handler.cache:
|
|
||||||
should_cache = True
|
|
||||||
if original_tool:
|
|
||||||
should_cache = original_tool.cache_function(
|
|
||||||
args_dict, raw_result
|
|
||||||
)
|
|
||||||
if should_cache:
|
|
||||||
self.tools_handler.cache.add(
|
|
||||||
tool=func_name, input=input_str, output=raw_result
|
|
||||||
)
|
|
||||||
|
|
||||||
# Convert to string for message
|
|
||||||
result = (
|
|
||||||
str(raw_result)
|
|
||||||
if not isinstance(raw_result, str)
|
|
||||||
else raw_result
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
result = f"Error executing tool: {e}"
|
|
||||||
if self.task:
|
|
||||||
self.task.increment_tools_errors()
|
|
||||||
# Emit tool usage error event
|
|
||||||
crewai_event_bus.emit(
|
|
||||||
self,
|
|
||||||
event=ToolUsageErrorEvent(
|
|
||||||
tool_name=func_name,
|
|
||||||
tool_args=args_dict,
|
|
||||||
from_agent=self.agent,
|
|
||||||
from_task=self.task,
|
|
||||||
agent_key=agent_key,
|
|
||||||
error=e,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
error_event_emitted = True
|
|
||||||
elif max_usage_reached and original_tool:
|
|
||||||
# Return error message when max usage limit is reached
|
|
||||||
result = f"Tool '{func_name}' has reached its usage limit of {original_tool.max_usage_count} times and cannot be used anymore."
|
|
||||||
|
|
||||||
# Execute after_tool_call hooks (even if blocked, to allow logging/monitoring)
|
|
||||||
after_hook_context = ToolCallHookContext(
|
|
||||||
tool_name=func_name,
|
|
||||||
tool_input=args_dict,
|
|
||||||
tool=structured_tool, # type: ignore[arg-type]
|
|
||||||
agent=self.agent,
|
|
||||||
task=self.task,
|
|
||||||
crew=self.crew,
|
|
||||||
tool_result=result,
|
|
||||||
)
|
|
||||||
after_hooks = get_after_tool_call_hooks()
|
|
||||||
try:
|
|
||||||
for after_hook in after_hooks:
|
|
||||||
after_hook_result = after_hook(after_hook_context)
|
|
||||||
if after_hook_result is not None:
|
|
||||||
result = after_hook_result
|
|
||||||
after_hook_context.tool_result = result
|
|
||||||
except Exception as hook_error:
|
|
||||||
if self.agent.verbose:
|
|
||||||
self._printer.print(
|
|
||||||
content=f"Error in after_tool_call hook: {hook_error}",
|
|
||||||
color="red",
|
|
||||||
)
|
|
||||||
|
|
||||||
if not error_event_emitted:
|
|
||||||
crewai_event_bus.emit(
|
|
||||||
self,
|
|
||||||
event=ToolUsageFinishedEvent(
|
|
||||||
output=result,
|
output=result,
|
||||||
tool_name=func_name,
|
text=result,
|
||||||
tool_args=args_dict,
|
)
|
||||||
from_agent=self.agent,
|
self.state.is_finished = True
|
||||||
from_task=self.task,
|
return "tool_result_is_final"
|
||||||
agent_key=agent_key,
|
|
||||||
started_at=started_at,
|
return "native_tool_completed"
|
||||||
finished_at=datetime.now(),
|
|
||||||
),
|
for execution_result in execution_results:
|
||||||
)
|
call_id = cast(str, execution_result["call_id"])
|
||||||
|
func_name = cast(str, execution_result["func_name"])
|
||||||
|
result = cast(str, execution_result["result"])
|
||||||
|
from_cache = cast(bool, execution_result["from_cache"])
|
||||||
|
original_tool = execution_result["original_tool"]
|
||||||
|
|
||||||
# Append tool result message
|
|
||||||
tool_message: LLMMessage = {
|
tool_message: LLMMessage = {
|
||||||
"role": "tool",
|
"role": "tool",
|
||||||
"tool_call_id": call_id,
|
"tool_call_id": call_id,
|
||||||
@@ -922,6 +810,224 @@ class AgentExecutor(Flow[AgentReActState], CrewAgentExecutorMixin):
|
|||||||
|
|
||||||
return "native_tool_completed"
|
return "native_tool_completed"
|
||||||
|
|
||||||
|
def _should_parallelize_native_tool_calls(self, tool_calls: list[Any]) -> bool:
|
||||||
|
"""Determine if native tool calls are safe to run in parallel."""
|
||||||
|
if len(tool_calls) <= 1:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for tool_call in tool_calls:
|
||||||
|
info = extract_tool_call_info(tool_call)
|
||||||
|
if not info:
|
||||||
|
continue
|
||||||
|
_, func_name, _ = info
|
||||||
|
|
||||||
|
original_tool = None
|
||||||
|
for tool in self.original_tools or []:
|
||||||
|
if sanitize_tool_name(tool.name) == func_name:
|
||||||
|
original_tool = tool
|
||||||
|
break
|
||||||
|
|
||||||
|
if not original_tool:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if getattr(original_tool, "result_as_answer", False):
|
||||||
|
return False
|
||||||
|
if getattr(original_tool, "max_usage_count", None) is not None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _execute_single_native_tool_call(self, tool_call: Any) -> dict[str, Any]:
|
||||||
|
"""Execute a single native tool call and return metadata/result."""
|
||||||
|
info = extract_tool_call_info(tool_call)
|
||||||
|
if not info:
|
||||||
|
raise ValueError("Invalid native tool call format")
|
||||||
|
|
||||||
|
call_id, func_name, func_args = info
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
if isinstance(func_args, str):
|
||||||
|
try:
|
||||||
|
args_dict = json.loads(func_args)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
args_dict = {}
|
||||||
|
else:
|
||||||
|
args_dict = func_args
|
||||||
|
|
||||||
|
# Get agent_key for event tracking
|
||||||
|
agent_key = getattr(self.agent, "key", "unknown") if self.agent else "unknown"
|
||||||
|
|
||||||
|
# Find original tool by matching sanitized name (needed for cache_function and result_as_answer)
|
||||||
|
original_tool = None
|
||||||
|
for tool in self.original_tools or []:
|
||||||
|
if sanitize_tool_name(tool.name) == func_name:
|
||||||
|
original_tool = tool
|
||||||
|
break
|
||||||
|
|
||||||
|
# Check if tool has reached max usage count
|
||||||
|
max_usage_reached = False
|
||||||
|
if (
|
||||||
|
original_tool
|
||||||
|
and original_tool.max_usage_count is not None
|
||||||
|
and original_tool.current_usage_count >= original_tool.max_usage_count
|
||||||
|
):
|
||||||
|
max_usage_reached = True
|
||||||
|
|
||||||
|
# Check cache before executing
|
||||||
|
from_cache = False
|
||||||
|
input_str = json.dumps(args_dict) if args_dict else ""
|
||||||
|
if self.tools_handler and self.tools_handler.cache:
|
||||||
|
cached_result = self.tools_handler.cache.read(
|
||||||
|
tool=func_name, input=input_str
|
||||||
|
)
|
||||||
|
if cached_result is not None:
|
||||||
|
result = (
|
||||||
|
str(cached_result)
|
||||||
|
if not isinstance(cached_result, str)
|
||||||
|
else cached_result
|
||||||
|
)
|
||||||
|
from_cache = True
|
||||||
|
|
||||||
|
# Emit tool usage started event
|
||||||
|
started_at = datetime.now()
|
||||||
|
crewai_event_bus.emit(
|
||||||
|
self,
|
||||||
|
event=ToolUsageStartedEvent(
|
||||||
|
tool_name=func_name,
|
||||||
|
tool_args=args_dict,
|
||||||
|
from_agent=self.agent,
|
||||||
|
from_task=self.task,
|
||||||
|
agent_key=agent_key,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
error_event_emitted = False
|
||||||
|
|
||||||
|
track_delegation_if_needed(func_name, args_dict, self.task)
|
||||||
|
|
||||||
|
structured_tool: CrewStructuredTool | None = None
|
||||||
|
for structured in self.tools or []:
|
||||||
|
if sanitize_tool_name(structured.name) == func_name:
|
||||||
|
structured_tool = structured
|
||||||
|
break
|
||||||
|
|
||||||
|
hook_blocked = False
|
||||||
|
before_hook_context = ToolCallHookContext(
|
||||||
|
tool_name=func_name,
|
||||||
|
tool_input=args_dict,
|
||||||
|
tool=structured_tool, # type: ignore[arg-type]
|
||||||
|
agent=self.agent,
|
||||||
|
task=self.task,
|
||||||
|
crew=self.crew,
|
||||||
|
)
|
||||||
|
before_hooks = get_before_tool_call_hooks()
|
||||||
|
try:
|
||||||
|
for hook in before_hooks:
|
||||||
|
hook_result = hook(before_hook_context)
|
||||||
|
if hook_result is False:
|
||||||
|
hook_blocked = True
|
||||||
|
break
|
||||||
|
except Exception as hook_error:
|
||||||
|
if self.agent.verbose:
|
||||||
|
self._printer.print(
|
||||||
|
content=f"Error in before_tool_call hook: {hook_error}",
|
||||||
|
color="red",
|
||||||
|
)
|
||||||
|
|
||||||
|
if hook_blocked:
|
||||||
|
result = f"Tool execution blocked by hook. Tool: {func_name}"
|
||||||
|
elif not from_cache and not max_usage_reached:
|
||||||
|
result = "Tool not found"
|
||||||
|
if func_name in self._available_functions:
|
||||||
|
try:
|
||||||
|
tool_func = self._available_functions[func_name]
|
||||||
|
raw_result = tool_func(**args_dict)
|
||||||
|
|
||||||
|
# Add to cache after successful execution (before string conversion)
|
||||||
|
if self.tools_handler and self.tools_handler.cache:
|
||||||
|
should_cache = True
|
||||||
|
if original_tool:
|
||||||
|
should_cache = original_tool.cache_function(
|
||||||
|
args_dict, raw_result
|
||||||
|
)
|
||||||
|
if should_cache:
|
||||||
|
self.tools_handler.cache.add(
|
||||||
|
tool=func_name, input=input_str, output=raw_result
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert to string for message
|
||||||
|
result = (
|
||||||
|
str(raw_result)
|
||||||
|
if not isinstance(raw_result, str)
|
||||||
|
else raw_result
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
result = f"Error executing tool: {e}"
|
||||||
|
if self.task:
|
||||||
|
self.task.increment_tools_errors()
|
||||||
|
# Emit tool usage error event
|
||||||
|
crewai_event_bus.emit(
|
||||||
|
self,
|
||||||
|
event=ToolUsageErrorEvent(
|
||||||
|
tool_name=func_name,
|
||||||
|
tool_args=args_dict,
|
||||||
|
from_agent=self.agent,
|
||||||
|
from_task=self.task,
|
||||||
|
agent_key=agent_key,
|
||||||
|
error=e,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
error_event_emitted = True
|
||||||
|
elif max_usage_reached and original_tool:
|
||||||
|
# Return error message when max usage limit is reached
|
||||||
|
result = f"Tool '{func_name}' has reached its usage limit of {original_tool.max_usage_count} times and cannot be used anymore."
|
||||||
|
|
||||||
|
# Execute after_tool_call hooks (even if blocked, to allow logging/monitoring)
|
||||||
|
after_hook_context = ToolCallHookContext(
|
||||||
|
tool_name=func_name,
|
||||||
|
tool_input=args_dict,
|
||||||
|
tool=structured_tool, # type: ignore[arg-type]
|
||||||
|
agent=self.agent,
|
||||||
|
task=self.task,
|
||||||
|
crew=self.crew,
|
||||||
|
tool_result=result,
|
||||||
|
)
|
||||||
|
after_hooks = get_after_tool_call_hooks()
|
||||||
|
try:
|
||||||
|
for after_hook in after_hooks:
|
||||||
|
after_hook_result = after_hook(after_hook_context)
|
||||||
|
if after_hook_result is not None:
|
||||||
|
result = after_hook_result
|
||||||
|
after_hook_context.tool_result = result
|
||||||
|
except Exception as hook_error:
|
||||||
|
if self.agent.verbose:
|
||||||
|
self._printer.print(
|
||||||
|
content=f"Error in after_tool_call hook: {hook_error}",
|
||||||
|
color="red",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not error_event_emitted:
|
||||||
|
crewai_event_bus.emit(
|
||||||
|
self,
|
||||||
|
event=ToolUsageFinishedEvent(
|
||||||
|
output=result,
|
||||||
|
tool_name=func_name,
|
||||||
|
tool_args=args_dict,
|
||||||
|
from_agent=self.agent,
|
||||||
|
from_task=self.task,
|
||||||
|
agent_key=agent_key,
|
||||||
|
started_at=started_at,
|
||||||
|
finished_at=datetime.now(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"call_id": call_id,
|
||||||
|
"func_name": func_name,
|
||||||
|
"result": result,
|
||||||
|
"from_cache": from_cache,
|
||||||
|
"original_tool": original_tool,
|
||||||
|
}
|
||||||
|
|
||||||
def _extract_tool_name(self, tool_call: Any) -> str:
|
def _extract_tool_name(self, tool_call: Any) -> str:
|
||||||
"""Extract tool name from various tool call formats."""
|
"""Extract tool name from various tool call formats."""
|
||||||
if hasattr(tool_call, "function"):
|
if hasattr(tool_call, "function"):
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import asyncio
|
|||||||
from collections.abc import (
|
from collections.abc import (
|
||||||
Callable,
|
Callable,
|
||||||
ItemsView,
|
ItemsView,
|
||||||
|
Iterable,
|
||||||
Iterator,
|
Iterator,
|
||||||
KeysView,
|
KeysView,
|
||||||
Sequence,
|
Sequence,
|
||||||
@@ -17,6 +18,7 @@ from collections.abc import (
|
|||||||
)
|
)
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
import copy
|
import copy
|
||||||
|
import enum
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
@@ -27,8 +29,10 @@ from typing import (
|
|||||||
Generic,
|
Generic,
|
||||||
Literal,
|
Literal,
|
||||||
ParamSpec,
|
ParamSpec,
|
||||||
|
SupportsIndex,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
cast,
|
cast,
|
||||||
|
overload,
|
||||||
)
|
)
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@@ -77,7 +81,12 @@ from crewai.flow.flow_wrappers import (
|
|||||||
StartMethod,
|
StartMethod,
|
||||||
)
|
)
|
||||||
from crewai.flow.persistence.base import FlowPersistence
|
from crewai.flow.persistence.base import FlowPersistence
|
||||||
from crewai.flow.types import FlowExecutionData, FlowMethodName, InputHistoryEntry, PendingListenerKey
|
from crewai.flow.types import (
|
||||||
|
FlowExecutionData,
|
||||||
|
FlowMethodName,
|
||||||
|
InputHistoryEntry,
|
||||||
|
PendingListenerKey,
|
||||||
|
)
|
||||||
from crewai.flow.utils import (
|
from crewai.flow.utils import (
|
||||||
_extract_all_methods,
|
_extract_all_methods,
|
||||||
_extract_all_methods_recursive,
|
_extract_all_methods_recursive,
|
||||||
@@ -426,8 +435,7 @@ class LockedListProxy(list, Generic[T]): # type: ignore[type-arg]
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, lst: list[T], lock: threading.Lock) -> None:
|
def __init__(self, lst: list[T], lock: threading.Lock) -> None:
|
||||||
# Do NOT call super().__init__() -- we don't want to copy data into
|
super().__init__() # empty builtin list; all access goes through self._list
|
||||||
# the builtin list storage. All access goes through self._list.
|
|
||||||
self._list = lst
|
self._list = lst
|
||||||
self._lock = lock
|
self._lock = lock
|
||||||
|
|
||||||
@@ -435,11 +443,11 @@ class LockedListProxy(list, Generic[T]): # type: ignore[type-arg]
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
self._list.append(item)
|
self._list.append(item)
|
||||||
|
|
||||||
def extend(self, items: list[T]) -> None:
|
def extend(self, items: Iterable[T]) -> None:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._list.extend(items)
|
self._list.extend(items)
|
||||||
|
|
||||||
def insert(self, index: int, item: T) -> None:
|
def insert(self, index: SupportsIndex, item: T) -> None:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._list.insert(index, item)
|
self._list.insert(index, item)
|
||||||
|
|
||||||
@@ -447,7 +455,7 @@ class LockedListProxy(list, Generic[T]): # type: ignore[type-arg]
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
self._list.remove(item)
|
self._list.remove(item)
|
||||||
|
|
||||||
def pop(self, index: int = -1) -> T:
|
def pop(self, index: SupportsIndex = -1) -> T:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
return self._list.pop(index)
|
return self._list.pop(index)
|
||||||
|
|
||||||
@@ -455,15 +463,23 @@ class LockedListProxy(list, Generic[T]): # type: ignore[type-arg]
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
self._list.clear()
|
self._list.clear()
|
||||||
|
|
||||||
def __setitem__(self, index: int, value: T) -> None:
|
@overload
|
||||||
|
def __setitem__(self, index: SupportsIndex, value: T) -> None: ...
|
||||||
|
@overload
|
||||||
|
def __setitem__(self, index: slice, value: Iterable[T]) -> None: ...
|
||||||
|
def __setitem__(self, index: Any, value: Any) -> None:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._list[index] = value
|
self._list[index] = value
|
||||||
|
|
||||||
def __delitem__(self, index: int) -> None:
|
def __delitem__(self, index: SupportsIndex | slice) -> None:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
del self._list[index]
|
del self._list[index]
|
||||||
|
|
||||||
def __getitem__(self, index: int) -> T:
|
@overload
|
||||||
|
def __getitem__(self, index: SupportsIndex) -> T: ...
|
||||||
|
@overload
|
||||||
|
def __getitem__(self, index: slice) -> list[T]: ...
|
||||||
|
def __getitem__(self, index: Any) -> Any:
|
||||||
return self._list[index]
|
return self._list[index]
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
@@ -481,7 +497,7 @@ class LockedListProxy(list, Generic[T]): # type: ignore[type-arg]
|
|||||||
def __bool__(self) -> bool:
|
def __bool__(self) -> bool:
|
||||||
return bool(self._list)
|
return bool(self._list)
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool: # type: ignore[override]
|
def __eq__(self, other: object) -> bool:
|
||||||
"""Compare based on the underlying list contents."""
|
"""Compare based on the underlying list contents."""
|
||||||
if isinstance(other, LockedListProxy):
|
if isinstance(other, LockedListProxy):
|
||||||
# Avoid deadlocks by acquiring locks in a consistent order.
|
# Avoid deadlocks by acquiring locks in a consistent order.
|
||||||
@@ -492,7 +508,7 @@ class LockedListProxy(list, Generic[T]): # type: ignore[type-arg]
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
return self._list == other
|
return self._list == other
|
||||||
|
|
||||||
def __ne__(self, other: object) -> bool: # type: ignore[override]
|
def __ne__(self, other: object) -> bool:
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
|
||||||
@@ -505,8 +521,7 @@ class LockedDictProxy(dict, Generic[T]): # type: ignore[type-arg]
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, d: dict[str, T], lock: threading.Lock) -> None:
|
def __init__(self, d: dict[str, T], lock: threading.Lock) -> None:
|
||||||
# Do NOT call super().__init__() -- we don't want to copy data into
|
super().__init__() # empty builtin dict; all access goes through self._dict
|
||||||
# the builtin dict storage. All access goes through self._dict.
|
|
||||||
self._dict = d
|
self._dict = d
|
||||||
self._lock = lock
|
self._lock = lock
|
||||||
|
|
||||||
@@ -518,11 +533,11 @@ class LockedDictProxy(dict, Generic[T]): # type: ignore[type-arg]
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
del self._dict[key]
|
del self._dict[key]
|
||||||
|
|
||||||
def pop(self, key: str, *default: T) -> T:
|
def pop(self, key: str, *default: T) -> T: # type: ignore[override]
|
||||||
with self._lock:
|
with self._lock:
|
||||||
return self._dict.pop(key, *default)
|
return self._dict.pop(key, *default)
|
||||||
|
|
||||||
def update(self, other: dict[str, T]) -> None:
|
def update(self, other: dict[str, T]) -> None: # type: ignore[override]
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._dict.update(other)
|
self._dict.update(other)
|
||||||
|
|
||||||
@@ -530,7 +545,7 @@ class LockedDictProxy(dict, Generic[T]): # type: ignore[type-arg]
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
self._dict.clear()
|
self._dict.clear()
|
||||||
|
|
||||||
def setdefault(self, key: str, default: T) -> T:
|
def setdefault(self, key: str, default: T) -> T: # type: ignore[override]
|
||||||
with self._lock:
|
with self._lock:
|
||||||
return self._dict.setdefault(key, default)
|
return self._dict.setdefault(key, default)
|
||||||
|
|
||||||
@@ -546,16 +561,16 @@ class LockedDictProxy(dict, Generic[T]): # type: ignore[type-arg]
|
|||||||
def __contains__(self, key: object) -> bool:
|
def __contains__(self, key: object) -> bool:
|
||||||
return key in self._dict
|
return key in self._dict
|
||||||
|
|
||||||
def keys(self) -> KeysView[str]:
|
def keys(self) -> KeysView[str]: # type: ignore[override]
|
||||||
return self._dict.keys()
|
return self._dict.keys()
|
||||||
|
|
||||||
def values(self) -> ValuesView[T]:
|
def values(self) -> ValuesView[T]: # type: ignore[override]
|
||||||
return self._dict.values()
|
return self._dict.values()
|
||||||
|
|
||||||
def items(self) -> ItemsView[str, T]:
|
def items(self) -> ItemsView[str, T]: # type: ignore[override]
|
||||||
return self._dict.items()
|
return self._dict.items()
|
||||||
|
|
||||||
def get(self, key: str, default: T | None = None) -> T | None:
|
def get(self, key: str, default: T | None = None) -> T | None: # type: ignore[override]
|
||||||
return self._dict.get(key, default)
|
return self._dict.get(key, default)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
@@ -564,7 +579,7 @@ class LockedDictProxy(dict, Generic[T]): # type: ignore[type-arg]
|
|||||||
def __bool__(self) -> bool:
|
def __bool__(self) -> bool:
|
||||||
return bool(self._dict)
|
return bool(self._dict)
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool: # type: ignore[override]
|
def __eq__(self, other: object) -> bool:
|
||||||
"""Compare based on the underlying dict contents."""
|
"""Compare based on the underlying dict contents."""
|
||||||
if isinstance(other, LockedDictProxy):
|
if isinstance(other, LockedDictProxy):
|
||||||
# Avoid deadlocks by acquiring locks in a consistent order.
|
# Avoid deadlocks by acquiring locks in a consistent order.
|
||||||
@@ -575,7 +590,7 @@ class LockedDictProxy(dict, Generic[T]): # type: ignore[type-arg]
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
return self._dict == other
|
return self._dict == other
|
||||||
|
|
||||||
def __ne__(self, other: object) -> bool: # type: ignore[override]
|
def __ne__(self, other: object) -> bool:
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
|
||||||
@@ -737,7 +752,9 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
name: str | None = None
|
name: str | None = None
|
||||||
tracing: bool | None = None
|
tracing: bool | None = None
|
||||||
stream: bool = False
|
stream: bool = False
|
||||||
memory: Any = None # Memory | MemoryScope | MemorySlice | None; auto-created if not set
|
memory: Any = (
|
||||||
|
None # Memory | MemoryScope | MemorySlice | None; auto-created if not set
|
||||||
|
)
|
||||||
input_provider: Any = None # InputProvider | None; per-flow override for self.ask()
|
input_provider: Any = None # InputProvider | None; per-flow override for self.ask()
|
||||||
|
|
||||||
def __class_getitem__(cls: type[Flow[T]], item: type[T]) -> type[Flow[T]]:
|
def __class_getitem__(cls: type[Flow[T]], item: type[T]) -> type[Flow[T]]:
|
||||||
@@ -881,7 +898,8 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
"""
|
"""
|
||||||
if self.memory is None:
|
if self.memory is None:
|
||||||
raise ValueError("No memory configured for this flow")
|
raise ValueError("No memory configured for this flow")
|
||||||
return self.memory.extract_memories(content)
|
result: list[str] = self.memory.extract_memories(content)
|
||||||
|
return result
|
||||||
|
|
||||||
def _mark_or_listener_fired(self, listener_name: FlowMethodName) -> bool:
|
def _mark_or_listener_fired(self, listener_name: FlowMethodName) -> bool:
|
||||||
"""Mark an OR listener as fired atomically.
|
"""Mark an OR listener as fired atomically.
|
||||||
@@ -1352,8 +1370,10 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
ValueError: If structured state model lacks 'id' field
|
ValueError: If structured state model lacks 'id' field
|
||||||
TypeError: If state is neither BaseModel nor dictionary
|
TypeError: If state is neither BaseModel nor dictionary
|
||||||
"""
|
"""
|
||||||
|
init_state = self.initial_state
|
||||||
|
|
||||||
# Handle case where initial_state is None but we have a type parameter
|
# Handle case where initial_state is None but we have a type parameter
|
||||||
if self.initial_state is None and hasattr(self, "_initial_state_t"):
|
if init_state is None and hasattr(self, "_initial_state_t"):
|
||||||
state_type = self._initial_state_t
|
state_type = self._initial_state_t
|
||||||
if isinstance(state_type, type):
|
if isinstance(state_type, type):
|
||||||
if issubclass(state_type, FlowState):
|
if issubclass(state_type, FlowState):
|
||||||
@@ -1377,12 +1397,12 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
return cast(T, {"id": str(uuid4())})
|
return cast(T, {"id": str(uuid4())})
|
||||||
|
|
||||||
# Handle case where no initial state is provided
|
# Handle case where no initial state is provided
|
||||||
if self.initial_state is None:
|
if init_state is None:
|
||||||
return cast(T, {"id": str(uuid4())})
|
return cast(T, {"id": str(uuid4())})
|
||||||
|
|
||||||
# Handle case where initial_state is a type (class)
|
# Handle case where initial_state is a type (class)
|
||||||
if isinstance(self.initial_state, type):
|
if isinstance(init_state, type):
|
||||||
state_class: type[T] = self.initial_state
|
state_class = init_state
|
||||||
if issubclass(state_class, FlowState):
|
if issubclass(state_class, FlowState):
|
||||||
return state_class()
|
return state_class()
|
||||||
if issubclass(state_class, BaseModel):
|
if issubclass(state_class, BaseModel):
|
||||||
@@ -1393,19 +1413,19 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
if not getattr(model_instance, "id", None):
|
if not getattr(model_instance, "id", None):
|
||||||
object.__setattr__(model_instance, "id", str(uuid4()))
|
object.__setattr__(model_instance, "id", str(uuid4()))
|
||||||
return model_instance
|
return model_instance
|
||||||
if self.initial_state is dict:
|
if init_state is dict:
|
||||||
return cast(T, {"id": str(uuid4())})
|
return cast(T, {"id": str(uuid4())})
|
||||||
|
|
||||||
# Handle dictionary instance case
|
# Handle dictionary instance case
|
||||||
if isinstance(self.initial_state, dict):
|
if isinstance(init_state, dict):
|
||||||
new_state = dict(self.initial_state) # Copy to avoid mutations
|
new_state = dict(init_state) # Copy to avoid mutations
|
||||||
if "id" not in new_state:
|
if "id" not in new_state:
|
||||||
new_state["id"] = str(uuid4())
|
new_state["id"] = str(uuid4())
|
||||||
return cast(T, new_state)
|
return cast(T, new_state)
|
||||||
|
|
||||||
# Handle BaseModel instance case
|
# Handle BaseModel instance case
|
||||||
if isinstance(self.initial_state, BaseModel):
|
if isinstance(init_state, BaseModel):
|
||||||
model = cast(BaseModel, self.initial_state)
|
model = cast(BaseModel, init_state)
|
||||||
if not hasattr(model, "id"):
|
if not hasattr(model, "id"):
|
||||||
raise ValueError("Flow state model must have an 'id' field")
|
raise ValueError("Flow state model must have an 'id' field")
|
||||||
|
|
||||||
@@ -2178,6 +2198,8 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
from crewai.flow.async_feedback.types import HumanFeedbackPending
|
from crewai.flow.async_feedback.types import HumanFeedbackPending
|
||||||
|
|
||||||
if isinstance(e, HumanFeedbackPending):
|
if isinstance(e, HumanFeedbackPending):
|
||||||
|
e.context.method_name = method_name
|
||||||
|
|
||||||
# Auto-save pending feedback (create default persistence if needed)
|
# Auto-save pending feedback (create default persistence if needed)
|
||||||
if self._persistence is None:
|
if self._persistence is None:
|
||||||
from crewai.flow.persistence import SQLiteFlowPersistence
|
from crewai.flow.persistence import SQLiteFlowPersistence
|
||||||
@@ -2277,14 +2299,23 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
router_name, router_input, current_triggering_event_id
|
router_name, router_input, current_triggering_event_id
|
||||||
)
|
)
|
||||||
if router_result: # Only add non-None results
|
if router_result: # Only add non-None results
|
||||||
router_results.append(FlowMethodName(str(router_result)))
|
router_result_str = (
|
||||||
|
router_result.value
|
||||||
|
if isinstance(router_result, enum.Enum)
|
||||||
|
else str(router_result)
|
||||||
|
)
|
||||||
|
router_results.append(FlowMethodName(router_result_str))
|
||||||
# If this was a human_feedback router, map the outcome to the feedback
|
# If this was a human_feedback router, map the outcome to the feedback
|
||||||
if self.last_human_feedback is not None:
|
if self.last_human_feedback is not None:
|
||||||
router_result_to_feedback[str(router_result)] = (
|
router_result_to_feedback[router_result_str] = (
|
||||||
self.last_human_feedback
|
self.last_human_feedback
|
||||||
)
|
)
|
||||||
current_trigger = (
|
current_trigger = (
|
||||||
FlowMethodName(str(router_result))
|
FlowMethodName(
|
||||||
|
router_result.value
|
||||||
|
if isinstance(router_result, enum.Enum)
|
||||||
|
else str(router_result)
|
||||||
|
)
|
||||||
if router_result is not None
|
if router_result is not None
|
||||||
else FlowMethodName("") # Update for next iteration of router chain
|
else FlowMethodName("") # Update for next iteration of router chain
|
||||||
)
|
)
|
||||||
@@ -2701,7 +2732,10 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
return topic
|
return topic
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError
|
from concurrent.futures import (
|
||||||
|
ThreadPoolExecutor,
|
||||||
|
TimeoutError as FuturesTimeoutError,
|
||||||
|
)
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from crewai.events.types.flow_events import (
|
from crewai.events.types.flow_events import (
|
||||||
@@ -2770,14 +2804,16 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
response = None
|
response = None
|
||||||
|
|
||||||
# Record in history
|
# Record in history
|
||||||
self._input_history.append({
|
self._input_history.append(
|
||||||
"message": message,
|
{
|
||||||
"response": response,
|
"message": message,
|
||||||
"method_name": method_name,
|
"response": response,
|
||||||
"timestamp": datetime.now(),
|
"method_name": method_name,
|
||||||
"metadata": metadata,
|
"timestamp": datetime.now(),
|
||||||
"response_metadata": response_metadata,
|
"metadata": metadata,
|
||||||
})
|
"response_metadata": response_metadata,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Emit input received event
|
# Emit input received event
|
||||||
crewai_event_bus.emit(
|
crewai_event_bus.emit(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Tests the Flow-based agent executor implementation including state management,
|
|||||||
flow methods, routing logic, and error handling.
|
flow methods, routing logic, and error handling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -462,3 +463,176 @@ class TestFlowInvoke:
|
|||||||
|
|
||||||
assert result == {"output": "Done"}
|
assert result == {"output": "Done"}
|
||||||
assert len(executor.state.messages) >= 2
|
assert len(executor.state.messages) >= 2
|
||||||
|
|
||||||
|
|
||||||
|
class TestNativeToolExecution:
|
||||||
|
"""Test native tool execution behavior."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_dependencies(self):
|
||||||
|
llm = Mock()
|
||||||
|
llm.supports_stop_words.return_value = True
|
||||||
|
|
||||||
|
task = Mock()
|
||||||
|
task.name = "Test Task"
|
||||||
|
task.description = "Test"
|
||||||
|
task.human_input = False
|
||||||
|
task.response_model = None
|
||||||
|
|
||||||
|
crew = Mock()
|
||||||
|
crew._memory = None
|
||||||
|
crew.verbose = False
|
||||||
|
crew._train = False
|
||||||
|
|
||||||
|
agent = Mock()
|
||||||
|
agent.id = "test-agent-id"
|
||||||
|
agent.role = "Test Agent"
|
||||||
|
agent.verbose = False
|
||||||
|
agent.key = "test-key"
|
||||||
|
|
||||||
|
prompt = {"prompt": "Test {input} {tool_names} {tools}"}
|
||||||
|
|
||||||
|
tools_handler = Mock()
|
||||||
|
tools_handler.cache = None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"llm": llm,
|
||||||
|
"task": task,
|
||||||
|
"crew": crew,
|
||||||
|
"agent": agent,
|
||||||
|
"prompt": prompt,
|
||||||
|
"max_iter": 10,
|
||||||
|
"tools": [],
|
||||||
|
"tools_names": "",
|
||||||
|
"stop_words": [],
|
||||||
|
"tools_description": "",
|
||||||
|
"tools_handler": tools_handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_execute_native_tool_runs_parallel_for_multiple_calls(
|
||||||
|
self, mock_dependencies
|
||||||
|
):
|
||||||
|
executor = AgentExecutor(**mock_dependencies)
|
||||||
|
|
||||||
|
def slow_one() -> str:
|
||||||
|
time.sleep(0.2)
|
||||||
|
return "one"
|
||||||
|
|
||||||
|
def slow_two() -> str:
|
||||||
|
time.sleep(0.2)
|
||||||
|
return "two"
|
||||||
|
|
||||||
|
executor._available_functions = {"slow_one": slow_one, "slow_two": slow_two}
|
||||||
|
executor.state.pending_tool_calls = [
|
||||||
|
{
|
||||||
|
"id": "call_1",
|
||||||
|
"function": {"name": "slow_one", "arguments": "{}"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "call_2",
|
||||||
|
"function": {"name": "slow_two", "arguments": "{}"},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
started = time.perf_counter()
|
||||||
|
result = executor.execute_native_tool()
|
||||||
|
elapsed = time.perf_counter() - started
|
||||||
|
|
||||||
|
assert result == "native_tool_completed"
|
||||||
|
assert elapsed < 0.5
|
||||||
|
tool_messages = [m for m in executor.state.messages if m.get("role") == "tool"]
|
||||||
|
assert len(tool_messages) == 2
|
||||||
|
assert tool_messages[0]["tool_call_id"] == "call_1"
|
||||||
|
assert tool_messages[1]["tool_call_id"] == "call_2"
|
||||||
|
|
||||||
|
def test_execute_native_tool_falls_back_to_sequential_for_result_as_answer(
|
||||||
|
self, mock_dependencies
|
||||||
|
):
|
||||||
|
executor = AgentExecutor(**mock_dependencies)
|
||||||
|
|
||||||
|
def slow_one() -> str:
|
||||||
|
time.sleep(0.2)
|
||||||
|
return "one"
|
||||||
|
|
||||||
|
def slow_two() -> str:
|
||||||
|
time.sleep(0.2)
|
||||||
|
return "two"
|
||||||
|
|
||||||
|
result_tool = Mock()
|
||||||
|
result_tool.name = "slow_one"
|
||||||
|
result_tool.result_as_answer = True
|
||||||
|
result_tool.max_usage_count = None
|
||||||
|
result_tool.current_usage_count = 0
|
||||||
|
|
||||||
|
executor.original_tools = [result_tool]
|
||||||
|
executor._available_functions = {"slow_one": slow_one, "slow_two": slow_two}
|
||||||
|
executor.state.pending_tool_calls = [
|
||||||
|
{
|
||||||
|
"id": "call_1",
|
||||||
|
"function": {"name": "slow_one", "arguments": "{}"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "call_2",
|
||||||
|
"function": {"name": "slow_two", "arguments": "{}"},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
started = time.perf_counter()
|
||||||
|
result = executor.execute_native_tool()
|
||||||
|
elapsed = time.perf_counter() - started
|
||||||
|
|
||||||
|
assert result == "tool_result_is_final"
|
||||||
|
assert elapsed >= 0.2
|
||||||
|
assert elapsed < 0.8
|
||||||
|
assert isinstance(executor.state.current_answer, AgentFinish)
|
||||||
|
assert executor.state.current_answer.output == "one"
|
||||||
|
|
||||||
|
def test_execute_native_tool_result_as_answer_short_circuits_remaining_calls(
|
||||||
|
self, mock_dependencies
|
||||||
|
):
|
||||||
|
executor = AgentExecutor(**mock_dependencies)
|
||||||
|
call_counts = {"slow_one": 0, "slow_two": 0}
|
||||||
|
|
||||||
|
def slow_one() -> str:
|
||||||
|
call_counts["slow_one"] += 1
|
||||||
|
time.sleep(0.2)
|
||||||
|
return "one"
|
||||||
|
|
||||||
|
def slow_two() -> str:
|
||||||
|
call_counts["slow_two"] += 1
|
||||||
|
time.sleep(0.2)
|
||||||
|
return "two"
|
||||||
|
|
||||||
|
result_tool = Mock()
|
||||||
|
result_tool.name = "slow_one"
|
||||||
|
result_tool.result_as_answer = True
|
||||||
|
result_tool.max_usage_count = None
|
||||||
|
result_tool.current_usage_count = 0
|
||||||
|
|
||||||
|
executor.original_tools = [result_tool]
|
||||||
|
executor._available_functions = {"slow_one": slow_one, "slow_two": slow_two}
|
||||||
|
executor.state.pending_tool_calls = [
|
||||||
|
{
|
||||||
|
"id": "call_1",
|
||||||
|
"function": {"name": "slow_one", "arguments": "{}"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "call_2",
|
||||||
|
"function": {"name": "slow_two", "arguments": "{}"},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
started = time.perf_counter()
|
||||||
|
result = executor.execute_native_tool()
|
||||||
|
elapsed = time.perf_counter() - started
|
||||||
|
|
||||||
|
assert result == "tool_result_is_final"
|
||||||
|
assert isinstance(executor.state.current_answer, AgentFinish)
|
||||||
|
assert executor.state.current_answer.output == "one"
|
||||||
|
assert call_counts["slow_one"] == 1
|
||||||
|
assert call_counts["slow_two"] == 0
|
||||||
|
assert elapsed < 0.5
|
||||||
|
|
||||||
|
tool_messages = [m for m in executor.state.messages if m.get("role") == "tool"]
|
||||||
|
assert len(tool_messages) == 1
|
||||||
|
assert tool_messages[0]["tool_call_id"] == "call_1"
|
||||||
|
|||||||
@@ -6,13 +6,20 @@ when the LLM supports it, across multiple providers.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
import os
|
import os
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from collections import Counter
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from crewai import Agent, Crew, Task
|
from crewai import Agent, Crew, Task
|
||||||
|
from crewai.events import crewai_event_bus
|
||||||
|
from crewai.hooks import register_after_tool_call_hook, register_before_tool_call_hook
|
||||||
|
from crewai.hooks.tool_hooks import ToolCallHookContext
|
||||||
from crewai.llm import LLM
|
from crewai.llm import LLM
|
||||||
from crewai.tools.base_tool import BaseTool
|
from crewai.tools.base_tool import BaseTool
|
||||||
|
|
||||||
@@ -64,6 +71,73 @@ class FailingTool(BaseTool):
|
|||||||
def _run(self) -> str:
|
def _run(self) -> str:
|
||||||
raise Exception("This tool always fails")
|
raise Exception("This tool always fails")
|
||||||
|
|
||||||
|
|
||||||
|
class LocalSearchInput(BaseModel):
|
||||||
|
query: str = Field(description="Search query")
|
||||||
|
|
||||||
|
|
||||||
|
class ParallelProbe:
|
||||||
|
"""Thread-safe in-memory recorder for tool execution windows."""
|
||||||
|
|
||||||
|
_lock = threading.Lock()
|
||||||
|
_windows: list[tuple[str, float, float]] = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reset(cls) -> None:
|
||||||
|
with cls._lock:
|
||||||
|
cls._windows = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def record(cls, tool_name: str, start: float, end: float) -> None:
|
||||||
|
with cls._lock:
|
||||||
|
cls._windows.append((tool_name, start, end))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def windows(cls) -> list[tuple[str, float, float]]:
|
||||||
|
with cls._lock:
|
||||||
|
return list(cls._windows)
|
||||||
|
|
||||||
|
|
||||||
|
def _parallel_prompt() -> str:
|
||||||
|
return (
|
||||||
|
"This is a tool-calling compliance test. "
|
||||||
|
"In your next assistant turn, emit exactly 3 tool calls in the same response (parallel tool calls), in this order: "
|
||||||
|
"1) parallel_local_search_one(query='latest OpenAI model release notes'), "
|
||||||
|
"2) parallel_local_search_two(query='latest Anthropic model release notes'), "
|
||||||
|
"3) parallel_local_search_three(query='latest Gemini model release notes'). "
|
||||||
|
"Do not call any other tools and do not answer before those 3 tool calls are emitted. "
|
||||||
|
"After the tool results return, provide a one paragraph summary."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _max_concurrency(windows: list[tuple[str, float, float]]) -> int:
|
||||||
|
points: list[tuple[float, int]] = []
|
||||||
|
for _, start, end in windows:
|
||||||
|
points.append((start, 1))
|
||||||
|
points.append((end, -1))
|
||||||
|
points.sort(key=lambda p: (p[0], p[1]))
|
||||||
|
|
||||||
|
current = 0
|
||||||
|
maximum = 0
|
||||||
|
for _, delta in points:
|
||||||
|
current += delta
|
||||||
|
if current > maximum:
|
||||||
|
maximum = current
|
||||||
|
return maximum
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_tools_overlapped() -> None:
|
||||||
|
windows = ParallelProbe.windows()
|
||||||
|
local_windows = [
|
||||||
|
w
|
||||||
|
for w in windows
|
||||||
|
if w[0].startswith("parallel_local_search_")
|
||||||
|
]
|
||||||
|
|
||||||
|
assert len(local_windows) >= 3, f"Expected at least 3 local tool calls, got {len(local_windows)}"
|
||||||
|
assert _max_concurrency(local_windows) >= 2, "Expected overlapping local tool executions"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def calculator_tool() -> CalculatorTool:
|
def calculator_tool() -> CalculatorTool:
|
||||||
"""Create a calculator tool for testing."""
|
"""Create a calculator tool for testing."""
|
||||||
@@ -82,6 +156,65 @@ def failing_tool() -> BaseTool:
|
|||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def parallel_tools() -> list[BaseTool]:
|
||||||
|
"""Create local tools used to verify native parallel execution deterministically."""
|
||||||
|
|
||||||
|
class ParallelLocalSearchOne(BaseTool):
|
||||||
|
name: str = "parallel_local_search_one"
|
||||||
|
description: str = "Local search tool #1 for concurrency testing."
|
||||||
|
args_schema: type[BaseModel] = LocalSearchInput
|
||||||
|
|
||||||
|
def _run(self, query: str) -> str:
|
||||||
|
start = time.perf_counter()
|
||||||
|
time.sleep(1.0)
|
||||||
|
end = time.perf_counter()
|
||||||
|
ParallelProbe.record(self.name, start, end)
|
||||||
|
return f"[one] {query}"
|
||||||
|
|
||||||
|
class ParallelLocalSearchTwo(BaseTool):
|
||||||
|
name: str = "parallel_local_search_two"
|
||||||
|
description: str = "Local search tool #2 for concurrency testing."
|
||||||
|
args_schema: type[BaseModel] = LocalSearchInput
|
||||||
|
|
||||||
|
def _run(self, query: str) -> str:
|
||||||
|
start = time.perf_counter()
|
||||||
|
time.sleep(1.0)
|
||||||
|
end = time.perf_counter()
|
||||||
|
ParallelProbe.record(self.name, start, end)
|
||||||
|
return f"[two] {query}"
|
||||||
|
|
||||||
|
class ParallelLocalSearchThree(BaseTool):
|
||||||
|
name: str = "parallel_local_search_three"
|
||||||
|
description: str = "Local search tool #3 for concurrency testing."
|
||||||
|
args_schema: type[BaseModel] = LocalSearchInput
|
||||||
|
|
||||||
|
def _run(self, query: str) -> str:
|
||||||
|
start = time.perf_counter()
|
||||||
|
time.sleep(1.0)
|
||||||
|
end = time.perf_counter()
|
||||||
|
ParallelProbe.record(self.name, start, end)
|
||||||
|
return f"[three] {query}"
|
||||||
|
|
||||||
|
return [
|
||||||
|
ParallelLocalSearchOne(),
|
||||||
|
ParallelLocalSearchTwo(),
|
||||||
|
ParallelLocalSearchThree(),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _attach_parallel_probe_handler() -> None:
|
||||||
|
@crewai_event_bus.on(ToolUsageFinishedEvent)
|
||||||
|
def _capture_tool_window(_source, event: ToolUsageFinishedEvent):
|
||||||
|
if not event.tool_name.startswith("parallel_local_search_"):
|
||||||
|
return
|
||||||
|
ParallelProbe.record(
|
||||||
|
event.tool_name,
|
||||||
|
event.started_at.timestamp(),
|
||||||
|
event.finished_at.timestamp(),
|
||||||
|
)
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# OpenAI Provider Tests
|
# OpenAI Provider Tests
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -122,7 +255,7 @@ class TestOpenAINativeToolCalling:
|
|||||||
self, calculator_tool: CalculatorTool
|
self, calculator_tool: CalculatorTool
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test OpenAI agent kickoff with mocked LLM call."""
|
"""Test OpenAI agent kickoff with mocked LLM call."""
|
||||||
llm = LLM(model="gpt-4o-mini")
|
llm = LLM(model="gpt-5-nano")
|
||||||
|
|
||||||
with patch.object(llm, "call", return_value="The answer is 120.") as mock_call:
|
with patch.object(llm, "call", return_value="The answer is 120.") as mock_call:
|
||||||
agent = Agent(
|
agent = Agent(
|
||||||
@@ -146,6 +279,174 @@ class TestOpenAINativeToolCalling:
|
|||||||
assert mock_call.called
|
assert mock_call.called
|
||||||
assert result is not None
|
assert result is not None
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
@pytest.mark.timeout(180)
|
||||||
|
def test_openai_parallel_native_tool_calling_test_crew(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="gpt-5-nano", temperature=1),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
task = Task(
|
||||||
|
description=_parallel_prompt(),
|
||||||
|
expected_output="A one sentence summary of both tool outputs",
|
||||||
|
agent=agent,
|
||||||
|
)
|
||||||
|
crew = Crew(agents=[agent], tasks=[task])
|
||||||
|
result = crew.kickoff()
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
@pytest.mark.timeout(180)
|
||||||
|
def test_openai_parallel_native_tool_calling_test_agent_kickoff(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="gpt-4o-mini"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
result = agent.kickoff(_parallel_prompt())
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
@pytest.mark.timeout(180)
|
||||||
|
def test_openai_parallel_native_tool_calling_tool_hook_parity_crew(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
hook_calls: dict[str, list[dict[str, str]]] = {"before": [], "after": []}
|
||||||
|
|
||||||
|
def before_hook(context: ToolCallHookContext) -> bool | None:
|
||||||
|
if context.tool_name.startswith("parallel_local_search_"):
|
||||||
|
hook_calls["before"].append(
|
||||||
|
{
|
||||||
|
"tool_name": context.tool_name,
|
||||||
|
"query": str(context.tool_input.get("query", "")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def after_hook(context: ToolCallHookContext) -> str | None:
|
||||||
|
if context.tool_name.startswith("parallel_local_search_"):
|
||||||
|
hook_calls["after"].append(
|
||||||
|
{
|
||||||
|
"tool_name": context.tool_name,
|
||||||
|
"query": str(context.tool_input.get("query", "")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
register_before_tool_call_hook(before_hook)
|
||||||
|
register_after_tool_call_hook(after_hook)
|
||||||
|
|
||||||
|
try:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="gpt-5-nano", temperature=1),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
task = Task(
|
||||||
|
description=_parallel_prompt(),
|
||||||
|
expected_output="A one sentence summary of both tool outputs",
|
||||||
|
agent=agent,
|
||||||
|
)
|
||||||
|
crew = Crew(agents=[agent], tasks=[task])
|
||||||
|
result = crew.kickoff()
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
before_names = [call["tool_name"] for call in hook_calls["before"]]
|
||||||
|
after_names = [call["tool_name"] for call in hook_calls["after"]]
|
||||||
|
assert len(before_names) >= 3, "Expected before hooks for all parallel calls"
|
||||||
|
assert Counter(before_names) == Counter(after_names)
|
||||||
|
assert all(call["query"] for call in hook_calls["before"])
|
||||||
|
assert all(call["query"] for call in hook_calls["after"])
|
||||||
|
finally:
|
||||||
|
from crewai.hooks import (
|
||||||
|
unregister_after_tool_call_hook,
|
||||||
|
unregister_before_tool_call_hook,
|
||||||
|
)
|
||||||
|
|
||||||
|
unregister_before_tool_call_hook(before_hook)
|
||||||
|
unregister_after_tool_call_hook(after_hook)
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
@pytest.mark.timeout(180)
|
||||||
|
def test_openai_parallel_native_tool_calling_tool_hook_parity_agent_kickoff(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
hook_calls: dict[str, list[dict[str, str]]] = {"before": [], "after": []}
|
||||||
|
|
||||||
|
def before_hook(context: ToolCallHookContext) -> bool | None:
|
||||||
|
if context.tool_name.startswith("parallel_local_search_"):
|
||||||
|
hook_calls["before"].append(
|
||||||
|
{
|
||||||
|
"tool_name": context.tool_name,
|
||||||
|
"query": str(context.tool_input.get("query", "")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def after_hook(context: ToolCallHookContext) -> str | None:
|
||||||
|
if context.tool_name.startswith("parallel_local_search_"):
|
||||||
|
hook_calls["after"].append(
|
||||||
|
{
|
||||||
|
"tool_name": context.tool_name,
|
||||||
|
"query": str(context.tool_input.get("query", "")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
register_before_tool_call_hook(before_hook)
|
||||||
|
register_after_tool_call_hook(after_hook)
|
||||||
|
|
||||||
|
try:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="gpt-5-nano", temperature=1),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
result = agent.kickoff(_parallel_prompt())
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
before_names = [call["tool_name"] for call in hook_calls["before"]]
|
||||||
|
after_names = [call["tool_name"] for call in hook_calls["after"]]
|
||||||
|
assert len(before_names) >= 3, "Expected before hooks for all parallel calls"
|
||||||
|
assert Counter(before_names) == Counter(after_names)
|
||||||
|
assert all(call["query"] for call in hook_calls["before"])
|
||||||
|
assert all(call["query"] for call in hook_calls["after"])
|
||||||
|
finally:
|
||||||
|
from crewai.hooks import (
|
||||||
|
unregister_after_tool_call_hook,
|
||||||
|
unregister_before_tool_call_hook,
|
||||||
|
)
|
||||||
|
|
||||||
|
unregister_before_tool_call_hook(before_hook)
|
||||||
|
unregister_after_tool_call_hook(after_hook)
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Anthropic Provider Tests
|
# Anthropic Provider Tests
|
||||||
@@ -217,6 +518,46 @@ class TestAnthropicNativeToolCalling:
|
|||||||
assert mock_call.called
|
assert mock_call.called
|
||||||
assert result is not None
|
assert result is not None
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_anthropic_parallel_native_tool_calling_test_crew(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="anthropic/claude-sonnet-4-6"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
task = Task(
|
||||||
|
description=_parallel_prompt(),
|
||||||
|
expected_output="A one sentence summary of both tool outputs",
|
||||||
|
agent=agent,
|
||||||
|
)
|
||||||
|
crew = Crew(agents=[agent], tasks=[task])
|
||||||
|
result = crew.kickoff()
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_anthropic_parallel_native_tool_calling_test_agent_kickoff(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="anthropic/claude-sonnet-4-6"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
result = agent.kickoff(_parallel_prompt())
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Google/Gemini Provider Tests
|
# Google/Gemini Provider Tests
|
||||||
@@ -247,7 +588,7 @@ class TestGeminiNativeToolCalling:
|
|||||||
goal="Help users with mathematical calculations",
|
goal="Help users with mathematical calculations",
|
||||||
backstory="You are a helpful math assistant.",
|
backstory="You are a helpful math assistant.",
|
||||||
tools=[calculator_tool],
|
tools=[calculator_tool],
|
||||||
llm=LLM(model="gemini/gemini-2.0-flash-exp"),
|
llm=LLM(model="gemini/gemini-2.5-flash"),
|
||||||
)
|
)
|
||||||
|
|
||||||
task = Task(
|
task = Task(
|
||||||
@@ -266,7 +607,7 @@ class TestGeminiNativeToolCalling:
|
|||||||
self, calculator_tool: CalculatorTool
|
self, calculator_tool: CalculatorTool
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test Gemini agent kickoff with mocked LLM call."""
|
"""Test Gemini agent kickoff with mocked LLM call."""
|
||||||
llm = LLM(model="gemini/gemini-2.0-flash-001")
|
llm = LLM(model="gemini/gemini-2.5-flash")
|
||||||
|
|
||||||
with patch.object(llm, "call", return_value="The answer is 120.") as mock_call:
|
with patch.object(llm, "call", return_value="The answer is 120.") as mock_call:
|
||||||
agent = Agent(
|
agent = Agent(
|
||||||
@@ -290,6 +631,46 @@ class TestGeminiNativeToolCalling:
|
|||||||
assert mock_call.called
|
assert mock_call.called
|
||||||
assert result is not None
|
assert result is not None
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_gemini_parallel_native_tool_calling_test_crew(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="gemini/gemini-2.5-flash"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
task = Task(
|
||||||
|
description=_parallel_prompt(),
|
||||||
|
expected_output="A one sentence summary of both tool outputs",
|
||||||
|
agent=agent,
|
||||||
|
)
|
||||||
|
crew = Crew(agents=[agent], tasks=[task])
|
||||||
|
result = crew.kickoff()
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_gemini_parallel_native_tool_calling_test_agent_kickoff(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="gemini/gemini-2.5-flash"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
result = agent.kickoff(_parallel_prompt())
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Azure Provider Tests
|
# Azure Provider Tests
|
||||||
@@ -324,7 +705,7 @@ class TestAzureNativeToolCalling:
|
|||||||
goal="Help users with mathematical calculations",
|
goal="Help users with mathematical calculations",
|
||||||
backstory="You are a helpful math assistant.",
|
backstory="You are a helpful math assistant.",
|
||||||
tools=[calculator_tool],
|
tools=[calculator_tool],
|
||||||
llm=LLM(model="azure/gpt-4o-mini"),
|
llm=LLM(model="azure/gpt-5-nano"),
|
||||||
verbose=False,
|
verbose=False,
|
||||||
max_iter=3,
|
max_iter=3,
|
||||||
)
|
)
|
||||||
@@ -347,7 +728,7 @@ class TestAzureNativeToolCalling:
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test Azure agent kickoff with mocked LLM call."""
|
"""Test Azure agent kickoff with mocked LLM call."""
|
||||||
llm = LLM(
|
llm = LLM(
|
||||||
model="azure/gpt-4o-mini",
|
model="azure/gpt-5-nano",
|
||||||
api_key="test-key",
|
api_key="test-key",
|
||||||
base_url="https://test.openai.azure.com",
|
base_url="https://test.openai.azure.com",
|
||||||
)
|
)
|
||||||
@@ -374,6 +755,46 @@ class TestAzureNativeToolCalling:
|
|||||||
assert mock_call.called
|
assert mock_call.called
|
||||||
assert result is not None
|
assert result is not None
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_azure_parallel_native_tool_calling_test_crew(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="azure/gpt-5-nano"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
task = Task(
|
||||||
|
description=_parallel_prompt(),
|
||||||
|
expected_output="A one sentence summary of both tool outputs",
|
||||||
|
agent=agent,
|
||||||
|
)
|
||||||
|
crew = Crew(agents=[agent], tasks=[task])
|
||||||
|
result = crew.kickoff()
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_azure_parallel_native_tool_calling_test_agent_kickoff(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="azure/gpt-5-nano"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
result = agent.kickoff(_parallel_prompt())
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Bedrock Provider Tests
|
# Bedrock Provider Tests
|
||||||
@@ -384,18 +805,30 @@ class TestBedrockNativeToolCalling:
|
|||||||
"""Tests for native tool calling with AWS Bedrock models."""
|
"""Tests for native tool calling with AWS Bedrock models."""
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_aws_env(self):
|
def validate_bedrock_credentials_for_live_recording(self):
|
||||||
"""Mock AWS environment variables for tests."""
|
"""Run Bedrock tests only when explicitly enabled."""
|
||||||
env_vars = {
|
run_live_bedrock = os.getenv("RUN_BEDROCK_LIVE_TESTS", "false").lower() == "true"
|
||||||
"AWS_ACCESS_KEY_ID": "test-key",
|
|
||||||
"AWS_SECRET_ACCESS_KEY": "test-secret",
|
if not run_live_bedrock:
|
||||||
"AWS_REGION": "us-east-1",
|
pytest.skip(
|
||||||
}
|
"Skipping Bedrock tests by default. "
|
||||||
if "AWS_ACCESS_KEY_ID" not in os.environ:
|
"Set RUN_BEDROCK_LIVE_TESTS=true with valid AWS credentials to enable."
|
||||||
with patch.dict(os.environ, env_vars):
|
)
|
||||||
yield
|
|
||||||
else:
|
access_key = os.getenv("AWS_ACCESS_KEY_ID", "")
|
||||||
yield
|
secret_key = os.getenv("AWS_SECRET_ACCESS_KEY", "")
|
||||||
|
if (
|
||||||
|
not access_key
|
||||||
|
or not secret_key
|
||||||
|
or access_key.startswith(("fake-", "test-"))
|
||||||
|
or secret_key.startswith(("fake-", "test-"))
|
||||||
|
):
|
||||||
|
pytest.skip(
|
||||||
|
"Skipping Bedrock tests: valid AWS credentials are required when "
|
||||||
|
"RUN_BEDROCK_LIVE_TESTS=true."
|
||||||
|
)
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
@pytest.mark.vcr()
|
@pytest.mark.vcr()
|
||||||
def test_bedrock_agent_kickoff_with_tools_mocked(
|
def test_bedrock_agent_kickoff_with_tools_mocked(
|
||||||
@@ -427,6 +860,46 @@ class TestBedrockNativeToolCalling:
|
|||||||
assert result.raw is not None
|
assert result.raw is not None
|
||||||
assert "120" in str(result.raw)
|
assert "120" in str(result.raw)
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_bedrock_parallel_native_tool_calling_test_crew(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="bedrock/anthropic.claude-3-haiku-20240307-v1:0"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
task = Task(
|
||||||
|
description=_parallel_prompt(),
|
||||||
|
expected_output="A one sentence summary of both tool outputs",
|
||||||
|
agent=agent,
|
||||||
|
)
|
||||||
|
crew = Crew(agents=[agent], tasks=[task])
|
||||||
|
result = crew.kickoff()
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_bedrock_parallel_native_tool_calling_test_agent_kickoff(
|
||||||
|
self, parallel_tools: list[BaseTool]
|
||||||
|
) -> None:
|
||||||
|
agent = Agent(
|
||||||
|
role="Parallel Tool Agent",
|
||||||
|
goal="Use both tools exactly as instructed",
|
||||||
|
backstory="You follow tool instructions precisely.",
|
||||||
|
tools=parallel_tools,
|
||||||
|
llm=LLM(model="bedrock/anthropic.claude-3-haiku-20240307-v1:0"),
|
||||||
|
verbose=False,
|
||||||
|
max_iter=3,
|
||||||
|
)
|
||||||
|
result = agent.kickoff(_parallel_prompt())
|
||||||
|
assert result is not None
|
||||||
|
_assert_tools_overlapped()
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Cross-Provider Native Tool Calling Behavior Tests
|
# Cross-Provider Native Tool Calling Behavior Tests
|
||||||
@@ -439,7 +912,7 @@ class TestNativeToolCallingBehavior:
|
|||||||
def test_supports_function_calling_check(self) -> None:
|
def test_supports_function_calling_check(self) -> None:
|
||||||
"""Test that supports_function_calling() is properly checked."""
|
"""Test that supports_function_calling() is properly checked."""
|
||||||
# OpenAI should support function calling
|
# OpenAI should support function calling
|
||||||
openai_llm = LLM(model="gpt-4o-mini")
|
openai_llm = LLM(model="gpt-5-nano")
|
||||||
assert hasattr(openai_llm, "supports_function_calling")
|
assert hasattr(openai_llm, "supports_function_calling")
|
||||||
assert openai_llm.supports_function_calling() is True
|
assert openai_llm.supports_function_calling() is True
|
||||||
|
|
||||||
@@ -475,7 +948,7 @@ class TestNativeToolCallingTokenUsage:
|
|||||||
goal="Perform calculations efficiently",
|
goal="Perform calculations efficiently",
|
||||||
backstory="You calculate things.",
|
backstory="You calculate things.",
|
||||||
tools=[calculator_tool],
|
tools=[calculator_tool],
|
||||||
llm=LLM(model="gpt-4o-mini"),
|
llm=LLM(model="gpt-5-nano"),
|
||||||
verbose=False,
|
verbose=False,
|
||||||
max_iter=3,
|
max_iter=3,
|
||||||
)
|
)
|
||||||
@@ -519,7 +992,7 @@ def test_native_tool_calling_error_handling(failing_tool: FailingTool):
|
|||||||
goal="Perform calculations efficiently",
|
goal="Perform calculations efficiently",
|
||||||
backstory="You calculate things.",
|
backstory="You calculate things.",
|
||||||
tools=[failing_tool],
|
tools=[failing_tool],
|
||||||
llm=LLM(model="gpt-4o-mini"),
|
llm=LLM(model="gpt-5-nano"),
|
||||||
verbose=False,
|
verbose=False,
|
||||||
max_iter=3,
|
max_iter=3,
|
||||||
)
|
)
|
||||||
@@ -578,7 +1051,7 @@ class TestMaxUsageCountWithNativeToolCalling:
|
|||||||
goal="Call the counting tool multiple times",
|
goal="Call the counting tool multiple times",
|
||||||
backstory="You are an agent that counts things.",
|
backstory="You are an agent that counts things.",
|
||||||
tools=[tool],
|
tools=[tool],
|
||||||
llm=LLM(model="gpt-4o-mini"),
|
llm=LLM(model="gpt-5-nano"),
|
||||||
verbose=False,
|
verbose=False,
|
||||||
max_iter=5,
|
max_iter=5,
|
||||||
)
|
)
|
||||||
@@ -606,7 +1079,7 @@ class TestMaxUsageCountWithNativeToolCalling:
|
|||||||
goal="Use the counting tool as many times as requested",
|
goal="Use the counting tool as many times as requested",
|
||||||
backstory="You are an agent that counts things. You must try to use the tool for each value requested.",
|
backstory="You are an agent that counts things. You must try to use the tool for each value requested.",
|
||||||
tools=[tool],
|
tools=[tool],
|
||||||
llm=LLM(model="gpt-4o-mini"),
|
llm=LLM(model="gpt-5-nano"),
|
||||||
verbose=False,
|
verbose=False,
|
||||||
max_iter=5,
|
max_iter=5,
|
||||||
)
|
)
|
||||||
@@ -638,7 +1111,7 @@ class TestMaxUsageCountWithNativeToolCalling:
|
|||||||
goal="Use the counting tool exactly as requested",
|
goal="Use the counting tool exactly as requested",
|
||||||
backstory="You are an agent that counts things precisely.",
|
backstory="You are an agent that counts things precisely.",
|
||||||
tools=[tool],
|
tools=[tool],
|
||||||
llm=LLM(model="gpt-4o-mini"),
|
llm=LLM(model="gpt-5-nano"),
|
||||||
verbose=False,
|
verbose=False,
|
||||||
max_iter=5,
|
max_iter=5,
|
||||||
)
|
)
|
||||||
@@ -653,5 +1126,6 @@ class TestMaxUsageCountWithNativeToolCalling:
|
|||||||
result = crew.kickoff()
|
result = crew.kickoff()
|
||||||
|
|
||||||
assert result is not None
|
assert result is not None
|
||||||
# Verify usage count was incremented for each successful call
|
# Verify the requested calls occurred while keeping usage bounded.
|
||||||
assert tool.current_usage_count == 2
|
assert tool.current_usage_count >= 2
|
||||||
|
assert tool.current_usage_count <= tool.max_usage_count
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"max_tokens":4096,"messages":[{"role":"user","content":"\nCurrent Task:
|
||||||
|
This is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}],"model":"claude-sonnet-4-6","stop_sequences":["\nObservation:"],"stream":false,"system":"You
|
||||||
|
are Parallel Tool Agent. You follow tool instructions precisely.\nYour personal
|
||||||
|
goal is: Use both tools exactly as instructed","tools":[{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}]}'
|
||||||
|
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:
|
||||||
|
- '1639'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
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.73.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_01XeN1XTXZgmPyLMMGjivabb","type":"message","role":"assistant","content":[{"type":"text","text":"I''ll
|
||||||
|
execute all 3 parallel searches simultaneously right now!"},{"type":"tool_use","id":"toolu_01NwzvrxEz6tvT3A8ydvMtHu","name":"parallel_local_search_one","input":{"query":"latest
|
||||||
|
OpenAI model release notes"},"caller":{"type":"direct"}},{"type":"tool_use","id":"toolu_01YCxzSB1suk9uPVC1uwfHz9","name":"parallel_local_search_two","input":{"query":"latest
|
||||||
|
Anthropic model release notes"},"caller":{"type":"direct"}},{"type":"tool_use","id":"toolu_01Mauvxzv58eDY7pUt9HMKGy","name":"parallel_local_search_three","input":{"query":"latest
|
||||||
|
Gemini model release notes"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":914,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":169,"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:
|
||||||
|
- Wed, 18 Feb 2026 23:54:43 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-02-18T23:54:41Z'
|
||||||
|
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
|
||||||
|
strict-transport-security:
|
||||||
|
- STS-XXX
|
||||||
|
x-envoy-upstream-service-time:
|
||||||
|
- '2099'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"max_tokens":4096,"messages":[{"role":"user","content":"\nCurrent Task:
|
||||||
|
This is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."},{"role":"assistant","content":[{"type":"tool_use","id":"toolu_01NwzvrxEz6tvT3A8ydvMtHu","name":"parallel_local_search_one","input":{"query":"latest
|
||||||
|
OpenAI model release notes"}},{"type":"tool_use","id":"toolu_01YCxzSB1suk9uPVC1uwfHz9","name":"parallel_local_search_two","input":{"query":"latest
|
||||||
|
Anthropic model release notes"}},{"type":"tool_use","id":"toolu_01Mauvxzv58eDY7pUt9HMKGy","name":"parallel_local_search_three","input":{"query":"latest
|
||||||
|
Gemini model release notes"}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_01NwzvrxEz6tvT3A8ydvMtHu","content":"[one]
|
||||||
|
latest OpenAI model release notes"},{"type":"tool_result","tool_use_id":"toolu_01YCxzSB1suk9uPVC1uwfHz9","content":"[two]
|
||||||
|
latest Anthropic model release notes"},{"type":"tool_result","tool_use_id":"toolu_01Mauvxzv58eDY7pUt9HMKGy","content":"[three]
|
||||||
|
latest Gemini model release notes"}]}],"model":"claude-sonnet-4-6","stop_sequences":["\nObservation:"],"stream":false,"system":"You
|
||||||
|
are Parallel Tool Agent. You follow tool instructions precisely.\nYour personal
|
||||||
|
goal is: Use both tools exactly as instructed","tools":[{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}]}'
|
||||||
|
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:
|
||||||
|
- '2517'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
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.73.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_01PFXqwwdwwHWadPdtNU5tUZ\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"The
|
||||||
|
three parallel searches were executed successfully, each targeting the latest
|
||||||
|
release notes for the leading AI model families. The search results confirm
|
||||||
|
that queries were dispatched simultaneously to retrieve the most recent developments
|
||||||
|
from **OpenAI** (via tool one), **Anthropic** (via tool two), and **Google's
|
||||||
|
Gemini** (via tool three). While the local search tools returned placeholder
|
||||||
|
outputs in this test environment rather than detailed release notes, the structure
|
||||||
|
of the test validates that all three parallel tool calls were emitted correctly
|
||||||
|
and in the specified order \u2014 demonstrating proper concurrent tool-call
|
||||||
|
behavior with no dependencies between the three independent searches.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1197,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":131,\"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:
|
||||||
|
- Wed, 18 Feb 2026 23:54:49 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-02-18T23:54:44Z'
|
||||||
|
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
|
||||||
|
strict-transport-security:
|
||||||
|
- STS-XXX
|
||||||
|
x-envoy-upstream-service-time:
|
||||||
|
- '4092'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"max_tokens":4096,"messages":[{"role":"user","content":"\nCurrent Task:
|
||||||
|
This is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}],"model":"claude-sonnet-4-6","stop_sequences":["\nObservation:"],"stream":false,"system":"You
|
||||||
|
are Parallel Tool Agent. You follow tool instructions precisely.\nYour personal
|
||||||
|
goal is: Use both tools exactly as instructed","tools":[{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}]}'
|
||||||
|
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:
|
||||||
|
- '1820'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
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.73.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_01RJ4CphwpmkmsJFJjeCNvXz","type":"message","role":"assistant","content":[{"type":"text","text":"I''ll
|
||||||
|
execute all 3 parallel tool calls simultaneously right away!"},{"type":"tool_use","id":"toolu_01YWY3cSomRuv4USmq55Prk3","name":"parallel_local_search_one","input":{"query":"latest
|
||||||
|
OpenAI model release notes"},"caller":{"type":"direct"}},{"type":"tool_use","id":"toolu_01Aaqj3LMXksE1nB3pscRhV5","name":"parallel_local_search_two","input":{"query":"latest
|
||||||
|
Anthropic model release notes"},"caller":{"type":"direct"}},{"type":"tool_use","id":"toolu_01AcYxQvy8aYmAoUg9zx9qfq","name":"parallel_local_search_three","input":{"query":"latest
|
||||||
|
Gemini model release notes"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":951,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":170,"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:
|
||||||
|
- Wed, 18 Feb 2026 23:54:51 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-02-18T23:54:49Z'
|
||||||
|
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
|
||||||
|
strict-transport-security:
|
||||||
|
- STS-XXX
|
||||||
|
x-envoy-upstream-service-time:
|
||||||
|
- '1967'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"max_tokens":4096,"messages":[{"role":"user","content":"\nCurrent Task:
|
||||||
|
This is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."},{"role":"assistant","content":[{"type":"tool_use","id":"toolu_01YWY3cSomRuv4USmq55Prk3","name":"parallel_local_search_one","input":{"query":"latest
|
||||||
|
OpenAI model release notes"}},{"type":"tool_use","id":"toolu_01Aaqj3LMXksE1nB3pscRhV5","name":"parallel_local_search_two","input":{"query":"latest
|
||||||
|
Anthropic model release notes"}},{"type":"tool_use","id":"toolu_01AcYxQvy8aYmAoUg9zx9qfq","name":"parallel_local_search_three","input":{"query":"latest
|
||||||
|
Gemini model release notes"}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_01YWY3cSomRuv4USmq55Prk3","content":"[one]
|
||||||
|
latest OpenAI model release notes"},{"type":"tool_result","tool_use_id":"toolu_01Aaqj3LMXksE1nB3pscRhV5","content":"[two]
|
||||||
|
latest Anthropic model release notes"},{"type":"tool_result","tool_use_id":"toolu_01AcYxQvy8aYmAoUg9zx9qfq","content":"[three]
|
||||||
|
latest Gemini model release notes"}]},{"role":"user","content":"Analyze the
|
||||||
|
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
||||||
|
the next tool. Deliver only the answer without meta-commentary."}],"model":"claude-sonnet-4-6","stop_sequences":["\nObservation:"],"stream":false,"system":"You
|
||||||
|
are Parallel Tool Agent. You follow tool instructions precisely.\nYour personal
|
||||||
|
goal is: Use both tools exactly as instructed","tools":[{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}},{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","input_schema":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}]}'
|
||||||
|
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:
|
||||||
|
- '2882'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
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.73.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_0143MHUne1az3Tt69EoLjyZd\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"Here
|
||||||
|
is the complete content returned from all three tool calls:\\n\\n- **parallel_local_search_one**
|
||||||
|
result: `[one] latest OpenAI model release notes`\\n- **parallel_local_search_two**
|
||||||
|
result: `[two] latest Anthropic model release notes`\\n- **parallel_local_search_three**
|
||||||
|
result: `[three] latest Gemini model release notes`\\n\\nAll three parallel
|
||||||
|
tool calls were executed successfully in the same response turn, returning
|
||||||
|
their respective outputs: the first tool searched for the latest OpenAI model
|
||||||
|
release notes, the second tool searched for the latest Anthropic model release
|
||||||
|
notes, and the third tool searched for the latest Gemini model release notes
|
||||||
|
\u2014 confirming that all search queries were dispatched concurrently and
|
||||||
|
their results retrieved as expected.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1272,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":172,\"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:
|
||||||
|
- Wed, 18 Feb 2026 23:54:55 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-02-18T23:54:52Z'
|
||||||
|
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
|
||||||
|
strict-transport-security:
|
||||||
|
- STS-XXX
|
||||||
|
x-envoy-upstream-service-time:
|
||||||
|
- '3144'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -5,20 +5,19 @@ interactions:
|
|||||||
calculations"}, {"role": "user", "content": "\nCurrent Task: Calculate what
|
calculations"}, {"role": "user", "content": "\nCurrent Task: Calculate what
|
||||||
is 15 * 8\n\nThis is the expected criteria for your final answer: The result
|
is 15 * 8\n\nThis is the expected criteria for your final answer: The result
|
||||||
of the calculation\nyou MUST return the actual complete content as the final
|
of the calculation\nyou MUST return the actual complete content as the final
|
||||||
answer, not a summary.\n\nThis is VERY important to you, your job depends on
|
answer, not a summary."}], "stream": false, "tool_choice": "auto", "tools":
|
||||||
it!"}], "stream": false, "stop": ["\nObservation:"], "tool_choice": "auto",
|
[{"function": {"name": "calculator", "description": "Perform mathematical calculations.
|
||||||
"tools": [{"function": {"name": "calculator", "description": "Perform mathematical
|
Use this for any math operations.", "parameters": {"properties": {"expression":
|
||||||
calculations. Use this for any math operations.", "parameters": {"properties":
|
{"description": "Mathematical expression to evaluate", "title": "Expression",
|
||||||
{"expression": {"description": "Mathematical expression to evaluate", "title":
|
"type": "string"}}, "required": ["expression"], "type": "object", "additionalProperties":
|
||||||
"Expression", "type": "string"}}, "required": ["expression"], "type": "object"}},
|
false}}, "type": "function"}]}'
|
||||||
"type": "function"}]}'
|
|
||||||
headers:
|
headers:
|
||||||
Accept:
|
Accept:
|
||||||
- application/json
|
- application/json
|
||||||
Connection:
|
Connection:
|
||||||
- keep-alive
|
- keep-alive
|
||||||
Content-Length:
|
Content-Length:
|
||||||
- '883'
|
- '828'
|
||||||
Content-Type:
|
Content-Type:
|
||||||
- application/json
|
- application/json
|
||||||
User-Agent:
|
User-Agent:
|
||||||
@@ -32,20 +31,20 @@ interactions:
|
|||||||
x-ms-client-request-id:
|
x-ms-client-request-id:
|
||||||
- X-MS-CLIENT-REQUEST-ID-XXX
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
method: POST
|
method: POST
|
||||||
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-12-01-preview
|
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-5-nano/chat/completions?api-version=2024-12-01-preview
|
||||||
response:
|
response:
|
||||||
body:
|
body:
|
||||||
string: '{"choices":[{"content_filter_results":{},"finish_reason":"tool_calls","index":0,"logprobs":null,"message":{"annotations":[],"content":null,"refusal":null,"role":"assistant","tool_calls":[{"function":{"arguments":"{\"expression\":\"15
|
string: '{"choices":[{"content_filter_results":{},"finish_reason":"tool_calls","index":0,"logprobs":null,"message":{"annotations":[],"content":null,"refusal":null,"role":"assistant","tool_calls":[{"function":{"arguments":"{\"expression\":\"15
|
||||||
* 8\"}","name":"calculator"},"id":"call_cJWzKh5LdBpY3Sk8GATS3eRe","type":"function"}]}}],"created":1769122114,"id":"chatcmpl-D0xlavS0V3m00B9Fsjyv39xQWUGFV","model":"gpt-4o-mini-2024-07-18","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_f97eff32c5","usage":{"completion_tokens":18,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":137,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":155}}
|
* 8\"}","name":"calculator"},"id":"call_Cow46pNllpDx0pxUgZFeqlh1","type":"function"}]}}],"created":1771459544,"id":"chatcmpl-DAlq4osCP9ABJ1HyXFBoYWylMg0bi","model":"gpt-5-nano-2025-08-07","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":null,"usage":{"completion_tokens":219,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":192,"rejected_prediction_tokens":0},"prompt_tokens":208,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":427}}
|
||||||
|
|
||||||
'
|
'
|
||||||
headers:
|
headers:
|
||||||
Content-Length:
|
Content-Length:
|
||||||
- '1058'
|
- '1049'
|
||||||
Content-Type:
|
Content-Type:
|
||||||
- application/json
|
- application/json
|
||||||
Date:
|
Date:
|
||||||
- Thu, 22 Jan 2026 22:48:34 GMT
|
- Thu, 19 Feb 2026 00:05:45 GMT
|
||||||
Strict-Transport-Security:
|
Strict-Transport-Security:
|
||||||
- STS-XXX
|
- STS-XXX
|
||||||
apim-request-id:
|
apim-request-id:
|
||||||
@@ -59,7 +58,7 @@ interactions:
|
|||||||
x-ms-client-request-id:
|
x-ms-client-request-id:
|
||||||
- X-MS-CLIENT-REQUEST-ID-XXX
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
x-ms-deployment-name:
|
x-ms-deployment-name:
|
||||||
- gpt-4o-mini
|
- gpt-5-nano
|
||||||
x-ms-rai-invoked:
|
x-ms-rai-invoked:
|
||||||
- 'true'
|
- 'true'
|
||||||
x-ms-region:
|
x-ms-region:
|
||||||
@@ -83,26 +82,25 @@ interactions:
|
|||||||
calculations"}, {"role": "user", "content": "\nCurrent Task: Calculate what
|
calculations"}, {"role": "user", "content": "\nCurrent Task: Calculate what
|
||||||
is 15 * 8\n\nThis is the expected criteria for your final answer: The result
|
is 15 * 8\n\nThis is the expected criteria for your final answer: The result
|
||||||
of the calculation\nyou MUST return the actual complete content as the final
|
of the calculation\nyou MUST return the actual complete content as the final
|
||||||
answer, not a summary.\n\nThis is VERY important to you, your job depends on
|
answer, not a summary."}, {"role": "assistant", "content": "", "tool_calls":
|
||||||
it!"}, {"role": "assistant", "content": "", "tool_calls": [{"id": "call_cJWzKh5LdBpY3Sk8GATS3eRe",
|
[{"id": "call_Cow46pNllpDx0pxUgZFeqlh1", "type": "function", "function": {"name":
|
||||||
"type": "function", "function": {"name": "calculator", "arguments": "{\"expression\":\"15
|
"calculator", "arguments": "{\"expression\":\"15 * 8\"}"}}]}, {"role": "tool",
|
||||||
* 8\"}"}}]}, {"role": "tool", "tool_call_id": "call_cJWzKh5LdBpY3Sk8GATS3eRe",
|
"tool_call_id": "call_Cow46pNllpDx0pxUgZFeqlh1", "content": "The result of 15
|
||||||
"content": "The result of 15 * 8 is 120"}, {"role": "user", "content": "Analyze
|
* 8 is 120"}, {"role": "user", "content": "Analyze the tool result. If requirements
|
||||||
the tool result. If requirements are met, provide the Final Answer. Otherwise,
|
are met, provide the Final Answer. Otherwise, call the next tool. Deliver only
|
||||||
call the next tool. Deliver only the answer without meta-commentary."}], "stream":
|
the answer without meta-commentary."}], "stream": false, "tool_choice": "auto",
|
||||||
false, "stop": ["\nObservation:"], "tool_choice": "auto", "tools": [{"function":
|
"tools": [{"function": {"name": "calculator", "description": "Perform mathematical
|
||||||
{"name": "calculator", "description": "Perform mathematical calculations. Use
|
calculations. Use this for any math operations.", "parameters": {"properties":
|
||||||
this for any math operations.", "parameters": {"properties": {"expression":
|
{"expression": {"description": "Mathematical expression to evaluate", "title":
|
||||||
{"description": "Mathematical expression to evaluate", "title": "Expression",
|
"Expression", "type": "string"}}, "required": ["expression"], "type": "object",
|
||||||
"type": "string"}}, "required": ["expression"], "type": "object"}}, "type":
|
"additionalProperties": false}}, "type": "function"}]}'
|
||||||
"function"}]}'
|
|
||||||
headers:
|
headers:
|
||||||
Accept:
|
Accept:
|
||||||
- application/json
|
- application/json
|
||||||
Connection:
|
Connection:
|
||||||
- keep-alive
|
- keep-alive
|
||||||
Content-Length:
|
Content-Length:
|
||||||
- '1375'
|
- '1320'
|
||||||
Content-Type:
|
Content-Type:
|
||||||
- application/json
|
- application/json
|
||||||
User-Agent:
|
User-Agent:
|
||||||
@@ -116,20 +114,19 @@ interactions:
|
|||||||
x-ms-client-request-id:
|
x-ms-client-request-id:
|
||||||
- X-MS-CLIENT-REQUEST-ID-XXX
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
method: POST
|
method: POST
|
||||||
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-12-01-preview
|
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-5-nano/chat/completions?api-version=2024-12-01-preview
|
||||||
response:
|
response:
|
||||||
body:
|
body:
|
||||||
string: '{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"protected_material_code":{"filtered":false,"detected":false},"protected_material_text":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"The
|
string: '{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"protected_material_code":{"filtered":false,"detected":false},"protected_material_text":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"120","refusal":null,"role":"assistant"}}],"created":1771459547,"id":"chatcmpl-DAlq7zJimnIMoXieNww8jY5f2pIPd","model":"gpt-5-nano-2025-08-07","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":null,"usage":{"completion_tokens":203,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":192,"rejected_prediction_tokens":0},"prompt_tokens":284,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":487}}
|
||||||
result of the calculation is 120.","refusal":null,"role":"assistant"}}],"created":1769122115,"id":"chatcmpl-D0xlbUNVA7RVkn0GsuBGoNhgQTtac","model":"gpt-4o-mini-2024-07-18","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_f97eff32c5","usage":{"completion_tokens":11,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":207,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":218}}
|
|
||||||
|
|
||||||
'
|
'
|
||||||
headers:
|
headers:
|
||||||
Content-Length:
|
Content-Length:
|
||||||
- '1250'
|
- '1207'
|
||||||
Content-Type:
|
Content-Type:
|
||||||
- application/json
|
- application/json
|
||||||
Date:
|
Date:
|
||||||
- Thu, 22 Jan 2026 22:48:34 GMT
|
- Thu, 19 Feb 2026 00:05:49 GMT
|
||||||
Strict-Transport-Security:
|
Strict-Transport-Security:
|
||||||
- STS-XXX
|
- STS-XXX
|
||||||
apim-request-id:
|
apim-request-id:
|
||||||
@@ -143,7 +140,7 @@ interactions:
|
|||||||
x-ms-client-request-id:
|
x-ms-client-request-id:
|
||||||
- X-MS-CLIENT-REQUEST-ID-XXX
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
x-ms-deployment-name:
|
x-ms-deployment-name:
|
||||||
- gpt-4o-mini
|
- gpt-5-nano
|
||||||
x-ms-rai-invoked:
|
x-ms-rai-invoked:
|
||||||
- 'true'
|
- 'true'
|
||||||
x-ms-region:
|
x-ms-region:
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "system", "content": "You are Parallel Tool Agent.
|
||||||
|
You follow tool instructions precisely.\nYour personal goal is: Use both tools
|
||||||
|
exactly as instructed"}, {"role": "user", "content": "\nCurrent Task: This is
|
||||||
|
a tool-calling compliance test. In your next assistant turn, emit exactly 3
|
||||||
|
tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}], "stream": false, "tool_choice": "auto", "tools": [{"function":
|
||||||
|
{"name": "parallel_local_search_one", "description": "Local search tool #1 for
|
||||||
|
concurrency testing.", "parameters": {"properties": {"query": {"description":
|
||||||
|
"Search query", "title": "Query", "type": "string"}}, "required": ["query"],
|
||||||
|
"type": "object", "additionalProperties": false}}, "type": "function"}, {"function":
|
||||||
|
{"name": "parallel_local_search_two", "description": "Local search tool #2 for
|
||||||
|
concurrency testing.", "parameters": {"properties": {"query": {"description":
|
||||||
|
"Search query", "title": "Query", "type": "string"}}, "required": ["query"],
|
||||||
|
"type": "object", "additionalProperties": false}}, "type": "function"}, {"function":
|
||||||
|
{"name": "parallel_local_search_three", "description": "Local search tool #3
|
||||||
|
for concurrency testing.", "parameters": {"properties": {"query": {"description":
|
||||||
|
"Search query", "title": "Query", "type": "string"}}, "required": ["query"],
|
||||||
|
"type": "object", "additionalProperties": false}}, "type": "function"}]}'
|
||||||
|
headers:
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '1763'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
api-key:
|
||||||
|
- X-API-KEY-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-5-nano/chat/completions?api-version=2024-12-01-preview
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"choices":[{"content_filter_results":{},"finish_reason":"tool_calls","index":0,"logprobs":null,"message":{"annotations":[],"content":null,"refusal":null,"role":"assistant","tool_calls":[{"function":{"arguments":"{\"query\":
|
||||||
|
\"latest OpenAI model release notes\"}","name":"parallel_local_search_one"},"id":"call_emQmocGydKuxvESfQopNngdm","type":"function"},{"function":{"arguments":"{\"query\":
|
||||||
|
\"latest Anthropic model release notes\"}","name":"parallel_local_search_two"},"id":"call_eNpK9WUYFCX2ZEUPhYCKvdMs","type":"function"},{"function":{"arguments":"{\"query\":
|
||||||
|
\"latest Gemini model release notes\"}","name":"parallel_local_search_three"},"id":"call_Wdtl6jFxGehSUMn5I1O4Mrdx","type":"function"}]}}],"created":1771459550,"id":"chatcmpl-DAlqAyJGnQKDkNCaTcjU2T8BeJaXM","model":"gpt-5-nano-2025-08-07","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":null,"usage":{"completion_tokens":666,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":576,"rejected_prediction_tokens":0},"prompt_tokens":343,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":1009}}
|
||||||
|
|
||||||
|
'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '1433'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:05:55 GMT
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
apim-request-id:
|
||||||
|
- APIM-REQUEST-ID-XXX
|
||||||
|
azureml-model-session:
|
||||||
|
- AZUREML-MODEL-SESSION-XXX
|
||||||
|
x-accel-buffering:
|
||||||
|
- 'no'
|
||||||
|
x-content-type-options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
x-ms-deployment-name:
|
||||||
|
- gpt-5-nano
|
||||||
|
x-ms-rai-invoked:
|
||||||
|
- 'true'
|
||||||
|
x-ms-region:
|
||||||
|
- X-MS-REGION-XXX
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "system", "content": "You are Parallel Tool Agent.
|
||||||
|
You follow tool instructions precisely.\nYour personal goal is: Use both tools
|
||||||
|
exactly as instructed"}, {"role": "user", "content": "\nCurrent Task: This is
|
||||||
|
a tool-calling compliance test. In your next assistant turn, emit exactly 3
|
||||||
|
tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}, {"role": "assistant", "content": "", "tool_calls": [{"id":
|
||||||
|
"call_emQmocGydKuxvESfQopNngdm", "type": "function", "function": {"name": "parallel_local_search_one",
|
||||||
|
"arguments": "{\"query\": \"latest OpenAI model release notes\"}"}}, {"id":
|
||||||
|
"call_eNpK9WUYFCX2ZEUPhYCKvdMs", "type": "function", "function": {"name": "parallel_local_search_two",
|
||||||
|
"arguments": "{\"query\": \"latest Anthropic model release notes\"}"}}, {"id":
|
||||||
|
"call_Wdtl6jFxGehSUMn5I1O4Mrdx", "type": "function", "function": {"name": "parallel_local_search_three",
|
||||||
|
"arguments": "{\"query\": \"latest Gemini model release notes\"}"}}]}, {"role":
|
||||||
|
"tool", "tool_call_id": "call_emQmocGydKuxvESfQopNngdm", "content": "[one] latest
|
||||||
|
OpenAI model release notes"}, {"role": "tool", "tool_call_id": "call_eNpK9WUYFCX2ZEUPhYCKvdMs",
|
||||||
|
"content": "[two] latest Anthropic model release notes"}, {"role": "tool", "tool_call_id":
|
||||||
|
"call_Wdtl6jFxGehSUMn5I1O4Mrdx", "content": "[three] latest Gemini model release
|
||||||
|
notes"}], "stream": false, "tool_choice": "auto", "tools": [{"function": {"name":
|
||||||
|
"parallel_local_search_one", "description": "Local search tool #1 for concurrency
|
||||||
|
testing.", "parameters": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}, "type": "function"}, {"function": {"name":
|
||||||
|
"parallel_local_search_two", "description": "Local search tool #2 for concurrency
|
||||||
|
testing.", "parameters": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}, "type": "function"}, {"function": {"name":
|
||||||
|
"parallel_local_search_three", "description": "Local search tool #3 for concurrency
|
||||||
|
testing.", "parameters": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}, "type": "function"}]}'
|
||||||
|
headers:
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '2727'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
api-key:
|
||||||
|
- X-API-KEY-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-5-nano/chat/completions?api-version=2024-12-01-preview
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"protected_material_code":{"filtered":false,"detected":false},"protected_material_text":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"The
|
||||||
|
latest release notes have been published for the OpenAI, Anthropic, and Gemini
|
||||||
|
models, signaling concurrent updates across the leading AI model families.
|
||||||
|
Each set outlines new capabilities and performance improvements, along with
|
||||||
|
changes to APIs, tooling, and deployment guidelines. Users should review the
|
||||||
|
individual notes to understand new features, adjustments to tokenization,
|
||||||
|
latency or throughput, safety and alignment enhancements, pricing or access
|
||||||
|
changes, and any breaking changes or migration steps required to adopt the
|
||||||
|
updated models in existing workflows.","refusal":null,"role":"assistant"}}],"created":1771459556,"id":"chatcmpl-DAlqGKWXfGNlTIbDY9F6oHQp6hbxM","model":"gpt-5-nano-2025-08-07","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":null,"usage":{"completion_tokens":747,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":640,"rejected_prediction_tokens":0},"prompt_tokens":467,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":1214}}
|
||||||
|
|
||||||
|
'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '1778'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:06:02 GMT
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
apim-request-id:
|
||||||
|
- APIM-REQUEST-ID-XXX
|
||||||
|
azureml-model-session:
|
||||||
|
- AZUREML-MODEL-SESSION-XXX
|
||||||
|
x-accel-buffering:
|
||||||
|
- 'no'
|
||||||
|
x-content-type-options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
x-ms-deployment-name:
|
||||||
|
- gpt-5-nano
|
||||||
|
x-ms-rai-invoked:
|
||||||
|
- 'true'
|
||||||
|
x-ms-region:
|
||||||
|
- X-MS-REGION-XXX
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "system", "content": "You are Parallel Tool Agent.
|
||||||
|
You follow tool instructions precisely.\nYour personal goal is: Use both tools
|
||||||
|
exactly as instructed"}, {"role": "user", "content": "\nCurrent Task: This is
|
||||||
|
a tool-calling compliance test. In your next assistant turn, emit exactly 3
|
||||||
|
tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}], "stream": false, "tool_choice":
|
||||||
|
"auto", "tools": [{"function": {"name": "parallel_local_search_one", "description":
|
||||||
|
"Local search tool #1 for concurrency testing.", "parameters": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, "type":
|
||||||
|
"function"}, {"function": {"name": "parallel_local_search_two", "description":
|
||||||
|
"Local search tool #2 for concurrency testing.", "parameters": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, "type":
|
||||||
|
"function"}, {"function": {"name": "parallel_local_search_three", "description":
|
||||||
|
"Local search tool #3 for concurrency testing.", "parameters": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, "type":
|
||||||
|
"function"}]}'
|
||||||
|
headers:
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '1944'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
api-key:
|
||||||
|
- X-API-KEY-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-5-nano/chat/completions?api-version=2024-12-01-preview
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"choices":[{"content_filter_results":{},"finish_reason":"tool_calls","index":0,"logprobs":null,"message":{"annotations":[],"content":null,"refusal":null,"role":"assistant","tool_calls":[{"function":{"arguments":"{\"query\":
|
||||||
|
\"latest OpenAI model release notes\"}","name":"parallel_local_search_one"},"id":"call_NEvGoF86nhPQfXRoJd5SOyLd","type":"function"},{"function":{"arguments":"{\"query\":
|
||||||
|
\"latest Anthropic model release notes\"}","name":"parallel_local_search_two"},"id":"call_q8Q2du4gAMQLrGTgWgfwfbDZ","type":"function"},{"function":{"arguments":"{\"query\":
|
||||||
|
\"latest Gemini model release notes\"}","name":"parallel_local_search_three"},"id":"call_yTBal9ofZzuo10j0pWqhHCSj","type":"function"}]}}],"created":1771459563,"id":"chatcmpl-DAlqN7kyC5ACI5Yl1Pj63rOH5HIvI","model":"gpt-5-nano-2025-08-07","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":null,"usage":{"completion_tokens":2457,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":2368,"rejected_prediction_tokens":0},"prompt_tokens":378,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":2835}}
|
||||||
|
|
||||||
|
'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '1435'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:06:17 GMT
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
apim-request-id:
|
||||||
|
- APIM-REQUEST-ID-XXX
|
||||||
|
azureml-model-session:
|
||||||
|
- AZUREML-MODEL-SESSION-XXX
|
||||||
|
x-accel-buffering:
|
||||||
|
- 'no'
|
||||||
|
x-content-type-options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
x-ms-deployment-name:
|
||||||
|
- gpt-5-nano
|
||||||
|
x-ms-rai-invoked:
|
||||||
|
- 'true'
|
||||||
|
x-ms-region:
|
||||||
|
- X-MS-REGION-XXX
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "system", "content": "You are Parallel Tool Agent.
|
||||||
|
You follow tool instructions precisely.\nYour personal goal is: Use both tools
|
||||||
|
exactly as instructed"}, {"role": "user", "content": "\nCurrent Task: This is
|
||||||
|
a tool-calling compliance test. In your next assistant turn, emit exactly 3
|
||||||
|
tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}, {"role": "assistant", "content":
|
||||||
|
"", "tool_calls": [{"id": "call_NEvGoF86nhPQfXRoJd5SOyLd", "type": "function",
|
||||||
|
"function": {"name": "parallel_local_search_one", "arguments": "{\"query\":
|
||||||
|
\"latest OpenAI model release notes\"}"}}, {"id": "call_q8Q2du4gAMQLrGTgWgfwfbDZ",
|
||||||
|
"type": "function", "function": {"name": "parallel_local_search_two", "arguments":
|
||||||
|
"{\"query\": \"latest Anthropic model release notes\"}"}}, {"id": "call_yTBal9ofZzuo10j0pWqhHCSj",
|
||||||
|
"type": "function", "function": {"name": "parallel_local_search_three", "arguments":
|
||||||
|
"{\"query\": \"latest Gemini model release notes\"}"}}]}, {"role": "tool", "tool_call_id":
|
||||||
|
"call_NEvGoF86nhPQfXRoJd5SOyLd", "content": "[one] latest OpenAI model release
|
||||||
|
notes"}, {"role": "tool", "tool_call_id": "call_q8Q2du4gAMQLrGTgWgfwfbDZ", "content":
|
||||||
|
"[two] latest Anthropic model release notes"}, {"role": "tool", "tool_call_id":
|
||||||
|
"call_yTBal9ofZzuo10j0pWqhHCSj", "content": "[three] latest Gemini model release
|
||||||
|
notes"}, {"role": "user", "content": "Analyze the tool result. If requirements
|
||||||
|
are met, provide the Final Answer. Otherwise, call the next tool. Deliver only
|
||||||
|
the answer without meta-commentary."}], "stream": false, "tool_choice": "auto",
|
||||||
|
"tools": [{"function": {"name": "parallel_local_search_one", "description":
|
||||||
|
"Local search tool #1 for concurrency testing.", "parameters": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, "type":
|
||||||
|
"function"}, {"function": {"name": "parallel_local_search_two", "description":
|
||||||
|
"Local search tool #2 for concurrency testing.", "parameters": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, "type":
|
||||||
|
"function"}, {"function": {"name": "parallel_local_search_three", "description":
|
||||||
|
"Local search tool #3 for concurrency testing.", "parameters": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, "type":
|
||||||
|
"function"}]}'
|
||||||
|
headers:
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '3096'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
api-key:
|
||||||
|
- X-API-KEY-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://fake-azure-endpoint.openai.azure.com/openai/deployments/gpt-5-nano/chat/completions?api-version=2024-12-01-preview
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"protected_material_code":{"filtered":false,"detected":false},"protected_material_text":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"The
|
||||||
|
three tool results indicate the latest release notes are available for OpenAI
|
||||||
|
models, Anthropic models, and Gemini models.","refusal":null,"role":"assistant"}}],"created":1771459579,"id":"chatcmpl-DAlqdRtr8EefmFfazuh4jm7KvVxim","model":"gpt-5-nano-2025-08-07","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":null,"usage":{"completion_tokens":1826,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":1792,"rejected_prediction_tokens":0},"prompt_tokens":537,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":2363}}
|
||||||
|
|
||||||
|
'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '1333'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:06:31 GMT
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
apim-request-id:
|
||||||
|
- APIM-REQUEST-ID-XXX
|
||||||
|
azureml-model-session:
|
||||||
|
- AZUREML-MODEL-SESSION-XXX
|
||||||
|
x-accel-buffering:
|
||||||
|
- 'no'
|
||||||
|
x-content-type-options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
x-ms-client-request-id:
|
||||||
|
- X-MS-CLIENT-REQUEST-ID-XXX
|
||||||
|
x-ms-deployment-name:
|
||||||
|
- gpt-5-nano
|
||||||
|
x-ms-rai-invoked:
|
||||||
|
- 'true'
|
||||||
|
x-ms-region:
|
||||||
|
- X-MS-REGION-XXX
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "user", "content": [{"text": "\nCurrent Task: This
|
||||||
|
is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}]}], "inferenceConfig": {"stopSequences": ["\nObservation:"]},
|
||||||
|
"system": [{"text": "You are Parallel Tool Agent. You follow tool instructions
|
||||||
|
precisely.\nYour personal goal is: Use both tools exactly as instructed"}],
|
||||||
|
"toolConfig": {"tools": [{"toolSpec": {"name": "parallel_local_search_one",
|
||||||
|
"description": "Local search tool #1 for concurrency testing.", "inputSchema":
|
||||||
|
{"json": {"properties": {"query": {"description": "Search query", "title": "Query",
|
||||||
|
"type": "string"}}, "required": ["query"], "type": "object", "additionalProperties":
|
||||||
|
false}}}}, {"toolSpec": {"name": "parallel_local_search_two", "description":
|
||||||
|
"Local search tool #2 for concurrency testing.", "inputSchema": {"json": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}}},
|
||||||
|
{"toolSpec": {"name": "parallel_local_search_three", "description": "Local search
|
||||||
|
tool #3 for concurrency testing.", "inputSchema": {"json": {"properties": {"query":
|
||||||
|
{"description": "Search query", "title": "Query", "type": "string"}}, "required":
|
||||||
|
["query"], "type": "object", "additionalProperties": false}}}}]}}'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '1773'
|
||||||
|
Content-Type:
|
||||||
|
- !!binary |
|
||||||
|
YXBwbGljYXRpb24vanNvbg==
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
amz-sdk-invocation-id:
|
||||||
|
- AMZ-SDK-INVOCATION-ID-XXX
|
||||||
|
amz-sdk-request:
|
||||||
|
- !!binary |
|
||||||
|
YXR0ZW1wdD0x
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-amz-date:
|
||||||
|
- X-AMZ-DATE-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-haiku-20240307-v1%3A0/converse
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"message":"The security token included in the request is invalid."}'
|
||||||
|
headers:
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '68'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:00:08 GMT
|
||||||
|
x-amzn-ErrorType:
|
||||||
|
- UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/
|
||||||
|
x-amzn-RequestId:
|
||||||
|
- X-AMZN-REQUESTID-XXX
|
||||||
|
status:
|
||||||
|
code: 403
|
||||||
|
message: Forbidden
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "user", "content": [{"text": "\nCurrent Task: This
|
||||||
|
is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}]}], "inferenceConfig": {"stopSequences":
|
||||||
|
["\nObservation:"]}, "system": [{"text": "You are Parallel Tool Agent. You follow
|
||||||
|
tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"}], "toolConfig": {"tools": [{"toolSpec": {"name": "parallel_local_search_one",
|
||||||
|
"description": "Local search tool #1 for concurrency testing.", "inputSchema":
|
||||||
|
{"json": {"properties": {"query": {"description": "Search query", "title": "Query",
|
||||||
|
"type": "string"}}, "required": ["query"], "type": "object", "additionalProperties":
|
||||||
|
false}}}}, {"toolSpec": {"name": "parallel_local_search_two", "description":
|
||||||
|
"Local search tool #2 for concurrency testing.", "inputSchema": {"json": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}}},
|
||||||
|
{"toolSpec": {"name": "parallel_local_search_three", "description": "Local search
|
||||||
|
tool #3 for concurrency testing.", "inputSchema": {"json": {"properties": {"query":
|
||||||
|
{"description": "Search query", "title": "Query", "type": "string"}}, "required":
|
||||||
|
["query"], "type": "object", "additionalProperties": false}}}}]}}'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '1954'
|
||||||
|
Content-Type:
|
||||||
|
- !!binary |
|
||||||
|
YXBwbGljYXRpb24vanNvbg==
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
amz-sdk-invocation-id:
|
||||||
|
- AMZ-SDK-INVOCATION-ID-XXX
|
||||||
|
amz-sdk-request:
|
||||||
|
- !!binary |
|
||||||
|
YXR0ZW1wdD0x
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-amz-date:
|
||||||
|
- X-AMZ-DATE-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-haiku-20240307-v1%3A0/converse
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"message":"The security token included in the request is invalid."}'
|
||||||
|
headers:
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '68'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:00:07 GMT
|
||||||
|
x-amzn-ErrorType:
|
||||||
|
- UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/
|
||||||
|
x-amzn-RequestId:
|
||||||
|
- X-AMZN-REQUESTID-XXX
|
||||||
|
status:
|
||||||
|
code: 403
|
||||||
|
message: Forbidden
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "user", "content": [{"text": "\nCurrent Task: This
|
||||||
|
is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}]}, {"role": "user", "content":
|
||||||
|
[{"text": "\nCurrent Task: This is a tool-calling compliance test. In your next
|
||||||
|
assistant turn, emit exactly 3 tool calls in the same response (parallel tool
|
||||||
|
calls), in this order: 1) parallel_local_search_one(query=''latest OpenAI model
|
||||||
|
release notes''), 2) parallel_local_search_two(query=''latest Anthropic model
|
||||||
|
release notes''), 3) parallel_local_search_three(query=''latest Gemini model
|
||||||
|
release notes''). Do not call any other tools and do not answer before those
|
||||||
|
3 tool calls are emitted. After the tool results return, provide a one paragraph
|
||||||
|
summary.\n\nThis is the expected criteria for your final answer: A one sentence
|
||||||
|
summary of both tool outputs\nyou MUST return the actual complete content as
|
||||||
|
the final answer, not a summary."}]}], "inferenceConfig": {"stopSequences":
|
||||||
|
["\nObservation:"]}, "system": [{"text": "You are Parallel Tool Agent. You follow
|
||||||
|
tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed\n\nYou are Parallel Tool Agent. You follow tool instructions precisely.\nYour
|
||||||
|
personal goal is: Use both tools exactly as instructed"}], "toolConfig": {"tools":
|
||||||
|
[{"toolSpec": {"name": "parallel_local_search_one", "description": "Local search
|
||||||
|
tool #1 for concurrency testing.", "inputSchema": {"json": {"properties": {"query":
|
||||||
|
{"description": "Search query", "title": "Query", "type": "string"}}, "required":
|
||||||
|
["query"], "type": "object", "additionalProperties": false}}}}, {"toolSpec":
|
||||||
|
{"name": "parallel_local_search_two", "description": "Local search tool #2 for
|
||||||
|
concurrency testing.", "inputSchema": {"json": {"properties": {"query": {"description":
|
||||||
|
"Search query", "title": "Query", "type": "string"}}, "required": ["query"],
|
||||||
|
"type": "object", "additionalProperties": false}}}}, {"toolSpec": {"name": "parallel_local_search_three",
|
||||||
|
"description": "Local search tool #3 for concurrency testing.", "inputSchema":
|
||||||
|
{"json": {"properties": {"query": {"description": "Search query", "title": "Query",
|
||||||
|
"type": "string"}}, "required": ["query"], "type": "object", "additionalProperties":
|
||||||
|
false}}}}]}}'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '2855'
|
||||||
|
Content-Type:
|
||||||
|
- !!binary |
|
||||||
|
YXBwbGljYXRpb24vanNvbg==
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
amz-sdk-invocation-id:
|
||||||
|
- AMZ-SDK-INVOCATION-ID-XXX
|
||||||
|
amz-sdk-request:
|
||||||
|
- !!binary |
|
||||||
|
YXR0ZW1wdD0x
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-amz-date:
|
||||||
|
- X-AMZ-DATE-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-haiku-20240307-v1%3A0/converse
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"message":"The security token included in the request is invalid."}'
|
||||||
|
headers:
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '68'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:00:07 GMT
|
||||||
|
x-amzn-ErrorType:
|
||||||
|
- UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/
|
||||||
|
x-amzn-RequestId:
|
||||||
|
- X-AMZN-REQUESTID-XXX
|
||||||
|
status:
|
||||||
|
code: 403
|
||||||
|
message: Forbidden
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "user", "content": [{"text": "\nCurrent Task: This
|
||||||
|
is a tool-calling compliance test. In your next assistant turn, emit exactly
|
||||||
|
3 tool calls in the same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}]}, {"role": "user", "content":
|
||||||
|
[{"text": "\nCurrent Task: This is a tool-calling compliance test. In your next
|
||||||
|
assistant turn, emit exactly 3 tool calls in the same response (parallel tool
|
||||||
|
calls), in this order: 1) parallel_local_search_one(query=''latest OpenAI model
|
||||||
|
release notes''), 2) parallel_local_search_two(query=''latest Anthropic model
|
||||||
|
release notes''), 3) parallel_local_search_three(query=''latest Gemini model
|
||||||
|
release notes''). Do not call any other tools and do not answer before those
|
||||||
|
3 tool calls are emitted. After the tool results return, provide a one paragraph
|
||||||
|
summary.\n\nThis is the expected criteria for your final answer: A one sentence
|
||||||
|
summary of both tool outputs\nyou MUST return the actual complete content as
|
||||||
|
the final answer, not a summary."}]}, {"role": "user", "content": [{"text":
|
||||||
|
"\nCurrent Task: This is a tool-calling compliance test. In your next assistant
|
||||||
|
turn, emit exactly 3 tool calls in the same response (parallel tool calls),
|
||||||
|
in this order: 1) parallel_local_search_one(query=''latest OpenAI model release
|
||||||
|
notes''), 2) parallel_local_search_two(query=''latest Anthropic model release
|
||||||
|
notes''), 3) parallel_local_search_three(query=''latest Gemini model release
|
||||||
|
notes''). Do not call any other tools and do not answer before those 3 tool
|
||||||
|
calls are emitted. After the tool results return, provide a one paragraph summary.\n\nThis
|
||||||
|
is the expected criteria for your final answer: A one sentence summary of both
|
||||||
|
tool outputs\nyou MUST return the actual complete content as the final answer,
|
||||||
|
not a summary."}]}], "inferenceConfig": {"stopSequences": ["\nObservation:"]},
|
||||||
|
"system": [{"text": "You are Parallel Tool Agent. You follow tool instructions
|
||||||
|
precisely.\nYour personal goal is: Use both tools exactly as instructed\n\nYou
|
||||||
|
are Parallel Tool Agent. You follow tool instructions precisely.\nYour personal
|
||||||
|
goal is: Use both tools exactly as instructed\n\nYou are Parallel Tool Agent.
|
||||||
|
You follow tool instructions precisely.\nYour personal goal is: Use both tools
|
||||||
|
exactly as instructed"}], "toolConfig": {"tools": [{"toolSpec": {"name": "parallel_local_search_one",
|
||||||
|
"description": "Local search tool #1 for concurrency testing.", "inputSchema":
|
||||||
|
{"json": {"properties": {"query": {"description": "Search query", "title": "Query",
|
||||||
|
"type": "string"}}, "required": ["query"], "type": "object", "additionalProperties":
|
||||||
|
false}}}}, {"toolSpec": {"name": "parallel_local_search_two", "description":
|
||||||
|
"Local search tool #2 for concurrency testing.", "inputSchema": {"json": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}}},
|
||||||
|
{"toolSpec": {"name": "parallel_local_search_three", "description": "Local search
|
||||||
|
tool #3 for concurrency testing.", "inputSchema": {"json": {"properties": {"query":
|
||||||
|
{"description": "Search query", "title": "Query", "type": "string"}}, "required":
|
||||||
|
["query"], "type": "object", "additionalProperties": false}}}}]}}'
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '3756'
|
||||||
|
Content-Type:
|
||||||
|
- !!binary |
|
||||||
|
YXBwbGljYXRpb24vanNvbg==
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
amz-sdk-invocation-id:
|
||||||
|
- AMZ-SDK-INVOCATION-ID-XXX
|
||||||
|
amz-sdk-request:
|
||||||
|
- !!binary |
|
||||||
|
YXR0ZW1wdD0x
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
x-amz-date:
|
||||||
|
- X-AMZ-DATE-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-haiku-20240307-v1%3A0/converse
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"message":"The security token included in the request is invalid."}'
|
||||||
|
headers:
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '68'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 00:00:07 GMT
|
||||||
|
x-amzn-ErrorType:
|
||||||
|
- UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/
|
||||||
|
x-amzn-RequestId:
|
||||||
|
- X-AMZN-REQUESTID-XXX
|
||||||
|
status:
|
||||||
|
code: 403
|
||||||
|
message: Forbidden
|
||||||
|
version: 1
|
||||||
@@ -3,14 +3,14 @@ interactions:
|
|||||||
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
calculation\nyou MUST return the actual complete content as the final answer,
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"}],
|
not a summary."}], "role": "user"}], "systemInstruction": {"parts": [{"text":
|
||||||
"role": "user"}], "systemInstruction": {"parts": [{"text": "You are Math Assistant.
|
"You are Math Assistant. You are a helpful math assistant.\nYour personal goal
|
||||||
You are a helpful math assistant.\nYour personal goal is: Help users with mathematical
|
is: Help users with mathematical calculations"}], "role": "user"}, "tools":
|
||||||
calculations"}], "role": "user"}, "tools": [{"functionDeclarations": [{"description":
|
[{"functionDeclarations": [{"description": "Perform mathematical calculations.
|
||||||
"Perform mathematical calculations. Use this for any math operations.", "name":
|
Use this for any math operations.", "name": "calculator", "parameters_json_schema":
|
||||||
"calculator", "parameters": {"properties": {"expression": {"description": "Mathematical
|
{"properties": {"expression": {"description": "Mathematical expression to evaluate",
|
||||||
expression to evaluate", "title": "Expression", "type": "STRING"}}, "required":
|
"title": "Expression", "type": "string"}}, "required": ["expression"], "type":
|
||||||
["expression"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences":
|
"object", "additionalProperties": false}}]}], "generationConfig": {"stopSequences":
|
||||||
["\nObservation:"]}}'
|
["\nObservation:"]}}'
|
||||||
headers:
|
headers:
|
||||||
User-Agent:
|
User-Agent:
|
||||||
@@ -22,7 +22,7 @@ interactions:
|
|||||||
connection:
|
connection:
|
||||||
- keep-alive
|
- keep-alive
|
||||||
content-length:
|
content-length:
|
||||||
- '907'
|
- '892'
|
||||||
content-type:
|
content-type:
|
||||||
- application/json
|
- application/json
|
||||||
host:
|
host:
|
||||||
@@ -32,31 +32,31 @@ interactions:
|
|||||||
x-goog-api-key:
|
x-goog-api-key:
|
||||||
- X-GOOG-API-KEY-XXX
|
- X-GOOG-API-KEY-XXX
|
||||||
method: POST
|
method: POST
|
||||||
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent
|
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
|
||||||
response:
|
response:
|
||||||
body:
|
body:
|
||||||
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
||||||
[\n {\n \"functionCall\": {\n \"name\": \"calculator\",\n
|
[\n {\n \"functionCall\": {\n \"name\": \"calculator\",\n
|
||||||
\ \"args\": {\n \"expression\": \"15 * 8\"\n }\n
|
\ \"args\": {\n \"expression\": \"15 * 8\"\n }\n
|
||||||
\ }\n }\n ],\n \"role\": \"model\"\n },\n
|
\ },\n \"thoughtSignature\": \"Cp8DAb4+9vu74rJ0QQNTa6oMMh3QAlvx3cS4TL0I1od7EdQZtMBbsr5viQiTUR/LKj8nwPvtLjZxib5SXqmV0t2B2ZMdq1nqD62vLPD3i7tmUeRoysODfxomRGRhy/CPysMhobt5HWF1W/n6tNiQz3V36f0/dRx5yJeyN4tJL/RZePv77FUqywOfFlYOkOIyAkrE5LT6FicOjhHm/B9bGV/y7TNmN6TtwQDxoE9nU92Q/UNZ7rNyZE7aSR7KPJZuRXrrBBh+akt5dX5n6N9kGWkyRpWVgUox01+b22RSj4S/QY45IvadtmmkFk8DMVAtAnEiK0WazltC+TOdUJHwVgBD494fngoVcHU+R1yIJrVe7h6Ce3Ts5IYLrRCedDU3wW1ghn/hXx1nvTqQumpsGTGtE2v3KjF/7DmQA96WzB1X7+QUOF2J3pK9HemiKxAQl4U9fP2eNN8shvy2YykBlahWDujEwye7ji4wIWtNHbf0t+uFwGTQ3QruAKXvWB04ExjHM2I/8O9U5tOsH0cwPqnpFR2EaTqaPXXUllZ2K+DaaA==\"\n
|
||||||
\ \"finishReason\": \"STOP\",\n \"avgLogprobs\": -0.00062879999833447594\n
|
\ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\":
|
||||||
\ }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": 103,\n \"candidatesTokenCount\":
|
\"STOP\",\n \"index\": 0,\n \"finishMessage\": \"Model generated
|
||||||
7,\n \"totalTokenCount\": 110,\n \"promptTokensDetails\": [\n {\n
|
function call(s).\"\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
|
||||||
\ \"modality\": \"TEXT\",\n \"tokenCount\": 103\n }\n ],\n
|
115,\n \"candidatesTokenCount\": 17,\n \"totalTokenCount\": 227,\n \"promptTokensDetails\":
|
||||||
\ \"candidatesTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n
|
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 115\n
|
||||||
\ \"tokenCount\": 7\n }\n ]\n },\n \"modelVersion\": \"gemini-2.0-flash-exp\",\n
|
\ }\n ],\n \"thoughtsTokenCount\": 95\n },\n \"modelVersion\":
|
||||||
\ \"responseId\": \"PpByabfUHsih_uMPlu2ysAM\"\n}\n"
|
\"gemini-2.5-flash\",\n \"responseId\": \"Y1KWadvNMKz1jMcPiJeJmAI\"\n}\n"
|
||||||
headers:
|
headers:
|
||||||
Alt-Svc:
|
Alt-Svc:
|
||||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||||
Content-Type:
|
Content-Type:
|
||||||
- application/json; charset=UTF-8
|
- application/json; charset=UTF-8
|
||||||
Date:
|
Date:
|
||||||
- Thu, 22 Jan 2026 21:01:50 GMT
|
- Wed, 18 Feb 2026 23:59:32 GMT
|
||||||
Server:
|
Server:
|
||||||
- scaffolding on HTTPServer2
|
- scaffolding on HTTPServer2
|
||||||
Server-Timing:
|
Server-Timing:
|
||||||
- gfet4t7; dur=521
|
- gfet4t7; dur=956
|
||||||
Transfer-Encoding:
|
Transfer-Encoding:
|
||||||
- chunked
|
- chunked
|
||||||
Vary:
|
Vary:
|
||||||
@@ -76,18 +76,19 @@ interactions:
|
|||||||
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
calculation\nyou MUST return the actual complete content as the final answer,
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"}],
|
not a summary."}], "role": "user"}, {"parts": [{"functionCall": {"args": {"expression":
|
||||||
"role": "user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text":
|
"15 * 8"}, "name": "calculator"}}], "role": "model"}, {"parts": [{"functionResponse":
|
||||||
"The result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze
|
{"name": "calculator", "response": {"result": "The result of 15 * 8 is 120"}}}],
|
||||||
the tool result. If requirements are met, provide the Final Answer. Otherwise,
|
"role": "user"}, {"parts": [{"text": "Analyze the tool result. If requirements
|
||||||
call the next tool. Deliver only the answer without meta-commentary."}], "role":
|
are met, provide the Final Answer. Otherwise, call the next tool. Deliver only
|
||||||
"user"}], "systemInstruction": {"parts": [{"text": "You are Math Assistant.
|
the answer without meta-commentary."}], "role": "user"}], "systemInstruction":
|
||||||
You are a helpful math assistant.\nYour personal goal is: Help users with mathematical
|
{"parts": [{"text": "You are Math Assistant. You are a helpful math assistant.\nYour
|
||||||
calculations"}], "role": "user"}, "tools": [{"functionDeclarations": [{"description":
|
personal goal is: Help users with mathematical calculations"}], "role": "user"},
|
||||||
"Perform mathematical calculations. Use this for any math operations.", "name":
|
"tools": [{"functionDeclarations": [{"description": "Perform mathematical calculations.
|
||||||
"calculator", "parameters": {"properties": {"expression": {"description": "Mathematical
|
Use this for any math operations.", "name": "calculator", "parameters_json_schema":
|
||||||
expression to evaluate", "title": "Expression", "type": "STRING"}}, "required":
|
{"properties": {"expression": {"description": "Mathematical expression to evaluate",
|
||||||
["expression"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences":
|
"title": "Expression", "type": "string"}}, "required": ["expression"], "type":
|
||||||
|
"object", "additionalProperties": false}}]}], "generationConfig": {"stopSequences":
|
||||||
["\nObservation:"]}}'
|
["\nObservation:"]}}'
|
||||||
headers:
|
headers:
|
||||||
User-Agent:
|
User-Agent:
|
||||||
@@ -99,7 +100,7 @@ interactions:
|
|||||||
connection:
|
connection:
|
||||||
- keep-alive
|
- keep-alive
|
||||||
content-length:
|
content-length:
|
||||||
- '1219'
|
- '1326'
|
||||||
content-type:
|
content-type:
|
||||||
- application/json
|
- application/json
|
||||||
host:
|
host:
|
||||||
@@ -109,378 +110,28 @@ interactions:
|
|||||||
x-goog-api-key:
|
x-goog-api-key:
|
||||||
- X-GOOG-API-KEY-XXX
|
- X-GOOG-API-KEY-XXX
|
||||||
method: POST
|
method: POST
|
||||||
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent
|
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
|
||||||
response:
|
response:
|
||||||
body:
|
body:
|
||||||
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
||||||
[\n {\n \"functionCall\": {\n \"name\": \"calculator\",\n
|
[\n {\n \"text\": \"The result of 15 * 8 is 120\"\n }\n
|
||||||
\ \"args\": {\n \"expression\": \"15 * 8\"\n }\n
|
\ ],\n \"role\": \"model\"\n },\n \"finishReason\":
|
||||||
\ }\n }\n ],\n \"role\": \"model\"\n },\n
|
\"STOP\",\n \"index\": 0\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
|
||||||
\ \"finishReason\": \"STOP\",\n \"avgLogprobs\": -0.013549212898526872\n
|
191,\n \"candidatesTokenCount\": 14,\n \"totalTokenCount\": 205,\n \"promptTokensDetails\":
|
||||||
\ }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": 149,\n \"candidatesTokenCount\":
|
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 191\n
|
||||||
7,\n \"totalTokenCount\": 156,\n \"promptTokensDetails\": [\n {\n
|
\ }\n ]\n },\n \"modelVersion\": \"gemini-2.5-flash\",\n \"responseId\":
|
||||||
\ \"modality\": \"TEXT\",\n \"tokenCount\": 149\n }\n ],\n
|
\"ZFKWaf2BMM6MjMcP6P--kQM\"\n}\n"
|
||||||
\ \"candidatesTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n
|
|
||||||
\ \"tokenCount\": 7\n }\n ]\n },\n \"modelVersion\": \"gemini-2.0-flash-exp\",\n
|
|
||||||
\ \"responseId\": \"P5Byadc8kJT-4w_p99XQAQ\"\n}\n"
|
|
||||||
headers:
|
headers:
|
||||||
Alt-Svc:
|
Alt-Svc:
|
||||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||||
Content-Type:
|
Content-Type:
|
||||||
- application/json; charset=UTF-8
|
- application/json; charset=UTF-8
|
||||||
Date:
|
Date:
|
||||||
- Thu, 22 Jan 2026 21:01:51 GMT
|
- Wed, 18 Feb 2026 23:59:33 GMT
|
||||||
Server:
|
Server:
|
||||||
- scaffolding on HTTPServer2
|
- scaffolding on HTTPServer2
|
||||||
Server-Timing:
|
Server-Timing:
|
||||||
- gfet4t7; dur=444
|
- gfet4t7; dur=421
|
||||||
Transfer-Encoding:
|
|
||||||
- chunked
|
|
||||||
Vary:
|
|
||||||
- Origin
|
|
||||||
- X-Origin
|
|
||||||
- Referer
|
|
||||||
X-Content-Type-Options:
|
|
||||||
- X-CONTENT-TYPE-XXX
|
|
||||||
X-Frame-Options:
|
|
||||||
- X-FRAME-OPTIONS-XXX
|
|
||||||
X-XSS-Protection:
|
|
||||||
- '0'
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: OK
|
|
||||||
- request:
|
|
||||||
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"}],
|
|
||||||
"role": "user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text":
|
|
||||||
"The result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze
|
|
||||||
the tool result. If requirements are met, provide the Final Answer. Otherwise,
|
|
||||||
call the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}], "systemInstruction": {"parts": [{"text": "You are Math Assistant.
|
|
||||||
You are a helpful math assistant.\nYour personal goal is: Help users with mathematical
|
|
||||||
calculations"}], "role": "user"}, "tools": [{"functionDeclarations": [{"description":
|
|
||||||
"Perform mathematical calculations. Use this for any math operations.", "name":
|
|
||||||
"calculator", "parameters": {"properties": {"expression": {"description": "Mathematical
|
|
||||||
expression to evaluate", "title": "Expression", "type": "STRING"}}, "required":
|
|
||||||
["expression"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences":
|
|
||||||
["\nObservation:"]}}'
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- X-USER-AGENT-XXX
|
|
||||||
accept:
|
|
||||||
- '*/*'
|
|
||||||
accept-encoding:
|
|
||||||
- ACCEPT-ENCODING-XXX
|
|
||||||
connection:
|
|
||||||
- keep-alive
|
|
||||||
content-length:
|
|
||||||
- '1531'
|
|
||||||
content-type:
|
|
||||||
- application/json
|
|
||||||
host:
|
|
||||||
- generativelanguage.googleapis.com
|
|
||||||
x-goog-api-client:
|
|
||||||
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
|
||||||
x-goog-api-key:
|
|
||||||
- X-GOOG-API-KEY-XXX
|
|
||||||
method: POST
|
|
||||||
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent
|
|
||||||
response:
|
|
||||||
body:
|
|
||||||
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
|
||||||
[\n {\n \"functionCall\": {\n \"name\": \"calculator\",\n
|
|
||||||
\ \"args\": {\n \"expression\": \"15 * 8\"\n }\n
|
|
||||||
\ }\n }\n ],\n \"role\": \"model\"\n },\n
|
|
||||||
\ \"finishReason\": \"STOP\",\n \"avgLogprobs\": -0.0409286447933742\n
|
|
||||||
\ }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": 195,\n \"candidatesTokenCount\":
|
|
||||||
7,\n \"totalTokenCount\": 202,\n \"promptTokensDetails\": [\n {\n
|
|
||||||
\ \"modality\": \"TEXT\",\n \"tokenCount\": 195\n }\n ],\n
|
|
||||||
\ \"candidatesTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n
|
|
||||||
\ \"tokenCount\": 7\n }\n ]\n },\n \"modelVersion\": \"gemini-2.0-flash-exp\",\n
|
|
||||||
\ \"responseId\": \"P5Byadn5HOK6_uMPnvmXwAk\"\n}\n"
|
|
||||||
headers:
|
|
||||||
Alt-Svc:
|
|
||||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
|
||||||
Content-Type:
|
|
||||||
- application/json; charset=UTF-8
|
|
||||||
Date:
|
|
||||||
- Thu, 22 Jan 2026 21:01:51 GMT
|
|
||||||
Server:
|
|
||||||
- scaffolding on HTTPServer2
|
|
||||||
Server-Timing:
|
|
||||||
- gfet4t7; dur=503
|
|
||||||
Transfer-Encoding:
|
|
||||||
- chunked
|
|
||||||
Vary:
|
|
||||||
- Origin
|
|
||||||
- X-Origin
|
|
||||||
- Referer
|
|
||||||
X-Content-Type-Options:
|
|
||||||
- X-CONTENT-TYPE-XXX
|
|
||||||
X-Frame-Options:
|
|
||||||
- X-FRAME-OPTIONS-XXX
|
|
||||||
X-XSS-Protection:
|
|
||||||
- '0'
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: OK
|
|
||||||
- request:
|
|
||||||
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"}],
|
|
||||||
"role": "user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text":
|
|
||||||
"The result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze
|
|
||||||
the tool result. If requirements are met, provide the Final Answer. Otherwise,
|
|
||||||
call the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}], "systemInstruction": {"parts": [{"text": "You are Math Assistant.
|
|
||||||
You are a helpful math assistant.\nYour personal goal is: Help users with mathematical
|
|
||||||
calculations"}], "role": "user"}, "tools": [{"functionDeclarations": [{"description":
|
|
||||||
"Perform mathematical calculations. Use this for any math operations.", "name":
|
|
||||||
"calculator", "parameters": {"properties": {"expression": {"description": "Mathematical
|
|
||||||
expression to evaluate", "title": "Expression", "type": "STRING"}}, "required":
|
|
||||||
["expression"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences":
|
|
||||||
["\nObservation:"]}}'
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- X-USER-AGENT-XXX
|
|
||||||
accept:
|
|
||||||
- '*/*'
|
|
||||||
accept-encoding:
|
|
||||||
- ACCEPT-ENCODING-XXX
|
|
||||||
connection:
|
|
||||||
- keep-alive
|
|
||||||
content-length:
|
|
||||||
- '1843'
|
|
||||||
content-type:
|
|
||||||
- application/json
|
|
||||||
host:
|
|
||||||
- generativelanguage.googleapis.com
|
|
||||||
x-goog-api-client:
|
|
||||||
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
|
||||||
x-goog-api-key:
|
|
||||||
- X-GOOG-API-KEY-XXX
|
|
||||||
method: POST
|
|
||||||
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent
|
|
||||||
response:
|
|
||||||
body:
|
|
||||||
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
|
||||||
[\n {\n \"functionCall\": {\n \"name\": \"calculator\",\n
|
|
||||||
\ \"args\": {\n \"expression\": \"15 * 8\"\n }\n
|
|
||||||
\ }\n }\n ],\n \"role\": \"model\"\n },\n
|
|
||||||
\ \"finishReason\": \"STOP\",\n \"avgLogprobs\": -0.018002046006066457\n
|
|
||||||
\ }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": 241,\n \"candidatesTokenCount\":
|
|
||||||
7,\n \"totalTokenCount\": 248,\n \"promptTokensDetails\": [\n {\n
|
|
||||||
\ \"modality\": \"TEXT\",\n \"tokenCount\": 241\n }\n ],\n
|
|
||||||
\ \"candidatesTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n
|
|
||||||
\ \"tokenCount\": 7\n }\n ]\n },\n \"modelVersion\": \"gemini-2.0-flash-exp\",\n
|
|
||||||
\ \"responseId\": \"P5Byafi2PKbn_uMPtIbfuQI\"\n}\n"
|
|
||||||
headers:
|
|
||||||
Alt-Svc:
|
|
||||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
|
||||||
Content-Type:
|
|
||||||
- application/json; charset=UTF-8
|
|
||||||
Date:
|
|
||||||
- Thu, 22 Jan 2026 21:01:52 GMT
|
|
||||||
Server:
|
|
||||||
- scaffolding on HTTPServer2
|
|
||||||
Server-Timing:
|
|
||||||
- gfet4t7; dur=482
|
|
||||||
Transfer-Encoding:
|
|
||||||
- chunked
|
|
||||||
Vary:
|
|
||||||
- Origin
|
|
||||||
- X-Origin
|
|
||||||
- Referer
|
|
||||||
X-Content-Type-Options:
|
|
||||||
- X-CONTENT-TYPE-XXX
|
|
||||||
X-Frame-Options:
|
|
||||||
- X-FRAME-OPTIONS-XXX
|
|
||||||
X-XSS-Protection:
|
|
||||||
- '0'
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: OK
|
|
||||||
- request:
|
|
||||||
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"}],
|
|
||||||
"role": "user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text":
|
|
||||||
"The result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze
|
|
||||||
the tool result. If requirements are met, provide the Final Answer. Otherwise,
|
|
||||||
call the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}], "systemInstruction": {"parts": [{"text": "You are Math Assistant.
|
|
||||||
You are a helpful math assistant.\nYour personal goal is: Help users with mathematical
|
|
||||||
calculations"}], "role": "user"}, "tools": [{"functionDeclarations": [{"description":
|
|
||||||
"Perform mathematical calculations. Use this for any math operations.", "name":
|
|
||||||
"calculator", "parameters": {"properties": {"expression": {"description": "Mathematical
|
|
||||||
expression to evaluate", "title": "Expression", "type": "STRING"}}, "required":
|
|
||||||
["expression"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences":
|
|
||||||
["\nObservation:"]}}'
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- X-USER-AGENT-XXX
|
|
||||||
accept:
|
|
||||||
- '*/*'
|
|
||||||
accept-encoding:
|
|
||||||
- ACCEPT-ENCODING-XXX
|
|
||||||
connection:
|
|
||||||
- keep-alive
|
|
||||||
content-length:
|
|
||||||
- '2155'
|
|
||||||
content-type:
|
|
||||||
- application/json
|
|
||||||
host:
|
|
||||||
- generativelanguage.googleapis.com
|
|
||||||
x-goog-api-client:
|
|
||||||
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
|
||||||
x-goog-api-key:
|
|
||||||
- X-GOOG-API-KEY-XXX
|
|
||||||
method: POST
|
|
||||||
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent
|
|
||||||
response:
|
|
||||||
body:
|
|
||||||
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
|
||||||
[\n {\n \"functionCall\": {\n \"name\": \"calculator\",\n
|
|
||||||
\ \"args\": {\n \"expression\": \"15 * 8\"\n }\n
|
|
||||||
\ }\n }\n ],\n \"role\": \"model\"\n },\n
|
|
||||||
\ \"finishReason\": \"STOP\",\n \"avgLogprobs\": -0.10329001290457589\n
|
|
||||||
\ }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": 287,\n \"candidatesTokenCount\":
|
|
||||||
7,\n \"totalTokenCount\": 294,\n \"promptTokensDetails\": [\n {\n
|
|
||||||
\ \"modality\": \"TEXT\",\n \"tokenCount\": 287\n }\n ],\n
|
|
||||||
\ \"candidatesTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n
|
|
||||||
\ \"tokenCount\": 7\n }\n ]\n },\n \"modelVersion\": \"gemini-2.0-flash-exp\",\n
|
|
||||||
\ \"responseId\": \"QJByaamVIP_g_uMPt6mI0Qg\"\n}\n"
|
|
||||||
headers:
|
|
||||||
Alt-Svc:
|
|
||||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
|
||||||
Content-Type:
|
|
||||||
- application/json; charset=UTF-8
|
|
||||||
Date:
|
|
||||||
- Thu, 22 Jan 2026 21:01:52 GMT
|
|
||||||
Server:
|
|
||||||
- scaffolding on HTTPServer2
|
|
||||||
Server-Timing:
|
|
||||||
- gfet4t7; dur=534
|
|
||||||
Transfer-Encoding:
|
|
||||||
- chunked
|
|
||||||
Vary:
|
|
||||||
- Origin
|
|
||||||
- X-Origin
|
|
||||||
- Referer
|
|
||||||
X-Content-Type-Options:
|
|
||||||
- X-CONTENT-TYPE-XXX
|
|
||||||
X-Frame-Options:
|
|
||||||
- X-FRAME-OPTIONS-XXX
|
|
||||||
X-XSS-Protection:
|
|
||||||
- '0'
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: OK
|
|
||||||
- request:
|
|
||||||
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: Calculate what is 15
|
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"}],
|
|
||||||
"role": "user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text":
|
|
||||||
"The result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze
|
|
||||||
the tool result. If requirements are met, provide the Final Answer. Otherwise,
|
|
||||||
call the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}, {"parts": [{"text": ""}], "role": "model"}, {"parts": [{"text": "The
|
|
||||||
result of 15 * 8 is 120"}], "role": "user"}, {"parts": [{"text": "Analyze the
|
|
||||||
tool result. If requirements are met, provide the Final Answer. Otherwise, call
|
|
||||||
the next tool. Deliver only the answer without meta-commentary."}], "role":
|
|
||||||
"user"}], "systemInstruction": {"parts": [{"text": "You are Math Assistant.
|
|
||||||
You are a helpful math assistant.\nYour personal goal is: Help users with mathematical
|
|
||||||
calculations"}], "role": "user"}, "tools": [{"functionDeclarations": [{"description":
|
|
||||||
"Perform mathematical calculations. Use this for any math operations.", "name":
|
|
||||||
"calculator", "parameters": {"properties": {"expression": {"description": "Mathematical
|
|
||||||
expression to evaluate", "title": "Expression", "type": "STRING"}}, "required":
|
|
||||||
["expression"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences":
|
|
||||||
["\nObservation:"]}}'
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- X-USER-AGENT-XXX
|
|
||||||
accept:
|
|
||||||
- '*/*'
|
|
||||||
accept-encoding:
|
|
||||||
- ACCEPT-ENCODING-XXX
|
|
||||||
connection:
|
|
||||||
- keep-alive
|
|
||||||
content-length:
|
|
||||||
- '2467'
|
|
||||||
content-type:
|
|
||||||
- application/json
|
|
||||||
host:
|
|
||||||
- generativelanguage.googleapis.com
|
|
||||||
x-goog-api-client:
|
|
||||||
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
|
||||||
x-goog-api-key:
|
|
||||||
- X-GOOG-API-KEY-XXX
|
|
||||||
method: POST
|
|
||||||
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent
|
|
||||||
response:
|
|
||||||
body:
|
|
||||||
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
|
||||||
[\n {\n \"text\": \"120\\n\"\n }\n ],\n
|
|
||||||
\ \"role\": \"model\"\n },\n \"finishReason\": \"STOP\",\n
|
|
||||||
\ \"avgLogprobs\": -0.0097615998238325119\n }\n ],\n \"usageMetadata\":
|
|
||||||
{\n \"promptTokenCount\": 333,\n \"candidatesTokenCount\": 4,\n \"totalTokenCount\":
|
|
||||||
337,\n \"promptTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n
|
|
||||||
\ \"tokenCount\": 333\n }\n ],\n \"candidatesTokensDetails\":
|
|
||||||
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 4\n }\n
|
|
||||||
\ ]\n },\n \"modelVersion\": \"gemini-2.0-flash-exp\",\n \"responseId\":
|
|
||||||
\"QZByaZHABO-i_uMP58aYqAk\"\n}\n"
|
|
||||||
headers:
|
|
||||||
Alt-Svc:
|
|
||||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
|
||||||
Content-Type:
|
|
||||||
- application/json; charset=UTF-8
|
|
||||||
Date:
|
|
||||||
- Thu, 22 Jan 2026 21:01:53 GMT
|
|
||||||
Server:
|
|
||||||
- scaffolding on HTTPServer2
|
|
||||||
Server-Timing:
|
|
||||||
- gfet4t7; dur=412
|
|
||||||
Transfer-Encoding:
|
Transfer-Encoding:
|
||||||
- chunked
|
- chunked
|
||||||
Vary:
|
Vary:
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}], "role": "user"}], "systemInstruction": {"parts": [{"text":
|
||||||
|
"You are Parallel Tool Agent. You follow tool instructions precisely.\nYour
|
||||||
|
personal goal is: Use both tools exactly as instructed"}], "role": "user"},
|
||||||
|
"tools": [{"functionDeclarations": [{"description": "Local search tool #1 for
|
||||||
|
concurrency testing.", "name": "parallel_local_search_one", "parameters_json_schema":
|
||||||
|
{"properties": {"query": {"description": "Search query", "title": "Query", "type":
|
||||||
|
"string"}}, "required": ["query"], "type": "object", "additionalProperties":
|
||||||
|
false}}, {"description": "Local search tool #2 for concurrency testing.", "name":
|
||||||
|
"parallel_local_search_two", "parameters_json_schema": {"properties": {"query":
|
||||||
|
{"description": "Search query", "title": "Query", "type": "string"}}, "required":
|
||||||
|
["query"], "type": "object", "additionalProperties": false}}, {"description":
|
||||||
|
"Local search tool #3 for concurrency testing.", "name": "parallel_local_search_three",
|
||||||
|
"parameters_json_schema": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}]}], "generationConfig": {"stopSequences": ["\nObservation:"]}}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- '*/*'
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '1783'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- generativelanguage.googleapis.com
|
||||||
|
x-goog-api-client:
|
||||||
|
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
||||||
|
x-goog-api-key:
|
||||||
|
- X-GOOG-API-KEY-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
||||||
|
[\n {\n \"functionCall\": {\n \"name\": \"parallel_local_search_one\",\n
|
||||||
|
\ \"args\": {\n \"query\": \"latest OpenAI model
|
||||||
|
release notes\"\n }\n },\n \"thoughtSignature\":
|
||||||
|
\"CrICAb4+9vtrrkiSatPyOs7fssb9akcgCIiQdJKp/k+hcEZVNFvU/H0e4FFmLIhTCPRyHxmU+AQPtBZ5vg6y9ZCcv11RdcWgYW8rPQzCnC+YTUxPAfDzaObky1QsL5pl9+yglQqVoVM31ZcnoiH02z85pwAv6TSJxdJZEekW6XwcIrCoHNCgY3ghHFEd3y3wLJ5JWL7wmiRNTC9TCT8aJHXKFohYrb+4JMULCx8BqKVxOucZPiDHA8GsoqSlzkYEe2xCh9oSdaZpCFrxhZ9bwoVDbVmPrjaq2hj5BoJ5hNxscHJ/E0EOl4ogeKZW+hIVfdzpjAFZW9Oejkb9G4ZSLbxXsoO7x8bi4LHFRABniGrWvNuOOH0Udh4t57oXHXZO4u5NNTood/GkJGcP+aHqUAH1fwqL\"\n
|
||||||
|
\ },\n {\n \"functionCall\": {\n \"name\":
|
||||||
|
\"parallel_local_search_two\",\n \"args\": {\n \"query\":
|
||||||
|
\"latest Anthropic model release notes\"\n }\n }\n
|
||||||
|
\ },\n {\n \"functionCall\": {\n \"name\":
|
||||||
|
\"parallel_local_search_three\",\n \"args\": {\n \"query\":
|
||||||
|
\"latest Gemini model release notes\"\n }\n }\n }\n
|
||||||
|
\ ],\n \"role\": \"model\"\n },\n \"finishReason\":
|
||||||
|
\"STOP\",\n \"index\": 0,\n \"finishMessage\": \"Model generated
|
||||||
|
function call(s).\"\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
|
||||||
|
291,\n \"candidatesTokenCount\": 70,\n \"totalTokenCount\": 428,\n \"promptTokensDetails\":
|
||||||
|
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 291\n
|
||||||
|
\ }\n ],\n \"thoughtsTokenCount\": 67\n },\n \"modelVersion\":
|
||||||
|
\"gemini-2.5-flash\",\n \"responseId\": \"alKWacytCLi5jMcPhISaoAI\"\n}\n"
|
||||||
|
headers:
|
||||||
|
Alt-Svc:
|
||||||
|
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=UTF-8
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:59:39 GMT
|
||||||
|
Server:
|
||||||
|
- scaffolding on HTTPServer2
|
||||||
|
Server-Timing:
|
||||||
|
- gfet4t7; dur=999
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
Vary:
|
||||||
|
- Origin
|
||||||
|
- X-Origin
|
||||||
|
- Referer
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
X-Frame-Options:
|
||||||
|
- X-FRAME-OPTIONS-XXX
|
||||||
|
X-XSS-Protection:
|
||||||
|
- '0'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}], "role": "user"}, {"parts": [{"functionCall": {"args":
|
||||||
|
{"query": "latest OpenAI model release notes"}, "name": "parallel_local_search_one"},
|
||||||
|
"thoughtSignature": "CrICAb4-9vtrrkiSatPyOs7fssb9akcgCIiQdJKp_k-hcEZVNFvU_H0e4FFmLIhTCPRyHxmU-AQPtBZ5vg6y9ZCcv11RdcWgYW8rPQzCnC-YTUxPAfDzaObky1QsL5pl9-yglQqVoVM31ZcnoiH02z85pwAv6TSJxdJZEekW6XwcIrCoHNCgY3ghHFEd3y3wLJ5JWL7wmiRNTC9TCT8aJHXKFohYrb-4JMULCx8BqKVxOucZPiDHA8GsoqSlzkYEe2xCh9oSdaZpCFrxhZ9bwoVDbVmPrjaq2hj5BoJ5hNxscHJ_E0EOl4ogeKZW-hIVfdzpjAFZW9Oejkb9G4ZSLbxXsoO7x8bi4LHFRABniGrWvNuOOH0Udh4t57oXHXZO4u5NNTood_GkJGcP-aHqUAH1fwqL"},
|
||||||
|
{"functionCall": {"args": {"query": "latest Anthropic model release notes"},
|
||||||
|
"name": "parallel_local_search_two"}}, {"functionCall": {"args": {"query": "latest
|
||||||
|
Gemini model release notes"}, "name": "parallel_local_search_three"}}], "role":
|
||||||
|
"model"}, {"parts": [{"functionResponse": {"name": "parallel_local_search_one",
|
||||||
|
"response": {"result": "[one] latest OpenAI model release notes"}}}], "role":
|
||||||
|
"user"}, {"parts": [{"functionResponse": {"name": "parallel_local_search_two",
|
||||||
|
"response": {"result": "[two] latest Anthropic model release notes"}}}], "role":
|
||||||
|
"user"}, {"parts": [{"functionResponse": {"name": "parallel_local_search_three",
|
||||||
|
"response": {"result": "[three] latest Gemini model release notes"}}}], "role":
|
||||||
|
"user"}], "systemInstruction": {"parts": [{"text": "You are Parallel Tool Agent.
|
||||||
|
You follow tool instructions precisely.\nYour personal goal is: Use both tools
|
||||||
|
exactly as instructed"}], "role": "user"}, "tools": [{"functionDeclarations":
|
||||||
|
[{"description": "Local search tool #1 for concurrency testing.", "name": "parallel_local_search_one",
|
||||||
|
"parameters_json_schema": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}, {"description": "Local search tool #2 for concurrency
|
||||||
|
testing.", "name": "parallel_local_search_two", "parameters_json_schema": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, {"description":
|
||||||
|
"Local search tool #3 for concurrency testing.", "name": "parallel_local_search_three",
|
||||||
|
"parameters_json_schema": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}]}], "generationConfig": {"stopSequences": ["\nObservation:"]}}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- '*/*'
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '3071'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- generativelanguage.googleapis.com
|
||||||
|
x-goog-api-client:
|
||||||
|
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
||||||
|
x-goog-api-key:
|
||||||
|
- X-GOOG-API-KEY-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
||||||
|
[\n {\n \"text\": \"Here is a summary of the latest model
|
||||||
|
release notes: I have retrieved information regarding the latest OpenAI model
|
||||||
|
release notes, the latest Anthropic model release notes, and the latest Gemini
|
||||||
|
model release notes. The specific details of these release notes are available
|
||||||
|
through the respective tool outputs.\",\n \"thoughtSignature\":
|
||||||
|
\"CsoBAb4+9vtPvWFM08lR1S4QrLN+Z1+Zpf04Y/bC8tjOpnxz3EEvHyRNEwkslUX5pftBi8J78Xk4/FUER0xjJZc8clUObTvayxLNup4h1JwJ5ZdatulInNGTEieFnF4w8KjSFB/vqNCZvXWZbiLkpzqAnsoAIf0x4VmMN11V0Ozo+3f2QftD+iBrfu3g21UI5tbG0Z+0QHxjRVKXrQOp7dmoZPzaxI0zalfDEI+A2jGpVl/VvauVNv0jQn0yItcA5tkVeWLq6717CjNoig==\"\n
|
||||||
|
\ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\":
|
||||||
|
\"STOP\",\n \"index\": 0\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
|
||||||
|
435,\n \"candidatesTokenCount\": 54,\n \"totalTokenCount\": 524,\n \"promptTokensDetails\":
|
||||||
|
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 435\n
|
||||||
|
\ }\n ],\n \"thoughtsTokenCount\": 35\n },\n \"modelVersion\":
|
||||||
|
\"gemini-2.5-flash\",\n \"responseId\": \"bFKWaZOZCqCvjMcPvvGNgAc\"\n}\n"
|
||||||
|
headers:
|
||||||
|
Alt-Svc:
|
||||||
|
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=UTF-8
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:59:41 GMT
|
||||||
|
Server:
|
||||||
|
- scaffolding on HTTPServer2
|
||||||
|
Server-Timing:
|
||||||
|
- gfet4t7; dur=967
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
Vary:
|
||||||
|
- Origin
|
||||||
|
- X-Origin
|
||||||
|
- Referer
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
X-Frame-Options:
|
||||||
|
- X-FRAME-OPTIONS-XXX
|
||||||
|
X-XSS-Protection:
|
||||||
|
- '0'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}], "role": "user"}], "systemInstruction":
|
||||||
|
{"parts": [{"text": "You are Parallel Tool Agent. You follow tool instructions
|
||||||
|
precisely.\nYour personal goal is: Use both tools exactly as instructed"}],
|
||||||
|
"role": "user"}, "tools": [{"functionDeclarations": [{"description": "Local
|
||||||
|
search tool #1 for concurrency testing.", "name": "parallel_local_search_one",
|
||||||
|
"parameters_json_schema": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}, {"description": "Local search tool #2 for concurrency
|
||||||
|
testing.", "name": "parallel_local_search_two", "parameters_json_schema": {"properties":
|
||||||
|
{"query": {"description": "Search query", "title": "Query", "type": "string"}},
|
||||||
|
"required": ["query"], "type": "object", "additionalProperties": false}}, {"description":
|
||||||
|
"Local search tool #3 for concurrency testing.", "name": "parallel_local_search_three",
|
||||||
|
"parameters_json_schema": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}]}], "generationConfig": {"stopSequences": ["\nObservation:"]}}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- '*/*'
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '1964'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- generativelanguage.googleapis.com
|
||||||
|
x-goog-api-client:
|
||||||
|
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
||||||
|
x-goog-api-key:
|
||||||
|
- X-GOOG-API-KEY-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
||||||
|
[\n {\n \"functionCall\": {\n \"name\": \"parallel_local_search_one\",\n
|
||||||
|
\ \"args\": {\n \"query\": \"latest OpenAI model
|
||||||
|
release notes\"\n }\n },\n \"thoughtSignature\":
|
||||||
|
\"CuMEAb4+9vu1V1iOC9o/a8+jQqow8F4RTrjlnjnDCwsisMHLLJ+Wj3pZxbFDeIjCJe9pa6+14InyYHh/ezgHrv+xPGIJtX9pJQatDCBAfCmcZ3fDipVIMAHLcl0Q660EVuZ+vRgvNhPSau+uSN9u303wJsaKvdzOQnfww2LfLtJMNtOhSHfkfhfw2bkBOtMa5/FuLqKSr6m94dSdE7HShR6+jLMLbiSXkBLWsRp0jGl85Wvd0hoA7dUyq+uIuyOBr5Myo9uMrLbxfnrRRbPMorOpYTCmHK0HE8mEBRjzh1hNwcBcfRL0VcgA2UnBIurStIeVbq51BJQ1UOq6r1wVi50Wdh1GjIQ/iN9C15T1Ql3adjom5QbmY+XY08RJOiNyVplh1YQ0qlWCVHEpueEfdzcIB+BUauVrLNqBcBr5g6ekO5QZCAdt7PLerQU8jhKjDQy367jCKQyaHir0GmAISS8RlZ8tkLKNZlZhd11D76ui6X8ep9yznViBbqH0AS1R2hMm+ielMVFjhidglTMjqB0X+yk1K2eZXkc+R/xsXRPlnlZWRygnV+IbU8RAnZWtneM464Wccmc1scfF45GKiji5bLYO7Zx+ZF8mSLcQaC8M3z121D6VbFonhaIdkJ3Wb7nI2vEyxFjdinVk3/P0zL8nu3nHeqQviTrQIoHMsZk0yPyqu9NWxg3wGJL5pbcaQh87ROQuTsInkuzzEr0QMzjw9W5iquhMh4/Wy/OKXAgf3maQB9Jb4HoHZlc0io+KYqewFSVx2BvqXbqJbIrTkTo6XRTbK7dkwlCbMmE1wKIwjrrzZQI=\"\n
|
||||||
|
\ },\n {\n \"functionCall\": {\n \"name\":
|
||||||
|
\"parallel_local_search_two\",\n \"args\": {\n \"query\":
|
||||||
|
\"latest Anthropic model release notes\"\n }\n }\n
|
||||||
|
\ },\n {\n \"functionCall\": {\n \"name\":
|
||||||
|
\"parallel_local_search_three\",\n \"args\": {\n \"query\":
|
||||||
|
\"latest Gemini model release notes\"\n }\n }\n }\n
|
||||||
|
\ ],\n \"role\": \"model\"\n },\n \"finishReason\":
|
||||||
|
\"STOP\",\n \"index\": 0,\n \"finishMessage\": \"Model generated
|
||||||
|
function call(s).\"\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
|
||||||
|
327,\n \"candidatesTokenCount\": 70,\n \"totalTokenCount\": 536,\n \"promptTokensDetails\":
|
||||||
|
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 327\n
|
||||||
|
\ }\n ],\n \"thoughtsTokenCount\": 139\n },\n \"modelVersion\":
|
||||||
|
\"gemini-2.5-flash\",\n \"responseId\": \"ZVKWabziF7bcjMcP3r2SuAg\"\n}\n"
|
||||||
|
headers:
|
||||||
|
Alt-Svc:
|
||||||
|
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=UTF-8
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:59:34 GMT
|
||||||
|
Server:
|
||||||
|
- scaffolding on HTTPServer2
|
||||||
|
Server-Timing:
|
||||||
|
- gfet4t7; dur=1262
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
Vary:
|
||||||
|
- Origin
|
||||||
|
- X-Origin
|
||||||
|
- Referer
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
X-Frame-Options:
|
||||||
|
- X-FRAME-OPTIONS-XXX
|
||||||
|
X-XSS-Protection:
|
||||||
|
- '0'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}], "role": "user"}, {"parts": [{"functionCall":
|
||||||
|
{"args": {"query": "latest OpenAI model release notes"}, "name": "parallel_local_search_one"}},
|
||||||
|
{"functionCall": {"args": {"query": "latest Anthropic model release notes"},
|
||||||
|
"name": "parallel_local_search_two"}}, {"functionCall": {"args": {"query": "latest
|
||||||
|
Gemini model release notes"}, "name": "parallel_local_search_three"}}], "role":
|
||||||
|
"model"}, {"parts": [{"functionResponse": {"name": "parallel_local_search_one",
|
||||||
|
"response": {"result": "[one] latest OpenAI model release notes"}}}], "role":
|
||||||
|
"user"}, {"parts": [{"functionResponse": {"name": "parallel_local_search_two",
|
||||||
|
"response": {"result": "[two] latest Anthropic model release notes"}}}], "role":
|
||||||
|
"user"}, {"parts": [{"functionResponse": {"name": "parallel_local_search_three",
|
||||||
|
"response": {"result": "[three] latest Gemini model release notes"}}}], "role":
|
||||||
|
"user"}, {"parts": [{"text": "Analyze the tool result. If requirements are met,
|
||||||
|
provide the Final Answer. Otherwise, call the next tool. Deliver only the answer
|
||||||
|
without meta-commentary."}], "role": "user"}], "systemInstruction": {"parts":
|
||||||
|
[{"text": "You are Parallel Tool Agent. You follow tool instructions precisely.\nYour
|
||||||
|
personal goal is: Use both tools exactly as instructed"}], "role": "user"},
|
||||||
|
"tools": [{"functionDeclarations": [{"description": "Local search tool #1 for
|
||||||
|
concurrency testing.", "name": "parallel_local_search_one", "parameters_json_schema":
|
||||||
|
{"properties": {"query": {"description": "Search query", "title": "Query", "type":
|
||||||
|
"string"}}, "required": ["query"], "type": "object", "additionalProperties":
|
||||||
|
false}}, {"description": "Local search tool #2 for concurrency testing.", "name":
|
||||||
|
"parallel_local_search_two", "parameters_json_schema": {"properties": {"query":
|
||||||
|
{"description": "Search query", "title": "Query", "type": "string"}}, "required":
|
||||||
|
["query"], "type": "object", "additionalProperties": false}}, {"description":
|
||||||
|
"Local search tool #3 for concurrency testing.", "name": "parallel_local_search_three",
|
||||||
|
"parameters_json_schema": {"properties": {"query": {"description": "Search query",
|
||||||
|
"title": "Query", "type": "string"}}, "required": ["query"], "type": "object",
|
||||||
|
"additionalProperties": false}}]}], "generationConfig": {"stopSequences": ["\nObservation:"]}}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- '*/*'
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '3014'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- generativelanguage.googleapis.com
|
||||||
|
x-goog-api-client:
|
||||||
|
- google-genai-sdk/1.49.0 gl-python/3.13.3
|
||||||
|
x-goog-api-key:
|
||||||
|
- X-GOOG-API-KEY-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
|
||||||
|
[\n {\n \"text\": \"The search results indicate the latest
|
||||||
|
model release notes for OpenAI, Anthropic, and Gemini are: [one] latest OpenAI
|
||||||
|
model release notes[two] latest Anthropic model release notes[three] latest
|
||||||
|
Gemini model release notes.\",\n \"thoughtSignature\": \"CsUPAb4+9vs4hkuatQAakl1FSHx5DIde9nHYobJdlWs2HEzES9gHn7uwjMIlFPTzJUbnZqxpAK93hqsCofdfGANr8dwK+/IbZAiMSikpAq2ZjEbWADjfalU3ke4LcQMh6TEYFVGz1QCinjne3jZx5jOVaL8YdAtjOYnBZWA6KqdvfKjD7+Ct/BLoEqvu4LW6kxhXQgcV+D3M1QxGlr1dxpajj4wyYFI9LXchE2vCdAMPYTkPQ4WPbS3xjz0jJb6qFAwwg+BY5kGemkWWVHsvq28t09pd7FEH0bod5cEpR65qEefpJfhHsXYqmOwHDkfNePYnYC+5qmn7kvkN+fhF41SoMRZahMZGDjIo+q6vvru3eXKmZiuLsrh8AqQIks/4S3sSuxt16ogYKE+LlFxml2ygXFPww59nRAtc+xK6VW8jB2vyv9Eo5cpnG9ZBv1dOznJnmj4AWA1ddMlp+yq8AdaboTSo5dysYMwFcSXS3kuU+xi92dC+7GqZZbDr5frvnc+MnSuzYwHhNjSQqvTo5DKGit53zDwlFJT74kLBXk36BOFQp4xlfs+BpKkw11bow6qQoTvC68D023ZHami+McO1WYBDoO5CrDoosU8fAYljqaGArBoMlssF4O7VKHEaEbEZnYCr0Wxo6XP/mtPIpHQE4OyCz/GAJSJtQv1hO7DNCMzpSpkLyuemB1SOZGl3mlLQhosh3TAGP0xgqmHpKccdCSWoXGWjO48VluFuV9E1FwW1Xi++XhMRcUaljJXPZaNVjGcAG1uAxeVkUMsY8tBvQ0vaumUK2jkzbyQTWeStEWwl1yKmklI8JDXske/k6tYJOyF+8t0mF7oCEqNHSNicj7TomihpPlVjNl1Mm4l5fvwlKtAPJwiKrchCunlZB3uGN1AR0h0Hvznffutc/lV/FWFbNgFAaNJZKRs40vMk1xmRZyH2rs+Ob2fZriQ3BSwzzNeiwDLXxm0m/ytOai+K9ObFuC/IEh5fJfvQbNeo3TmiCAMCZPNXMDtlOyLqQzzKwmMFH4c53Ol+kkTiuAKECNQR1dOCufAL0U5lzEUFRxFvOq67lp6xqG8m+WzCIkbnF8QyJHfujtXVMJACaevUkM7+kAVyTwETEKQsanp0tBwzV42ieChp/h7pivcC++cFXdSG5dvR94BgkHmtpC9+jfNH32RREPLuyWfU5aBXiOkxjRs9fDexAFjrkGjM18I+jqHZNeuUR20BKe2jFsU8xJS3Fa4eXabm/YPL1t8R5jr572Ch/r4bspFp8MQ5RcFo8Nn/HiBmW8uZ2BcLEY1RPWUBvxVhfvh/hNxaRKu21x8vGz72RoiNuOjNbeADYAaBJqBGLp0MALxZ/rnXPzDLQUt6Mv07fWHAZr5p3r/skleot25lr2Tcl4qJCPM4/cfs6U0x4CY26ktBiCs4bWKqSEV1Q05nf5kpxVOIRSTgxqFOj/rWIAF3uw7mvsuRKd3YXILV5OrvEoETdQvf7BdYPbQbIQYDf7DBKhf51O8RKQgcfl6mVQswamdJ+PyqLbozTkFCjXMKI0PwJdy8tfKfCeeEe0TbOXSfeTczKQkL8WyWkBg4tS81JnWAVzfVlNjbvo/fk+wv7FyfJJS1HJGlxZ0kUlWi1369rSlldYPoSqopuekOxtYnpYpz92y/jVLNQXE1IVLqWYh9o3gTwjeyaHG7fCaWF2QRGrCUvejT8eJjevhj/sgadjPVcEP5o7Zcw5yTBCgc0+FX1j5KpCmfZ/dVvT4iIX8bOkhxjHQ8ifOx39BMM4EObgCA+g+BFN+Ra7kOf4hJ6tPNhqvJa4E4fyISlVrRiBqSt59ZkuLyWuY9SYy0nvbklP30WDUHSAvcuEwVMSuT524afHISfO/+tSgE7JAKzEPSOoVO3Z5NS9kcAqHuBSe/LL4XJbCKF9Oggm9/gwdAulnBANd4ydQ/raTPE/QUu/CGqqGhBd+wo8x0Jg/BMZWkwhz0fEzsh+OjnrEkHv4QIqZ9v/j1Rv9uc+cDeK7eGi62okGLrPFX2pNQtsZRdUM9aBSlTBUVSdCDpkvieENzLnR257EDZy1EV2HxGRfOFZVVdaW1n8XvL73pcFoQ5XABpfYuigOS8i4S8g43Qfe77GosnuXR5rcJCrL03q3hptb97K5ysKFLgumsaaWo92MBhZYKvQ6SwStgyWRlb22uQGQJYsS8OTD/uVNiQzFjOMsR/l71c9RI1Eb7SQJT6WWvL1YhA7sQw/lQf8soLKfWshoky6mMrGopjRak8xHpJe5VWbqK8PK6iXDd403JrHICyh4M3FpEja3eX2V3SN6U+EgIWKIE8lE/iQZakhLtG2KL7nNQy/cksxzIh5ElQCe5NkrQZO0fai6ek8qwbmz07RVg2FknD7F2hvmxZBqoJSXhsFVn/9+fnkcsZekEtUevFmlQQNspPc63XgO0XmpTye9uM/BbTEsNEWeHSFZTEQLLx1l+pgwsYO3NlNSIUN24/GIR7JrZFG4fAoljkDKjhrYQzr1Fiy3t5G+CmadZ0TcjRQQdDw36ETlf7cizcrQc4FNtnx5rNWEaf54vUvlsd2DD19UIkzP9omITsiuNPPcUNq0A6v1TkgnSNYfhb26nxJIg34r8MmCAhWzB2eCy54gvOHDGLFAwfFZrQdvl\"\n
|
||||||
|
\ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\":
|
||||||
|
\"STOP\",\n \"index\": 0\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
|
||||||
|
504,\n \"candidatesTokenCount\": 45,\n \"totalTokenCount\": 973,\n \"promptTokensDetails\":
|
||||||
|
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 504\n
|
||||||
|
\ }\n ],\n \"thoughtsTokenCount\": 424\n },\n \"modelVersion\":
|
||||||
|
\"gemini-2.5-flash\",\n \"responseId\": \"Z1KWaYbTKZvnjMcP7piEoAg\"\n}\n"
|
||||||
|
headers:
|
||||||
|
Alt-Svc:
|
||||||
|
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=UTF-8
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:59:37 GMT
|
||||||
|
Server:
|
||||||
|
- scaffolding on HTTPServer2
|
||||||
|
Server-Timing:
|
||||||
|
- gfet4t7; dur=2283
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
Vary:
|
||||||
|
- Origin
|
||||||
|
- X-Origin
|
||||||
|
- Referer
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
X-Frame-Options:
|
||||||
|
- X-FRAME-OPTIONS-XXX
|
||||||
|
X-XSS-Protection:
|
||||||
|
- '0'
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -5,9 +5,9 @@ interactions:
|
|||||||
calculations"},{"role":"user","content":"\nCurrent Task: Calculate what is 15
|
calculations"},{"role":"user","content":"\nCurrent Task: Calculate what is 15
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
calculation\nyou MUST return the actual complete content as the final answer,
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"}],"model":"gpt-4o-mini","tool_choice":"auto","tools":[{"type":"function","function":{"name":"calculator","description":"Perform
|
not a summary."}],"model":"gpt-5-nano","tool_choice":"auto","tools":[{"type":"function","function":{"name":"calculator","description":"Perform
|
||||||
mathematical calculations. Use this for any math operations.","parameters":{"properties":{"expression":{"description":"Mathematical
|
mathematical calculations. Use this for any math operations.","strict":true,"parameters":{"properties":{"expression":{"description":"Mathematical
|
||||||
expression to evaluate","title":"Expression","type":"string"}},"required":["expression"],"type":"object"}}}]}'
|
expression to evaluate","title":"Expression","type":"string"}},"required":["expression"],"type":"object","additionalProperties":false}}}]}'
|
||||||
headers:
|
headers:
|
||||||
User-Agent:
|
User-Agent:
|
||||||
- X-USER-AGENT-XXX
|
- X-USER-AGENT-XXX
|
||||||
@@ -20,7 +20,7 @@ interactions:
|
|||||||
connection:
|
connection:
|
||||||
- keep-alive
|
- keep-alive
|
||||||
content-length:
|
content-length:
|
||||||
- '829'
|
- '813'
|
||||||
content-type:
|
content-type:
|
||||||
- application/json
|
- application/json
|
||||||
host:
|
host:
|
||||||
@@ -47,140 +47,17 @@ interactions:
|
|||||||
uri: https://api.openai.com/v1/chat/completions
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
response:
|
response:
|
||||||
body:
|
body:
|
||||||
string: "{\n \"id\": \"chatcmpl-D0vm7joOuDBPcMpfmOnftOoTCPtc8\",\n \"object\":
|
string: "{\n \"id\": \"chatcmpl-DAlG9W2mJYuOgpf3FwCRgbqaiHWf3\",\n \"object\":
|
||||||
\"chat.completion\",\n \"created\": 1769114459,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
\"chat.completion\",\n \"created\": 1771457317,\n \"model\": \"gpt-5-nano-2025-08-07\",\n
|
||||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
|
||||||
\"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n
|
|
||||||
\ \"id\": \"call_G73UZDvL4wC9EEdvm1UcRIRM\",\n \"type\":
|
|
||||||
\"function\",\n \"function\": {\n \"name\": \"calculator\",\n
|
|
||||||
\ \"arguments\": \"{\\\"expression\\\":\\\"15 * 8\\\"}\"\n }\n
|
|
||||||
\ }\n ],\n \"refusal\": null,\n \"annotations\":
|
|
||||||
[]\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n
|
|
||||||
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 137,\n \"completion_tokens\":
|
|
||||||
17,\n \"total_tokens\": 154,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
|
||||||
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
|
||||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
|
||||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
|
||||||
\"default\",\n \"system_fingerprint\": \"fp_c4585b5b9c\"\n}\n"
|
|
||||||
headers:
|
|
||||||
CF-RAY:
|
|
||||||
- CF-RAY-XXX
|
|
||||||
Connection:
|
|
||||||
- keep-alive
|
|
||||||
Content-Type:
|
|
||||||
- application/json
|
|
||||||
Date:
|
|
||||||
- Thu, 22 Jan 2026 20:40:59 GMT
|
|
||||||
Server:
|
|
||||||
- cloudflare
|
|
||||||
Set-Cookie:
|
|
||||||
- SET-COOKIE-XXX
|
|
||||||
Strict-Transport-Security:
|
|
||||||
- STS-XXX
|
|
||||||
Transfer-Encoding:
|
|
||||||
- chunked
|
|
||||||
X-Content-Type-Options:
|
|
||||||
- X-CONTENT-TYPE-XXX
|
|
||||||
access-control-expose-headers:
|
|
||||||
- ACCESS-CONTROL-XXX
|
|
||||||
alt-svc:
|
|
||||||
- h3=":443"; ma=86400
|
|
||||||
cf-cache-status:
|
|
||||||
- DYNAMIC
|
|
||||||
openai-organization:
|
|
||||||
- OPENAI-ORG-XXX
|
|
||||||
openai-processing-ms:
|
|
||||||
- '761'
|
|
||||||
openai-project:
|
|
||||||
- OPENAI-PROJECT-XXX
|
|
||||||
openai-version:
|
|
||||||
- '2020-10-01'
|
|
||||||
x-envoy-upstream-service-time:
|
|
||||||
- '1080'
|
|
||||||
x-openai-proxy-wasm:
|
|
||||||
- v0.1
|
|
||||||
x-ratelimit-limit-requests:
|
|
||||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
|
||||||
x-ratelimit-limit-tokens:
|
|
||||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
|
||||||
x-ratelimit-remaining-requests:
|
|
||||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
|
||||||
x-ratelimit-remaining-tokens:
|
|
||||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
|
||||||
x-ratelimit-reset-requests:
|
|
||||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
|
||||||
x-ratelimit-reset-tokens:
|
|
||||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
|
||||||
x-request-id:
|
|
||||||
- X-REQUEST-ID-XXX
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: OK
|
|
||||||
- request:
|
|
||||||
body: '{"messages":[{"role":"system","content":"You are Math Assistant. You are
|
|
||||||
a helpful math assistant.\nYour personal goal is: Help users with mathematical
|
|
||||||
calculations"},{"role":"user","content":"\nCurrent Task: Calculate what is 15
|
|
||||||
* 8\n\nThis is the expected criteria for your final answer: The result of the
|
|
||||||
calculation\nyou MUST return the actual complete content as the final answer,
|
|
||||||
not a summary.\n\nThis is VERY important to you, your job depends on it!"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_G73UZDvL4wC9EEdvm1UcRIRM","type":"function","function":{"name":"calculator","arguments":"{\"expression\":\"15
|
|
||||||
* 8\"}"}}]},{"role":"tool","tool_call_id":"call_G73UZDvL4wC9EEdvm1UcRIRM","content":"The
|
|
||||||
result of 15 * 8 is 120"},{"role":"user","content":"Analyze the tool result.
|
|
||||||
If requirements are met, provide the Final Answer. Otherwise, call the next
|
|
||||||
tool. Deliver only the answer without meta-commentary."}],"model":"gpt-4o-mini","tool_choice":"auto","tools":[{"type":"function","function":{"name":"calculator","description":"Perform
|
|
||||||
mathematical calculations. Use this for any math operations.","parameters":{"properties":{"expression":{"description":"Mathematical
|
|
||||||
expression to evaluate","title":"Expression","type":"string"}},"required":["expression"],"type":"object"}}}]}'
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- X-USER-AGENT-XXX
|
|
||||||
accept:
|
|
||||||
- application/json
|
|
||||||
accept-encoding:
|
|
||||||
- ACCEPT-ENCODING-XXX
|
|
||||||
authorization:
|
|
||||||
- AUTHORIZATION-XXX
|
|
||||||
connection:
|
|
||||||
- keep-alive
|
|
||||||
content-length:
|
|
||||||
- '1299'
|
|
||||||
content-type:
|
|
||||||
- application/json
|
|
||||||
cookie:
|
|
||||||
- COOKIE-XXX
|
|
||||||
host:
|
|
||||||
- api.openai.com
|
|
||||||
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:
|
|
||||||
- 1.83.0
|
|
||||||
x-stainless-read-timeout:
|
|
||||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
|
||||||
x-stainless-retry-count:
|
|
||||||
- '0'
|
|
||||||
x-stainless-runtime:
|
|
||||||
- CPython
|
|
||||||
x-stainless-runtime-version:
|
|
||||||
- 3.13.3
|
|
||||||
method: POST
|
|
||||||
uri: https://api.openai.com/v1/chat/completions
|
|
||||||
response:
|
|
||||||
body:
|
|
||||||
string: "{\n \"id\": \"chatcmpl-D0vm8mUnzLxu9pf1rc7MODkrMsCmf\",\n \"object\":
|
|
||||||
\"chat.completion\",\n \"created\": 1769114460,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
|
||||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
\"assistant\",\n \"content\": \"120\",\n \"refusal\": null,\n
|
\"assistant\",\n \"content\": \"120\",\n \"refusal\": null,\n
|
||||||
\ \"annotations\": []\n },\n \"logprobs\": null,\n \"finish_reason\":
|
\ \"annotations\": []\n },\n \"finish_reason\": \"stop\"\n
|
||||||
\"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 207,\n \"completion_tokens\":
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 208,\n \"completion_tokens\":
|
||||||
2,\n \"total_tokens\": 209,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
138,\n \"total_tokens\": 346,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
{\n \"reasoning_tokens\": 128,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
\"default\",\n \"system_fingerprint\": \"fp_c4585b5b9c\"\n}\n"
|
\"default\",\n \"system_fingerprint\": null\n}\n"
|
||||||
headers:
|
headers:
|
||||||
CF-RAY:
|
CF-RAY:
|
||||||
- CF-RAY-XXX
|
- CF-RAY-XXX
|
||||||
@@ -189,7 +66,7 @@ interactions:
|
|||||||
Content-Type:
|
Content-Type:
|
||||||
- application/json
|
- application/json
|
||||||
Date:
|
Date:
|
||||||
- Thu, 22 Jan 2026 20:41:00 GMT
|
- Wed, 18 Feb 2026 23:28:39 GMT
|
||||||
Server:
|
Server:
|
||||||
- cloudflare
|
- cloudflare
|
||||||
Strict-Transport-Security:
|
Strict-Transport-Security:
|
||||||
@@ -207,13 +84,13 @@ interactions:
|
|||||||
openai-organization:
|
openai-organization:
|
||||||
- OPENAI-ORG-XXX
|
- OPENAI-ORG-XXX
|
||||||
openai-processing-ms:
|
openai-processing-ms:
|
||||||
- '262'
|
- '1869'
|
||||||
openai-project:
|
openai-project:
|
||||||
- OPENAI-PROJECT-XXX
|
- OPENAI-PROJECT-XXX
|
||||||
openai-version:
|
openai-version:
|
||||||
- '2020-10-01'
|
- '2020-10-01'
|
||||||
x-envoy-upstream-service-time:
|
set-cookie:
|
||||||
- '496'
|
- SET-COOKIE-XXX
|
||||||
x-openai-proxy-wasm:
|
x-openai-proxy-wasm:
|
||||||
- v0.1
|
- v0.1
|
||||||
x-ratelimit-limit-requests:
|
x-ratelimit-limit-requests:
|
||||||
|
|||||||
@@ -0,0 +1,265 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}],"model":"gpt-4o-mini","tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '1733'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DAldZHfQGVcV3FNwAJAtNooU3PAU7\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771458769,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n
|
||||||
|
\ \"id\": \"call_kz1qLLRsugXwWiQMeH9oFAep\",\n \"type\":
|
||||||
|
\"function\",\n \"function\": {\n \"name\": \"parallel_local_search_one\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest OpenAI model release
|
||||||
|
notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_yNouGq1Kv6P5W9fhTng6acZi\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_two\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Anthropic model
|
||||||
|
release notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_O7MqnuniDmyT6a0BS31GTunB\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_three\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Gemini model release
|
||||||
|
notes\\\"}\"\n }\n }\n ],\n \"refusal\":
|
||||||
|
null,\n \"annotations\": []\n },\n \"logprobs\": null,\n
|
||||||
|
\ \"finish_reason\": \"tool_calls\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\":
|
||||||
|
259,\n \"completion_tokens\": 78,\n \"total_tokens\": 337,\n \"prompt_tokens_details\":
|
||||||
|
{\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
|
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": \"fp_414ba99a04\"\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:52:50 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '1418'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
set-cookie:
|
||||||
|
- SET-COOKIE-XXX
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."},{"role":"assistant","content":null,"tool_calls":[{"id":"call_kz1qLLRsugXwWiQMeH9oFAep","type":"function","function":{"name":"parallel_local_search_one","arguments":"{\"query\":
|
||||||
|
\"latest OpenAI model release notes\"}"}},{"id":"call_yNouGq1Kv6P5W9fhTng6acZi","type":"function","function":{"name":"parallel_local_search_two","arguments":"{\"query\":
|
||||||
|
\"latest Anthropic model release notes\"}"}},{"id":"call_O7MqnuniDmyT6a0BS31GTunB","type":"function","function":{"name":"parallel_local_search_three","arguments":"{\"query\":
|
||||||
|
\"latest Gemini model release notes\"}"}}]},{"role":"tool","tool_call_id":"call_kz1qLLRsugXwWiQMeH9oFAep","name":"parallel_local_search_one","content":"[one]
|
||||||
|
latest OpenAI model release notes"},{"role":"tool","tool_call_id":"call_yNouGq1Kv6P5W9fhTng6acZi","name":"parallel_local_search_two","content":"[two]
|
||||||
|
latest Anthropic model release notes"},{"role":"tool","tool_call_id":"call_O7MqnuniDmyT6a0BS31GTunB","name":"parallel_local_search_three","content":"[three]
|
||||||
|
latest Gemini model release notes"}],"model":"gpt-4o-mini","tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '2756'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
cookie:
|
||||||
|
- COOKIE-XXX
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DAldbawkFNpOeXbaJTkTlsSi7OiII\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771458771,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": \"The latest release notes for OpenAI,
|
||||||
|
Anthropic, and Gemini models highlight significant updates and improvements
|
||||||
|
in each respective technology. OpenAI's notes detail new features and optimizations
|
||||||
|
that enhance user interaction and performance. Anthropic's release emphasizes
|
||||||
|
their focus on safety and alignment in AI development, showcasing advancements
|
||||||
|
in responsible AI practices. Gemini's notes underline their innovative approaches
|
||||||
|
and cutting-edge functionalities designed to push the boundaries of current
|
||||||
|
AI capabilities.\",\n \"refusal\": null,\n \"annotations\":
|
||||||
|
[]\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n
|
||||||
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 377,\n \"completion_tokens\":
|
||||||
|
85,\n \"total_tokens\": 462,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
|
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": \"fp_414ba99a04\"\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:52:53 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '1755'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}],"model":"gpt-5-nano","temperature":1,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '1929'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DAlddfEozIpgleBufPaffZMQWK0Hj\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771458773,\n \"model\": \"gpt-5-nano-2025-08-07\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n
|
||||||
|
\ \"id\": \"call_Putc2jV5GhiIZMwx8mDcI61Q\",\n \"type\":
|
||||||
|
\"function\",\n \"function\": {\n \"name\": \"parallel_local_search_one\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest OpenAI model release
|
||||||
|
notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_iyjwcvkL3PdoOddxsqkHCT9T\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_two\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Anthropic model
|
||||||
|
release notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_G728RseEU7SbGk5YTiyyp9IH\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_three\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Gemini model release
|
||||||
|
notes\\\"}\"\n }\n }\n ],\n \"refusal\":
|
||||||
|
null,\n \"annotations\": []\n },\n \"finish_reason\": \"tool_calls\"\n
|
||||||
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 378,\n \"completion_tokens\":
|
||||||
|
1497,\n \"total_tokens\": 1875,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
|
{\n \"reasoning_tokens\": 1408,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": null\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:53:08 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '14853'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
set-cookie:
|
||||||
|
- SET-COOKIE-XXX
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."},{"role":"assistant","content":null,"tool_calls":[{"id":"call_Putc2jV5GhiIZMwx8mDcI61Q","type":"function","function":{"name":"parallel_local_search_one","arguments":"{\"query\":
|
||||||
|
\"latest OpenAI model release notes\"}"}},{"id":"call_iyjwcvkL3PdoOddxsqkHCT9T","type":"function","function":{"name":"parallel_local_search_two","arguments":"{\"query\":
|
||||||
|
\"latest Anthropic model release notes\"}"}},{"id":"call_G728RseEU7SbGk5YTiyyp9IH","type":"function","function":{"name":"parallel_local_search_three","arguments":"{\"query\":
|
||||||
|
\"latest Gemini model release notes\"}"}}]},{"role":"tool","tool_call_id":"call_Putc2jV5GhiIZMwx8mDcI61Q","name":"parallel_local_search_one","content":"[one]
|
||||||
|
latest OpenAI model release notes"},{"role":"tool","tool_call_id":"call_iyjwcvkL3PdoOddxsqkHCT9T","name":"parallel_local_search_two","content":"[two]
|
||||||
|
latest Anthropic model release notes"},{"role":"tool","tool_call_id":"call_G728RseEU7SbGk5YTiyyp9IH","name":"parallel_local_search_three","content":"[three]
|
||||||
|
latest Gemini model release notes"},{"role":"user","content":"Analyze the tool
|
||||||
|
result. If requirements are met, provide the Final Answer. Otherwise, call the
|
||||||
|
next tool. Deliver only the answer without meta-commentary."}],"model":"gpt-5-nano","temperature":1,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '3136'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
cookie:
|
||||||
|
- COOKIE-XXX
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DAldt2BXNqiYYLPgInjHCpYKfk2VK\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771458789,\n \"model\": \"gpt-5-nano-2025-08-07\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": \"The results show the latest model release
|
||||||
|
notes for OpenAI, Anthropic, and Gemini.\",\n \"refusal\": null,\n
|
||||||
|
\ \"annotations\": []\n },\n \"finish_reason\": \"stop\"\n
|
||||||
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 537,\n \"completion_tokens\":
|
||||||
|
2011,\n \"total_tokens\": 2548,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
|
{\n \"reasoning_tokens\": 1984,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": null\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Wed, 18 Feb 2026 23:53:25 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '15368'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."}],"model":"gpt-5-nano","temperature":1,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '1748'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DB244zBgA66fzl8TNcIPRWoE4lDIQ\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771521916,\n \"model\": \"gpt-5-nano-2025-08-07\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n
|
||||||
|
\ \"id\": \"call_D2ojRWqkng6krQ51vWQEU8wR\",\n \"type\":
|
||||||
|
\"function\",\n \"function\": {\n \"name\": \"parallel_local_search_one\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest OpenAI model release
|
||||||
|
notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_v1tpTKw1sYcI75SWG1LCkAC3\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_two\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Anthropic model
|
||||||
|
release notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_RrbyZClymnngoNLhlkQLLpwM\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_three\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Gemini model release
|
||||||
|
notes\\\"}\"\n }\n }\n ],\n \"refusal\":
|
||||||
|
null,\n \"annotations\": []\n },\n \"finish_reason\": \"tool_calls\"\n
|
||||||
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 343,\n \"completion_tokens\":
|
||||||
|
855,\n \"total_tokens\": 1198,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
|
{\n \"reasoning_tokens\": 768,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": null\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 17:25:23 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '6669'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
set-cookie:
|
||||||
|
- SET-COOKIE-XXX
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary."},{"role":"assistant","content":null,"tool_calls":[{"id":"call_D2ojRWqkng6krQ51vWQEU8wR","type":"function","function":{"name":"parallel_local_search_one","arguments":"{\"query\":
|
||||||
|
\"latest OpenAI model release notes\"}"}},{"id":"call_v1tpTKw1sYcI75SWG1LCkAC3","type":"function","function":{"name":"parallel_local_search_two","arguments":"{\"query\":
|
||||||
|
\"latest Anthropic model release notes\"}"}},{"id":"call_RrbyZClymnngoNLhlkQLLpwM","type":"function","function":{"name":"parallel_local_search_three","arguments":"{\"query\":
|
||||||
|
\"latest Gemini model release notes\"}"}}]},{"role":"tool","tool_call_id":"call_D2ojRWqkng6krQ51vWQEU8wR","name":"parallel_local_search_one","content":"[one]
|
||||||
|
latest OpenAI model release notes"},{"role":"tool","tool_call_id":"call_v1tpTKw1sYcI75SWG1LCkAC3","name":"parallel_local_search_two","content":"[two]
|
||||||
|
latest Anthropic model release notes"},{"role":"tool","tool_call_id":"call_RrbyZClymnngoNLhlkQLLpwM","name":"parallel_local_search_three","content":"[three]
|
||||||
|
latest Gemini model release notes"}],"model":"gpt-5-nano","temperature":1,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '2771'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
cookie:
|
||||||
|
- COOKIE-XXX
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DB24DjyYsIHiQJ7hHXob8tQFfeXBs\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771521925,\n \"model\": \"gpt-5-nano-2025-08-07\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": \"The three latest release-note references
|
||||||
|
retrieved encompass OpenAI, Anthropic, and Gemini, indicating that all three
|
||||||
|
major model families are actively updating their offerings. These notes typically
|
||||||
|
cover improvements to capabilities, safety measures, performance enhancements,
|
||||||
|
and any new APIs or features, suggesting a trend of ongoing refinement across
|
||||||
|
providers. If you\u2019d like, I can pull the full release notes or extract
|
||||||
|
and compare the key changes across the three sources.\",\n \"refusal\":
|
||||||
|
null,\n \"annotations\": []\n },\n \"finish_reason\": \"stop\"\n
|
||||||
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 467,\n \"completion_tokens\":
|
||||||
|
1437,\n \"total_tokens\": 1904,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
|
{\n \"reasoning_tokens\": 1344,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": null\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 17:25:35 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '10369'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"trace_id": "e456cc10-ce7b-4e68-a2cc-ddb806a2e7b9", "execution_type":
|
||||||
|
"crew", "user_identifier": null, "execution_context": {"crew_fingerprint": null,
|
||||||
|
"crew_name": "crew", "flow_name": null, "crewai_version": "1.9.3", "privacy_level":
|
||||||
|
"standard"}, "execution_metadata": {"expected_duration_estimate": 300, "agent_count":
|
||||||
|
0, "task_count": 0, "flow_method_count": 0, "execution_started_at": "2026-02-19T17:24:41.723158+00:00"},
|
||||||
|
"ephemeral_trace_id": "e456cc10-ce7b-4e68-a2cc-ddb806a2e7b9"}'
|
||||||
|
headers:
|
||||||
|
Accept:
|
||||||
|
- '*/*'
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '488'
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
X-Crewai-Organization-Id:
|
||||||
|
- 3433f0ee-8a94-4aa4-822b-2ac71aa38b18
|
||||||
|
X-Crewai-Version:
|
||||||
|
- 1.9.3
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
method: POST
|
||||||
|
uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: '{"id":"a78f2aca-0525-47c7-8f37-b3fca0ad6672","ephemeral_trace_id":"e456cc10-ce7b-4e68-a2cc-ddb806a2e7b9","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"running","duration_ms":null,"crewai_version":"1.9.3","total_events":0,"execution_context":{"crew_fingerprint":null,"crew_name":"crew","flow_name":null,"crewai_version":"1.9.3","privacy_level":"standard"},"created_at":"2026-02-19T17:24:41.989Z","updated_at":"2026-02-19T17:24:41.989Z","access_code":"TRACE-bd80d6be74","user_identifier":null}'
|
||||||
|
headers:
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '515'
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=utf-8
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 17:24:41 GMT
|
||||||
|
cache-control:
|
||||||
|
- no-store
|
||||||
|
content-security-policy:
|
||||||
|
- CSP-FILTERED
|
||||||
|
etag:
|
||||||
|
- ETAG-XXX
|
||||||
|
expires:
|
||||||
|
- '0'
|
||||||
|
permissions-policy:
|
||||||
|
- PERMISSIONS-POLICY-XXX
|
||||||
|
pragma:
|
||||||
|
- no-cache
|
||||||
|
referrer-policy:
|
||||||
|
- REFERRER-POLICY-XXX
|
||||||
|
strict-transport-security:
|
||||||
|
- STS-XXX
|
||||||
|
vary:
|
||||||
|
- Accept
|
||||||
|
x-content-type-options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
x-frame-options:
|
||||||
|
- X-FRAME-OPTIONS-XXX
|
||||||
|
x-permitted-cross-domain-policies:
|
||||||
|
- X-PERMITTED-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
x-runtime:
|
||||||
|
- X-RUNTIME-XXX
|
||||||
|
x-xss-protection:
|
||||||
|
- X-XSS-PROTECTION-XXX
|
||||||
|
status:
|
||||||
|
code: 201
|
||||||
|
message: Created
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."}],"model":"gpt-5-nano","temperature":1,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '1929'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DB23W8RBF6zlxweiHYGb6maVfyctt\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771521882,\n \"model\": \"gpt-5-nano-2025-08-07\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n
|
||||||
|
\ \"id\": \"call_sge1FXUkpmPEDe8nTOgn0tQG\",\n \"type\":
|
||||||
|
\"function\",\n \"function\": {\n \"name\": \"parallel_local_search_one\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest OpenAI model release
|
||||||
|
notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_z5jRPH4DQ7Wp3HdDUlZe8gGh\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_two\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Anthropic model
|
||||||
|
release notes\\\"}\"\n }\n },\n {\n \"id\":
|
||||||
|
\"call_DNlgqnadODDsyQkSuLcXZCX2\",\n \"type\": \"function\",\n
|
||||||
|
\ \"function\": {\n \"name\": \"parallel_local_search_three\",\n
|
||||||
|
\ \"arguments\": \"{\\\"query\\\": \\\"latest Gemini model release
|
||||||
|
notes\\\"}\"\n }\n }\n ],\n \"refusal\":
|
||||||
|
null,\n \"annotations\": []\n },\n \"finish_reason\": \"tool_calls\"\n
|
||||||
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 378,\n \"completion_tokens\":
|
||||||
|
2456,\n \"total_tokens\": 2834,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||||
|
{\n \"reasoning_tokens\": 2368,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": null\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 17:25:02 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '19582'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
set-cookie:
|
||||||
|
- SET-COOKIE-XXX
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"messages":[{"role":"system","content":"You are Parallel Tool Agent. You
|
||||||
|
follow tool instructions precisely.\nYour personal goal is: Use both tools exactly
|
||||||
|
as instructed"},{"role":"user","content":"\nCurrent Task: This is a tool-calling
|
||||||
|
compliance test. In your next assistant turn, emit exactly 3 tool calls in the
|
||||||
|
same response (parallel tool calls), in this order: 1) parallel_local_search_one(query=''latest
|
||||||
|
OpenAI model release notes''), 2) parallel_local_search_two(query=''latest Anthropic
|
||||||
|
model release notes''), 3) parallel_local_search_three(query=''latest Gemini
|
||||||
|
model release notes''). Do not call any other tools and do not answer before
|
||||||
|
those 3 tool calls are emitted. After the tool results return, provide a one
|
||||||
|
paragraph summary.\n\nThis is the expected criteria for your final answer: A
|
||||||
|
one sentence summary of both tool outputs\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary."},{"role":"assistant","content":null,"tool_calls":[{"id":"call_sge1FXUkpmPEDe8nTOgn0tQG","type":"function","function":{"name":"parallel_local_search_one","arguments":"{\"query\":
|
||||||
|
\"latest OpenAI model release notes\"}"}},{"id":"call_z5jRPH4DQ7Wp3HdDUlZe8gGh","type":"function","function":{"name":"parallel_local_search_two","arguments":"{\"query\":
|
||||||
|
\"latest Anthropic model release notes\"}"}},{"id":"call_DNlgqnadODDsyQkSuLcXZCX2","type":"function","function":{"name":"parallel_local_search_three","arguments":"{\"query\":
|
||||||
|
\"latest Gemini model release notes\"}"}}]},{"role":"tool","tool_call_id":"call_sge1FXUkpmPEDe8nTOgn0tQG","name":"parallel_local_search_one","content":"[one]
|
||||||
|
latest OpenAI model release notes"},{"role":"tool","tool_call_id":"call_z5jRPH4DQ7Wp3HdDUlZe8gGh","name":"parallel_local_search_two","content":"[two]
|
||||||
|
latest Anthropic model release notes"},{"role":"tool","tool_call_id":"call_DNlgqnadODDsyQkSuLcXZCX2","name":"parallel_local_search_three","content":"[three]
|
||||||
|
latest Gemini model release notes"},{"role":"user","content":"Analyze the tool
|
||||||
|
result. If requirements are met, provide the Final Answer. Otherwise, call the
|
||||||
|
next tool. Deliver only the answer without meta-commentary."}],"model":"gpt-5-nano","temperature":1,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"parallel_local_search_one","description":"Local
|
||||||
|
search tool #1 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_two","description":"Local
|
||||||
|
search tool #2 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}},{"type":"function","function":{"name":"parallel_local_search_three","description":"Local
|
||||||
|
search tool #3 for concurrency testing.","strict":true,"parameters":{"properties":{"query":{"description":"Search
|
||||||
|
query","title":"Query","type":"string"}},"required":["query"],"type":"object","additionalProperties":false}}}]}'
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- X-USER-AGENT-XXX
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- ACCEPT-ENCODING-XXX
|
||||||
|
authorization:
|
||||||
|
- AUTHORIZATION-XXX
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '3136'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
cookie:
|
||||||
|
- COOKIE-XXX
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
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:
|
||||||
|
- 1.83.0
|
||||||
|
x-stainless-read-timeout:
|
||||||
|
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.13.3
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "{\n \"id\": \"chatcmpl-DB23sY0Ahpd1yAgLZ882KkA50Zljx\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1771521904,\n \"model\": \"gpt-5-nano-2025-08-07\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": \"Results returned three items: the latest
|
||||||
|
OpenAI model release notes, the latest Anthropic model release notes, and
|
||||||
|
the latest Gemini model release notes.\",\n \"refusal\": null,\n \"annotations\":
|
||||||
|
[]\n },\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\":
|
||||||
|
{\n \"prompt_tokens\": 537,\n \"completion_tokens\": 1383,\n \"total_tokens\":
|
||||||
|
1920,\n \"prompt_tokens_details\": {\n \"cached_tokens\": 0,\n \"audio_tokens\":
|
||||||
|
0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\":
|
||||||
|
1344,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": 0,\n
|
||||||
|
\ \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||||
|
\"default\",\n \"system_fingerprint\": null\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-RAY:
|
||||||
|
- CF-RAY-XXX
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Thu, 19 Feb 2026 17:25:16 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- STS-XXX
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- X-CONTENT-TYPE-XXX
|
||||||
|
access-control-expose-headers:
|
||||||
|
- ACCESS-CONTROL-XXX
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
cf-cache-status:
|
||||||
|
- DYNAMIC
|
||||||
|
openai-organization:
|
||||||
|
- OPENAI-ORG-XXX
|
||||||
|
openai-processing-ms:
|
||||||
|
- '12339'
|
||||||
|
openai-project:
|
||||||
|
- OPENAI-PROJECT-XXX
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
x-openai-proxy-wasm:
|
||||||
|
- v0.1
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||||
|
x-request-id:
|
||||||
|
- X-REQUEST-ID-XXX
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
version: 1
|
||||||
Reference in New Issue
Block a user