Merge pull request #1904 from pipecat-ai/mb/aws-bedrock-no-op-tool
Add no_op tool to AWSBedrockLLMService
This commit is contained in:
@@ -13,6 +13,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
`DailyTransport.stop_transcription()` to be able to start and stop Daily
|
||||
transcription dynamically (maybe with different settings).
|
||||
|
||||
### Fixed
|
||||
|
||||
- In `AWSBedrockLLMService`, worked around a possible bug in AWS Bedrock where
|
||||
a `toolConfig` is required if there has been previous tool use in the
|
||||
messages array. This workaround includes a no_op factory function call is
|
||||
used to satisfy the requirement.
|
||||
|
||||
## [0.0.68] - 2025-05-28
|
||||
|
||||
### Added
|
||||
|
||||
@@ -606,6 +606,21 @@ class AWSBedrockLLMService(LLMService):
|
||||
assistant = AWSBedrockAssistantContextAggregator(context, params=assistant_params)
|
||||
return AWSBedrockContextAggregatorPair(_user=user, _assistant=assistant)
|
||||
|
||||
def _create_no_op_tool(self):
|
||||
"""Create a no-operation tool for AWS Bedrock when tool content exists but no tools are defined.
|
||||
|
||||
This is required because AWS Bedrock doesn't allow empty tool configurations after tools were
|
||||
previously set. Other LLM vendors allow NOT_GIVEN or empty tool configurations,
|
||||
but AWS Bedrock requires at least one tool to be defined.
|
||||
"""
|
||||
return {
|
||||
"toolSpec": {
|
||||
"name": "no_operation",
|
||||
"description": "Internal placeholder function. Do not call this function.",
|
||||
"inputSchema": {"json": {"type": "object", "properties": {}, "required": []}},
|
||||
}
|
||||
}
|
||||
|
||||
@traced_llm
|
||||
async def _process_context(self, context: AWSBedrockLLMContext):
|
||||
# Usage tracking
|
||||
@@ -616,6 +631,8 @@ class AWSBedrockLLMService(LLMService):
|
||||
cache_creation_input_tokens = 0
|
||||
use_completion_tokens_estimate = False
|
||||
|
||||
using_noop_tool = False
|
||||
|
||||
try:
|
||||
await self.push_frame(LLMFullResponseStartFrame())
|
||||
await self.start_processing_metrics()
|
||||
@@ -640,12 +657,28 @@ class AWSBedrockLLMService(LLMService):
|
||||
# Add system message
|
||||
request_params["system"] = context.system
|
||||
|
||||
# Add tools if present
|
||||
if context.tools:
|
||||
tool_config = {"tools": context.tools}
|
||||
# Check if messages contain tool use or tool result content blocks
|
||||
has_tool_content = False
|
||||
for message in context.messages:
|
||||
if isinstance(message.get("content"), list):
|
||||
for content_item in message["content"]:
|
||||
if "toolUse" in content_item or "toolResult" in content_item:
|
||||
has_tool_content = True
|
||||
break
|
||||
if has_tool_content:
|
||||
break
|
||||
|
||||
# Add tool_choice if specified
|
||||
if context.tool_choice:
|
||||
# Handle tools: use current tools, or no-op if tool content exists but no current tools
|
||||
tools = context.tools or []
|
||||
if has_tool_content and not tools:
|
||||
tools = [self._create_no_op_tool()]
|
||||
using_noop_tool = True
|
||||
|
||||
if tools:
|
||||
tool_config = {"tools": tools}
|
||||
|
||||
# Only add tool_choice if we have real tools (not just no-op)
|
||||
if not using_noop_tool and context.tool_choice:
|
||||
if context.tool_choice == "auto":
|
||||
tool_config["toolChoice"] = {"auto": {}}
|
||||
elif context.tool_choice == "none":
|
||||
@@ -704,12 +737,17 @@ class AWSBedrockLLMService(LLMService):
|
||||
if event["messageStop"]["stopReason"] == "tool_use" and tool_use_block:
|
||||
try:
|
||||
arguments = json.loads(json_accumulator) if json_accumulator else {}
|
||||
await self.call_function(
|
||||
context=context,
|
||||
tool_call_id=tool_use_block["id"],
|
||||
function_name=tool_use_block["name"],
|
||||
arguments=arguments,
|
||||
)
|
||||
|
||||
# Only call function if it's not the no_operation tool
|
||||
if not using_noop_tool:
|
||||
await self.call_function(
|
||||
context=context,
|
||||
tool_call_id=tool_use_block["id"],
|
||||
function_name=tool_use_block["name"],
|
||||
arguments=arguments,
|
||||
)
|
||||
else:
|
||||
logger.debug("Ignoring no_operation tool call")
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"Failed to parse tool arguments: {json_accumulator}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user