mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-26 16:48:13 +00:00
Compare commits
3 Commits
devin/1768
...
devin/1742
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a6ede9ae5 | ||
|
|
6df3007190 | ||
|
|
3060c6f919 |
@@ -305,8 +305,6 @@ class LLM:
|
|||||||
Args:
|
Args:
|
||||||
messages: Input messages for the LLM
|
messages: Input messages for the LLM
|
||||||
tools: Optional list of tool schemas
|
tools: Optional list of tool schemas
|
||||||
callbacks: Optional list of callback functions
|
|
||||||
available_functions: Optional dict of available functions
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, Any]: Parameters for the completion call
|
Dict[str, Any]: Parameters for the completion call
|
||||||
@@ -316,7 +314,10 @@ class LLM:
|
|||||||
messages = [{"role": "user", "content": messages}]
|
messages = [{"role": "user", "content": messages}]
|
||||||
formatted_messages = self._format_messages_for_provider(messages)
|
formatted_messages = self._format_messages_for_provider(messages)
|
||||||
|
|
||||||
# --- 2) Prepare the parameters for the completion call
|
# --- 2) If using Gemini, ensure additionalProperties is not in tool schemas
|
||||||
|
self._clean_gemini_tool_parameters(tools)
|
||||||
|
|
||||||
|
# --- 3) Prepare the parameters for the completion call
|
||||||
params = {
|
params = {
|
||||||
"model": self.model,
|
"model": self.model,
|
||||||
"messages": formatted_messages,
|
"messages": formatted_messages,
|
||||||
@@ -346,6 +347,22 @@ class LLM:
|
|||||||
# Remove None values from params
|
# Remove None values from params
|
||||||
return {k: v for k, v in params.items() if v is not None}
|
return {k: v for k, v in params.items() if v is not None}
|
||||||
|
|
||||||
|
def _clean_gemini_tool_parameters(
|
||||||
|
self, tools: Optional[List[dict]]
|
||||||
|
) -> None:
|
||||||
|
"""Remove additionalProperties from tool parameters for Gemini compatibility.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tools: List of tool dictionaries that may contain function schemas
|
||||||
|
"""
|
||||||
|
if not tools or "gemini" not in self.model.lower():
|
||||||
|
return
|
||||||
|
|
||||||
|
for tool in tools:
|
||||||
|
if isinstance(tool, dict) and "function" in tool:
|
||||||
|
params = tool["function"].get("parameters", {})
|
||||||
|
params.pop("additionalProperties", None)
|
||||||
|
|
||||||
def _handle_streaming_response(
|
def _handle_streaming_response(
|
||||||
self,
|
self,
|
||||||
params: Dict[str, Any],
|
params: Dict[str, Any],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import textwrap
|
import textwrap
|
||||||
from typing import Any, Callable, Optional, Union, get_type_hints
|
from typing import Any, Callable, Dict, List, Optional, Type, Union, get_type_hints
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, create_model
|
from pydantic import BaseModel, Field, create_model
|
||||||
|
|
||||||
@@ -240,6 +240,37 @@ class CrewStructuredTool:
|
|||||||
"""Get the tool's input arguments schema."""
|
"""Get the tool's input arguments schema."""
|
||||||
return self.args_schema.model_json_schema()["properties"]
|
return self.args_schema.model_json_schema()["properties"]
|
||||||
|
|
||||||
|
def to_openai_function(self) -> Dict[str, Any]:
|
||||||
|
"""Convert the tool to an OpenAI function format.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: A dictionary in the OpenAI function format.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```python
|
||||||
|
tool = CrewStructuredTool(...)
|
||||||
|
function_dict = tool.to_openai_function()
|
||||||
|
# Use with OpenAI or compatible APIs
|
||||||
|
```
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the schema conversion fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
schema = self.args_schema.model_json_schema()
|
||||||
|
schema.pop("additionalProperties", None)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": self.name,
|
||||||
|
"description": self.description,
|
||||||
|
"parameters": schema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"Failed to convert tool to OpenAI function format: {str(e)}")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"CrewStructuredTool(name='{self.name}', description='{self.description}')"
|
f"CrewStructuredTool(name='{self.name}', description='{self.description}')"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def schema_class():
|
|||||||
return TestSchema
|
return TestSchema
|
||||||
|
|
||||||
|
|
||||||
class InternalCrewStructuredTool:
|
class TestInternalCrewStructuredTool:
|
||||||
def test_initialization(self, basic_function, schema_class):
|
def test_initialization(self, basic_function, schema_class):
|
||||||
"""Test basic initialization of CrewStructuredTool"""
|
"""Test basic initialization of CrewStructuredTool"""
|
||||||
tool = CrewStructuredTool(
|
tool = CrewStructuredTool(
|
||||||
@@ -144,3 +144,55 @@ class InternalCrewStructuredTool:
|
|||||||
{"required_param": "test", "optional_param": "custom", "nullable_param": 42}
|
{"required_param": "test", "optional_param": "custom", "nullable_param": 42}
|
||||||
)
|
)
|
||||||
assert result == "test custom 42"
|
assert result == "test custom 42"
|
||||||
|
|
||||||
|
def test_to_openai_function_no_additional_properties(self):
|
||||||
|
"""Test that the to_openai_function method doesn't include additionalProperties."""
|
||||||
|
|
||||||
|
class TestSchema(BaseModel):
|
||||||
|
test_field: str = Field(..., description="A test field")
|
||||||
|
|
||||||
|
def test_func(test_field: str) -> str:
|
||||||
|
"""Test function that returns the input."""
|
||||||
|
return f"Test function received: {test_field}"
|
||||||
|
|
||||||
|
tool = CrewStructuredTool(
|
||||||
|
name="test_tool",
|
||||||
|
description="A test tool",
|
||||||
|
args_schema=TestSchema,
|
||||||
|
func=test_func
|
||||||
|
)
|
||||||
|
|
||||||
|
function_dict = tool.to_openai_function()
|
||||||
|
assert "additionalProperties" not in function_dict["function"]["parameters"]
|
||||||
|
|
||||||
|
# Verify other properties are correct
|
||||||
|
assert function_dict["type"] == "function"
|
||||||
|
assert function_dict["function"]["name"] == "test_tool"
|
||||||
|
assert function_dict["function"]["description"] == "A test tool"
|
||||||
|
assert "properties" in function_dict["function"]["parameters"]
|
||||||
|
assert "test_field" in function_dict["function"]["parameters"]["properties"]
|
||||||
|
|
||||||
|
def test_to_openai_function_edge_cases(self):
|
||||||
|
"""Test edge cases for to_openai_function conversion."""
|
||||||
|
class EmptySchema(BaseModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def empty_func() -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
tool = CrewStructuredTool(
|
||||||
|
name="empty_tool",
|
||||||
|
description="A tool with empty schema",
|
||||||
|
args_schema=EmptySchema,
|
||||||
|
func=empty_func
|
||||||
|
)
|
||||||
|
|
||||||
|
function_dict = tool.to_openai_function()
|
||||||
|
assert function_dict["type"] == "function"
|
||||||
|
assert function_dict["function"]["name"] == "empty_tool"
|
||||||
|
|
||||||
|
# Check that parameters contains the expected fields
|
||||||
|
params = function_dict["function"]["parameters"]
|
||||||
|
assert params["title"] == "EmptySchema"
|
||||||
|
assert params["type"] == "object"
|
||||||
|
assert "properties" in params # Empty schema still has a properties field
|
||||||
|
|||||||
Reference in New Issue
Block a user