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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.253
3
+ Version: 0.1.255
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
@@ -1,17 +1,17 @@
1
1
  autocoder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- autocoder/auto_coder.py,sha256=KTc-twhc4MNabzgNWj8nDZZTD-uMneDABnFSWnt6Ta8,64640
2
+ autocoder/auto_coder.py,sha256=uzNAtguu7O2m7vve1H2FQnWCmVuqT07RITxS5jgWz4U,64774
3
3
  autocoder/auto_coder_lang.py,sha256=Rtupq6N3_HT7JRhDKdgCBcwRaiAnyCOR_Gsp4jUomrI,3229
4
4
  autocoder/auto_coder_rag.py,sha256=DDAmqw36CO6phtdQuN8LYIbIR3YGdoZw5-pG0LjVxMc,29063
5
5
  autocoder/auto_coder_rag_client_mcp.py,sha256=WV7j5JUiQge0x4-B7Hp5-pSAFXLbvLpzQMcCovbauIM,6276
6
6
  autocoder/auto_coder_rag_mcp.py,sha256=-RrjNwFaS2e5v8XDIrKR-zlUNUE8UBaeOtojffBrvJo,8521
7
7
  autocoder/auto_coder_server.py,sha256=XU9b4SBH7zjPPXaTWWHV4_zJm-XYa6njuLQaplYJH_c,20290
8
8
  autocoder/benchmark.py,sha256=Ypomkdzd1T3GE6dRICY3Hj547dZ6_inqJbBJIp5QMco,4423
9
- autocoder/chat_auto_coder.py,sha256=1QgTy6SiZjN2mkYe5pVvPTdZyedwijDctTUS8tSukVM,107527
10
- autocoder/chat_auto_coder_lang.py,sha256=OiwjnOqbiWwbaVuOhehEqdUFPOJbhfvQr3sIq3b0xj4,17455
9
+ autocoder/chat_auto_coder.py,sha256=a1YEp6OPMzpLbRpr2hrbzF6pRnhVPTVxyZfBiQHFPIw,109283
10
+ autocoder/chat_auto_coder_lang.py,sha256=1cJrjFGrcOQnuP2LdZpgGDSX4CNaIYI7KZGvEEtj6_Q,18242
11
11
  autocoder/command_args.py,sha256=9aYJ-AmPxP1sQh6ciw04FWHjSn31f2W9afXFwo8wgx4,30441
12
12
  autocoder/lang.py,sha256=U6AjVV8Rs1uLyjFCZ8sT6WWuNUxMBqkXXIOs4S120uk,14511
13
- autocoder/models.py,sha256=uo4mFWb-kdtd-8e2HPfCuXjhkDsE37n1dY6Nt1SU6kk,7625
14
- autocoder/version.py,sha256=r3svv_IwmWAh4ENi7e33jEo4y7TePaK1ugMkXwnow5w,23
13
+ autocoder/models.py,sha256=0f653gjpQN_JO5k7h6wmTF4bVd6CW3fpQOyHIZ3ZUv4,7558
14
+ autocoder/version.py,sha256=vxd_TSbCxiXnBHQnfprB361BOJEhjQMPeDRseFI3YrY,23
15
15
  autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  autocoder/agent/auto_demand_organizer.py,sha256=NWSAEsEk94vT3lGjfo25kKLMwYdPcpy9e-i21txPasQ,6942
17
17
  autocoder/agent/auto_filegroup.py,sha256=CW7bqp0FW1GIEMnl-blyAc2UGT7O9Mom0q66ITz1ckM,6635
@@ -29,7 +29,7 @@ autocoder/common/__init__.py,sha256=6maackdzrYnUPvpgVPl92JdMOnw7X4n3EnEQA9OnLGE,
29
29
  autocoder/common/anything2images.py,sha256=0ILBbWzY02M-CiWB-vzuomb_J1hVdxRcenAfIrAXq9M,25283
30
30
  autocoder/common/anything2img.py,sha256=4TREa-sOA-iargieUy7MpyCYVUE-9Mmq0wJtwomPqnE,7662
31
31
  autocoder/common/audio.py,sha256=Kn9nWKQddWnUrAz0a_ZUgjcu4VUU_IcZBigT7n3N3qc,7439
32
- autocoder/common/auto_coder_lang.py,sha256=9qaluOOt0X6soURfQs27_jw2tIA95yoLdAcACt-Zsf4,16359
32
+ autocoder/common/auto_coder_lang.py,sha256=nF8XrHpSbibk6ro8Oum-0V0FXcDS1lHP_hL-CnPJtT4,16974
33
33
  autocoder/common/buildin_tokenizer.py,sha256=L7d5t39ZFvUd6EoMPXUhYK1toD0FHlRH1jtjKRGokWU,1236
34
34
  autocoder/common/chunk_validation.py,sha256=BrR_ZWavW8IANuueEE7hS8NFAwEvm8TX34WnPx_1hs8,3030
