auto-coder 0.1.302__py3-none-any.whl → 0.1.303__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
@@ -10,6 +10,7 @@ from rich.table import Table
10
10
  from rich.panel import Panel
11
11
 
12
12
  from autocoder.common.printer import Printer
13
+ from autocoder.events.event_types import EventMetadata
13
14
  from autocoder.utils.queue_communicate import (
14
15
  queue_communicate,
15
16
  CommunicateEvent,
@@ -27,10 +28,14 @@ from autocoder.common import SourceCodeList
27
28
  from autocoder.common.context_pruner import PruneContext
28
29
  from autocoder.common.action_yml_file_manager import ActionYmlFileManager
29
30
 
31
+ from autocoder.events.event_manager_singleton import get_event_manager
32
+ from autocoder.events import event_content as EventContentCreator
33
+
34
+
30
35
  def build_index_and_filter_files(
31
36
  llm, args: AutoCoderArgs, sources: List[SourceCode]
32
37
  ) -> SourceCodeList:
33
-
38
+
34
39
  action_yml_file_manager = ActionYmlFileManager(args.source_dir)
35
40
  # Initialize timing and statistics
36
41
  total_start_time = time.monotonic()
@@ -61,11 +66,11 @@ def build_index_and_filter_files(
61
66
  return file_path.strip()[2:]
62
67
  return file_path
63
68
 
64
- # 文件路径 -> TargetFile
69
+ # 文件路径 -> TargetFile
65
70
  final_files: Dict[str, TargetFile] = {}
66
71
 
67
72
  # 文件路径 -> 文件在文件列表中的位置(越前面表示越相关)
68
- file_positions:Dict[str,int] = {}
73
+ file_positions: Dict[str, int] = {}
69
74
 
70
75
  # Phase 1: Process REST/RAG/Search sources
71
76
  printer = Printer()
@@ -78,7 +83,7 @@ def build_index_and_filter_files(
78
83
  )
79
84
  phase_end = time.monotonic()
80
85
  stats["timings"]["process_tagged_sources"] = phase_end - phase_start
81
-
86
+
82
87
  if not args.skip_build_index and llm:
83
88
  # Phase 2: Build index
84
89
  if args.request_id and not args.skip_events:
@@ -109,31 +114,34 @@ def build_index_and_filter_files(
109
114
  })
110
115
  )
111
116
  )
112
-
113
-
117
+
114
118
  if not args.skip_filter_index and args.index_filter_model:
115
- model_name = getattr(index_manager.index_filter_llm, 'default_model_name', None)
119
+ model_name = getattr(
120
+ index_manager.index_filter_llm, 'default_model_name', None)
116
121
  if not model_name:
117
- model_name = "unknown(without default model name)"
118
- printer.print_in_terminal("quick_filter_start", style="blue", model_name=model_name)
119
- quick_filter = QuickFilter(index_manager,stats,sources)
120
- quick_filter_result = quick_filter.filter(index_manager.read_index(),args.query)
121
-
122
+ model_name = "unknown(without default model name)"
123
+ printer.print_in_terminal(
124
+ "quick_filter_start", style="blue", model_name=model_name)
125
+ quick_filter = QuickFilter(index_manager, stats, sources)
126
+ quick_filter_result = quick_filter.filter(
127
+ index_manager.read_index(), args.query)
128
+
122
129
  final_files.update(quick_filter_result.files)
123
-
130
+
124
131
  if quick_filter_result.file_positions:
125
132
  file_positions.update(quick_filter_result.file_positions)
126
-
133
+
127
134
  if not args.skip_filter_index and not args.index_filter_model:
128
135
  model_name = getattr(index_manager.llm, 'default_model_name', None)
129
136
  if not model_name:
130
- model_name = "unknown(without default model name)"
131
- printer.print_in_terminal("normal_filter_start", style="blue",model_name=model_name)
132
- normal_filter = NormalFilter(index_manager,stats,sources)
133
- normal_filter_result = normal_filter.filter(index_manager.read_index(),args.query)
137
+ model_name = "unknown(without default model name)"
138
+ printer.print_in_terminal(
139
+ "normal_filter_start", style="blue", model_name=model_name)
140
+ normal_filter = NormalFilter(index_manager, stats, sources)
141
+ normal_filter_result = normal_filter.filter(
142
+ index_manager.read_index(), args.query)
134
143
  # Merge normal filter results into final_files
