versionhq 1.1.8.1__py3-none-any.whl → 1.1.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
versionhq/task/model.py CHANGED
@@ -4,7 +4,7 @@ import uuid
4
4
  from concurrent.futures import Future
5
5
  from hashlib import md5
6
6
  from typing import Any, Dict, List, Set, Optional, Tuple, Callable, Type
7
- from typing_extensions import Annotated
7
+ from typing_extensions import Annotated, Self
8
8
 
9
9
  from pydantic import UUID4, BaseModel, Field, PrivateAttr, field_validator, model_validator, create_model, InstanceOf
10
10
  from pydantic_core import PydanticCustomError
@@ -12,7 +12,7 @@ from pydantic_core import PydanticCustomError
12
12
  from versionhq._utils.process_config import process_config
13
13
  from versionhq.task import TaskOutputFormat
14
14
  from versionhq.task.log_handler import TaskOutputStorageHandler
15
- from versionhq.tool.model import Tool, ToolCalled
15
+ from versionhq.tool.model import Tool, ToolSet
16
16
  from versionhq._utils.logger import Logger
17
17
 
18
18
 
@@ -72,22 +72,11 @@ class TaskOutput(BaseModel):
72
72
  raw: str = Field(default="", description="Raw output of the task")
73
73
  json_dict: Dict[str, Any] = Field(default=None, description="`raw` converted to dictionary")
74
74
  pydantic: Optional[Any] = Field(default=None, description="`raw` converted to the abs. pydantic model")
75
+ tool_output: Optional[Any] = Field(default=None, description="store tool result when the task takes tool output as its final output")
75
76
 
76
77
  def __str__(self) -> str:
77
78
  return str(self.pydantic) if self.pydantic else str(self.json_dict) if self.json_dict else self.raw
78
79
 
79
- @property
80
- def json(self) -> Optional[str]:
81
- if self.output_format != TaskOutputFormat.JSON:
82
- raise ValueError(
83
- """
84
- Invalid output format requested.
85
- If you would like to access the JSON output,
86
- pleae make sure to set the output_json property for the task
87
- """
88
- )
89
- return json.dumps(self.json_dict)
90
-
91
80
 
92
81
  def to_dict(self) -> Dict[str, Any]:
93
82
  """
@@ -112,6 +101,19 @@ class TaskOutput(BaseModel):
112
101
  return json.dumps(self.json_dict) if self.json_dict else self.raw[0: 127]
113
102
 
114
103
 
104
+ @property
105
+ def json(self) -> Optional[str]:
106
+ if self.output_format != TaskOutputFormat.JSON:
107
+ raise ValueError(
108
+ """
109
+ Invalid output format requested.
110
+ If you would like to access the JSON output,
111
+ pleae make sure to set the output_json property for the task
112
+ """
113
+ )
114
+ return json.dumps(self.json_dict)
115
+
116
+
115
117
 
116
118
  class Task(BaseModel):
117
119
  """
@@ -124,6 +126,7 @@ class Task(BaseModel):
124
126
  _original_description: str = PrivateAttr(default=None)
125
127
  _logger: Logger = PrivateAttr()
126
128
  _task_output_handler = TaskOutputStorageHandler()
129
+ config: Optional[Dict[str, Any]] = Field(default=None, description="configuration for the agent")
127
130
 
128
131
  id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="unique identifier for the object, not set by user")
129
132
  name: Optional[str] = Field(default=None)
@@ -133,90 +136,35 @@ class Task(BaseModel):
133
136
  expected_output_json: bool = Field(default=True)
134
137
  expected_output_pydantic: bool = Field(default=False)
135
138
  output_field_list: List[ResponseField] = Field(
136
- default=[ResponseField(title="output", type=str, required=False)],
139
+ default_factory=list,
137
140
  description="provide output key and data type. this will be cascaded to the agent via task.prompt()"
138
141
  )
139
142
  output: Optional[TaskOutput] = Field(default=None, description="store the final task output in TaskOutput class")
140
143
 
141
144
  # task setup
142
145
  context: Optional[List["Task"]] = Field(default=None, description="other tasks whose outputs should be used as context")