35
35
  autocoder/common/cleaner.py,sha256=NU72i8C6o9m0vXExab7nao5bstBUsfJFcj11cXa9l4U,1089
@@ -43,7 +43,7 @@ autocoder/common/code_auto_merge_diff.py,sha256=qpEuHJEgX6sWK7EDFEKqcYkyI28wOyM4
43
43
  autocoder/common/code_auto_merge_editblock.py,sha256=sxgYMLMACRwJvw-bABkdDHezPelsDFrOCpGuhtT5Dzs,17504
44
44
  autocoder/common/code_auto_merge_strict_diff.py,sha256=P0nKNkBrFMybTSZ7kOdA_JixoVmLCZIhAP5q7ILJ9j0,9538
45
45
  autocoder/common/code_modification_ranker.py,sha256=qfadP9P-iiidCG2A_MjAf3Ca8cMz7YlnN08D_kH6uFc,6447
46
- autocoder/common/command_completer.py,sha256=SSeb8MDH0JPvfdyW-S2uaHnui4VBDfSQvQPLbv3ORPA,9314
46
+ autocoder/common/command_completer.py,sha256=IShrZJSpR-Q_MCj_aCVdVyscLYDKj5ZQK357QBcQ_oQ,9420
47
47
  autocoder/common/command_generator.py,sha256=-hmbD_AnCa5HxL4BznuEfYAf_l8AxU5fAG5F0sM_fuE,2116
48
48
  autocoder/common/command_templates.py,sha256=mnB3n8i0yjH1mqzyClEg8Wpr9VbZV44kxky66Zu6OJY,8557
49
49
  autocoder/common/const.py,sha256=eTjhjh4Aj4CUzviJ81jaf3Y5cwqsLATySn2wJxaS6RQ,2911
@@ -57,6 +57,7 @@ autocoder/common/mcp_hub.py,sha256=2ZyJv3Aiv4Y97UHut49oYhIFcu7ICR-mptDEBSgT3uE,1
57
57
  autocoder/common/mcp_server.py,sha256=1G6e0IbeS_h7CA1vr0dPAnf0o2H1f1A8I4bua8EUtKw,12318
58
58
  autocoder/common/mcp_tools.py,sha256=KsLvRrB6pvmebqd-lDaSH6IBJR0AIxWRE-dtCEG_w9k,12485
59
59
  autocoder/common/memory_manager.py,sha256=2ZjYG7BPyvbYalZBF6AM_G5e10Qkw_zrqtD4Zd7GSsQ,3663
60
+ autocoder/common/model_speed_test.py,sha256=U48xUUpOnbwUal1cdij4YAn_H2PD2pNaqrMHaYtQRfI,15200
60
61
  autocoder/common/printer.py,sha256=P1WU0QjlfnjqTP5uA55GkHZCpFzRPFkc34DMMandreg,2023
61
62
  autocoder/common/recall_validation.py,sha256=Avt9Q9dX3kG6Pf2zsdlOHmsjd-OeSj7U1PFBDp_Cve0,1700
62
63
  autocoder/common/screenshots.py,sha256=_gA-z1HxGjPShBrtgkdideq58MG6rqFB2qMUJKjrycs,3769
@@ -69,6 +70,7 @@ autocoder/common/types.py,sha256=PXTETrsTvhLE49jqAeUKGySvxBN9pjeyCgRHLDYdd9U,664
69
70
  autocoder/common/utils_code_auto_generate.py,sha256=kDW5B_2wRLk7hAls2hewliDacV86lrPz8Jan01BvtCw,3573
70
71
  autocoder/common/mcp_servers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
72
  autocoder/common/mcp_servers/mcp_server_perplexity.py,sha256=IXTyMpd1CQcBLzVinA-_OIOHoNmbzvuW6pXIadaKHJE,5533
73
+ autocoder/data/byzerllm.md,sha256=SGCMpEaUQ0ysPxQsgzyyp5sgvEr8dZsxEGAfVcPBIq0,47741
72
74
  autocoder/data/tokenizer.json,sha256=7Lb5_DaYlDRvBRH0B0ynXO5c1fOwbQLxujX805-OEh0,7847602
73
75
  autocoder/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
76
  autocoder/db/store.py,sha256=tFT66bP2ZKIqZip-uhLkHRSLaaOAUUDZfozJwcqix3c,1908
@@ -80,21 +82,21 @@ autocoder/dispacher/actions/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
80
82
  autocoder/dispacher/actions/plugins/action_regex_project.py,sha256=ckTbisMlvwMNHQbrt5WB7pBvf2XAhYQYGH8uyYrvGXU,6060
81
83
  autocoder/dispacher/actions/plugins/action_translate.py,sha256=nVAtRSQpdGNmZxg1R_9zXG3AuTv3CHf2v7ODgj8u65c,7727
82
84
  autocoder/index/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