135
144
  final_files.update(normal_filter_result.files)
136
-
137
145
 
138
146
  def display_table_and_get_selections(data):
139
147
  from prompt_toolkit.shortcuts import checkboxlist_dialog
@@ -173,7 +181,7 @@ def build_index_and_filter_files(
173
181
 
174
182
  def print_selected(data):
175
183
  console = Console()
176
-
184
+
177
185
  # 获取终端宽度
178
186
  console_width = console.width
179
187
 
@@ -185,18 +193,19 @@ def build_index_and_filter_files(
185
193
  width=min(console_width - 10, 120),
186
194
  expand=True
187
195
  )
188
-
196
+
189
197
  # 优化列配置
190
- table.add_column("File Path",
191
- style="cyan",
192
- width=int((console_width - 10) * 0.6), # 分配 60% 宽度给文件路径
193
- overflow="fold", # 自动折叠过长的路径
194
- no_wrap=False) # 允许换行
195
-
196
- table.add_column("Reason",
197
- style="green",
198
- width=int((console_width - 10) * 0.4), # 分配 40% 宽度给原因
199
- no_wrap=False)
198
+ table.add_column("File Path",
199
+ style="cyan",
200
+ # 分配 60% 宽度给文件路径
201
+ width=int((console_width - 10) * 0.6),
202
+ overflow="fold", # 自动折叠过长的路径
203
+ no_wrap=False) # 允许换行
204
+
205
+ table.add_column("Reason",
206
+ style="green",
207
+ width=int((console_width - 10) * 0.4), # 分配 40% 宽度给原因
208
+ no_wrap=False)
200
209
 
201
210
  # 添加处理过的文件路径
202
211
  for file, reason in data:
@@ -215,7 +224,7 @@ def build_index_and_filter_files(
215
224
 
216
225
  # Phase 6: File selection and limitation
217
226
  printer.print_in_terminal("phase6_file_selection")
218
- phase_start = time.monotonic()
227
+ phase_start = time.monotonic()
219
228
 
220
229
  if args.index_filter_file_num > 0:
221
230
  logger.info(
@@ -246,7 +255,7 @@ def build_index_and_filter_files(
246
255
 
247
256
  # Phase 7: Display results and prepare output
248
257
  printer.print_in_terminal("phase7_preparing_output")
249
- phase_start = time.monotonic()
258
+ phase_start = time.monotonic()
250
259
  try:
251
260
  print_selected(
252
261
  [
@@ -263,11 +272,11 @@ def build_index_and_filter_files(
263
272
  for file in final_filenames:
264
273
  print(f"{file} - {final_files[file].reason}")
265
274
 
266
- # source_code = ""
275
+ # source_code = ""
267
276
  source_code_list = SourceCodeList(sources=[])
268
277
  depulicated_sources = set()
269
-
270
- ## 先去重
278
+
279
+ # 先去重
271
280
  temp_sources = []
272
281
  for file in sources:
273
282
  if file.module_name in final_filenames:
@@ -278,27 +287,31 @@ def build_index_and_filter_files(
278
287
  # source_code += f"{file.source_code}\n\n"
279
288
  temp_sources.append(file)
280
289
 
281
- ## 开启了裁剪,则需要做裁剪,不过目前只针对 quick filter 生效
290
+ # 开启了裁剪,则需要做裁剪,不过目前只针对 quick filter 生效
282
291
  if args.context_prune:
283
- context_pruner = PruneContext(max_tokens=args.conversation_prune_safe_zone_tokens, args=args, llm=llm)
292
+ context_pruner = PruneContext(
293
+ max_tokens=args.conversation_prune_safe_zone_tokens, args=args, llm=llm)
284
294
  # 如果 file_positions 不为空,则通过 file_positions 来获取文件
285
295
  if file_positions:
286
- ## 拿到位置列表,然后根据位置排序 得到 [(pos,file_path)]
287
- ## 将 [(pos,file_path)] 转换为 [file_path]
288
- ## 通过 [file_path] 顺序调整 temp_sources 的顺序
289
- ## MARK
290
- # 将 file_positions 转换为 [(pos, file_path)] 的列表
291
- position_file_pairs = [(pos, file_path) for file_path, pos in file_positions.items()]
296
+ # 拿到位置列表,然后根据位置排序 得到 [(pos,file_path)]
297
+ # 将 [(pos,file_path)] 转换为 [file_path]
298
+ # 通过 [file_path] 顺序调整 temp_sources 的顺序
299
+ # MARK
300
+ # 将 file_positions 转换为 [(pos, file_path)] 的列表
301
+ position_file_pairs = [(pos, file_path)
302
+ for file_path, pos in file_positions.items()]
292
303
  # 按位置排序
293
304
  position_file_pairs.sort(key=lambda x: x[0])
294
305
  # 提取排序后的文件路径列表
295
- sorted_file_paths = [file_path for _, file_path in position_file_pairs]
306
+ sorted_file_paths = [file_path for _,
307
+ file_path in position_file_pairs]
296
308
  # 根据 sorted_file_paths 重新排序 temp_sources
297
- temp_sources.sort(key=lambda x: sorted_file_paths.index(x.module_name) if x.module_name in sorted_file_paths else len(sorted_file_paths))
298
-
299
- pruned_files = context_pruner.handle_overflow(temp_sources, [{"role":"user","content":args.query}], args.context_prune_strategy)
300
- source_code_list.sources = pruned_files
309
+ temp_sources.sort(key=lambda x: sorted_file_paths.index(
310
+ x.module_name) if x.module_name in sorted_file_paths else len(sorted_file_paths))
301
311
 
312
+ pruned_files = context_pruner.handle_overflow(
313
+ temp_sources, [{"role": "user", "content": args.query}], args.context_prune_strategy)
314
+ source_code_list.sources = pruned_files
302
315
 
303
316
  if args.request_id and not args.skip_events:
304
317
  queue_communicate.send_event(
@@ -309,7 +322,7 @@ def build_index_and_filter_files(
309
322
  (file.module_name, "") for file in source_code_list.sources
310
323
  ])
311
324
  )
312
- )
325
+ )
313
326
 
314
327
  stats["final_files"] = len(source_code_list.sources)
315
328
  phase_end = time.monotonic()
@@ -319,7 +332,7 @@ def build_index_and_filter_files(
319
332
  total_end_time = time.monotonic()
320
333
  total_time = total_end_time - total_start_time
321
334
  stats["timings"]["total"] = total_time
322
-
335
+
323
336
  # Calculate total filter time
324
337
  total_filter_time = (
325
338
  stats["timings"]["quick_filter"] +
@@ -354,7 +367,7 @@ def build_index_and_filter_files(
354
367
  # summary,
355
368
  # text_options={"justify": "left", "style": "bold white"},
356
369
  # panel_options={
357
- # "title": "Indexing and Filtering Summary",
370
+ # "title": "Indexing and Filtering Summary",
358
371
  # "border_style": "bold blue",
359
372
  # "padding": (1, 2),
360
373
  # "expand": False
@@ -372,19 +385,34 @@ def build_index_and_filter_files(
372
385
  })
373
386
  )
374
387
  )
375
-
388
+
389
+ get_event_manager(args.event_file).write_result(
390
+ EventContentCreator.create_result(
391
+ content=EventContentCreator.ResultContextUsedContent(
392
+ files=final_filenames,
393
+ title="Files Used as Context",
394
+ description=""
395
+ ).to_dict()
396
+ ),
397
+ metadata=EventMetadata(
398
+ action_file=args.file
399
+ ).to_dict()
400
+ )
401
+
376
402
  if args.file:
377
403
  action_file_name = os.path.basename(args.file)
378
404
  dynamic_urls = []
379
-
405
+
380
406
  for file in source_code_list.sources:
381
407
  dynamic_urls.append(file.module_name)
382
-
408
+
383
409
  args.dynamic_urls = dynamic_urls
384
410
 
385
- update_yaml_success = action_yml_file_manager.update_yaml_field(action_file_name, "dynamic_urls", args.dynamic_urls)
386
- if not update_yaml_success:
411
+ update_yaml_success = action_yml_file_manager.update_yaml_field(
412
+ action_file_name, "dynamic_urls", args.dynamic_urls)
413
+ if not update_yaml_success:
387
414
  printer = Printer()
388
- printer.print_in_terminal("yaml_save_error", style="red", yaml_file=action_file_name)
389
-
390
- return source_code_list
415
+ printer.print_in_terminal(
416
+ "yaml_save_error", style="red", yaml_file=action_file_name)
417
+
418
+ return source_code_list
@@ -1,5 +1,6 @@
1
1
  from typing import List, Union, Dict, Any, Optional
2
2
  from pydantic import BaseModel
3
+ from autocoder.common.stream_out_type import IndexFilterStreamOutType
3
4
  from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
4
5
  from autocoder.common.utils_code_auto_generate import stream_chat_with_continue
5
6
  from byzerllm.utils.str2model import to_model
@@ -24,6 +25,9 @@ from byzerllm.utils.client.code_utils import extract_code
24
25
  import json
25
26
  from autocoder.index.symbols_utils import extract_symbols
26
27
  import os.path
28
+ from autocoder.events.event_manager_singleton import get_event_manager
29
+ from autocoder.events import event_content as EventContentCreator
30
+ from autocoder.events.event_types import EventMetadata
27
31
 
28
32
 
29
33
  def get_file_path(file_path):
@@ -123,7 +127,10 @@ class QuickFilter():
123
127
  model_name=model_name,
124
128
  title=self.printer.get_message_from_key_with_format(
125
129
  "quick_filter_title", model_name=model_name),
126
- args=self.args
130
+ args=self.args,
131
+ extra_meta={
132
+ "stream_out_type": IndexFilterStreamOutType.FILE_NUMBER_LIST.value
133
+ }
127
134
  )
128
135
  file_number_list = to_model(full_response, FileNumberList)
129
136
 
@@ -153,6 +160,17 @@ class QuickFilter():
153
160
  output_cost=total_output_cost,
154
161
  model_names=model_name
155
162
  )
163
+
164
+ get_event_manager(self.args.event_file).write_result(
165
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
166
+ model_name=model_name,
167
+ elapsed_time=0,
168
+ first_token_time=0,
169
+ input_tokens=last_meta.input_tokens_count,
170
+ output_tokens=last_meta.generated_tokens_count,
171
+ input_cost=total_input_cost,
172
+ output_cost=total_output_cost
173
+ ).to_dict()))
156
174
  else:
157
175
  # 其他chunks直接使用with_llm
158
176
  meta_holder = MetaHolder()
@@ -178,6 +196,16 @@ class QuickFilter():
178
196
  model_names=model_name,
179
197
  elapsed_time=f"{end_time - start_time:.2f}"
180
198
  )