143
- tools_called: Optional[List[ToolCalled]] = Field(default_factory=list, description="tools that the agent can use for this task")
146
+ prompt_context: Optional[str] = Field(default=None)
147
+
148
+ # tool usage
149
+ tools: Optional[List[ToolSet | Tool | Any]] = Field(default_factory=list, description="tools that the agent can use aside from their tools")
150
+ can_use_agent_tools: bool = Field(default=False, description="whether the agent can use their own tools when executing the task")
144
151
  take_tool_res_as_final: bool = Field(default=False, description="when set True, tools res will be stored in the `TaskOutput`")
145
- allow_delegation: bool = Field(default=False, description="ask other agents for help and run the task instead")
146
152
 
147
- prompt_context: Optional[str] = Field(default=None)
153
+ # execution rules
154
+ allow_delegation: bool = Field(default=False, description="ask other agents for help and run the task instead")
148
155
  async_execution: bool = Field(default=False,description="whether the task should be executed asynchronously or not")
149
- config: Optional[Dict[str, Any]] = Field(default=None, description="configuration for the agent")
150
156
  callback: Optional[Any] = Field(default=None, description="callback to be executed after the task is completed.")
151
157
  callback_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict, description="kwargs for the callback when the callback is callable")
152
158
 
153
159
  # recording
154
160
  processed_by_agents: Set[str] = Field(default_factory=set, description="store responsible agents' roles")
155
- used_tools: int = 0
156
161
  tools_errors: int = 0
157
162
  delegations: int = 0
158
163
 
159
164
 
160
- @property
161
- def output_prompt(self) -> str:
162
- """
163
- Draft prompts on the output format by converting `output_field_list` to dictionary.
164
- """
165
-
166
- output_prompt, output_formats_to_follow = "", dict()
167
- for item in self.output_field_list:
168
- output_formats_to_follow[item.title] = f"<Return your answer in {item.type.__name__}>"
169
-
170
- output_prompt = f"""
171
- Your outputs MUST adhere to the following format and should NOT include any irrelevant elements:
172
- {output_formats_to_follow}
173
- """
174
- return output_prompt
175
-
176
-
177
- @property
178
- def expected_output_formats(self) -> List[TaskOutputFormat]:
179
- """
180
- Return output formats in list with the ENUM item.
181
- `TaskOutputFormat.RAW` is set as default.
182
- """
183
- outputs = [TaskOutputFormat.RAW,]
184
- if self.expected_output_json:
185
- outputs.append(TaskOutputFormat.JSON)
186
- if self.expected_output_pydantic:
187
- outputs.append(TaskOutputFormat.PYDANTIC)
188
- return outputs
189
-
190
-
191
- @property
192
- def key(self) -> str:
193
- output_format = (
194
- TaskOutputFormat.JSON
195
- if self.expected_output_json == True
196
- else (
197
- TaskOutputFormat.PYDANTIC
198
- if self.expected_output_pydantic == True
199
- else TaskOutputFormat.RAW
200
- )
201
- )
202
- source = [self.description, output_format]
203
- return md5("|".join(source).encode(), usedforsecurity=False).hexdigest()
204
-
205
-
206
- @property
207
- def summary(self) -> str:
208
- return f"""
209
- Task ID: {str(self.id)}
210
- "Description": {self.description}
211
- "Prompt": {self.output_prompt}
212
- "Tools": {", ".join([tool_called.tool.name for tool_called in self.tools_called])}
213
- """
214
-
215
-
216
- # validators
217
165
  @model_validator(mode="before")
218
166
  @classmethod
219
- def process_model_config(cls, values: Dict[str, Any]):
167
+ def process_model_config(cls, values: Dict[str, Any]) -> None:
220
168
  return process_config(values_to_update=values, model_class=cls)
221
169
 
222
170
 
@@ -228,7 +176,7 @@ Your outputs MUST adhere to the following format and should NOT include any irre
228
176
 
229
177
 
230
178
  @model_validator(mode="after")
231
- def validate_required_fields(self):
179
+ def validate_required_fields(self) -> Self:
232
180
  required_fields = ["description",]
233
181
  for field in required_fields:
234
182
  if getattr(self, field) is None:
@@ -237,7 +185,7 @@ Your outputs MUST adhere to the following format and should NOT include any irre
237
185
 
238
186
 
239
187
  @model_validator(mode="after")
240
- def set_attributes_based_on_config(self) -> "Task":
188
+ def set_attributes_based_on_config(self) -> Self:
241
189
  """
242
190
  Set attributes based on the agent configuration.
243
191
  """
@@ -248,12 +196,21 @@ Your outputs MUST adhere to the following format and should NOT include any irre
248
196
  return self
249
197
 
250
198
 
251
- ## comment out as we set raw as the default TaskOutputFormat
252
- # @model_validator(mode="after")
253
- # def validate_output_format(self):
254
- # if self.expected_output_json == False and self.expected_output_pydantic == False:
255
- # raise PydanticCustomError("Need to choose at least one output format.")
256
- # return self
199
+ @model_validator(mode="after")
200
+ def set_up_tools(self) -> Self:
201
+ if not self.tools:
202
+ pass
203
+ else:
204
+ tool_list = []
205
+ for item in self.tools:
206
+ if isinstance(item, Tool) or isinstance(item, ToolSet):
207
+ tool_list.append(item)
208
+ elif (isinstance(item, dict) and "function" not in item) or isinstance(item, str):
209
+ pass
210
+ else:
211
+ tool_list.append(item) # address custom tool
212
+ self.tools = tool_list
213
+ return self
257
214
 
258
215
 
259
216
  @model_validator(mode="after")
@@ -317,7 +274,6 @@ Your outputs MUST adhere to the following format and should NOT include any irre
317
274
  return output_json_dict
318
275
 
319
276
 
320
-
321
277
  def create_pydantic_output(self, output_json_dict: Dict[str, Any], raw_result: Any = None) -> Optional[Any]:
322
278
  """
323
279
  Create pydantic output from the `raw` result.
@@ -358,11 +314,10 @@ Your outputs MUST adhere to the following format and should NOT include any irre
358
314
  """
359
315
  if inputs:
360
316
  self.description = self._original_description.format(**inputs)
361
- # self.expected_output = self._original_expected_output.format(**inputs)
362
317
 
363
318
 
364
319
  # task execution
365
- def execute_sync(self, agent, context: Optional[str] = None, tools: Optional[List[Any]] = None) -> TaskOutput:
320
+ def execute_sync(self, agent, context: Optional[str] = None) -> TaskOutput:
366
321
  """
367
322
  Execute the task synchronously.
368
323
  When the task has context, make sure we have executed all the tasks in the context first.
@@ -376,26 +331,26 @@ Your outputs MUST adhere to the following format and should NOT include any irre
376
331
  return self._execute_core(agent, context)
377
332
 
378
333
 
379
- def execute_async(self, agent, context: Optional[str] = None, tools: Optional[List[Any]] = None) -> Future[TaskOutput]:
334
+ def execute_async(self, agent, context: Optional[str] = None) -> Future[TaskOutput]:
380
335
  """
381
336
  Execute the task asynchronously.
382
337
  """
383
338
 
384
339
  future: Future[TaskOutput] = Future()
385
- threading.Thread(daemon=True, target=self._execute_task_async, args=(agent, context, tools, future)).start()
340
+ threading.Thread(daemon=True, target=self._execute_task_async, args=(agent, context, future)).start()
386
341
  return future
387
342
 
388
343
 
389
- def _execute_task_async(self, agent, context: Optional[str], tools: Optional[List[Any]], future: Future[TaskOutput]) -> None:
344
+ def _execute_task_async(self, agent, context: Optional[str], future: Future[TaskOutput]) -> None:
390
345
  """
391
346
  Execute the task asynchronously with context handling.
392
347
  """
393
348
 
394
- result = self._execute_core(agent, context, tools)
349
+ result = self._execute_core(agent, context)
395
350
  future.set_result(result)
396
351
 
397
352
 
398
- def _execute_core(self, agent, context: Optional[str], tools: Optional[List[Any]] = []) -> TaskOutput:
353
+ def _execute_core(self, agent, context: Optional[str]) -> TaskOutput:
399
354
  """
400
355
  Run the core execution logic of the task.
