Skip to content

Commit 83f6951

Browse files
committed
introduce task deliverable
1 parent f3cd652 commit 83f6951

2 files changed

Lines changed: 142 additions & 64 deletions

File tree

bindings/ceylon/ceylon/llm/llm_task_coordinator.py

Lines changed: 120 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from textwrap import dedent
12
from typing import Dict, List, Set
23

34
import networkx as nx
@@ -10,6 +11,27 @@
1011
from ceylon.static_val import DEFAULT_WORKSPACE_ID, DEFAULT_ADMIN_PORT
1112
from ceylon.task import TaskResult, Task, SubTask
1213
from ceylon.task.task_coordinator import TaskCoordinator
14+
from ceylon.task.task_operation import TaskDeliverable
15+
16+
17+
class TaskDeliverableModel(pydantic.v1.BaseModel):
18+
objectives: List[str] = pydantic.v1.Field(
19+
description="the objectives of the task, Explains the task in detail",
20+
default=[]
21+
)
22+
final_output: str = pydantic.v1.Field(
23+
description="the final output of the task",
24+
default="")
25+
final_output_type: str = pydantic.v1.Field(
26+
description="the type of the final output of the task",
27+
default="")
28+
29+
def to_v2(self) -> TaskDeliverable:
30+
return TaskDeliverable(
31+
objectives=self.objectives,
32+
final_output=self.final_output,
33+
final_output_type=self.final_output_type
34+
)
1335

1436

1537
class SubTaskModel(pydantic.v1.BaseModel):
@@ -63,6 +85,10 @@ def initialize_team_network(self):
6385
self.team_network.add_node(agent.details().name, role=agent.details().role)
6486

6587
async def update_task(self, idx: int, task: Task):
88+
89+
task_deliverable = await self.build_task_deliverable(task)
90+
task.set_deliverable(task_deliverable)
91+
6692
if len(task.subtasks) == 0:
6793
sub_tasks = await self.generate_tasks_from_description(task)
6894
for sub_task in sub_tasks:
@@ -83,7 +109,7 @@ async def update_team_network(self, task: Task):
83109