199
+ get_event_manager(self.args.event_file).write_result(
200
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
201
+ model_name=model_name,
202
+ elapsed_time=end_time - start_time,
203
+ first_token_time=0,
204
+ input_tokens=meta_dict.get("input_tokens_count", 0),
205
+ output_tokens=meta_dict.get("generated_tokens_count", 0),
206
+ input_cost=total_input_cost,
207
+ output_cost=total_output_cost
208
+ ).to_dict()))
181
209
 
182
210
  if file_number_list:
183
211
  for index,file_number in enumerate(file_number_list.file_list):
@@ -475,7 +503,10 @@ class QuickFilter():
475
503
  title=self.printer.get_message_from_key_with_format(
476
504
  "quick_filter_title", model_name=model_name),
477
505
  args=self.args,
478
- display_func=extract_file_number_list
506
+ display_func=extract_file_number_list,
507
+ extra_meta={
508
+ "stream_out_type": IndexFilterStreamOutType.FILE_NUMBER_LIST.value
509
+ }
479
510
  )
480
511
  # 解析结果
481
512
  file_number_list = to_model(full_response, FileNumberList)
@@ -510,6 +541,16 @@ class QuickFilter():
510
541
  model_names=model_name,
511
542
  speed=f"{speed:.2f}"
512
543
  )
