versionhq 1.2.4.1__py3-none-any.whl → 1.2.4.3__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/__init__.py +3 -2
- versionhq/_prompt/auto_feedback.py +103 -0
- versionhq/_prompt/constants.py +30 -0
- versionhq/_prompt/model.py +134 -63
- versionhq/_utils/__init__.py +1 -0
- versionhq/_utils/usage_metrics.py +69 -52
- versionhq/agent/model.py +31 -80
- versionhq/agent_network/formation.py +14 -28
- versionhq/agent_network/model.py +0 -1
- versionhq/llm/model.py +3 -6
- versionhq/storage/task_output_storage.py +2 -2
- versionhq/task/model.py +112 -161
- versionhq/task_graph/draft.py +4 -14
- versionhq/task_graph/model.py +104 -44
- {versionhq-1.2.4.1.dist-info → versionhq-1.2.4.3.dist-info}/METADATA +3 -8
- {versionhq-1.2.4.1.dist-info → versionhq-1.2.4.3.dist-info}/RECORD +19 -17
- {versionhq-1.2.4.1.dist-info → versionhq-1.2.4.3.dist-info}/WHEEL +1 -1
- {versionhq-1.2.4.1.dist-info → versionhq-1.2.4.3.dist-info}/LICENSE +0 -0
- {versionhq-1.2.4.1.dist-info → versionhq-1.2.4.3.dist-info}/top_level.txt +0 -0
versionhq/task/model.py
CHANGED
@@ -4,7 +4,6 @@ import datetime
|
|
4
4
|
import uuid
|
5
5
|
import inspect
|
6
6
|
import enum
|
7
|
-
from textwrap import dedent
|
8
7
|
from concurrent.futures import Future
|
9
8
|
from hashlib import md5
|
10
9
|
from typing import Any, Dict, List, Set, Optional, Callable, Type
|
@@ -16,7 +15,7 @@ from pydantic_core import PydanticCustomError
|
|
16
15
|
import versionhq as vhq
|
17
16
|
from versionhq.task.evaluation import Evaluation, EvaluationItem
|
18
17
|
from versionhq.tool.model import Tool, ToolSet
|
19
|
-
from versionhq._utils import process_config, Logger,
|
18
|
+
from versionhq._utils import process_config, Logger, UsageMetrics, ErrorType
|
20
19
|
|
21
20
|
|
22
21
|
class TaskExecutionType(enum.Enum):
|
@@ -175,7 +174,6 @@ class TaskOutput(BaseModel):
|
|
175
174
|
"""
|
176
175
|
A class to store the final output of the given task in raw (string), json_dict, and pydantic class formats.
|
177
176
|
"""
|
178
|
-
_tokens: int = PrivateAttr(default=0)
|
179
177
|
|
180
178
|
task_id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="store Task ID")
|
181
179
|
raw: str = Field(default="", description="Raw output of the task")
|
@@ -183,23 +181,38 @@ class TaskOutput(BaseModel):
|
|
183
181
|
pydantic: Optional[Any] = Field(default=None)
|
184
182
|
tool_output: Optional[Any] = Field(default=None, description="stores tool result when the task takes tool output as its final output")
|
185
183
|
callback_output: Optional[Any] = Field(default=None, description="stores task or agent callback outcome")
|
186
|
-
latency: float = Field(default=None, description="job latency in ms")
|
187
184
|
evaluation: Optional[InstanceOf[Evaluation]] = Field(default=None, description="stores overall evaluation of the task output. stored in ltm")
|
188
185
|
|
189
186
|
|
187
|
+
def _fetch_value_of(self, key: str = None) -> Any:
|
188
|
+
"""Returns a value to the given key."""
|
189
|
+
|
190
|
+
if not key:
|
191
|
+
return None
|
192
|
+
|
193
|
+
if self.pydantic and hasattr(self.pydantic, key):
|
194
|
+
return getattr(self.pydantic, key)
|
195
|
+
|
196
|
+
elif self.json_dict and key in self.json_dict:
|
197
|
+
return self.json_dict[key]
|
198
|
+
|
199
|
+
else:
|
200
|
+
return None
|
201
|
+
|
202
|
+
|
190
203
|
def _to_context_prompt(self) -> str:
|
191
|
-
"""
|
192
|
-
|
193
|
-
"""
|
204
|
+
"""Formats prompt context in text formats from the final response."""
|
205
|
+
|
194
206
|
context = ""
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
context = self.
|
201
|
-
|
202
|
-
context = self.
|
207
|
+
match self.final:
|
208
|
+
case dict() | self.pydantic:
|
209
|
+
try:
|
210
|
+
context = json.dumps(self.final)
|
211
|
+
except:
|
212
|
+
context = str(self.final)
|
213
|
+
case _:
|
214
|
+
context = str(self.final)
|
215
|
+
|
203
216
|
return context
|
204
217
|
|
205
218
|
|
@@ -225,7 +238,6 @@ class TaskOutput(BaseModel):
|
|
225
238
|
|
226
239
|
task_eval = Task(description=description, pydantic_output=EvaluationItem)
|
227
240
|
res = task_eval.execute(agent=self.evaluation.eval_by)
|
228
|
-
self._tokens += task_eval._tokens
|
229
241
|
|
230
242
|
if res.pydantic:
|
231
243
|
item = EvaluationItem(
|
@@ -252,6 +264,24 @@ class TaskOutput(BaseModel):
|
|
252
264
|
return self.evaluation
|
253
265
|
|
254
266
|
|
267
|
+
@property
|
268
|
+
def final(self) -> Any:
|
269
|
+
"""Returns final output from the task."""
|
270
|
+
|
271
|
+
output = None
|
272
|
+
|
273
|
+
if self.callback_output:
|
274
|
+
output = self.callback_output
|
275
|
+
|
276
|
+
elif self.tool_output and str(self.tool_output) == self.raw: # tool_output_as_final
|
277
|
+
output = self.tool_output
|
278
|
+
|
279
|
+
else:
|
280
|
+
output = self.pydantic if self.pydantic else self.json_dict if self.json_dict else self.raw
|
281
|
+
|
282
|
+
return output
|
283
|
+
|
284
|
+
|
255
285
|
@property
|
256
286
|
def aggregate_score(self) -> float | int:
|
257
287
|
return self.evaluation.aggregate_score if self.evaluation is not None else 0
|
@@ -280,6 +310,7 @@ class Task(BaseModel):
|
|
280
310
|
description: str = Field(description="Description of the actual task")
|
281
311
|
|
282
312
|
# response format
|
313
|
+
response_schema: Optional[Type[BaseModel] | List[ResponseField]] = Field(default=None)
|
283
314
|
pydantic_output: Optional[Type[BaseModel]] = Field(default=None, description="store Pydantic class as structured response format")
|
284
315
|
response_fields: Optional[List[ResponseField]] = Field(default_factory=list, description="store list of ResponseField as structured response format")
|
285
316
|
|
@@ -292,6 +323,11 @@ class Task(BaseModel):
|
|
292
323
|
file: Optional[str] = Field(default=None, description="absolute file path or url in string")
|
293
324
|
audio: Optional[str] = Field(default=None, description="absolute file path or url in string")
|
294
325
|
|
326
|
+
# test run
|
327
|
+
should_test_run: bool = Field(default=False)
|
328
|
+
human: bool = Field(default=False)
|
329
|
+
_pfg: Any = None
|
330
|
+
|
295
331
|
# executing
|
296
332
|
execution_type: TaskExecutionType = Field(default=TaskExecutionType.SYNC)
|
297
333
|
allow_delegation: bool = Field(default=False, description="whether to delegate the task to another agent")
|
@@ -304,9 +340,7 @@ class Task(BaseModel):
|
|
304
340
|
fsls: Optional[list[str]] = Field(default=None, description="stores ideal/weak responses")
|
305
341
|
|
306
342
|
# recording
|
307
|
-
|
308
|
-
_tool_errors: int = 0
|
309
|
-
_format_errors: int = 0
|
343
|
+
_usage: UsageMetrics = PrivateAttr(default=None)
|
310
344
|
_delegations: int = 0
|
311
345
|
processed_agents: Set[str] = Field(default_factory=set, description="store keys of the agents that executed the task")
|
312
346
|
output: Optional[TaskOutput] = Field(default=None, description="store the final TaskOutput object")
|
@@ -331,6 +365,8 @@ class Task(BaseModel):
|
|
331
365
|
for field in required_fields:
|
332
366
|
if getattr(self, field) is None:
|
333
367
|
raise ValueError( f"{field} must be provided either directly or through config")
|
368
|
+
|
369
|
+
self._usage = UsageMetrics(id=self.id)
|
334
370
|
return self
|
335
371
|
|
336
372
|
|
@@ -351,121 +387,6 @@ class Task(BaseModel):
|
|
351
387
|
return self
|
352
388
|
|
353
389
|
|
354
|
-
def _draft_output_prompt(self, model_provider: str = None) -> str:
|
355
|
-
output_prompt = ""
|
356
|
-
|
357
|
-
if self.pydantic_output:
|
358
|
-
output_prompt, output_formats_to_follow = "", dict()
|
359
|
-
response_format = str(self._structure_response_format(model_provider=model_provider))
|
360
|
-
for k, v in self.pydantic_output.model_fields.items():
|
361
|
-
output_formats_to_follow[k] = f"<Return your answer in {v.annotation}>"
|
362
|
-
|
363
|
-
output_prompt = f"""Your response MUST be a valid JSON string that strictly follows the response format. Use double quotes for all keys and string values. Do not use single quotes, trailing commas, or any other non-standard JSON syntax.
|
364
|
-
Response format: {response_format}
|
365
|
-
Ref. Output image: {output_formats_to_follow}
|
366
|
-
"""
|
367
|
-
elif self.response_fields:
|
368
|
-
output_prompt, output_formats_to_follow = "", dict()
|
369
|
-
response_format = str(self._structure_response_format(model_provider=model_provider))
|
370
|
-
for item in self.response_fields:
|
371
|
-
if item:
|
372
|
-
output_formats_to_follow[item.title] = f"<Return your answer in {item.data_type.__name__}>"
|
373
|
-
|
374
|
-
output_prompt = f"""Your response MUST be a valid JSON string that strictly follows the response format. Use double quotes for all keys and string values. Do not use single quotes, trailing commas, or any other non-standard JSON syntax.
|
375
|
-
Response format: {response_format}
|
376
|
-
Ref. Output image: {output_formats_to_follow}
|
377
|
-
"""
|
378
|
-
# elif not self.tools or self.can_use_agent_tools == False:
|
379
|
-
else:
|
380
|
-
output_prompt = "You MUST return your response as a valid JSON serializable string, enclosed in double quotes. Use double quotes for all keys and string values. Do NOT use single quotes, trailing commas, or other non-standard JSON syntax."
|
381
|
-
|
382
|
-
# else:
|
383
|
-
# output_prompt = "You will return a response in a concise manner."
|
384
|
-
|
385
|
-
return dedent(output_prompt)
|
386
|
-
|
387
|
-
|
388
|
-
def _draft_context_prompt(self, context: Any) -> str:
|
389
|
-
"""
|
390
|
-
Create a context prompt from the given context in any format: a task object, task output object, list, dict.
|
391
|
-
"""
|
392
|
-
|
393
|
-
context_to_add = None
|
394
|
-
if not context:
|
395
|
-
# Logger().log(level="error", color="red", message="Missing a context to add to the prompt. We'll return ''.")
|
396
|
-
return context_to_add
|
397
|
-
|
398
|
-
match context:
|
399
|
-
case str():
|
400
|
-
context_to_add = context
|
401
|
-
|
402
|
-
case Task():
|
403
|
-
if not context.output:
|
404
|
-
res = context.execute()
|
405
|
-
context_to_add = res._to_context_prompt()
|
406
|
-
|
407
|
-
else:
|
408
|
-
context_to_add = context.output.raw
|
409
|
-
|
410
|
-
case TaskOutput():
|
411
|
-
context_to_add = context._to_context_prompt()
|
412
|
-
|
413
|
-
|
414
|
-
case dict():
|
415
|
-
context_to_add = str(context)
|
416
|
-
|
417
|
-
case list():
|
418
|
-
res = ", ".join([self._draft_context_prompt(context=item) for item in context])
|
419
|
-
context_to_add = res
|
420
|
-
|
421
|
-
case _:
|
422
|
-
pass
|
423
|
-
|
424
|
-
return dedent(context_to_add)
|
425
|
-
|
426
|
-
|
427
|
-
def _user_prompt(self, model_provider: str = None, context: Optional[Any] = None) -> str:
|
428
|
-
"""
|
429
|
-
Format the task prompt and cascade it to the agent.
|
430
|
-
"""
|
431
|
-
output_prompt = self._draft_output_prompt(model_provider=model_provider)
|
432
|
-
task_slices = [self.description, output_prompt, ]
|
433
|
-
|
434
|
-
if context:
|
435
|
-
context_prompt = self._draft_context_prompt(context=context)
|
436
|
-
task_slices.insert(len(task_slices), f"Consider the following context when responding: {context_prompt}")
|
437
|
-
|
438
|
-
return "\n".join(task_slices)
|
439
|
-
|
440
|
-
|
441
|
-
def _format_content_prompt(self) -> Dict[str, str]:
|
442
|
-
"""Formats content (file, image, audio) prompts that added to the messages sent to the LLM."""
|
443
|
-
|
444
|
-
from pathlib import Path
|
445
|
-
import base64
|
446
|
-
|
447
|
-
content_messages = {}
|
448
|
-
|
449
|
-
if self.image:
|
450
|
-
with open(self.image, "rb") as file:
|
451
|
-
content = file.read()
|
452
|
-
if content:
|
453
|
-
encoded_file = base64.b64encode(content).decode("utf-8")
|
454
|
-
img_url = f"data:image/jpeg;base64,{encoded_file}"
|
455
|
-
content_messages.update({ "type": "image_url", "image_url": { "url": img_url }})
|
456
|
-
|
457
|
-
if self.file:
|
458
|
-
if is_valid_url(self.file):
|
459
|
-
content_messages.update({ "type": "image_url", "image_url": self.file })
|
460
|
-
|
461
|
-
if self.audio:
|
462
|
-
audio_bytes = Path(self.audio).read_bytes()
|
463
|
-
encoded_data = base64.b64encode(audio_bytes).decode("utf-8")
|
464
|
-
content_messages.update({ "type": "image_url", "image_url": "data:audio/mp3;base64,{}".format(encoded_data)})
|
465
|
-
|
466
|
-
return content_messages
|
467
|
-
|
468
|
-
|
469
390
|
def _structure_response_format(self, data_type: str = "object", model_provider: str = "gemini") -> Dict[str, Any] | None:
|
470
391
|
"""Structures `response_fields` or `pydantic_output` to a LLM response format."""
|
471
392
|
|
@@ -495,7 +416,6 @@ Ref. Output image: {output_formats_to_follow}
|
|
495
416
|
"json_schema": { "name": "outcome", "schema": response_schema }
|
496
417
|
}
|
497
418
|
|
498
|
-
|
499
419
|
elif self.pydantic_output:
|
500
420
|
response_format = StructuredOutput(response_format=self.pydantic_output, provider=model_provider)._format()
|
501
421
|
|
@@ -525,14 +445,15 @@ Ref. Output image: {output_formats_to_follow}
|
|
525
445
|
output = json.loads(j)
|
526
446
|
|
527
447
|
if isinstance(output, dict):
|
528
|
-
return output
|
448
|
+
return output["json_schema"] if "json_schema" in output else output
|
529
449
|
else:
|
530
450
|
try:
|
531
451
|
output = ast.literal_eval(j)
|
532
452
|
except:
|
533
453
|
output = ast.literal_eval(r)
|
534
454
|
|
535
|
-
|
455
|
+
|
456
|
+
return output["json_schema"] if isinstance(output, dict) and "json_schema" in output else output if isinstance(output, dict) else { "output": str(r) }
|
536
457
|
|
537
458
|
|
538
459
|
def _create_json_output(self, raw: str) -> Dict[str, Any]:
|
@@ -548,12 +469,13 @@ Ref. Output image: {output_formats_to_follow}
|
|
548
469
|
try:
|
549
470
|
output = json.loads(raw)
|
550
471
|
if isinstance(output, dict):
|
551
|
-
return output
|
472
|
+
return output["json_schema"] if "json_schema" in output else output
|
552
473
|
else:
|
553
474
|
output = self._sanitize_raw_output(raw=raw)
|
554
475
|
return output
|
555
476
|
except:
|
556
477
|
output = self._sanitize_raw_output(raw=raw)
|
478
|
+
self._usage.record_errors(type=ErrorType.FORMAT)
|
557
479
|
return output
|
558
480
|
|
559
481
|
|
@@ -673,23 +595,25 @@ Ref. Output image: {output_formats_to_follow}
|
|
673
595
|
|
674
596
|
|
675
597
|
# task execution
|
676
|
-
def execute(
|
677
|
-
|
678
|
-
|
679
|
-
"""
|
680
|
-
A main method to handle task execution. Build an agent when the agent is not given.
|
681
|
-
"""
|
598
|
+
def execute(self, type: TaskExecutionType = None, agent: "vhq.Agent" = None, context: Any = None) -> TaskOutput | Future[TaskOutput]:
|
599
|
+
"""A main method to handle task execution."""
|
600
|
+
|
682
601
|
type = type if type else self.execution_type if self.execution_type else TaskExecutionType.SYNC
|
602
|
+
agent = agent if agent else self._build_agent_from_task(task_description=self.description)
|
603
|
+
res = None
|
683
604
|
|
684
|
-
if not
|
685
|
-
|
605
|
+
if (self.should_test_run or agent.self_learn) and not self._pfg:
|
606
|
+
res = self._test_time_computation(agent=agent, context=context)
|
607
|
+
return res
|
686
608
|
|
687
609
|
match type:
|
688
610
|
case TaskExecutionType.SYNC:
|
689
|
-
|
611
|
+
res = self._execute_sync(agent=agent, context=context)
|
690
612
|
|
691
613
|
case TaskExecutionType.ASYNC:
|
692
|
-
|
614
|
+
res = self._execute_async(agent=agent, context=context)
|
615
|
+
|
616
|
+
return res
|
693
617
|
|
694
618
|
|
695
619
|
def _execute_sync(self, agent, context: Optional[Any] = None) -> TaskOutput:
|
@@ -710,14 +634,14 @@ Ref. Output image: {output_formats_to_follow}
|
|
710
634
|
|
711
635
|
|
712
636
|
def _execute_core(self, agent, context: Optional[Any]) -> TaskOutput:
|
713
|
-
"""
|
714
|
-
|
715
|
-
|
637
|
+
"""A core method to execute a single task."""
|
638
|
+
|
639
|
+
start_dt = datetime.datetime.now()
|
716
640
|
task_output: InstanceOf[TaskOutput] = None
|
717
641
|
raw_output: str = None
|
718
642
|
tool_output: str | list = None
|
719
643
|
task_tools: List[List[InstanceOf[Tool]| InstanceOf[ToolSet] | Type[Tool]]] = []
|
720
|
-
|
644
|
+
user_prompt, dev_prompt = None, None
|
721
645
|
|
722
646
|
if self.tools:
|
723
647
|
for item in self.tools:
|
@@ -730,17 +654,14 @@ Ref. Output image: {output_formats_to_follow}
|
|
730
654
|
self._delegations += 1
|
731
655
|
|
732
656
|
if self.tool_res_as_final == True:
|
733
|
-
|
734
|
-
tool_output = agent.execute_task(task=self, context=context, task_tools=task_tools)
|
657
|
+
user_prompt, dev_prompt, tool_output = agent.execute_task(task=self, context=context, task_tools=task_tools)
|
735
658
|
raw_output = str(tool_output) if tool_output else ""
|
736
|
-
|
659
|
+
if not raw_output:
|
660
|
+
self._usage.record_errors(type=ErrorType.TOOL)
|
737
661
|
task_output = TaskOutput(task_id=self.id, tool_output=tool_output, raw=raw_output)
|
738
662
|
|
739
663
|
else:
|
740
|
-
|
741
|
-
raw_output = agent.execute_task(task=self, context=context, task_tools=task_tools)
|
742
|
-
ended_at = datetime.datetime.now()
|
743
|
-
|
664
|
+
user_prompt, dev_prompt, raw_output = agent.execute_task(task=self, context=context, task_tools=task_tools)
|
744
665
|
json_dict_output = self._create_json_output(raw=raw_output)
|
745
666
|
if "outcome" in json_dict_output:
|
746
667
|
json_dict_output = self._create_json_output(raw=str(json_dict_output["outcome"]))
|
@@ -754,8 +675,6 @@ Ref. Output image: {output_formats_to_follow}
|
|
754
675
|
json_dict=json_dict_output,
|
755
676
|
)
|
756
677
|
|
757
|
-
task_output.latency = round((ended_at - started_at).total_seconds() * 1000, 3)
|
758
|
-
task_output._tokens = self._tokens
|
759
678
|
self.output = task_output
|
760
679
|
self.processed_agents.add(agent.key)
|
761
680
|
|
@@ -767,6 +686,11 @@ Ref. Output image: {output_formats_to_follow}
|
|
767
686
|
# )
|
768
687
|
# self._save_file(content)
|
769
688
|
|
689
|
+
if self._pfg:
|
690
|
+
index = self._pfg.index
|
691
|
+
self._pfg.user_prompts.update({ index: user_prompt })
|
692
|
+
self._pfg.dev_prompts.update({ index: dev_prompt })
|
693
|
+
|
770
694
|
if raw_output:
|
771
695
|
if self.should_evaluate:
|
772
696
|
task_output.evaluate(task=self)
|
@@ -784,9 +708,36 @@ Ref. Output image: {output_formats_to_follow}
|
|
784
708
|
self.output = task_output
|
785
709
|
self._store_logs()
|
786
710
|
|
711
|
+
end_dt = datetime.datetime.now()
|
712
|
+
self._usage.record_latency(start_dt=start_dt, end_dt=end_dt)
|
787
713
|
return task_output
|
788
714
|
|
789
715
|
|
716
|
+
def _test_time_computation(self, agent, context: Optional[Any]) -> TaskOutput | None:
|
717
|
+
"""Handles test-time computation."""
|
718
|
+
|
719
|
+
from versionhq.task_graph.model import ReformTriggerEvent
|
720
|
+
from versionhq._prompt.model import Prompt
|
721
|
+
from versionhq._prompt.auto_feedback import PromptFeedbackGraph
|
722
|
+
|
723
|
+
# self._usage = None
|
724
|
+
prompt = Prompt(task=self, agent=agent, context=context)
|
725
|
+
pfg = PromptFeedbackGraph(prompt=prompt, should_reform=self.human, reform_trigger_event=ReformTriggerEvent.USER_INPUT if self.human else None)
|
726
|
+
pfg = pfg.set_up_graph()
|
727
|
+
self._pfg = pfg
|
728
|
+
|
729
|
+
try:
|
730
|
+
if self._pfg and self.output is None:
|
731
|
+
res, all_outputs = self._pfg.activate()
|
732
|
+
if all_outputs: self._usage = self._pfg._usage
|
733
|
+
return res
|
734
|
+
|
735
|
+
except:
|
736
|
+
self._usage.record_errors(type=ErrorType.API)
|
737
|
+
Logger().log(level="error", message="Failed to execute the task.", color="red")
|
738
|
+
return None
|
739
|
+
|
740
|
+
|
790
741
|
@property
|
791
742
|
def key(self) -> str:
|
792
743
|
output_format = "json" if self.response_fields else "pydantic" if self.pydantic_output is not None else "raw"
|
versionhq/task_graph/draft.py
CHANGED
@@ -10,7 +10,7 @@ sys.modules['pydantic.main'].ModelMetaclass = ModelMetaclass
|
|
10
10
|
|
11
11
|
from versionhq.agent.model import Agent
|
12
12
|
from versionhq.task.model import ResponseField
|
13
|
-
from versionhq.task_graph.model import TaskGraph, Task, DependencyType, Node
|
13
|
+
from versionhq.task_graph.model import TaskGraph, Task, DependencyType, Node, ReformTriggerEvent
|
14
14
|
from versionhq._utils.logger import Logger
|
15
15
|
|
16
16
|
|
@@ -81,7 +81,8 @@ def workflow(final_output: Type[BaseModel], context: Any = None, human: bool = F
|
|
81
81
|
nodes={node.identifier: node for node in nodes},
|
82
82
|
concl_format=final_output,
|
83
83
|
concl=None,
|
84
|
-
should_reform=
|
84
|
+
should_reform=human,
|
85
|
+
reform_trigger_event=ReformTriggerEvent.USER_INPUT if human else None,
|
85
86
|
)
|
86
87
|
|
87
88
|
for res in task_items:
|
@@ -95,17 +96,6 @@ def workflow(final_output: Type[BaseModel], context: Any = None, human: bool = F
|
|
95
96
|
task_graph.add_dependency(
|
96
97
|
source=source.identifier, target=target.identifier, dependency_type=dependency_type)
|
97
98
|
|
98
|
-
task_graph.visualize()
|
99
|
-
|
100
|
-
if human:
|
101
|
-
print('Proceed? Y/n:')
|
102
|
-
x = input()
|
103
|
-
|
104
|
-
if x.lower() == "y":
|
105
|
-
print("ok. generating agent network")
|
106
|
-
|
107
|
-
else:
|
108
|
-
request = input("request?")
|
109
|
-
print('ok. regenerating the graph based on your input: ', request)
|
99
|
+
# task_graph.visualize()
|
110
100
|
|
111
101
|
return task_graph
|