auto-coder 0.1.311__py3-none-any.whl → 0.1.313__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.
- {auto_coder-0.1.311.dist-info → auto_coder-0.1.313.dist-info}/METADATA +1 -1
- {auto_coder-0.1.311.dist-info → auto_coder-0.1.313.dist-info}/RECORD +14 -13
- autocoder/auto_coder_runner.py +22 -8
- autocoder/commands/tools.py +94 -13
- autocoder/common/action_yml_file_manager.py +2 -2
- autocoder/common/token_cost_caculate.py +200 -0
- autocoder/memory/active_context_manager.py +353 -171
- autocoder/memory/active_package.py +175 -34
- autocoder/rag/lang.py +50 -28
- autocoder/version.py +1 -1
- {auto_coder-0.1.311.dist-info → auto_coder-0.1.313.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.311.dist-info → auto_coder-0.1.313.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.311.dist-info → auto_coder-0.1.313.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.311.dist-info → auto_coder-0.1.313.dist-info}/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ autocoder/auto_coder_lang.py,sha256=Rtupq6N3_HT7JRhDKdgCBcwRaiAnyCOR_Gsp4jUomrI,
|
|
|
4
4
|
autocoder/auto_coder_rag.py,sha256=vOqwBHdK_KwMNUUc8ji_tlZ5DoALAG1rDjWAic3rM-4,34561
|
|
5
5
|
autocoder/auto_coder_rag_client_mcp.py,sha256=QRxUbjc6A8UmDMQ8lXgZkjgqtq3lgKYeatJbDY6rSo0,6270
|
|
6
6
|
autocoder/auto_coder_rag_mcp.py,sha256=-RrjNwFaS2e5v8XDIrKR-zlUNUE8UBaeOtojffBrvJo,8521
|
|
7
|
-
autocoder/auto_coder_runner.py,sha256=
|
|
7
|
+
autocoder/auto_coder_runner.py,sha256=ic8ZRGpyuhFNOl4tUVLhSmibOKHNXJudGwsiSjyW--Q,107324
|
|
8
8
|
autocoder/auto_coder_server.py,sha256=E3Z829TPSooRSNhuh3_x9yaZi0f5G0Lm0ntoZhjGaoQ,20576
|
|
9
9
|
autocoder/benchmark.py,sha256=Ypomkdzd1T3GE6dRICY3Hj547dZ6_inqJbBJIp5QMco,4423
|
|
10
10
|
autocoder/chat_auto_coder.py,sha256=Cp5_m3pCxEDcRrVG1uojTfD8xecdl9FvYtD948TvLsg,25223
|
|
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
|
|
|
14
14
|
autocoder/lang.py,sha256=U6AjVV8Rs1uLyjFCZ8sT6WWuNUxMBqkXXIOs4S120uk,14511
|
|
15
15
|
autocoder/models.py,sha256=AyoZ-Pzy0oyYUmWCxOIRiOImsqboSfRET7LO9-UOuxI,11172
|
|
16
16
|
autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
|
|
17
|
-
autocoder/version.py,sha256=
|
|
17
|
+
autocoder/version.py,sha256=O3BsbD2rT7pC9_mUbayTS658pf_QJpU1V9cCPbAITT0,23
|
|
18
18
|
autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
autocoder/agent/auto_demand_organizer.py,sha256=URAq0gSEiHeV_W4zwhOI_83kHz0Ryfj1gcfh5jwCv_w,6501
|
|
20
20
|
autocoder/agent/auto_filegroup.py,sha256=pBsAkBcpFTff-9L5OwI8xhf2xPKpl-aZwz-skF2B6dc,6296
|
|
@@ -30,11 +30,11 @@ autocoder/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
30
30
|
autocoder/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
31
|
autocoder/commands/auto_command.py,sha256=fZ4TNU4YA9qdoVHmZX0rb6sV_N-DEcgSLaC5s3PGSHA,64200
|
|
32
32
|
autocoder/commands/auto_web.py,sha256=Cc0eb6JN3SvFy3GD_lpSLvIqj7F1eFDTcwg1t-zDcKg,39024
|
|
33
|
-
autocoder/commands/tools.py,sha256
|
|
33
|
+
autocoder/commands/tools.py,sha256=nLXzcmwwOaMjgTUjad4usq1W0VH79jard1irxsNrRLk,31576
|
|
34
34
|
autocoder/common/JupyterClient.py,sha256=O-wi6pXeAEYhAY24kDa0BINrLYvKS6rKyWe98pDClS0,2816
|
|
35
35
|
autocoder/common/ShellClient.py,sha256=fM1q8t_XMSbLBl2zkCNC2J9xuyKN3eXzGm6hHhqL2WY,2286
|
|
36
36
|
autocoder/common/__init__.py,sha256=sIbE0Gm30MkCmZ9ClQeKx1Hw-vjdcVF65hO5TschS1Y,14070
|
|
37
|
-
autocoder/common/action_yml_file_manager.py,sha256
|
|
37
|
+
autocoder/common/action_yml_file_manager.py,sha256=-bbTB8nyxgzf_GNa_TCL21TV87b_9ENmDmybZWVrwSA,17371
|
|
38
38
|
autocoder/common/anything2images.py,sha256=0ILBbWzY02M-CiWB-vzuomb_J1hVdxRcenAfIrAXq9M,25283
|
|
39
39
|
autocoder/common/anything2img.py,sha256=iZQmg8srXlD7N5uGl5b_ONKJMBjYoW8kPmokkG6ISF0,10118
|
|
40
40
|
autocoder/common/audio.py,sha256=Kn9nWKQddWnUrAz0a_ZUgjcu4VUU_IcZBigT7n3N3qc,7439
|
|
@@ -86,6 +86,7 @@ autocoder/common/stats_panel.py,sha256=wGl9O45pjVVDxhNumLv4_NfLYSlUP_18Tw4hcJSjw
|
|
|
86
86
|
autocoder/common/stream_out_type.py,sha256=uNHRg-1my6CxcPzQM6pACrYpTZbxOenVT8aLxnruv2w,333
|
|
87
87
|
autocoder/common/sys_prompt.py,sha256=JlexfjZt554faqbgkCmzOJqYUzDHfbnxly5ugFfHfEE,26403
|
|
88
88
|
autocoder/common/text.py,sha256=KGRQq314GHBmY4MWG8ossRoQi1_DTotvhxchpn78c-k,1003
|
|
89
|
+
autocoder/common/token_cost_caculate.py,sha256=5IoaFtcXrHMW-xDWoO5a1UQ8rSZqTeQRLXrgNGXE5NY,7626
|
|
89
90
|
autocoder/common/types.py,sha256=PXTETrsTvhLE49jqAeUKGySvxBN9pjeyCgRHLDYdd9U,664
|
|
90
91
|
autocoder/common/utils_code_auto_generate.py,sha256=Tvb3Mqxedj0Jhi0faKyWvILuNCty9fZi341fyIs33tA,4550
|
|
91
92
|
autocoder/common/mcp_servers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -118,8 +119,8 @@ autocoder/index/filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
118
119
|
autocoder/index/filter/normal_filter.py,sha256=MI-8xdXCrniaxYCHVTLkq5tafvcUiauD3LN0b3ymRXI,8361
|
|
119
120
|
autocoder/index/filter/quick_filter.py,sha256=ozESEgy506FQ5ecjOumyo4D_KMrterB1QLmnVtiyOiM,43264
|
|
120
121
|
autocoder/memory/__init__.py,sha256=5FeGvsesRViYL4BkFiHw9SdlyHeWlqALpTyqOpfnBRw,179
|
|
121
|
-
autocoder/memory/active_context_manager.py,sha256=
|
|
122
|
-
autocoder/memory/active_package.py,sha256=
|
|
122
|
+
autocoder/memory/active_context_manager.py,sha256=zeBOhOrlSeUU2-qaJPDCEGryJ5IorTrdsDoRJ3FHOac,33238
|
|
123
|
+
autocoder/memory/active_package.py,sha256=ccTs5BTrbno0Ji2YHdNfA4j1JbBTuTCvpW5bLl5xfiY,25336
|
|
123
124
|
autocoder/memory/async_processor.py,sha256=htHzLGupw9IHQAEdLe2AEaALZSItPi3AltDt8FMTRHk,4643
|
|
124
125
|
autocoder/memory/directory_mapper.py,sha256=BXHblOdRpeZb7URDECALp9uN5oi91KmkW9g_UaWFuZY,2513
|
|
125
126
|
autocoder/plugins/__init__.py,sha256=uc8UNSAVb9uQvr5zUSS-Xu_RhrdbO2i3w2NhkrVtODM,43023
|
|
@@ -136,7 +137,7 @@ autocoder/rag/api_server.py,sha256=StGyxrM-7-W2vYHJq-i_Fv-MHrl9UgVWY272Hd-6VJ4,1
|
|
|
136
137
|
autocoder/rag/conversation_to_queries.py,sha256=xwmErn4WbdADnhK1me-h_6fV3KYrl_y1qPNQl1aoI6o,4810
|
|
137
138
|
autocoder/rag/doc_filter.py,sha256=UduVO2mlrngwJICrefjDJTYfdmQ4GcRXrfWDQ7xXksk,14206
|
|
138
139
|
autocoder/rag/document_retriever.py,sha256=5BDqKVJqLPScEnua5S5suXhWuCaALIfPf5obXeJoWfs,8461
|
|
139
|
-
autocoder/rag/lang.py,sha256=
|
|
140
|
+
autocoder/rag/lang.py,sha256=HvcMeu6jReEJOGxyLMn4rwBoD-myFwmykS3VLceBJLs,3364
|
|
140
141
|
autocoder/rag/llm_wrapper.py,sha256=Ht5GF5yJtrztoliujsZzx_ooWZmHkd5xLZKcGEiicZw,4303
|
|
141
142
|
autocoder/rag/long_context_rag.py,sha256=RE4xse3XxSC_HQA5erqrx6MhanP_29mBRdYOTJQZYGc,42106
|
|
142
143
|
autocoder/rag/qa_conversation_strategy.py,sha256=1AcHV0MU00yTls20LlCPO-Un_OhSrr_p-H5lxLleAq4,6060
|
|
@@ -199,9 +200,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
199
200
|
autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
200
201
|
autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=xuBeWD0YOckqRo8JB1WkVIMOYH6c24m7JfV4svBfPDo,15113
|
|
201
202
|
autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
202
|
-
auto_coder-0.1.
|
|
203
|
-
auto_coder-0.1.
|
|
204
|
-
auto_coder-0.1.
|
|
205
|
-
auto_coder-0.1.
|
|
206
|
-
auto_coder-0.1.
|
|
207
|
-
auto_coder-0.1.
|
|
203
|
+
auto_coder-0.1.313.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
204
|
+
auto_coder-0.1.313.dist-info/METADATA,sha256=-QD-1xqhvWFPOYOyHkK8QQCD8N6ocoSb2Qm1w3-b_pQ,2747
|
|
205
|
+
auto_coder-0.1.313.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
206
|
+
auto_coder-0.1.313.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
|
|
207
|
+
auto_coder-0.1.313.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
|
|
208
|
+
auto_coder-0.1.313.dist-info/RECORD,,
|
autocoder/auto_coder_runner.py
CHANGED
|
@@ -1854,12 +1854,14 @@ def active_context(query: str):
|
|
|
1854
1854
|
return
|
|
1855
1855
|
|
|
1856
1856
|
# 创建表格
|
|
1857
|
-
table = Table(title="活动上下文任务列表")
|
|
1858
|
-
table.add_column("任务ID", style="cyan")
|
|
1859
|
-
table.add_column("状态", style="green")
|
|
1860
|
-
table.add_column("开始时间", style="yellow")
|
|
1861
|
-
table.add_column("完成时间", style="yellow")
|
|
1862
|
-
table.add_column("文件", style="blue")
|
|
1857
|
+
table = Table(title="活动上下文任务列表", show_lines=True, expand=True)
|
|
1858
|
+
table.add_column("任务ID", style="cyan", no_wrap=False)
|
|
1859
|
+
table.add_column("状态", style="green", no_wrap=False)
|
|
1860
|
+
table.add_column("开始时间", style="yellow", no_wrap=False)
|
|
1861
|
+
table.add_column("完成时间", style="yellow", no_wrap=False)
|
|
1862
|
+
table.add_column("文件", style="blue", no_wrap=False)
|
|
1863
|
+
table.add_column("Token统计", style="magenta", no_wrap=False)
|
|
1864
|
+
table.add_column("费用", style="red", no_wrap=False)
|
|
1863
1865
|
|
|
1864
1866
|
# 添加任务数据
|
|
1865
1867
|
for task in all_tasks:
|
|
@@ -1887,17 +1889,29 @@ def active_context(query: str):
|
|
|
1887
1889
|
# 获取文件名
|
|
1888
1890
|
file_name = task.get("file_name", "未知")
|
|
1889
1891
|
|
|
1892
|
+
# 获取token信息
|
|
1893
|
+
total_tokens = task.get("total_tokens", 0)
|
|
1894
|
+
input_tokens = task.get("input_tokens", 0)
|
|
1895
|
+
output_tokens = task.get("output_tokens", 0)
|
|
1896
|
+
token_info = f"总计: {total_tokens:,}\n输入: {input_tokens:,}\n输出: {output_tokens:,}"
|
|
1897
|
+
|
|
1898
|
+
# 获取费用信息
|
|
1899
|
+
cost = task.get("cost", 0.0)
|
|
1900
|
+
cost_info = f"${cost:.6f}"
|
|
1901
|
+
|
|
1890
1902
|
# 添加到表格
|
|
1891
1903
|
table.add_row(
|
|
1892
1904
|
task.get("task_id", "未知"),
|
|
1893
1905
|
status_display,
|
|
1894
1906
|
start_time_str,
|
|
1895
1907
|
completion_time_str,
|
|
1896
|
-
file_name
|
|
1908
|
+
file_name,
|
|
1909
|
+
token_info,
|
|
1910
|
+
cost_info
|
|
1897
1911
|
)
|
|
1898
1912
|
|
|
1899
1913
|
# 显示表格
|
|
1900
|
-
console = Console()
|
|
1914
|
+
console = Console(width=120) # 设置更宽的显示宽度
|
|
1901
1915
|
console.print(table)
|
|
1902
1916
|
|
|
1903
1917
|
|
autocoder/commands/tools.py
CHANGED
|
@@ -40,6 +40,8 @@ from autocoder.index.symbols_utils import (
|
|
|
40
40
|
from autocoder.run_context import get_run_context
|
|
41
41
|
from autocoder.events.event_manager_singleton import get_event_manager
|
|
42
42
|
from autocoder.events import event_content as EventContentCreator
|
|
43
|
+
from autocoder.linters.linter_factory import LinterFactory, lint_file, lint_project, format_lint_result
|
|
44
|
+
import traceback
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
@byzerllm.prompt()
|
|
@@ -159,23 +161,31 @@ class AutoCommandTools:
|
|
|
159
161
|
return answer
|
|
160
162
|
|
|
161
163
|
def response_user(self, response: str):
|
|
162
|
-
|
|
163
164
|
# 如果是在web模式下,则使用event_manager事件来询问用户
|
|
164
165
|
if get_run_context().is_web():
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
EventContentCreator.
|
|
169
|
-
|
|
166
|
+
try:
|
|
167
|
+
answer = get_event_manager(
|
|
168
|
+
self.args.event_file).write_result(
|
|
169
|
+
EventContentCreator.create_result(
|
|
170
|
+
EventContentCreator.ResultSummaryContent(
|
|
171
|
+
summary=response
|
|
172
|
+
)
|
|
170
173
|
)
|
|
171
174
|
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
175
|
+
self.result_manager.append(content=response, meta={
|
|
176
|
+
"action": "response_user",
|
|
177
|
+
"input": {
|
|
178
|
+
"response": response
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
except Exception as e:
|
|
182
|
+
error_message = f"Error: {str(e)}\n\n完整异常堆栈信息:\n{traceback.format_exc()}"
|
|
183
|
+
self.result_manager.append(content=f"Error: {error_message}", meta={
|
|
184
|
+
"action": "response_user",
|
|
185
|
+
"input": {
|
|
186
|
+
"response": response
|
|
187
|
+
}
|
|
188
|
+
})
|
|
179
189
|
return answer
|
|
180
190
|
|
|
181
191
|
console = Console()
|
|
@@ -764,3 +774,74 @@ class AutoCommandTools:
|
|
|
764
774
|
}
|
|
765
775
|
})
|
|
766
776
|
return v
|
|
777
|
+
|
|
778
|
+
def lint_code(self, path: str, language: Optional[str] = None, fix: bool = False, verbose: bool = False) -> str:
|
|
779
|
+
"""
|
|
780
|
+
对代码进行质量检查,支持多种编程语言。
|
|
781
|
+
|
|
782
|
+
参数说明:
|
|
783
|
+
path (str): 要检查的文件路径或项目目录
|
|
784
|
+
language (str, optional): 明确指定语言类型,如'python', 'javascript', 'typescript', 'react', 'vue'等
|
|
785
|
+
如果不指定,将尝试根据文件扩展名或项目结构自动检测
|
|
786
|
+
fix (bool): 是否自动修复可修复的问题,默认为False
|
|
787
|
+
verbose (bool): 是否显示详细输出,默认为False
|
|
788
|
+
|
|
789
|
+
返回值:
|
|
790
|
+
格式化后的lint结果,包含错误和警告信息
|
|
791
|
+
|
|
792
|
+
支持的语言:
|
|
793
|
+
- 前端: JavaScript, TypeScript, React, Vue (使用ESLint)
|
|
794
|
+
- Python: 使用pylint, flake8, black
|
|
795
|
+
|
|
796
|
+
说明:
|
|
797
|
+
- 对于前端代码,需要Node.js环境
|
|
798
|
+
- 对于Python代码,需要pylint/flake8/black
|
|
799
|
+
- 工具会尝试自动安装缺少的依赖
|
|
800
|
+
- 如果路径是文件,则只检查该文件
|
|
801
|
+
- 如果路径是目录,则检查整个项目
|
|
802
|
+
- fix=True时会尝试自动修复问题
|
|
803
|
+
"""
|
|
804
|
+
try:
|
|
805
|
+
# 检查是否是目录或文件
|
|
806
|
+
is_directory = os.path.isdir(path)
|
|
807
|
+
|
|
808
|
+
# 根据路径类型执行相应的lint操作
|
|
809
|
+
if is_directory:
|
|
810
|
+
# 对整个项目进行lint
|
|
811
|
+
result = lint_project(path, language=language, fix=fix, verbose=verbose)
|
|
812
|
+
else:
|
|
813
|
+
# 对单个文件进行lint
|
|
814
|
+
result = lint_file(path, fix=fix, verbose=verbose)
|
|
815
|
+
|
|
816
|
+
# 格式化结果
|
|
817
|
+
formatted_result = format_lint_result(result, language=language)
|
|
818
|
+
|
|
819
|
+
# 记录操作结果
|
|
820
|
+
self.result_manager.add_result(content=formatted_result, meta={
|
|
821
|
+
"action": "lint_code",
|
|
822
|
+
"input": {
|
|
823
|
+
"path": path,
|
|
824
|
+
"language": language,
|
|
825
|
+
"fix": fix,
|
|
826
|
+
"verbose": verbose
|
|
827
|
+
},
|
|
828
|
+
"result": result
|
|
829
|
+
})
|
|
830
|
+
|
|
831
|
+
return formatted_result
|
|
832
|
+
|
|
833
|
+
except Exception as e:
|
|
834
|
+
error_message = f"Linting failed: {str(e)}\n\n完整异常堆栈信息:\n{traceback.format_exc()}"
|
|
835
|
+
|
|
836
|
+
self.result_manager.add_result(content=error_message, meta={
|
|
837
|
+
"action": "lint_code",
|
|
838
|
+
"input": {
|
|
839
|
+
"path": path,
|
|
840
|
+
"language": language,
|
|
841
|
+
"fix": fix,
|
|
842
|
+
"verbose": verbose
|
|
843
|
+
},
|
|
844
|
+
"error": error_message
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
return error_message
|
|
@@ -63,7 +63,7 @@ class ActionYmlFileManager:
|
|
|
63
63
|
|
|
64
64
|
action_files = [
|
|
65
65
|
f for f in os.listdir(self.actions_dir)
|
|
66
|
-
if f[:3].isdigit() and "_" in f and f.endswith('.yml')
|
|
66
|
+
if f[:3].isdigit() and "_" in f and f.endswith('_chat_action.yml')
|
|
67
67
|
]
|
|
68
68
|
|
|
69
69
|
if filter_prefix:
|
|
@@ -185,7 +185,7 @@ class ActionYmlFileManager:
|
|
|
185
185
|
Returns:
|
|
186
186
|
Dict: YAML 内容,如果加载失败返回空字典
|
|
187
187
|
"""
|
|
188
|
-
yaml_path = os.path.join(self.actions_dir, file_name)
|
|
188
|
+
yaml_path = os.path.join(self.actions_dir, file_name)
|
|
189
189
|
try:
|
|
190
190
|
with open(yaml_path, 'r', encoding='utf-8') as f:
|
|
191
191
|
content = yaml.safe_load(f) or {}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional, Tuple, List
|
|
2
|
+
import time
|
|
3
|
+
from loguru import logger
|
|
4
|
+
import byzerllm
|
|
5
|
+
from byzerllm import MetaHolder
|
|
6
|
+
from autocoder.utils import llms
|
|
7
|
+
from autocoder.events.event_manager_singleton import get_event_manager
|
|
8
|
+
from autocoder.events import event_content as EventContentCreator
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TokenUsageStats(BaseModel):
|
|
13
|
+
"""Token使用统计数据模型"""
|
|
14
|
+
input_tokens: int = Field(default=0, description="输入的token数量")
|
|
15
|
+
output_tokens: int = Field(default=0, description="输出的token数量")
|
|
16
|
+
input_cost: float = Field(default=0.0, description="输入token的成本")
|
|
17
|
+
output_cost: float = Field(default=0.0, description="输出token的成本")
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def total_tokens(self) -> int:
|
|
21
|
+
"""总token数"""
|
|
22
|
+
return self.input_tokens + self.output_tokens
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def total_cost(self) -> float:
|
|
26
|
+
"""总成本"""
|
|
27
|
+
return self.input_cost + self.output_cost
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TokenCostCalculator:
|
|
31
|
+
"""
|
|
32
|
+
Token计费计算器 - 提供获取模型信息、计算token成本和记录token统计信息的公共功能
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, logger_name: str = "TokenCostCalculator"):
|
|
36
|
+
"""
|
|
37
|
+
初始化Token计费计算器
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
logger_name: logger名称,默认为TokenCostCalculator
|
|
41
|
+
"""
|
|
42
|
+
self.logger = logger.bind(name=logger_name)
|
|
43
|
+
|
|
44
|
+
def get_model_info(self, llm: byzerllm.ByzerLLM,product_mode:str="lite") -> Tuple[str, Dict[str, Dict[str, float]]]:
|
|
45
|
+
"""
|
|
46
|
+
获取模型名称和价格信息
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
llm: ByzerLLM实例
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Tuple[str, Dict]: 模型名称字符串和价格信息字典
|
|
53
|
+
"""
|
|
54
|
+
# 获取模型名称列表
|
|
55
|
+
model_names = llms.get_llm_names(llm)
|
|
56
|
+
model_name = ",".join(model_names)
|
|
57
|
+
|
|
58
|
+
# 获取模型价格信息
|
|
59
|
+
model_info_map = {}
|
|
60
|
+
for name in model_names:
|
|
61
|
+
info = llms.get_model_info(name, product_mode)
|
|
62
|
+
if info:
|
|
63
|
+
model_info_map[name] = {
|
|
64
|
+
"input_price": info.get("input_price", 0.0),
|
|
65
|
+
"output_price": info.get("output_price", 0.0)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return model_name, model_info_map
|
|
69
|
+
|
|
70
|
+
def calculate_token_costs(self, meta_holder: MetaHolder,
|
|
71
|
+
model_info_map: Dict[str, Dict[str, float]]) -> TokenUsageStats:
|
|
72
|
+
"""
|
|
73
|
+
计算token统计和成本
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
meta_holder: MetaHolder实例
|
|
77
|
+
model_info_map: 模型价格信息字典
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
TokenUsageStats: Token使用统计数据对象
|
|
81
|
+
"""
|
|
82
|
+
stats = TokenUsageStats()
|
|
83
|
+
|
|
84
|
+
if meta_holder.get_meta():
|
|
85
|
+
meta_dict = meta_holder.get_meta()
|
|
86
|
+
stats.input_tokens = meta_dict.get("input_tokens_count", 0)
|
|
87
|
+
stats.output_tokens = meta_dict.get("generated_tokens_count", 0)
|
|
88
|
+
|
|
89
|
+
for name, info in model_info_map.items():
|
|
90
|
+
stats.input_cost += (stats.input_tokens *
|
|
91
|
+
info.get("input_price", 0.0)) / 1000000
|
|
92
|
+
stats.output_cost += (stats.output_tokens *
|
|
93
|
+
info.get("output_price", 0.0)) / 1000000
|
|
94
|
+
|
|
95
|
+
return stats
|
|
96
|
+
|
|
97
|
+
def log_event(self,
|
|
98
|
+
model_name: str,
|
|
99
|
+
operation_name: str,
|
|
100
|
+
start_time: float,
|
|
101
|
+
end_time: float,
|
|
102
|
+
stats: TokenUsageStats,
|
|
103
|
+
event_file: Optional[str] = None):
|
|
104
|
+
"""
|
|
105
|
+
记录token统计信息到日志和事件系统
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
model_name: 模型名称
|
|
109
|
+
operation_name: 操作名称,用于日志记录
|
|
110
|
+
start_time: 开始时间
|
|
111
|
+
end_time: 结束时间
|
|
112
|
+
stats: Token使用统计数据
|
|
113
|
+
event_file: 事件文件路径,如果提供则写入事件
|
|
114
|
+
"""
|
|
115
|
+
elapsed_time = end_time - start_time
|
|
116
|
+
|
|
117
|
+
# 计算token生成速度(每秒生成的token数)
|
|
118
|
+
speed = stats.output_tokens / elapsed_time if elapsed_time > 0 else 0
|
|
119
|
+
|
|
120
|
+
# 记录日志
|
|
121
|
+
self.logger.info(f"{operation_name} Stats - Model: {model_name}, "
|
|
122
|
+
f"Input Tokens: {stats.input_tokens}, "
|
|
123
|
+
f"Output Tokens: {stats.output_tokens}, "
|
|
124
|
+
f"Total Tokens: {stats.total_tokens}, "
|
|
125
|
+
f"Time: {elapsed_time:.2f}s, "
|
|
126
|
+
f"Speed: {speed:.2f}tokens/s, "
|
|
127
|
+
f"Total Cost: ${stats.total_cost:.6f}")
|
|
128
|
+
|
|
129
|
+
# 记录事件(如果提供了event_file)
|
|
130
|
+
try:
|
|
131
|
+
if event_file:
|
|
132
|
+
get_event_manager(event_file).write_result(
|
|
133
|
+
EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
|
|
134
|
+
model_name=model_name,
|
|
135
|
+
elapsed_time=elapsed_time,
|
|
136
|
+
first_token_time=0, # 不跟踪首个token时间
|
|
137
|
+
input_tokens=stats.input_tokens,
|
|
138
|
+
output_tokens=stats.output_tokens,
|
|
139
|
+
input_cost=stats.input_cost,
|
|
140
|
+
output_cost=stats.output_cost,
|
|
141
|
+
speed=speed
|
|
142
|
+
).to_dict()))
|
|
143
|
+
else:
|
|
144
|
+
# 尝试使用默认事件管理器
|
|
145
|
+
get_event_manager().write_result(
|
|
146
|
+
EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
|
|
147
|
+
model_name=model_name,
|
|
148
|
+
elapsed_time=elapsed_time,
|
|
149
|
+
input_tokens=stats.input_tokens,
|
|
150
|
+
output_tokens=stats.output_tokens,
|
|
151
|
+
input_cost=stats.input_cost,
|
|
152
|
+
output_cost=stats.output_cost,
|
|
153
|
+
speed=speed
|
|
154
|
+
).to_dict()))
|
|
155
|
+
except Exception as e:
|
|
156
|
+
self.logger.error(f"Error writing event for {operation_name}: {e}")
|
|
157
|
+
|
|
158
|
+
def track_token_usage(self,
|
|
159
|
+
llm: byzerllm.ByzerLLM,
|
|
160
|
+
meta_holder: MetaHolder,
|
|
161
|
+
operation_name: str,
|
|
162
|
+
start_time: float,
|
|
163
|
+
end_time: float,
|
|
164
|
+
product_mode: Optional[str] = None,
|
|
165
|
+
event_file: Optional[str] = None) -> TokenUsageStats:
|
|
166
|
+
"""
|
|
167
|
+
跟踪token使用情况的便捷方法,整合了获取模型信息、计算成本和记录统计的功能
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
llm: ByzerLLM实例
|
|
171
|
+
meta_holder: MetaHolder实例
|
|
172
|
+
operation_name: 操作名称
|
|
173
|
+
start_time: 开始时间
|
|
174
|
+
end_time: 结束时间
|
|
175
|
+
product_mode: 产品模式,如果不提供则使用实例的product_mode
|
|
176
|
+
event_file: 事件文件路径
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
TokenUsageStats: Token使用统计数据对象
|
|
180
|
+
"""
|
|
181
|
+
# 使用传入的product_mode或实例的product_mode
|
|
182
|
+
actual_product_mode = product_mode
|
|
183
|
+
|
|
184
|
+
# 获取模型信息
|
|
185
|
+
model_name, model_info_map = self.get_model_info(llm,actual_product_mode)
|
|
186
|
+
|
|
187
|
+
# 计算token统计和成本
|
|
188
|
+
stats = self.calculate_token_costs(meta_holder, model_info_map)
|
|
189
|
+
|
|
190
|
+
# 记录事件
|
|
191
|
+
self.log_event(
|
|
192
|
+
model_name=model_name,
|
|
193
|
+
operation_name=operation_name,
|
|
194
|
+
start_time=start_time,
|
|
195
|
+
end_time=end_time,
|
|
196
|
+
stats=stats,
|
|
197
|
+
event_file=event_file
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return stats
|