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

@@ -6,6 +6,7 @@ from autocoder.utils.queue_communicate import queue_communicate, CommunicateEven
6
6
  from autocoder.common import sys_prompt
7
7
  from concurrent.futures import ThreadPoolExecutor
8
8
  import json
9
+ from autocoder.common.utils_code_auto_generate import chat_with_continue
9
10
 
10
11
 
11
12
  class CodeAutoGenerateDiff:
@@ -336,35 +337,48 @@ class CodeAutoGenerateDiff:
336
337
 
337
338
  conversations_list = []
338
339
  results = []
340
+ input_tokens_count = 0
341
+ generated_tokens_count = 0
339
342
  if not self.args.human_as_model:
340
343
  with ThreadPoolExecutor(max_workers=len(self.llms) * self.generate_times_same_model) as executor:
341
344
  futures = []
342
345
  for llm in self.llms:
343
346
  for _ in range(self.generate_times_same_model):
344
347
  futures.append(executor.submit(
345
- llm.chat_oai, conversations=conversations, llm_config=llm_config))
346
- results = [future.result()[0].output for future in futures]
348
+ chat_with_continue, llm=llm, conversations=conversations, llm_config=llm_config))
349
+ temp_results = [future.result() for future in futures]
350
+ for result in temp_results:
351
+ results.append(result.content)
352
+ input_tokens_count += result.input_tokens_count
353
+ generated_tokens_count += result.generated_tokens_count
354
+
347
355
  for result in results:
348
356
  conversations_list.append(
349
357
  conversations + [{"role": "assistant", "content": result}])
350
358
  else:
351
359
  for _ in range(self.args.human_model_num):
352
- v = self.llms[0].chat_oai(
353
- conversations=conversations, llm_config=llm_config)
354
- results.append(v[0].output)
360
+ single_result = chat_with_continue(llm=self.llms[0], conversations=conversations, llm_config=llm_config)
361
+ results.append(single_result.content)
362
+ input_tokens_count += single_result.input_tokens_count
363
+ generated_tokens_count += single_result.generated_tokens_count
355
364
  conversations_list.append(
356
- conversations + [{"role": "assistant", "content": v[0].output}])
357
-
365
+ conversations + [{"role": "assistant", "content": single_result.content}])
366
+
367
+ statistics = {
368
+ "input_tokens_count": input_tokens_count,
369
+ "generated_tokens_count": generated_tokens_count
370
+ }
371
+
358
372
  if self.args.request_id and not self.args.skip_events:
359
373
  _ = queue_communicate.send_event(
360
374
  request_id=self.args.request_id,
361
375
  event=CommunicateEvent(
362
376
  event_type=CommunicateEventType.CODE_GENERATE_END.value,
363
- data=json.dumps({}, ensure_ascii=False),
377
+ data=json.dumps(statistics, ensure_ascii=False),
364
378
  ),
365
379
  )
366
380
 
367
- return CodeGenerateResult(contents=results, conversations=conversations_list)
381
+ return CodeGenerateResult(contents=results, conversations=conversations_list, metadata=statistics)
368
382
 
369
383
  def multi_round_run(
370
384
  self, query: str, source_content: str, max_steps: int = 10
@@ -10,6 +10,7 @@ from autocoder.utils.queue_communicate import (
10
10
  )
11
11
  import json
12
12
  from concurrent.futures import ThreadPoolExecutor
13
+ from autocoder.common.utils_code_auto_generate import chat_with_continue
13
14
 
14
15
 
15
16
  class CodeAutoGenerateEditBlock:
@@ -418,34 +419,47 @@ class CodeAutoGenerateEditBlock:
418
419
 
419
420
  conversations_list = []
420
421
  results = []
422
+ input_tokens_count = 0
423
+ generated_tokens_count = 0
421
424
  if not self.args.human_as_model:
422
425
  with ThreadPoolExecutor(max_workers=len(self.llms) * self.generate_times_same_model) as executor:
423
426
  futures = []
424
427
  for llm in self.llms:
425
428
  for _ in range(self.generate_times_same_model):
426
429
  futures.append(executor.submit(
427
- llm.chat_oai, conversations=conversations, llm_config=llm_config))
428
- results = [future.result()[0].output for future in futures]
430
+ chat_with_continue,llm=llm, conversations=conversations, llm_config=llm_config))
431
+ temp_results = [future.result() for future in futures]
432
+ for result in temp_results:
433
+ results.append(result.content)
434
+ input_tokens_count += result.input_tokens_count
435
+ generated_tokens_count += result.generated_tokens_count
436
+
429
437
  for result in results:
430
438
  conversations_list.append(
431
439
  conversations + [{"role": "assistant", "content": result}])
432
440
  else:
433
441
  for _ in range(self.args.human_model_num):
434
- v = self.llms[0].chat_oai(
435
- conversations=conversations, llm_config=llm_config)
436
- results.append(v[0].output)
437
- conversations_list.append(conversations + [{"role": "assistant", "content": v[0].output}])
438
-
442
+ single_result = chat_with_continue(llm=self.llms[0], conversations=conversations, llm_config=llm_config)
443
+ results.append(single_result.content)
444
+ input_tokens_count += single_result.input_tokens_count
445
+ generated_tokens_count += single_result.generated_tokens_count
446
+ conversations_list.append(conversations + [{"role": "assistant", "content": single_result.content}])
447
+
448
+ statistics = {
449
+ "input_tokens_count": input_tokens_count,
450
+ "generated_tokens_count": generated_tokens_count
451
+ }
452
+
439
453
  if self.args.request_id and not self.args.skip_events:
440
454
  _ = queue_communicate.send_event(
441
455
  request_id=self.args.request_id,
442
456
  event=CommunicateEvent(
443
457
  event_type=CommunicateEventType.CODE_GENERATE_END.value,
444
- data=json.dumps({}, ensure_ascii=False),
458
+ data=json.dumps(statistics, ensure_ascii=False),
445
459
  ),
446
460
  )
447
461
 
448
- return CodeGenerateResult(contents=results, conversations=conversations_list)
462
+ return CodeGenerateResult(contents=results, conversations=conversations_list, metadata=statistics)
449
463
 
450
464
  def multi_round_run(
451
465
  self, query: str, source_content: str, max_steps: int = 10
@@ -6,6 +6,7 @@ from autocoder.utils.queue_communicate import queue_communicate, CommunicateEven
6
6
  from autocoder.common import sys_prompt
7
7
  from concurrent.futures import ThreadPoolExecutor
8
8
  import json
9
+ from autocoder.common.utils_code_auto_generate import chat_with_continue
9
10
 
10
11
  class CodeAutoGenerateStrictDiff:
11
12
  def __init__(
@@ -306,34 +307,47 @@ class CodeAutoGenerateStrictDiff:
306
307
 
307
308
  conversations_list = []
308
309
  results = []
310
+ input_tokens_count = 0
311
+ generated_tokens_count = 0
309
312
  if not self.args.human_as_model:
310
313
  with ThreadPoolExecutor(max_workers=len(self.llms) * self.generate_times_same_model) as executor:
311
314
  futures = []
312
315
  for llm in self.llms:
313
316
  for _ in range(self.generate_times_same_model):
314
317
  futures.append(executor.submit(
315
- llm.chat_oai, conversations=conversations, llm_config=llm_config))
316
- results = [future.result()[0].output for future in futures]
318
+ chat_with_continue, llm=llm, conversations=conversations, llm_config=llm_config))
319
+ temp_results = [future.result() for future in futures]
320
+ for result in temp_results:
321
+ results.append(result.content)
322
+ input_tokens_count += result.input_tokens_count
323
+ generated_tokens_count += result.generated_tokens_count
324
+
317
325
  for result in results:
318
326
  conversations_list.append(
319
327
  conversations + [{"role": "assistant", "content": result}])
320
328
  else:
321
329
  for _ in range(self.args.human_model_num):
322
- v = self.llms[0].chat_oai(
323
- conversations=conversations, llm_config=llm_config)
324
- results.append(v[0].output)
325
- conversations_list.append(conversations + [{"role": "assistant", "content": v[0].output}])
326
-
330
+ single_result = chat_with_continue(llm=self.llms[0], conversations=conversations, llm_config=llm_config)
331
+ results.append(single_result.content)
332
+ input_tokens_count += single_result.input_tokens_count
333
+ generated_tokens_count += single_result.generated_tokens_count
334
+ conversations_list.append(conversations + [{"role": "assistant", "content": single_result.content}])
335
+
336
+ statistics = {
337
+ "input_tokens_count": input_tokens_count,
338
+ "generated_tokens_count": generated_tokens_count
339
+ }
340
+
327
341
  if self.args.request_id and not self.args.skip_events:
328
342
  _ = queue_communicate.send_event(
329
343
  request_id=self.args.request_id,
330
344
  event=CommunicateEvent(
331
345
  event_type=CommunicateEventType.CODE_GENERATE_END.value,
332
- data=json.dumps({}, ensure_ascii=False),
346
+ data=json.dumps(statistics, ensure_ascii=False),
333
347
  ),
334
348
  )
335
349
 
336
- return CodeGenerateResult(contents=results, conversations=conversations_list)
350
+ return CodeGenerateResult(contents=results, conversations=conversations_list, metadata=statistics)
337
351
 
338
352
  def multi_round_run(
339
353
  self, query: str, source_content: str, max_steps: int = 10
@@ -29,6 +29,12 @@ COMMANDS = {
29
29
  "/refresh": "",
30
30
  "/get": "",
31
31
  },
32
+ "/models": {
33
+ "/add": "",
34
+ "/add_model": "",
35
+ "/remove": "",
36
+ "/list": ""
37
+ }
32
38
  }
33
39
 
34
40
 
autocoder/common/types.py CHANGED
@@ -12,6 +12,7 @@ class StepNum(pydantic.BaseModel):
12
12
  class CodeGenerateResult(pydantic.BaseModel):
13
13
  contents:List[str]
14
14
  conversations:List[List[Dict[str, Any]]]
15
+ metadata:Dict[str, Any] = {}
15
16
 
16
17
  class MergeCodeWithoutEffect(pydantic.BaseModel):
17
18
  success_blocks: List[Tuple[str, str]]
@@ -0,0 +1,38 @@
1
+ from byzerllm import ByzerLLM
2
+ from typing import List,Any,Union
3
+ from pydantic import BaseModel
4
+ from loguru import logger
5
+ class ChatWithContinueResult(BaseModel):
6
+ content: str
7
+ input_tokens_count: int
8
+ generated_tokens_count: int
9
+
10
+
11
+ def chat_with_continue(llm: ByzerLLM, conversations: List[dict], llm_config: dict) -> ChatWithContinueResult:
12
+ final_result = ChatWithContinueResult(content="", input_tokens_count=0, generated_tokens_count=0)
13
+ v = llm.chat_oai(
14
+ conversations=conversations, llm_config=llm_config)
15
+
16
+ single_result = v[0].output
17
+ metadata = v[0].metadata
18
+
19
+ final_result.input_tokens_count += metadata.get("input_tokens_count", 0)
20
+ final_result.generated_tokens_count += metadata.get("generated_tokens_count", 0)
21
+
22
+ temp_conversations = conversations + \
23
+ [{"role": "assistant", "content": single_result}]
24
+
25
+ count = 1
26
+ while (metadata.get("finish_reason", "stop") == "length" and count < 6):
27
+ v = llm.chat_oai(
28
+ conversations=temp_conversations, llm_config={**llm_config, "gen.response_prefix": True})
29
+ metadata = v[0].metadata
30
+ single_result += v[0].output
31
+ final_result.input_tokens_count += metadata.get("input_tokens_count", 0)
32
+ final_result.generated_tokens_count += metadata.get("generated_tokens_count", 0)
33
+ count += 1
34
+
35
+ # if count >= 2:
36
+ # logger.info(f"The code generation is exceed the max length, continue to generate the code {count -1 } times")
37
+ final_result.content = single_result
38
+ return final_result
@@ -116,7 +116,7 @@ class ActionTSProject(BaseAction):
116
116
  generate_result = generate.single_round_run(
117
117
  query=args.query, source_content=content
118
118
  )
119
- logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds")
119
+ logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds, input_tokens_count: {generate_result.metadata.get('input_tokens_count', 0)}, generated_tokens_count: {generate_result.metadata.get('generated_tokens_count', 0)}")
120
120
  merge_result = None
121
121
  if args.execute and args.auto_merge:
122
122
  logger.info("Auto merge the code...")
@@ -200,7 +200,7 @@ class ActionPyScriptProject(BaseAction):
200
200
  query=args.query, source_content=content
201
201
  )
202
202
 
203
- logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds")
203
+ logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds, input_tokens_count: {generate_result.metadata.get('input_tokens_count', 0)}, generated_tokens_count: {generate_result.metadata.get('generated_tokens_count', 0)}")
204
204
  merge_result = None
205
205
  if args.execute and args.auto_merge:
206
206
  logger.info("Auto merge the code...")
