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.
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/METADATA +2 -2
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/RECORD +27 -57
- autocoder/agent/base_agentic/base_agent.py +202 -52
- autocoder/agent/base_agentic/default_tools.py +38 -6
- autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +83 -43
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +88 -25
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +171 -62
- autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +101 -56
- autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +5 -0
- autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +5 -0
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +145 -32
- autocoder/auto_coder_rag.py +80 -11
- autocoder/models.py +2 -2
- autocoder/rag/agentic_rag.py +217 -0
- autocoder/rag/cache/local_duckdb_storage_cache.py +63 -33
- autocoder/rag/conversation_to_queries.py +37 -5
- autocoder/rag/long_context_rag.py +161 -41
- autocoder/rag/tools/__init__.py +10 -0
- autocoder/rag/tools/recall_tool.py +163 -0
- autocoder/rag/tools/search_tool.py +126 -0
- autocoder/rag/types.py +36 -0
- autocoder/utils/_markitdown.py +59 -13
- autocoder/version.py +1 -1
- autocoder/agent/agentic_edit.py +0 -833
- autocoder/agent/agentic_edit_tools/__init__.py +0 -28
- autocoder/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +0 -32
- autocoder/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +0 -29
- autocoder/agent/agentic_edit_tools/base_tool_resolver.py +0 -29
- autocoder/agent/agentic_edit_tools/execute_command_tool_resolver.py +0 -84
- autocoder/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +0 -75
- autocoder/agent/agentic_edit_tools/list_files_tool_resolver.py +0 -62
- autocoder/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +0 -30
- autocoder/agent/agentic_edit_tools/read_file_tool_resolver.py +0 -36
- autocoder/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +0 -95
- autocoder/agent/agentic_edit_tools/search_files_tool_resolver.py +0 -70
- autocoder/agent/agentic_edit_tools/use_mcp_tool_resolver.py +0 -55
- autocoder/agent/agentic_edit_tools/write_to_file_tool_resolver.py +0 -98
- autocoder/agent/agentic_edit_types.py +0 -124
- autocoder/auto_coder_lang.py +0 -60
- autocoder/auto_coder_rag_client_mcp.py +0 -170
- autocoder/auto_coder_rag_mcp.py +0 -193
- autocoder/common/llm_rerank.py +0 -84
- autocoder/common/model_speed_test.py +0 -392
- autocoder/common/v2/agent/agentic_edit_conversation.py +0 -188
- autocoder/common/v2/agent/ignore_utils.py +0 -50
- autocoder/dispacher/actions/plugins/action_translate.py +0 -214
- autocoder/ignorefiles/__init__.py +0 -4
- autocoder/ignorefiles/ignore_file_utils.py +0 -63
- autocoder/ignorefiles/test_ignore_file_utils.py +0 -91
- autocoder/linters/code_linter.py +0 -588
- autocoder/rag/loaders/test_image_loader.py +0 -209
- autocoder/rag/raw_rag.py +0 -96
- autocoder/rag/simple_directory_reader.py +0 -646
- autocoder/rag/simple_rag.py +0 -404
- autocoder/regex_project/__init__.py +0 -162
- autocoder/utils/coder.py +0 -125
- autocoder/utils/tests.py +0 -37
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/entry_points.txt +0 -0
- {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. 格式如  的 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
|
|
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
|
|
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)}
|
|
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(
|
|
102
|
+
logger.info("正在启动 DuckDBVectorStore.")
|
|
101
103
|
|
|
102
104
|
if self.database_name != ":memory:":
|
|
103
|
-
self.database_path = os.path.join(
|
|
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},
|
|
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}
|
|
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}/
|
|
178
|
-
f"Error: {str(e)}. Retrying in
|
|
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"][
|
|
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 =
|
|
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,
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
|
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]
|
|
517
|
-
f"
|
|
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]
|
|
531
|
-
f"
|
|
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}/
|
|
574
|
-
f"
|
|
575
|
-
f"
|
|
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:
|
|
820
|
-
f"
|
|
821
|
-
f"经过向量搜索共检索出 {len(result.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"
|
|
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
|
-
|
|
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 []
|