auto-coder 0.1.374__py3-none-any.whl → 0.1.376__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.

Files changed (61) hide show
  1. {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/METADATA +2 -2
  2. {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/RECORD +27 -57
  3. autocoder/agent/base_agentic/base_agent.py +202 -52
  4. autocoder/agent/base_agentic/default_tools.py +38 -6
  5. autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +83 -43
  6. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +88 -25
  7. autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +171 -62
  8. autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +101 -56
  9. autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +5 -0
  10. autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +5 -0
  11. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +145 -32
  12. autocoder/auto_coder_rag.py +80 -11
  13. autocoder/models.py +2 -2
  14. autocoder/rag/agentic_rag.py +217 -0
  15. autocoder/rag/cache/local_duckdb_storage_cache.py +63 -33
  16. autocoder/rag/conversation_to_queries.py +37 -5
  17. autocoder/rag/long_context_rag.py +161 -41
  18. autocoder/rag/tools/__init__.py +10 -0
  19. autocoder/rag/tools/recall_tool.py +163 -0
  20. autocoder/rag/tools/search_tool.py +126 -0
  21. autocoder/rag/types.py +36 -0
  22. autocoder/utils/_markitdown.py +59 -13
  23. autocoder/version.py +1 -1
  24. autocoder/agent/agentic_edit.py +0 -833
  25. autocoder/agent/agentic_edit_tools/__init__.py +0 -28
  26. autocoder/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +0 -32
  27. autocoder/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +0 -29
  28. autocoder/agent/agentic_edit_tools/base_tool_resolver.py +0 -29
  29. autocoder/agent/agentic_edit_tools/execute_command_tool_resolver.py +0 -84
  30. autocoder/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +0 -75
  31. autocoder/agent/agentic_edit_tools/list_files_tool_resolver.py +0 -62
  32. autocoder/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +0 -30
  33. autocoder/agent/agentic_edit_tools/read_file_tool_resolver.py +0 -36
  34. autocoder/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +0 -95
  35. autocoder/agent/agentic_edit_tools/search_files_tool_resolver.py +0 -70
  36. autocoder/agent/agentic_edit_tools/use_mcp_tool_resolver.py +0 -55
  37. autocoder/agent/agentic_edit_tools/write_to_file_tool_resolver.py +0 -98
  38. autocoder/agent/agentic_edit_types.py +0 -124
  39. autocoder/auto_coder_lang.py +0 -60
  40. autocoder/auto_coder_rag_client_mcp.py +0 -170
  41. autocoder/auto_coder_rag_mcp.py +0 -193
  42. autocoder/common/llm_rerank.py +0 -84
  43. autocoder/common/model_speed_test.py +0 -392
  44. autocoder/common/v2/agent/agentic_edit_conversation.py +0 -188
  45. autocoder/common/v2/agent/ignore_utils.py +0 -50
  46. autocoder/dispacher/actions/plugins/action_translate.py +0 -214
  47. autocoder/ignorefiles/__init__.py +0 -4
  48. autocoder/ignorefiles/ignore_file_utils.py +0 -63
  49. autocoder/ignorefiles/test_ignore_file_utils.py +0 -91
  50. autocoder/linters/code_linter.py +0 -588
  51. autocoder/rag/loaders/test_image_loader.py +0 -209
  52. autocoder/rag/raw_rag.py +0 -96
  53. autocoder/rag/simple_directory_reader.py +0 -646
  54. autocoder/rag/simple_rag.py +0 -404
  55. autocoder/regex_project/__init__.py +0 -162
  56. autocoder/utils/coder.py +0 -125
  57. autocoder/utils/tests.py +0 -37
  58. {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/LICENSE +0 -0
  59. {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/WHEEL +0 -0
  60. {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/entry_points.txt +0 -0
  61. {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,217 @@
1
+ import json
2
+ import os
3
+ import time
4
+ from typing import Any, Dict, Generator, List, Optional, Tuple, Union
5
+
6
+ import pathspec
7
+ from byzerllm import ByzerLLM
8
+ import byzerllm
9
+ from loguru import logger
10
+ import traceback
11
+
12
+ from autocoder.common import AutoCoderArgs, SourceCode
13
+ from importlib.metadata import version
14
+ from pydantic import BaseModel
15
+ from autocoder.common import openai_content as OpenAIContentProcessor
16
+ from autocoder.rag.long_context_rag import LongContextRAG
17
+ import json, os
18
+ from autocoder.agent.base_agentic.base_agent import BaseAgent
19
+ from autocoder.agent.base_agentic.types import AgentRequest
20
+ from autocoder.common import SourceCodeList
21
+ from autocoder.rag.tools import register_search_tool, register_recall_tool
22
+ from byzerllm.utils.types import SingleOutputMeta
23
+ from autocoder.utils.llms import get_single_llm
24
+ try:
25
+ from autocoder_pro.rag.llm_compute import LLMComputeEngine
26
+ pro_version = version("auto-coder-pro")
27
+ autocoder_version = version("auto-coder")
28
+ logger.warning(
29
+ f"auto-coder-pro({pro_version}) plugin is enabled in auto-coder.rag({autocoder_version})")
30
+ except ImportError:
31
+ logger.warning(
32
+ "Please install auto-coder-pro to enhance llm compute ability")
33
+ LLMComputeEngine = None
34
+
35
+
36
+ class RAGAgent(BaseAgent):
37
+ def __init__(self, name: str,
38
+ llm: Union[byzerllm.ByzerLLM, byzerllm.SimpleByzerLLM],
39
+ files: SourceCodeList,
40
+ args: AutoCoderArgs,
41
+ rag: LongContextRAG,
42
+ conversation_history: Optional[List[Dict[str, Any]]] = None):
43
+
44
+ self.default_llm = self.llm
45
+ self.context_prune_llm = self.default_llm
46
+ if self.default_llm.get_sub_client("context_prune_model"):
47
+ self.context_prune_llm = self.default_llm.get_sub_client("context_prune_model")
48
+
49
+ self.llm = self.default_llm
50
+ if self.default_llm.get_sub_client("agentic_model"):
51
+ self.llm = self.default_llm.get_sub_client("agentic_model")
52
+
53
+ self.rag = rag
54
+ super().__init__(name, self.llm, files, args, conversation_history, default_tools_list=["read_file"])
55
+ # 注册RAG工具
56
+ # register_search_tool()
57
+ register_recall_tool()
58
+
59
+ class AgenticRAG:
60
+ def __init__(
61
+ self,
62
+ llm: ByzerLLM,
63
+ args: AutoCoderArgs,
64
+ path: str,
65
+ tokenizer_path: Optional[str] = None,
66
+ ) -> None:
67
+ self.llm = llm
68
+ self.args = args
69
+ self.path = path
70
+ self.tokenizer_path = tokenizer_path
71
+ self.rag = LongContextRAG(llm=self.llm, args=self.args, path=self.path, tokenizer_path=self.tokenizer_path)
72
+
73
+
74
+ def build(self):
75
+ pass
76
+
77
+ def search(self, query: str) -> List[SourceCode]:
78
+ return []
79
+
80
+
81
+ def stream_chat_oai(
82
+ self,
83
+ conversations,
84
+ model: Optional[str] = None,
85
+ role_mapping=None,
86
+ llm_config: Dict[str, Any] = {},
87
+ extra_request_params: Dict[str, Any] = {}
88
+ ):
89
+ try:
90
+ return self._stream_chat_oai(
91
+ conversations,
92
+ model=model,
93
+ role_mapping=role_mapping,
94
+ llm_config=llm_config,
95
+ extra_request_params=extra_request_params
96
+ )
97
+ except Exception as e:
98
+ logger.error(f"Error in stream_chat_oai: {str(e)}")
99
+ traceback.print_exc()
100
+ return ["出现错误,请稍后再试。"], []
101
+
102
+ @byzerllm.prompt()
103
+ def conversation_to_query(self,messages: List[Dict[str, Any]]):
104
+ '''
105
+ 【历史对话】按时间顺序排列,从旧到新:
106
+ {% for message in messages %}
107
+ <message>
108
+ {% if message.role == "user" %}【用户】{% else %}【助手】{% endif %}
109
+ <content>
110
+ {{ message.content }}
111
+ </content>
112
+ </message>
113
+ {% endfor %}
114
+
115
+ 【当前问题】用户的最新需求如下:
116
+ <current_query>
117
+ {{ query }}
118
+ </current_query>
119
+ '''
120
+ temp_messages = messages[0:-1]
121
+ message = messages[-1]
122
+
123
+ return {
124
+ "messages": temp_messages,
125
+ "query":message["content"]
126
+ }
127
+
128
+
129
+ def system_prompt(self):
130
+ '''
131
+ 你是一个基于知识库的智能助手,我的核心能力是通过检索增强生成(RAG)技术来回答用户问题。
132
+
133
+ 你的工作流程如下:
134
+ 1. 当用户提出问题时,我会首先理解问题的核心意图和关键信息需求
135
+ 2. 你会从多个角度分析问题,确定最佳的检索策略和关键词,然后召回工具 recall 获取与问题最相关的详细内容,只有在特别有必要的情况下,你才回使用 read_file 来获得相关文件更详细的信息。
136
+ 5. 如果获得的信息足够回答用户问题,你会直接生成回答。
137
+ 6. 如果获得的信息不足以回答用户问题,你会继续使用 recall 工具,直到你确信已经获取了足够的信息来回答用户问题。
138
+ 7. 有的问题可能需要拆解成多个问题,分别进行recall,然后最终得到的结果才是完整信息,最后才能进行回答。
139
+
140
+ 此外,你回答会遵循以下要求:
141
+
142
+ 1. 严格基于召回的文档内容回答
143
+ - 如果召回的文档提供的信息无法回答问题,请明确回复:"抱歉,文档中没有足够的信息来回答这个问题。"
144
+ - 不要添加、推测或扩展文档未提及的信息
145
+
146
+ 2. 格式如 ![image](/path/to/images/path.png) 的 Markdown 图片处理
147
+ - 根据Markdown 图片前后文本内容推测改图片与问题的相关性,有相关性则在回答中输出该Markdown图片路径
148
+ - 根据相关图片在文档中的位置,自然融入答复内容,保持上下文连贯
149
+ - 完整保留原始图片路径,不省略任何部分
150
+
151
+ 3. 回答格式要求
152
+ - 使用markdown格式提升可读性
153
+ {% if local_image_host %}
154
+ 4. 图片路径处理
155
+ - 图片地址需返回绝对路径,
156
+ - 对于Windows风格的路径,需要转换为Linux风格, 例如:C:\\Users\\user\\Desktop\\image.png 转换为 C:/Users/user/Desktop/image.png
157
+ - 为请求图片资源 需增加 http://{{ local_image_host }}/static/ 作为前缀
158
+ 例如:/path/to/images/image.png, 返回 http://{{ local_image_host }}/static/path/to/images/image.png
159
+ {% endif %}
160
+ '''
161
+ return {
162
+ "local_image_host": self.args.local_image_host
163
+ }
164
+
165
+
166
+ def _stream_chat_oai(
167
+ self,
168
+ conversations,
169
+ model: Optional[str] = None,
170
+ role_mapping=None,
171
+ llm_config: Dict[str, Any] = {},
172
+ extra_request_params: Dict[str, Any] = {}
173
+ ):
174
+ if not llm_config:
175
+ llm_config = {}
176
+
177
+ if extra_request_params:
178
+ llm_config.update(extra_request_params)
179
+
180
+ conversations = OpenAIContentProcessor.process_conversations(conversations)
181
+
182
+ context = []
183
+
184
+ def _generate_sream():
185
+
186
+ recall_request = AgentRequest(user_input=self.conversation_to_query.prompt(conversations))
187
+ rag_agent = RAGAgent(
188
+ name="RAGAgent",
189
+ llm=self.llm,
190
+ files=SourceCodeList(sources=[]),
191
+ args=self.args,
192
+ rag=self.rag,
193
+ conversation_history=[]
194
+ )
195
+
196
+ rag_agent.who_am_i(self.system_prompt.prompt())
197
+
198
+ events =rag_agent.run_with_generator(recall_request)
199
+ for (t,content) in events:
200
+ if t == "thinking":
201
+ yield ("", SingleOutputMeta(
202
+ generated_tokens_count=0,
203
+ input_tokens_count=0,
204
+ reasoning_content=content,
205
+ ))
206
+ else:
207
+ yield (content, SingleOutputMeta(
208
+ generated_tokens_count=0,
209
+ input_tokens_count=0,
210
+ reasoning_content="",
211
+ ))
212
+
213
+ return _generate_sream(), context
214
+
215
+
216
+
217
+
@@ -10,7 +10,6 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
10
10
  from typing import List, Dict, Any, Optional, Tuple, Union
11
11
  import numpy as np
12
12
  from loguru import logger
13
- from typing import Union
14
13
  from byzerllm import SimpleByzerLLM, ByzerLLM
15
14
  from autocoder.utils.llms import get_llm_names
16
15
 
@@ -31,10 +30,12 @@ from autocoder.rag.cache.base_cache import (
31
30
  FileInfo,
32
31
  CacheItem,
33
32
  )
34
- from autocoder.rag.utils import process_file_in_multi_process, process_file_local
33
+ from autocoder.rag.utils import (
34
+ process_file_in_multi_process,
35
+ process_file_local,
36
+ )
35
37
  from autocoder.rag.variable_holder import VariableHolder
36
- from byzerllm import SimpleByzerLLM, ByzerLLM
37
- from .failed_files_utils import save_failed_files, load_failed_files
38
+ from .failed_files_utils import save_failed_files
38
39
 
39
40
  if platform.system() != "Windows":
40
41
  import fcntl
@@ -66,7 +67,8 @@ class DuckDBLocalContext:
66
67
  def __enter__(self) -> "duckdb.DuckDBPyConnection":
67
68
  if not os.path.exists(os.path.dirname(self.database_path)):
68
69
  raise ValueError(
69
- f"Directory {os.path.dirname(self.database_path)} does not exist."
70
+ f"Directory {os.path.dirname(self.database_path)} "
71
+ f"does not exist."
70
72
  )
71
73
 
72
74
  self._conn = duckdb.connect(self.database_path)
@@ -97,10 +99,12 @@ class LocalDuckdbStorage:
97
99
  self.persist_dir = persist_dir
98
100
  self.cache_dir = os.path.join(self.persist_dir, ".cache")
99
101
  self.args = args
100
- logger.info(f"正在启动 DuckDBVectorStore.")
102
+ logger.info("正在启动 DuckDBVectorStore.")
101
103
 
102
104
  if self.database_name != ":memory:":
103
- self.database_path = os.path.join(self.cache_dir, self.database_name)
105
+ self.database_path = os.path.join(
106
+ self.cache_dir, self.database_name
107
+ )
104
108
 
105
109
  if self.database_name == ":memory:":
106
110
  self._conn = duckdb.connect(self.database_name)
@@ -114,7 +118,8 @@ class LocalDuckdbStorage:
114
118
  self._conn = None
115
119
  logger.info(
116
120
  f"DuckDBVectorStore 初始化完成, 存储目录: {self.cache_dir}, "
117
- f"数据库名称: {self.database_name}, 数据表名称: {self.table_name}"
121
+ f"数据库名称: {self.database_name}, "
122
+ f"数据表名称: {self.table_name}"
118
123
  )
119
124
 
120
125
  @classmethod
@@ -167,15 +172,17 @@ class LocalDuckdbStorage:
167
172
  retry_count += 1
168
173
  if retry_count >= max_retries:
169
174
  logger.error(
170
- f"Failed to get embedding after {max_retries} attempts: {str(e)}"
175
+ f"Failed to get embedding after {max_retries} "
176
+ f"attempts: {str(e)}"
171
177
  )
172
178
  raise
173
179
 
174
180
  # Sleep between 1-5 seconds before retrying
175
181
  sleep_time = 1 + (retry_count * 1.5)
176
182
  logger.warning(
177
- f"Embedding API call failed (attempt {retry_count}/{max_retries}). "
178
- f"Error: {str(e)}. Retrying in {sleep_time:.1f} seconds..."
183
+ f"Embedding API call failed (attempt {retry_count}/"
184
+ f"{max_retries}). Error: {str(e)}. Retrying in "
185
+ f"{sleep_time:.1f} seconds..."
179
186
  )
180
187
  time.sleep(sleep_time)
181
188
 
@@ -244,7 +251,9 @@ class LocalDuckdbStorage:
244
251
 
245
252
  if not context_chunk["raw_content"]:
246
253
  context_chunk["raw_content"] = "empty"
247
- context_chunk["raw_content"] = context_chunk["raw_content"][: self.args.rag_emb_text_size]
254
+ context_chunk["raw_content"] = context_chunk["raw_content"][
255
+ : self.args.rag_emb_text_size
256
+ ]
248
257
 
249
258
  return (
250
259
  context_chunk["_id"],
@@ -343,11 +352,14 @@ class LocalDuckDBStorageCache(BaseCacheManager):
343
352
  )
344
353
  self.queue = []
345
354
  self.chunk_size = 1000
346
- self.max_output_tokens = extra_params.hybrid_index_max_output_tokens
355
+ self.max_output_tokens = (
356
+ extra_params.hybrid_index_max_output_tokens
357
+ )
347
358
 
348
359
  # 设置缓存文件路径
349
360
  self.cache_dir = os.path.join(self.path, ".cache")
350
- self.cache_file = os.path.join(self.cache_dir, "duckdb_storage_speedup.jsonl")
361
+ self.cache_file = os.path.join(self.cache_dir,
362
+ "duckdb_storage_speedup.jsonl")
351
363
  self.cache: Dict[str, CacheItem] = {}
352
364
  # 创建缓存目录
353
365
  if not os.path.exists(self.cache_dir):
@@ -356,7 +368,9 @@ class LocalDuckDBStorageCache(BaseCacheManager):
356
368
  # failed files support
357
369
  from .failed_files_utils import load_failed_files
358
370
 
359
- self.failed_files_path = os.path.join(self.cache_dir, "failed_files.json")
371
+ self.failed_files_path = os.path.join(
372
+ self.cache_dir, "failed_files.json"
373
+ )
360
374
  self.failed_files = load_failed_files(self.failed_files_path)
361
375
 
362
376
  self.lock = threading.Lock()
@@ -406,7 +420,8 @@ class LocalDuckDBStorageCache(BaseCacheManager):
406
420
  continue
407
421
  return cache
408
422
  except Exception as e:
409
- logger.error(f"Error loading cache file: {str(e)}")
423
+ logger.warning(f"Error loading cache file: {str(e)}")
424
+ logger.exception(e)
410
425
  return {}
411
426
  return {}
412
427
 
@@ -421,7 +436,8 @@ class LocalDuckDBStorageCache(BaseCacheManager):
421
436
  json.dump(cache_item.model_dump(), f, ensure_ascii=False)
422
437
  f.write("\n")
423
438
  except IOError as e:
424
- logger.error(f"Error writing cache file: {str(e)}")
439
+ logger.warning(f"Error writing cache file: {str(e)}")
440
+ logger.exception(e)
425
441
  else:
426
442
  lock_file = cache_file + ".lock"
427
443
  with open(lock_file, "w", encoding="utf-8") as lockf:
@@ -510,11 +526,12 @@ class LocalDuckDBStorageCache(BaseCacheManager):
510
526
  self.write_cache()
511
527
 
512
528
  if items:
513
- logger.info("[BUILD CACHE] Clearing existing cache from DuckDB Storage")
529
+ logger.info("[BUILD CACHE] Clearing DuckDB Storage cache")
514
530
  self.storage.truncate_table()
531
+ logger.info(f"[BUILD CACHE] Preparing to write to DuckDB Storage.")
515
532
  logger.info(
516
- f"[BUILD CACHE] Preparing to write to DuckDB Storage, "
517
- f"total chunks: {len(items)}, total files: {len(files_to_process)}"
533
+ f"[BUILD CACHE] Total chunks: {len(items)}, "
534
+ f"Total files: {len(files_to_process)}"
518
535
  )
519
536
 
520
537
  # Use a fixed optimal batch size instead of dividing by worker count
@@ -526,9 +543,10 @@ class LocalDuckDBStorageCache(BaseCacheManager):
526
543
  total_batches = len(item_batches)
527
544
  completed_batches = 0
528
545
 
546
+ logger.info(f"[BUILD CACHE] Writing to DuckDB Storage.")
529
547
  logger.info(
530
- f"[BUILD CACHE] Starting to write to DuckDB Storage using {batch_size} items per batch, "
531
- f"total batches: {total_batches}"
548
+ f"[BUILD CACHE] Batch size: {batch_size}, "
549
+ f"Total batches: {total_batches}"
532
550
  )
533
551
  start_time = time.time()
534
552
 
@@ -569,18 +587,26 @@ class LocalDuckDBStorageCache(BaseCacheManager):
569
587
  or (completed_batches == total_batches)
570
588
  or (completed_batches % max(1, total_batches // 10) == 0)
571
589
  ):
590
+ progress_percent = (
591
+ completed_batches / total_batches * 100
592
+ if total_batches > 0
593
+ else 0
594
+ )
572
595
  logger.info(
573
- f"[BUILD CACHE] Progress: {completed_batches}/{total_batches} batches completed "
574
- f"({(completed_batches / total_batches * 100):.1f}%) "
575
- f"Estimated time remaining: {remaining:.1f}s"
596
+ f"[BUILD CACHE] Progress: {completed_batches}/"
597
+ f"{total_batches} ({progress_percent:.1f}%). "
598
+ f"ETA: {remaining:.1f}s"
576
599
  )
577
600
  except Exception as e:
578
601
  logger.error(f"[BUILD CACHE] Error saving batch: {str(e)}")
579
602
  # Add more detailed error information
603
+ batch_len_info = (
604
+ len(batch) if "batch" in locals() else "unknown"
605
+ )
580
606
  logger.error(
581
- f"[BUILD CACHE] Error details: batch size: "
582
- f"{len(batch) if 'batch' in locals() else 'unknown'}"
607
+ f"[BUILD CACHE] Error details: batch size: {batch_len_info}"
583
608
  )
609
+ logger.exception(e)
584
610
 
585
611
  total_time = time.time() - start_time
586
612
  logger.info(
@@ -622,6 +648,7 @@ class LocalDuckDBStorageCache(BaseCacheManager):
622
648
  time.sleep(self.extra_params.anti_quota_limit)
623
649
  except Exception as err:
624
650
  logger.error(f"Error in saving chunk: {str(err)}")
651
+ logger.exception(err)
625
652
 
626
653
  def process_queue(self):
627
654
  while self.queue:
@@ -671,7 +698,8 @@ class LocalDuckDBStorageCache(BaseCacheManager):
671
698
  self.failed_files.add(file_info.file_path)
672
699
  save_failed_files(self.failed_files_path, self.failed_files)
673
700
  except Exception as e:
674
- logger.error(f"Error in process_queue: {e}")
701
+ logger.error(f"Error in process_queue: {str(e)}")
702
+ logger.exception(e)
675
703
  self.failed_files.add(file_info.file_path)
676
704
  save_failed_files(self.failed_files_path, self.failed_files)
677
705
 
@@ -816,17 +844,18 @@ class LocalDuckDBStorageCache(BaseCacheManager):
816
844
  for doc in cached_data.content:
817
845
  if total_tokens + doc["tokens"] > self.max_output_tokens:
818
846
  logger.info(
819
- f"当前检索已超出用户设置 Hybrid Index Max Tokens:{self.max_output_tokens},"
820
- f"累计tokens: {total_tokens}, "
821
- f"经过向量搜索共检索出 {len(result.keys())} 个文档, 共 {len(self.cache.keys())} 个文档"
847
+ f"当前检索已超出用户设置 Hybrid Index Max Tokens:"
848
+ f"{self.max_output_tokens},累计tokens: {total_tokens}, "
849
+ f"经过向量搜索共检索出 {len(result.keys())} 个文档, "
850
+ f"共 {len(self.cache.keys())} 个文档"
822
851
  )
823
852
  return result
824
853
  total_tokens += doc["tokens"]
825
854
  result[file_path] = cached_data.model_dump()
826
855
  logger.info(
827
856
  f"用户Hybrid Index Max Tokens设置为:{self.max_output_tokens},"
828
- f"累计tokens: {total_tokens}, "
829
- f"经过向量搜索共检索出 {len(result.keys())} 个文档, 共 {len(self.cache.keys())} 个文档"
857
+ f"累计tokens: {total_tokens}, 经过向量搜索共检索出 "
858
+ f"{len(result.keys())} 个文档, 共 {len(self.cache.keys())} 个文档"
830
859
  )
831
860
  return result
832
861
 
@@ -904,6 +933,7 @@ class LocalDuckDBStorageCache(BaseCacheManager):
904
933
  query_results.append((query, query_result))
905
934
  except Exception as e:
906
935
  logger.error(f"处理查询 '{query}' 时出错: {str(e)}")
936
+ logger.exception(e)
907
937
 
908
938
  logger.info(f"所有查询共返回 {sum(len(r) for _, r in query_results)} 条记录")
909
939
 
@@ -2,7 +2,9 @@ from typing import List, Dict, Any, Optional, Union
2
2
  import logging
3
3
  import byzerllm
4
4
  from pydantic import BaseModel
5
+ from autocoder.rag.types import RAGStat
5
6
  from autocoder.common import AutoCoderArgs
7
+ from byzerllm import MetaHolder
6
8
 
7
9
  logger = logging.getLogger(__name__)
8
10
 
@@ -86,7 +88,7 @@ class ConversationToQueries:
86
88
  ```
87
89
  """
88
90
 
89
- def extract_queries(self, conversations: List[Dict[str, Any]], max_queries: int = 3) -> List[SearchQuery]:
91
+ def extract_queries(self, conversations: List[Dict[str, Any]], max_queries: int = 3,rag_stat:Optional[RAGStat] = None) -> List[SearchQuery]:
90
92
  """
91
93
  从对话历史中提取搜索查询。
92
94
 
@@ -99,10 +101,39 @@ class ConversationToQueries:
99
101
  """
100
102
  try:
101
103
  # 使用 prompt 函数生成搜索查询
102
- queries = self.generate_search_queries.with_llm(self.llm).with_return_type(SearchQuery).run(
104
+ model_name = self.llm.default_model_name
105
+ meta_holder = MetaHolder()
106
+ queries = self.generate_search_queries.with_llm(self.llm).with_return_type(SearchQuery).with_meta(
107
+ meta_holder).run(
103
108
  conversations=conversations,
104
109
  max_queries=max_queries
105
- )
110
+ )
111
+
112
+ # 如果有元数据且有 rag_stat,则记录模型使用情况
113
+ if meta_holder.get_meta() and rag_stat:
114
+ meta_dict = meta_holder.get_meta()
115
+ input_tokens_count = meta_dict.get("input_tokens_count", 0)
116
+ generated_tokens_count = meta_dict.get("generated_tokens_count", 0)
117
+
118
+ # 检查模型是否已存在于 other_stats 中
119
+ found = False
120
+ for other_stat in rag_stat.other_stats:
121
+ if other_stat.model_name == model_name:
122
+ # 模型已存在,累加统计数据
123
+ other_stat.total_input_tokens += input_tokens_count
124
+ other_stat.total_generated_tokens += generated_tokens_count
125
+ found = True
126
+ break
127
+
128
+ # 如果模型不存在,添加新的 OtherStat
129
+ if not found and (input_tokens_count > 0 or generated_tokens_count > 0):
130
+ from autocoder.rag.types import OtherStat
131
+ new_stat = OtherStat(
132
+ total_input_tokens=input_tokens_count,
133
+ total_generated_tokens=generated_tokens_count,
134
+ model_name=model_name
135
+ )
136
+ rag_stat.other_stats.append(new_stat)
106
137
 
107
138
  # 按重要性排序
108
139
  queries.sort(key=lambda x: x.importance, reverse=True)
@@ -116,7 +147,8 @@ def extract_search_queries(
116
147
  conversations: List[Dict[str, Any]],
117
148
  args:AutoCoderArgs,
118
149
  llm: Union[byzerllm.ByzerLLM, byzerllm.SimpleByzerLLM],
119
- max_queries: int = 3,
150
+ max_queries: int = 3,
151
+ rag_stat:Optional[RAGStat] = None
120
152
  ) -> List[SearchQuery]:
121
153
  """
122
154
  从对话历史中提取搜索查询的便捷函数。
@@ -133,7 +165,7 @@ def extract_search_queries(
133
165
  return []
134
166
  try:
135
167
  extractor = ConversationToQueries(llm)
136
- return extractor.extract_queries(conversations, max_queries)
168
+ return extractor.extract_queries(conversations, max_queries,rag_stat)
137
169
  except Exception as e:
138
170
  logger.error(f"Error extracting search queries from conversation: {str(e)}")
139
171
  return []