401
356
  To speed up the process, when the format is not expected to return, we will skip the conversion process.
@@ -405,13 +360,14 @@ Your outputs MUST adhere to the following format and should NOT include any irre
405
360
  from versionhq.team.model import Team
406
361
 
407
362
  self.prompt_context = context
363
+ task_output: InstanceOf[TaskOutput] = None
408
364
 
409
365
  if self.allow_delegation:
410
366
  agent_to_delegate = None
411
367
 
412
368
  if hasattr(agent, "team") and isinstance(agent.team, Team):
413
369
  if agent.team.managers:
414
- idling_manager_agents = [manager.agent for manager in agent.team.managers if manager.task is None]
370
+ idling_manager_agents = [manager.agent for manager in agent.team.managers if manager.is_idling]
415
371
  agent_to_delegate = idling_manager_agents[0] if idling_manager_agents else agent.team.managers[0]
416
372
  else:
417
373
  peers = [member.agent for member in agent.team.members if member.is_manager == False and member.agent.id is not agent.id]
@@ -423,20 +379,26 @@ Your outputs MUST adhere to the following format and should NOT include any irre
423
379
  agent = agent_to_delegate
424
380
  self.delegations += 1
425
381
 
426
- output_raw, output_json_dict, output_pydantic = agent.execute_task(task=self, context=context, tools=tools), None, None
382
+ if self.take_tool_res_as_final is True:
383
+ output = agent.execute_task(task=self, context=context)
384
+ task_output = TaskOutput(task_id=self.id, tool_output=output)
427
385
 
428
- if self.expected_output_json:
429
- output_json_dict = self.create_json_output(raw_result=output_raw)
386
+ else:
387
+ output_raw, output_json_dict, output_pydantic = agent.execute_task(task=self, context=context), None, None
430
388
 
431
- if self.expected_output_pydantic:
432
- output_pydantic = self.create_pydantic_output(output_json_dict=output_json_dict)
389
+ if self.expected_output_json:
390
+ output_json_dict = self.create_json_output(raw_result=output_raw)
391
+
392
+ if self.expected_output_pydantic:
393
+ output_pydantic = self.create_pydantic_output(output_json_dict=output_json_dict)
394
+
395
+ task_output = TaskOutput(
396
+ task_id=self.id,
397
+ raw=output_raw,
398
+ pydantic=output_pydantic,
399
+ json_dict=output_json_dict
400
+ )
433
401
 
434
- task_output = TaskOutput(
435
- task_id=self.id,
436
- raw=output_raw,
437
- pydantic=output_pydantic,
438
- json_dict=output_json_dict
439
- )
440
402
  self.output = task_output
441
403
  self.processed_by_agents.add(agent.role)
442
404
 
@@ -456,6 +418,7 @@ Your outputs MUST adhere to the following format and should NOT include any irre
456
418
  # else pydantic_output.model_dump_json() if pydantic_output else result
457
419
  # )
458
420
  # self._save_file(content)
421
+
459
422
  return task_output
460
423
 
461
424
 
@@ -467,6 +430,63 @@ Your outputs MUST adhere to the following format and should NOT include any irre
467
430
  self._task_output_handler.update(task=self, task_index=task_index, was_replayed=was_replayed, inputs=inputs)
468
431
 
469
432
 