84110
async def analyze_team_dynamics(self) -> str:
85111
prompt = PromptTemplate.from_template(
86-
"""
112+
dedent("""
87113
Analyze the following team network and provide insights on team dynamics:
88114
89115
Nodes (Agents): {nodes}
@@ -96,7 +122,7 @@ async def analyze_team_dynamics(self) -> str:
96122
97123
Respond in a concise paragraph.
98124
"""
99-
)
125+
))
100126

101127
nodes = [f"{node}: {data}" for node, data in self.team_network.nodes(data=True)]
102128
edges = [f"{u} - {v}: {data}" for u, v, data in self.team_network.edges(data=True)]
@@ -139,15 +165,15 @@ async def get_best_agent_for_subtask(self, subtask: SubTask) -> str:
139165
prompt_template = ChatPromptTemplate.from_template(
140166
"""
141167
Select the most suitable agent for this subtask:
142-
168+
143169
Subtask: {subtask_description}
144170
Required specialty: {required_specialty}
145-
171+
146172
Agent specialties:
147173
{agent_specialties}
148-
174+
149175
Available agents: {agent_names}
150-
176+
151177
Important: Respond ONLY with the exact name of the single most suitable agent from the available agents list.
152178
If no agent perfectly matches, choose the closest fit.
153179
"""
@@ -175,114 +201,144 @@ def get_valid_agent_name(max_attempts=3):
175201

176202
return get_valid_agent_name()
177203

204+
async def generate_final_sub_task_from_description(self, task: Task) -> SubTask:
205+
prompt = PromptTemplate.from_template(
206+
dedent("""
207+
Given the following job description and existing subtasks, add a final delivery step to complete the project.
208+
This final step should focus on delivering the completed work.
209+
210+
Job Description: {description}
211+
Job Deliverable Data : {task_deliverable}
212+
213+
Existing Subtasks:
214+
{existing_subtasks}
215+
216+
Respond with only the final delivery step in the following format:
217+
218+
Final Step:
219+
<Specific description of the final delivery step> - Specialty: <Required specialty or skill> - Depends on: <Names of subtasks that must be completed before this one>
220+
221+
Additional Considerations:
222+
- Ensure the final step encompasses presenting or delivering the completed work
223+
- Use clear, action-oriented language for the description
224+
- Include dependencies on relevant existing subtasks
225+
- Focus on delivering the functionality and value of the completed project
226+
""")
227+
)
228+
229+
# Chain
230+
structured_llm = self.tool_llm.with_structured_output(SubTaskModel)
231+
chain = prompt | structured_llm
232+
sub_task: SubTaskModel = chain.invoke(input={
233+
"description": task.description,
234+
"task_deliverable": task.task_deliverable,
235+
"existing_subtasks": "\n".join([f"{t.name}- {t.description}" for t in task.subtasks.values()])
236+
})
237+
print(sub_task)
238+
return sub_task.to_v2(task.id)
239+
178240
async def generate_tasks_from_description(self, task: Task) -> List[SubTask]:
241+
242+
# Prompt template
179243
prompt = PromptTemplate.from_template(
180-
"""
181-
Given the following job description, context, and team goal, break it down into a main task and 3-5 key subtasks. Each subtask should have a specific description and required specialty.
244+
dedent(
245+
"""
246+
Given the following job description, break it down into a main task and 3-5 key subtasks. Each subtask should have a specific description and required specialty.
182247
183248
Job Description: {description}
184-
Context: {context}
185-
Team Goal: {team_goal}
249+
Job Deliverable Data : {task_deliverable}
186250
187251
Respond with the tasks and their subtasks in the following format:
188252
189253
Main Task: <Concise description of the overall task>
190254
191255
Subtasks:
192-
1. <Specific subtask description> - Specialty: <Required specialty or skill> - Depends on: <Subtasks name that must be completed before this one>
193-
2. <Specific subtask description> - Specialty: <Required specialty or skill> - Depends on: <Subtasks name that must be completed before this one>
194-
3. <Specific subtask description> - Specialty: <Required specialty or skill> - Depends on: <Subtasks name that must be completed before this one>
256+
1. <Describe the first action item needed to progress towards the objective.> - Specialty: <Required specialty or skill> - Depends on: <Subtasks name that must be completed before this one>
257+
2. <Describe the second action item needed to progress towards the objective.> - Specialty: <Required specialty or skill> - Depends on: <Subtasks name that must be completed before this one>
258+
3. <Describe the third action item needed to progress towards the objective.> - Specialty: <Required specialty or skill> - Depends on: <Subtasks name that must be completed before this one>
195259
(Add up to 2 more subtasks if necessary)
196260
197261
Additional Considerations:
198-
- Ensure the subtasks align with the given context and contribute to the team goal
199262
- Prioritize the subtasks in order of importance or chronological sequence
200263
- Ensure each subtask is distinct and contributes uniquely to the main task
201264
- Use clear, action-oriented language for each description
202265
- If applicable, include any interdependencies between subtasks
203-
"""
266+
""")
204267
)
205268

269+
# Chain
206270
structured_llm = self.tool_llm.with_structured_output(SubTaskList)
207271
chain = prompt | structured_llm
208272
sub_task_list = chain.invoke(input={
209273
"description": task.description,
210-
"context": self.context,
211-
"team_goal": self.team_goal
274+
"metadata": task.metadata,
275+
"task_deliverable": task.task_deliverable,
212276
})
277+
# Make sure subtasks are valid
213278
sub_task_list = self.recheck_and_update_subtasks(subtasks=sub_task_list)
214279
return [t.to_v2(task.id) for t in sub_task_list.sub_task_list]
215280

216-
async def generate_final_sub_task_from_description(self, task: Task) -> SubTask:
217-
prompt = PromptTemplate.from_template(
218-
"""
219-
Given the following job description, existing subtasks, context, and team goal, add a final delivery step to complete the project.
220-
This final step should focus on delivering the completed work and ensuring it aligns with the team goal.
221-
222-
Job Description: {description}
223-
Context: {context}
224-
Team Goal: {team_goal}
225-
226-
Existing Subtasks:
227-
{existing_subtasks}
228-
229-
Respond with only the final delivery step in the following format:
230-
231-
Final Step:
232-
<Specific description of the final delivery step> - Specialty: <Required specialty or skill> - Depends on: <Names of subtasks that must be completed before this one>
233-
234-
Additional Considerations:
235-
- Ensure the final step encompasses presenting or delivering the completed work
236-
- Make sure the final step aligns with the context and contributes to the team goal
237-
- Use clear, action-oriented language for the description
238-
- Include dependencies on relevant existing subtasks
239-
- Focus on delivering the functionality and value of the completed project
240-
"""
241-
)
242-
243-
structured_llm = self.tool_llm.with_structured_output(SubTaskModel)
244-
chain = prompt | structured_llm
245-
sub_task: SubTaskModel = chain.invoke(input={
246-
"description": task.description,
247-
"existing_subtasks": "\n".join([f"{t.name}- {t.description}" for t in task.subtasks.values()]),
248-
"context": self.context,
249-
"team_goal": self.team_goal
250-
})
251-
return sub_task.to_v2(task.id)
252-
253281
def recheck_and_update_subtasks(self, subtasks):
254-
subtask_validation_template = PromptTemplate.from_template("""
282+
subtask_validation_template = PromptTemplate.from_template(
283+
dedent("""
255284
You are tasked with reviewing and modifying a list of SubTasks. Please process the following list of SubTasks according to these criteria:
256-
285+
257286
1. Name Format:
258287
- Convert all SubTask names to snake_case format.
259288
- Ensure each word is lowercase and separated by underscores.
260289
- Example: "draftArticleOutline" should become "draft_article_outline"
261-
290+
262291
2. Dependency Validation:
263292
- Check that all dependencies listed in the 'depends_on' field are valid SubTask names within the list.
264293
- Ensure that the dependency names are also in snake_case format.
265294
- Remove any dependencies that do not correspond to existing SubTask names.
266-
295+
267296
3. Circular Dependency Check:
268297
- Verify that there are no circular dependencies among the SubTasks.
269298
- A circular dependency occurs when Task A depends on Task B, and Task B directly or indirectly depends on Task A.
270-
299+
271300
4. Consistency:
272301
- Ensure all other fields (id, parent_task_id, description, required_specialty, completed, completed_at, result) remain unchanged.
273-
302+
274303
5. Output Format:
275304
- Return the modified list of SubTasks in the same structure as the input.
276-
305+
277306
Here is the list of SubTasks to process:
278-
307+
279308
{subtasks}
280-
309+
281310
Please provide the updated list of SubTasks with all the above modifications and validations applied.
282-
""")
311+
"""))
283312
structured_llm = self.tool_llm.with_structured_output(SubTaskList)
284313
chain = subtask_validation_template | structured_llm
285314
sub_task_list = chain.invoke(input={
286315
"subtasks": subtasks
287316
})
288317
return sub_task_list
318+
319+
async def build_task_deliverable(self, task: Task):
320+
build_task_deliverable_template = PromptTemplate.from_template(dedent("""
321+
Context: {context}
322+
323+
Team Objectives: {objectives}
324+
325+
Task Description: {task_description}
326+
327+
Based on the given context, team objectives, and task description, please provide:
328+
329+
1. Clear goals for the task management application project
330+
2. A list of specific deliverables
331+
3. Key features to be implemented
332+
4. Any considerations or constraints based on the team's objectives
333+
334+
Please format your response in a structured manner, using bullet points or numbered lists where appropriate.
335+
"""))
336+
337+
structured_llm = self.tool_llm.with_structured_output(TaskDeliverableModel)
338+
chain = build_task_deliverable_template | structured_llm
339+
task_deliverable: TaskDeliverableModel = chain.invoke(input={
340+
"context": self.context,
341+
"objectives": self.team_goal,
342+
"task_description": task.description
343+
})
344+
return task_deliverable.to_v2()

bindings/ceylon/ceylon/task/task_operation.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ def __str__(self):
3131
return f"SubTask: {self.name} (ID: {self.id}) - {status} - Dependencies: {self.depends_on}"
3232

3333

34+
class TaskDeliverable(BaseModel):
35+
objectives: List[str] = Field(
36+
description="the objectives of the task, Explains the task in detail",
37+
default=[]
38+
)
39+
final_output: str = Field(
40+
description="the final output of the task",
41+
default="")
42+
final_output_type: str = Field(
43+
description="the type of the final output of the task",
44+
default="")
45+
46+
def __str__(self):
47+
return f"Objectives: {self.objectives} - Final Output: {self.final_output} - Final Output Type: {self.final_output_type}"
48+
49+
3450
class Task(BaseModel):
3551
id: str = Field(default_factory=lambda: str(uuid4()))
3652
name: str
@@ -39,12 +55,18 @@ class Task(BaseModel):
3955

4056
execution_order: List[str] = Field(default_factory=list)
4157

58+
metadata: Dict[str, str] = Field(default_factory=dict, alias="metadata", description="metadata")
59+
task_deliverable: TaskDeliverable = Field(default=None)
60+
4261
def add_subtask(self, subtask: SubTask):
4362
subtask.parent_task_id = self.id
4463
self.subtasks[subtask.name] = subtask
4564
self._validate_dependencies()
4665
self.execution_order = self.get_execution_order()
4766

67+
def set_deliverable(self, task_deliverable: TaskDeliverable):
68+
self.task_deliverable = task_deliverable
69+
4870
def _validate_dependencies(self):
4971
graph = self._create_dependency_graph()
5072
if not nx.is_directed_acyclic_graph(graph):

0 commit comments

Comments
 (0)