auto-coder 2.0.0__py3-none-any.whl → 2.0.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.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

autocoder/index/entry.py CHANGED
@@ -295,7 +295,7 @@ def build_index_and_filter_files(
295
295
  # for file in final_filenames:
296
296
  # print(f"{file} - {final_files[file].reason}")
297
297
 
298
- # 先去重
298
+ # 先去重
299
299
  temp_sources = []
300
300
  for file in sources:
301
301
  # print(f"{file.module_name} in project")
@@ -197,16 +197,7 @@ class RunAgentic:
197
197
 
198
198
  # 3. 处理特殊对话操作
199
199
  if self._handle_conversation_actions(conversation_config):
200
- return conversation_config.conversation_id
201
-
202
- # 4. 创建新对话
203
- conversation_manager = get_conversation_manager()
204
- conversation_id = conversation_manager.create_conversation(
205
- name=query or "New Conversation",
206
- description=query or "New Conversation",
207
- )
208
- conversation_manager.set_current_conversation(conversation_id)
209
- conversation_config.conversation_id = conversation_id
200
+ return conversation_config.conversation_id
210
201
 
211
202
  # 5. 配置过滤模式参数
212
203
  args_copy = deepcopy(args)
autocoder/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = '2.0.0'
4
+ __version__ = '2.0.1'
@@ -7,6 +7,8 @@ Subagent Workflow 执行器
7
7
  from pathlib import Path
8
8
  from typing import Any, Dict, List, Optional, Set, Tuple
9
9
  from copy import deepcopy
10
+ from concurrent.futures import ThreadPoolExecutor, as_completed
11
+ import json
10
12
  from loguru import logger
11
13
 
12
14
  from autocoder.common import AutoCoderArgs
@@ -421,6 +423,22 @@ class SubagentWorkflowExecutor:
421
423
  step_id=step.id, status=StepStatus.FAILED, error=error_msg
422
424
  )
423
425
 
426
+ # 检查是否需要并行执行
427
+ if step.replicas > 1:
428
+ return self._execute_step_parallel(step)
429
+ else:
430
+ return self._execute_step_single(step)
431
+
432
+ def _execute_step_single(self, step: StepSpec) -> StepResult:
433
+ """
434
+ 执行单个步骤(非并行)
435
+
436
+ Args:
437
+ step: 步骤规格
438
+
439
+ Returns:
440
+ StepResult 对象
441
+ """
424
442
  try:
425
443
  # 渲染用户输入