433
+ @property
434
+ def output_prompt(self) -> str:
435
+ """
436
+ Draft prompts on the output format by converting `output_field_list` to dictionary.
437
+ """
438
+
439
+ output_prompt, output_formats_to_follow = "", dict()
440
+ for item in self.output_field_list:
441
+ output_formats_to_follow[item.title] = f"<Return your answer in {item.type.__name__}>"
442
+
443
+ output_prompt = f"""
444
+ Your outputs MUST adhere to the following format and should NOT include any irrelevant elements:
445
+ {output_formats_to_follow}
446
+ """
447
+ return output_prompt
448
+
449
+
450
+ @property
451
+ def expected_output_formats(self) -> List[TaskOutputFormat]:
452
+ """
453
+ Return output formats in list with the ENUM item.
454
+ `TaskOutputFormat.RAW` is set as default.
455
+ """
456
+ outputs = [TaskOutputFormat.RAW,]
457
+ if self.expected_output_json:
458
+ outputs.append(TaskOutputFormat.JSON)
459
+ if self.expected_output_pydantic:
460
+ outputs.append(TaskOutputFormat.PYDANTIC)
461
+ return outputs
462
+
463
+
464
+ @property
465
+ def key(self) -> str:
466
+ output_format = (
467
+ TaskOutputFormat.JSON
468
+ if self.expected_output_json == True
469
+ else (
470
+ TaskOutputFormat.PYDANTIC
471
+ if self.expected_output_pydantic == True
472
+ else TaskOutputFormat.RAW
473
+ )
474
+ )
475
+ source = [self.description, output_format]
476
+ return md5("|".join(source).encode(), usedforsecurity=False).hexdigest()
477
+
478
+
479
+ @property
480
+ def summary(self) -> str:
481
+ return f"""
482
+ Task ID: {str(self.id)}
483
+ "Description": {self.description}
484
+ "Prompt": {self.output_prompt}
485
+ "Tools": {", ".join([tool.name for tool in self.tools])}
486
+ """
487
+
488
+
489
+
470
490
 
471
491
  class ConditionalTask(Task):
472
492
  """
versionhq/team/model.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import uuid
2
2
  import warnings
3
3
  import json
4
- from abc import ABC
5
4
  from enum import Enum
6
5
  from dotenv import load_dotenv
7
6
  from concurrent.futures import Future
@@ -108,13 +107,14 @@ class TeamOutput(BaseModel):
108
107
  return res
109
108
 
110
109
 
111
- class TeamMember(ABC, BaseModel):
110
+ class TeamMember(BaseModel):
112
111
  agent: Agent | None = Field(default=None, description="store the agent to be a member")
113
112
  is_manager: bool = Field(default=False)
114
113
  task: Optional[Task] = Field(default=None)
115
114
 
116
- def update(self, task: Task):
117
- self.task = task
115
+ @property
116
+ def is_idling(self):
117
+ return bool(self.task is None)
118
118
 
119
119
 
120
120
  class Team(BaseModel):
@@ -161,54 +161,6 @@ class Team(BaseModel):
161
161
  return self.name if self.name is not None else self.id.__str__
162
162
 
163
163
 
164
- @property
165
- def key(self) -> str:
166
- source = [str(member.agent.id.__str__) for member in self.members] + [str(task.id.__str__) for task in self.tasks]
167
- return md5("|".join(source).encode(), usedforsecurity=False).hexdigest()
168
-
169
-
170
- @property
171
- def managers(self) -> List[TeamMember] | None:
172
- managers = [member for member in self.members if member.is_manager == True]
173
- return managers if len(managers) > 0 else None
174
-
175
-
176
- @property
177
- def manager_tasks(self) -> List[Task] | None:
178
- """
179
- Tasks (incl. team tasks) handled by managers in the team.
180
- """
181
- if self.managers:
182
- tasks = [manager.task for manager in self.managers if manager.task is not None]
183
- return tasks if len(tasks) > 0 else None
184
-
185
- return None
186
-
187
-
188
- @property
189
- def tasks(self):
190
- """
191
- Return all the tasks that the team needs to handle in order of priority:
192
- 1. team tasks, -> assigned to the member
193
- 2. manager_task,
194
- 3. members' tasks
195
- """
196
-
197
- team_tasks = self.team_tasks
198
- manager_tasks = [member.task for member in self.members if member.is_manager == True and member.task is not None and member.task not in team_tasks]
199
- member_tasks = [member.task for member in self.members if member.is_manager == False and member.task is not None and member.task not in team_tasks]
200
-
201
- return team_tasks + manager_tasks + member_tasks
202
-
203
-
204
- @property
205
- def member_tasks_without_agent(self) -> List[Task] | None:
206
- if self.members:
207
- return [member.task for member in self.members if member.agent is None]
208
-
209
- return None
210
-
211
-
212
164
  @field_validator("id", mode="before")
213
165
  @classmethod
214
166
  def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
@@ -218,7 +170,7 @@ class Team(BaseModel):
218
170
 
219
171
 
220
172
  @model_validator(mode="after")
221
- def validate_tasks(self):
173
+ def assess_tasks(self):
222
174
  """
