This commit is contained in:
UnCLAS-Prommer
2025-07-24 00:32:04 +08:00
13 changed files with 672 additions and 3535 deletions

View File

@@ -1,192 +0,0 @@
import os
import json
from typing import List, Dict, Tuple
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import glob
import sqlite3
import re
from datetime import datetime
def clean_group_name(name: str) -> str:
"""清理群组名称,只保留中文和英文字符"""
cleaned = re.sub(r"[^\u4e00-\u9fa5a-zA-Z]", "", name)
if not cleaned:
cleaned = datetime.now().strftime("%Y%m%d")
return cleaned
def get_group_name(stream_id: str) -> str:
"""从数据库中获取群组名称"""
conn = sqlite3.connect("data/maibot.db")
cursor = conn.cursor()
cursor.execute(
"""
SELECT group_name, user_nickname, platform
FROM chat_streams
WHERE stream_id = ?
""",
(stream_id,),
)
result = cursor.fetchone()
conn.close()
if result:
group_name, user_nickname, platform = result
if group_name:
return clean_group_name(group_name)
if user_nickname:
return clean_group_name(user_nickname)
if platform:
return clean_group_name(f"{platform}{stream_id[:8]}")
return stream_id
def format_timestamp(timestamp: float) -> str:
"""将时间戳转换为可读的时间格式"""
if not timestamp:
return "未知"
try:
dt = datetime.fromtimestamp(timestamp)
return dt.strftime("%Y-%m-%d %H:%M:%S")
except Exception as e:
print(f"时间戳格式化错误: {e}")
return "未知"
def load_expressions(chat_id: str) -> List[Dict]:
"""加载指定群聊的表达方式"""
style_file = os.path.join("data", "expression", "learnt_style", str(chat_id), "expressions.json")
style_exprs = []
if os.path.exists(style_file):
with open(style_file, "r", encoding="utf-8") as f:
style_exprs = json.load(f)
return style_exprs
def find_similar_expressions(expressions: List[Dict], top_k: int = 5) -> Dict[str, List[Tuple[str, float]]]:
"""找出每个表达方式最相似的top_k个表达方式"""
if not expressions:
return {}
# 分别准备情景和表达方式的文本数据
situations = [expr["situation"] for expr in expressions]
styles = [expr["style"] for expr in expressions]
# 使用TF-IDF向量化
vectorizer = TfidfVectorizer()
situation_matrix = vectorizer.fit_transform(situations)
style_matrix = vectorizer.fit_transform(styles)
# 计算余弦相似度
situation_similarity = cosine_similarity(situation_matrix)
style_similarity = cosine_similarity(style_matrix)
# 对每个表达方式找出最相似的top_k个
similar_expressions = {}
for i, _ in enumerate(expressions):
# 获取相似度分数
situation_scores = situation_similarity[i]
style_scores = style_similarity[i]
# 获取top_k的索引排除自己
situation_indices = np.argsort(situation_scores)[::-1][1 : top_k + 1]
style_indices = np.argsort(style_scores)[::-1][1 : top_k + 1]
similar_situations = []
similar_styles = []
# 处理相似情景
for idx in situation_indices:
if situation_scores[idx] > 0: # 只保留有相似度的
similar_situations.append(
(
expressions[idx]["situation"],
expressions[idx]["style"], # 添加对应的原始表达
situation_scores[idx],
)
)
# 处理相似表达
for idx in style_indices:
if style_scores[idx] > 0: # 只保留有相似度的
similar_styles.append(
(
expressions[idx]["style"],
expressions[idx]["situation"], # 添加对应的原始情景
style_scores[idx],
)
)
if similar_situations or similar_styles:
similar_expressions[i] = {"situations": similar_situations, "styles": similar_styles}
return similar_expressions
def main():
# 获取所有群聊ID
style_dirs = glob.glob(os.path.join("data", "expression", "learnt_style", "*"))
chat_ids = [os.path.basename(d) for d in style_dirs]
if not chat_ids:
print("没有找到任何群聊的表达方式数据")
return
print("可用的群聊:")
for i, chat_id in enumerate(chat_ids, 1):
group_name = get_group_name(chat_id)
print(f"{i}. {group_name}")
while True:
try:
choice = int(input("\n请选择要分析的群聊编号 (输入0退出): "))
if choice == 0:
break
if 1 <= choice <= len(chat_ids):
chat_id = chat_ids[choice - 1]
break
print("无效的选择,请重试")
except ValueError:
print("请输入有效的数字")
if choice == 0:
return
# 加载表达方式
style_exprs = load_expressions(chat_id)
group_name = get_group_name(chat_id)
print(f"\n分析群聊 {group_name} 的表达方式:")
similar_styles = find_similar_expressions(style_exprs)
for i, expr in enumerate(style_exprs):
if i in similar_styles:
print("\n" + "-" * 20)
print(f"表达方式:{expr['style']} <---> 情景:{expr['situation']}")
if similar_styles[i]["styles"]:
print("\n\033[33m相似表达\033[0m")
for similar_style, original_situation, score in similar_styles[i]["styles"]:
print(f"\033[33m{similar_style},score:{score:.3f},对应情景:{original_situation}\033[0m")
if similar_styles[i]["situations"]:
print("\n\033[32m相似情景\033[0m")
for similar_situation, original_style, score in similar_styles[i]["situations"]:
print(f"\033[32m{similar_situation},score:{score:.3f},对应表达:{original_style}\033[0m")
print(
f"\n激活值:{expr.get('count', 1):.3f},上次激活时间:{format_timestamp(expr.get('last_active_time'))}"
)
print("-" * 20)
if __name__ == "__main__":
main()

View File

