pro-craft 0.1.56__py3-none-any.whl → 0.2.58__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 pro-craft might be problematic. Click here for more details.
- pro_craft/__init__.py +2 -10
- pro_craft/code_helper/__init__.py +0 -0
- pro_craft/code_helper/agent.py +90 -0
- pro_craft/code_helper/codermanager.py +143 -0
- pro_craft/code_helper/database.py +36 -0
- pro_craft/code_helper/paper_program.py +183 -0
- pro_craft/code_helper/template_extract.py +134 -0
- pro_craft/code_helper/tools.py +113 -0
- pro_craft/code_helper/vectorstore.py +81 -0
- pro_craft/code_helper/write_code.py +61 -0
- pro_craft/database.py +30 -90
- pro_craft/log.py +63 -33
- pro_craft/prompt_craft/async_.py +190 -401
- pro_craft/server/router/prompt.py +19 -0
- pro_craft/utils.py +1 -1
- {pro_craft-0.1.56.dist-info → pro_craft-0.2.58.dist-info}/METADATA +7 -1
- pro_craft-0.2.58.dist-info/RECORD +28 -0
- pro_craft/code_helper/coder.py +0 -660
- pro_craft/code_helper/designer.py +0 -115
- pro_craft-0.1.56.dist-info/RECORD +0 -21
- {pro_craft-0.1.56.dist-info → pro_craft-0.2.58.dist-info}/WHEEL +0 -0
- {pro_craft-0.1.56.dist-info → pro_craft-0.2.58.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from .codermanager import CoderTemplateManager
|
|
2
|
+
from typing import List, Dict, Any,Optional
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
coder = CoderTemplateManager()
|
|
6
|
+
|
|
7
|
+
# --- 工具函数实现 ---
|
|
8
|
+
|
|
9
|
+
def search_template_by_text(query: str, top_k: int = 5) -> List[Dict[str, Any]]:
|
|
10
|
+
"""
|
|
11
|
+
根据自然语言查询在模板库中进行语义搜索(使用 Qdrant 向量检索),返回最相关的 `top_k` 个模板的摘要信息。
|
|
12
|
+
"""
|
|
13
|
+
print(f"input & {type(query)} & query: {query} top:k {top_k} ")
|
|
14
|
+
search_result = coder.search(
|
|
15
|
+
text=query,
|
|
16
|
+
limit=top_k,
|
|
17
|
+
# query_filter=None # 可以在这里添加额外的过滤条件,例如根据语言、框架过滤
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
templates_summary = []
|
|
21
|
+
for hit in search_result:
|
|
22
|
+
# 在实际 Qdrant 中,hit.id 是模板的ID,hit.payload 包含其他元数据
|
|
23
|
+
# 假设你的 Qdrant payload 中存储了 template_name 和 description
|
|
24
|
+
templates_summary.append({
|
|
25
|
+
'template_id': hit.payload.get("template_id"),
|
|
26
|
+
'description': hit.payload.get('description', 'No description provided.'),
|
|
27
|
+
'match_score': hit.score
|
|
28
|
+
})
|
|
29
|
+
print(f"output & {type(templates_summary)} & {templates_summary} ")
|
|
30
|
+
return templates_summary
|
|
31
|
+
|
|
32
|
+
def get_template_details(template_id: str) -> Optional[Dict[str, Any]]: # template_id 根据你的模型是 Integer
|
|
33
|
+
"""
|
|
34
|
+
根据 `template_id` 从数据库中获取模板的完整详细信息,包括模板代码、推断出的命名约定和使用建议。
|
|
35
|
+
"""
|
|
36
|
+
print(f"input & {type(template_id)} & query: {template_id} ")
|
|
37
|
+
template = coder.get_template_obj(template_id = template_id)
|
|
38
|
+
if template:
|
|
39
|
+
return {
|
|
40
|
+
'template_id': template.template_id,
|
|
41
|
+
'description': template.description,
|
|
42
|
+
'template_code': template.template_code,
|
|
43
|
+
'version': template.version,
|
|
44
|
+
}
|
|
45
|
+
print(f"output & {type(template)} & {template} ")
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def ask_user_for_clarification(question: str) -> str:
|
|
50
|
+
"""
|
|
51
|
+
当你对用户需求有疑问,或需要用户做选择(例如在多个匹配模板中选择一个)时,使用此工具向用户提问。
|
|
52
|
+
"""
|
|
53
|
+
print(f"input & {type(question)} & query: {question} ")
|
|
54
|
+
print("\n--- Agent 需要你的帮助 ---")
|
|
55
|
+
print(f"Agent: {question}")
|
|
56
|
+
user_input = input("你的回答: ")
|
|
57
|
+
print("-------------------------\n")
|
|
58
|
+
print(f"output & {type(user_input)} & query: {user_input} ")
|
|
59
|
+
return user_input
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def generate_request_file(template_code: str, user_request_details: Dict[str, Any], naming_conventions: Optional[Dict[str, Any]] = None) -> str:
|
|
63
|
+
"""
|
|
64
|
+
根据选定的模板代码、解析后的用户需求(结构化形式)和模板的命名约定,生成符合 `REQUEST_START/END` 格式的指令文件。
|
|
65
|
+
`user_request_details` 应该是一个字典,键是 BLOCK/PLACEHOLDER 的名称,值是包含 '指令' 和 '约束/示例' 的字典。
|
|
66
|
+
"""
|
|
67
|
+
print(f"input & {type(template_code)} & template_code: {template_code} user_request_details: {user_request_details} naming_conventions: {naming_conventions}")
|
|
68
|
+
|
|
69
|
+
request_parts = []
|
|
70
|
+
|
|
71
|
+
request_parts.append("--- REQUEST_START ---")
|
|
72
|
+
request_parts.append("template.py # 目标文件通常是这个,可以根据实际情况调整") # 假设一个通用文件名
|
|
73
|
+
|
|
74
|
+
# 添加整体目标描述
|
|
75
|
+
overall_goal = user_request_details.get("overall_goal", "完善代码模板以满足以下需求。")
|
|
76
|
+
request_parts.append(f"\n**目标:** {overall_goal}\n")
|
|
77
|
+
|
|
78
|
+
# 添加命名约定 (如果提供了)
|
|
79
|
+
if naming_conventions:
|
|
80
|
+
request_parts.append("**命名约定:**")
|
|
81
|
+
for key, value in naming_conventions.items():
|
|
82
|
+
request_parts.append(f"* **{key}:** {value}")
|
|
83
|
+
request_parts.append("") # 空行
|
|
84
|
+
|
|
85
|
+
request_parts.append("**具体修改点:**\n")
|
|
86
|
+
|
|
87
|
+
# 遍历模板代码,找到所有的 BLOCK 和 PLACEHOLDER
|
|
88
|
+
# 然后根据 user_request_details 填充指令
|
|
89
|
+
# 这是一个简化版本,实际可能需要更复杂的解析器来处理嵌套块或动态生成的块
|
|
90
|
+
# 对于 MVP,我们可以假设 user_request_details 中包含了所有需要填充的块/占位符
|
|
91
|
+
block_pattern = r"(BLOCK_START|PLACEHOLDER):\s*(\w+)"
|
|
92
|
+
for match in re.finditer(block_pattern, template_code):
|
|
93
|
+
block_type = match.group(1)
|
|
94
|
+
block_name = match.group(2)
|
|
95
|
+
|
|
96
|
+
if block_name in user_request_details:
|
|
97
|
+
details = user_request_details[block_name]
|
|
98
|
+
instruction = details.get("指令", "")
|
|
99
|
+
constraint_example = details.get("约束/示例", "")
|
|
100
|
+
|
|
101
|
+
request_parts.append(f"* **{block_type}: {block_name}**")
|
|
102
|
+
request_parts.append(f" * **指令:** {instruction}")
|
|
103
|
+
if constraint_example:
|
|
104
|
+
# 确保多行约束/示例能正确缩进
|
|
105
|
+
formatted_ce = "\n".join([f" * **约束/示例:** {line}" if i == 0 else f" * {line}" for i, line in enumerate(str(constraint_example).splitlines())])
|
|
106
|
+
request_parts.append(formatted_ce)
|
|
107
|
+
request_parts.append("") # 空行
|
|
108
|
+
|
|
109
|
+
request_parts.append("--- REQUEST_END ---")
|
|
110
|
+
print(f"output & {type(request_parts)} & request_parts: {request_parts} ")
|
|
111
|
+
|
|
112
|
+
return "\n".join(request_parts)
|
|
113
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# --- 假设的依赖项 ---
|
|
2
|
+
# Qdrant 客户端
|
|
3
|
+
from qdrant_client import QdrantClient, models # 你需要根据实际情况导入 Qdrant 客户端
|
|
4
|
+
qdrant_client = QdrantClient(host="localhost", port=6333) # 示例Qdrant客户端初始化
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from typing import Any, List
|
|
8
|
+
from llama_index.core.bridge.pydantic import PrivateAttr
|
|
9
|
+
from llama_index.core.embeddings import BaseEmbedding
|
|
10
|
+
from volcenginesdkarkruntime import Ark
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class VolcanoEmbedding(BaseEmbedding):
|
|
14
|
+
_model = PrivateAttr()
|
|
15
|
+
_ark_client = PrivateAttr()
|
|
16
|
+
_encoding_format = PrivateAttr()
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
model_name: str = "doubao-embedding-text-240715",
|
|
21
|
+
api_key: str = "",
|
|
22
|
+
**kwargs: Any,
|
|
23
|
+
) -> None:
|
|
24
|
+
super().__init__(**kwargs)
|
|
25
|
+
self._ark_client = Ark(api_key=api_key)
|
|
26
|
+
self._model = model_name
|
|
27
|
+
self._encoding_format = "float"
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def class_name(cls) -> str:
|
|
31
|
+
return "ark"
|
|
32
|
+
|
|
33
|
+
def _get_query_embedding(self, query: str) -> List[float]:
|
|
34
|
+
"""
|
|
35
|
+
获取查询字符串的 embedding。
|
|
36
|
+
通常查询和文档使用相同的 embedding 模型。
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
resp = self._ark_client.embeddings.create(
|
|
40
|
+
model=self._model,
|
|
41
|
+
input=[query],
|
|
42
|
+
encoding_format=self._encoding_format,
|
|
43
|
+
)
|
|
44
|
+
return resp.data[0].embedding
|
|
45
|
+
|
|
46
|
+
def _get_text_embedding(self, text: str) -> List[float]:
|
|
47
|
+
"""
|
|
48
|
+
获取单个文档字符串的 embedding。
|
|
49
|
+
"""
|
|
50
|
+
resp = self._ark_client.embeddings.create(
|
|
51
|
+
model=self._model,
|
|
52
|
+
input=[text],
|
|
53
|
+
encoding_format=self._encoding_format,
|
|
54
|
+
)
|
|
55
|
+
return resp.data[0].embedding
|
|
56
|
+
|
|
57
|
+
def _get_text_embeddings(self, texts: List[str]) -> List[List[float]]:
|
|
58
|
+
"""
|
|
59
|
+
批量获取文档字符串的 embedding。
|
|
60
|
+
如果你的火山模型支持批量推理,强烈建议实现此方法以提高效率。
|
|
61
|
+
否则,它可以简单地循环调用 _get_text_embedding。
|
|
62
|
+
"""
|
|
63
|
+
resp = self._ark_client.embeddings.create(
|
|
64
|
+
model=self._model,
|
|
65
|
+
input=texts,
|
|
66
|
+
encoding_format=self._encoding_format,
|
|
67
|
+
)
|
|
68
|
+
return [i.embedding for i in resp.data]
|
|
69
|
+
|
|
70
|
+
async def _aget_query_embedding(self, query: str) -> List[float]:
|
|
71
|
+
return self._get_query_embedding(query)
|
|
72
|
+
|
|
73
|
+
async def _aget_text_embedding(self, text: str) -> List[float]:
|
|
74
|
+
return self._get_text_embedding(text)
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
|
|
78
|
+
embedding_model = VolcanoEmbedding(
|
|
79
|
+
model_name = "doubao-embedding-text-240715",
|
|
80
|
+
api_key = "39ad310a-c6f7-4d66-962e-1fbfa7e6edf1"
|
|
81
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
llm2 = """
|
|
2
|
+
你是一个专业的Python代码修改和生成AI。你的任务是根据用户提供的代码模板文件和详细的修改指令文件,精确地对模板进行补充、完善和修改。
|
|
3
|
+
|
|
4
|
+
**你的目标是:**
|
|
5
|
+
1. **严格遵循指令文件中的所有要求,尤其是针对特定 `BLOCK` 和 `PLACEHOLDER` 的指令和约束/示例。**
|
|
6
|
+
2. **尽可能应用指令文件中提供的命名约定。**
|
|
7
|
+
3. **仅修改指令明确要求修改的部分,模板中未被指令覆盖的固定部分必须保持不变。**
|
|
8
|
+
4. **最终输出完整且可运行的Python代码。**
|
|
9
|
+
|
|
10
|
+
**输入格式:**
|
|
11
|
+
用户将以以下两个部分向你提供信息:
|
|
12
|
+
|
|
13
|
+
--- TEMPLATE_CODE_START ---
|
|
14
|
+
[原始的代码模板内容,其中包含 BLOCK_START/END 和 PLACEHOLDER/END_PLACEHOLDER 标记]
|
|
15
|
+
--- TEMPLATE_CODE_END ---
|
|
16
|
+
|
|
17
|
+
--- REQUEST_FILE_START ---
|
|
18
|
+
[一个结构化的指令文件,格式为 REQUEST_START/END,包含目标、命名约定和具体修改点]
|
|
19
|
+
--- REQUEST_FILE_END ---
|
|
20
|
+
|
|
21
|
+
**你的工作流程和生成原则:**
|
|
22
|
+
|
|
23
|
+
1. **解析指令文件:**
|
|
24
|
+
* 首先解析 `REQUEST_FILE_START` 中的所有内容,理解其 `目标`、`命名约定` 和 `具体修改点`。
|
|
25
|
+
* 将 `具体修改点` 中的每个 `BLOCK` 和 `PLACEHOLDER` 指令及其 `约束/示例` 映射到模板代码中的对应位置。
|
|
26
|
+
2. **处理模板代码:**
|
|
27
|
+
* 逐行读取 `TEMPLATE_CODE_START` 中的模板代码。
|
|
28
|
+
* 当遇到 `BLOCK_START` 或 `PLACEHOLDER` 标记时:
|
|
29
|
+
* 查找指令文件中对应 `块名称` 的修改指令。
|
|
30
|
+
* **如果存在指令:**
|
|
31
|
+
* 删除 `BLOCK_START` 和 `BLOCK_END` (或 `PLACEHOLDER` 和 `END_PLACEHOLDER`) 及其内部的原始内容(包括 `AI:` 注释)。
|
|
32
|
+
* 用指令中提供的代码**替换**该区域。
|
|
33
|
+
* 在替换的代码块的开始和结束位置,添加特殊的标记 `// AI_MODIFIED_START` 和 `// AI_MODIFIED_END` (如果只是新增内容,可以使用 `// AI_ADDED_START` 和 `// AI_ADDED_END`)。
|
|
34
|
+
* 如果指令是要求删除某些内容,请用 `// AI_DELETED_LINE: [原始行内容]` 标记被删除的行。
|
|
35
|
+
* **如果不存在指令:**
|
|
36
|
+
* 保留该 `BLOCK` 或 `PLACEHOLDER` 及其内部的原始内容(包括 `AI:` 注释和标记本身),不做任何改动。这允许模板中的可选部分在没有明确指令时保持原样。
|
|
37
|
+
* 当遇到非标记的普通代码行时,保持其不变。
|
|
38
|
+
3. **应用命名约定:**
|
|
39
|
+
* 在生成或修改代码时,优先应用 `REQUEST_FILE_START` 中提供的 `命名约定`。
|
|
40
|
+
* **重要:** 命名约定只应影响由你**生成或修改**的代码部分(即 `AI_ADDED` 或 `AI_MODIFIED` 区域)。你不能随意修改模板中未被明确指令触及的固定代码部分的命名。
|
|
41
|
+
4. **生成中间输出:**
|
|
42
|
+
* 首先生成包含所有 `// AI_ADDED/MODIFIED/DELETED` 标记的完整代码。这有助于后续的自动化工具进行变更追踪和人工核查。
|
|
43
|
+
5. **生成最终输出:**
|
|
44
|
+
* 在生成中间输出后,进一步处理该代码,**移除所有 `// AI_ADDED/MODIFIED/DELETED` 类型的标记**。
|
|
45
|
+
* 移除所有模板中遗留的 `BLOCK_START/END` 和 `PLACEHOLDER/END_PLACEHOLDER` 标记。
|
|
46
|
+
* 保留所有的 Docstrings 和常规的代码注释。
|
|
47
|
+
|
|
48
|
+
**你的输出必须是最终的、清理后的完整 Python 代码文件内容。**
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
from modusched.core import BianXieAdapter
|
|
53
|
+
bx = BianXieAdapter()
|
|
54
|
+
from pro_craft.utils import extract_
|
|
55
|
+
def write_code(request):
|
|
56
|
+
result = bx.product(llm2 + request)
|
|
57
|
+
print(result)
|
|
58
|
+
python_code = extract_(result,r"python")
|
|
59
|
+
with open("output.py","w",encoding="utf-8") as f:
|
|
60
|
+
f.write(python_code)
|
|
61
|
+
return "success" + " output.py generated."
|
pro_craft/database.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
from sqlalchemy import Column, Integer, String, Text, DateTime, text, UniqueConstraint, Boolean, func, Float
|
|
2
|
+
from sqlalchemy import Column, Integer, String, Text, DateTime, text, UniqueConstraint, Boolean, func, Float, Double
|
|
3
3
|
from sqlalchemy.orm import declarative_base
|
|
4
4
|
|
|
5
5
|
from datetime import datetime, timedelta
|
|
@@ -179,13 +179,10 @@ class Prompt(PromptBase):
|
|
|
179
179
|
class UseCase(PromptBase):
|
|
180
180
|
__tablename__ = 'ai_usecase' # 数据库中的表名,你可以改成你希望的名字
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
# id (int, primary_key=True, autoincrement=True)
|
|
188
|
-
# 你的属性表中 id 为 int, true (not null), true (primary key), 0 (length), ASC (key order), true (auto increment)
|
|
182
|
+
__table_args__ = (
|
|
183
|
+
UniqueConstraint('time',name='time_double_uc'),
|
|
184
|
+
# 'name' 参数是可选的,用于给数据库中的约束指定一个名称,方便管理和调试
|
|
185
|
+
)
|
|
189
186
|
id = Column(
|
|
190
187
|
Integer,
|
|
191
188
|
primary_key=True,
|
|
@@ -196,19 +193,16 @@ class UseCase(PromptBase):
|
|
|
196
193
|
|
|
197
194
|
# prompt_id (varchar 255, not null, unique)
|
|
198
195
|
# 你的属性表中 prompt_id 为 varchar, 255 (length), true (not null)
|
|
199
|
-
|
|
196
|
+
|
|
197
|
+
level = Column(
|
|
200
198
|
String(255), # VARCHAR 类型,长度 255
|
|
201
199
|
nullable=False, # 不能为空 # 必须是唯一的,这会创建唯一索引
|
|
202
|
-
comment="
|
|
200
|
+
comment="level"
|
|
203
201
|
)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
use_case = Column(
|
|
209
|
-
Text, # TEXT 类型,适用于长文本
|
|
210
|
-
nullable=False, # 不能为空
|
|
211
|
-
comment="用例"
|
|
202
|
+
time = Column(
|
|
203
|
+
Double,
|
|
204
|
+
nullable=False, # 不能为空
|
|
205
|
+
comment="时间戳"
|
|
212
206
|
)
|
|
213
207
|
|
|
214
208
|
timestamp = Column(
|
|
@@ -219,101 +213,47 @@ class UseCase(PromptBase):
|
|
|
219
213
|
comment="时间戳"
|
|
220
214
|
)
|
|
221
215
|
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
filepath = Column(
|
|
217
|
+
String(255), # TEXT 类型,适用于长文本
|
|
224
218
|
nullable=False, # 不能为空
|
|
225
|
-
comment="
|
|
219
|
+
comment="文件路径"
|
|
226
220
|
)
|
|
227
221
|
|
|
228
|
-
# use_case (text, nullable)
|
|
229
|
-
# 你的属性表中 use_case 为 text, false (not null, 即 nullable=True), NULL (default value), '用例' (comment)
|
|
230
|
-
solution = Column(
|
|
231
|
-
Text,
|
|
232
|
-
nullable=True, # 可以为空 (因为你的表格中 Not Null 为 false)
|
|
233
|
-
comment="理想结果" # 列注释
|
|
234
|
-
)
|
|
235
222
|
|
|
236
|
-
|
|
237
|
-
|
|
223
|
+
function = Column(
|
|
224
|
+
String(255), # TEXT 类型,适用于长文本
|
|
238
225
|
nullable=False, # 不能为空
|
|
239
|
-
comment="
|
|
226
|
+
comment="函数"
|
|
240
227
|
)
|
|
241
228
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
# 定义 __repr__ 方法以便打印对象时有清晰的表示
|
|
245
|
-
def __repr__(self):
|
|
246
|
-
return (f"<Prompt(id={self.id}, prompt_id='{self.prompt_id}', "
|
|
247
|
-
f"use_case='{self.use_case[:50]}...', solution='{self.solution}')>"
|
|
248
|
-
f"is_deleted='{self.is_deleted}...'>")
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
class EvalsInfo(PromptBase):
|
|
252
|
-
__tablename__ = 'ai_evals' # 数据库中的表名,你可以改成你希望的名字
|
|
253
|
-
|
|
254
|
-
# __table_args__ = (
|
|
255
|
-
# UniqueConstraint('prompt_id', 'use_case', name='_prompt_id_version_uc'),
|
|
256
|
-
# # 'name' 参数是可选的,用于给数据库中的约束指定一个名称,方便管理和调试
|
|
257
|
-
# )
|
|
258
|
-
|
|
259
|
-
# id (int, primary_key=True, autoincrement=True)
|
|
260
|
-
# 你的属性表中 id 为 int, true (not null), true (primary key), 0 (length), ASC (key order), true (auto increment)
|
|
261
|
-
id = Column(
|
|
262
|
-
Integer,
|
|
263
|
-
primary_key=True,
|
|
264
|
-
autoincrement=True, # 自动递增
|
|
229
|
+
lines = Column(
|
|
230
|
+
String(255), # TEXT 类型,适用于长文本
|
|
265
231
|
nullable=False, # 不能为空
|
|
266
|
-
comment="
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
# prompt_id (varchar 255, not null, unique)
|
|
270
|
-
# 你的属性表中 prompt_id 为 varchar, 255 (length), true (not null)
|
|
271
|
-
prompt_id = Column(
|
|
272
|
-
String(255), # VARCHAR 类型,长度 255
|
|
273
|
-
nullable=False, # 不能为空 # 必须是唯一的,这会创建唯一索引
|
|
274
|
-
comment="Unique identifier for the prompt"
|
|
275
|
-
)
|
|
276
|
-
status = Column(
|
|
277
|
-
String(255), # VARCHAR 类型,长度 255
|
|
278
|
-
nullable=False, # 不能为空 # 必须是唯一的,这会创建唯一索引
|
|
279
|
-
comment="通过/未通过"
|
|
232
|
+
comment="函数"
|
|
280
233
|
)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
Float,
|
|
234
|
+
target = Column(
|
|
235
|
+
Text, # TEXT 类型,适用于长文本
|
|
284
236
|
nullable=False, # 不能为空
|
|
285
|
-
comment="
|
|
237
|
+
comment="标签"
|
|
286
238
|
)
|
|
287
239
|
|
|
288
|
-
|
|
289
|
-
|
|
240
|
+
type_ = Column(
|
|
241
|
+
String(255), # TEXT 类型,适用于长文本
|
|
290
242
|
nullable=False, # 不能为空
|
|
291
|
-
comment="
|
|
243
|
+
comment="函数"
|
|
292
244
|
)
|
|
293
245
|
|
|
294
|
-
|
|
295
|
-
# 你的属性表中 prompt 为 text, true (not null)
|
|
296
|
-
bad_case = Column(
|
|
246
|
+
content = Column(
|
|
297
247
|
Text, # TEXT 类型,适用于长文本
|
|
298
248
|
nullable=False, # 不能为空
|
|
299
|
-
comment="
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
timestamp = Column(
|
|
303
|
-
DateTime,
|
|
304
|
-
nullable=False, # 不能为空
|
|
305
|
-
server_default=text('CURRENT_TIMESTAMP'),
|
|
306
|
-
onupdate=text('CURRENT_TIMESTAMP'),
|
|
307
|
-
comment="时间戳"
|
|
249
|
+
comment="The actual prompt text content"
|
|
308
250
|
)
|
|
309
251
|
|
|
310
|
-
|
|
311
252
|
is_deleted = Column(Boolean, default=False, server_default=text('0'))
|
|
312
253
|
|
|
313
254
|
# 定义 __repr__ 方法以便打印对象时有清晰的表示
|
|
314
255
|
def __repr__(self):
|
|
315
256
|
return (f"<Prompt(id={self.id}, prompt_id='{self.prompt_id}', "
|
|
316
|
-
f"
|
|
317
|
-
f"total='{self.total}...', score='{self.score}')>"
|
|
257
|
+
f"use_case='{self.use_case[:50]}...', solution='{self.solution}')>"
|
|
318
258
|
f"is_deleted='{self.is_deleted}...'>")
|
|
319
259
|
|
pro_craft/log.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
|
|
4
|
-
|
|
4
|
+
import inspect
|
|
5
5
|
class Log:
|
|
6
6
|
_instance = None
|
|
7
7
|
def __new__(cls, *args, **kwargs):
|
|
@@ -12,18 +12,18 @@ class Log:
|
|
|
12
12
|
def __init__(self, console_level = logging.INFO, log_file_name="app.log"):
|
|
13
13
|
self.Console_LOG_LEVEL = console_level
|
|
14
14
|
self.log_file_name = log_file_name
|
|
15
|
-
os.makedirs("logs", exist_ok=True)
|
|
16
15
|
self.LOG_FILE_PATH = os.path.join("logs", log_file_name)
|
|
16
|
+
os.makedirs(os.path.dirname(self.LOG_FILE_PATH), exist_ok=True)
|
|
17
17
|
self.logger = self.get_logger()
|
|
18
|
-
self.super_log_level = self.logger.critical
|
|
19
18
|
|
|
20
19
|
def get_logger(self):
|
|
21
20
|
logger = logging.getLogger()
|
|
22
|
-
|
|
21
|
+
logger.setLevel(logging.DEBUG)
|
|
23
22
|
if not logger.handlers:
|
|
24
23
|
# --- 4. 配置 Formatter (格式化器) ---
|
|
24
|
+
# 以后有一个标准化的日志要使用logger 而非标的则使用super-log
|
|
25
25
|
formatter = logging.Formatter(
|
|
26
|
-
"%(asctime)s
|
|
26
|
+
"%(asctime)s $ %(created)f $ %(levelname)s $ %(funcName)s $ :%(lineno)d $ %(pathname)s $ %(message)s||"
|
|
27
27
|
)
|
|
28
28
|
# --- 5. 配置 Handler (处理器) ---
|
|
29
29
|
|
|
@@ -33,50 +33,80 @@ class Log:
|
|
|
33
33
|
console_handler.setFormatter(formatter)
|
|
34
34
|
logger.addHandler(console_handler)
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# 5.2 信息数据
|
|
40
|
-
|
|
41
|
-
# RotatingFileHandler: 按文件大小轮转
|
|
42
|
-
# maxBytes: 单个日志文件的最大字节数 (例如 10MB)
|
|
43
|
-
# backupCount: 保留的旧日志文件数量
|
|
44
|
-
file_handler = RotatingFileHandler(
|
|
36
|
+
# 文件系统
|
|
37
|
+
## 主日志本
|
|
38
|
+
file_handler = RotatingFileHandler( # RotatingFileHandler: 按文件大小轮转
|
|
45
39
|
self.LOG_FILE_PATH,
|
|
46
|
-
maxBytes=10 * 1024 * 1024, # 10 MB
|
|
47
|
-
backupCount=
|
|
40
|
+
maxBytes=10 * 1024 * 1024, # 10 MB # maxBytes: 单个日志文件的最大字节数 (例如 10MB)
|
|
41
|
+
backupCount=10, # backupCount: 保留的旧日志文件数量
|
|
48
42
|
encoding="utf-8",
|
|
49
43
|
)
|
|
50
|
-
file_handler.setLevel(logging.
|
|
44
|
+
file_handler.setLevel(logging.DEBUG) # 文件中显示所有指定级别的日志
|
|
51
45
|
file_handler.setFormatter(formatter)
|
|
52
46
|
logger.addHandler(file_handler)
|
|
53
47
|
|
|
54
|
-
|
|
48
|
+
## 运行日志本
|
|
49
|
+
file_handler_info = RotatingFileHandler(
|
|
50
|
+
self.LOG_FILE_PATH.replace('.log','_info.log'),
|
|
51
|
+
maxBytes=10 * 1024 * 1024, # 10 MB
|
|
52
|
+
backupCount=5,
|
|
53
|
+
encoding="utf-8",
|
|
54
|
+
)
|
|
55
|
+
file_handler_info.setLevel(logging.INFO) # 文件中显示所有指定级别的日志
|
|
56
|
+
file_handler_info.setFormatter(formatter)
|
|
57
|
+
logger.addHandler(file_handler_info)
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
## 错误日志本
|
|
60
|
+
file_handler_warning = RotatingFileHandler(
|
|
57
61
|
self.LOG_FILE_PATH.replace('.log','_err.log'),
|
|
58
62
|
maxBytes=10 * 1024 * 1024, # 10 MB
|
|
59
63
|
backupCount=5,
|
|
60
64
|
encoding="utf-8",
|
|
61
65
|
)
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
file_handler_warning.setLevel(logging.WARNING) # 文件中显示所有指定级别的日志
|
|
67
|
+
file_handler_warning.setFormatter(formatter)
|
|
68
|
+
logger.addHandler(file_handler_warning)
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
## 指定日志本
|
|
71
|
+
file_handler_super = RotatingFileHandler(
|
|
72
|
+
self.LOG_FILE_PATH.replace('.log','_s.log'),
|
|
67
73
|
maxBytes=5 * 1024 * 1024, # 10 MB
|
|
68
74
|
backupCount=5,
|
|
69
75
|
encoding="utf-8",
|
|
70
76
|
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
logger.addHandler(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
file_handler_super.setLevel(logging.CRITICAL) # 文件中显示所有指定级别的日志
|
|
78
|
+
file_handler_super.setFormatter(formatter)
|
|
79
|
+
logger.addHandler(file_handler_super)
|
|
80
|
+
|
|
81
|
+
# class ConfigFilter(logging.Filter):
|
|
82
|
+
# # 初始化的方案
|
|
83
|
+
# def __init__(self, config_value, name=''):
|
|
84
|
+
# super().__init__(name)
|
|
85
|
+
# self.config_value = config_value
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
# def filter(self, record):
|
|
88
|
+
# record.config_info = self.config_value # 添加到LogRecord
|
|
89
|
+
# return True
|
|
82
90
|
|
|
91
|
+
|
|
92
|
+
# class CustomFilter(logging.Filter):
|
|
93
|
+
# def filter(self, record):
|
|
94
|
+
# # 添加自定义的用户ID
|
|
95
|
+
# print(record,'recordrecordrecordrecordrecord')
|
|
96
|
+
# record.user_id = os.getenv("CURRENT_USER_ID", "anonymous")
|
|
97
|
+
# # 添加会话ID
|
|
98
|
+
# # record.session_id = "SESSION_XYZ123" # 实际应用中可能从request或全局变量获取
|
|
99
|
+
# frame = inspect.currentframe()
|
|
100
|
+
# info = inspect.getframeinfo(frame)
|
|
101
|
+
# # logger.error(f"Function name: {info.function} : {e}")
|
|
102
|
+
# record.session_id = f"Function name: {info.function}"
|
|
103
|
+
|
|
104
|
+
# return True # 必须返回True才能继续处理该日志记录
|
|
105
|
+
|
|
106
|
+
# # 将自定义过滤器添加到处理器或记录器
|
|
107
|
+
# logger.addFilter(CustomFilter())
|
|
108
|
+
# handler.addFilter(CustomFilter()) # 也可以加到handler上
|
|
109
|
+
|
|
110
|
+
logger.info("这是一个包含自定义信息的日志")
|
|
111
|
+
return logger
|
|
112
|
+
|