223
175
  Validates if the model recognize all tasks that the team needs to handle.
224
176
  """
@@ -261,7 +213,7 @@ class Team(BaseModel):
261
213
  if self.process == TaskHandlingProcess.sequential and self.team_tasks is None:
262
214
  for member in self.members:
263
215
  if member.task is None:
264
- raise PydanticCustomError("missing_agent_in_task", f"Sequential process error: Agent is missing the task", {})
216
+ raise PydanticCustomError("missing_agent_in_task", "Sequential process error: Agent is missing the task", {})
265
217
  return self
266
218
 
267
219
  @model_validator(mode="after")
@@ -280,11 +232,7 @@ class Team(BaseModel):
280
232
  break
281
233
 
282
234
  if async_task_count > 1:
283
- raise PydanticCustomError(
284
- "async_task_count",
285
- "The team must end with max. one asynchronous task.",
286
- {},
287
- )
235
+ raise PydanticCustomError("async_task_count", "The team must end with max. one asynchronous task.", {})
288
236
  return self
289
237
 
290
238
 
@@ -305,12 +253,11 @@ class Team(BaseModel):
305
253
  """
306
254
 
307
255
  team_planner = TeamPlanner(tasks=self.tasks, planner_llm=self.planning_llm)
308
- idling_managers: List[TeamMember] = [member for member in self.members if member.task is None and member.is_manager is True]
309
- idling_members: List[TeamMember] = [member for member in self.members if member.task is None and member.is_manager is False]
256
+ idling_managers: List[TeamMember] = [member for member in self.members if member.is_idling and member.is_manager is True]
257
+ idling_members: List[TeamMember] = [member for member in self.members if member.is_idling and member.is_manager is False]
310
258
  unassigned_tasks: List[Task] = self.member_tasks_without_agent
311
259
  new_team_members: List[TeamMember] = []
312
260
 
313
-
314
261
  if self.team_tasks:
315
262
  candidates = idling_managers + idling_members
316
263
  if candidates:
@@ -433,16 +380,15 @@ class Team(BaseModel):
433
380
  if skipped_task_output:
434
381
  continue
435
382
 
436
- # self._prepare_agent_tools(task)
437
383
  # self._log_task_start(task, responsible_agent)
438
384
 
439
385
  if task.async_execution:
440
386
  context = create_raw_outputs(tasks=[task, ], task_outputs=([last_sync_output,] if last_sync_output else []))
441
- future = task.execute_async(agent=responsible_agent, context=context, tools=responsible_agent.tools)
387
+ future = task.execute_async(agent=responsible_agent, context=context)
442
388
  futures.append((task, future, task_index))
443
389
  else:
444
390
  context = create_raw_outputs(tasks=[task,], task_outputs=([last_sync_output,] if last_sync_output else [] ))
445
- task_output = task.execute_sync(agent=responsible_agent, context=context, tools=responsible_agent.tools)
391
+ task_output = task.execute_sync(agent=responsible_agent, context=context)
446
392
  if self.managers and responsible_agent in [manager.agent for manager in self.managers]:
447
393
  lead_task_output = task_output
448
394
 
@@ -468,7 +414,6 @@ class Team(BaseModel):
468
414
 
469
415
  metrics: List[UsageMetrics] = []
470
416
 
471
-
472
417
  if self.team_tasks or self.member_tasks_without_agent:
473
418
  self._handle_team_planning()
474
419
 
@@ -511,3 +456,51 @@ class Team(BaseModel):
511
456
  self.usage_metrics.add_usage_metrics(metric)
512
457
 
513
458
  return result