@@ -1,215 +0,0 @@
import os
import json
import time
import re
from datetime import datetime
from typing import Dict, List, Any
import sqlite3
def clean_group_name(name: str) -> str:
"""清理群组名称,只保留中文和英文字符"""
# 提取中文和英文字符
cleaned = re.sub(r"[^\u4e00-\u9fa5a-zA-Z]", "", name)
# 如果清理后为空,使用当前日期
if not cleaned:
cleaned = datetime.now().strftime("%Y%m%d")
return cleaned
def get_group_name(stream_id: str) -> str:
"""从数据库中获取群组名称"""
conn = sqlite3.connect("data/maibot.db")
cursor = conn.cursor()
cursor.execute(
"""
SELECT group_name, user_nickname, platform
FROM chat_streams
WHERE stream_id = ?
""",
(stream_id,),
)
result = cursor.fetchone()
conn.close()
if result:
group_name, user_nickname, platform = result
if group_name:
return clean_group_name(group_name)
if user_nickname:
return clean_group_name(user_nickname)
if platform:
return clean_group_name(f"{platform}{stream_id[:8]}")
return stream_id
def load_expressions(chat_id: str) -> tuple[List[Dict[str, Any]], List[Dict[str, Any]], List[Dict[str, Any]]]:
"""加载指定群组的表达方式"""
learnt_style_file = os.path.join("data", "expression", "learnt_style", str(chat_id), "expressions.json")
learnt_grammar_file = os.path.join("data", "expression", "learnt_grammar", str(chat_id), "expressions.json")
personality_file = os.path.join("data", "expression", "personality", "expressions.json")
style_expressions = []
grammar_expressions = []
personality_expressions = []
if os.path.exists(learnt_style_file):
with open(learnt_style_file, "r", encoding="utf-8") as f:
style_expressions = json.load(f)
if os.path.exists(learnt_grammar_file):
with open(learnt_grammar_file, "r", encoding="utf-8") as f:
grammar_expressions = json.load(f)
if os.path.exists(personality_file):
with open(personality_file, "r", encoding="utf-8") as f:
personality_expressions = json.load(f)
return style_expressions, grammar_expressions, personality_expressions
def format_time(timestamp: float) -> str:
"""格式化时间戳为可读字符串"""
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
def write_expressions(f, expressions: List[Dict[str, Any]], title: str):
"""写入表达方式列表"""
if not expressions:
f.write(f"{title}:暂无数据\n")
f.write("-" * 40 + "\n")
return
f.write(f"{title}\n")
for expr in expressions:
count = expr.get("count", 0)
last_active = expr.get("last_active_time", time.time())
f.write(f"场景: {expr['situation']}\n")
f.write(f"表达: {expr['style']}\n")
f.write(f"计数: {count:.4f}\n")
f.write(f"最后活跃: {format_time(last_active)}\n")
f.write("-" * 40 + "\n")
def write_group_report(
group_file: str,
group_name: str,
chat_id: str,
style_exprs: List[Dict[str, Any]],
grammar_exprs: List[Dict[str, Any]],
):
"""写入群组详细报告"""
with open(group_file, "w", encoding="utf-8") as gf:
gf.write(f"群组: {group_name} (ID: {chat_id})\n")
gf.write("=" * 80 + "\n\n")
# 写入语言风格
gf.write("【语言风格】\n")
gf.write("=" * 40 + "\n")
write_expressions(gf, style_exprs, "语言风格")
gf.write("\n")
# 写入句法特点
gf.write("【句法特点】\n")
gf.write("=" * 40 + "\n")
write_expressions(gf, grammar_exprs, "句法特点")
def analyze_expressions():
"""分析所有群组的表达方式"""
# 获取所有群组ID
style_dir = os.path.join("data", "expression", "learnt_style")
chat_ids = [d for d in os.listdir(style_dir) if os.path.isdir(os.path.join(style_dir, d))]
# 创建输出目录
output_dir = "data/expression_analysis"
personality_dir = os.path.join(output_dir, "personality")
os.makedirs(output_dir, exist_ok=True)
os.makedirs(personality_dir, exist_ok=True)
# 生成时间戳
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# 创建总报告
summary_file = os.path.join(output_dir, f"summary_{timestamp}.txt")
with open(summary_file, "w", encoding="utf-8") as f:
f.write(f"表达方式分析报告 - 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("=" * 80 + "\n\n")
# 先处理人格表达
personality_exprs = []
personality_file = os.path.join("data", "expression", "personality", "expressions.json")
if os.path.exists(personality_file):
with open(personality_file, "r", encoding="utf-8") as pf:
personality_exprs = json.load(pf)
# 保存人格表达总数
total_personality = len(personality_exprs)
# 排序并取前20条
personality_exprs.sort(key=lambda x: x.get("count", 0), reverse=True)
personality_exprs = personality_exprs[:20]
# 写入人格表达报告
personality_report = os.path.join(personality_dir, f"expressions_{timestamp}.txt")
with open(personality_report, "w", encoding="utf-8") as pf:
pf.write("【人格表达方式】\n")
pf.write("=" * 40 + "\n")
write_expressions(pf, personality_exprs, "人格表达")
# 写入总报告摘要中的人格表达部分
f.write("【人格表达方式】\n")
f.write("=" * 40 + "\n")
f.write(f"人格表达总数: {total_personality} (显示前20条)\n")
f.write(f"详细报告: {personality_report}\n")
f.write("-" * 40 + "\n\n")
# 处理各个群组的表达方式
f.write("【群组表达方式】\n")
f.write("=" * 40 + "\n\n")
for chat_id in chat_ids:
style_exprs, grammar_exprs, _ = load_expressions(chat_id)
# 保存总数
total_style = len(style_exprs)
total_grammar = len(grammar_exprs)
# 分别排序
style_exprs.sort(key=lambda x: x.get("count", 0), reverse=True)
grammar_exprs.sort(key=lambda x: x.get("count", 0), reverse=True)
# 只取前20条
style_exprs = style_exprs[:20]
grammar_exprs = grammar_exprs[:20]
# 获取群组名称
group_name = get_group_name(chat_id)
# 创建群组子目录(使用清理后的名称)
safe_group_name = clean_group_name(group_name)
group_dir = os.path.join(output_dir, f"{safe_group_name}_{chat_id}")
os.makedirs(group_dir, exist_ok=True)
# 写入群组详细报告
group_file = os.path.join(group_dir, f"expressions_{timestamp}.txt")
write_group_report(group_file, group_name, chat_id, style_exprs, grammar_exprs)
# 写入总报告摘要
f.write(f"群组: {group_name} (ID: {chat_id})\n")
f.write("-" * 40 + "\n")
f.write(f"语言风格总数: {total_style} (显示前20条)\n")
f.write(f"句法特点总数: {total_grammar} (显示前20条)\n")
f.write(f"详细报告: {group_file}\n")
f.write("-" * 40 + "\n\n")
print("分析报告已生成:")
print(f"总报告: {summary_file}")
print(f"人格表达报告: {personality_report}")
print(f"各群组详细报告位于: {output_dir}")
if __name__ == "__main__":
analyze_expressions()

View File

@@ -1,196 +0,0 @@
import json
from pathlib import Path
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import matplotlib.pyplot as plt
import seaborn as sns
import sqlite3
# 设置中文字体
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] # 使用微软雅黑
plt.rcParams["axes.unicode_minus"] = False # 用来正常显示负号
plt.rcParams["font.family"] = "sans-serif"
# 获取脚本所在目录
SCRIPT_DIR = Path(__file__).parent
def get_group_name(stream_id):
"""从数据库中获取群组名称"""
conn = sqlite3.connect("data/maibot.db")
cursor = conn.cursor()
cursor.execute(
"""
SELECT group_name, user_nickname, platform
FROM chat_streams
WHERE stream_id = ?
""",
(stream_id,),
)
result = cursor.fetchone()
conn.close()
if result:
group_name, user_nickname, platform = result
if group_name:
return group_name
if user_nickname:
return user_nickname
if platform:
return f"{platform}-{stream_id[:8]}"
return stream_id
def load_group_data(group_dir):
"""加载单个群组的数据"""
json_path = Path(group_dir) / "expressions.json"
if not json_path.exists():
return [], [], [], 0
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
situations = []
styles = []
combined = []
total_count = sum(item["count"] for item in data)
for item in data:
count = item["count"]
situations.extend([item["situation"]] * int(count))
styles.extend([item["style"]] * int(count))
combined.extend([f"{item['situation']} {item['style']}"] * int(count))
return situations, styles, combined, total_count
def analyze_group_similarity():
# 获取所有群组目录
base_dir = Path("data/expression/learnt_style")
group_dirs = [d for d in base_dir.iterdir() if d.is_dir()]
# 加载所有群组的数据并过滤
valid_groups = []
valid_names = []
valid_situations = []
valid_styles = []
valid_combined = []
for d in group_dirs:
situations, styles, combined, total_count = load_group_data(d)
if total_count >= 50: # 只保留数据量大于等于50的群组
valid_groups.append(d)
valid_names.append(get_group_name(d.name))
valid_situations.append(" ".join(situations))
valid_styles.append(" ".join(styles))
valid_combined.append(" ".join(combined))
if not valid_groups:
print("没有找到数据量大于等于50的群组")
return
# 创建TF-IDF向量化器
vectorizer = TfidfVectorizer()
# 计算三种相似度矩阵
situation_matrix = cosine_similarity(vectorizer.fit_transform(valid_situations))
style_matrix = cosine_similarity(vectorizer.fit_transform(valid_styles))
combined_matrix = cosine_similarity(vectorizer.fit_transform(valid_combined))
# 对相似度矩阵进行对数变换
log_situation_matrix = np.log10(situation_matrix * 100 + 1) * 10 / np.log10(4)
log_style_matrix = np.log10(style_matrix * 100 + 1) * 10 / np.log10(4)
log_combined_matrix = np.log10(combined_matrix * 100 + 1) * 10 / np.log10(4)
# 创建一个大图,包含三个子图
plt.figure(figsize=(45, 12))
# 场景相似度热力图
plt.subplot(1, 3, 1)
sns.heatmap(
log_situation_matrix,
xticklabels=valid_names,
yticklabels=valid_names,
cmap="YlOrRd",
annot=True,
fmt=".1f",
vmin=0,
vmax=30,
)
plt.title("群组场景相似度热力图 (对数百分比)")
plt.xticks(rotation=45, ha="right")
# 表达方式相似度热力图
plt.subplot(1, 3, 2)
sns.heatmap(
log_style_matrix,
xticklabels=valid_names,
yticklabels=valid_names,
cmap="YlOrRd",
annot=True,
fmt=".1f",
vmin=0,
vmax=30,
)
plt.title("群组表达方式相似度热力图 (对数百分比)")
plt.xticks(rotation=45, ha="right")
# 组合相似度热力图
plt.subplot(1, 3, 3)
sns.heatmap(
log_combined_matrix,
xticklabels=valid_names,
yticklabels=valid_names,
cmap="YlOrRd",
annot=True,
fmt=".1f",
vmin=0,
vmax=30,
)
plt.title("群组场景+表达方式相似度热力图 (对数百分比)")
plt.xticks(rotation=45, ha="right")
plt.tight_layout()
plt.savefig(SCRIPT_DIR / "group_similarity_heatmaps.png", dpi=300, bbox_inches="tight")
plt.close()
# 保存匹配详情到文本文件
with open(SCRIPT_DIR / "group_similarity_details.txt", "w", encoding="utf-8") as f:
f.write("群组相似度详情\n")
f.write("=" * 50 + "\n\n")
for i in range(len(valid_names)):
for j in range(i + 1, len(valid_names)):
if log_combined_matrix[i][j] > 50:
f.write(f"群组1: {valid_names[i]}\n")
f.write(f"群组2: {valid_names[j]}\n")
f.write(f"场景相似度: {situation_matrix[i][j]:.4f}\n")
f.write(f"表达方式相似度: {style_matrix[i][j]:.4f}\n")
f.write(f"组合相似度: {combined_matrix[i][j]:.4f}\n")
# 获取两个群组的数据
situations1, styles1, _ = load_group_data(valid_groups[i])
situations2, styles2, _ = load_group_data(valid_groups[j])
# 找出共同的场景
common_situations = set(situations1) & set(situations2)
if common_situations:
f.write("\n共同场景:\n")
for situation in common_situations:
f.write(f"- {situation}\n")
# 找出共同的表达方式
common_styles = set(styles1) & set(styles2)
if common_styles:
f.write("\n共同表达方式:\n")
for style in common_styles:
f.write(f"- {style}\n")
f.write("\n" + "-" * 50 + "\n\n")
if __name__ == "__main__":
analyze_group_similarity()