@@ -302,7 +302,7 @@ class ActionPyProject(BaseAction):
302
302
  generate_result = generate.single_round_run(
303
303
  query=args.query, source_content=content
304
304
  )
305
- logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds")
305
+ logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds, input_tokens_count: {generate_result.metadata.get('input_tokens_count', 0)}, generated_tokens_count: {generate_result.metadata.get('generated_tokens_count', 0)}")
306
306
  merge_result = None
307
307
  if args.execute and args.auto_merge:
308
308
  logger.info("Auto merge the code...")
@@ -396,7 +396,7 @@ class ActionSuffixProject(BaseAction):
396
396
  query=args.query, source_content=content
397
397
  )
398
398
 
399
- logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds")
399
+ logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds, input_tokens_count: {generate_result.metadata.get('input_tokens_count', 0)}, generated_tokens_count: {generate_result.metadata.get('generated_tokens_count', 0)}")
400
400
  merge_result = None
401
401
  if args.execute and args.auto_merge:
402
402
  logger.info("Auto merge the code...")
@@ -13,7 +13,7 @@ from autocoder.index.index import build_index_and_filter_files
13
13
  from autocoder.regexproject import RegexProject
14
14
  from autocoder.utils.conversation_store import store_code_model_conversation
15
15
  from loguru import logger
16
-
16
+ import time
17
17
 
18
18
  class ActionRegexProject:
19
19
  def __init__(
@@ -51,8 +51,10 @@ class ActionRegexProject:
51
51
  )
52
52
  content = content[: self.args.model_max_input_length]
53
53
 
54
+ start_time = time.time()
54
55
  if args.execute:
55
56
  logger.info("Auto generate the code...")
57
+
56
58
  if args.auto_merge == "diff":