544
+ get_event_manager(self.args.event_file).write_result(
545
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
546
+ model_name=model_name,
547
+ elapsed_time=end_time - start_time,
548
+ input_tokens=last_meta.input_tokens_count,
549
+ output_tokens=last_meta.generated_tokens_count,
550
+ input_cost=total_input_cost,
551
+ output_cost=total_output_cost,
552
+ speed=speed
553
+ ).to_dict()))
513
554
 
514
555
  except Exception as e:
515
556
  self.printer.print_in_terminal(
@@ -737,7 +778,10 @@ class QuickFilter():
737
778
  title=self.printer.get_message_from_key_with_format(
738
779
  "super_big_filter_title", model_name=model_name),
739
780
  args=self.args,
740
- display_func=extract_file_number_list
781
+ display_func=extract_file_number_list,
782
+ extra_meta={
783
+ "stream_out_type": IndexFilterStreamOutType.FILE_NUMBER_LIST.value
784
+ }
741
785
  )
742
786
 
743
787
  # 解析结果
@@ -773,6 +817,18 @@ class QuickFilter():
773
817
  speed=f"{speed:.2f}",
774
818
  chunk_index=chunk_index
775
819
  )
820
+
821
+ get_event_manager(self.args.event_file).write_result(
822
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
823
+ model_name=model_name,
824
+ elapsed_time=end_time - start_time,
825
+ input_tokens=last_meta.input_tokens_count,
826
+ output_tokens=last_meta.generated_tokens_count,
827
+ input_cost=total_input_cost,
828
+ output_cost=total_output_cost,
829
+ speed=speed
830
+ ).to_dict()))
831
+
776
832
  else:
777
833
  # 非UI模式,直接使用LLM处理
778
834
  meta_holder = MetaHolder()
@@ -797,6 +853,18 @@ class QuickFilter():
797
853
  elapsed_time=f"{end_time - start_time:.2f}",
798
854
  chunk_index=chunk_index
799
855
  )
856
+
857
+ get_event_manager(self.args.event_file).write_result(
858
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
859
+ model_name=model_name,
860
+ elapsed_time=end_time - start_time,
861
+ input_tokens=meta_dict.get("input_tokens_count", 0),
862
+ output_tokens=meta_dict.get("generated_tokens_count", 0),
863
+ input_cost=total_input_cost,
864
+ output_cost=total_output_cost,
865
+ speed=speed
866
+ ).to_dict()))
867
+
800
868
 
801
869
  # 构建返回结果
802
870
  files = {}
@@ -0,0 +1,62 @@
1
+ """
2
+ Module providing a singleton class to track run context (terminal or web mode).
3
+ """
4
+ from enum import Enum, auto
5
+ from typing import Optional
6
+
7
+
8
+ class RunMode(Enum):
9
+ """Enum representing different run modes for Auto-Coder."""
10
+ TERMINAL = auto()
11
+ WEB = auto()
12
+
13
+
14
+ class RunContext:
15
+ """
16
+ Singleton class to track whether Auto-Coder is running in terminal or web mode.
17
+
18
+ Usage:
19
+ from autocoder.run_context import get_run_context
20
+
21
+ # Get current mode
22
+ context = get_run_context()
23
+ if context.mode == RunMode.WEB:
24
+ # Do web-specific things
25
+
26
+ # Set mode
27
+ context.set_mode(RunMode.WEB)
28
+ """
29
+ _instance: Optional['RunContext'] = None
30
+
31
+ def __new__(cls):
32
+ if cls._instance is None:
33
+ cls._instance = super(RunContext, cls).__new__(cls)
34
+ cls._instance._mode = RunMode.TERMINAL # Default to terminal mode
35
+ return cls._instance
36
+
37
+ @property
38
+ def mode(self) -> RunMode:
39
+ """Get the current run mode."""
40
+ return self._mode
41
+
42
+ def set_mode(self, mode: RunMode) -> None:
43
+ """Set the current run mode."""
44
+ self._mode = mode
45
+
46
+ def is_terminal(self) -> bool:
47
+ """Check if running in terminal mode."""
48
+ return self._mode == RunMode.TERMINAL
49
+
50
+ def is_web(self) -> bool:
51
+ """Check if running in web mode."""
52
+ return self._mode == RunMode.WEB
53
+
54
+
55
+ def get_run_context() -> RunContext:
56
+ """
57
+ Get the singleton RunContext instance.
58
+
59
+ Returns:
60
+ RunContext: The singleton instance of RunContext
61
+ """
62
+ return RunContext()
@@ -7,12 +7,16 @@ from rich.layout import Layout
7
7
  from threading import Thread, Lock