- autocoder/index/entry.py,sha256=hXSOi-jPgRBKQB55eqWkT95vxvWrbVHDuIMtDkqjNNw,12232
85
+ autocoder/index/entry.py,sha256=1KIGPCtxQN0OdErAco9OmGTd5hB8WJTpWGrxsGLsTcE,12634
84
86
  autocoder/index/for_command.py,sha256=BFvljE4t6VaMBGboZAuhUCzVK0EitCy_n5D_7FEnihw,3204
85
87
  autocoder/index/index.py,sha256=hVAIyF10N9hxKMWHA_ibYygGRZYJQZfZxRRrhQhrTvk,21225
86
88
  autocoder/index/symbols_utils.py,sha256=CjcjUVajmJZB75Ty3a7kMv1BZphrm-tIBAdOJv6uo-0,2037
87
89
  autocoder/index/types.py,sha256=a2s_KV5FJlq7jqA2ELSo9E1sjuLwDB-JJYMhSpzBAhU,596
88
90
  autocoder/index/filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
91
  autocoder/index/filter/normal_filter.py,sha256=APu34iSvWhtlLtWgkj8N3Vo4oW1TegtZQq2bwDX_cs4,8031
90
- autocoder/index/filter/quick_filter.py,sha256=8R54qtcAAAu0C6TQgJ4WkqkryGOke1mJXrv82vnvMAA,9342
92
+ autocoder/index/filter/quick_filter.py,sha256=Dsm23Z_RrJ_UwCypGUPN1BlKUMibae_9_D8jWD1UDFw,10518
91
93
  autocoder/pyproject/__init__.py,sha256=dQ2_7YZ7guybT9BhfxSGn43eLQJGQN2zgeKa6--JlaQ,14403
92
94
  autocoder/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
95
  autocoder/rag/api_server.py,sha256=dRbhAZVRAOlZ64Cnxf4_rKb4iJwHnrWS9Zr67IVORw0,7288
94
- autocoder/rag/doc_filter.py,sha256=bGx3OEmSwmqAFOT12nGDE8saMpukSsHFMVRVbHBgXik,6516
96
+ autocoder/rag/doc_filter.py,sha256=ZCixxUXNBbz6UiGbgXvbDWdn5moLac3HnZEphpasTDc,6579
95
97
  autocoder/rag/document_retriever.py,sha256=5oThtxukGuRFF96o3pHKsk306a8diXbhgSrbqyU2BvM,8894
96
98
  autocoder/rag/llm_wrapper.py,sha256=sbDxCANiZyWb_ocqNgqu2oy3c2t8orPNRGleEs-Uwl8,2649
97
- autocoder/rag/long_context_rag.py,sha256=aBp0fJ9JrlGb1KsJkQw0CSy7mX4kP52GPVKLX6sIXB4,25366
99
+ autocoder/rag/long_context_rag.py,sha256=wmNmGsXN8RAFl6e9HaVzlwISXV9D-3bvf2qiaWjRy7w,24646
98
100
  autocoder/rag/rag_config.py,sha256=8LwFcTd8OJWWwi1_WY4IzjqgtT6RyE2j4PjxS5cCTDE,802
99
101
  autocoder/rag/rag_entry.py,sha256=6TKtErZ0Us9XSV6HgRKXA6yR3SiZGPHpynOKSaR1wgE,2463
100
102
  autocoder/rag/raw_rag.py,sha256=yS2Ur6kG0IRjhCj2_VonwxjY_xls_E62jO5Gz5j2nqE,2952
@@ -144,9 +146,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
146
  autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
147
  autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=xWXqICANbDOovH4wcFW1eSI7lB7TjXbk1mSU4bTKEW4,11434
146
148
  autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
- auto_coder-0.1.253.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
148
- auto_coder-0.1.253.dist-info/METADATA,sha256=NipnGMN1xPzRQLC0rvEbcWax5fPTf5T2pixSG_qVyYA,2616
149
- auto_coder-0.1.253.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
150
- auto_coder-0.1.253.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
151
- auto_coder-0.1.253.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
152
- auto_coder-0.1.253.dist-info/RECORD,,
149
+ auto_coder-0.1.255.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
150
+ auto_coder-0.1.255.dist-info/METADATA,sha256=2uB08jgGHyp3_DWMI2_vxoFoptVJO76Va-yek1umBac,2616
151
+ auto_coder-0.1.255.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
152
+ auto_coder-0.1.255.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
153
+ auto_coder-0.1.255.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
154
+ auto_coder-0.1.255.dist-info/RECORD,,
autocoder/auto_coder.py CHANGED
@@ -256,11 +256,13 @@ def main(input_args: Optional[List[str]] = None):
256
256
  libs_dir = os.path.join(auto_coder_dir, "storage", "libs")
257
257
  code_search_path = None
258
258
  if os.path.exists(libs_dir):