426
444
  user_input = render_template(
@@ -433,7 +451,7 @@ class SubagentWorkflowExecutor:
433
451
  conversation_config = self._get_conversation_config(step)
434
452
 
435
453
  # 运行代理
436
- agent = self.agents[agent_id]
454
+ agent = self.agents[step.agent]
437
455
  completion = agent.run(
438
456
  user_input=user_input,
439
457
  conversation_config=conversation_config,
@@ -487,3 +505,161 @@ class SubagentWorkflowExecutor:
487
505
  return StepResult(
488
506
  step_id=step.id, status=StepStatus.FAILED, error=error_msg
489
507
  )
508
+
509
+ def _execute_step_parallel(self, step: StepSpec) -> StepResult:
510
+ """
511
+ 并行执行单个步骤的多个副本
512
+
513
+ Args:
514
+ step: 步骤规格
515
+
516
+ Returns:
517
+ StepResult 对象,包含合并后的结果
518
+ """
519
+ logger.info(f"步骤 {step.id} 将并行执行 {step.replicas} 个副本")
520
+
521
+ # 渲染用户输入(所有副本使用相同输入)
522
+ try:
523
+ user_input = render_template(
524
+ step.with_args.get("user_input", ""), self.context
525
+ )
526
+ if not isinstance(user_input, str):
527
+ user_input = str(user_input)
528
+ except Exception as e:
529
+ error_msg = f"渲染输入失败: {str(e)}"
530
+ logger.error(f"步骤 {step.id} {error_msg}", exc_info=True)
531
+ return StepResult(
532
+ step_id=step.id, status=StepStatus.FAILED, error=error_msg
533
+ )
534
+
535
+ # 并行执行所有副本
536
+ agent = self.agents[step.agent]
537
+ results = []
538
+ errors = []
539
+
540
+ def run_replica(replica_idx: int):
541
+ """运行单个副本"""
542
+ try:
543
+ logger.info(
544
+ f"步骤 {step.id} 副本 {replica_idx + 1}/{step.replicas} 开始执行"
545
+ )
546
+
547
+ # 获取会话配置(每个副本独立会话)
548
+ conversation_config = self._get_conversation_config(step)
549
+
550
+ # 运行代理
551
+ completion = agent.run(
552
+ user_input=user_input,
553
+ conversation_config=conversation_config,
554
+ args=self.args,
555
+ llm=self.llm,
556
+ cancel_token=self.cancel_token,
557
+ )
558
+
559
+ if completion is None:
560
+ return (replica_idx, None, "代理未返回结果")
561
+
562
+ logger.info(
563
+ f"步骤 {step.id} 副本 {replica_idx + 1}/{step.replicas} 执行完成"
564
+ )
565
+ return (replica_idx, completion.result, None)
566
+
567
+ except Exception as e:
568
+ error_msg = f"副本 {replica_idx + 1} 执行异常: {str(e)}"
569
+ logger.error(f"步骤 {step.id} {error_msg}", exc_info=True)
570
+ return (replica_idx, None, error_msg)
571
+
572
+ # 使用线程池并行执行
573
+ with ThreadPoolExecutor(max_workers=step.replicas) as executor:
574
+ futures = [executor.submit(run_replica, i) for i in range(step.replicas)]
575
+
576
+ for future in as_completed(futures):
577
+ replica_idx, result, error = future.result()
578
+ if error:
579
+ errors.append(f"副本 {replica_idx + 1}: {error}")
580
+ else:
581
+ results.append((replica_idx, result))
582
+
583
+ # 判断成功:任意一个成功即可(any-success 策略)
584
+ if not results:
585
+ # 全部失败
586
+ error_msg = f"所有 {step.replicas} 个副本都失败了: " + "; ".join(errors)
587
+ logger.error(f"步骤 {step.id} {error_msg}")
588
+ return StepResult(
589
+ step_id=step.id, status=StepStatus.FAILED, error=error_msg
590
+ )
591
+
592
+ # 至少有一个成功
593
+ logger.info(f"步骤 {step.id} 成功执行了 {len(results)}/{step.replicas} 个副本")
594
+
595
+ # 合并结果
596
+ merged_result = self._merge_replica_results(results, step)
597
+
598
+ # 更新上下文
599
+ self.context["_last_attempt_result"] = merged_result
600
+
601
+ # 提取输出(从合并后的结果中提取)
602
+ try:
603
+ outputs = extract_outputs(
604
+ outputs_map=step.outputs,
605
+ attempt_result=merged_result,
606
+ attempt_format=self.spec.attempt.format,
607
+ default_jsonpaths=self.spec.attempt.jsonpaths,
608
+ context=self.context,
609
+ )
610
+ except Exception as e:
611
+ logger.warning(f"步骤 {step.id} 提取输出失败: {e},使用空输出")
612
+ outputs = {}
613
+
614
+ # 注入内置变量:conversation_id
615
+ outputs["conversation_id"] = self._conversation_id
616
+
617
+ # 保存到上下文
618
+ if step.id not in self.context["steps"]:
619
+ self.context["steps"][step.id] = {"outputs": {}}
620
+
621
+ self.context["steps"][step.id]["outputs"].update(outputs)
622
+ logger.debug(f"步骤 {step.id} 输出: {list(outputs.keys())}")
623
+
624
+ return StepResult(
625
+ step_id=step.id,
626
+ status=StepStatus.SUCCESS,
627
+ attempt_result=merged_result,
628
+ outputs=outputs,
629
+ )
630
+
631
+ def _merge_replica_results(
632
+ self, results: List[Tuple[int, str]], step: StepSpec
633
+ ) -> str:
634
+ """
635
+ 合并多个副本的结果
636
+
637
+ Args:
638
+ results: [(replica_idx, attempt_result), ...]
639
+ step: 步骤规格
640
+
641
+ Returns:
642
+ 合并后的结果字符串
643
+ """
644
+ # 按副本索引排序
645
+ sorted_results = sorted(results, key=lambda x: x[0])
646
+ attempt_results = [result for _, result in sorted_results]
647
+
648
+ # 根据 attempt.format 决定合并方式
649
+ if self.spec.attempt.format == "json":
650
+ # JSON 格式:尝试将多个结果打包成 JSON 数组
651
+ json_objects = []
652
+ for result in attempt_results:
653
+ try:
654
+ obj = json.loads(result)
655
+ json_objects.append(obj)
656
+ except json.JSONDecodeError:
657
+ # 如果解析失败,保留原始字符串
658
+ json_objects.append({"raw": result})
659
+
660
+ # 返回 JSON 数组字符串
661
+ return json.dumps(json_objects, ensure_ascii=False, indent=2)
662
+ else:
663
+ # Text 格式:用分隔符拼接
664
+ separator = "\n---\n"
665
+ return separator.join(attempt_results)
@@ -383,6 +383,17 @@ def parse_step_spec(
383
383
  parse_step_conversation_config(conv_data, context) if conv_data else None
384
384
  )
385
385
 
386
+ # 解析 replicas
387
+ replicas = data.get("replicas", 1)
388
+ if not isinstance(replicas, int) or replicas < 1:
389
+ raise WorkflowValidationError(
390
+ message="replicas 必须是正整数",
391
+ field_path=f"{context}.replicas",
392
+ expected="正整数(>= 1)",
393
+ actual=str(replicas),
394
+ suggestion="将 replicas 设置为正整数,如 2(表示并行运行2个副本)",
395
+ )
396
+
386
397
  return StepSpec(
387
398
  id=step_id,
388
399
  agent=agent_id,
@@ -391,6 +402,7 @@ def parse_step_spec(
391
402
  when=when_config,
392
403
  outputs=outputs,
393
404
  conversation=conversation,
405
+ replicas=replicas,
394
406
  )
395
407
 
396
408
 
@@ -108,6 +108,7 @@ class StepSpec:
108
108
  when: Optional[WhenConfig] = None
109
109
  outputs: Dict[str, OutputConfig] = field(default_factory=dict)
110
110
  conversation: Optional[StepConversationConfig] = None
111
+ replicas: int = 1 # 并行副本数,默认为1(不并行)
111
112
 
112
113
 
113
114
  @dataclass