From 22badbf0b4c0b1e382634e7bf52fd9d5fa03bb4c Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:56:24 -0400 Subject: [PATCH] Update dependency docs --- docs/mint.json | 5 +- docs/patterns/dependencies.mdx | 113 +++++++++++++++++++++------------ docs/patterns/subtasks.mdx | 64 ------------------- 3 files changed, 74 insertions(+), 108 deletions(-) delete mode 100644 docs/patterns/subtasks.mdx diff --git a/docs/mint.json b/docs/mint.json index 46c74bf2..6c5ed467 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -56,10 +56,9 @@ "patterns/task-results", "patterns/tools", "patterns/interactivity", - "patterns/instructions", - "patterns/planning", "patterns/dependencies", - "patterns/subtasks" + "patterns/instructions", + "patterns/planning" ] }, { diff --git a/docs/patterns/dependencies.mdx b/docs/patterns/dependencies.mdx index 30be3d08..ebc44ae3 100644 --- a/docs/patterns/dependencies.mdx +++ b/docs/patterns/dependencies.mdx @@ -1,16 +1,16 @@ --- title: Dependencies -description: Manage task dependencies to create complex, multi-step workflows. -icon: link +description: Manage task dependencies and subtasks to create complex workflows. +icon: sitemap --- -In complex workflows, tasks often need to be executed in a specific order. Some tasks may rely on the outputs of others, or there might be a logical sequence that must be followed to achieve the desired outcome. ControlFlow provides several mechanisms to define and manage these task dependencies, ensuring that your workflow executes in the correct order and that data flows properly between tasks. +In complex workflows, tasks often need to be executed in a specific order. Some tasks may rely on the outputs of others, or there might be a logical sequence that must be followed to achieve the desired outcome. ControlFlow provides several mechanisms to define and manage these task relationships, ensuring that your workflow executes in the correct order and that data flows properly between tasks. -ControlFlow offers three primary ways to establish dependencies between tasks: sequential dependencies, context dependencies, and subtask relationships. Each method has its own use cases and benefits, allowing you to structure your workflows in the most appropriate way for your specific needs. +ControlFlow offers two primary ways to establish relationships between tasks: sequential dependencies and subtask relationships. Each method has its own use cases and benefits, allowing you to structure your workflows in the most appropriate way for your specific needs. -# Sequential Dependencies +## Upstream dependencies -Sequential dependencies are the most straightforward way to specify that one task must wait for another to complete before it can begin. This is done using the `depends_on` parameter when creating a task. +Upstream dependencies are the most straightforward way to specify that one task must wait for another to complete before it can begin. This is done using the `depends_on` parameter when creating a task. ```python import controlflow as cf @@ -33,58 +33,87 @@ print(result) In this example, `analyze_sources` will not start until `gather_sources` has completed successfully. -## Context Dependencies +## Subtasks -Context dependencies are created when you use the result of one task as input for another. This creates an implicit dependency between the tasks. +Subtasks create a hierarchical dependency structure. A parent task can not be completed until all of its subtasks have finished. This hierarchical structure enables you to create detailed, step-by-step workflows that an AI agent can follow, ensuring thorough and accurate task completion. -```python +### Imperative creation + +You can create subtasks imperatively by passing the parent task as an argument when creating a new task: + + +```python Code import controlflow as cf -@cf.flow -def research_flow(): - gather_sources = cf.Task("Gather research sources", result_type=list[str]) - - analyze_sources = cf.Task( - "Analyze gathered sources", - result_type=dict, - context={"sources": gather_sources} # implicit dependency - ) - - return analyze_sources +parent_task = cf.Task("Create a greeting") -result = research_flow() +t1 = cf.Task("Choose a greeting word", parent=parent_task) +t2 = cf.Task("Add a friendly adjective", parent=parent_task, depends_on=[t1]) +t3 = cf.Task("Construct the final greeting", parent=parent_task, depends_on=[t2]) + +result = parent_task.run() print(result) ``` -Here, `analyze_sources` depends on `gather_sources` because it needs the `sources` data to perform its analysis. +```text t1 Result +Hello +``` -## Subtask Relationships +```text t2 Result +Warm +``` -Subtasks create a hierarchical dependency structure. A parent task is considered complete only when all its subtasks have finished. +```text t3 Result +Hello, I wish you a warm welcome! +``` -```python +```text parent_task Result +Hello, I wish you a warm welcome! +``` + + + + +### Context managers + +Another way to create subtasks is by using a context manager. This approach allows you to dynamically generate and execute subtasks within the scope of a parent task. + + +```python Code import controlflow as cf -@cf.flow -def review_flow(doc): - with cf.Task("Review the document", result_type=str, context=dict(doc=doc)) as review: - cf.Task("Proofread") - cf.Task("Format") - - return review +with cf.Task("Create a greeting") as parent_task: + t1 = cf.Task("Choose a greeting word") + t2 = cf.Task("Add a friendly adjective", depends_on=[t1]) + t3 = cf.Task("Construct the final greeting", depends_on=[t2]) -result = review_flow() +result = parent_task.run() print(result) ``` -In this example, the "Review the document" task won't be considered complete until both the "Proofread" and "Format" subtasks have finished. +```text t1 Result +Hello +``` + +```text t2 Result +Warm +``` + +```text t3 Result +Hello, I wish you a warm welcome! +``` + +```text parent_task Result +Hello, I wish you a warm welcome! +``` + + ## Automatic Execution of Dependencies A key feature of ControlFlow's dependency management is that you don't need to explicitly run dependent tasks. When you run a task, ControlFlow automatically executes all of its dependencies, including: - Tasks specified in the `depends_on` parameter -- Tasks used in the `context` parameter - Subtasks (for parent tasks) This means that when you run a flow or task, you only need to run or return the final task(s) in the workflow DAG. ControlFlow will ensure that all necessary upstream tasks and subtasks are executed in the correct order. @@ -101,7 +130,7 @@ def research_flow(): analyze_sources = cf.Task( "Analyze gathered sources", result_type=dict, - context={"sources": gather_sources} + depends_on=[gather_sources] ) write_report = cf.Task( @@ -110,10 +139,12 @@ def research_flow(): depends_on=[analyze_sources] ) - # Only need to return or run the final task - return write_report + # Only need to run the final task + return write_report.run() -result = research_flow() -print(result) +research_flow() ``` -In this example, running write_report will automatically trigger the execution of analyze_sources, which in turn will trigger gather_sources. You don't need to explicitly run or return gather_sources or analyze_sources. + +In this example, running `write_report` will automatically trigger the execution of `analyze_sources`, which in turn will trigger `gather_sources`. You don't need to explicitly run or return `gather_sources` or `analyze_sources`. + +To learn more, see [running tasks](/patterns/running-tasks). \ No newline at end of file diff --git a/docs/patterns/subtasks.mdx b/docs/patterns/subtasks.mdx deleted file mode 100644 index fdd5e53c..00000000 --- a/docs/patterns/subtasks.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Subtasks -description: Break complex or open-ended tasks into smaller, manageable steps. -icon: sitemap ---- - - -In complex AI workflows, breaking down large tasks into smaller, manageable steps can significantly improve the quality and reliability of the results. ControlFlow's subtask feature provides a powerful mechanism for structuring these hierarchical task relationships, allowing you to guide AI agents through a series of logical steps to achieve a larger goal. - -Subtasks in ControlFlow are child tasks that must be completed before their parent task can be considered finished. This hierarchical structure enables you to create detailed, step-by-step workflows that an AI agent can follow, ensuring thorough and accurate task completion. - - - -When you run a parent task, all of its subtasks are [automatically executed](/patterns/dependencies#automatic-execution-of-dependencies) because they become dependencies of the parent. You don't need to also explicitly run the subtasks or return them from the flow. - - - -## Creating Subtasks with Context Managers - -One way to create subtasks is by using a context manager. This approach allows you to dynamically generate and execute subtasks within the scope of a parent task. - -```python -import controlflow as cf - -@cf.flow -def counting_flow(): - with cf.Task("Count to three", result_type=None) as count_task: - cf.Task("Say one") - cf.Task("Say two") - cf.Task("Say three") - - return count_task - -result = counting_flow() -``` - -In this example, the AI agent must complete all three subtasks ("Say one", "Say two", "Say three") before the parent task "Count to three" can be considered complete. - -## Creating Subtasks Imperatively - -You can also create subtasks imperatively by passing the parent task as an argument when creating a new task. - -```python -import controlflow as cf - -@cf.flow -def greeting_flow(): - parent_task = cf.Task("Create a greeting", result_type=str) - - cf.Task("Choose a greeting word", parent=parent_task) - cf.Task("Add a friendly adjective", parent=parent_task) - cf.Task("Construct the final greeting", parent=parent_task) - - return parent_task - -result = greeting_flow() -``` -This approach provides more flexibility in creating and organizing subtasks, especially when the parent-child relationships are determined dynamically during runtime. - -## Generating Subtasks Automatically - -For more complex scenarios where you need to automatically generate subtasks based on the parent task's objective, ControlFlow provides a `generate_subtasks()` method. This powerful feature allows for dynamic task planning and is especially useful for breaking down complex tasks into more manageable steps. - -For more information on how to use `generate_subtasks()`, please refer to the [Task Planning pattern](/patterns/task-planning).