33from __future__ import annotations
44
55import os
6- from typing import Any
6+ from typing import Any , Final , Literal , TypedDict , cast
77from urllib .parse import urljoin
88
99import httpx
10+ from typing_extensions import NotRequired
1011
1112from crewai_core .constants import DEFAULT_CREWAI_ENTERPRISE_URL
1213from crewai_core .settings import Settings
1314from crewai_core .version import get_crewai_version
1415
1516
17+ HttpMethod = Literal ["GET" , "POST" , "PATCH" , "DELETE" ]
18+
19+
20+ class AvailableExport (TypedDict ):
21+ name : str
22+
23+
24+ class EnvVarEntry (TypedDict ):
25+ name : str
26+ description : str
27+ required : bool
28+ default : str | None
29+
30+
31+ class ToolMetadata (TypedDict ):
32+ name : str
33+ module : str
34+ humanized_name : str
35+ description : str
36+ run_params_schema : dict [str , Any ]
37+ init_params_schema : dict [str , Any ]
38+ env_vars : list [EnvVarEntry ]
39+
40+
41+ class ToolsMetadataPayload (TypedDict ):
42+ package : str
43+ tools : list [ToolMetadata ] | None
44+
45+
46+ class PublishToolPayload (TypedDict ):
47+ handle : str
48+ public : bool
49+ version : str
50+ file : str
51+ description : str | None
52+ available_exports : list [AvailableExport ] | None
53+ tools_metadata : ToolsMetadataPayload | None
54+
55+
56+ class CrewDeploymentSpec (TypedDict ):
57+ name : str
58+ repo_clone_url : str
59+ env : dict [str , str ]
60+
61+
62+ class CreateCrewPayload (TypedDict ):
63+ deploy : CrewDeploymentSpec
64+
65+
66+ class _WithUserIdentifier (TypedDict ):
67+ user_identifier : NotRequired [str ]
68+
69+
70+ class LoginPayload (_WithUserIdentifier ):
71+ pass
72+
73+
74+ class TraceExecutionContext (TypedDict ):
75+ crew_fingerprint : str | None
76+ crew_name : str | None
77+ flow_name : str | None
78+ crewai_version : str
79+ privacy_level : str
80+
81+
82+ class TraceExecutionMetadata (TypedDict ):
83+ expected_duration_estimate : int
84+ agent_count : int
85+ task_count : int
86+ flow_method_count : int
87+ execution_started_at : str
88+
89+
90+ class TraceBatchInitPayload (_WithUserIdentifier ):
91+ trace_id : str
92+ execution_type : str
93+ execution_context : TraceExecutionContext
94+ execution_metadata : TraceExecutionMetadata
95+ ephemeral_trace_id : NotRequired [str ]
96+
97+
98+ class TraceBatchMetadata (TypedDict ):
99+ events_count : int
100+ batch_sequence : int
101+ is_final_batch : bool
102+
103+
104+ class TraceEventsPayload (TypedDict ):
105+ events : list [dict [str , Any ]]
106+ batch_metadata : TraceBatchMetadata
107+
108+
109+ class TraceFinalizePayload (TypedDict ):
110+ status : Literal ["completed" ]
111+ duration_ms : float | None
112+ final_event_count : int
113+
114+
115+ class TraceFailedPayload (TypedDict ):
116+ status : Literal ["failed" ]
117+ failure_reason : str
118+
119+
120+ Headers = TypedDict (
121+ "Headers" ,
122+ {
123+ "Content-Type" : str ,
124+ "User-Agent" : str ,
125+ "X-Crewai-Version" : str ,
126+ "Authorization" : NotRequired [str ],
127+ "X-Crewai-Organization-Id" : NotRequired [str ],
128+ },
129+ )
130+
131+
132+ class RequestKwargs (TypedDict ):
133+ headers : dict [str , str ]
134+ json : NotRequired [Any ]
135+ params : NotRequired [dict [str , str ]]
136+ timeout : NotRequired [float ]
137+
138+
16139class PlusAPI :
17140 """Client for working with the CrewAI+ API."""
18141
19- TOOLS_RESOURCE = "/crewai_plus/api/v1/tools"
20- ORGANIZATIONS_RESOURCE = "/crewai_plus/api/v1/me/organizations"
21- CREWS_RESOURCE = "/crewai_plus/api/v1/crews"
22- AGENTS_RESOURCE = "/crewai_plus/api/v1/agents"
23- TRACING_RESOURCE = "/crewai_plus/api/v1/tracing"
24- EPHEMERAL_TRACING_RESOURCE = "/crewai_plus/api/v1/tracing/ephemeral"
25- INTEGRATIONS_RESOURCE = "/crewai_plus/api/v1/integrations"
142+ TOOLS_RESOURCE : Final = "/crewai_plus/api/v1/tools"
143+ ORGANIZATIONS_RESOURCE : Final = "/crewai_plus/api/v1/me/organizations"
144+ CREWS_RESOURCE : Final = "/crewai_plus/api/v1/crews"
145+ AGENTS_RESOURCE : Final = "/crewai_plus/api/v1/agents"
146+ TRACING_RESOURCE : Final = "/crewai_plus/api/v1/tracing"
147+ EPHEMERAL_TRACING_RESOURCE : Final = "/crewai_plus/api/v1/tracing/ephemeral"
148+ INTEGRATIONS_RESOURCE : Final = "/crewai_plus/api/v1/integrations"
26149
27150 def __init__ (self , api_key : str | None = None ) -> None :
151+ version = get_crewai_version ()
28152 self .api_key = api_key
29- self .headers = {
153+ self .headers : Headers = {
30154 "Content-Type" : "application/json" ,
31- "User-Agent" : f"CrewAI-CLI/{ get_crewai_version () } " ,
32- "X-Crewai-Version" : get_crewai_version () ,
155+ "User-Agent" : f"CrewAI-CLI/{ version } " ,
156+ "X-Crewai-Version" : version ,
33157 }
34158 if api_key :
35159 self .headers ["Authorization" ] = f"Bearer { api_key } "
160+
36161 settings = Settings ()
37162 if settings .org_uuid :
38163 self .headers ["X-Crewai-Organization-Id" ] = settings .org_uuid
@@ -44,17 +169,30 @@ def __init__(self, api_key: str | None = None) -> None:
44169 )
45170
46171 def _make_request (
47- self , method : str , endpoint : str , ** kwargs : Any
172+ self ,
173+ method : HttpMethod ,
174+ endpoint : str ,
175+ * ,
176+ json : Any = None ,
177+ params : dict [str , str ] | None = None ,
178+ timeout : float | None = None ,
179+ verify : bool = True ,
48180 ) -> httpx .Response :
49181 url = urljoin (self .base_url , endpoint )
50- verify = kwargs .pop ("verify" , True )
182+ request_kwargs : RequestKwargs = {"headers" : cast (dict [str , str ], self .headers )}
183+ if json is not None :
184+ request_kwargs ["json" ] = json
185+ if params is not None :
186+ request_kwargs ["params" ] = params
187+ if timeout is not None :
188+ request_kwargs ["timeout" ] = timeout
51189 with httpx .Client (trust_env = False , verify = verify ) as client :
52- return client .request (method , url , headers = self . headers , ** kwargs )
190+ return client .request (method , url , ** request_kwargs )
53191
54192 def login_to_tool_repository (
55193 self , user_identifier : str | None = None
56194 ) -> httpx .Response :
57- payload = {}
195+ payload : LoginPayload = {}
58196 if user_identifier :
59197 payload ["user_identifier" ] = user_identifier
60198 return self ._make_request ("POST" , f"{ self .TOOLS_RESOURCE } /login" , json = payload )
@@ -65,7 +203,7 @@ def get_tool(self, handle: str) -> httpx.Response:
65203 async def get_agent (self , handle : str ) -> httpx .Response :
66204 url = urljoin (self .base_url , f"{ self .AGENTS_RESOURCE } /{ handle } " )
67205 async with httpx .AsyncClient () as client :
68- return await client .get (url , headers = self .headers )
206+ return await client .get (url , headers = cast ( dict [ str , str ], self .headers ) )
69207
70208 def publish_tool (
71209 self ,
@@ -74,10 +212,10 @@ def publish_tool(
74212 version : str ,
75213 description : str | None ,
76214 encoded_file : str ,
77- available_exports : list [dict [ str , Any ] ] | None = None ,
78- tools_metadata : list [dict [ str , Any ] ] | None = None ,
215+ available_exports : list [AvailableExport ] | None = None ,
216+ tools_metadata : list [ToolMetadata ] | None = None ,
79217 ) -> httpx .Response :
80- params = {
218+ params : PublishToolPayload = {
81219 "handle" : handle ,
82220 "public" : is_public ,
83221 "version" : version ,
@@ -129,13 +267,13 @@ def delete_crew_by_uuid(self, uuid: str) -> httpx.Response:
129267 def list_crews (self ) -> httpx .Response :
130268 return self ._make_request ("GET" , self .CREWS_RESOURCE )
131269
132- def create_crew (self , payload : dict [ str , Any ] ) -> httpx .Response :
270+ def create_crew (self , payload : CreateCrewPayload ) -> httpx .Response :
133271 return self ._make_request ("POST" , self .CREWS_RESOURCE , json = payload )
134272
135273 def get_organizations (self ) -> httpx .Response :
136274 return self ._make_request ("GET" , self .ORGANIZATIONS_RESOURCE )
137275
138- def initialize_trace_batch (self , payload : dict [ str , Any ] ) -> httpx .Response :
276+ def initialize_trace_batch (self , payload : TraceBatchInitPayload ) -> httpx .Response :
139277 return self ._make_request (
140278 "POST" ,
141279 f"{ self .TRACING_RESOURCE } /batches" ,
@@ -144,7 +282,7 @@ def initialize_trace_batch(self, payload: dict[str, Any]) -> httpx.Response:
144282 )
145283
146284 def initialize_ephemeral_trace_batch (
147- self , payload : dict [ str , Any ]
285+ self , payload : TraceBatchInitPayload
148286 ) -> httpx .Response :
149287 return self ._make_request (
150288 "POST" ,
@@ -153,7 +291,7 @@ def initialize_ephemeral_trace_batch(
153291 )
154292
155293 def send_trace_events (
156- self , trace_batch_id : str , payload : dict [ str , Any ]
294+ self , trace_batch_id : str , payload : TraceEventsPayload
157295 ) -> httpx .Response :
158296 return self ._make_request (
159297 "POST" ,
@@ -163,7 +301,7 @@ def send_trace_events(
163301 )
164302
165303 def send_ephemeral_trace_events (
166- self , trace_batch_id : str , payload : dict [ str , Any ]
304+ self , trace_batch_id : str , payload : TraceEventsPayload
167305 ) -> httpx .Response :
168306 return self ._make_request (
169307 "POST" ,
@@ -173,7 +311,7 @@ def send_ephemeral_trace_events(
173311 )
174312
175313 def finalize_trace_batch (
176- self , trace_batch_id : str , payload : dict [ str , Any ]
314+ self , trace_batch_id : str , payload : TraceFinalizePayload
177315 ) -> httpx .Response :
178316 return self ._make_request (
179317 "PATCH" ,
@@ -183,7 +321,7 @@ def finalize_trace_batch(
183321 )
184322
185323 def finalize_ephemeral_trace_batch (
186- self , trace_batch_id : str , payload : dict [ str , Any ]
324+ self , trace_batch_id : str , payload : TraceFinalizePayload
187325 ) -> httpx .Response :
188326 return self ._make_request (
189327 "PATCH" ,
@@ -195,20 +333,28 @@ def finalize_ephemeral_trace_batch(
195333 def mark_trace_batch_as_failed (
196334 self , trace_batch_id : str , error_message : str
197335 ) -> httpx .Response :
336+ payload : TraceFailedPayload = {
337+ "status" : "failed" ,
338+ "failure_reason" : error_message ,
339+ }
198340 return self ._make_request (
199341 "PATCH" ,
200342 f"{ self .TRACING_RESOURCE } /batches/{ trace_batch_id } " ,
201- json = { "status" : "failed" , "failure_reason" : error_message } ,
343+ json = payload ,
202344 timeout = 30 ,
203345 )
204346
205347 def mark_ephemeral_trace_batch_as_failed (
206348 self , trace_batch_id : str , error_message : str
207349 ) -> httpx .Response :
350+ payload : TraceFailedPayload = {
351+ "status" : "failed" ,
352+ "failure_reason" : error_message ,
353+ }
208354 return self ._make_request (
209355 "PATCH" ,
210356 f"{ self .EPHEMERAL_TRACING_RESOURCE } /batches/{ trace_batch_id } " ,
211- json = { "status" : "failed" , "failure_reason" : error_message } ,
357+ json = payload ,
212358 timeout = 30 ,
213359 )
214360
0 commit comments