datum.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import os
  2. import time
  3. import subprocess
  4. import sys
  5. import argparse
  6. import time
  7. from openai import OpenAI
  8. def fetch_generator(statement, model, maxtokens):
  9. APIKEY = os.environ.get('DEEPSEEK_API_KEY')
  10. if not APIKEY:
  11. raise Exception('需要设置 DEEPSEEK_API_KEY 环境变量')
  12. client = OpenAI(
  13. api_key=APIKEY,
  14. base_url='https://api.deepseek.com')
  15. start_time = time.time()
  16. msgs = [{ 'role': 'user', 'content':
  17. """
  18. 请根据以下信息学竞赛题目生成数据生成器。只输出 gen.py 代码。
  19. 要求:
  20. 1. 解析题目中的输入格式、数据范围和子任务划分
  21. 2. 创建 data/ 目录并生成所有 .in 文件
  22. 3. 数据严格符合题目约束
  23. 4. 使用合理的数据梯度,包含边界情况
  24. 5. 数据文件名一定为 'data{编号}.in',编号不含前导零
  25. 6. 没有子任务的,数据文件直接放在 data/ 下;有子任务的,为每个子任务创建 subtask{编号}/ 目录,把数据文件移动到对应的子任务目录中
  26. 7. 数据点在 10 到 20 个内
  27. 8. 务必确保你给出的数据是正确的!
  28. 输出只包含可以直接运行的 gen.py 的代码,无其他内容。
  29. 题目描述:""" + statement}]
  30. resp = client.chat.completions.create(
  31. model=model,
  32. messages=msgs,
  33. stream=True,
  34. temperature=0.0,
  35. max_tokens=maxtokens)
  36. response = ''
  37. for c in resp:
  38. d = c.choices[0].delta
  39. if hasattr(d, 'reasoning_content') and d.reasoning_content is not None:
  40. delta = d.reasoning_content
  41. elif hasattr(d, 'content') and d.content is not None:
  42. delta = d.content
  43. if response == '':
  44. delta = '\nThinking done\n' + delta
  45. response += d.content
  46. else:
  47. continue
  48. with open('stream', 'a') as f:
  49. f.write(delta)
  50. if hasattr(c, 'usage') and c.usage:
  51. tokens = c.usage
  52. if response.startswith('```python'):
  53. response = response[9:-3]
  54. return (response, tokens, time.time() - start_time)
  55. def execute(s):
  56. print('正在执行命令:', s)
  57. try:
  58. subprocess.run(['sh', '-c', s], check=True, timeout=20)
  59. except subprocess.CalledProcessError as e:
  60. raise Exception('命令执行错误')
  61. except subprocess.TimeoutExpired:
  62. raise Exception('命令执行超时(20s)')
  63. def generate_in():
  64. # 其实应该用个沙盒比较安全,但是我懒得写了。可以最后扔到容器里头跑。
  65. execute('python gen.py')
  66. def flatten_data():
  67. execute('find -type f | xargs -I {} mv -n {} . || true')
  68. execute('rmdir subtask* || true')
  69. def run_std():
  70. if not os.path.exists('../std.cpp'):
  71. raise Exception('未找到 std.cpp')
  72. execute('g++ ../std.cpp -o std -Wall -Wextra -O2 -std=c++14')
  73. for i in os.listdir():
  74. if not i.endswith('.in'):
  75. print(f'警告:未知文件 {i}')
  76. continue
  77. out = i[:-2] + 'ans'
  78. execute(f'./std < {i} > {out}')
  79. execute('rm std')
  80. def gen_problem_conf(time, mem):
  81. execute(f'python ../autoconf.py {time} {mem}')
  82. def compress_data():
  83. execute('zip ../data.zip -r .')
  84. def cleanup():
  85. os.chdir('..')
  86. execute('rm -r data/')
  87. def pretty_size(d):
  88. if d < 1024:
  89. return f'{d}'
  90. if d < 1048576:
  91. return f'{d/1024:.2f} KB'
  92. return f'{d/1048576:.2f} MB'
  93. if __name__ == '__main__':
  94. start_time = time.time()
  95. parser = argparse.ArgumentParser()
  96. parser.add_argument('statement', help='题面文件')
  97. parser.add_argument('--model', type=str, default='deepseek-v4-pro', help='使用的模型(默认为 deepseek-v4-pro,可以改为 deepseek-v4-flash)')
  98. parser.add_argument('--maxtokens', type=int, default=20000, help='token 上限(默认为 20000)')
  99. parser.add_argument('--time', type=int, default=1, help='时间限制(秒,默认为 1)')
  100. parser.add_argument('--mem', type=int, default=256, help='空间限制(MB,默认为 256)')
  101. parser.add_argument('--offline', help='不生成新的 gen.py', action='store_true')
  102. args = parser.parse_args()
  103. if os.path.exists('data'):
  104. print('正在清空已有的 data 目录')
  105. execute('rm -r data')
  106. if os.path.exists('data.zip'):
  107. print('删除已存在的 data.zip')
  108. execute('rm data.zip')
  109. tokens = 0
  110. with open(args.statement) as f:
  111. if not args.offline:
  112. print(f'正在请求 {args.model},token 上限为 {args.maxtokens} 个')
  113. resp, tokens, duration = fetch_generator(f.read(), args.model, args.maxtokens)
  114. with open('gen.py', 'w') as fw:
  115. fw.write(resp)
  116. print(f'--- tokens: {tokens}, time cost: {duration:.6f} seconds')
  117. else:
  118. if not os.path.exists('gen.py'):
  119. raise Exception('离线模式下必须提供已有的 gen.py')
  120. print('(离线模式)')
  121. generate_in()
  122. os.chdir('data')
  123. gen_problem_conf(args.time, args.mem)
  124. # run_std 需要一个扁平的目录,也就是所有文件放在 data/ 下,而 autoconf.py 不要求这么做
  125. flatten_data()
  126. run_std()
  127. compress_data()
  128. cleanup()
  129. prettysize = pretty_size(os.path.getsize('data.zip'))
  130. print(f'已生成 data.zip({prettysize}),耗时 {(time.time() - start_time):.2f} 秒,消耗 {tokens} 个 token')