259
- retrieval_libs_dir = os.path.join(
260
- libs_dir, get_latest_byzer_retrieval_lib(libs_dir)
261
- )
262
- if os.path.exists(retrieval_libs_dir):
263
- code_search_path = [retrieval_libs_dir]
259
+ latest_retrieval_lib_dir = get_latest_byzer_retrieval_lib(libs_dir)
260
+ if latest_retrieval_lib_dir :
261
+ retrieval_libs_dir = os.path.join(
262
+ libs_dir, latest_retrieval_lib_dir
263
+ )
264
+ if os.path.exists(retrieval_libs_dir):
265
+ code_search_path = [retrieval_libs_dir]
264
266
 
265
267
  try:
266
268
  init_options = {}
@@ -2197,24 +2197,49 @@ def manage_models(params, query: str):
2197
2197
  subcmd = "/remove"
2198
2198
  query = query.replace("/remove", "", 1).strip()
2199
2199
 
2200
+ if "/speed-test" in query:
2201
+ subcmd = "/speed-test"
2202
+ query = query.replace("/speed-test", "", 1).strip()
2203
+
2204
+ if "/speed_test" in query:
2205
+ subcmd = "/speed-test"
2206
+ query = query.replace("/speed_test", "", 1).strip()
2207
+
2208
+ if "input_price" in query:
2209
+ subcmd = "/input_price"
2210
+ query = query.replace("/input_price", "", 1).strip()
2211
+
2212
+ if "output_price" in query:
2213
+ subcmd = "/output_price"
2214
+ query = query.replace("/output_price", "", 1).strip()
2215
+
2216
+ if "/speed" in query:
2217
+ subcmd = "/speed"
2218
+ query = query.replace("/speed", "", 1).strip()
2219
+
2220
+
2221
+
2200
2222
  if not subcmd:
2201
- printer.print_in_terminal("models_usage")
2202
- return
2223
+ printer.print_in_terminal("models_usage")
2203
2224
 
2204
2225
  if subcmd == "/list":
2205
2226
  if models_data:
2227
+ # Sort models by speed (average_speed)
2228
+ sorted_models = sorted(models_data, key=lambda x: float(x.get('average_speed', 0)))
2229
+ sorted_models.reverse()
2230
+
2206
2231
  table = Table(
2207
2232
  title=printer.get_message_from_key("models_title"),
2208
2233
  expand=True,
2209
2234
  show_lines=True
2210
2235
  )
2211
- table.add_column("Name", style="cyan", width=40, no_wrap=False)
2212
- table.add_column("Model Name", style="magenta", width=30, overflow="fold")
2213
- table.add_column("Base URL", style="white", width=50, overflow="fold")
2214
- table.add_column("Input Price (M)", style="magenta", width=15)
2215
- table.add_column("Output Price (M)", style="magenta", width=15)
2216
- table.add_column("Speed (s/req)", style="blue", width=15)
2217
- for m in models_data:
2236
+ table.add_column("Name", style="cyan", width=30, overflow="fold", no_wrap=False)
2237
+ table.add_column("Model Name", style="magenta", width=30, overflow="fold", no_wrap=False)
2238
+ table.add_column("Base URL", style="white", width=40, overflow="fold", no_wrap=False)
2239
+ table.add_column("Input Price (M)", style="magenta", width=15, overflow="fold", no_wrap=False)
2240
+ table.add_column("Output Price (M)", style="magenta", width=15, overflow="fold", no_wrap=False)
2241
+ table.add_column("Speed (s/req)", style="blue", width=15, overflow="fold", no_wrap=False)
2242
+ for m in sorted_models:
2218
2243
  # Check if api_key_path exists and file exists
2219
2244
  is_api_key_set = "api_key" in m
2220
2245
  name = m.get("name", "")
@@ -2281,6 +2306,26 @@ def manage_models(params, query: str):
2281
2306
  else:
2282
2307
  printer.print_in_terminal("models_speed_usage", style="red")
2283
2308
 
2309
+ elif subcmd == "/speed-test":
2310
+ from autocoder.common.model_speed_test import render_speed_test_in_terminal
2311
+ test_rounds = 1 # 默认测试轮数
2312
+
2313
+ enable_long_context = False
2314
+ if "/long_context" in query:
2315
+ enable_long_context = True
2316
+ query = query.replace("/long_context", "", 1).strip()
2317
+
2318
+ if "/long-context" in query:
2319
+ enable_long_context = True
2320
+ query = query.replace("/long-context", "", 1).strip()
2321
+
2322
+ # 解析可选的测试轮数参数
2323
+ args = query.strip().split()
2324
+ if args and args[0].isdigit():
2325
+ test_rounds = int(args[0])
2326
+
2327
+ render_speed_test_in_terminal(params.product_mode, test_rounds,enable_long_context=enable_long_context)
2328
+
2284
2329
  elif subcmd == "/add":
2285
2330
  # Support both simplified and legacy formats
2286
2331
  args = query.strip().split(" ")
