diff --git a/src/control_flow/__init__.py b/src/control_flow/__init__.py index 4a8e2bcb..7ab48972 100644 --- a/src/control_flow/__init__.py +++ b/src/control_flow/__init__.py @@ -1,9 +1,11 @@ from .settings import settings # from .agent_old import ai_task, Agent, run_ai -from .core.flow import Flow +from .core.flow import Flow, reset_global_flow as _reset_global_flow from .core.agent import Agent from .core.task import Task from .core.controller.controller import Controller from .instructions import instructions from .dx import ai_flow, run_ai, ai_task + +_reset_global_flow() diff --git a/src/control_flow/core/agent.py b/src/control_flow/core/agent.py index c43a6464..201eb224 100644 --- a/src/control_flow/core/agent.py +++ b/src/control_flow/core/agent.py @@ -17,7 +17,7 @@ class Agent(Assistant, ControlFlowModel, ExposeSyncMethodsMixin): - name: str = "Agent" + name: str user_access: bool = Field( False, description="If True, the agent is given tools for interacting with a human user.", @@ -42,6 +42,3 @@ async def run_async(self, tasks: list[Task] | Task | None = None): def __hash__(self): return id(self) - - -DEFAULT_AGENT = Agent(name="Marvin") diff --git a/src/control_flow/core/controller/instruction_template.py b/src/control_flow/core/controller/instruction_template.py index 3d13d2a3..bedc6ab7 100644 --- a/src/control_flow/core/controller/instruction_template.py +++ b/src/control_flow/core/controller/instruction_template.py @@ -65,21 +65,18 @@ class TasksTemplate(Template): You have been assigned to complete certain tasks. Each task has an objective and criteria for success. Your job is to perform any required - actions and then mark each assigned task as successful. If a task also + actions and then mark each assigned task as successful. If a task requires a result, you must provide it. Only work on tasks that are - assigned to you. If the task requires a result, do not also post the - result in a message, as this would be redundant. Messages should be used - only to provide context that is not captured in task results. - - A "parent" is a task that spawned another task as a subtask. Generally, - the subtasks will need to be completed BEFORE the parent task. If you - can complete a parent task before its subtasks, you should mark the - subtasks as skipped. - - Tasks have a "depends_on" list of upstream tasks that must be completed - before the task itself can be completed. The `mark_success` tool will - not be available until all dependencies are met. + assigned to you. + A task may have a "parent", meaning it is a subtask and should be + completed before its parent. You can `skip` a subtask if you are able to + complete the parent task first. + + A task may have tasks it "depends_on", meaning it must be completed + after the tasks it depends on. Tasks will error if you try to complete + them before their dependencies are met. + ### Current tasks At the start of your turn, these are all the tasks being worked on. This @@ -98,22 +95,23 @@ class TasksTemplate(Template): result. For example, a task that asks you to choose, discuss, or perform an action must be completed by posting messages before the task is marked complete. The objective may require participation from multiple - agents. Do not mark a task as complete until the objective is fully met. + agents. Do not mark a task as complete until the objective is fully met. + + If a task does take a result, do not also write the result in a message. - Some tasks may require collaboration with other agents to be completed; others - may take you multiple attempts. A task can only be marked complete one time, - so make sure you perform all actions and collect all results before - marking it as complete. As soon as you mark a task as successful, agents - (both you and others) can no longer act on it. + Some tasks may require collaboration with other agents to be completed; + others may take you multiple attempts. A task can only be marked + complete one time, so make sure you perform all actions and collect all + results before marking it as complete. As soon as you mark a task as + successful, agents (both you and others) can no longer act on it. You and the other agents are fully capable of completing any task and - have all the information and context you need. Tasks should only be marked - failed due to technical errors like a broken or erroring tool or unresponsive human. - Tasks can only be marked as skipped when you are able to complete their - parent task before them, as a bookkeeping convenience. Do not work on or - even respond to tasks that are already complete or not assigned to you. - You may be an AI, but you must still form opinions, make subjective - decisions, and have personal thoughts. + have all the information and context you need. Tasks should only be + marked failed due to technical errors like a broken or erroring tool or + unresponsive human. Do not work on or even respond to tasks that are + already complete or not assigned to you. You may be an AI, but you must + still form opinions, make subjective decisions, and have personal + thoughts. """ tasks: list[Task] diff --git a/src/control_flow/core/flow.py b/src/control_flow/core/flow.py index 40b67b4f..a3737f0d 100644 --- a/src/control_flow/core/flow.py +++ b/src/control_flow/core/flow.py @@ -1,5 +1,5 @@ from contextlib import contextmanager -from typing import Callable, Literal +from typing import TYPE_CHECKING, Callable, Literal from marvin.beta.assistants import Thread from openai.types.beta.threads import Message @@ -10,14 +10,32 @@ from control_flow.utilities.logging import get_logger from control_flow.utilities.types import AssistantTool, ControlFlowModel +if TYPE_CHECKING: + from control_flow.core.agent import Agent logger = get_logger(__name__) +def default_agent(): + from control_flow.core.agent import Agent + + return [ + Agent( + name="Marvin", + description="I am Marvin, the default agent for Control Flow.", + ) + ] + + class Flow(ControlFlowModel): thread: Thread = Field(None, validate_default=True) tools: list[AssistantTool | Callable] = Field( [], description="Tools that will be available to every agent in the flow" ) + agents: list["Agent"] = Field( + default_factory=default_agent, + description="The default agents for the flow. These agents will be used " + "for any task that does not specify agents.", + ) model: str | None = None context: dict = {} @@ -48,7 +66,7 @@ def __exit__(self, *exc_info): return self.__cm.__exit__(*exc_info) -GLOBAL_FLOW = Flow() +GLOBAL_FLOW = None def get_flow() -> Flow: diff --git a/src/control_flow/core/task.py b/src/control_flow/core/task.py index b8d9d7de..770cb855 100644 --- a/src/control_flow/core/task.py +++ b/src/control_flow/core/task.py @@ -22,6 +22,7 @@ model_validator, ) +from control_flow.core.flow import get_flow from control_flow.utilities.context import ctx from control_flow.utilities.logging import get_logger from control_flow.utilities.prefect import wrap_prefect_tool @@ -86,9 +87,7 @@ def __repr__(self): @field_validator("agents", mode="before") def _default_agent(cls, v): if v is None: - from control_flow.core.agent import DEFAULT_AGENT - - return [DEFAULT_AGENT] + v = get_flow().agents return v @field_validator("result_type", mode="before")