View File

@@ -1,252 +0,0 @@
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import json
from typing import List, Dict, Tuple
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import glob
import sqlite3
import re
from datetime import datetime
import random
from src.llm_models.utils_model import LLMRequest
from src.config.config import global_config
def clean_group_name(name: str) -> str:
"""清理群组名称,只保留中文和英文字符"""
cleaned = re.sub(r"[^\u4e00-\u9fa5a-zA-Z]", "", name)
if not cleaned:
cleaned = datetime.now().strftime("%Y%m%d")
return cleaned
def get_group_name(stream_id: str) -> str:
"""从数据库中获取群组名称"""
conn = sqlite3.connect("data/maibot.db")
cursor = conn.cursor()
cursor.execute(
"""
SELECT group_name, user_nickname, platform
FROM chat_streams
WHERE stream_id = ?
""",
(stream_id,),
)
result = cursor.fetchone()
conn.close()
if result:
group_name, user_nickname, platform = result
if group_name:
return clean_group_name(group_name)
if user_nickname:
return clean_group_name(user_nickname)
if platform:
return clean_group_name(f"{platform}{stream_id[:8]}")
return stream_id
def load_expressions(chat_id: str) -> List[Dict]:
"""加载指定群聊的表达方式"""
style_file = os.path.join("data", "expression", "learnt_style", str(chat_id), "expressions.json")
style_exprs = []
if os.path.exists(style_file):
with open(style_file, "r", encoding="utf-8") as f:
style_exprs = json.load(f)
# 如果表达方式超过10个随机选择10个
if len(style_exprs) > 50:
style_exprs = random.sample(style_exprs, 50)
print(f"\n{len(style_exprs)} 个表达方式中随机选择了 10 个进行匹配")
return style_exprs
def find_similar_expressions_tfidf(
input_text: str, expressions: List[Dict], mode: str = "both", top_k: int = 10
) -> List[Tuple[str, str, float]]:
"""使用TF-IDF方法找出与输入文本最相似的top_k个表达方式"""
if not expressions:
return []
# 准备文本数据
if mode == "style":
texts = [expr["style"] for expr in expressions]
elif mode == "situation":
texts = [expr["situation"] for expr in expressions]
else: # both
texts = [f"{expr['situation']} {expr['style']}" for expr in expressions]
texts.append(input_text) # 添加输入文本
# 使用TF-IDF向量化
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(texts)
# 计算余弦相似度
similarity_matrix = cosine_similarity(tfidf_matrix)
# 获取输入文本的相似度分数(最后一行)
scores = similarity_matrix[-1][:-1] # 排除与自身的相似度
# 获取top_k的索引
top_indices = np.argsort(scores)[::-1][:top_k]
# 获取相似表达
similar_exprs = []
for idx in top_indices:
if scores[idx] > 0: # 只保留有相似度的
similar_exprs.append((expressions[idx]["style"], expressions[idx]["situation"], scores[idx]))
return similar_exprs
async def find_similar_expressions_embedding(
input_text: str, expressions: List[Dict], mode: str = "both", top_k: int = 5
) -> List[Tuple[str, str, float]]:
"""使用嵌入模型找出与输入文本最相似的top_k个表达方式"""
if not expressions:
return []
# 准备文本数据
if mode == "style":
texts = [expr["style"] for expr in expressions]
elif mode == "situation":
texts = [expr["situation"] for expr in expressions]
else: # both
texts = [f"{expr['situation']} {expr['style']}" for expr in expressions]
# 获取嵌入向量
llm_request = LLMRequest(global_config.model.embedding)
text_embeddings = []
for text in texts:
embedding = await llm_request.get_embedding(text)
if embedding:
text_embeddings.append(embedding)
input_embedding = await llm_request.get_embedding(input_text)
if not input_embedding or not text_embeddings:
return []
# 计算余弦相似度
text_embeddings = np.array(text_embeddings)
similarities = np.dot(text_embeddings, input_embedding) / (
np.linalg.norm(text_embeddings, axis=1) * np.linalg.norm(input_embedding)
)
# 获取top_k的索引
top_indices = np.argsort(similarities)[::-1][:top_k]
# 获取相似表达
similar_exprs = []
for idx in top_indices:
if similarities[idx] > 0: # 只保留有相似度的
similar_exprs.append((expressions[idx]["style"], expressions[idx]["situation"], similarities[idx]))
return similar_exprs
async def main():
# 获取所有群聊ID
style_dirs = glob.glob(os.path.join("data", "expression", "learnt_style", "*"))
chat_ids = [os.path.basename(d) for d in style_dirs]
if not chat_ids:
print("没有找到任何群聊的表达方式数据")
return
print("可用的群聊:")
for i, chat_id in enumerate(chat_ids, 1):
group_name = get_group_name(chat_id)
print(f"{i}. {group_name}")
while True:
try:
choice = int(input("\n请选择要分析的群聊编号 (输入0退出): "))
if choice == 0:
break
if 1 <= choice <= len(chat_ids):
chat_id = chat_ids[choice - 1]
break
print("无效的选择,请重试")
except ValueError:
print("请输入有效的数字")
if choice == 0:
return
# 加载表达方式
style_exprs = load_expressions(chat_id)
group_name = get_group_name(chat_id)
print(f"\n已选择群聊:{group_name}")
# 选择匹配模式
print("\n请选择匹配模式:")
print("1. 匹配表达方式")
print("2. 匹配情景")
print("3. 两者都考虑")
while True:
try:
mode_choice = int(input("\n请选择匹配模式 (1-3): "))
if 1 <= mode_choice <= 3:
break
print("无效的选择,请重试")
except ValueError:
print("请输入有效的数字")
mode_map = {1: "style", 2: "situation", 3: "both"}
mode = mode_map[mode_choice]
# 选择匹配方法
print("\n请选择匹配方法:")
print("1. TF-IDF方法")
print("2. 嵌入模型方法")
while True:
try:
method_choice = int(input("\n请选择匹配方法 (1-2): "))
if 1 <= method_choice <= 2:
break
print("无效的选择,请重试")
except ValueError:
print("请输入有效的数字")
while True:
input_text = input("\n请输入要匹配的文本输入q退出: ")
if input_text.lower() == "q":
break
if not input_text.strip():
continue
if method_choice == 1:
similar_exprs = find_similar_expressions_tfidf(input_text, style_exprs, mode)
else:
similar_exprs = await find_similar_expressions_embedding(input_text, style_exprs, mode)
if similar_exprs:
print("\n找到以下相似表达:")
for style, situation, score in similar_exprs:
print(f"\n\033[33m表达方式{style}\033[0m")
print(f"\033[32m对应情景{situation}\033[0m")
print(f"相似度:{score:.3f}")
print("-" * 20)
else:
print("\n没有找到相似的表达方式")
if __name__ == "__main__":
import asyncio
asyncio.run(main())

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk, messagebox, filedialog from tkinter import ttk, messagebox, filedialog, colorchooser
import json import json
from pathlib import Path from pathlib import Path
import threading import threading
@@ -8,6 +8,7 @@ from datetime import datetime
from collections import defaultdict from collections import defaultdict
import os import os
import time import time
import queue
class LogIndex: class LogIndex:
@@ -206,6 +207,23 @@ class LogFormatter:
parts.append(str(event)) parts.append(str(event))
tags.append("message") tags.append("message")
# 处理其他字段
extras = []
for key, value in log_entry.items():
if key not in ("timestamp", "level", "logger_name", "event"):
if isinstance(value, (dict, list)):
try:
value_str = json.dumps(value, ensure_ascii=False, indent=None)
except (TypeError, ValueError):
value_str = str(value)
else:
value_str = str(value)
extras.append(f"{key}={value_str}")
if extras:
parts.append(" ".join(extras))
tags.append("extras")
return parts, tags return parts, tags
def format_timestamp(self, timestamp): def format_timestamp(self, timestamp):
@@ -287,6 +305,7 @@ class VirtualLogDisplay:
self.text_widget.tag_configure("level", foreground="#808080") self.text_widget.tag_configure("level", foreground="#808080")
self.text_widget.tag_configure("module", foreground="#808080") self.text_widget.tag_configure("module", foreground="#808080")
self.text_widget.tag_configure("message", foreground="#ffffff") self.text_widget.tag_configure("message", foreground="#ffffff")
self.text_widget.tag_configure("extras", foreground="#808080")
# 日志级别颜色标签 # 日志级别颜色标签
for level, color in self.formatter.level_colors.items(): for level, color in self.formatter.level_colors.items():
@@ -449,7 +468,7 @@ class LogViewer:
self.load_config() self.load_config()
# 初始化日志格式化器 # 初始化日志格式化器
self.formatter = LogFormatter(self.log_config, {}, {}) self.formatter = LogFormatter(self.log_config, self.custom_module_colors, self.custom_level_colors)
# 初始化日志文件路径 # 初始化日志文件路径
self.current_log_file = Path("logs/app.log.jsonl") self.current_log_file = Path("logs/app.log.jsonl")
@@ -467,6 +486,9 @@ class LogViewer:
self.main_frame = ttk.Frame(root) self.main_frame = ttk.Frame(root)
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 创建菜单栏
self.create_menu()
# 创建控制面板 # 创建控制面板
self.create_control_panel() self.create_control_panel()
@@ -477,12 +499,30 @@ class LogViewer:
# 模块名映射 # 模块名映射
self.module_name_mapping = { self.module_name_mapping = {
"api": "API接口", "api": "API接口",
"async_task_manager": "异步任务管理器",
"background_tasks": "后台任务",
"base_tool": "基础工具",
"chat_stream": "聊天流",
"component_registry": "组件注册器",
"config": "配置", "config": "配置",
"chat": "聊天", "database_model": "数据库模型",
"plugin": "插件", "emoji": "表情",
"heartflow": "心流",
"local_storage": "本地存储",
"lpmm": "LPMM",
"maibot_statistic": "MaiBot统计",
"main_message": "主消息",
"main": "主程序", "main": "主程序",
"memory": "内存",
"mood": "情绪",
"plugin_manager": "插件管理器",
"remote": "远程",
"willing": "意愿",
} }
# 加载自定义映射
self.load_module_mapping()
# 选中的模块集合 # 选中的模块集合
self.selected_modules = set() self.selected_modules = set()
self.modules = set() self.modules = set()
@@ -491,19 +531,35 @@ class LogViewer:
self.level_combo.bind("<<ComboboxSelected>>", self.filter_logs) self.level_combo.bind("<<ComboboxSelected>>", self.filter_logs)
self.search_var.trace("w", self.filter_logs) self.search_var.trace("w", self.filter_logs)
# 绑定快捷键
self.root.bind("<Control-o>", lambda e: self.select_log_file())
self.root.bind("<F5>", lambda e: self.refresh_log_file())
self.root.bind("<Control-s>", lambda e: self.export_logs())
# 初始加载文件 # 初始加载文件
if self.current_log_file.exists(): if self.current_log_file.exists():
self.load_log_file_async() self.load_log_file_async()
def load_config(self): def load_config(self):
"""加载配置文件""" """加载配置文件"""
# 默认配置
self.default_config = { self.default_config = {
"log": {"date_style": "m-d H:i:s", "log_level_style": "lite", "color_text": "full"}, "log": {"date_style": "m-d H:i:s", "log_level_style": "lite", "color_text": "full", "log_level": "INFO"},
"viewer": {
"theme": "dark",
"font_size": 10,
"max_lines": 1000,
"auto_scroll": True,
"show_milliseconds": False,
"window": {"width": 1200, "height": 800, "remember_position": True},
},
} }
self.log_config = self.default_config["log"].copy() # 从bot_config.toml加载日志配置
config_path = Path("config/bot_config.toml") config_path = Path("config/bot_config.toml")
self.log_config = self.default_config["log"].copy()
self.viewer_config = self.default_config["viewer"].copy()
try: try:
if config_path.exists(): if config_path.exists():
with open(config_path, "r", encoding="utf-8") as f: with open(config_path, "r", encoding="utf-8") as f:
@@ -511,7 +567,377 @@ class LogViewer:
if "log" in bot_config: if "log" in bot_config:
self.log_config.update(bot_config["log"]) self.log_config.update(bot_config["log"])
except Exception as e: except Exception as e:
print(f"加载配置失败: {e}") print(f"加载bot配置失败: {e}")
# 从viewer配置文件加载查看器配置
viewer_config_path = Path("config/log_viewer_config.toml")
self.custom_module_colors = {}
self.custom_level_colors = {}
try:
if viewer_config_path.exists():
with open(viewer_config_path, "r", encoding="utf-8") as f:
viewer_config = toml.load(f)
if "viewer" in viewer_config:
self.viewer_config.update(viewer_config["viewer"])
# 加载自定义模块颜色
if "module_colors" in viewer_config["viewer"]:
self.custom_module_colors = viewer_config["viewer"]["module_colors"]
# 加载自定义级别颜色
if "level_colors" in viewer_config["viewer"]:
self.custom_level_colors = viewer_config["viewer"]["level_colors"]
if "log" in viewer_config:
self.log_config.update(viewer_config["log"])
except Exception as e:
print(f"加载查看器配置失败: {e}")
# 应用窗口配置
window_config = self.viewer_config.get("window", {})
window_width = window_config.get("width", 1200)
window_height = window_config.get("height", 800)
self.root.geometry(f"{window_width}x{window_height}")
def save_viewer_config(self):
"""保存查看器配置"""
# 准备完整的配置数据
viewer_config_copy = self.viewer_config.copy()
# 保存自定义颜色(只保存与默认值不同的颜色)
if self.custom_module_colors:
viewer_config_copy["module_colors"] = self.custom_module_colors
if self.custom_level_colors:
viewer_config_copy["level_colors"] = self.custom_level_colors
config_data = {"log": self.log_config, "viewer": viewer_config_copy}
config_path = Path("config/log_viewer_config.toml")
config_path.parent.mkdir(exist_ok=True)
try:
with open(config_path, "w", encoding="utf-8") as f:
toml.dump(config_data, f)
except Exception as e:
print(f"保存查看器配置失败: {e}")
def create_menu(self):
"""创建菜单栏"""
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
# 配置菜单
config_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="配置", menu=config_menu)
config_menu.add_command(label="日志格式设置", command=self.show_format_settings)
config_menu.add_command(label="颜色设置", command=self.show_color_settings)
config_menu.add_command(label="查看器设置", command=self.show_viewer_settings)
config_menu.add_separator()
config_menu.add_command(label="重新加载配置", command=self.reload_config)
# 文件菜单
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="选择日志文件", command=self.select_log_file, accelerator="Ctrl+O")
file_menu.add_command(label="刷新当前文件", command=self.refresh_log_file, accelerator="F5")
file_menu.add_separator()
file_menu.add_command(label="导出当前日志", command=self.export_logs, accelerator="Ctrl+S")
# 工具菜单
tools_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="工具", menu=tools_menu)
tools_menu.add_command(label="清空日志显示", command=self.clear_log_display)
def show_format_settings(self):
"""显示格式设置窗口"""
format_window = tk.Toplevel(self.root)
format_window.title("日志格式设置")
format_window.geometry("400x300")
frame = ttk.Frame(format_window)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 日期格式
ttk.Label(frame, text="日期格式:").pack(anchor="w", pady=2)
date_style_var = tk.StringVar(value=self.log_config.get("date_style", "m-d H:i:s"))
date_entry = ttk.Entry(frame, textvariable=date_style_var, width=30)
date_entry.pack(anchor="w", pady=2)
ttk.Label(frame, text="格式说明: Y=年份, m=月份, d=日期, H=小时, i=分钟, s=秒", font=("", 8)).pack(
anchor="w", pady=2
)
# 日志级别样式
ttk.Label(frame, text="日志级别样式:").pack(anchor="w", pady=(10, 2))
level_style_var = tk.StringVar(value=self.log_config.get("log_level_style", "lite"))
level_frame = ttk.Frame(frame)
level_frame.pack(anchor="w", pady=2)
ttk.Radiobutton(level_frame, text="简洁(lite)", variable=level_style_var, value="lite").pack(
side="left", padx=(0, 10)
)
ttk.Radiobutton(level_frame, text="紧凑(compact)", variable=level_style_var, value="compact").pack(
side="left", padx=(0, 10)
)
ttk.Radiobutton(level_frame, text="完整(full)", variable=level_style_var, value="full").pack(
side="left", padx=(0, 10)
)
# 颜色文本设置
ttk.Label(frame, text="文本颜色设置:").pack(anchor="w", pady=(10, 2))
color_text_var = tk.StringVar(value=self.log_config.get("color_text", "full"))
color_frame = ttk.Frame(frame)
color_frame.pack(anchor="w", pady=2)
ttk.Radiobutton(color_frame, text="无颜色(none)", variable=color_text_var, value="none").pack(
side="left", padx=(0, 10)
)
ttk.Radiobutton(color_frame, text="仅标题(title)", variable=color_text_var, value="title").pack(
side="left", padx=(0, 10)
)
ttk.Radiobutton(color_frame, text="全部(full)", variable=color_text_var, value="full").pack(
side="left", padx=(0, 10)
)
# 按钮
button_frame = ttk.Frame(frame)
button_frame.pack(fill="x", pady=(20, 0))
def apply_format():
self.log_config["date_style"] = date_style_var.get()
self.log_config["log_level_style"] = level_style_var.get()
self.log_config["color_text"] = color_text_var.get()
# 重新初始化格式化器
self.formatter = LogFormatter(self.log_config, self.custom_module_colors, self.custom_level_colors)
self.log_display.formatter = self.formatter
self.log_display.configure_text_tags()
# 保存配置
self.save_viewer_config()
# 重新过滤日志以应用新格式
self.filter_logs()
format_window.destroy()
ttk.Button(button_frame, text="应用", command=apply_format).pack(side="right", padx=(5, 0))
ttk.Button(button_frame, text="取消", command=format_window.destroy).pack(side="right")
def show_viewer_settings(self):
"""显示查看器设置窗口"""
viewer_window = tk.Toplevel(self.root)
viewer_window.title("查看器设置")
viewer_window.geometry("350x250")
frame = ttk.Frame(viewer_window)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 主题设置
ttk.Label(frame, text="主题:").pack(anchor="w", pady=2)
theme_var = tk.StringVar(value=self.viewer_config.get("theme", "dark"))
theme_frame = ttk.Frame(frame)
theme_frame.pack(anchor="w", pady=2)
ttk.Radiobutton(theme_frame, text="深色", variable=theme_var, value="dark").pack(side="left", padx=(0, 10))
ttk.Radiobutton(theme_frame, text="浅色", variable=theme_var, value="light").pack(side="left")
# 字体大小
ttk.Label(frame, text="字体大小:").pack(anchor="w", pady=(10, 2))
font_size_var = tk.IntVar(value=self.viewer_config.get("font_size", 10))
font_size_spin = ttk.Spinbox(frame, from_=8, to=20, textvariable=font_size_var, width=10)
font_size_spin.pack(anchor="w", pady=2)
# 最大行数
ttk.Label(frame, text="最大显示行数:").pack(anchor="w", pady=(10, 2))
max_lines_var = tk.IntVar(value=self.viewer_config.get("max_lines", 1000))
max_lines_spin = ttk.Spinbox(frame, from_=100, to=10000, increment=100, textvariable=max_lines_var, width=10)
max_lines_spin.pack(anchor="w", pady=2)
# 自动滚动
auto_scroll_var = tk.BooleanVar(value=self.viewer_config.get("auto_scroll", True))
ttk.Checkbutton(frame, text="自动滚动到底部", variable=auto_scroll_var).pack(anchor="w", pady=(10, 2))
# 按钮
button_frame = ttk.Frame(frame)
button_frame.pack(fill="x", pady=(20, 0))
def apply_viewer_settings():
self.viewer_config["theme"] = theme_var.get()
self.viewer_config["font_size"] = font_size_var.get()
self.viewer_config["max_lines"] = max_lines_var.get()
self.viewer_config["auto_scroll"] = auto_scroll_var.get()
# 应用主题
self.apply_theme()
# 保存配置
self.save_viewer_config()
viewer_window.destroy()
ttk.Button(button_frame, text="应用", command=apply_viewer_settings).pack(side="right", padx=(5, 0))
ttk.Button(button_frame, text="取消", command=viewer_window.destroy).pack(side="right")
def apply_theme(self):
"""应用主题设置"""
theme = self.viewer_config.get("theme", "dark")
font_size = self.viewer_config.get("font_size", 10)
# 更新虚拟显示组件的主题
if theme == "dark":
bg_color = "#1e1e1e"
fg_color = "#ffffff"
select_bg = "#404040"
else:
bg_color = "#ffffff"
fg_color = "#000000"
select_bg = "#c0c0c0"
self.log_display.text_widget.config(
background=bg_color, foreground=fg_color, selectbackground=select_bg, font=("Consolas", font_size)
)
# 重新配置标签样式
self.log_display.configure_text_tags()
def reload_config(self):
"""重新加载配置"""
self.load_config()
self.formatter = LogFormatter(self.log_config, self.custom_module_colors, self.custom_level_colors)
self.log_display.formatter = self.formatter
self.log_display.configure_text_tags()
self.apply_theme()
self.filter_logs()
def clear_log_display(self):
"""清空日志显示"""
self.log_display.text_widget.delete(1.0, tk.END)
def export_logs(self):
"""导出当前显示的日志"""
filename = filedialog.asksaveasfilename(
defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if filename:
try:
# 获取当前显示的所有日志条目
if self.log_index:
filtered_count = self.log_index.get_filtered_count()
log_lines = []
for i in range(filtered_count):
log_entry = self.log_index.get_entry_at_filtered_position(i)
if log_entry:
parts, tags = self.formatter.format_log_entry(log_entry)
line_text = " ".join(parts)
log_lines.append(line_text)
with open(filename, "w", encoding="utf-8") as f:
f.write("\n".join(log_lines))
messagebox.showinfo("导出成功", f"日志已导出到: {filename}")
else:
messagebox.showwarning("导出失败", "没有日志可导出")
except Exception as e:
messagebox.showerror("导出失败", f"导出日志时出错: {e}")
def load_module_mapping(self):
"""加载自定义模块映射"""
mapping_file = Path("config/module_mapping.json")
if mapping_file.exists():
try:
with open(mapping_file, "r", encoding="utf-8") as f:
custom_mapping = json.load(f)
self.module_name_mapping.update(custom_mapping)
except Exception as e:
print(f"加载模块映射失败: {e}")
def save_module_mapping(self):
"""保存自定义模块映射"""
mapping_file = Path("config/module_mapping.json")
mapping_file.parent.mkdir(exist_ok=True)
try:
with open(mapping_file, "w", encoding="utf-8") as f:
json.dump(self.module_name_mapping, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存模块映射失败: {e}")
def show_color_settings(self):
"""显示颜色设置窗口"""
color_window = tk.Toplevel(self.root)
color_window.title("颜色设置")
color_window.geometry("300x400")
# 创建滚动框架
frame = ttk.Frame(color_window)
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 创建滚动条
scrollbar = ttk.Scrollbar(frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 创建颜色设置列表
canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=canvas.yview)
# 创建内部框架
inner_frame = ttk.Frame(canvas)
canvas.create_window((0, 0), window=inner_frame, anchor="nw")
# 添加日志级别颜色设置
ttk.Label(inner_frame, text="日志级别颜色", font=("", 10, "bold")).pack(anchor="w", padx=5, pady=5)
for level in ["info", "warning", "error"]:
frame = ttk.Frame(inner_frame)
frame.pack(fill=tk.X, padx=5, pady=2)
ttk.Label(frame, text=level).pack(side=tk.LEFT)
color_btn = ttk.Button(
frame, text="选择颜色", command=lambda level_name=level: self.choose_color(level_name)
)
color_btn.pack(side=tk.RIGHT)
# 显示当前颜色
color_label = ttk.Label(frame, text="", foreground=self.formatter.level_colors[level])
color_label.pack(side=tk.RIGHT, padx=5)
# 添加模块颜色设置
ttk.Label(inner_frame, text="\n模块颜色", font=("", 10, "bold")).pack(anchor="w", padx=5, pady=5)
for module in sorted(self.modules):
frame = ttk.Frame(inner_frame)
frame.pack(fill=tk.X, padx=5, pady=2)
ttk.Label(frame, text=module).pack(side=tk.LEFT)
color_btn = ttk.Button(frame, text="选择颜色", command=lambda m=module: self.choose_module_color(m))
color_btn.pack(side=tk.RIGHT)
# 显示当前颜色
color = self.formatter.module_colors.get(module, "black")
color_label = ttk.Label(frame, text="", foreground=color)
color_label.pack(side=tk.RIGHT, padx=5)
# 更新画布滚动区域
inner_frame.update_idletasks()
canvas.config(scrollregion=canvas.bbox("all"))
# 添加确定按钮
ttk.Button(color_window, text="确定", command=color_window.destroy).pack(pady=5)
def choose_color(self, level):
"""选择日志级别颜色"""
color = colorchooser.askcolor(color=self.formatter.level_colors[level])[1]
if color:
self.formatter.level_colors[level] = color
self.custom_level_colors[level] = color # 保存到自定义颜色
self.log_display.formatter = self.formatter
self.log_display.configure_text_tags()
self.save_viewer_config() # 自动保存配置
self.filter_logs()
def choose_module_color(self, module):
"""选择模块颜色"""
color = colorchooser.askcolor(color=self.formatter.module_colors.get(module, "black"))[1]
if color:
self.formatter.module_colors[module] = color
self.custom_module_colors[module] = color # 保存到自定义颜色
self.log_display.formatter = self.formatter
self.log_display.configure_text_tags()
self.save_viewer_config() # 自动保存配置
self.filter_logs()
def create_control_panel(self): def create_control_panel(self):
"""创建控制面板""" """创建控制面板"""
@@ -549,30 +975,43 @@ class LogViewer:
side=tk.LEFT, padx=2 side=tk.LEFT, padx=2
) )
# 过滤控制框架 # 模块选择框架
filter_frame = ttk.Frame(self.control_frame) self.module_frame = ttk.LabelFrame(self.control_frame, text="模块")
filter_frame.pack(fill=tk.X, padx=5) self.module_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
# 创建模块选择滚动区域
self.module_canvas = tk.Canvas(self.module_frame, height=80)
self.module_canvas.pack(side=tk.LEFT, fill=tk.X, expand=True)
# 创建模块选择内部框架
self.module_inner_frame = ttk.Frame(self.module_canvas)
self.module_canvas.create_window((0, 0), window=self.module_inner_frame, anchor="nw")
# 创建右侧控制区域(级别和搜索)
self.right_control_frame = ttk.Frame(self.control_frame)
self.right_control_frame.pack(side=tk.RIGHT, padx=5)
# 映射编辑按钮
mapping_btn = ttk.Button(self.right_control_frame, text="模块映射", command=self.edit_module_mapping)
mapping_btn.pack(side=tk.TOP, fill=tk.X, pady=1)
# 日志级别选择 # 日志级别选择
ttk.Label(filter_frame, text="级别:").pack(side=tk.LEFT, padx=2) level_frame = ttk.Frame(self.right_control_frame)
level_frame.pack(side=tk.TOP, fill=tk.X, pady=1)
ttk.Label(level_frame, text="级别:").pack(side=tk.LEFT, padx=2)
self.level_var = tk.StringVar(value="全部") self.level_var = tk.StringVar(value="全部")
self.level_combo = ttk.Combobox(filter_frame, textvariable=self.level_var, width=8) self.level_combo = ttk.Combobox(level_frame, textvariable=self.level_var, width=8)
self.level_combo["values"] = ["全部", "debug", "info", "warning", "error", "critical"] self.level_combo["values"] = ["全部", "debug", "info", "warning", "error", "critical"]
self.level_combo.pack(side=tk.LEFT, padx=2) self.level_combo.pack(side=tk.LEFT, padx=2)
# 搜索框 # 搜索框
ttk.Label(filter_frame, text="搜索:").pack(side=tk.LEFT, padx=(20, 2)) search_frame = ttk.Frame(self.right_control_frame)
search_frame.pack(side=tk.TOP, fill=tk.X, pady=1)
ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT, padx=2)
self.search_var = tk.StringVar() self.search_var = tk.StringVar()
self.search_entry = ttk.Entry(filter_frame, textvariable=self.search_var, width=20) self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=15)
self.search_entry.pack(side=tk.LEFT, padx=2) self.search_entry.pack(side=tk.LEFT, padx=2)
# 模块选择
ttk.Label(filter_frame, text="模块:").pack(side=tk.LEFT, padx=(20, 2))
self.module_var = tk.StringVar(value="全部")
self.module_combo = ttk.Combobox(filter_frame, textvariable=self.module_var, width=15)
self.module_combo.pack(side=tk.LEFT, padx=2)
self.module_combo.bind("<<ComboboxSelected>>", self.on_module_selected)
def on_file_loaded(self, log_index, error): def on_file_loaded(self, log_index, error):
"""文件加载完成回调""" """文件加载完成回调"""
self.progress_bar.pack_forget() self.progress_bar.pack_forget()
@@ -590,6 +1029,7 @@ class LogViewer:
self.status_var.set(f"已加载 {log_index.total_entries} 条日志") self.status_var.set(f"已加载 {log_index.total_entries} 条日志")
# 更新模块列表 # 更新模块列表
self.modules = set(log_index.module_index.keys())
self.update_module_list() self.update_module_list()
# 应用过滤并显示 # 应用过滤并显示
@@ -623,22 +1063,11 @@ class LogViewer:
# 清空当前数据 # 清空当前数据
self.log_index = LogIndex() self.log_index = LogIndex()
self.modules.clear()
self.selected_modules.clear() self.selected_modules.clear()
self.module_var.set("全部")
# 开始异步加载 # 开始异步加载
self.async_loader.load_file_async(str(self.current_log_file), self.on_loading_progress) self.async_loader.load_file_async(str(self.current_log_file), self.on_loading_progress)
def on_module_selected(self, event=None):
"""模块选择事件"""
module = self.module_var.get()
if module == "全部":
self.selected_modules = {"全部"}
else:
self.selected_modules = {module}
self.filter_logs()
def filter_logs(self, *args): def filter_logs(self, *args):
"""过滤日志""" """过滤日志"""
if not self.log_index: if not self.log_index:
@@ -743,7 +1172,7 @@ class LogViewer:
def read_new_logs(self, from_position): def read_new_logs(self, from_position):
"""读取新的日志条目并返回它们""" """读取新的日志条目并返回它们"""
new_entries = [] new_entries = []
new_modules_found = False new_modules = set() # 收集新发现的模块
with open(self.current_log_file, "r", encoding="utf-8") as f: with open(self.current_log_file, "r", encoding="utf-8") as f:
f.seek(from_position) f.seek(from_position)
line_count = self.log_index.total_entries line_count = self.log_index.total_entries
@@ -756,14 +1185,20 @@ class LogViewer:
logger_name = log_entry.get("logger_name", "") logger_name = log_entry.get("logger_name", "")
if logger_name and logger_name not in self.modules: if logger_name and logger_name not in self.modules:
self.modules.add(logger_name) new_modules.add(logger_name)
new_modules_found = True
line_count += 1 line_count += 1
except json.JSONDecodeError: except json.JSONDecodeError:
continue continue
if new_modules_found:
self.root.after(0, self.update_module_list) # 如果发现了新模块,在主线程中更新模块集合
if new_modules:
def update_modules():
self.modules.update(new_modules)
self.update_module_list()
self.root.after(0, update_modules)
return new_entries return new_entries
def append_new_logs(self, new_entries): def append_new_logs(self, new_entries):
@@ -791,15 +1226,196 @@ class LogViewer:
self.status_var.set(f"显示 {total_count} 条日志") self.status_var.set(f"显示 {total_count} 条日志")
def update_module_list(self): def update_module_list(self):
"""更新模块下拉列表""" """更新模块列表"""
current_selection = self.module_var.get() # 清空现有选项
self.modules = set(self.log_index.module_index.keys()) for widget in self.module_inner_frame.winfo_children():
module_values = ["全部"] + sorted(list(self.modules)) widget.destroy()
self.module_combo["values"] = module_values
if current_selection in module_values: # 计算总模块数(包括"全部"
self.module_var.set(current_selection) total_modules = len(self.modules) + 1
max_cols = min(4, max(2, total_modules)) # 减少最大列数,避免超出边界
# 配置网格列权重,让每列平均分配空间
for i in range(max_cols):
self.module_inner_frame.grid_columnconfigure(i, weight=1, uniform="module_col")
# 创建一个多行布局
current_row = 0
current_col = 0
# 添加"全部"选项
all_frame = ttk.Frame(self.module_inner_frame)
all_frame.grid(row=current_row, column=current_col, padx=3, pady=2, sticky="ew")
all_var = tk.BooleanVar(value="全部" in self.selected_modules)
all_check = ttk.Checkbutton(
all_frame, text="全部", variable=all_var, command=lambda: self.toggle_module("全部", all_var)
)
all_check.pack(side=tk.LEFT)
# 使用颜色标签替代按钮
all_color = self.formatter.module_colors.get("全部", "black")
all_color_label = ttk.Label(all_frame, text="", foreground=all_color, width=2, cursor="hand2")
all_color_label.pack(side=tk.LEFT, padx=2)
all_color_label.bind("<Button-1>", lambda e: self.choose_module_color("全部"))
current_col += 1
# 添加其他模块选项
for module in sorted(self.modules):
if current_col >= max_cols:
current_row += 1
current_col = 0
frame = ttk.Frame(self.module_inner_frame)
frame.grid(row=current_row, column=current_col, padx=3, pady=2, sticky="ew")
var = tk.BooleanVar(value=module in self.selected_modules)
# 使用中文映射名称显示
display_name = self.get_display_name(module)
if len(display_name) > 12:
display_name = display_name[:10] + "..."
check = ttk.Checkbutton(
frame, text=display_name, variable=var, command=lambda m=module, v=var: self.toggle_module(m, v)
)
check.pack(side=tk.LEFT)
# 添加工具提示显示完整名称和英文名
full_tooltip = f"{self.get_display_name(module)}"
if module != self.get_display_name(module):
full_tooltip += f"\n({module})"
self.create_tooltip(check, full_tooltip)
# 使用颜色标签替代按钮
color = self.formatter.module_colors.get(module, "black")
color_label = ttk.Label(frame, text="", foreground=color, width=2, cursor="hand2")
color_label.pack(side=tk.LEFT, padx=2)
color_label.bind("<Button-1>", lambda e, m=module: self.choose_module_color(m))
current_col += 1
# 更新画布滚动区域
self.module_inner_frame.update_idletasks()
self.module_canvas.config(scrollregion=self.module_canvas.bbox("all"))
# 添加垂直滚动条
if not hasattr(self, "module_scrollbar"):
self.module_scrollbar = ttk.Scrollbar(
self.module_frame, orient=tk.VERTICAL, command=self.module_canvas.yview
)
self.module_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.module_canvas.config(yscrollcommand=self.module_scrollbar.set)
def create_tooltip(self, widget, text):
"""为控件创建工具提示"""
def on_enter(event):
tooltip = tk.Toplevel()
tooltip.wm_overrideredirect(True)
tooltip.wm_geometry(f"+{event.x_root + 10}+{event.y_root + 10}")
label = ttk.Label(tooltip, text=text, background="lightyellow", relief="solid", borderwidth=1)
label.pack()
widget.tooltip = tooltip
def on_leave(event):
if hasattr(widget, "tooltip"):
widget.tooltip.destroy()
del widget.tooltip
widget.bind("<Enter>", on_enter)
widget.bind("<Leave>", on_leave)
def toggle_module(self, module, var):
"""切换模块选择状态"""
if module == "全部":
if var.get():
self.selected_modules = {"全部"}
else: else:
self.module_var.set("全部") self.selected_modules.clear()
else:
if var.get():
self.selected_modules.add(module)
if "全部" in self.selected_modules:
self.selected_modules.remove("全部")
else:
self.selected_modules.discard(module)
self.filter_logs()
def get_display_name(self, module_name):
"""获取模块的显示名称"""
return self.module_name_mapping.get(module_name, module_name)
def edit_module_mapping(self):
"""编辑模块映射"""
mapping_window = tk.Toplevel(self.root)
mapping_window.title("编辑模块映射")
mapping_window.geometry("500x600")
# 创建滚动框架
frame = ttk.Frame(mapping_window)
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 创建滚动条
scrollbar = ttk.Scrollbar(frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 创建映射编辑列表
canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=canvas.yview)
# 创建内部框架
inner_frame = ttk.Frame(canvas)
canvas.create_window((0, 0), window=inner_frame, anchor="nw")
# 添加标题
ttk.Label(inner_frame, text="模块映射编辑", font=("", 12, "bold")).pack(anchor="w", padx=5, pady=5)
ttk.Label(inner_frame, text="英文名 -> 中文名", font=("", 10)).pack(anchor="w", padx=5, pady=2)
# 映射编辑字典
mapping_vars = {}
# 添加现有模块的映射编辑
all_modules = sorted(self.modules)
for module in all_modules:
frame_row = ttk.Frame(inner_frame)
frame_row.pack(fill=tk.X, padx=5, pady=2)
ttk.Label(frame_row, text=module, width=20).pack(side=tk.LEFT, padx=5)
ttk.Label(frame_row, text="->").pack(side=tk.LEFT, padx=5)
var = tk.StringVar(value=self.module_name_mapping.get(module, module))
mapping_vars[module] = var
entry = ttk.Entry(frame_row, textvariable=var, width=25)
entry.pack(side=tk.LEFT, padx=5)
# 更新画布滚动区域
inner_frame.update_idletasks()
canvas.config(scrollregion=canvas.bbox("all"))
def save_mappings():
# 更新映射
for module, var in mapping_vars.items():
new_name = var.get().strip()
if new_name and new_name != module:
self.module_name_mapping[module] = new_name
elif module in self.module_name_mapping and not new_name:
del self.module_name_mapping[module]
# 保存到文件
self.save_module_mapping()
# 更新模块列表显示
self.update_module_list()
mapping_window.destroy()
# 添加按钮
button_frame = ttk.Frame(mapping_window)
button_frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Button(button_frame, text="保存", command=save_mappings).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="取消", command=mapping_window.destroy).pack(side=tk.RIGHT, padx=5)
def main(): def main():

