2222
2323DEFAULT_LLM_BASE_URL = "https://api.openai.com/v1"
2424DEFAULT_LLM_MODEL = "gpt-4.1-mini"
25- DEFAULT_LLM_TIMEOUT = 600
25+ DEFAULT_LLM_TIMEOUT = 1200
2626DEFAULT_MAX_VERSIONS_PER_MERGE = 8
2727DEFAULT_MAX_PROMPT_CHARS = 50000
2828DEFAULT_LLM_MAX_RETRIES = 3
@@ -88,6 +88,7 @@ def _build_prompt(source_type: str, hostname: str, versions: List[Dict[str, Any]
8888 "请把同一个站点的多个版本阅读源合并成一个最优版本。"
8989 "输出必须是合并后的单个源对象本身,必须是紧凑 JSON。"
9090 "不要输出解释,不要输出 markdown,不要输出数组,不要输出外层包装对象。"
91+ "请直接给出最终答案,不要解释、不要分步骤、不要用‘思考’标签。"
9192 "要求:\n "
9293 "1. 保留同一含义下信息更完整、更具体的字段。\n "
9394 "2. 不要引入原始数据中不存在的新站点 URL。\n "
@@ -98,32 +99,6 @@ def _build_prompt(source_type: str, hostname: str, versions: List[Dict[str, Any]
9899 f"原始版本如下:\n { versions_json } "
99100 )
100101
101- @staticmethod
102- def _extract_stream_delta (payload : Dict [str , Any ]) -> Dict [str , str ]:
103- choices = payload .get ("choices" )
104- if not isinstance (choices , list ) or not choices :
105- return {}
106- choice = choices [0 ]
107- if not isinstance (choice , dict ):
108- return {}
109- delta = choice .get ("delta" )
110- if isinstance (delta , dict ):
111- reasoning = delta .get ("reasoning_content" )
112- content = delta .get ("content" )
113- result : Dict [str , str ] = {}
114- if isinstance (reasoning , str ):
115- result ["reasoning" ] = reasoning
116- if isinstance (content , str ):
117- result ["content" ] = content
118- if result :
119- return result
120- message = choice .get ("message" )
121- if isinstance (message , dict ):
122- content = message .get ("content" )
123- if isinstance (content , str ):
124- return {"content" : content }
125- return {}
126-
127102 def _post_and_collect_content (self , payload : Dict [str , Any ]) -> str :
128103 headers = {
129104 "Authorization" : f"Bearer { self .api_key } " ,
@@ -135,97 +110,38 @@ def _post_and_collect_content(self, payload: Dict[str, Any]) -> str:
135110 "Start LLM merge request: "
136111 f"model={ self .model } , versions_payload_chars={ len (json .dumps (payload , ensure_ascii = False ))} "
137112 )
138- with requests .post (
139- url ,
140- headers = headers ,
141- json = payload ,
142- timeout = self .timeout ,
143- stream = bool (payload .get ("stream" )),
144- ) as response :
113+ with requests .post (url , headers = headers , json = payload , timeout = self .timeout ) as response :
145114 logger .info (
146115 "LLM merge response headers received: "
147116 f"status={ response .status_code } , elapsed={ time .time () - request_started_at :.2f} s"
148117 )
149118 response .raise_for_status ()
150- if not payload .get ("stream" ):
151- try :
152- result = response .json ()
153- except Exception :
154- return response .text
155- choices = result .get ("choices" , [])
156- if choices and isinstance (choices [0 ], dict ):
157- message = choices [0 ].get ("message" , {})
158- if isinstance (message , dict ):
159- content = message .get ("content" )
160- if isinstance (content , str ):
161- logger .info (
162- "LLM merge request finished: "
163- f"elapsed={ time .time () - request_started_at :.2f} s, content_chars={ len (content )} "
164- )
165- return content
119+ try :
120+ result = response .json ()
121+ except Exception :
166122 text = response .text
167123 logger .info (
168124 "LLM merge request finished with raw text: "
169125 f"elapsed={ time .time () - request_started_at :.2f} s, content_chars={ len (text )} "
170126 )
171127 return text
172-
173- content_fragments : List [str ] = []
174- reasoning_chars = 0
175- event_count = 0
176- first_chunk_logged = False
177- for raw_line in response .iter_lines (decode_unicode = False ):
178- if not raw_line :
179- continue
180- if isinstance (raw_line , bytes ):
181- try :
182- line = raw_line .decode ("utf-8" ).strip ()
183- except UnicodeDecodeError :
184- line = raw_line .decode ("utf-8" , errors = "ignore" ).strip ()
185- logger .warning (
186- "Decode LLM stream line with ignored utf-8 errors: "
187- f"raw_bytes={ len (raw_line )} "
188- )
189- else :
190- line = raw_line .strip ()
191- if not line .startswith ("data:" ):
192- continue
193- data = line [5 :].strip ()
194- if data == "[DONE]" :
195- break
196- event_count += 1
197- try :
198- event_payload = json .loads (data )
199- except json .JSONDecodeError :
200- logger .warning (f"Invalid LLM stream payload: { data [:200 ]} " )
201- continue
202- delta = self ._extract_stream_delta (event_payload )
203- reasoning_piece = delta .get ("reasoning" , "" )
204- content_piece = delta .get ("content" , "" )
205- if reasoning_piece :
206- reasoning_chars += len (reasoning_piece )
207- if content_piece :
208- content_fragments .append (content_piece )
209- if not first_chunk_logged :
210- logger .info (
211- "LLM merge first stream chunk received: "
212- f"elapsed={ time .time () - request_started_at :.2f} s, chunk_chars={ len (content_piece )} "
213- )
214- first_chunk_logged = True
215- elif event_count % 20 == 0 :
128+ choices = result .get ("choices" , [])
129+ if choices and isinstance (choices [0 ], dict ):
130+ message = choices [0 ].get ("message" , {})
131+ if isinstance (message , dict ):
132+ content = message .get ("content" )
133+ if isinstance (content , str ):
216134 logger .info (
217- "LLM merge stream progress: "
218- f"elapsed={ time .time () - request_started_at :.2f} s, "
219- f"events={ event_count } , reasoning_chars={ reasoning_chars } , "
220- f"content_chars={ sum (len (item ) for item in content_fragments )} "
135+ "LLM merge request finished: "
136+ f"elapsed={ time .time () - request_started_at :.2f} s, content_chars={ len (content )} "
221137 )
222- content = "" .join (content_fragments )
138+ return content
139+ text = response .text
223140 logger .info (
224- "LLM merge stream finished: "
225- f"elapsed={ time .time () - request_started_at :.2f} s, "
226- f"events={ event_count } , reasoning_chars={ reasoning_chars } , content_chars={ len (content )} "
141+ "LLM merge request finished with raw text: "
142+ f"elapsed={ time .time () - request_started_at :.2f} s, content_chars={ len (text )} "
227143 )
228- return content
144+ return text
229145
230146 def merge_sources (
231147 self ,
@@ -238,7 +154,6 @@ def merge_sources(
238154 payload = {
239155 "model" : self .model ,
240156 "temperature" : 0 ,
241- "stream" : True ,
242157 "messages" : [
243158 {"role" : "system" , "content" : "你只返回一个合法、紧凑、无包装层的 JSON 对象。" },
244159 {
@@ -325,7 +240,7 @@ def iter_source_files(self) -> List[str]:
325240 if name .endswith (".json" ):
326241 file_path = os .path .join (root , name )
327242 file_list .append ((self ._read_version_count (file_path ), file_path ))
328- file_list .sort (key = lambda item : (- item [0 ], item [1 ]))
243+ file_list .sort (key = lambda item : (item [0 ], item [1 ]))
329244 return [file_path for _ , file_path in file_list ]
330245
331246 def merge_file (self , file_path : str ) -> str :
0 commit comments