-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinference_java.py
More file actions
705 lines (590 loc) · 25.7 KB
/
Copy pathinference_java.py
File metadata and controls
705 lines (590 loc) · 25.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
import os
# 设置Hugging Face缓存目录
import sys
# 最优先:禁用 vLLM v1(必须在导入 vLLM 之前设置)
os.environ['VLLM_USE_V1'] = '0'
# 在导入torch前设置GPU,让整个进程只看到指定的GPU
if len(sys.argv) >= 5:
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = sys.argv[4] # gpu_id 是第5个参数(索引4)
import re
import time
import psutil
import torch
from pathlib import Path
from typing import Tuple
# 修复 vLLM 多进程问题
import multiprocessing
try:
multiprocessing.set_start_method('spawn', force=True)
except RuntimeError:
pass
# vLLM 环境变量优化 - WSL2兼容性
os.environ['VLLM_USE_MODELSCOPE'] = '0'
os.environ['VLLM_WORKER_MULTIPROC_METHOD'] = 'spawn'
os.environ['VLLM_ALLOW_LONG_MAX_MODEL_LEN'] = '1'
os.environ['CUDA_LAUNCH_BLOCKING'] = '1' # 同步CUDA调用以便调试
# 将 vllm 缓存和临时文件重定向到 /data1,避免根分区空间不足
os.environ['VLLM_CACHE_ROOT'] = '/data1/vllm_cache'
os.environ['TMPDIR'] = '/data1/tmp'
os.environ['TEMP'] = '/data1/tmp'
os.environ['TMP'] = '/data1/tmp'
# 禁用 vLLM 使用统计收集,避免磁盘空间不足问题
os.environ['VLLM_DO_NOT_TRACK'] = '1' # 禁用使用统计跟踪
os.environ['VLLM_USAGE_STATS_SERVER'] = '' # 禁用使用统计服务器
# 确保目录存在
os.makedirs('/data1/tmp', exist_ok=True)
os.makedirs('/data1/vllm_cache', exist_ok=True)
# 离线模式设置 - 避免连接HuggingFace Hub (已禁用)
# os.environ['HF_HUB_OFFLINE'] = '1' # HuggingFace Hub离线模式
# os.environ['TRANSFORMERS_OFFLINE'] = '1' # Transformers离线模式
# os.environ['HF_DATASETS_OFFLINE'] = '1' # Datasets离线模式
# 导入 vLLM
from vllm import LLM, SamplingParams
from vllm.lora.request import LoRARequest
# 性能优化设置
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
torch.backends.cuda.enable_flash_sdp(True)
torch.backends.cuda.enable_math_sdp(True)
torch.backends.cuda.enable_mem_efficient_sdp(True)
torch.backends.cudnn.benchmark = True
torch.backends.cudnn.deterministic = False
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:512,expandable_segments:True'
def setup_gpu_environment():
"""设置GPU环境,包括冲突检测和预防"""
if len(sys.argv) < 5:
print("Usage: python inference_java.py <model_key> <num_processes> <process_id> <gpu_id> [num_generations]")
print("参数说明:")
print(" model_key: 模型名称")
print(" num_processes: 总进程数")
print(" process_id: 当前进程ID (0开始)")
print(" gpu_id: GPU编号 (0开始)")
print(" num_generations: 生成次数 (可选,默认10)")
sys.exit(1)
gpu_id = sys.argv[4]
# 验证GPU编号格式
try:
gpu_num = int(gpu_id)
if gpu_num < 0:
raise ValueError("GPU编号不能为负数")
except ValueError as e:
print(f"错误:无效的GPU编号 '{gpu_id}': {e}")
sys.exit(1)
# 由于CUDA_VISIBLE_DEVICES已设置,PyTorch只能看到一个GPU(编号为0)
if torch.cuda.is_available():
gpu_count = torch.cuda.device_count()
print(f"物理GPU {gpu_id} 已映射为 cuda:0 (PyTorch可见GPU数: {gpu_count})")
# 测试GPU是否可用
try:
test_tensor = torch.tensor([1.0]).cuda(0)
print(f"GPU 测试成功: {test_tensor.device}")
del test_tensor
except Exception as e:
print(f"GPU 测试失败: {e}")
sys.exit(1)
else:
print("错误:CUDA不可用")
sys.exit(1)
# GPU冲突检测和预防(每个进程有独立的锁)
# 使用项目目录下的锁文件,避免 /tmp 空间不足
lock_dir = os.path.join(os.getcwd(), '.gpu_locks')
os.makedirs(lock_dir, exist_ok=True)
lock_file = os.path.join(lock_dir, f"gpu_{gpu_id}_proc_{sys.argv[3]}_java.lock")
try:
# 尝试获取GPU锁
lock_fd = os.open(lock_file, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
# 写入进程信息
process_info = f"PID:{os.getpid()},TIME:{time.time()},SCRIPT:{sys.argv[0]}\n"
os.write(lock_fd, process_info.encode())
os.close(lock_fd)
print(f"成功获取物理GPU {gpu_id} 的锁 (锁文件: {lock_file})")
# 注册清理函数
import atexit
def cleanup_gpu_lock():
try:
if os.path.exists(lock_file):
os.remove(lock_file)
print(f"已释放GPU {gpu_id} 锁")
except:
pass
atexit.register(cleanup_gpu_lock)
except FileExistsError:
# GPU已被占用,检查占用进程状态
try:
with open(lock_file, 'r') as f:
lock_info = f.read().strip()
# 解析锁信息
if lock_info.startswith("PID:"):
parts = lock_info.split(',')
pid_str = parts[0].split(':')[1]
lock_pid = int(pid_str)
# 检查进程是否还存在
if psutil.pid_exists(lock_pid):
proc = psutil.Process(lock_pid)
if proc.is_running():
print(f"错误:GPU {gpu_id} 已被进程 {lock_pid} 占用")
print(f"占用进程信息: {proc.name()} (状态: {proc.status()})")
print("请等待该进程完成或手动终止该进程")
sys.exit(1)
else:
print(f"检测到僵尸锁文件,进程 {lock_pid} 已不存在,清理锁文件")
os.remove(lock_file)
return setup_gpu_environment() # 递归重试
else:
print(f"检测到过期锁文件,进程 {lock_pid} 已不存在,清理锁文件")
os.remove(lock_file)
return setup_gpu_environment() # 递归重试
else:
print(f"检测到格式错误的锁文件,清理并重试")
os.remove(lock_file)
return setup_gpu_environment() # 递归重试
except Exception as e:
print(f"检查GPU锁时出错: {e}")
print(f"建议手动检查并清理 {lock_dir} 目录下的锁文件")
sys.exit(1)
except Exception as e:
print(f"设置GPU锁时出错: {e}")
print(f"锁文件目录: {lock_dir}")
print(f"如果是空间不足问题,请检查该目录的磁盘空间")
sys.exit(1)
print(f"GPU设置完成")
def monitor_gpu_status():
"""监控GPU状态,提供详细的GPU使用信息"""
if not torch.cuda.is_available():
return "CUDA不可用"
try:
current_device = torch.cuda.current_device()
gpu_name = torch.cuda.get_device_name(current_device)
total_memory = torch.cuda.get_device_properties(current_device).total_memory
allocated_memory = torch.cuda.memory_allocated(current_device)
cached_memory = torch.cuda.memory_reserved(current_device)
memory_usage = (allocated_memory / total_memory) * 100
cache_usage = (cached_memory / total_memory) * 100
status = f"当前GPU cuda:{current_device} ({gpu_name}): "
status += f"内存使用 {allocated_memory/1024**3:.1f}GB/{total_memory/1024**3:.1f}GB ({memory_usage:.1f}%), "
status += f"缓存 {cached_memory/1024**3:.1f}GB ({cache_usage:.1f}%)"
return status
except Exception as e:
return f"GPU状态监控失败: {e}"
def cleanup_resources():
"""清理资源,避免文件句柄泄露"""
global llm
try:
print("开始清理资源...")
print(f"清理前GPU状态: {monitor_gpu_status()}")
if llm is not None:
if hasattr(llm, 'llm_engine') and llm.llm_engine is not None:
try:
llm.llm_engine.shutdown()
except:
pass
del llm
llm = None
print("vLLM 模型资源已清理")
# 强制垃圾回收
import gc
gc.collect()
# 清理CUDA缓存
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.synchronize()
print(f"清理后GPU状态: {monitor_gpu_status()}")
except Exception as e:
print(f"资源清理时出错: {e}")
def set_file_limits():
"""设置文件句柄限制"""
try:
import resource
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
print(f"当前文件句柄限制: soft={soft}, hard={hard}")
if soft < hard:
resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
print(f"已提高文件句柄限制到: {hard}")
except ImportError:
print("无法导入resource模块,跳过文件句柄限制设置")
except Exception as e:
print(f"设置文件句柄限制时出错: {e}")
def extract_java_code(text: str) -> str:
"""从生成的文本中提取Java代码(提取最后一个代码块)"""
# 首先尝试匹配完整的代码块(有闭合标记)
matches = re.findall(r'```java(.*?)```', text, re.DOTALL)
if matches:
return matches[-1].strip() # 返回最后一个代码块
return ""
# 模型提示格式(必须与训练时格式一致!)
MODEL_PROMPT_FORMATS = {
'qwen': ('<|im_start|>user\n', '<|im_end|>\n<|im_start|>assistant\n'),
'llama3': ('<|start_header_id|>user<|end_header_id|>\n\n', '<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n'),
'deepseek': ('###Instruction\n', '###response\n\n'), # DeepSeek 格式
'opencoder': ('<|im_start|>user\n', '<|im_end|>\n<|im_start|>assistant\n'),
}
# 模型配置
MODEL_CONFIGS = {
'qwen3-8b': {
'model_path': 'model/qwen3-8b',
},
'qwen3-8b-nopro': {
'model_path': 'merged_models/qwen3-8b-trained-noprompt',
},
'qwen3-8b-parepair': {
'model_path': 'merged_models/qwen3-8b-prarepair',
},
'llama3.1-8b': {
'model_path': 'model/Llama-3-8B-Instruct',
},
'deepseek-6.7b': {
'model_path': 'model/deepseek-coder-6.7b',
},
'deepseek-6.7b-nopro': {
'model_path': 'merged_models/deepseek-6,7b-nopro',
},
'deepseek-6.7b-parepair-fl': {
'model_path': '/root/autodl-tmp/models/deepseek-6.7b-parepair-fl/merged',
},
'opencoder-8b': {
'model_path': 'model/opencoder-8b',
},
'opencoder-8b-nopro': {
'model_path': 'merged_models/opencoder-8b-nopro',
},
'opencoder-8b-parepair': {
'model_path': 'merged_models/opencoder-8b-parepair',
},
}
def get_prompt_format(model_key: str) -> tuple:
"""获取模型的提示格式"""
for key in MODEL_PROMPT_FORMATS:
if model_key.startswith(key):
return MODEL_PROMPT_FORMATS[key]
return '[INST]', '[/INST]' # 默认格式
def load_vllm_model(model_config):
"""使用 vLLM 加载模型"""
from transformers import AutoTokenizer
model_path = model_config['model_path']
adapter_path = model_config.get('adapter_path')
# 如果有 LoRA 适配器,报错退出
if adapter_path and os.path.exists(adapter_path):
print(f"错误:检测到 LoRA 适配器: {adapter_path}")
print("vLLM 不支持 LoRA 适配器,请使用预合并模型")
sys.exit(1)
# vLLM 配置(与 d4j.py 保持一致)
vllm_config = {
"model": model_path,
"gpu_memory_utilization": 0.9,
"trust_remote_code": True,
"max_model_len": 4096,
"enforce_eager": True,
"disable_custom_all_reduce": True,
"disable_log_stats": True,
"download_dir": None,
"tensor_parallel_size": 1,
}
print(f"加载vLLM模型: {model_path}")
llm = LLM(**vllm_config)
# 加载对应的 tokenizer
tokenizer = AutoTokenizer.from_pretrained(
model_path,
trust_remote_code=True,
# local_files_only=True # 已禁用以允许从网络下载
)
if tokenizer.pad_token is None:
if tokenizer.eos_token is not None:
tokenizer.pad_token = tokenizer.eos_token
else:
tokenizer.add_special_tokens({'pad_token': '[PAD]'})
return llm, tokenizer, None
def generate_with_vllm(llm, prompt: str, model_key: str) -> Tuple[str, str]:
"""使用 vLLM 生成代码"""
global tokenizer, EOF, BOF
# vLLM 采样参数 - 保守配置
params = {
"temperature": 1.0,
"top_p": 0.9,
"top_k": 50,
"max_tokens": 512,
"repetition_penalty": 1.1,
"stop": [tokenizer.eos_token] if tokenizer.eos_token else None,
}
sampling_params = SamplingParams(**params)
# 循环生成,直到获取到合法的 Java 代码
max_retries = 5
retry_count = 0
ret = ""
complete_text = None
while not ret and retry_count < max_retries:
retry_count += 1
print(f"尝试生成 Java 代码 (第 {retry_count}/{max_retries} 次)...", flush=True)
# vLLM 生成 - 添加错误处理
try:
outputs = llm.generate([prompt], sampling_params)
output = outputs[0]
full_text = output.outputs[0].text
except RuntimeError as e:
if "CUDA" in str(e) or "unknown error" in str(e):
print(f"vLLM CUDA错误,尝试清理缓存后重试: {e}")
# 清理CUDA缓存
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.synchronize()
# 重试一次
try:
outputs = llm.generate([prompt], sampling_params)
output = outputs[0]
full_text = output.outputs[0].text
except Exception as e2:
print(f"vLLM重试失败: {e2}")
continue
else:
print(f"vLLM生成错误: {e}")
continue
except Exception as e:
print(f"vLLM生成异常: {e}")
continue
# 组合完整文本
complete_text = prompt + full_text
# 从 EOF 标记之后提取代码
ret = None
if EOF and EOF in complete_text:
try:
after_eof = complete_text.split(EOF)[1]
ret = extract_java_code(after_eof)
except (IndexError, AttributeError):
ret = None
else:
ret = None
if ret:
print('code:', ret, flush=True)
else:
ret = "" # 重置为空字符串,继续循环
# 检查最终结果
if not ret or not ret.strip():
print(f"❌ 经过 {max_retries} 次尝试仍未获取到有效的 Java 代码", flush=True)
return "", ""
return complete_text, ret
# 添加 reextract 功能
def reextract_code_from_log(log_file_path, eof_marker=None, model_key=None):
"""
从log文件中提取Java代码块。
逻辑:
- 从EOF之后找到package
- 如果找到package,往前找最近的```java
- 若代码块未闭合,也删除对应的java/log文件
- 若找不到package,也删除对应的java/log文件
"""
def delete_related_files():
"""删除对应的 .java 和 .log 文件"""
java_file = log_file_path[:-4] # 去掉 .log 得到 .java
try:
if os.path.exists(java_file):
os.remove(java_file)
print(f" 已删除: {os.path.basename(java_file)}")
if os.path.exists(log_file_path):
os.remove(log_file_path)
print(f" 已删除: {os.path.basename(log_file_path)}")
except Exception as e:
print(f" 删除文件时出错: {e}")
try:
with open(log_file_path, 'r', encoding='utf-8') as f:
log_content = f.read()
# Step 1: EOF 位置
eof_pos = -1
if eof_marker:
eof_pos = log_content.find(eof_marker)
search_start = eof_pos + len(eof_marker) if eof_pos != -1 and eof_marker else 0
content_after_eof = log_content[search_start:]
# Step 2: 找 package
package_match = re.search(r'package\s+', content_after_eof)
if package_match:
package_pos = search_start + package_match.start()
content_before_package = log_content[:package_pos]
# Step 3: 找最近的 ```java
java_markers = [m.start() for m in re.finditer(r'```java', content_before_package)]
if java_markers:
nearest_java_marker = java_markers[-1]
start_pos = nearest_java_marker + len('```java')
remaining_content = log_content[start_pos:]
# Step 4: 找结束 ```
end_match = re.search(r'```', remaining_content)
if end_match:
extracted_code = remaining_content[:end_match.start()].strip()
print(f"✓ 从 {os.path.basename(log_file_path)} 提取代码 (长度:{len(extracted_code)}字符)")
return extracted_code
else:
print(f"✗ {os.path.basename(log_file_path)} 代码块未闭合 → 删除文件")
delete_related_files()
return None
else:
print(f"✗ {os.path.basename(log_file_path)} 在package前找不到```java → 删除文件")
delete_related_files()
return None
else:
print(f"✗ {os.path.basename(log_file_path)} EOF后找不到package → 删除文件")
delete_related_files()
return None
except Exception as e:
print(f"✗ 读取 {os.path.basename(log_file_path)} 出错: {e}")
return None
def generate_fix(code: str, filename: str, model_key: str) -> Tuple[str, str]:
"""
生成代码修复,自动适配 Fill-Me prompt(与 d4j.py 一致)
"""
global BOF, EOF, llm
if '<FILL_ME>' in code:
prompt = (
f"/no_think\n"
f"{BOF}# This is a buggy code with <FILL_ME> placeholder ({filename}):\n"
f"```java\n{code}\n```\n"
"You are a software engineer. Please replace the <FILL_ME> placeholder with the correct code to fix the bug.\n"
f"{EOF}\n```java\n"
)
else:
prompt = (
f"/no_think\n"
f"{BOF}This is an incorrect code ({filename}):\n"
f"```java\n{code}\n```\n"
"You are a software engineer. Can you repair the incorrect java code?\n"
f"{EOF}\n```java\n"
)
print(prompt, flush=True)
return generate_with_vllm(llm, prompt, model_key)
# 在导入其他模块前先设置GPU环境
if __name__ == '__main__':
# 检查是否为 reextract 模式(支持两种参数顺序)
reextract_mode = False
result_tag = None
if len(sys.argv) >= 3:
if sys.argv[1] == '--reextract':
# 格式1: python inference_java.py --reextract <result_tag>
reextract_mode = True
result_tag = sys.argv[2]
elif sys.argv[2] == '--reextract':
# 格式2: python inference_java.py <result_tag> --reextract
reextract_mode = True
result_tag = sys.argv[1]
if reextract_mode:
# Reextract 模式:只需要结果标签
if not result_tag:
print(f"使用方法: python {sys.argv[0]} --reextract <result_tag>")
print(f" 或者: python {sys.argv[0]} <result_tag> --reextract")
print("示例: python inference_java.py --reextract qwen3-8b")
print(" python inference_java.py qwen3-8b --reextract")
print("将从 EOF 之后找 package,然后往前定位 ```java 代码块")
print("如果找不到 package,将删除对应的 .java 和 .log 文件")
sys.exit(1)
result_base_dir = f'evalrepair-java-res/{result_tag}'
if not os.path.exists(result_base_dir):
print(f"错误: 结果目录不存在: {result_base_dir}")
sys.exit(1)
# 根据 result_tag 获取对应的 EOF 标记
BOF, EOF = get_prompt_format(result_tag)
print("=" * 60)
print(f"📝 重新提取模式: {result_tag}")
print(f"📂 目标目录: {result_base_dir}")
print(f"🎯 提取策略: EOF之后 → package → 往前找最近的```java")
print(f"📌 EOF标记: {EOF if EOF else '(无EOF标记)'}")
print(f"⚠️ 找不到package将删除对应的java和log文件")
print("=" * 60)
# 遍历所有 fixed* 目录
total_processed = 0
total_success = 0
total_deleted = 0
for fixed_dir in sorted(Path(result_base_dir).glob('fixed*')):
print(f"\n📁 处理目录: {fixed_dir.name}")
for log_file in sorted(fixed_dir.glob('*.log')):
total_processed += 1
java_file = str(log_file)[:-4] # 移除 .log 扩展名
# 记录文件是否存在(用于判断是否被删除)
files_exist_before = os.path.exists(str(log_file)) and os.path.exists(java_file)
# 传入 EOF 标记和 model_key
extracted_code = reextract_code_from_log(str(log_file), eof_marker=EOF, model_key=result_tag)
if extracted_code:
with open(java_file, 'w', encoding='utf-8') as f:
f.write(extracted_code)
total_success += 1
else:
# 检查文件是否被删除
if files_exist_before and not os.path.exists(str(log_file)):
total_deleted += 1
print("\n" + "=" * 60)
print(f"重新提取完成!")
print(f"总计处理: {total_processed} 个文件")
print(f"成功提取: {total_success} 个文件")
print(f"删除文件: {total_deleted} 个文件(找不到package)")
print(f"失败: {total_processed - total_success - total_deleted} 个文件")
print("=" * 60)
sys.exit(0)
else:
# 正常模式:设置GPU环境
setup_gpu_environment()
set_file_limits()
# 初始化模型
if len(sys.argv) < 5 or sys.argv[1] not in MODEL_CONFIGS:
print(f"使用方法: python {sys.argv[0]} <model_key> <num_processes> <process_id> <gpu_id> [num_generations]")
print(f"可用模型: {list(MODEL_CONFIGS.keys())}")
print()
print(f"或者: python {sys.argv[0]} --reextract <result_tag>")
print(" 重新提取已有log文件中的代码(不重新生成)")
sys.exit(1)
# 全局变量
llm = None
tokenizer = None
model_key = sys.argv[1]
model_config = MODEL_CONFIGS[model_key]
BOF, EOF = get_prompt_format(model_key)
# 加载 vLLM 模型
llm, tokenizer, _ = load_vllm_model(model_config)
print(f"Tokenizer vocab size: {len(tokenizer)}")
print(f"GPU状态: {monitor_gpu_status()}")
# 配置路径
# 支持 repairllama 参数,自动切换 Fill-Me 数据目录
use_repairllama = len(sys.argv) >= 7 and 'repairllama' in sys.argv[6].lower()
if use_repairllama:
base_dir = 'evalrepair-java/dataset_fillme'
print(f"数据目录: {base_dir}")
print("✓ 使用 EvalRepair-Java Fill-Me 格式数据集")
else:
base_dir = 'evalrepair-java/origin/'
print(f"数据目录: {base_dir}")
print("使用原始格式数据集")
result_base_dir = f'evalrepair-java-res/{model_key}/'
# 参数解析 (注意:现在参数顺序变了,gpu_id是第4个参数)
total_processes = int(sys.argv[2]) if len(sys.argv) >= 3 else 1
process_id = int(sys.argv[3]) if len(sys.argv) >= 4 else 0
gpu_id = sys.argv[4] if len(sys.argv) >= 5 else "0"
num_generations = int(sys.argv[5]) if len(sys.argv) >= 6 else 10
print(f"处理配置: 进程 {process_id + 1}/{total_processes}, 生成 {num_generations} 个版本")
# 处理文件
cnt = 0
for file_path in sorted(Path(base_dir).rglob('*.java'), reverse=True):
cnt += 1
if total_processes > 1 and cnt % total_processes != process_id:
continue
full_path = str(file_path)
print(full_path, flush=True)
with open(full_path, 'r', encoding='utf-8') as file:
content = file.read()
print(content)
file_name = os.path.basename(full_path)
for e in range(num_generations):
fix_subdir = os.path.join(result_base_dir, f'fixed{e}')
fix_name = os.path.join(fix_subdir, file_name)
log_name = fix_name + '.log'
os.makedirs(fix_subdir, exist_ok=True)
print(f"Output path: {fix_name}", flush=True)
# 跳过已存在的文件
if os.path.exists(fix_name) and os.path.exists(log_name):
print('result exists ...')
continue
# 生成修复代码
full_text, code_result = generate_fix(content, file_name, model_key)
if not full_text:
continue
# 保存结果
with open(fix_name, 'w', encoding='utf-8') as f:
f.write(code_result)
with open(log_name, 'w', encoding='utf-8') as f:
f.write(full_text)
print("✅ Java 代码修复完成!")
# 清理资源
cleanup_resources()