57
59
  generate = CodeAutoGenerateDiff(
58
60
  llm=self.llm, args=self.args, action=self
@@ -75,6 +77,8 @@ class ActionRegexProject:
75
77
  generate_result = generate.single_round_run(
76
78
  query=args.query, source_content=content
77
79
  )
80
+
81
+ logger.info(f"Code generation completed in {time.time() - start_time:.2f} seconds, input_tokens_count: {generate_result.metadata.get('input_tokens_count', 0)}, generated_tokens_count: {generate_result.metadata.get('generated_tokens_count', 0)}")
78
82
  merge_result = None
79
83
  if args.execute and args.auto_merge:
80
84
  logger.info("Auto merge the code...")
@@ -90,7 +94,7 @@ class ActionRegexProject:
90
94
  else:
91
95
  code_merge = CodeAutoMerge(llm=self.llm, args=self.args)
92
96
  merge_result = code_merge.merge_code(generate_result=generate_result)
93
-
97
+
94
98
  if merge_result is not None:
95
99
  content = merge_result.contents[0]
96
100
  store_code_model_conversation(
autocoder/models.py ADDED
@@ -0,0 +1,158 @@
1
+ import os
2
+ import json
3
+ from typing import List, Dict
4
+ from urllib.parse import urlparse
5
+
6
+ MODELS_JSON = os.path.expanduser("~/.auto-coder/keys/models.json")
7
+
8
+ # Default built-in models
9
+ default_models_list = [
10
+ {
11
+ "name": "deepseek_r1_chat",
12
+ "description": "DeepSeek Reasoner is for design/review",
13
+ "model_name": "deepseek-reasoner",
14
+ "model_type": "saas/openai",
15
+ "base_url": "https://api.deepseek.com/v1",
16
+ "api_key_path": "api.deepseek.com"
17
+ },
18
+ {
19
+ "name": "deepsee_chat",
20
+ "description": "DeepSeek Chat is for coding",
21
+ "model_name": "deepseek-chat",
22
+ "model_type": "saas/openai",
23
+ "base_url": "https://api.deepseek.com/v1",
24
+ "api_key_path": "api.deepseek.com"
25
+ },
26
+ {
27
+ "name":"o1",
28
+ "description": "o1 is for design/review",
29
+ "model_name": "o1-2024-12-17",
30
+ "model_type": "saas/openai",
31
+ "base_url": "https://api.openai.com/v1",
32
+ "api_key_path": ""
33
+ }
34
+ ]
35
+
36
+ def load_models() -> List[Dict]:
37
+ """
38
+ Load models from ~/.auto-coder/keys/models.json and merge with default_models_list.
39
+ Models are merged and deduplicated based on their name field.
40
+ If file doesn't exist or is invalid, use default_models_list.
41
+ """
42
+ os.makedirs(os.path.dirname(MODELS_JSON), exist_ok=True)
43
+
44
+ # Start with default models
45
+ models_dict = {model["name"]: model for model in default_models_list}
46
+
47
+ # If JSON file exists, read and merge with defaults
48
+ if os.path.exists(MODELS_JSON):
49
+ try:
50
+ with open(MODELS_JSON, 'r', encoding='utf-8') as f:
51
+ custom_models = json.load(f)
52
+ # Custom models will override defaults with same name
53
+ for model in custom_models:
54
+ models_dict[model["name"]] = model
55
+ except json.JSONDecodeError:
56
+ # If JSON is invalid, just use defaults
57
+ save_models(default_models_list)
58
+ else:
59
+ # If file doesn't exist, create it with defaults
60
+ save_models(default_models_list)
61
+
62
+ # Convert merged dictionary back to list
63
+ target_models = list(models_dict.values())
64
+ api_key_dir = os.path.expanduser("~/.auto-coder/keys")
65
+ for model in target_models:
66
+ if model.get("api_key_path",""):
67
+ api_key_file = os.path.join(api_key_dir, model["api_key_path"])
68
+ if os.path.exists(api_key_file):
69
+ with open(api_key_file, "r") as f:
70
+ model["api_key"] = f.read()
71
+ return target_models
72
+
73
+ def save_models(models: List[Dict]) -> None:
74
+ """
75
+ Save models to ~/.auto-coder/keys/models.json
76
+ """
77
+ os.makedirs(os.path.dirname(MODELS_JSON), exist_ok=True)
78
+ with open(MODELS_JSON, 'w', encoding='utf-8') as f:
79
+ json.dump(models, f, indent=2, ensure_ascii=False)
80
+
81
+
82
+ def process_api_key_path(base_url: str) -> str:
83
+ """
84
+ 从 base_url 中提取 host 部分并处理特殊字符
85
+ 例如: https://api.example.com:8080/v1 -> api.example.com_8080
86
+ """
87
+ if not base_url:
88
+ return ""
89
+
90
+ parsed = urlparse(base_url)
91
+ host = parsed.netloc
92
+
93
+ # 将冒号替换为下划线
94
+ host = host.replace(":", "_")
95
+
96
+ return host
97
+
98
+ def get_model_by_name(name: str) -> Dict:
99
+ """
100
+ 根据模型名称查找模型
101
+ """
102
+ models = load_models()
103
+ v = [m for m in models if m["name"] == name.strip()]
104
+
105
+ if len(v) == 0:
106
+ raise Exception(f"Model {name} not found")
107
+ return v[0]
108
+
109
+ def update_model_with_api_key(name: str, api_key: str) -> Dict:
110
+ """
111
+ 根据模型名称查找并更新模型的 api_key_path。
112
+ 如果找到模型,会根据其 base_url 处理 api_key_path。
113
+
114
+ Args:
115
+ name: 模型名称
116
+ api_key: API密钥
117
+
118
+ Returns:
119
+ Dict: 更新后的模型信息,如果未找到则返回None
120
+ """
121
+ models = load_models()
122
+
123
+ # 在现有模型中查找
124
+ found_model = None
125
+ for model in models:
126
+ if model["name"] == name:
127
+ found_model = model
128
+ break
129
+
130
+ if not found_model:
131
+ return None
132
+
133
+ # 从 base_url 中提取并处理 host
134
+ api_key_path = process_api_key_path(found_model["base_url"])
135
+ if api_key_path:
136
+ found_model["api_key_path"] = api_key_path
137
+
138
+ # 保存 API 密钥
139
+ api_key_dir = os.path.expanduser("~/.auto-coder/keys")
140
+ os.makedirs(api_key_dir, exist_ok=True)
141
+ api_key_file = os.path.join(api_key_dir, api_key_path)
142
+ with open(api_key_file, "w") as f:
143
+ f.write(api_key)
144
+
145
+ # 如果是新模型,添加到模型列表中
146
+ if all(model["name"] != name for model in models):
147
+ models.append(found_model)
148
+ else:
149
+ # 更新现有模型
150
+ for i, model in enumerate(models):
151
+ if model["name"] == name:
152
+ models[i] = found_model
153
+ break
154
+
155
+ save_models(models)
156
+
157
+ return found_model
158
+
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.226"
1
+ __version__ = "0.1.227"