459
+
460
+
461
+ @property
462
+ def key(self) -> str:
463
+ source = [str(member.agent.id.__str__) for member in self.members] + [str(task.id.__str__) for task in self.tasks]
464
+ return md5("|".join(source).encode(), usedforsecurity=False).hexdigest()
465
+
466
+
467
+ @property
468
+ def managers(self) -> List[TeamMember] | None:
469
+ managers = [member for member in self.members if member.is_manager == True]
470
+ return managers if len(managers) > 0 else None
471
+
472
+
473
+ @property
474
+ def manager_tasks(self) -> List[Task] | None:
475
+ """
476
+ Tasks (incl. team tasks) handled by managers in the team.
477
+ """
478
+ if self.managers:
479
+ tasks = [manager.task for manager in self.managers if manager.task is not None]
480
+ return tasks if len(tasks) > 0 else None
481
+
482
+ return None
483
+
484
+
485
+ @property
486
+ def tasks(self):
487
+ """
488
+ Return all the tasks that the team needs to handle in order of priority:
489
+ 1. team tasks, -> assigned to the member
490
+ 2. manager_task,
491
+ 3. members' tasks
492
+ """
493
+
494
+ team_tasks = self.team_tasks
495
+ manager_tasks = [member.task for member in self.members if member.is_manager == True and member.task is not None and member.task not in team_tasks]
496
+ member_tasks = [member.task for member in self.members if member.is_manager == False and member.task is not None and member.task not in team_tasks]
497
+
498
+ return team_tasks + manager_tasks + member_tasks
499
+
500
+
501
+ @property
502
+ def member_tasks_without_agent(self) -> List[Task] | None:
503
+ if self.members:
504
+ return [member.task for member in self.members if member.agent is None]
505
+
506
+ return None
@@ -0,0 +1,53 @@
1
+ from enum import Enum
2
+
3
+ DEFAULT_AUTH_SCHEME = "OAUTH2"
4
+
5
+ class ComposioAuthScheme(str, Enum):
6
+ OAUTH2 = "OAUTH2"
7
+ BEARER_TOKEN = "BEARER_TOKEN"
8
+ API_KEY = "API_KEY"
9
+
10
+
11
+ class ComposioAppName(str, Enum):
12
+ """
13
+ Enum to store app names that we can connect via Composio as data pipelines or destination services.
14
+ """
15
+
16
+ SALESFORCE = "salesforce"
17
+ AIRTABLE = "airtable"
18
+ MAILCHIMP = "mailchimp"
19
+ HUBSPOT = "hubspot"
20
+ KLAVIYO = "klaviyo"
21
+ GOOGLESHEET = "googlesheets"
22
+ GMAIL = "gmail"
23
+ FACEBOOK = "facebook"
24
+ TWITTER = "twitter"
25
+ TWITTER_MEDIA = "twitter_media"
26
+ LINKEDIN = "linkedin"
27
+
28
+
29
+ composio_app_set = [
30
+ (ComposioAppName.SALESFORCE, ComposioAuthScheme.OAUTH2),
31
+ (ComposioAppName.AIRTABLE, ComposioAuthScheme.OAUTH2, ComposioAuthScheme.API_KEY, ComposioAuthScheme.BEARER_TOKEN),
32
+ (ComposioAppName.MAILCHIMP, ComposioAuthScheme.OAUTH2),
33
+ (ComposioAppName.HUBSPOT, ComposioAuthScheme.OAUTH2, ComposioAuthScheme.BEARER_TOKEN),
34
+ (ComposioAppName.KLAVIYO, ComposioAuthScheme.OAUTH2, ComposioAuthScheme.API_KEY),
35
+ (ComposioAppName.GOOGLESHEET, ComposioAuthScheme.OAUTH2),
36
+ (ComposioAppName.GMAIL, ComposioAuthScheme.OAUTH2, ComposioAuthScheme.BEARER_TOKEN),
37
+ (ComposioAppName.TWITTER, ComposioAuthScheme.OAUTH2),
38
+ (ComposioAppName.TWITTER_MEDIA, ComposioAuthScheme.OAUTH2),
39
+ (ComposioAppName.FACEBOOK, ComposioAuthScheme.OAUTH2),
40
+ (ComposioAppName.LINKEDIN, ComposioAuthScheme.OAUTH2),
41
+ ]
42
+
43
+ class COMPOSIO_STATUS(str, Enum):
44
+ INITIATED = "initiated"
45
+ ACTIVE = "active"
46
+ FAILED = "failed"
47
+
48
+
49
+
50
+ class ComposioAction(str, Enum):
51
+ """
52
+ Enum to store composio's action that can be called via `Actions.xxx`
53
+ """