1919"""
2020
2121import os
22+ from typing import Any
2223
2324from crewai import Agent , Crew , Task
25+ from crewai .tools .base_tool import BaseTool as CrewAIBaseTool
2426from dotenv import load_dotenv
27+ from pydantic import BaseModel , Field
2528
2629from stackone_ai import StackOneToolSet
30+ from stackone_ai .models import StackOneTool
2731
2832load_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+
3384def 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 (
0 commit comments