8
8
  from queue import Queue, Empty
9
9
  from typing import Generator, List, Dict, Any, Optional, Tuple, Callable
10
+ from autocoder.events.event_types import EventType
10
11
  from autocoder.utils.request_queue import RequestValue, RequestOption, StreamValue
11
12
  from autocoder.utils.request_queue import request_queue
12
13
  import time
13
14
  from byzerllm.utils.types import SingleOutputMeta
14
15
  from autocoder.common import AutoCoderArgs
15
16
  from autocoder.common.global_cancel import global_cancel
17
+ from autocoder.events.event_manager_singleton import get_event_manager
18
+ from autocoder.events import event_content as EventContentCreator
19
+ from autocoder.events.event_types import EventMetadata
16
20
 
17
21
  MAX_HISTORY_LINES = 40 # 最大保留历史行数
18
22
 
@@ -150,7 +154,8 @@ def stream_out(
150
154
  title: Optional[str] = None,
151
155
  final_title: Optional[str] = None,
152
156
  args: Optional[AutoCoderArgs] = None,
153
- display_func: Optional[Callable] = None
157
+ display_func: Optional[Callable] = None,
158
+ extra_meta: Dict[str, Any] = {}
154
159
  ) -> Tuple[str, Optional[SingleOutputMeta]]:
155
160
  """
156
161
  处理流式输出事件并在终端中展示
@@ -184,6 +189,7 @@ def stream_out(
184
189
  final_panel_title = final_title if final_title is not None else title
185
190
  first_token_time = 0.0
186
191
  first_token_time_start = time.time()
192
+ sequence = 0
187
193
  try:
188
194
  with Live(
189
195
  Panel("", title=panel_title, border_style="green"),
@@ -246,6 +252,21 @@ def stream_out(
246
252
  # 构建显示内容 = 历史行 + 当前行
247
253
  display_content = "\n".join(lines_buffer[-MAX_HISTORY_LINES:] + [current_line])
248
254
 
255
+
256
+ content = EventContentCreator.create_stream_thinking(
257
+ content=display_delta,
258
+ sequence=sequence
259
+ )
260
+ get_event_manager(args.event_file).write_stream(content.to_dict(),
261
+ metadata=EventMetadata(
262
+ stream_out_type=extra_meta.get("stream_out_type", ""),
263
+ is_streaming=True,
264
+ output="delta",
265
+ action_file=args.file
266
+ ).to_dict()
267
+ )
268
+ sequence += 1
269
+
249
270
  if request_id and request_queue:
250
271
  request_queue.add_request(
251
272
  request_id,
@@ -273,6 +294,16 @@ def stream_out(
273
294
  if display_func:
274
295
  final_display_content = display_func(assistant_response)
275
296
 
297
+ content = EventContentCreator.create_markdown_result(
298
+ content=final_display_content
299
+ )
300
+ get_event_manager(args.event_file).write_result(content.to_dict(), metadata=EventMetadata(
301
+ stream_out_type=extra_meta.get("stream_out_type", ""),
302
+ is_streaming=True,
303
+ output="result",
304
+ action_file=args.file
305
+ ).to_dict())
306
+
276
307
  live.update(
277
308
  Panel(
278
309
  Markdown(final_display_content),
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.302"
1
+ __version__ = "0.1.303"