Skip to content

Commit 76bedac

Browse files
committed
move the crewAI tools conversation back in the example
1 parent fbd9c79 commit 76bedac

File tree

2 files changed

+54
-70
lines changed

2 files changed

+54
-70
lines changed

examples/crewai_semantic_search.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,68 @@
1919
"""
2020

2121
import os
22+
from typing import Any
2223

2324
from crewai import Agent, Crew, Task
25+
from crewai.tools.base_tool import BaseTool as CrewAIBaseTool
2426
from dotenv import load_dotenv
27+
from pydantic import BaseModel, Field
2528

2629
from stackone_ai import StackOneToolSet
30+
from stackone_ai.models import StackOneTool
2731

2832
load_dotenv()
2933

3034
_account_ids = [aid.strip() for aid in os.getenv("STACKONE_ACCOUNT_ID", "").split(",") if aid.strip()]
3135

3236

37+
def _to_crewai_tool(tool: StackOneTool) -> CrewAIBaseTool:
38+
"""Wrap a StackOneTool as a CrewAI BaseTool.
39+
40+
CrewAI has its own BaseTool (not LangChain's), so we create a
41+
lightweight wrapper that delegates execution to the StackOne tool.
42+
"""
43+
schema_props: dict[str, Any] = {}
44+
annotations: dict[str, Any] = {}
45+
46+
for name, details in tool.parameters.properties.items():
47+
python_type: type = str
48+
if isinstance(details, dict):
49+
type_str = details.get("type", "string")
50+
if type_str == "number":
51+
python_type = float
52+
elif type_str == "integer":
53+
python_type = int
54+
elif type_str == "boolean":
55+
python_type = bool
56+
field = Field(description=details.get("description", ""))
57+
else:
58+
field = Field(description="")
59+
60+
schema_props[name] = field
61+
annotations[name] = python_type
62+
63+
_schema = type(
64+
f"{tool.name.title().replace('_', '')}Args",
65+
(BaseModel,),
66+
{"__annotations__": annotations, "__module__": __name__, **schema_props},
67+
)
68+
69+
_parent = tool
70+
_name = tool.name
71+
_description = tool.description
72+
73+
class WrappedTool(CrewAIBaseTool):
74+
name: str = _name
75+
description: str = _description
76+
args_schema: type[BaseModel] = _schema
77+
78+
def _run(self, **kwargs: Any) -> Any:
79+
return _parent.execute(kwargs)
80+
81+
return WrappedTool()
82+
83+
3384
def crewai_semantic_search() -> None:
3485
toolset = StackOneToolSet()
3586

@@ -38,7 +89,7 @@ def crewai_semantic_search() -> None:
3889
# tool definitions. Useful for inspecting what's available before committing.
3990
preview = toolset.search_action_names(
4091
"book a meeting or check availability",
41-
account_ids=_account_ids
92+
account_ids=_account_ids,
4293
)
4394
print("Semantic search preview (action names only):")
4495
for r in preview:
@@ -51,7 +102,7 @@ def crewai_semantic_search() -> None:
51102
tools = toolset.search_tools(
52103
"schedule meetings, check availability, list events",
53104
connector="calendly",
54-
account_ids=_account_ids
105+
account_ids=_account_ids,
55106
)
56107
assert len(tools) > 0, "Expected at least one scheduling tool"
57108

@@ -61,7 +112,7 @@ def crewai_semantic_search() -> None:
61112
print()
62113

63114
# Step 3: Convert to CrewAI format
64-
crewai_tools = tools.to_crewai()
115+
crewai_tools = [_to_crewai_tool(t) for t in tools]
65116

66117
# Step 4: Create a CrewAI meeting booking agent
67118
agent = Agent(

stackone_ai/models.py

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -450,64 +450,6 @@ def _run(self, **kwargs: Any) -> Any:
450450

451451
return StackOneLangChainTool()
452452

453-
def to_crewai(self) -> Any:
454-
"""Convert this tool to CrewAI format
455-
456-
Requires the ``crewai`` package (``pip install crewai``).
457-
458-
Returns:
459-
Tool as a ``crewai.tools.BaseTool`` instance
460-
"""
461-
try:
462-
from crewai.tools.base_tool import BaseTool as CrewAIBaseTool
463-
except ImportError as e:
464-
raise ImportError("crewai is required for to_crewai(). Install with: pip install crewai") from e
465-
466-
schema_props: dict[str, Any] = {}
467-
annotations: dict[str, Any] = {}
468-
469-
for name, details in self.parameters.properties.items():
470-
python_type: type = str
471-
if isinstance(details, dict):
472-
type_str = details.get("type", "string")
473-
if type_str == "number":
474-
python_type = float
475-
elif type_str == "integer":
476-
python_type = int
477-
elif type_str == "boolean":
478-
python_type = bool
479-
480-
field = Field(description=details.get("description", ""))
481-
else:
482-
field = Field(description="")
483-
484-
schema_props[name] = field
485-
annotations[name] = python_type
486-
487-
schema_class = type(
488-
f"{self.name.title()}Args",
489-
(BaseModel,),
490-
{
491-
"__annotations__": annotations,
492-
"__module__": __name__,
493-
**schema_props,
494-
},
495-
)
496-
497-
parent_tool = self
498-
_name = parent_tool.name
499-
_description = parent_tool.description
500-
501-
class StackOneCrewAITool(CrewAIBaseTool):
502-
name: str = _name
503-
description: str = _description
504-
args_schema: type[BaseModel] = schema_class
505-
506-
def _run(self, **kwargs: Any) -> Any:
507-
return parent_tool.execute(kwargs)
508-
509-
return StackOneCrewAITool()
510-
511453
def set_account_id(self, account_id: str | None) -> None:
512454
"""Set the account ID for this tool
513455
@@ -616,15 +558,6 @@ def to_langchain(self) -> Sequence[BaseTool]:
616558
"""
617559
return [tool.to_langchain() for tool in self.tools]
618560

619-
def to_crewai(self) -> list[Any]:
620-
"""Convert all tools to CrewAI format
621-
622-
Requires the ``crewai`` package (``pip install crewai``).
623-
624-
Returns:
625-
List of tools as ``crewai.tools.BaseTool`` instances
626-
"""
627-
return [tool.to_crewai() for tool in self.tools]
628561

629562
def utility_tools(
630563
self,

0 commit comments

Comments
 (0)