View File

@@ -1,278 +0,0 @@
import tkinter as tk
from tkinter import ttk
import json
import os
from pathlib import Path
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from collections import defaultdict
class ExpressionViewer:
def __init__(self, root):
self.root = root
self.root.title("表达方式预览器")
self.root.geometry("1200x800")
# 创建主框架
self.main_frame = ttk.Frame(root)
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 创建左侧控制面板
self.control_frame = ttk.Frame(self.main_frame)
self.control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
# 创建搜索框
self.search_frame = ttk.Frame(self.control_frame)
self.search_frame.pack(fill=tk.X, pady=(0, 10))
self.search_var = tk.StringVar()
self.search_var.trace("w", self.filter_expressions)
self.search_entry = ttk.Entry(self.search_frame, textvariable=self.search_var)
self.search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
ttk.Label(self.search_frame, text="搜索:").pack(side=tk.LEFT, padx=(0, 5))
# 创建文件选择下拉框
self.file_var = tk.StringVar()
self.file_combo = ttk.Combobox(self.search_frame, textvariable=self.file_var)
self.file_combo.pack(side=tk.LEFT, padx=5)
self.file_combo.bind("<<ComboboxSelected>>", self.load_file)
# 创建排序选项
self.sort_frame = ttk.LabelFrame(self.control_frame, text="排序选项")
self.sort_frame.pack(fill=tk.X, pady=5)
self.sort_var = tk.StringVar(value="count")
ttk.Radiobutton(
self.sort_frame, text="按计数排序", variable=self.sort_var, value="count", command=self.apply_sort
).pack(anchor=tk.W)
ttk.Radiobutton(
self.sort_frame, text="按情境排序", variable=self.sort_var, value="situation", command=self.apply_sort
).pack(anchor=tk.W)
ttk.Radiobutton(
self.sort_frame, text="按风格排序", variable=self.sort_var, value="style", command=self.apply_sort
).pack(anchor=tk.W)
# 创建分群选项
self.group_frame = ttk.LabelFrame(self.control_frame, text="分群选项")
self.group_frame.pack(fill=tk.X, pady=5)
self.group_var = tk.StringVar(value="none")
ttk.Radiobutton(
self.group_frame, text="不分群", variable=self.group_var, value="none", command=self.apply_grouping
).pack(anchor=tk.W)
ttk.Radiobutton(
self.group_frame, text="按情境分群", variable=self.group_var, value="situation", command=self.apply_grouping
).pack(anchor=tk.W)
ttk.Radiobutton(
self.group_frame, text="按风格分群", variable=self.group_var, value="style", command=self.apply_grouping
).pack(anchor=tk.W)
# 创建相似度阈值滑块
self.similarity_frame = ttk.LabelFrame(self.control_frame, text="相似度设置")
self.similarity_frame.pack(fill=tk.X, pady=5)
self.similarity_var = tk.DoubleVar(value=0.5)
self.similarity_scale = ttk.Scale(
self.similarity_frame,
from_=0.0,
to=1.0,
variable=self.similarity_var,
orient=tk.HORIZONTAL,
command=self.update_similarity,
)
self.similarity_scale.pack(fill=tk.X, padx=5, pady=5)
ttk.Label(self.similarity_frame, text="相似度阈值: 0.5").pack()
# 创建显示选项
self.view_frame = ttk.LabelFrame(self.control_frame, text="显示选项")
self.view_frame.pack(fill=tk.X, pady=5)
self.show_graph_var = tk.BooleanVar(value=True)
ttk.Checkbutton(
self.view_frame, text="显示关系图", variable=self.show_graph_var, command=self.toggle_graph
).pack(anchor=tk.W)
# 创建右侧内容区域
self.content_frame = ttk.Frame(self.main_frame)
self.content_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 创建文本显示区域
self.text_area = tk.Text(self.content_frame, wrap=tk.WORD)
self.text_area.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
# 添加滚动条
scrollbar = ttk.Scrollbar(self.text_area, command=self.text_area.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.text_area.config(yscrollcommand=scrollbar.set)
# 创建图形显示区域
self.graph_frame = ttk.Frame(self.content_frame)
self.graph_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
# 初始化数据
self.current_data = []
self.graph = nx.Graph()
self.canvas = None
# 加载文件列表
self.load_file_list()
def load_file_list(self):
expression_dir = Path("data/expression")
files = []
for root, _, filenames in os.walk(expression_dir):
for filename in filenames:
if filename.endswith(".json"):
rel_path = os.path.relpath(os.path.join(root, filename), expression_dir)
files.append(rel_path)
self.file_combo["values"] = files
if files:
self.file_combo.set(files[0])
self.load_file(None)
def load_file(self, event):
selected_file = self.file_var.get()
if not selected_file:
return
file_path = os.path.join("data/expression", selected_file)
try:
with open(file_path, "r", encoding="utf-8") as f:
self.current_data = json.load(f)
self.apply_sort()
self.update_similarity()
except Exception as e:
self.text_area.delete(1.0, tk.END)
self.text_area.insert(tk.END, f"加载文件时出错: {str(e)}")
def apply_sort(self):
if not self.current_data:
return
sort_key = self.sort_var.get()
reverse = sort_key == "count"
self.current_data.sort(key=lambda x: x.get(sort_key, ""), reverse=reverse)
self.apply_grouping()
def apply_grouping(self):
if not self.current_data:
return
group_key = self.group_var.get()
if group_key == "none":
self.display_data(self.current_data)
return
grouped_data = defaultdict(list)
for item in self.current_data:
key = item.get(group_key, "未分类")
grouped_data[key].append(item)
self.text_area.delete(1.0, tk.END)
for group, items in grouped_data.items():
self.text_area.insert(tk.END, f"\n=== {group} ===\n\n")
for item in items:
self.text_area.insert(tk.END, f"情境: {item.get('situation', 'N/A')}\n")
self.text_area.insert(tk.END, f"风格: {item.get('style', 'N/A')}\n")
self.text_area.insert(tk.END, f"计数: {item.get('count', 'N/A')}\n")
self.text_area.insert(tk.END, "-" * 50 + "\n")
def display_data(self, data):
self.text_area.delete(1.0, tk.END)
for item in data:
self.text_area.insert(tk.END, f"情境: {item.get('situation', 'N/A')}\n")
self.text_area.insert(tk.END, f"风格: {item.get('style', 'N/A')}\n")
self.text_area.insert(tk.END, f"计数: {item.get('count', 'N/A')}\n")
self.text_area.insert(tk.END, "-" * 50 + "\n")
def update_similarity(self, *args):
if not self.current_data:
return
threshold = self.similarity_var.get()
self.similarity_frame.winfo_children()[-1].config(text=f"相似度阈值: {threshold:.2f}")
# 计算相似度
texts = [f"{item['situation']} {item['style']}" for item in self.current_data]
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(texts)
similarity_matrix = cosine_similarity(tfidf_matrix)
# 创建图
self.graph.clear()
for i, item in enumerate(self.current_data):
self.graph.add_node(i, label=f"{item['situation']}\n{item['style']}")
# 添加边
for i in range(len(self.current_data)):
for j in range(i + 1, len(self.current_data)):
if similarity_matrix[i, j] > threshold:
self.graph.add_edge(i, j, weight=similarity_matrix[i, j])
if self.show_graph_var.get():
self.draw_graph()
def draw_graph(self):
if self.canvas:
self.canvas.get_tk_widget().destroy()
fig = plt.figure(figsize=(8, 6))
pos = nx.spring_layout(self.graph)
# 绘制节点
nx.draw_networkx_nodes(self.graph, pos, node_color="lightblue", node_size=1000, alpha=0.6)
# 绘制边
nx.draw_networkx_edges(self.graph, pos, alpha=0.4)
# 添加标签
labels = nx.get_node_attributes(self.graph, "label")
nx.draw_networkx_labels(self.graph, pos, labels, font_size=8)
plt.title("表达方式关系图")
plt.axis("off")
self.canvas = FigureCanvasTkAgg(fig, master=self.graph_frame)
self.canvas.draw()
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
def toggle_graph(self):
if self.show_graph_var.get():
self.draw_graph()
else:
if self.canvas:
self.canvas.get_tk_widget().destroy()
self.canvas = None
def filter_expressions(self, *args):
search_text = self.search_var.get().lower()
if not search_text:
self.apply_sort()
return
filtered_data = []
for item in self.current_data:
situation = item.get("situation", "").lower()
style = item.get("style", "").lower()
if search_text in situation or search_text in style:
filtered_data.append(item)
self.display_data(filtered_data)
def main():
root = tk.Tk()
# app = ExpressionViewer(root)
root.mainloop()
if __name__ == "__main__":
main()

View File

@@ -1,185 +0,0 @@
#!/usr/bin/env python3
"""
HFC性能统计数据查看工具
"""
import sys
import json
import argparse
from pathlib import Path
from typing import Dict, Any
# 添加项目根目录到Python路径
sys.path.insert(0, str(Path(__file__).parent.parent))
def format_time(seconds: float) -> str:
"""格式化时间显示"""
if seconds < 1:
return f"{seconds * 1000:.1f}毫秒"
else:
return f"{seconds:.3f}"
def display_chat_stats(chat_id: str, stats: Dict[str, Any]):
"""显示单个聊天的统计数据"""
print(f"\n=== Chat ID: {chat_id} ===")
print(f"版本: {stats.get('version', 'unknown')}")
print(f"最后更新: {stats['last_updated']}")
overall = stats["overall"]
print("\n📊 总体统计:")
print(f" 总记录数: {overall['total_records']}")
print(f" 平均总时间: {format_time(overall['avg_total_time'])}")
print("\n⏱️ 各步骤平均时间:")
for step, avg_time in overall["avg_step_times"].items():
print(f" {step}: {format_time(avg_time)}")
print("\n🎯 按动作类型统计:")
by_action = stats["by_action"]
# 按比例排序
sorted_actions = sorted(by_action.items(), key=lambda x: x[1]["percentage"], reverse=True)
for action, action_stats in sorted_actions:
print(f" 📌 {action}:")
print(f" 次数: {action_stats['count']} ({action_stats['percentage']:.1f}%)")
print(f" 平均总时间: {format_time(action_stats['avg_total_time'])}")
if action_stats["avg_step_times"]:
print(" 步骤时间:")
for step, step_time in action_stats["avg_step_times"].items():
print(f" {step}: {format_time(step_time)}")
def display_comparison(stats_data: Dict[str, Dict[str, Any]]):
"""显示多个聊天的对比数据"""
if len(stats_data) < 2:
return
print("\n=== 多聊天对比 ===")
# 创建对比表格
chat_ids = list(stats_data.keys())
print("\n📊 总体对比:")
print(f"{'Chat ID':<20} {'版本':<12} {'记录数':<8} {'平均时间':<12} {'最常见动作':<15}")
print("-" * 70)
for chat_id in chat_ids:
stats = stats_data[chat_id]
overall = stats["overall"]
# 找到最常见的动作
most_common_action = max(stats["by_action"].items(), key=lambda x: x[1]["count"])
most_common_name = most_common_action[0]
most_common_pct = most_common_action[1]["percentage"]
version = stats.get("version", "unknown")
print(
f"{chat_id:<20} {version:<12} {overall['total_records']:<8} {format_time(overall['avg_total_time']):<12} {most_common_name}({most_common_pct:.0f}%)"
)
def view_session_logs(chat_id: str = None, latest: bool = False):
"""查看会话日志文件"""
log_dir = Path("log/hfc_loop")
if not log_dir.exists():
print("❌ 日志目录不存在")
return
if chat_id:
pattern = f"{chat_id}_*.json"
else:
pattern = "*.json"
log_files = list(log_dir.glob(pattern))
if not log_files:
print(f"❌ 没有找到匹配的日志文件: {pattern}")
return
if latest:
# 按文件修改时间排序,取最新的
log_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
log_files = log_files[:1]
for log_file in log_files:
print(f"\n=== 会话日志: {log_file.name} ===")
try:
with open(log_file, "r", encoding="utf-8") as f:
records = json.load(f)
if not records:
print(" 空文件")
continue
print(f" 记录数: {len(records)}")
print(f" 时间范围: {records[0]['timestamp']} ~ {records[-1]['timestamp']}")
# 统计动作分布
action_counts = {}
total_time = 0
for record in records:
action = record["action_type"]
action_counts[action] = action_counts.get(action, 0) + 1
total_time += record["total_time"]
print(f" 总耗时: {format_time(total_time)}")
print(f" 平均耗时: {format_time(total_time / len(records))}")
print(f" 动作分布: {dict(action_counts)}")
except Exception as e:
print(f" ❌ 读取文件失败: {e}")
def main():
parser = argparse.ArgumentParser(description="HFC性能统计数据查看工具")
parser.add_argument("--chat-id", help="指定要查看的Chat ID")
parser.add_argument("--logs", action="store_true", help="查看会话日志文件")
parser.add_argument("--latest", action="store_true", help="只显示最新的日志文件")
parser.add_argument("--compare", action="store_true", help="显示多聊天对比")
args = parser.parse_args()
if args.logs:
view_session_logs(args.chat_id, args.latest)
return
# 读取统计数据
stats_file = Path("data/hfc/time.json")
if not stats_file.exists():
print("❌ 统计数据文件不存在请先运行一些HFC循环以生成数据")
return
try:
with open(stats_file, "r", encoding="utf-8") as f:
stats_data = json.load(f)
except Exception as e:
print(f"❌ 读取统计数据失败: {e}")
return
if not stats_data:
print("❌ 统计数据为空")
return
if args.chat_id:
if args.chat_id in stats_data:
display_chat_stats(args.chat_id, stats_data[args.chat_id])
else:
print(f"❌ 没有找到Chat ID '{args.chat_id}' 的数据")
print(f"可用的Chat ID: {list(stats_data.keys())}")
else:
# 显示所有聊天的统计数据
for chat_id, stats in stats_data.items():
display_chat_stats(chat_id, stats)
if args.compare:
display_comparison(stats_data)
if __name__ == "__main__":
main()

View File

@@ -20,7 +20,7 @@ from src.person_info.person_info import get_person_info_manager
from src.plugin_system.base.component_types import ActionInfo, ChatMode from src.plugin_system.base.component_types import ActionInfo, ChatMode
from src.plugin_system.apis import generator_api, send_api, message_api from src.plugin_system.apis import generator_api, send_api, message_api
from src.chat.willing.willing_manager import get_willing_manager from src.chat.willing.willing_manager import get_willing_manager
from src.chat.mai_thinking.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from maim_message.message_base import GroupInfo from maim_message.message_base import GroupInfo
from src.mais4u.constant_s4u import ENABLE_S4U from src.mais4u.constant_s4u import ENABLE_S4U

View File

@@ -6,7 +6,7 @@ import re
from typing import List, Optional, Dict, Any, Tuple from typing import List, Optional, Dict, Any, Tuple
from datetime import datetime from datetime import datetime
from src.chat.mai_thinking.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.config import global_config from src.config.config import global_config
from src.individuality.individuality import get_individuality from src.individuality.individuality import get_individuality

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@ from src.common.logger import get_logger
from src.plugin_system.apis import generator_api, message_api from src.plugin_system.apis import generator_api, message_api
from src.plugins.built_in.core_actions.no_reply import NoReplyAction from src.plugins.built_in.core_actions.no_reply import NoReplyAction
from src.person_info.person_info import get_person_info_manager from src.person_info.person_info import get_person_info_manager
from src.chat.mai_thinking.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from src.mais4u.constant_s4u import ENABLE_S4U from src.mais4u.constant_s4u import ENABLE_S4U
logger = get_logger("reply_action") logger = get_logger("reply_action")