@@ -85,7 +85,7 @@ MESSAGES = {
85
85
  "design_desc": "Generate SVG image based on the provided description",
86
86
  "commit_desc": "Auto generate yaml file and commit changes based on user's manual changes",
87
87
  "models_desc": "Manage model configurations, only available in lite mode",
88
- "models_usage": "Usage: /models /list|/add|/add_model|/remove|/price|/speed ...",
88
+ "models_usage": "Usage: /models <command>\nAvailable subcommands:\n /list - List all models\n /add <name> <api_key> - Add a built-in model\n /add_model - Add a custom model\n /remove <name> - Remove a model\n /input_price <name> <value> - Set model input price\n /output_price <name> <value> - Set model output price\n /speed <name> <value> - Set model speed\n /speed-test - Test models speed\n /speed-test-long - Test models speed with long context",
89
89
  "models_added": "Added/Updated model '{{name}}' successfully.",
90
90
  "models_add_failed": "Failed to add model '{{name}}'. Model not found in defaults.",
91
91
  "models_add_usage": "Usage: /models /add <name> <api_key> or\n/models /add <name> <model_type> <model_name> <base_url> <api_key_path> [description]",
@@ -213,7 +213,7 @@ MESSAGES = {
213
213
  "conf_value": "值",
214
214
  "conf_title": "配置设置",
215
215
  "conf_subtitle": "使用 /conf <key>:<value> 修改这些设置",
216
- "models_usage": "用法: /models /list|/add|/add_model|/remove|/price|/speed ...",
216
+ "models_usage": "用法: /models <命令>\n可用的子命令:\n /list - 列出所有模型\n /add <名称> <API密钥> - 添加内置模型\n /add_model - 添加自定义模型\n /remove <名称> - 移除模型\n /input_price <名称> <价格> - 设置模型输入价格\n /output_price <名称> <价格> - 设置模型输出价格\n /speed <名称> <速度> - 设置模型速度\n /speed-test - 测试模型速度\n /speed-test-long - 使用长文本上下文测试模型速度",
217
217
  "models_added": "成功添加/更新模型 '{{name}}'。",
218
218
  "models_add_failed": "添加模型 '{{name}}' 失败。在默认模型中未找到该模型。",
219
219
  "models_add_usage": "用法: /models /add <name> <api_key> 或\n/models /add <name> <model_type> <model_name> <base_url> <api_key_path> [description]",
@@ -3,6 +3,11 @@ from byzerllm.utils import format_str_jinja2
3
3
 
4
4
  MESSAGES = {
5
5
  "en": {
6
+ "models_no_active": "No active models found",
7
+ "models_speed_test_results": "Model Speed Test Results",
8
+ "models_testing": "Testing model: {{name}}...",
9
+ "models_testing_start": "Starting speed test for all active models...",
10
+ "models_testing_progress": "Testing progress: {{ completed }}/{{ total }} models",
6
11
  "generation_cancelled": "[Interrupted] Generation cancelled",
7
12
  "model_not_found": "Model {{model_name}} not found",
8
13
  "generating_shell_script": "Generating Shell Script",
@@ -92,6 +97,10 @@ MESSAGES = {
92
97
  "estimated_input_tokens_in_generate": "Estimated input tokens in generate ({{ generate_mode }}): {{ estimated_input_tokens }}",
93
98
  },
94
99
  "zh": {
100
+ "models_no_active": "未找到激活的模型",
101
+ "models_speed_test_results": "模型速度测试结果",
102
+ "models_testing": "正在测试模型: {{name}}...",
103
+ "models_testing_start": "开始对所有激活的模型进行速度测试...",
95
104
  "generation_cancelled": "[已中断] 生成已取消",
96
105
  "model_not_found": "未找到模型: {{model_name}}",
97
106
  "generating_shell_script": "正在生成 Shell 脚本",
@@ -33,7 +33,11 @@ COMMANDS = {
33
33
  "/add": "",
34
34
  "/add_model": "",
35
35
  "/remove": "",
36
- "/list": ""
36
+ "/list": "",
37
+ "/speed": "",
38
+ "/speed-test": "",
39
+ "/input_price": "",
40
+ "/output_price": "",
37
41
  }
38
42
  }
39
43
 
@@ -0,0 +1,392 @@
1
+ import time
2
+ import byzerllm
3
+ from typing import Dict, Any, List, Optional
4
+ from rich.console import Console
5
+ from rich.table import Table
6
+ from rich.panel import Panel
7
+ from autocoder.common.printer import Printer
8
+ from autocoder import models as models_module
9
+ from autocoder.utils.llms import get_single_llm
10
+ import byzerllm
11
+ import pkg_resources
12
+ from concurrent.futures import ThreadPoolExecutor
13
+ from typing import Dict, List, Tuple
14
+ from pydantic import BaseModel
15
+
16
+ class ModelSpeedTestResult(BaseModel):
17
+ model_name: str
18
+ tokens_per_second: float
19
+ first_token_time: float
20
+ input_tokens_count: float
21
+ generated_tokens_count: float
22
+ input_tokens_cost: float
23
+ generated_tokens_cost: float
24
+ status: str
25
+ error: Optional[str] = None
26
+
27
+ class SpeedTestResults(BaseModel):
28
+ results: List[ModelSpeedTestResult]
29
+
30
+ byzerllm_content = ""
31
+ try:
32
+ byzerllm_conten_path = pkg_resources.resource_filename(
33
+ "autocoder", "data/byzerllm.md"
34
+ )
35
+ with open(byzerllm_conten_path, "r",encoding="utf-8") as f:
36
+ byzerllm_content = f.read()
37
+ except FileNotFoundError:
38
+ pass
39
+
40
+ @byzerllm.prompt()
41
+ def long_context_prompt() -> str:
42
+ '''
43
+ 下面是我们提供的一份文档:
44
+ <document>
45
+ {{ content }}
46
+ </document>
47
+
48
+ 请根据上述文档,实现用户的需求:
49
+
50
+ <query>
51
+ 我想开发一个翻译程序,使用prompt 函数实现。
52
+ </query>
53
+ '''
54
+ return {
55
+ "content": byzerllm_content
56
+ }
57
+
58
+ @byzerllm.prompt()
59
+ def short_context_prompt() -> str:
60
+ '''
61
+ Hello, can you help me test the response speed?
62
+ '''
63
+ return {}
64
+
65
+ def test_model_speed(model_name: str,
66
+ product_mode: str,
67
+ test_rounds: int = 3,
68
+ enable_long_context: bool = False
69
+ ) -> Dict[str, Any]:
70
+ from autocoder.models import get_model_by_name
71
+ """
72
+ 测试单个模型的速度
73
+
74
+ Args:
75
+ model_name: 模型名称
76
+ product_mode: 产品模式 (lite/pro)
77
+ test_rounds: 测试轮数
78
+
79
+ Returns:
80
+ Dict包含测试结果:
81
+ - avg_time: 平均响应时间
82
+ - min_time: 最小响应时间
83
+ - max_time: 最大响应时间
84
+ - first_token_time: 首token时间
85
+ - success: 是否测试成功
86
+ - error: 错误信息(如果有)
87
+ """
88
+ try:
89
+ llm = get_single_llm(model_name, product_mode)
90
+ model_info = get_model_by_name(model_name)
91
+
92
+ times = []
93
+ first_token_times = []
94
+ tokens_per_seconds = []
95
+ input_tokens_counts = []
96
+ generated_tokens_counts = []
97
+
98
+ input_tokens_costs = []
99
+ generated_tokens_costs = []
100
+
101
+ input_tokens_cost_per_m = model_info.get("input_price", 0.0) / 1000000
102
+ output_tokens_cost_per_m = model_info.get("output_price", 0.0) / 1000000
103
+
104
+ test_query = short_context_prompt.prompt()
105
+ if enable_long_context:
106
+ test_query = long_context_prompt.prompt()
107
+
108
+ content = ""
109
+ for _ in range(test_rounds):
110
+ start_time = time.time()
111
+ first_token_received = False
112
+ first_token_time = None
113
+ last_meta = None
114
+ input_tokens_count = 0
115
+ generated_tokens_count = 0
116
+ input_tokens_cost = 0
117
+ generated_tokens_cost = 0
118
+ for chunk,meta in llm.stream_chat_oai(conversations=[{
119
+ "role": "user",
120
+ "content": test_query
121
+ }],delta_mode=True):
122
+ content += chunk
123
+ last_meta = meta
124
+ current_time = time.time()
125
+ if not first_token_received:
126
+ first_token_time = current_time - start_time
127
+ first_token_received = True
128
+ first_token_times.append(first_token_time)
129
+
130
+ end_time = time.time()
131
+ generated_tokens_count = 0
132
+ if last_meta:
133
+ generated_tokens_count = last_meta.generated_tokens_count
134
+ input_tokens_count = last_meta.input_tokens_count
135
+ input_tokens_cost = input_tokens_count * input_tokens_cost_per_m
136
+ generated_tokens_cost = generated_tokens_count * output_tokens_cost_per_m
137
+
138
+ input_tokens_costs.append(input_tokens_cost)
139
+ generated_tokens_costs.append(generated_tokens_cost)
140
+ generated_tokens_counts.append(generated_tokens_count)
141
+ input_tokens_counts.append(input_tokens_count)
142
+
143
+ tokens_per_seconds.append(generated_tokens_count / (end_time - start_time))
144
+ times.append(end_time - start_time)
145
+
146
+
147
+ avg_time = sum(times) / len(times)
148
+ return {
149
+ "tokens_per_second": sum(tokens_per_seconds) / len(tokens_per_seconds),
150
+ "avg_time": avg_time,
151
+ "min_time": min(times),
152
+ "max_time": max(times),
153
+ "first_token_time": sum(first_token_times) / len(first_token_times),
154
+ "input_tokens_count": sum(input_tokens_counts) / len(input_tokens_counts),
155
+ "generated_tokens_count": sum(generated_tokens_counts) / len(generated_tokens_counts),
156
+ "success": True,
157
+ "error": None,
158
+ "input_tokens_cost": sum(input_tokens_costs) / len(input_tokens_costs),
159
+ "generated_tokens_cost": sum(generated_tokens_costs) / len(generated_tokens_costs)
160
+ }
161
+ except Exception as e:
162
+ return {
163
+ "tokens_per_second": 0,
164
+ "avg_time": 0,
165
+ "min_time": 0,
166
+ "max_time": 0,
167
+ "first_token_time": 0,
168
+ "input_tokens_count": 0,
169
+ "generated_tokens_count": 0,
170
+ "success": False,
171
+ "error": str(e),
172
+ "input_tokens_cost": 0.0,
173
+ "generated_tokens_cost": 0.0
174
+ }
175
+
176
+ def test_model_speed_wrapper(args: Tuple[str, str, int, bool]) -> Tuple[str, Dict[str, Any]]:
177
+ """
178
+ 包装测试函数以适应线程池调用
179
+
180
+ Args:
181
+ args: (model_name, product_mode, test_rounds)的元组
182
+
183
+ Returns:
184
+ (model_name, test_results)的元组
185
+ """
186
+ model_name, product_mode, test_rounds,enable_long_context = args
187
+ results = test_model_speed(model_name, product_mode, test_rounds,enable_long_context)
188
+ return (model_name, results)
189
+
190
+
191
+ def run_speed_test(product_mode: str, test_rounds: int = 3, max_workers: Optional[int] = None, enable_long_context: bool = False) -> SpeedTestResults:
192
+ """
193
+ 运行所有已激活模型的速度测试
194
+
195
+ Args:
196
+ product_mode: 产品模式 (lite/pro)
197
+ test_rounds: 每个模型测试的轮数
198
+ max_workers: 最大线程数,默认为None(ThreadPoolExecutor会自动设置)
199
+ enable_long_context: 是否启用长文本上下文测试
200
+
201
+ Returns:
202
+ SpeedTestResults: 包含所有模型测试结果的pydantic模型
203
+ """
204
+ # 获取所有模型
205
+ models_data = models_module.load_models()
206
+ active_models = [m for m in models_data if "api_key" in m] if product_mode == "lite" else models_data
207
+
208
+ if not active_models:
209
+ return SpeedTestResults(results=[])
210
+
211
+ # 准备测试参数
212
+ test_args = [(model["name"], product_mode, test_rounds, enable_long_context) for model in active_models]
213
+
214
+ # 存储结果用于排序
215
+ results_list = []
216
+
217
+ # 使用线程池并发测试
218
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
219
+ # 提交所有测试任务并获取future对象
220
+ future_to_model = {executor.submit(test_model_speed_wrapper, args): args[0]
221
+ for args in test_args}
222
+
223
+ # 收集结果
224
+ for future in future_to_model:
225
+ model_name = future_to_model[future]
226
+
227
+ try:
228
+ _, results = future.result()
229
+
230
+ if results["success"]:
231
+ status = "✓"
232
+ results_list.append((
233
+ results['tokens_per_second'],
234
+ ModelSpeedTestResult(
235
+ model_name=model_name,
236
+ tokens_per_second=results['tokens_per_second'],
237
+ first_token_time=results['first_token_time'],
238
+ input_tokens_count=results['input_tokens_count'],
239
+ generated_tokens_count=results['generated_tokens_count'],
240
+ status=status,
241
+ input_tokens_cost=results['input_tokens_cost'],
242
+ generated_tokens_cost=results['generated_tokens_cost'],
243
+ )
244
+ ))
245
+ try:
246
+ # 更新模型的平均速度
247
+ models_module.update_model_speed(model_name, results['tokens_per_second'])
248
+ except Exception:
249
+ pass
250
+ else:
251
+ results_list.append((
252
+ 0,
253
+ ModelSpeedTestResult(
254
+ model_name=model_name,
255
+ tokens_per_second=0,
256
+ first_token_time=0,
257
+ input_tokens_count=0,
258
+ generated_tokens_count=0,
259
+ status=f"✗ {results['error']}",
260
+ error=results['error'],
261
+ input_tokens_cost=0.0,
262
+ generated_tokens_cost=0.0
263
+ )
264
+ ))
265
+ except Exception as e:
266
+ results_list.append((
267
+ 0,
268
+ ModelSpeedTestResult(
269
+ model_name=model_name,
270
+ tokens_per_second=0,
271
+ first_token_time=0,
272
+ input_tokens_count=0,
273
+ generated_tokens_count=0,
274
+ status=f"✗ {str(e)}",
275
+ error=str(e),
276
+ input_tokens_cost=0.0,
277
+ generated_tokens_cost=0.0
278
+ )
279
+ ))
280
+
281
+ # 按速度排序
282
+ results_list.sort(key=lambda x: x[0], reverse=True)
283
+
284
+ return SpeedTestResults(results=[result[1] for result in results_list])
285
+
286
+ def render_speed_test_in_terminal(product_mode: str, test_rounds: int = 3, max_workers: Optional[int] = None,enable_long_context: bool = False) -> None:
287
+ """
288
+ 运行所有已激活模型的速度测试
289
+
290
+ Args:
291
+ product_mode: 产品模式 (lite/pro)
292
+ test_rounds: 每个模型测试的轮数
293
+ max_workers: 最大线程数,默认为None(ThreadPoolExecutor会自动设置)
294
+ """
295
+ printer = Printer()
296
+ console = Console()
297
+
298
+ # 获取所有模型
299
+ models_data = models_module.load_models()
300
+ active_models = [m for m in models_data if "api_key" in m] if product_mode == "lite" else models_data
301
+
302
+ if not active_models:
303
+ printer.print_in_terminal("models_no_active", style="yellow")
304
+ return
305
+
306
+ # 创建结果表格
307
+ table = Table(
308
+ title=printer.get_message_from_key("models_speed_test_results"),
309
+ show_header=True,
310
+ header_style="bold magenta",
311
+ show_lines=True
312
+ )
313
+
314
+ table.add_column("Model", style="cyan", width=30)
315
+ table.add_column("Tokens/s", style="green", width=15)
316
+ table.add_column("First Token(s)", style="magenta", width=15)
317
+ table.add_column("Input Tokens", style="magenta", width=15)
318
+ table.add_column("Generated Tokens", style="magenta", width=15)
319
+ table.add_column("Input Tokens Cost", style="yellow", width=15)
320
+ table.add_column("Generated Tokens Cost", style="yellow", width=15)
321
+ table.add_column("Status", style="red", width=20)
322
+
323
+ # 准备测试参数
324
+ test_args = [(model["name"], product_mode, test_rounds, enable_long_context) for model in active_models]
325
+
326
+ # 存储结果用于排序
327
+ results_list = []
328
+
329
+ # 使用线程池并发测试
330
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
331
+ printer.print_in_terminal("models_testing_start", style="yellow")
332
+
333
+ # 提交所有测试任务并获取future对象
334
+ future_to_model = {executor.submit(test_model_speed_wrapper, args): args[0]
335
+ for args in test_args}
336
+
337
+ # 收集结果
338
+ completed = 0
339
+ total = len(future_to_model)
340
+ for future in future_to_model:
341
+ completed += 1
342
+ printer.print_in_terminal("models_testing_progress", style="yellow", completed=completed, total=total)
343
+ model_name = future_to_model[future]
344
+ printer.print_in_terminal("models_testing", style="yellow", name=model_name)
345
+
346
+ try:
347
+ _, results = future.result()
348
+
349
+ if results["success"]:
350
+ status = "✓"
351
+ results['status'] = status
352
+ results_list.append((
353
+ results['tokens_per_second'],
354
+ model_name,
355
+ results
356
+ ))
357
+ try:
358
+ # 更新模型的平均速度
359
+ models_module.update_model_speed(model_name, results['tokens_per_second'])
360
+ except Exception as e:
361
+ pass
362
+ else:
363
+ status = f"✗ ({results['error']})"
364
+ results_list.append((
365
+ 0,
366
+ model_name,
367
+ {"tokens_per_second":0,"avg_time": 0, "input_tokens_count":0, "generated_tokens_count":0, "min_time": 0, "max_time": 0, "first_token_time": 0, "input_tokens_cost": 0.0, "generated_tokens_cost": 0.0, "status": status}
368
+ ))
369
+ except Exception as e:
370
+ results_list.append((
371
+ 0,
372
+ model_name,
373
+ {"tokens_per_second":0,"avg_time": 0, "input_tokens_count":0, "generated_tokens_count":0, "min_time": 0, "max_time": 0, "first_token_time": 0, "input_tokens_cost": 0.0, "generated_tokens_cost": 0.0, "status": f"✗ ({str(e)})"}
374
+ ))
375
+
376
+ # 按速度排序
377
+ results_list.sort(key=lambda x: x[0], reverse=True)
378
+
379
+ # 添加排序后的结果到表格
380
+ for tokens_per_second, model_name, results in results_list:
381
+ table.add_row(
382
+ model_name,
383
+ f"{tokens_per_second:.2f}",
384
+ f"{results['first_token_time']:.2f}",
385
+ f"{results['input_tokens_count']}",
386
+ f"{results['generated_tokens_count']}",
387
+ f"{results['input_tokens_cost']:.4f}",
388
+ f"{results['generated_tokens_cost']:.4f}",
389
+ results['status']
390
+ )
391
+
392
+ console.print(Panel(table, border_style="blue"))