fix: auto-fix code issues (cron)
- 修复重复导入/字段 - 修复异常处理 - 修复PEP8格式问题 - 添加类型注解
This commit is contained in:
Binary file not shown.
@@ -291,7 +291,7 @@ class AIManager:
|
||||
return self._row_to_custom_model(row)
|
||||
|
||||
def list_custom_models(
|
||||
self, tenant_id: str, model_type: ModelType | None = None, status: ModelStatus | None = None
|
||||
self, tenant_id: str, model_type: ModelType | None = None, status: ModelStatus | None = None,
|
||||
) -> list[CustomModel]:
|
||||
"""列出自定义模型"""
|
||||
query = "SELECT * FROM custom_models WHERE tenant_id = ?"
|
||||
@@ -311,7 +311,7 @@ class AIManager:
|
||||
return [self._row_to_custom_model(row) for row in rows]
|
||||
|
||||
def add_training_sample(
|
||||
self, model_id: str, text: str, entities: list[dict], metadata: dict = None
|
||||
self, model_id: str, text: str, entities: list[dict], metadata: dict = None,
|
||||
) -> TrainingSample:
|
||||
"""添加训练样本"""
|
||||
sample_id = f"ts_{uuid.uuid4().hex[:16]}"
|
||||
@@ -638,7 +638,7 @@ class AIManager:
|
||||
}
|
||||
|
||||
def get_multimodal_analyses(
|
||||
self, tenant_id: str, project_id: str | None = None
|
||||
self, tenant_id: str, project_id: str | None = None,
|
||||
) -> list[MultimodalAnalysis]:
|
||||
"""获取多模态分析历史"""
|
||||
query = "SELECT * FROM multimodal_analyses WHERE tenant_id = ?"
|
||||
@@ -721,7 +721,7 @@ class AIManager:
|
||||
return self._row_to_kg_rag(row)
|
||||
|
||||
def list_kg_rags(
|
||||
self, tenant_id: str, project_id: str | None = None
|
||||
self, tenant_id: str, project_id: str | None = None,
|
||||
) -> list[KnowledgeGraphRAG]:
|
||||
"""列出知识图谱 RAG 配置"""
|
||||
query = "SELECT * FROM kg_rag_configs WHERE tenant_id = ?"
|
||||
@@ -738,7 +738,7 @@ class AIManager:
|
||||
return [self._row_to_kg_rag(row) for row in rows]
|
||||
|
||||
async def query_kg_rag(
|
||||
self, rag_id: str, query: str, project_entities: list[dict], project_relations: list[dict]
|
||||
self, rag_id: str, query: str, project_entities: list[dict], project_relations: list[dict],
|
||||
) -> RAGQuery:
|
||||
"""基于知识图谱的 RAG 查询"""
|
||||
start_time = time.time()
|
||||
@@ -1123,7 +1123,7 @@ class AIManager:
|
||||
"""获取预测模型"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM prediction_models WHERE id = ?", (model_id,)
|
||||
"SELECT * FROM prediction_models WHERE id = ?", (model_id,),
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
@@ -1132,7 +1132,7 @@ class AIManager:
|
||||
return self._row_to_prediction_model(row)
|
||||
|
||||
def list_prediction_models(
|
||||
self, tenant_id: str, project_id: str | None = None
|
||||
self, tenant_id: str, project_id: str | None = None,
|
||||
) -> list[PredictionModel]:
|
||||
"""列出预测模型"""
|
||||
query = "SELECT * FROM prediction_models WHERE tenant_id = ?"
|
||||
@@ -1149,7 +1149,7 @@ class AIManager:
|
||||
return [self._row_to_prediction_model(row) for row in rows]
|
||||
|
||||
async def train_prediction_model(
|
||||
self, model_id: str, historical_data: list[dict]
|
||||
self, model_id: str, historical_data: list[dict],
|
||||
) -> PredictionModel:
|
||||
"""训练预测模型"""
|
||||
model = self.get_prediction_model(model_id)
|
||||
@@ -1369,7 +1369,7 @@ class AIManager:
|
||||
predicted_relations = [
|
||||
{"type": rel_type, "likelihood": min(count / len(relation_history), 0.95)}
|
||||
for rel_type, count in sorted(
|
||||
relation_counts.items(), key=lambda x: x[1], reverse=True
|
||||
relation_counts.items(), key=lambda x: x[1], reverse=True,
|
||||
)[:5]
|
||||
]
|
||||
|
||||
@@ -1394,7 +1394,7 @@ class AIManager:
|
||||
return [self._row_to_prediction_result(row) for row in rows]
|
||||
|
||||
def update_prediction_feedback(
|
||||
self, prediction_id: str, actual_value: str, is_correct: bool
|
||||
self, prediction_id: str, actual_value: str, is_correct: bool,
|
||||
) -> None:
|
||||
"""更新预测反馈(用于模型改进)"""
|
||||
with self._get_db() as conn:
|
||||
|
||||
@@ -238,7 +238,7 @@ class ApiKeyManager:
|
||||
# 验证所有权(如果提供了 owner_id)
|
||||
if owner_id:
|
||||
row = conn.execute(
|
||||
"SELECT owner_id FROM api_keys WHERE id = ?", (key_id,)
|
||||
"SELECT owner_id FROM api_keys WHERE id = ?", (key_id,),
|
||||
).fetchone()
|
||||
if not row or row[0] != owner_id:
|
||||
return False
|
||||
@@ -267,7 +267,7 @@ class ApiKeyManager:
|
||||
|
||||
if owner_id:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM api_keys WHERE id = ? AND owner_id = ?", (key_id, owner_id)
|
||||
"SELECT * FROM api_keys WHERE id = ? AND owner_id = ?", (key_id, owner_id),
|
||||
).fetchone()
|
||||
else:
|
||||
row = conn.execute("SELECT * FROM api_keys WHERE id = ?", (key_id,)).fetchone()
|
||||
@@ -337,7 +337,7 @@ class ApiKeyManager:
|
||||
# 验证所有权
|
||||
if owner_id:
|
||||
row = conn.execute(
|
||||
"SELECT owner_id FROM api_keys WHERE id = ?", (key_id,)
|
||||
"SELECT owner_id FROM api_keys WHERE id = ?", (key_id,),
|
||||
).fetchone()
|
||||
if not row or row[0] != owner_id:
|
||||
return False
|
||||
@@ -465,7 +465,7 @@ class ApiKeyManager:
|
||||
endpoint_params = []
|
||||
if api_key_id:
|
||||
endpoint_query = endpoint_query.replace(
|
||||
"WHERE created_at", "WHERE api_key_id = ? AND created_at"
|
||||
"WHERE created_at", "WHERE api_key_id = ? AND created_at",
|
||||
)
|
||||
endpoint_params.insert(0, api_key_id)
|
||||
|
||||
@@ -486,7 +486,7 @@ class ApiKeyManager:
|
||||
daily_params = []
|
||||
if api_key_id:
|
||||
daily_query = daily_query.replace(
|
||||
"WHERE created_at", "WHERE api_key_id = ? AND created_at"
|
||||
"WHERE created_at", "WHERE api_key_id = ? AND created_at",
|
||||
)
|
||||
daily_params.insert(0, api_key_id)
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ class CollaborationManager:
|
||||
is_active=bool(row[10]),
|
||||
allow_download=bool(row[11]),
|
||||
allow_export=bool(row[12]),
|
||||
)
|
||||
),
|
||||
)
|
||||
return shares
|
||||
|
||||
@@ -435,7 +435,7 @@ class CollaborationManager:
|
||||
self.db.conn.commit()
|
||||
|
||||
def get_comments(
|
||||
self, target_type: str, target_id: str, include_resolved: bool = True
|
||||
self, target_type: str, target_id: str, include_resolved: bool = True,
|
||||
) -> list[Comment]:
|
||||
"""获取评论列表"""
|
||||
if not self.db:
|
||||
@@ -554,7 +554,7 @@ class CollaborationManager:
|
||||
return cursor.rowcount > 0
|
||||
|
||||
def get_project_comments(
|
||||
self, project_id: str, limit: int = 50, offset: int = 0
|
||||
self, project_id: str, limit: int = 50, offset: int = 0,
|
||||
) -> list[Comment]:
|
||||
"""获取项目下的所有评论"""
|
||||
if not self.db:
|
||||
|
||||
@@ -149,7 +149,7 @@ class DatabaseManager:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return Project(
|
||||
id=project_id, name=name, description=description, created_at=now, updated_at=now
|
||||
id=project_id, name=name, description=description, created_at=now, updated_at=now,
|
||||
)
|
||||
|
||||
def get_project(self, project_id: str) -> Project | None:
|
||||
@@ -206,7 +206,7 @@ class DatabaseManager:
|
||||
return None
|
||||
|
||||
def find_similar_entities(
|
||||
self, project_id: str, name: str, threshold: float = 0.8
|
||||
self, project_id: str, name: str, threshold: float = 0.8,
|
||||
) -> list[Entity]:
|
||||
"""查找相似实体"""
|
||||
conn = self.get_conn()
|
||||
@@ -243,7 +243,7 @@ class DatabaseManager:
|
||||
(json.dumps(list(target_aliases)), datetime.now().isoformat(), target_id),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE entity_mentions SET entity_id = ? WHERE entity_id = ?", (target_id, source_id)
|
||||
"UPDATE entity_mentions SET entity_id = ? WHERE entity_id = ?", (target_id, source_id),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE entity_relations SET source_entity_id = ? WHERE source_entity_id = ?",
|
||||
@@ -272,7 +272,7 @@ class DatabaseManager:
|
||||
def list_project_entities(self, project_id: str) -> list[Entity]:
|
||||
conn = self.get_conn()
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM entities WHERE project_id = ? ORDER BY updated_at DESC", (project_id,)
|
||||
"SELECT * FROM entities WHERE project_id = ? ORDER BY updated_at DESC", (project_id,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
@@ -478,7 +478,7 @@ class DatabaseManager:
|
||||
conn.commit()
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT * FROM entity_relations WHERE id = ?", (relation_id,)
|
||||
"SELECT * FROM entity_relations WHERE id = ?", (relation_id,),
|
||||
).fetchone()
|
||||
conn.close()
|
||||
return dict(row) if row else None
|
||||
@@ -494,12 +494,12 @@ class DatabaseManager:
|
||||
def add_glossary_term(self, project_id: str, term: str, pronunciation: str = "") -> str:
|
||||
conn = self.get_conn()
|
||||
existing = conn.execute(
|
||||
"SELECT * FROM glossary WHERE project_id = ? AND term = ?", (project_id, term)
|
||||
"SELECT * FROM glossary WHERE project_id = ? AND term = ?", (project_id, term),
|
||||
).fetchone()
|
||||
|
||||
if existing:
|
||||
conn.execute(
|
||||
"UPDATE glossary SET frequency = frequency + 1 WHERE id = ?", (existing["id"],)
|
||||
"UPDATE glossary SET frequency = frequency + 1 WHERE id = ?", (existing["id"],),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@@ -519,7 +519,7 @@ class DatabaseManager:
|
||||
def list_glossary(self, project_id: str) -> list[dict]:
|
||||
conn = self.get_conn()
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM glossary WHERE project_id = ? ORDER BY frequency DESC", (project_id,)
|
||||
"SELECT * FROM glossary WHERE project_id = ? ORDER BY frequency DESC", (project_id,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return [dict(r) for r in rows]
|
||||
@@ -605,15 +605,15 @@ class DatabaseManager:
|
||||
project = conn.execute("SELECT * FROM projects WHERE id = ?", (project_id,)).fetchone()
|
||||
|
||||
entity_count = conn.execute(
|
||||
"SELECT COUNT(*) as count FROM entities WHERE project_id = ?", (project_id,)
|
||||
"SELECT COUNT(*) as count FROM entities WHERE project_id = ?", (project_id,),
|
||||
).fetchone()["count"]
|
||||
|
||||
transcript_count = conn.execute(
|
||||
"SELECT COUNT(*) as count FROM transcripts WHERE project_id = ?", (project_id,)
|
||||
"SELECT COUNT(*) as count FROM transcripts WHERE project_id = ?", (project_id,),
|
||||
).fetchone()["count"]
|
||||
|
||||
relation_count = conn.execute(
|
||||
"SELECT COUNT(*) as count FROM entity_relations WHERE project_id = ?", (project_id,)
|
||||
"SELECT COUNT(*) as count FROM entity_relations WHERE project_id = ?", (project_id,),
|
||||
).fetchone()["count"]
|
||||
|
||||
recent_transcripts = conn.execute(
|
||||
@@ -645,11 +645,11 @@ class DatabaseManager:
|
||||
}
|
||||
|
||||
def get_transcript_context(
|
||||
self, transcript_id: str, position: int, context_chars: int = 200
|
||||
self, transcript_id: str, position: int, context_chars: int = 200,
|
||||
) -> str:
|
||||
conn = self.get_conn()
|
||||
row = conn.execute(
|
||||
"SELECT full_text FROM transcripts WHERE id = ?", (transcript_id,)
|
||||
"SELECT full_text FROM transcripts WHERE id = ?", (transcript_id,),
|
||||
).fetchone()
|
||||
conn.close()
|
||||
if not row:
|
||||
@@ -662,7 +662,7 @@ class DatabaseManager:
|
||||
# ==================== Phase 5: Timeline Operations ====================
|
||||
|
||||
def get_project_timeline(
|
||||
self, project_id: str, entity_id: str = None, start_date: str = None, end_date: str = None
|
||||
self, project_id: str, entity_id: str = None, start_date: str = None, end_date: str = None,
|
||||
) -> list[dict]:
|
||||
conn = self.get_conn()
|
||||
|
||||
@@ -708,7 +708,7 @@ class DatabaseManager:
|
||||
"filename": m["filename"],
|
||||
"type": m["source_type"],
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -776,7 +776,7 @@ class DatabaseManager:
|
||||
def get_attribute_template(self, template_id: str) -> AttributeTemplate | None:
|
||||
conn = self.get_conn()
|
||||
row = conn.execute(
|
||||
"SELECT * FROM attribute_templates WHERE id = ?", (template_id,)
|
||||
"SELECT * FROM attribute_templates WHERE id = ?", (template_id,),
|
||||
).fetchone()
|
||||
conn.close()
|
||||
if row:
|
||||
@@ -841,7 +841,7 @@ class DatabaseManager:
|
||||
conn.close()
|
||||
|
||||
def set_entity_attribute(
|
||||
self, attr: EntityAttribute, changed_by: str = "system", change_reason: str = ""
|
||||
self, attr: EntityAttribute, changed_by: str = "system", change_reason: str = "",
|
||||
) -> EntityAttribute:
|
||||
conn = self.get_conn()
|
||||
now = datetime.now().isoformat()
|
||||
@@ -930,7 +930,7 @@ class DatabaseManager:
|
||||
return entity
|
||||
|
||||
def delete_entity_attribute(
|
||||
self, entity_id: str, template_id: str, changed_by: str = "system", change_reason: str = ""
|
||||
self, entity_id: str, template_id: str, changed_by: str = "system", change_reason: str = "",
|
||||
) -> None:
|
||||
conn = self.get_conn()
|
||||
old_row = conn.execute(
|
||||
@@ -964,7 +964,7 @@ class DatabaseManager:
|
||||
conn.close()
|
||||
|
||||
def get_attribute_history(
|
||||
self, entity_id: str = None, template_id: str = None, limit: int = 50
|
||||
self, entity_id: str = None, template_id: str = None, limit: int = 50,
|
||||
) -> list[AttributeHistory]:
|
||||
conn = self.get_conn()
|
||||
conditions = []
|
||||
@@ -990,7 +990,7 @@ class DatabaseManager:
|
||||
return [AttributeHistory(**dict(r)) for r in rows]
|
||||
|
||||
def search_entities_by_attributes(
|
||||
self, project_id: str, attribute_filters: dict[str, str]
|
||||
self, project_id: str, attribute_filters: dict[str, str],
|
||||
) -> list[Entity]:
|
||||
entities = self.list_project_entities(project_id)
|
||||
if not attribute_filters:
|
||||
@@ -1098,7 +1098,7 @@ class DatabaseManager:
|
||||
"""获取项目的所有视频"""
|
||||
conn = self.get_conn()
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM videos WHERE project_id = ? ORDER BY created_at DESC", (project_id,)
|
||||
"SELECT * FROM videos WHERE project_id = ? ORDER BY created_at DESC", (project_id,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
@@ -1153,7 +1153,7 @@ class DatabaseManager:
|
||||
"""获取视频的所有帧"""
|
||||
conn = self.get_conn()
|
||||
rows = conn.execute(
|
||||
"""SELECT * FROM video_frames WHERE video_id = ? ORDER BY timestamp""", (video_id,)
|
||||
"""SELECT * FROM video_frames WHERE video_id = ? ORDER BY timestamp""", (video_id,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
@@ -1223,7 +1223,7 @@ class DatabaseManager:
|
||||
"""获取项目的所有图片"""
|
||||
conn = self.get_conn()
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM images WHERE project_id = ? ORDER BY created_at DESC", (project_id,)
|
||||
"SELECT * FROM images WHERE project_id = ? ORDER BY created_at DESC", (project_id,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
@@ -1381,13 +1381,13 @@ class DatabaseManager:
|
||||
|
||||
# 视频数量
|
||||
row = conn.execute(
|
||||
"SELECT COUNT(*) as count FROM videos WHERE project_id = ?", (project_id,)
|
||||
"SELECT COUNT(*) as count FROM videos WHERE project_id = ?", (project_id,),
|
||||
).fetchone()
|
||||
stats["video_count"] = row["count"]
|
||||
|
||||
# 图片数量
|
||||
row = conn.execute(
|
||||
"SELECT COUNT(*) as count FROM images WHERE project_id = ?", (project_id,)
|
||||
"SELECT COUNT(*) as count FROM images WHERE project_id = ?", (project_id,),
|
||||
).fetchone()
|
||||
stats["image_count"] = row["count"]
|
||||
|
||||
|
||||
@@ -495,7 +495,7 @@ class DeveloperEcosystemManager:
|
||||
updates["updated_at"] = datetime.now().isoformat()
|
||||
|
||||
with self._get_db() as conn:
|
||||
set_clause = ", ".join([f"{k} = ?" for k in updates.keys()])
|
||||
set_clause = ", ".join([f"{k} = ?" for k in updates])
|
||||
conn.execute(
|
||||
f"UPDATE sdk_releases SET {set_clause} WHERE id = ?",
|
||||
list(updates.values()) + [sdk_id],
|
||||
@@ -538,7 +538,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取 SDK 版本历史"""
|
||||
with self._get_db() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM sdk_versions WHERE sdk_id = ? ORDER BY created_at DESC", (sdk_id,)
|
||||
"SELECT * FROM sdk_versions WHERE sdk_id = ? ORDER BY created_at DESC", (sdk_id,),
|
||||
).fetchall()
|
||||
return [self._row_to_sdk_version(row) for row in rows]
|
||||
|
||||
@@ -700,7 +700,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取模板详情"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM template_market WHERE id = ?", (template_id,)
|
||||
"SELECT * FROM template_market WHERE id = ?", (template_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1076,7 +1076,7 @@ class DeveloperEcosystemManager:
|
||||
return [self._row_to_plugin(row) for row in rows]
|
||||
|
||||
def review_plugin(
|
||||
self, plugin_id: str, reviewed_by: str, status: PluginStatus, notes: str = ""
|
||||
self, plugin_id: str, reviewed_by: str, status: PluginStatus, notes: str = "",
|
||||
) -> PluginMarketItem | None:
|
||||
"""审核插件"""
|
||||
now = datetime.now().isoformat()
|
||||
@@ -1420,7 +1420,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取开发者档案"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM developer_profiles WHERE id = ?", (developer_id,)
|
||||
"SELECT * FROM developer_profiles WHERE id = ?", (developer_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1431,7 +1431,7 @@ class DeveloperEcosystemManager:
|
||||
"""通过用户 ID 获取开发者档案"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM developer_profiles WHERE user_id = ?", (user_id,)
|
||||
"SELECT * FROM developer_profiles WHERE user_id = ?", (user_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1439,7 +1439,7 @@ class DeveloperEcosystemManager:
|
||||
return None
|
||||
|
||||
def verify_developer(
|
||||
self, developer_id: str, status: DeveloperStatus
|
||||
self, developer_id: str, status: DeveloperStatus,
|
||||
) -> DeveloperProfile | None:
|
||||
"""验证开发者"""
|
||||
now = datetime.now().isoformat()
|
||||
@@ -1469,7 +1469,7 @@ class DeveloperEcosystemManager:
|
||||
with self._get_db() as conn:
|
||||
# 统计插件数量
|
||||
plugin_row = conn.execute(
|
||||
"SELECT COUNT(*) as count FROM plugin_market WHERE author_id = ?", (developer_id,)
|
||||
"SELECT COUNT(*) as count FROM plugin_market WHERE author_id = ?", (developer_id,),
|
||||
).fetchone()
|
||||
|
||||
# 统计模板数量
|
||||
@@ -1583,7 +1583,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取代码示例"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM code_examples WHERE id = ?", (example_id,)
|
||||
"SELECT * FROM code_examples WHERE id = ?", (example_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1699,7 +1699,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取 API 文档"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM api_documentation WHERE id = ?", (doc_id,)
|
||||
"SELECT * FROM api_documentation WHERE id = ?", (doc_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1710,7 +1710,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取最新 API 文档"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM api_documentation ORDER BY generated_at DESC LIMIT 1"
|
||||
"SELECT * FROM api_documentation ORDER BY generated_at DESC LIMIT 1",
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1799,7 +1799,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取开发者门户配置"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM developer_portal_configs WHERE id = ?", (config_id,)
|
||||
"SELECT * FROM developer_portal_configs WHERE id = ?", (config_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1810,7 +1810,7 @@ class DeveloperEcosystemManager:
|
||||
"""获取活跃的开发者门户配置"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM developer_portal_configs WHERE is_active = 1 LIMIT 1"
|
||||
"SELECT * FROM developer_portal_configs WHERE is_active = 1 LIMIT 1",
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
|
||||
@@ -35,7 +35,7 @@ class DocumentProcessor:
|
||||
|
||||
if ext not in self.supported_formats:
|
||||
raise ValueError(
|
||||
f"Unsupported file format: {ext}. Supported: {list(self.supported_formats.keys())}"
|
||||
f"Unsupported file format: {ext}. Supported: {list(self.supported_formats.keys())}",
|
||||
)
|
||||
|
||||
extractor = self.supported_formats[ext]
|
||||
@@ -75,7 +75,7 @@ class DocumentProcessor:
|
||||
return "\n\n".join(text_parts)
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"PDF processing requires PyPDF2 or pdfplumber. Install with: pip install PyPDF2"
|
||||
"PDF processing requires PyPDF2 or pdfplumber. Install with: pip install PyPDF2",
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError(f"PDF extraction failed: {str(e)}")
|
||||
@@ -106,7 +106,7 @@ class DocumentProcessor:
|
||||
return "\n\n".join(text_parts)
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"DOCX processing requires python-docx. Install with: pip install python-docx"
|
||||
"DOCX processing requires python-docx. Install with: pip install python-docx",
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError(f"DOCX extraction failed: {str(e)}")
|
||||
|
||||
@@ -531,40 +531,40 @@ class EnterpriseManager:
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_sso_tenant ON sso_configs(tenant_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_sso_provider ON sso_configs(provider)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_saml_requests_config ON saml_auth_requests(sso_config_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_saml_requests_config ON saml_auth_requests(sso_config_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_saml_requests_expires ON saml_auth_requests(expires_at)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_saml_requests_expires ON saml_auth_requests(expires_at)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_saml_responses_request ON saml_auth_responses(request_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_saml_responses_request ON saml_auth_responses(request_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_scim_config_tenant ON scim_configs(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_scim_config_tenant ON scim_configs(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_scim_users_tenant ON scim_users(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_scim_users_tenant ON scim_users(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_scim_users_external ON scim_users(external_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_scim_users_external ON scim_users(external_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_export_tenant ON audit_log_exports(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_export_tenant ON audit_log_exports(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_export_status ON audit_log_exports(status)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_export_status ON audit_log_exports(status)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_tenant ON data_retention_policies(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_tenant ON data_retention_policies(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_type ON data_retention_policies(resource_type)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_type ON data_retention_policies(resource_type)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_jobs_policy ON data_retention_jobs(policy_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_jobs_policy ON data_retention_jobs(policy_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_jobs_status ON data_retention_jobs(status)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_retention_jobs_status ON data_retention_jobs(status)",
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
@@ -699,7 +699,7 @@ class EnterpriseManager:
|
||||
conn.close()
|
||||
|
||||
def get_tenant_sso_config(
|
||||
self, tenant_id: str, provider: str | None = None
|
||||
self, tenant_id: str, provider: str | None = None,
|
||||
) -> SSOConfig | None:
|
||||
"""获取租户的 SSO 配置"""
|
||||
conn = self._get_connection()
|
||||
@@ -871,7 +871,7 @@ class EnterpriseManager:
|
||||
return metadata
|
||||
|
||||
def create_saml_auth_request(
|
||||
self, tenant_id: str, config_id: str, relay_state: str | None = None
|
||||
self, tenant_id: str, config_id: str, relay_state: str | None = None,
|
||||
) -> SAMLAuthRequest:
|
||||
"""创建 SAML 认证请求"""
|
||||
conn = self._get_connection()
|
||||
@@ -1235,7 +1235,7 @@ class EnterpriseManager:
|
||||
return []
|
||||
|
||||
def _upsert_scim_user(
|
||||
self, conn: sqlite3.Connection, tenant_id: str, user_data: dict[str, Any]
|
||||
self, conn: sqlite3.Connection, tenant_id: str, user_data: dict[str, Any],
|
||||
) -> None:
|
||||
"""插入或更新 SCIM 用户"""
|
||||
cursor = conn.cursor()
|
||||
@@ -1405,7 +1405,7 @@ class EnterpriseManager:
|
||||
try:
|
||||
# 获取审计日志数据
|
||||
logs = self._fetch_audit_logs(
|
||||
export.tenant_id, export.start_date, export.end_date, export.filters, db_manager
|
||||
export.tenant_id, export.start_date, export.end_date, export.filters, db_manager,
|
||||
)
|
||||
|
||||
# 根据合规标准过滤字段
|
||||
@@ -1414,7 +1414,7 @@ class EnterpriseManager:
|
||||
|
||||
# 生成导出文件
|
||||
file_path, file_size, checksum = self._generate_export_file(
|
||||
export_id, logs, export.export_format
|
||||
export_id, logs, export.export_format,
|
||||
)
|
||||
|
||||
now = datetime.now()
|
||||
@@ -1465,7 +1465,7 @@ class EnterpriseManager:
|
||||
return []
|
||||
|
||||
def _apply_compliance_filter(
|
||||
self, logs: list[dict[str, Any]], standard: str
|
||||
self, logs: list[dict[str, Any]], standard: str,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""应用合规标准字段过滤"""
|
||||
fields = self.COMPLIANCE_FIELDS.get(ComplianceStandard(standard), [])
|
||||
@@ -1481,7 +1481,7 @@ class EnterpriseManager:
|
||||
return filtered_logs
|
||||
|
||||
def _generate_export_file(
|
||||
self, export_id: str, logs: list[dict[str, Any]], format: str
|
||||
self, export_id: str, logs: list[dict[str, Any]], format: str,
|
||||
) -> tuple[str, int, str]:
|
||||
"""生成导出文件"""
|
||||
import hashlib
|
||||
@@ -1672,7 +1672,7 @@ class EnterpriseManager:
|
||||
conn.close()
|
||||
|
||||
def list_retention_policies(
|
||||
self, tenant_id: str, resource_type: str | None = None
|
||||
self, tenant_id: str, resource_type: str | None = None,
|
||||
) -> list[DataRetentionPolicy]:
|
||||
"""列出数据保留策略"""
|
||||
conn = self._get_connection()
|
||||
@@ -1876,7 +1876,7 @@ class EnterpriseManager:
|
||||
conn.close()
|
||||
|
||||
def _retain_audit_logs(
|
||||
self, conn: sqlite3.Connection, policy: DataRetentionPolicy, cutoff_date: datetime
|
||||
self, conn: sqlite3.Connection, policy: DataRetentionPolicy, cutoff_date: datetime,
|
||||
) -> dict[str, int]:
|
||||
"""保留审计日志"""
|
||||
cursor = conn.cursor()
|
||||
@@ -1909,14 +1909,14 @@ class EnterpriseManager:
|
||||
return {"affected": 0, "archived": 0, "deleted": 0, "errors": 0}
|
||||
|
||||
def _retain_projects(
|
||||
self, conn: sqlite3.Connection, policy: DataRetentionPolicy, cutoff_date: datetime
|
||||
self, conn: sqlite3.Connection, policy: DataRetentionPolicy, cutoff_date: datetime,
|
||||
) -> dict[str, int]:
|
||||
"""保留项目数据"""
|
||||
# 简化实现
|
||||
return {"affected": 0, "archived": 0, "deleted": 0, "errors": 0}
|
||||
|
||||
def _retain_transcripts(
|
||||
self, conn: sqlite3.Connection, policy: DataRetentionPolicy, cutoff_date: datetime
|
||||
self, conn: sqlite3.Connection, policy: DataRetentionPolicy, cutoff_date: datetime,
|
||||
) -> dict[str, int]:
|
||||
"""保留转录数据"""
|
||||
# 简化实现
|
||||
|
||||
@@ -178,7 +178,7 @@ class EntityAligner:
|
||||
return best_match
|
||||
|
||||
def _fallback_similarity_match(
|
||||
self, entities: list[object], name: str, exclude_id: str | None = None
|
||||
self, entities: list[object], name: str, exclude_id: str | None = None,
|
||||
) -> object | None:
|
||||
"""
|
||||
回退到简单的相似度匹配(不使用 embedding)
|
||||
@@ -212,7 +212,7 @@ class EntityAligner:
|
||||
return None
|
||||
|
||||
def batch_align_entities(
|
||||
self, project_id: str, new_entities: list[dict], threshold: float | None = None
|
||||
self, project_id: str, new_entities: list[dict], threshold: float | None = None,
|
||||
) -> list[dict]:
|
||||
"""
|
||||
批量对齐实体
|
||||
@@ -232,7 +232,7 @@ class EntityAligner:
|
||||
|
||||
for new_ent in new_entities:
|
||||
matched = self.find_similar_entity(
|
||||
project_id, new_ent["name"], new_ent.get("definition", ""), threshold=threshold
|
||||
project_id, new_ent["name"], new_ent.get("definition", ""), threshold=threshold,
|
||||
)
|
||||
|
||||
result = {
|
||||
|
||||
@@ -75,7 +75,7 @@ class ExportManager:
|
||||
self.db = db_manager
|
||||
|
||||
def export_knowledge_graph_svg(
|
||||
self, project_id: str, entities: list[ExportEntity], relations: list[ExportRelation]
|
||||
self, project_id: str, entities: list[ExportEntity], relations: list[ExportRelation],
|
||||
) -> str:
|
||||
"""
|
||||
导出知识图谱为 SVG 格式
|
||||
@@ -151,7 +151,7 @@ class ExportManager:
|
||||
|
||||
svg_parts.append(
|
||||
f'<line x1 = "{x1}" y1 = "{y1}" x2 = "{x2}" y2 = "{y2}" '
|
||||
f'stroke = "#7f8c8d" stroke-width = "2" marker-end = "url(#arrowhead)" opacity = "0.6"/>'
|
||||
f'stroke = "#7f8c8d" stroke-width = "2" marker-end = "url(#arrowhead)" opacity = "0.6"/>',
|
||||
)
|
||||
|
||||
# 关系标签
|
||||
@@ -159,11 +159,11 @@ class ExportManager:
|
||||
mid_y = (y1 + y2) / 2
|
||||
svg_parts.append(
|
||||
f'<rect x = "{mid_x - 30}" y = "{mid_y - 10}" width = "60" height = "20" '
|
||||
f'fill = "white" stroke = "#bdc3c7" rx = "3"/>'
|
||||
f'fill = "white" stroke = "#bdc3c7" rx = "3"/>',
|
||||
)
|
||||
svg_parts.append(
|
||||
f'<text x = "{mid_x}" y = "{mid_y + 5}" text-anchor = "middle" '
|
||||
f'font-size = "10" fill = "#2c3e50">{rel.relation_type}</text>'
|
||||
f'font-size = "10" fill = "#2c3e50">{rel.relation_type}</text>',
|
||||
)
|
||||
|
||||
# 绘制实体节点
|
||||
@@ -174,19 +174,19 @@ class ExportManager:
|
||||
|
||||
# 节点圆圈
|
||||
svg_parts.append(
|
||||
f'<circle cx = "{x}" cy = "{y}" r = "35" fill = "{color}" stroke = "white" stroke-width = "3"/>'
|
||||
f'<circle cx = "{x}" cy = "{y}" r = "35" fill = "{color}" stroke = "white" stroke-width = "3"/>',
|
||||
)
|
||||
|
||||
# 实体名称
|
||||
svg_parts.append(
|
||||
f'<text x = "{x}" y = "{y + 5}" text-anchor = "middle" font-size = "12" '
|
||||
f'font-weight = "bold" fill = "white">{entity.name[:8]}</text>'
|
||||
f'font-weight = "bold" fill = "white">{entity.name[:8]}</text>',
|
||||
)
|
||||
|
||||
# 实体类型
|
||||
svg_parts.append(
|
||||
f'<text x = "{x}" y = "{y + 55}" text-anchor = "middle" font-size = "10" '
|
||||
f'fill = "#7f8c8d">{entity.type}</text>'
|
||||
f'fill = "#7f8c8d">{entity.type}</text>',
|
||||
)
|
||||
|
||||
# 图例
|
||||
@@ -197,30 +197,30 @@ class ExportManager:
|
||||
rect_height = len(type_colors) * 25 + 10
|
||||
svg_parts.append(
|
||||
f'<rect x = "{rect_x}" y = "{rect_y}" width = "140" height = "{rect_height}" '
|
||||
f'fill = "white" stroke = "#bdc3c7" rx = "5"/>'
|
||||
f'fill = "white" stroke = "#bdc3c7" rx = "5"/>',
|
||||
)
|
||||
svg_parts.append(
|
||||
f'<text x = "{legend_x}" y = "{legend_y}" font-size = "12" font-weight = "bold" '
|
||||
f'fill = "#2c3e50">实体类型</text>'
|
||||
f'fill = "#2c3e50">实体类型</text>',
|
||||
)
|
||||
|
||||
for i, (etype, color) in enumerate(type_colors.items()):
|
||||
if etype != "default":
|
||||
y_pos = legend_y + 25 + i * 20
|
||||
svg_parts.append(
|
||||
f'<circle cx = "{legend_x + 10}" cy = "{y_pos}" r = "8" fill = "{color}"/>'
|
||||
f'<circle cx = "{legend_x + 10}" cy = "{y_pos}" r = "8" fill = "{color}"/>',
|
||||
)
|
||||
text_y = y_pos + 4
|
||||
svg_parts.append(
|
||||
f'<text x = "{legend_x + 25}" y = "{text_y}" font-size = "10" '
|
||||
f'fill = "#2c3e50">{etype}</text>'
|
||||
f'fill = "#2c3e50">{etype}</text>',
|
||||
)
|
||||
|
||||
svg_parts.append("</svg>")
|
||||
return "\n".join(svg_parts)
|
||||
|
||||
def export_knowledge_graph_png(
|
||||
self, project_id: str, entities: list[ExportEntity], relations: list[ExportRelation]
|
||||
self, project_id: str, entities: list[ExportEntity], relations: list[ExportRelation],
|
||||
) -> bytes:
|
||||
"""
|
||||
导出知识图谱为 PNG 格式
|
||||
@@ -337,7 +337,7 @@ class ExportManager:
|
||||
return output.getvalue()
|
||||
|
||||
def export_transcript_markdown(
|
||||
self, transcript: ExportTranscript, entities_map: dict[str, ExportEntity]
|
||||
self, transcript: ExportTranscript, entities_map: dict[str, ExportEntity],
|
||||
) -> str:
|
||||
"""
|
||||
导出转录文本为 Markdown 格式
|
||||
@@ -366,7 +366,7 @@ class ExportManager:
|
||||
[
|
||||
"## 分段详情",
|
||||
"",
|
||||
]
|
||||
],
|
||||
)
|
||||
for seg in transcript.segments:
|
||||
speaker = seg.get("speaker", "Unknown")
|
||||
@@ -384,7 +384,7 @@ class ExportManager:
|
||||
"",
|
||||
"| 实体 | 类型 | 位置 | 上下文 |",
|
||||
"|------|------|------|--------|",
|
||||
]
|
||||
],
|
||||
)
|
||||
for mention in transcript.entity_mentions:
|
||||
entity_id = mention.get("entity_id", "")
|
||||
@@ -417,7 +417,7 @@ class ExportManager:
|
||||
|
||||
output = io.BytesIO()
|
||||
doc = SimpleDocTemplate(
|
||||
output, pagesize=A4, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18
|
||||
output, pagesize=A4, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18,
|
||||
)
|
||||
|
||||
# 样式
|
||||
@@ -446,7 +446,7 @@ class ExportManager:
|
||||
Paragraph(
|
||||
f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
|
||||
styles["Normal"],
|
||||
)
|
||||
),
|
||||
)
|
||||
story.append(Spacer(1, 0.3 * inch))
|
||||
|
||||
@@ -479,8 +479,8 @@ class ExportManager:
|
||||
("BOTTOMPADDING", (0, 0), (-1, 0), 12),
|
||||
("BACKGROUND", (0, 1), (-1, -1), colors.HexColor("#ecf0f1")),
|
||||
("GRID", (0, 0), (-1, -1), 1, colors.HexColor("#bdc3c7")),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
story.append(stats_table)
|
||||
story.append(Spacer(1, 0.3 * inch))
|
||||
@@ -506,11 +506,11 @@ class ExportManager:
|
||||
e.type,
|
||||
str(e.mention_count),
|
||||
(e.definition[:100] + "...") if len(e.definition) > 100 else e.definition,
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
entity_table = Table(
|
||||
entity_data, colWidths=[1.5 * inch, 1 * inch, 1 * inch, 2.5 * inch]
|
||||
entity_data, colWidths=[1.5 * inch, 1 * inch, 1 * inch, 2.5 * inch],
|
||||
)
|
||||
entity_table.setStyle(
|
||||
TableStyle(
|
||||
@@ -524,8 +524,8 @@ class ExportManager:
|
||||
("BACKGROUND", (0, 1), (-1, -1), colors.HexColor("#ecf0f1")),
|
||||
("GRID", (0, 0), (-1, -1), 1, colors.HexColor("#bdc3c7")),
|
||||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
story.append(entity_table)
|
||||
|
||||
@@ -539,7 +539,7 @@ class ExportManager:
|
||||
relation_data.append([r.source, r.relation_type, r.target, f"{r.confidence:.2f}"])
|
||||
|
||||
relation_table = Table(
|
||||
relation_data, colWidths=[2 * inch, 1.5 * inch, 2 * inch, 1 * inch]
|
||||
relation_data, colWidths=[2 * inch, 1.5 * inch, 2 * inch, 1 * inch],
|
||||
)
|
||||
relation_table.setStyle(
|
||||
TableStyle(
|
||||
@@ -552,8 +552,8 @@ class ExportManager:
|
||||
("BOTTOMPADDING", (0, 0), (-1, 0), 12),
|
||||
("BACKGROUND", (0, 1), (-1, -1), colors.HexColor("#ecf0f1")),
|
||||
("GRID", (0, 0), (-1, -1), 1, colors.HexColor("#bdc3c7")),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
story.append(relation_table)
|
||||
|
||||
|
||||
@@ -475,7 +475,7 @@ class GrowthManager:
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.post(
|
||||
"https://api.mixpanel.com/track", headers=headers, json=[payload], timeout=10.0
|
||||
"https://api.mixpanel.com/track", headers=headers, json=[payload], timeout=10.0,
|
||||
)
|
||||
except (RuntimeError, ValueError, TypeError) as e:
|
||||
print(f"Failed to send to Mixpanel: {e}")
|
||||
@@ -494,7 +494,7 @@ class GrowthManager:
|
||||
"time": int(event.timestamp.timestamp() * 1000),
|
||||
"event_properties": event.properties,
|
||||
"user_properties": {},
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -509,7 +509,7 @@ class GrowthManager:
|
||||
print(f"Failed to send to Amplitude: {e}")
|
||||
|
||||
async def _update_user_profile(
|
||||
self, tenant_id: str, user_id: str, event_type: EventType, event_name: str
|
||||
self, tenant_id: str, user_id: str, event_type: EventType, event_name: str,
|
||||
) -> None:
|
||||
"""更新用户画像"""
|
||||
with self._get_db() as conn:
|
||||
@@ -581,7 +581,7 @@ class GrowthManager:
|
||||
return None
|
||||
|
||||
def get_user_analytics_summary(
|
||||
self, tenant_id: str, start_date: datetime = None, end_date: datetime = None
|
||||
self, tenant_id: str, start_date: datetime = None, end_date: datetime = None,
|
||||
) -> dict:
|
||||
"""获取用户分析汇总"""
|
||||
with self._get_db() as conn:
|
||||
@@ -635,7 +635,7 @@ class GrowthManager:
|
||||
}
|
||||
|
||||
def create_funnel(
|
||||
self, tenant_id: str, name: str, description: str, steps: list[dict], created_by: str
|
||||
self, tenant_id: str, name: str, description: str, steps: list[dict], created_by: str,
|
||||
) -> Funnel:
|
||||
"""创建转化漏斗"""
|
||||
funnel_id = f"fnl_{uuid.uuid4().hex[:16]}"
|
||||
@@ -673,12 +673,12 @@ class GrowthManager:
|
||||
return funnel
|
||||
|
||||
def analyze_funnel(
|
||||
self, funnel_id: str, period_start: datetime = None, period_end: datetime = None
|
||||
self, funnel_id: str, period_start: datetime = None, period_end: datetime = None,
|
||||
) -> FunnelAnalysis | None:
|
||||
"""分析漏斗转化率"""
|
||||
with self._get_db() as conn:
|
||||
funnel_row = conn.execute(
|
||||
"SELECT * FROM funnels WHERE id = ?", (funnel_id,)
|
||||
"SELECT * FROM funnels WHERE id = ?", (funnel_id,),
|
||||
).fetchone()
|
||||
|
||||
if not funnel_row:
|
||||
@@ -704,7 +704,7 @@ class GrowthManager:
|
||||
WHERE event_name = ? AND timestamp >= ? AND timestamp <= ?
|
||||
"""
|
||||
row = conn.execute(
|
||||
query, (event_name, period_start.isoformat(), period_end.isoformat())
|
||||
query, (event_name, period_start.isoformat(), period_end.isoformat()),
|
||||
).fetchone()
|
||||
|
||||
user_count = row["user_count"] if row else 0
|
||||
@@ -723,7 +723,7 @@ class GrowthManager:
|
||||
"user_count": user_count,
|
||||
"conversion_rate": round(conversion_rate, 4),
|
||||
"drop_off_rate": round(drop_off_rate, 4),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
previous_count = user_count
|
||||
@@ -752,7 +752,7 @@ class GrowthManager:
|
||||
)
|
||||
|
||||
def calculate_retention(
|
||||
self, tenant_id: str, cohort_date: datetime, periods: list[int] = None
|
||||
self, tenant_id: str, cohort_date: datetime, periods: list[int] = None,
|
||||
) -> dict:
|
||||
"""计算留存率"""
|
||||
if periods is None:
|
||||
@@ -893,7 +893,7 @@ class GrowthManager:
|
||||
"""获取实验详情"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM experiments WHERE id = ?", (experiment_id,)
|
||||
"SELECT * FROM experiments WHERE id = ?", (experiment_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -916,7 +916,7 @@ class GrowthManager:
|
||||
return [self._row_to_experiment(row) for row in rows]
|
||||
|
||||
def assign_variant(
|
||||
self, experiment_id: str, user_id: str, user_attributes: dict = None
|
||||
self, experiment_id: str, user_id: str, user_attributes: dict = None,
|
||||
) -> str | None:
|
||||
"""为用户分配实验变体"""
|
||||
experiment = self.get_experiment(experiment_id)
|
||||
@@ -939,11 +939,11 @@ class GrowthManager:
|
||||
variant_id = self._random_allocation(experiment.variants, experiment.traffic_split)
|
||||
elif experiment.traffic_allocation == TrafficAllocationType.STRATIFIED:
|
||||
variant_id = self._stratified_allocation(
|
||||
experiment.variants, experiment.traffic_split, user_attributes
|
||||
experiment.variants, experiment.traffic_split, user_attributes,
|
||||
)
|
||||
else: # TARGETED
|
||||
variant_id = self._targeted_allocation(
|
||||
experiment.variants, experiment.target_audience, user_attributes
|
||||
experiment.variants, experiment.target_audience, user_attributes,
|
||||
)
|
||||
|
||||
if variant_id:
|
||||
@@ -978,7 +978,7 @@ class GrowthManager:
|
||||
return random.choices(variant_ids, weights=normalized_weights, k=1)[0]
|
||||
|
||||
def _stratified_allocation(
|
||||
self, variants: list[dict], traffic_split: dict[str, float], user_attributes: dict
|
||||
self, variants: list[dict], traffic_split: dict[str, float], user_attributes: dict,
|
||||
) -> str:
|
||||
"""分层分配(基于用户属性)"""
|
||||
# 简化的分层分配:根据用户 ID 哈希值分配
|
||||
@@ -991,7 +991,7 @@ class GrowthManager:
|
||||
return self._random_allocation(variants, traffic_split)
|
||||
|
||||
def _targeted_allocation(
|
||||
self, variants: list[dict], target_audience: dict, user_attributes: dict
|
||||
self, variants: list[dict], target_audience: dict, user_attributes: dict,
|
||||
) -> str | None:
|
||||
"""定向分配(基于目标受众条件)"""
|
||||
# 检查用户是否符合目标受众条件
|
||||
@@ -1005,13 +1005,7 @@ class GrowthManager:
|
||||
|
||||
user_value = user_attributes.get(attr_name) if user_attributes else None
|
||||
|
||||
if operator == "equals" and user_value != value:
|
||||
matches = False
|
||||
break
|
||||
elif operator == "not_equals" and user_value == value:
|
||||
matches = False
|
||||
break
|
||||
elif operator == "in" and user_value not in value:
|
||||
if operator == "equals" and user_value != value or operator == "not_equals" and user_value == value or operator == "in" and user_value not in value:
|
||||
matches = False
|
||||
break
|
||||
|
||||
@@ -1248,7 +1242,7 @@ class GrowthManager:
|
||||
"""获取邮件模板"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM email_templates WHERE id = ?", (template_id,)
|
||||
"SELECT * FROM email_templates WHERE id = ?", (template_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1256,7 +1250,7 @@ class GrowthManager:
|
||||
return None
|
||||
|
||||
def list_email_templates(
|
||||
self, tenant_id: str, template_type: EmailTemplateType = None
|
||||
self, tenant_id: str, template_type: EmailTemplateType = None,
|
||||
) -> list[EmailTemplate]:
|
||||
"""列出邮件模板"""
|
||||
query = "SELECT * FROM email_templates WHERE tenant_id = ? AND is_active = 1"
|
||||
@@ -1383,7 +1377,7 @@ class GrowthManager:
|
||||
return campaign
|
||||
|
||||
async def send_email(
|
||||
self, campaign_id: str, user_id: str, email: str, template_id: str, variables: dict
|
||||
self, campaign_id: str, user_id: str, email: str, template_id: str, variables: dict,
|
||||
) -> bool:
|
||||
"""发送单封邮件"""
|
||||
template = self.get_email_template(template_id)
|
||||
@@ -1454,7 +1448,7 @@ class GrowthManager:
|
||||
"""发送整个营销活动"""
|
||||
with self._get_db() as conn:
|
||||
campaign_row = conn.execute(
|
||||
"SELECT * FROM email_campaigns WHERE id = ?", (campaign_id,)
|
||||
"SELECT * FROM email_campaigns WHERE id = ?", (campaign_id,),
|
||||
).fetchone()
|
||||
|
||||
if not campaign_row:
|
||||
@@ -1484,7 +1478,7 @@ class GrowthManager:
|
||||
variables = self._get_user_variables(log["tenant_id"], log["user_id"])
|
||||
|
||||
success = await self.send_email(
|
||||
campaign_id, log["user_id"], log["email"], log["template_id"], variables
|
||||
campaign_id, log["user_id"], log["email"], log["template_id"], variables,
|
||||
)
|
||||
|
||||
if success:
|
||||
@@ -1769,7 +1763,7 @@ class GrowthManager:
|
||||
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT 1 FROM referrals WHERE referral_code = ?", (code,)
|
||||
"SELECT 1 FROM referrals WHERE referral_code = ?", (code,),
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
@@ -1779,7 +1773,7 @@ class GrowthManager:
|
||||
"""获取推荐计划"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM referral_programs WHERE id = ?", (program_id,)
|
||||
"SELECT * FROM referral_programs WHERE id = ?", (program_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1865,7 +1859,7 @@ class GrowthManager:
|
||||
"expired": stats["expired"] or 0,
|
||||
"unique_referrers": stats["unique_referrers"] or 0,
|
||||
"conversion_rate": round(
|
||||
(stats["converted"] or 0) / max(stats["total_referrals"] or 1, 1), 4
|
||||
(stats["converted"] or 0) / max(stats["total_referrals"] or 1, 1), 4,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -1928,7 +1922,7 @@ class GrowthManager:
|
||||
return incentive
|
||||
|
||||
def check_team_incentive_eligibility(
|
||||
self, tenant_id: str, current_tier: str, team_size: int
|
||||
self, tenant_id: str, current_tier: str, team_size: int,
|
||||
) -> list[TeamIncentive]:
|
||||
"""检查团队激励资格"""
|
||||
with self._get_db() as conn:
|
||||
@@ -2007,7 +2001,7 @@ class GrowthManager:
|
||||
).fetchone()
|
||||
|
||||
hourly_trend.append(
|
||||
{"hour": hour_end.strftime("%H:00"), "active_users": row["count"] or 0}
|
||||
{"hour": hour_end.strftime("%H:00"), "active_users": row["count"] or 0},
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -328,7 +328,7 @@ class ImageProcessor:
|
||||
return unique_entities
|
||||
|
||||
def generate_description(
|
||||
self, image_type: str, ocr_text: str, entities: list[ImageEntity]
|
||||
self, image_type: str, ocr_text: str, entities: list[ImageEntity],
|
||||
) -> str:
|
||||
"""
|
||||
生成图片描述
|
||||
@@ -481,13 +481,13 @@ class ImageProcessor:
|
||||
target=sentence_entities[j].name,
|
||||
relation_type="related",
|
||||
confidence=0.5,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return relations
|
||||
|
||||
def process_batch(
|
||||
self, images_data: list[tuple[bytes, str]], project_id: str = None
|
||||
self, images_data: list[tuple[bytes, str]], project_id: str = None,
|
||||
) -> BatchProcessingResult:
|
||||
"""
|
||||
批量处理图片
|
||||
|
||||
@@ -4,7 +4,6 @@ InsightFlow Knowledge Reasoning - Phase 5
|
||||
知识推理与问答增强模块
|
||||
"""
|
||||
|
||||
import json
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@@ -83,7 +82,7 @@ class KnowledgeReasoner:
|
||||
return result["choices"][0]["message"]["content"]
|
||||
|
||||
async def enhanced_qa(
|
||||
self, query: str, project_context: dict, graph_data: dict, reasoning_depth: str = "medium"
|
||||
self, query: str, project_context: dict, graph_data: dict, reasoning_depth: str = "medium",
|
||||
) -> ReasoningResult:
|
||||
"""
|
||||
增强问答 - 结合图谱推理的问答
|
||||
@@ -140,7 +139,7 @@ class KnowledgeReasoner:
|
||||
return {"type": "factual", "entities": [], "intent": "general", "complexity": "simple"}
|
||||
|
||||
async def _causal_reasoning(
|
||||
self, query: str, project_context: dict, graph_data: dict
|
||||
self, query: str, project_context: dict, graph_data: dict,
|
||||
) -> ReasoningResult:
|
||||
"""因果推理 - 分析原因和影响"""
|
||||
|
||||
@@ -201,7 +200,7 @@ class KnowledgeReasoner:
|
||||
)
|
||||
|
||||
async def _comparative_reasoning(
|
||||
self, query: str, project_context: dict, graph_data: dict
|
||||
self, query: str, project_context: dict, graph_data: dict,
|
||||
) -> ReasoningResult:
|
||||
"""对比推理 - 比较实体间的异同"""
|
||||
|
||||
@@ -255,7 +254,7 @@ class KnowledgeReasoner:
|
||||
)
|
||||
|
||||
async def _temporal_reasoning(
|
||||
self, query: str, project_context: dict, graph_data: dict
|
||||
self, query: str, project_context: dict, graph_data: dict,
|
||||
) -> ReasoningResult:
|
||||
"""时序推理 - 分析时间线和演变"""
|
||||
|
||||
@@ -309,7 +308,7 @@ class KnowledgeReasoner:
|
||||
)
|
||||
|
||||
async def _associative_reasoning(
|
||||
self, query: str, project_context: dict, graph_data: dict
|
||||
self, query: str, project_context: dict, graph_data: dict,
|
||||
) -> ReasoningResult:
|
||||
"""关联推理 - 发现实体间的隐含关联"""
|
||||
|
||||
@@ -363,7 +362,7 @@ class KnowledgeReasoner:
|
||||
)
|
||||
|
||||
def find_inference_paths(
|
||||
self, start_entity: str, end_entity: str, graph_data: dict, max_depth: int = 3
|
||||
self, start_entity: str, end_entity: str, graph_data: dict, max_depth: int = 3,
|
||||
) -> list[InferencePath]:
|
||||
"""
|
||||
发现两个实体之间的推理路径
|
||||
@@ -384,7 +383,7 @@ class KnowledgeReasoner:
|
||||
adj[src].append({"target": tgt, "relation": r.get("type", "related"), "data": r})
|
||||
# 无向图也添加反向
|
||||
adj[tgt].append(
|
||||
{"target": src, "relation": r.get("type", "related"), "data": r, "reverse": True}
|
||||
{"target": src, "relation": r.get("type", "related"), "data": r, "reverse": True},
|
||||
)
|
||||
|
||||
# BFS 搜索路径
|
||||
@@ -405,7 +404,7 @@ class KnowledgeReasoner:
|
||||
end_entity=end_entity,
|
||||
path=path,
|
||||
strength=self._calculate_path_strength(path),
|
||||
)
|
||||
),
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -420,7 +419,7 @@ class KnowledgeReasoner:
|
||||
"entity": next_entity,
|
||||
"relation": neighbor["relation"],
|
||||
"relation_data": neighbor.get("data", {}),
|
||||
}
|
||||
},
|
||||
]
|
||||
queue.append((next_entity, new_path))
|
||||
|
||||
@@ -450,7 +449,7 @@ class KnowledgeReasoner:
|
||||
return length_factor * confidence_factor
|
||||
|
||||
async def summarize_project(
|
||||
self, project_context: dict, graph_data: dict, summary_type: str = "comprehensive"
|
||||
self, project_context: dict, graph_data: dict, summary_type: str = "comprehensive",
|
||||
) -> dict:
|
||||
"""
|
||||
项目智能总结
|
||||
|
||||
@@ -52,7 +52,7 @@ class LLMClient:
|
||||
}
|
||||
|
||||
async def chat(
|
||||
self, messages: list[ChatMessage], temperature: float = 0.3, stream: bool = False
|
||||
self, messages: list[ChatMessage], temperature: float = 0.3, stream: bool = False,
|
||||
) -> str:
|
||||
"""发送聊天请求"""
|
||||
if not self.api_key:
|
||||
@@ -77,7 +77,7 @@ class LLMClient:
|
||||
return result["choices"][0]["message"]["content"]
|
||||
|
||||
async def chat_stream(
|
||||
self, messages: list[ChatMessage], temperature: float = 0.3
|
||||
self, messages: list[ChatMessage], temperature: float = 0.3,
|
||||
) -> AsyncGenerator[str, None]:
|
||||
"""流式聊天请求"""
|
||||
if not self.api_key:
|
||||
@@ -90,30 +90,29 @@ class LLMClient:
|
||||
"stream": True,
|
||||
}
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
async with client.stream(
|
||||
"POST",
|
||||
f"{self.base_url}/v1/chat/completions",
|
||||
headers=self.headers,
|
||||
json=payload,
|
||||
timeout=120.0,
|
||||
) as response:
|
||||
response.raise_for_status()
|
||||
async for line in response.aiter_lines():
|
||||
if line.startswith("data: "):
|
||||
data = line[6:]
|
||||
if data == "[DONE]":
|
||||
break
|
||||
try:
|
||||
chunk = json.loads(data)
|
||||
delta = chunk["choices"][0]["delta"]
|
||||
if "content" in delta:
|
||||
yield delta["content"]
|
||||
except (json.JSONDecodeError, KeyError, IndexError):
|
||||
pass
|
||||
async with httpx.AsyncClient() as client, client.stream(
|
||||
"POST",
|
||||
f"{self.base_url}/v1/chat/completions",
|
||||
headers=self.headers,
|
||||
json=payload,
|
||||
timeout=120.0,
|
||||
) as response:
|
||||
response.raise_for_status()
|
||||
async for line in response.aiter_lines():
|
||||
if line.startswith("data: "):
|
||||
data = line[6:]
|
||||
if data == "[DONE]":
|
||||
break
|
||||
try:
|
||||
chunk = json.loads(data)
|
||||
delta = chunk["choices"][0]["delta"]
|
||||
if "content" in delta:
|
||||
yield delta["content"]
|
||||
except (json.JSONDecodeError, KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def extract_entities_with_confidence(
|
||||
self, text: str
|
||||
self, text: str,
|
||||
) -> tuple[list[EntityExtractionResult], list[RelationExtractionResult]]:
|
||||
"""提取实体和关系,带置信度分数"""
|
||||
prompt = f"""从以下会议文本中提取关键实体和它们之间的关系,以 JSON 格式返回:
|
||||
@@ -190,7 +189,7 @@ class LLMClient:
|
||||
|
||||
messages = [
|
||||
ChatMessage(
|
||||
role="system", content="你是一个专业的项目分析助手,擅长从会议记录中提取洞察。"
|
||||
role="system", content="你是一个专业的项目分析助手,擅长从会议记录中提取洞察。",
|
||||
),
|
||||
ChatMessage(role="user", content=prompt),
|
||||
]
|
||||
@@ -241,7 +240,7 @@ class LLMClient:
|
||||
[
|
||||
f"[{m.get('created_at', '未知时间')}] {m.get('text_snippet', '')}"
|
||||
for m in mentions[:20]
|
||||
] # 限制数量
|
||||
], # 限制数量
|
||||
)
|
||||
|
||||
prompt = f"""分析实体 "{entity_name}" 在项目中的演变和态度变化:
|
||||
|
||||
@@ -830,30 +830,30 @@ class LocalizationManager:
|
||||
""")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_translations_key ON translations(key)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_translations_lang ON translations(language)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_translations_lang ON translations(language)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_translations_ns ON translations(namespace)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_translations_ns ON translations(namespace)",
|
||||
)
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_dc_region ON data_centers(region_code)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_dc_status ON data_centers(status)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_tenant_dc ON tenant_data_center_mappings(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_tenant_dc ON tenant_data_center_mappings(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_payment_provider ON localized_payment_methods(provider)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_payment_provider ON localized_payment_methods(provider)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_payment_active ON localized_payment_methods(is_active)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_payment_active ON localized_payment_methods(is_active)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_country_region ON country_configs(region)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_country_region ON country_configs(region)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_tz_country ON timezone_configs(country_code)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_tz_country ON timezone_configs(country_code)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_locale_settings_tenant ON localization_settings(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_locale_settings_tenant ON localization_settings(tenant_id)",
|
||||
)
|
||||
conn.commit()
|
||||
logger.info("Localization tables initialized successfully")
|
||||
@@ -963,7 +963,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def get_translation(
|
||||
self, key: str, language: str, namespace: str = "common", fallback: bool = True
|
||||
self, key: str, language: str, namespace: str = "common", fallback: bool = True,
|
||||
) -> str | None:
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
@@ -979,7 +979,7 @@ class LocalizationManager:
|
||||
lang_config = self.get_language_config(language)
|
||||
if lang_config and lang_config.fallback_language:
|
||||
return self.get_translation(
|
||||
key, lang_config.fallback_language, namespace, False
|
||||
key, lang_config.fallback_language, namespace, False,
|
||||
)
|
||||
if language != "en":
|
||||
return self.get_translation(key, "en", namespace, False)
|
||||
@@ -1019,7 +1019,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def _get_translation_internal(
|
||||
self, conn: sqlite3.Connection, key: str, language: str, namespace: str
|
||||
self, conn: sqlite3.Connection, key: str, language: str, namespace: str,
|
||||
) -> Translation | None:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
@@ -1121,7 +1121,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def list_data_centers(
|
||||
self, status: str | None = None, region: str | None = None
|
||||
self, status: str | None = None, region: str | None = None,
|
||||
) -> list[DataCenter]:
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
@@ -1146,7 +1146,7 @@ class LocalizationManager:
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT * FROM tenant_data_center_mappings WHERE tenant_id = ?", (tenant_id,)
|
||||
"SELECT * FROM tenant_data_center_mappings WHERE tenant_id = ?", (tenant_id,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
@@ -1156,7 +1156,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def set_tenant_data_center(
|
||||
self, tenant_id: str, region_code: str, data_residency: str = "regional"
|
||||
self, tenant_id: str, region_code: str, data_residency: str = "regional",
|
||||
) -> TenantDataCenterMapping:
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
@@ -1222,7 +1222,7 @@ class LocalizationManager:
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT * FROM localized_payment_methods WHERE provider = ?", (provider,)
|
||||
"SELECT * FROM localized_payment_methods WHERE provider = ?", (provider,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
@@ -1232,7 +1232,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def list_payment_methods(
|
||||
self, country_code: str | None = None, currency: str | None = None, active_only: bool = True
|
||||
self, country_code: str | None = None, currency: str | None = None, active_only: bool = True,
|
||||
) -> list[LocalizedPaymentMethod]:
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
@@ -1255,7 +1255,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def get_localized_payment_methods(
|
||||
self, country_code: str, language: str = "en"
|
||||
self, country_code: str, language: str = "en",
|
||||
) -> list[dict[str, Any]]:
|
||||
methods = self.list_payment_methods(country_code=country_code)
|
||||
result = []
|
||||
@@ -1270,7 +1270,7 @@ class LocalizationManager:
|
||||
"min_amount": method.min_amount,
|
||||
"max_amount": method.max_amount,
|
||||
"supported_currencies": method.supported_currencies,
|
||||
}
|
||||
},
|
||||
)
|
||||
return result
|
||||
|
||||
@@ -1287,7 +1287,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def list_country_configs(
|
||||
self, region: str | None = None, active_only: bool = True
|
||||
self, region: str | None = None, active_only: bool = True,
|
||||
) -> list[CountryConfig]:
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
@@ -1345,14 +1345,14 @@ class LocalizationManager:
|
||||
return dt.strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
def format_number(
|
||||
self, number: float, language: str = "en", decimal_places: int | None = None
|
||||
self, number: float, language: str = "en", decimal_places: int | None = None,
|
||||
) -> str:
|
||||
try:
|
||||
if BABEL_AVAILABLE:
|
||||
try:
|
||||
locale = Locale.parse(language.replace("_", "-"))
|
||||
return numbers.format_decimal(
|
||||
number, locale=locale, decimal_quantization=(decimal_places is not None)
|
||||
number, locale=locale, decimal_quantization=(decimal_places is not None),
|
||||
)
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
@@ -1514,7 +1514,7 @@ class LocalizationManager:
|
||||
self._close_if_file_db(conn)
|
||||
|
||||
def detect_user_preferences(
|
||||
self, accept_language: str | None = None, ip_country: str | None = None
|
||||
self, accept_language: str | None = None, ip_country: str | None = None,
|
||||
) -> dict[str, str]:
|
||||
preferences = {"language": "en", "country": "US", "timezone": "UTC", "currency": "USD"}
|
||||
if accept_language:
|
||||
|
||||
467
backend/main.py
467
backend/main.py
File diff suppressed because it is too large
Load Diff
@@ -137,7 +137,7 @@ class MultimodalEntityLinker:
|
||||
"""
|
||||
# 名称相似度
|
||||
name_sim = self.calculate_string_similarity(
|
||||
entity1.get("name", ""), entity2.get("name", "")
|
||||
entity1.get("name", ""), entity2.get("name", ""),
|
||||
)
|
||||
|
||||
# 如果名称完全匹配
|
||||
@@ -158,7 +158,7 @@ class MultimodalEntityLinker:
|
||||
|
||||
# 定义相似度
|
||||
def_sim = self.calculate_string_similarity(
|
||||
entity1.get("definition", ""), entity2.get("definition", "")
|
||||
entity1.get("definition", ""), entity2.get("definition", ""),
|
||||
)
|
||||
|
||||
# 综合相似度
|
||||
@@ -170,7 +170,7 @@ class MultimodalEntityLinker:
|
||||
return combined_sim, "none"
|
||||
|
||||
def find_matching_entity(
|
||||
self, query_entity: dict, candidate_entities: list[dict], exclude_ids: set[str] = None
|
||||
self, query_entity: dict, candidate_entities: list[dict], exclude_ids: set[str] = None,
|
||||
) -> AlignmentResult | None:
|
||||
"""
|
||||
在候选实体中查找匹配的实体
|
||||
@@ -270,7 +270,7 @@ class MultimodalEntityLinker:
|
||||
return links
|
||||
|
||||
def fuse_entity_knowledge(
|
||||
self, entity_id: str, linked_entities: list[dict], multimodal_mentions: list[dict]
|
||||
self, entity_id: str, linked_entities: list[dict], multimodal_mentions: list[dict],
|
||||
) -> FusionResult:
|
||||
"""
|
||||
融合多模态实体知识
|
||||
@@ -388,13 +388,13 @@ class MultimodalEntityLinker:
|
||||
"entities": group,
|
||||
"type": "homonym_conflict",
|
||||
"suggestion": "Consider disambiguating these entities",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return conflicts
|
||||
|
||||
def suggest_entity_merges(
|
||||
self, entities: list[dict], existing_links: list[EntityLink] = None
|
||||
self, entities: list[dict], existing_links: list[EntityLink] = None,
|
||||
) -> list[dict]:
|
||||
"""
|
||||
建议实体合并
|
||||
@@ -437,7 +437,7 @@ class MultimodalEntityLinker:
|
||||
"similarity": similarity,
|
||||
"match_type": match_type,
|
||||
"suggested_action": "merge" if similarity > 0.95 else "link",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# 按相似度排序
|
||||
@@ -489,7 +489,7 @@ class MultimodalEntityLinker:
|
||||
Returns:
|
||||
模态分布统计
|
||||
"""
|
||||
distribution = {mod: 0 for mod in self.MODALITIES}
|
||||
distribution = dict.fromkeys(self.MODALITIES, 0)
|
||||
|
||||
# 统计每个模态的实体数
|
||||
for me in multimodal_entities:
|
||||
|
||||
@@ -130,10 +130,10 @@ class MultimodalProcessor:
|
||||
if FFMPEG_AVAILABLE:
|
||||
probe = ffmpeg.probe(video_path)
|
||||
video_stream = next(
|
||||
(s for s in probe["streams"] if s["codec_type"] == "video"), None
|
||||
(s for s in probe["streams"] if s["codec_type"] == "video"), None,
|
||||
)
|
||||
audio_stream = next(
|
||||
(s for s in probe["streams"] if s["codec_type"] == "audio"), None
|
||||
(s for s in probe["streams"] if s["codec_type"] == "audio"), None,
|
||||
)
|
||||
|
||||
if video_stream:
|
||||
@@ -260,7 +260,7 @@ class MultimodalProcessor:
|
||||
if frame_number % frame_interval_frames == 0:
|
||||
timestamp = frame_number / fps
|
||||
frame_path = os.path.join(
|
||||
video_frames_dir, f"frame_{frame_number:06d}_{timestamp:.2f}.jpg"
|
||||
video_frames_dir, f"frame_{frame_number:06d}_{timestamp:.2f}.jpg",
|
||||
)
|
||||
cv2.imwrite(frame_path, frame)
|
||||
frame_paths.append(frame_path)
|
||||
@@ -292,7 +292,7 @@ class MultimodalProcessor:
|
||||
os.path.join(video_frames_dir, f)
|
||||
for f in os.listdir(video_frames_dir)
|
||||
if f.startswith("frame_")
|
||||
]
|
||||
],
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error extracting keyframes: {e}")
|
||||
@@ -333,7 +333,7 @@ class MultimodalProcessor:
|
||||
return "", 0.0
|
||||
|
||||
def process_video(
|
||||
self, video_data: bytes, filename: str, project_id: str, video_id: str = None
|
||||
self, video_data: bytes, filename: str, project_id: str, video_id: str = None,
|
||||
) -> VideoProcessingResult:
|
||||
"""
|
||||
处理视频文件:提取音频、关键帧、OCR
|
||||
@@ -399,7 +399,7 @@ class MultimodalProcessor:
|
||||
"timestamp": timestamp,
|
||||
"text": ocr_text,
|
||||
"confidence": confidence,
|
||||
}
|
||||
},
|
||||
)
|
||||
all_ocr_text.append(ocr_text)
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ class Neo4jManager:
|
||||
# ==================== 数据同步 ====================
|
||||
|
||||
def sync_project(
|
||||
self, project_id: str, project_name: str, project_description: str = ""
|
||||
self, project_id: str, project_name: str, project_description: str = "",
|
||||
) -> None:
|
||||
"""同步项目节点到 Neo4j"""
|
||||
if not self._driver:
|
||||
@@ -352,7 +352,7 @@ class Neo4jManager:
|
||||
# ==================== 复杂图查询 ====================
|
||||
|
||||
def find_shortest_path(
|
||||
self, source_id: str, target_id: str, max_depth: int = 10
|
||||
self, source_id: str, target_id: str, max_depth: int = 10,
|
||||
) -> PathResult | None:
|
||||
"""
|
||||
查找两个实体之间的最短路径
|
||||
@@ -404,11 +404,11 @@ class Neo4jManager:
|
||||
]
|
||||
|
||||
return PathResult(
|
||||
nodes=nodes, relationships=relationships, length=len(path.relationships)
|
||||
nodes=nodes, relationships=relationships, length=len(path.relationships),
|
||||
)
|
||||
|
||||
def find_all_paths(
|
||||
self, source_id: str, target_id: str, max_depth: int = 5, limit: int = 10
|
||||
self, source_id: str, target_id: str, max_depth: int = 5, limit: int = 10,
|
||||
) -> list[PathResult]:
|
||||
"""
|
||||
查找两个实体之间的所有路径
|
||||
@@ -460,14 +460,14 @@ class Neo4jManager:
|
||||
|
||||
paths.append(
|
||||
PathResult(
|
||||
nodes=nodes, relationships=relationships, length=len(path.relationships)
|
||||
)
|
||||
nodes=nodes, relationships=relationships, length=len(path.relationships),
|
||||
),
|
||||
)
|
||||
|
||||
return paths
|
||||
|
||||
def find_neighbors(
|
||||
self, entity_id: str, relation_type: str = None, limit: int = 50
|
||||
self, entity_id: str, relation_type: str = None, limit: int = 50,
|
||||
) -> list[dict]:
|
||||
"""
|
||||
查找实体的邻居节点
|
||||
@@ -516,7 +516,7 @@ class Neo4jManager:
|
||||
"type": node["type"],
|
||||
"relation_type": record["rel_type"],
|
||||
"evidence": record["evidence"],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return neighbors
|
||||
@@ -628,7 +628,7 @@ class Neo4jManager:
|
||||
entity_name=record["entity_name"],
|
||||
score=record["score"],
|
||||
rank=rank,
|
||||
)
|
||||
),
|
||||
)
|
||||
rank += 1
|
||||
|
||||
@@ -680,7 +680,7 @@ class Neo4jManager:
|
||||
entity_name=record["entity_name"],
|
||||
score=float(record["score"]),
|
||||
rank=rank,
|
||||
)
|
||||
),
|
||||
)
|
||||
rank += 1
|
||||
|
||||
@@ -737,7 +737,7 @@ class Neo4jManager:
|
||||
"name": record["entity_name"],
|
||||
"type": record["entity_type"],
|
||||
"connections": record["connection_count"],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# 构建结果
|
||||
@@ -752,8 +752,8 @@ class Neo4jManager:
|
||||
|
||||
results.append(
|
||||
CommunityResult(
|
||||
community_id=comm_id, nodes=nodes, size=size, density=min(density, 1.0)
|
||||
)
|
||||
community_id=comm_id, nodes=nodes, size=size, density=min(density, 1.0),
|
||||
),
|
||||
)
|
||||
|
||||
# 按大小排序
|
||||
@@ -761,7 +761,7 @@ class Neo4jManager:
|
||||
return results
|
||||
|
||||
def find_central_entities(
|
||||
self, project_id: str, metric: str = "degree"
|
||||
self, project_id: str, metric: str = "degree",
|
||||
) -> list[CentralityResult]:
|
||||
"""
|
||||
查找中心实体
|
||||
@@ -812,7 +812,7 @@ class Neo4jManager:
|
||||
entity_name=record["entity_name"],
|
||||
score=float(record["score"]),
|
||||
rank=rank,
|
||||
)
|
||||
),
|
||||
)
|
||||
rank += 1
|
||||
|
||||
@@ -942,7 +942,7 @@ class Neo4jManager:
|
||||
"name": node["name"],
|
||||
"type": node["type"],
|
||||
"definition": node.get("definition", ""),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# 获取这些节点之间的关系
|
||||
@@ -993,7 +993,7 @@ def close_neo4j_manager() -> None:
|
||||
|
||||
|
||||
def sync_project_to_neo4j(
|
||||
project_id: str, project_name: str, entities: list[dict], relations: list[dict]
|
||||
project_id: str, project_name: str, entities: list[dict], relations: list[dict],
|
||||
) -> None:
|
||||
"""
|
||||
同步整个项目到 Neo4j
|
||||
@@ -1042,7 +1042,7 @@ def sync_project_to_neo4j(
|
||||
manager.sync_relations_batch(graph_relations)
|
||||
|
||||
logger.info(
|
||||
f"Synced project {project_id} to Neo4j: {len(entities)} entities, {len(relations)} relations"
|
||||
f"Synced project {project_id} to Neo4j: {len(entities)} entities, {len(relations)} relations",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -604,7 +604,7 @@ class OpsManager:
|
||||
updates["updated_at"] = datetime.now().isoformat()
|
||||
|
||||
with self._get_db() as conn:
|
||||
set_clause = ", ".join([f"{k} = ?" for k in updates.keys()])
|
||||
set_clause = ", ".join([f"{k} = ?" for k in updates])
|
||||
conn.execute(
|
||||
f"UPDATE alert_rules SET {set_clause} WHERE id = ?",
|
||||
list(updates.values()) + [rule_id],
|
||||
@@ -680,7 +680,7 @@ class OpsManager:
|
||||
"""获取告警渠道"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM alert_channels WHERE id = ?", (channel_id,)
|
||||
"SELECT * FROM alert_channels WHERE id = ?", (channel_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -819,7 +819,7 @@ class OpsManager:
|
||||
for rule in rules:
|
||||
# 获取相关指标
|
||||
metrics = self.get_recent_metrics(
|
||||
tenant_id, rule.metric, seconds=rule.duration + rule.evaluation_interval
|
||||
tenant_id, rule.metric, seconds=rule.duration + rule.evaluation_interval,
|
||||
)
|
||||
|
||||
# 评估规则
|
||||
@@ -1129,7 +1129,7 @@ class OpsManager:
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
"https://events.pagerduty.com/v2/enqueue", json=message, timeout=30.0
|
||||
"https://events.pagerduty.com/v2/enqueue", json=message, timeout=30.0,
|
||||
)
|
||||
success = response.status_code == 202
|
||||
self._update_channel_stats(channel.id, success)
|
||||
@@ -1299,12 +1299,12 @@ class OpsManager:
|
||||
conn.commit()
|
||||
|
||||
def _update_alert_notification_status(
|
||||
self, alert_id: str, channel_id: str, success: bool
|
||||
self, alert_id: str, channel_id: str, success: bool,
|
||||
) -> None:
|
||||
"""更新告警通知状态"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT notification_sent FROM alerts WHERE id = ?", (alert_id,)
|
||||
"SELECT notification_sent FROM alerts WHERE id = ?", (alert_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1394,7 +1394,7 @@ class OpsManager:
|
||||
"""检查告警是否被抑制"""
|
||||
with self._get_db() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM alert_suppression_rules WHERE tenant_id = ?", (rule.tenant_id,)
|
||||
"SELECT * FROM alert_suppression_rules WHERE tenant_id = ?", (rule.tenant_id,),
|
||||
).fetchall()
|
||||
|
||||
for row in rows:
|
||||
@@ -1479,7 +1479,7 @@ class OpsManager:
|
||||
return metric
|
||||
|
||||
def get_recent_metrics(
|
||||
self, tenant_id: str, metric_name: str, seconds: int = 3600
|
||||
self, tenant_id: str, metric_name: str, seconds: int = 3600,
|
||||
) -> list[ResourceMetric]:
|
||||
"""获取最近的指标数据"""
|
||||
cutoff_time = (datetime.now() - timedelta(seconds=seconds)).isoformat()
|
||||
@@ -1531,7 +1531,7 @@ class OpsManager:
|
||||
|
||||
# 基于历史数据预测
|
||||
metrics = self.get_recent_metrics(
|
||||
tenant_id, f"{resource_type.value}_usage", seconds=30 * 24 * 3600
|
||||
tenant_id, f"{resource_type.value}_usage", seconds=30 * 24 * 3600,
|
||||
)
|
||||
|
||||
if metrics:
|
||||
@@ -1704,7 +1704,7 @@ class OpsManager:
|
||||
"""获取自动扩缩容策略"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM auto_scaling_policies WHERE id = ?", (policy_id,)
|
||||
"SELECT * FROM auto_scaling_policies WHERE id = ?", (policy_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -1721,7 +1721,7 @@ class OpsManager:
|
||||
return [self._row_to_auto_scaling_policy(row) for row in rows]
|
||||
|
||||
def evaluate_scaling_policy(
|
||||
self, policy_id: str, current_instances: int, current_utilization: float
|
||||
self, policy_id: str, current_instances: int, current_utilization: float,
|
||||
) -> ScalingEvent | None:
|
||||
"""评估扩缩容策略"""
|
||||
policy = self.get_auto_scaling_policy(policy_id)
|
||||
@@ -1826,7 +1826,7 @@ class OpsManager:
|
||||
return None
|
||||
|
||||
def update_scaling_event_status(
|
||||
self, event_id: str, status: str, error_message: str = None
|
||||
self, event_id: str, status: str, error_message: str = None,
|
||||
) -> ScalingEvent | None:
|
||||
"""更新扩缩容事件状态"""
|
||||
now = datetime.now().isoformat()
|
||||
@@ -1864,7 +1864,7 @@ class OpsManager:
|
||||
return None
|
||||
|
||||
def list_scaling_events(
|
||||
self, tenant_id: str, policy_id: str = None, limit: int = 100
|
||||
self, tenant_id: str, policy_id: str = None, limit: int = 100,
|
||||
) -> list[ScalingEvent]:
|
||||
"""列出租户的扩缩容事件"""
|
||||
query = "SELECT * FROM scaling_events WHERE tenant_id = ?"
|
||||
@@ -2056,7 +2056,7 @@ class OpsManager:
|
||||
start_time = time.time()
|
||||
try:
|
||||
reader, writer = await asyncio.wait_for(
|
||||
asyncio.open_connection(host, port), timeout=check.timeout
|
||||
asyncio.open_connection(host, port), timeout=check.timeout,
|
||||
)
|
||||
response_time = (time.time() - start_time) * 1000
|
||||
writer.close()
|
||||
@@ -2153,7 +2153,7 @@ class OpsManager:
|
||||
"""获取故障转移配置"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM failover_configs WHERE id = ?", (config_id,)
|
||||
"SELECT * FROM failover_configs WHERE id = ?", (config_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -2259,7 +2259,7 @@ class OpsManager:
|
||||
"""获取故障转移事件"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM failover_events WHERE id = ?", (event_id,)
|
||||
"SELECT * FROM failover_events WHERE id = ?", (event_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -2430,7 +2430,7 @@ class OpsManager:
|
||||
"""获取备份记录"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM backup_records WHERE id = ?", (record_id,)
|
||||
"SELECT * FROM backup_records WHERE id = ?", (record_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
@@ -2438,7 +2438,7 @@ class OpsManager:
|
||||
return None
|
||||
|
||||
def list_backup_records(
|
||||
self, tenant_id: str, job_id: str = None, limit: int = 100
|
||||
self, tenant_id: str, job_id: str = None, limit: int = 100,
|
||||
) -> list[BackupRecord]:
|
||||
"""列出租户的备份记录"""
|
||||
query = "SELECT * FROM backup_records WHERE tenant_id = ?"
|
||||
@@ -2544,7 +2544,7 @@ class OpsManager:
|
||||
"resource_id": util.resource_id,
|
||||
"utilization_rate": util.utilization_rate,
|
||||
"severity": "high" if util.utilization_rate < 0.05 else "medium",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# 检测高峰利用率
|
||||
@@ -2556,7 +2556,7 @@ class OpsManager:
|
||||
"resource_id": util.resource_id,
|
||||
"peak_utilization": util.peak_utilization,
|
||||
"severity": "medium",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return anomalies
|
||||
@@ -2624,7 +2624,7 @@ class OpsManager:
|
||||
return util
|
||||
|
||||
def get_resource_utilizations(
|
||||
self, tenant_id: str, report_period: str
|
||||
self, tenant_id: str, report_period: str,
|
||||
) -> list[ResourceUtilization]:
|
||||
"""获取资源利用率列表"""
|
||||
with self._get_db() as conn:
|
||||
@@ -2709,7 +2709,7 @@ class OpsManager:
|
||||
return [self._row_to_idle_resource(row) for row in rows]
|
||||
|
||||
def generate_cost_optimization_suggestions(
|
||||
self, tenant_id: str
|
||||
self, tenant_id: str,
|
||||
) -> list[CostOptimizationSuggestion]:
|
||||
"""生成成本优化建议"""
|
||||
suggestions = []
|
||||
@@ -2777,7 +2777,7 @@ class OpsManager:
|
||||
return suggestions
|
||||
|
||||
def get_cost_optimization_suggestions(
|
||||
self, tenant_id: str, is_applied: bool = None
|
||||
self, tenant_id: str, is_applied: bool = None,
|
||||
) -> list[CostOptimizationSuggestion]:
|
||||
"""获取成本优化建议"""
|
||||
query = "SELECT * FROM cost_optimization_suggestions WHERE tenant_id = ?"
|
||||
@@ -2794,7 +2794,7 @@ class OpsManager:
|
||||
return [self._row_to_cost_optimization_suggestion(row) for row in rows]
|
||||
|
||||
def apply_cost_optimization_suggestion(
|
||||
self, suggestion_id: str
|
||||
self, suggestion_id: str,
|
||||
) -> CostOptimizationSuggestion | None:
|
||||
"""应用成本优化建议"""
|
||||
now = datetime.now().isoformat()
|
||||
@@ -2813,12 +2813,12 @@ class OpsManager:
|
||||
return self.get_cost_optimization_suggestion(suggestion_id)
|
||||
|
||||
def get_cost_optimization_suggestion(
|
||||
self, suggestion_id: str
|
||||
self, suggestion_id: str,
|
||||
) -> CostOptimizationSuggestion | None:
|
||||
"""获取成本优化建议详情"""
|
||||
with self._get_db() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM cost_optimization_suggestions WHERE id = ?", (suggestion_id,)
|
||||
"SELECT * FROM cost_optimization_suggestions WHERE id = ?", (suggestion_id,),
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
|
||||
@@ -221,10 +221,10 @@ class CacheManager:
|
||||
""")
|
||||
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_metrics_type ON performance_metrics(metric_type)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_metrics_type ON performance_metrics(metric_type)",
|
||||
)
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_metrics_time ON performance_metrics(timestamp)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_metrics_time ON performance_metrics(timestamp)",
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
@@ -444,10 +444,10 @@ class CacheManager:
|
||||
"memory_size_bytes": self.current_memory_size,
|
||||
"max_memory_size_bytes": self.max_memory_size,
|
||||
"memory_usage_percent": round(
|
||||
self.current_memory_size / self.max_memory_size * 100, 2
|
||||
self.current_memory_size / self.max_memory_size * 100, 2,
|
||||
),
|
||||
"cache_entries": len(self.memory_cache),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return stats
|
||||
@@ -548,11 +548,11 @@ class CacheManager:
|
||||
|
||||
# 预热项目知识库摘要
|
||||
entity_count = conn.execute(
|
||||
"SELECT COUNT(*) FROM entities WHERE project_id = ?", (project_id,)
|
||||
"SELECT COUNT(*) FROM entities WHERE project_id = ?", (project_id,),
|
||||
).fetchone()[0]
|
||||
|
||||
relation_count = conn.execute(
|
||||
"SELECT COUNT(*) FROM entity_relations WHERE project_id = ?", (project_id,)
|
||||
"SELECT COUNT(*) FROM entity_relations WHERE project_id = ?", (project_id,),
|
||||
).fetchone()[0]
|
||||
|
||||
summary = {
|
||||
@@ -757,11 +757,11 @@ class DatabaseSharding:
|
||||
source_conn.row_factory = sqlite3.Row
|
||||
|
||||
entities = source_conn.execute(
|
||||
"SELECT * FROM entities WHERE project_id = ?", (project_id,)
|
||||
"SELECT * FROM entities WHERE project_id = ?", (project_id,),
|
||||
).fetchall()
|
||||
|
||||
relations = source_conn.execute(
|
||||
"SELECT * FROM entity_relations WHERE project_id = ?", (project_id,)
|
||||
"SELECT * FROM entity_relations WHERE project_id = ?", (project_id,),
|
||||
).fetchall()
|
||||
|
||||
source_conn.close()
|
||||
@@ -865,7 +865,7 @@ class DatabaseSharding:
|
||||
"is_active": shard_info.is_active,
|
||||
"created_at": shard_info.created_at,
|
||||
"last_accessed": shard_info.last_accessed,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return stats
|
||||
@@ -1061,7 +1061,7 @@ class TaskQueue:
|
||||
task.status = "retrying"
|
||||
# 延迟重试
|
||||
threading.Timer(
|
||||
10 * task.retry_count, self._execute_task, args=(task_id,)
|
||||
10 * task.retry_count, self._execute_task, args=(task_id,),
|
||||
).start()
|
||||
else:
|
||||
task.status = "failed"
|
||||
@@ -1163,7 +1163,7 @@ class TaskQueue:
|
||||
return self.tasks.get(task_id)
|
||||
|
||||
def list_tasks(
|
||||
self, status: str | None = None, task_type: str | None = None, limit: int = 100
|
||||
self, status: str | None = None, task_type: str | None = None, limit: int = 100,
|
||||
) -> list[TaskInfo]:
|
||||
"""列出任务"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
@@ -1209,7 +1209,7 @@ class TaskQueue:
|
||||
error_message=row["error_message"],
|
||||
retry_count=row["retry_count"],
|
||||
max_retries=row["max_retries"],
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return tasks
|
||||
@@ -1754,12 +1754,12 @@ _performance_manager = None
|
||||
|
||||
|
||||
def get_performance_manager(
|
||||
db_path: str = "insightflow.db", redis_url: str | None = None, enable_sharding: bool = False
|
||||
db_path: str = "insightflow.db", redis_url: str | None = None, enable_sharding: bool = False,
|
||||
) -> PerformanceManager:
|
||||
"""获取性能管理器单例"""
|
||||
global _performance_manager
|
||||
if _performance_manager is None:
|
||||
_performance_manager = PerformanceManager(
|
||||
db_path=db_path, redis_url=redis_url, enable_sharding=enable_sharding
|
||||
db_path=db_path, redis_url=redis_url, enable_sharding=enable_sharding,
|
||||
)
|
||||
return _performance_manager
|
||||
|
||||
@@ -220,7 +220,7 @@ class PluginManager:
|
||||
return None
|
||||
|
||||
def list_plugins(
|
||||
self, project_id: str = None, plugin_type: str = None, status: str = None
|
||||
self, project_id: str = None, plugin_type: str = None, status: str = None,
|
||||
) -> list[Plugin]:
|
||||
"""列出插件"""
|
||||
conn = self.db.get_conn()
|
||||
@@ -241,7 +241,7 @@ class PluginManager:
|
||||
where_clause = " AND ".join(conditions) if conditions else "1 = 1"
|
||||
|
||||
rows = conn.execute(
|
||||
f"SELECT * FROM plugins WHERE {where_clause} ORDER BY created_at DESC", params
|
||||
f"SELECT * FROM plugins WHERE {where_clause} ORDER BY created_at DESC", params,
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
@@ -310,7 +310,7 @@ class PluginManager:
|
||||
# ==================== Plugin Config ====================
|
||||
|
||||
def set_plugin_config(
|
||||
self, plugin_id: str, key: str, value: str, is_encrypted: bool = False
|
||||
self, plugin_id: str, key: str, value: str, is_encrypted: bool = False,
|
||||
) -> PluginConfig:
|
||||
"""设置插件配置"""
|
||||
conn = self.db.get_conn()
|
||||
@@ -367,7 +367,7 @@ class PluginManager:
|
||||
"""获取插件所有配置"""
|
||||
conn = self.db.get_conn()
|
||||
rows = conn.execute(
|
||||
"SELECT config_key, config_value FROM plugin_configs WHERE plugin_id = ?", (plugin_id,)
|
||||
"SELECT config_key, config_value FROM plugin_configs WHERE plugin_id = ?", (plugin_id,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
@@ -377,7 +377,7 @@ class PluginManager:
|
||||
"""删除插件配置"""
|
||||
conn = self.db.get_conn()
|
||||
cursor = conn.execute(
|
||||
"DELETE FROM plugin_configs WHERE plugin_id = ? AND config_key = ?", (plugin_id, key)
|
||||
"DELETE FROM plugin_configs WHERE plugin_id = ? AND config_key = ?", (plugin_id, key),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@@ -512,7 +512,7 @@ class ChromeExtensionHandler:
|
||||
"""撤销令牌"""
|
||||
conn = self.pm.db.get_conn()
|
||||
cursor = conn.execute(
|
||||
"UPDATE chrome_extension_tokens SET is_revoked = 1 WHERE id = ?", (token_id,)
|
||||
"UPDATE chrome_extension_tokens SET is_revoked = 1 WHERE id = ?", (token_id,),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@@ -520,7 +520,7 @@ class ChromeExtensionHandler:
|
||||
return cursor.rowcount > 0
|
||||
|
||||
def list_tokens(
|
||||
self, user_id: str = None, project_id: str = None
|
||||
self, user_id: str = None, project_id: str = None,
|
||||
) -> list[ChromeExtensionToken]:
|
||||
"""列出令牌"""
|
||||
conn = self.pm.db.get_conn()
|
||||
@@ -558,7 +558,7 @@ class ChromeExtensionHandler:
|
||||
last_used_at=row["last_used_at"],
|
||||
use_count=row["use_count"],
|
||||
is_revoked=bool(row["is_revoked"]),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return tokens
|
||||
@@ -897,12 +897,12 @@ class BotHandler:
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
session.webhook_url, json=payload, headers={"Content-Type": "application/json"}
|
||||
session.webhook_url, json=payload, headers={"Content-Type": "application/json"},
|
||||
)
|
||||
return response.status_code == 200
|
||||
|
||||
async def _send_dingtalk_message(
|
||||
self, session: BotSession, message: str, msg_type: str
|
||||
self, session: BotSession, message: str, msg_type: str,
|
||||
) -> bool:
|
||||
"""发送钉钉消息"""
|
||||
timestamp = str(round(time.time() * 1000))
|
||||
@@ -928,7 +928,7 @@ class BotHandler:
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
url, json=payload, headers={"Content-Type": "application/json"}
|
||||
url, json=payload, headers={"Content-Type": "application/json"},
|
||||
)
|
||||
return response.status_code == 200
|
||||
|
||||
@@ -1115,7 +1115,7 @@ class WebhookIntegration:
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
endpoint.endpoint_url, json=payload, headers=headers, timeout=30.0
|
||||
endpoint.endpoint_url, json=payload, headers=headers, timeout=30.0,
|
||||
)
|
||||
|
||||
success = response.status_code in [200, 201, 202]
|
||||
@@ -1343,7 +1343,7 @@ class WebDAVSyncManager:
|
||||
remote_project_path = f"{sync.remote_path}/{sync.project_id}"
|
||||
try:
|
||||
client.mkdir(remote_project_path)
|
||||
except (OSError, IOError):
|
||||
except OSError:
|
||||
pass # 目录可能已存在
|
||||
|
||||
# 获取项目数据
|
||||
|
||||
@@ -120,7 +120,7 @@ class RateLimiter:
|
||||
await counter.add_request()
|
||||
|
||||
return RateLimitInfo(
|
||||
allowed=True, remaining=remaining - 1, reset_time=reset_time, retry_after=0
|
||||
allowed=True, remaining=remaining - 1, reset_time=reset_time, retry_after=0,
|
||||
)
|
||||
|
||||
async def get_limit_info(self, key: str) -> RateLimitInfo:
|
||||
@@ -195,7 +195,7 @@ def rate_limit(requests_per_minute: int = 60, key_func: Callable | None = None)
|
||||
|
||||
if not info.allowed:
|
||||
raise RateLimitExceeded(
|
||||
f"Rate limit exceeded. Try again in {info.retry_after} seconds."
|
||||
f"Rate limit exceeded. Try again in {info.retry_after} seconds.",
|
||||
)
|
||||
|
||||
return await func(*args, **kwargs)
|
||||
@@ -208,7 +208,7 @@ def rate_limit(requests_per_minute: int = 60, key_func: Callable | None = None)
|
||||
|
||||
if not info.allowed:
|
||||
raise RateLimitExceeded(
|
||||
f"Rate limit exceeded. Try again in {info.retry_after} seconds."
|
||||
f"Rate limit exceeded. Try again in {info.retry_after} seconds.",
|
||||
)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@@ -233,12 +233,12 @@ class FullTextSearch:
|
||||
|
||||
# 创建索引
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_search_content ON search_indexes(content_id, content_type)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_search_content ON search_indexes(content_id, content_type)",
|
||||
)
|
||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_search_project ON search_indexes(project_id)")
|
||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_term_freq_term ON search_term_freq(term)")
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_term_freq_project ON search_term_freq(project_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_term_freq_project ON search_term_freq(project_id)",
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
@@ -538,26 +538,26 @@ class FullTextSearch:
|
||||
or self._get_project_id(conn, content_id, content_type),
|
||||
"content": content,
|
||||
"terms": parsed_query["and"] + parsed_query["or"] + parsed_query["phrases"],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
def _get_content_by_id(
|
||||
self, conn: sqlite3.Connection, content_id: str, content_type: str
|
||||
self, conn: sqlite3.Connection, content_id: str, content_type: str,
|
||||
) -> str | None:
|
||||
"""根据ID获取内容"""
|
||||
try:
|
||||
if content_type == "transcript":
|
||||
row = conn.execute(
|
||||
"SELECT full_text FROM transcripts WHERE id = ?", (content_id,)
|
||||
"SELECT full_text FROM transcripts WHERE id = ?", (content_id,),
|
||||
).fetchone()
|
||||
return row["full_text"] if row else None
|
||||
|
||||
elif content_type == "entity":
|
||||
row = conn.execute(
|
||||
"SELECT name, definition FROM entities WHERE id = ?", (content_id,)
|
||||
"SELECT name, definition FROM entities WHERE id = ?", (content_id,),
|
||||
).fetchone()
|
||||
if row:
|
||||
return f"{row['name']} {row['definition'] or ''}"
|
||||
@@ -583,21 +583,21 @@ class FullTextSearch:
|
||||
return None
|
||||
|
||||
def _get_project_id(
|
||||
self, conn: sqlite3.Connection, content_id: str, content_type: str
|
||||
self, conn: sqlite3.Connection, content_id: str, content_type: str,
|
||||
) -> str | None:
|
||||
"""获取内容所属的项目ID"""
|
||||
try:
|
||||
if content_type == "transcript":
|
||||
row = conn.execute(
|
||||
"SELECT project_id FROM transcripts WHERE id = ?", (content_id,)
|
||||
"SELECT project_id FROM transcripts WHERE id = ?", (content_id,),
|
||||
).fetchone()
|
||||
elif content_type == "entity":
|
||||
row = conn.execute(
|
||||
"SELECT project_id FROM entities WHERE id = ?", (content_id,)
|
||||
"SELECT project_id FROM entities WHERE id = ?", (content_id,),
|
||||
).fetchone()
|
||||
elif content_type == "relation":
|
||||
row = conn.execute(
|
||||
"SELECT project_id FROM entity_relations WHERE id = ?", (content_id,)
|
||||
"SELECT project_id FROM entity_relations WHERE id = ?", (content_id,),
|
||||
).fetchone()
|
||||
else:
|
||||
return None
|
||||
@@ -661,7 +661,7 @@ class FullTextSearch:
|
||||
score=round(score, 4),
|
||||
highlights=highlights[:10], # 限制高亮数量
|
||||
metadata={},
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return scored
|
||||
@@ -843,7 +843,7 @@ class SemanticSearch:
|
||||
""")
|
||||
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_embedding_content ON embeddings(content_id, content_type)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_embedding_content ON embeddings(content_id, content_type)",
|
||||
)
|
||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_embedding_project ON embeddings(project_id)")
|
||||
|
||||
@@ -880,7 +880,7 @@ class SemanticSearch:
|
||||
return None
|
||||
|
||||
def index_embedding(
|
||||
self, content_id: str, content_type: str, project_id: str, text: str
|
||||
self, content_id: str, content_type: str, project_id: str, text: str,
|
||||
) -> bool:
|
||||
"""
|
||||
为内容生成并保存 embedding
|
||||
@@ -1012,7 +1012,7 @@ class SemanticSearch:
|
||||
similarity=float(similarity),
|
||||
embedding=None, # 不返回 embedding 以节省带宽
|
||||
metadata={},
|
||||
)
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"计算相似度失败: {e}")
|
||||
@@ -1029,13 +1029,13 @@ class SemanticSearch:
|
||||
try:
|
||||
if content_type == "transcript":
|
||||
row = conn.execute(
|
||||
"SELECT full_text FROM transcripts WHERE id = ?", (content_id,)
|
||||
"SELECT full_text FROM transcripts WHERE id = ?", (content_id,),
|
||||
).fetchone()
|
||||
result = row["full_text"] if row else None
|
||||
|
||||
elif content_type == "entity":
|
||||
row = conn.execute(
|
||||
"SELECT name, definition FROM entities WHERE id = ?", (content_id,)
|
||||
"SELECT name, definition FROM entities WHERE id = ?", (content_id,),
|
||||
).fetchone()
|
||||
result = f"{row['name']}: {row['definition']}" if row else None
|
||||
|
||||
@@ -1067,7 +1067,7 @@ class SemanticSearch:
|
||||
return None
|
||||
|
||||
def find_similar_content(
|
||||
self, content_id: str, content_type: str, top_k: int = 5
|
||||
self, content_id: str, content_type: str, top_k: int = 5,
|
||||
) -> list[SemanticSearchResult]:
|
||||
"""
|
||||
查找与指定内容相似的内容
|
||||
@@ -1127,7 +1127,7 @@ class SemanticSearch:
|
||||
project_id=row["project_id"],
|
||||
similarity=float(similarity),
|
||||
metadata={},
|
||||
)
|
||||
),
|
||||
)
|
||||
except (KeyError, ValueError):
|
||||
continue
|
||||
@@ -1175,7 +1175,7 @@ class EntityPathDiscovery:
|
||||
return conn
|
||||
|
||||
def find_shortest_path(
|
||||
self, source_entity_id: str, target_entity_id: str, max_depth: int = 5
|
||||
self, source_entity_id: str, target_entity_id: str, max_depth: int = 5,
|
||||
) -> EntityPath | None:
|
||||
"""
|
||||
查找两个实体之间的最短路径(BFS算法)
|
||||
@@ -1192,7 +1192,7 @@ class EntityPathDiscovery:
|
||||
|
||||
# 获取项目ID
|
||||
row = conn.execute(
|
||||
"SELECT project_id FROM entities WHERE id = ?", (source_entity_id,)
|
||||
"SELECT project_id FROM entities WHERE id = ?", (source_entity_id,),
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
@@ -1250,7 +1250,7 @@ class EntityPathDiscovery:
|
||||
return None
|
||||
|
||||
def find_all_paths(
|
||||
self, source_entity_id: str, target_entity_id: str, max_depth: int = 4, max_paths: int = 10
|
||||
self, source_entity_id: str, target_entity_id: str, max_depth: int = 4, max_paths: int = 10,
|
||||
) -> list[EntityPath]:
|
||||
"""
|
||||
查找两个实体之间的所有路径(限制数量和深度)
|
||||
@@ -1268,7 +1268,7 @@ class EntityPathDiscovery:
|
||||
|
||||
# 获取项目ID
|
||||
row = conn.execute(
|
||||
"SELECT project_id FROM entities WHERE id = ?", (source_entity_id,)
|
||||
"SELECT project_id FROM entities WHERE id = ?", (source_entity_id,),
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
@@ -1280,7 +1280,7 @@ class EntityPathDiscovery:
|
||||
paths = []
|
||||
|
||||
def dfs(
|
||||
current_id: str, target_id: str, path: list[str], visited: set[str], depth: int
|
||||
current_id: str, target_id: str, path: list[str], visited: set[str], depth: int,
|
||||
) -> None:
|
||||
if depth > max_depth:
|
||||
return
|
||||
@@ -1328,7 +1328,7 @@ class EntityPathDiscovery:
|
||||
nodes = []
|
||||
for entity_id in entity_ids:
|
||||
row = conn.execute(
|
||||
"SELECT id, name, type FROM entities WHERE id = ?", (entity_id,)
|
||||
"SELECT id, name, type FROM entities WHERE id = ?", (entity_id,),
|
||||
).fetchone()
|
||||
if row:
|
||||
nodes.append({"id": row["id"], "name": row["name"], "type": row["type"]})
|
||||
@@ -1358,7 +1358,7 @@ class EntityPathDiscovery:
|
||||
"target": target_id,
|
||||
"relation_type": row["relation_type"],
|
||||
"evidence": row["evidence"],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -1398,7 +1398,7 @@ class EntityPathDiscovery:
|
||||
|
||||
# 获取项目ID
|
||||
row = conn.execute(
|
||||
"SELECT project_id, name FROM entities WHERE id = ?", (entity_id,)
|
||||
"SELECT project_id, name FROM entities WHERE id = ?", (entity_id,),
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
@@ -1445,7 +1445,7 @@ class EntityPathDiscovery:
|
||||
|
||||
# 获取邻居信息
|
||||
neighbor_info = conn.execute(
|
||||
"SELECT name, type FROM entities WHERE id = ?", (neighbor_id,)
|
||||
"SELECT name, type FROM entities WHERE id = ?", (neighbor_id,),
|
||||
).fetchone()
|
||||
|
||||
if neighbor_info:
|
||||
@@ -1458,9 +1458,9 @@ class EntityPathDiscovery:
|
||||
"relation_type": neighbor["relation_type"],
|
||||
"evidence": neighbor["evidence"],
|
||||
"path": self._get_path_to_entity(
|
||||
entity_id, neighbor_id, project_id, conn
|
||||
entity_id, neighbor_id, project_id, conn,
|
||||
),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -1470,7 +1470,7 @@ class EntityPathDiscovery:
|
||||
return relations
|
||||
|
||||
def _get_path_to_entity(
|
||||
self, source_id: str, target_id: str, project_id: str, conn: sqlite3.Connection
|
||||
self, source_id: str, target_id: str, project_id: str, conn: sqlite3.Connection,
|
||||
) -> list[str]:
|
||||
"""获取从源实体到目标实体的路径(简化版)"""
|
||||
# BFS 找路径
|
||||
@@ -1528,7 +1528,7 @@ class EntityPathDiscovery:
|
||||
"type": node["type"],
|
||||
"is_source": node["id"] == path.source_entity_id,
|
||||
"is_target": node["id"] == path.target_entity_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# 边数据
|
||||
@@ -1540,7 +1540,7 @@ class EntityPathDiscovery:
|
||||
"target": edge["target"],
|
||||
"relation_type": edge["relation_type"],
|
||||
"evidence": edge["evidence"],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -1565,7 +1565,7 @@ class EntityPathDiscovery:
|
||||
|
||||
# 获取所有实体
|
||||
entities = conn.execute(
|
||||
"SELECT id, name FROM entities WHERE project_id = ?", (project_id,)
|
||||
"SELECT id, name FROM entities WHERE project_id = ?", (project_id,),
|
||||
).fetchall()
|
||||
|
||||
# 计算每个实体作为桥梁的次数
|
||||
@@ -1617,7 +1617,7 @@ class EntityPathDiscovery:
|
||||
"entity_name": entity["name"],
|
||||
"neighbor_count": len(neighbor_ids),
|
||||
"bridge_score": round(bridge_score, 4),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -1706,7 +1706,7 @@ class KnowledgeGapDetection:
|
||||
|
||||
# 检查每个实体的属性完整性
|
||||
entities = conn.execute(
|
||||
"SELECT id, name FROM entities WHERE project_id = ?", (project_id,)
|
||||
"SELECT id, name FROM entities WHERE project_id = ?", (project_id,),
|
||||
).fetchall()
|
||||
|
||||
for entity in entities:
|
||||
@@ -1714,7 +1714,7 @@ class KnowledgeGapDetection:
|
||||
|
||||
# 获取实体已有的属性
|
||||
existing_attrs = conn.execute(
|
||||
"SELECT template_id FROM entity_attributes WHERE entity_id = ?", (entity_id,)
|
||||
"SELECT template_id FROM entity_attributes WHERE entity_id = ?", (entity_id,),
|
||||
).fetchall()
|
||||
|
||||
existing_template_ids = {a["template_id"] for a in existing_attrs}
|
||||
@@ -1726,7 +1726,7 @@ class KnowledgeGapDetection:
|
||||
missing_names = []
|
||||
for template_id in missing_templates:
|
||||
template = conn.execute(
|
||||
"SELECT name FROM attribute_templates WHERE id = ?", (template_id,)
|
||||
"SELECT name FROM attribute_templates WHERE id = ?", (template_id,),
|
||||
).fetchone()
|
||||
if template:
|
||||
missing_names.append(template["name"])
|
||||
@@ -1746,7 +1746,7 @@ class KnowledgeGapDetection:
|
||||
],
|
||||
related_entities=[],
|
||||
metadata={"missing_attributes": missing_names},
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -1759,7 +1759,7 @@ class KnowledgeGapDetection:
|
||||
|
||||
# 获取所有实体及其关系数量
|
||||
entities = conn.execute(
|
||||
"SELECT id, name, type FROM entities WHERE project_id = ?", (project_id,)
|
||||
"SELECT id, name, type FROM entities WHERE project_id = ?", (project_id,),
|
||||
).fetchall()
|
||||
|
||||
for entity in entities:
|
||||
@@ -1812,7 +1812,7 @@ class KnowledgeGapDetection:
|
||||
"relation_count": relation_count,
|
||||
"potential_related": [r["name"] for r in potential_related],
|
||||
},
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -1853,7 +1853,7 @@ class KnowledgeGapDetection:
|
||||
],
|
||||
related_entities=[],
|
||||
metadata={"entity_type": entity["type"]},
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -1887,7 +1887,7 @@ class KnowledgeGapDetection:
|
||||
suggestions=[f"为 '{entity['name']}' 添加定义", "从转录文本中提取定义信息"],
|
||||
related_entities=[],
|
||||
metadata={"entity_type": entity["type"]},
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -1900,7 +1900,7 @@ class KnowledgeGapDetection:
|
||||
|
||||
# 分析转录文本中频繁提及但未提取为实体的词
|
||||
transcripts = conn.execute(
|
||||
"SELECT full_text FROM transcripts WHERE project_id = ?", (project_id,)
|
||||
"SELECT full_text FROM transcripts WHERE project_id = ?", (project_id,),
|
||||
).fetchall()
|
||||
|
||||
# 合并所有文本
|
||||
@@ -1908,7 +1908,7 @@ class KnowledgeGapDetection:
|
||||
|
||||
# 获取现有实体名称
|
||||
existing_entities = conn.execute(
|
||||
"SELECT name FROM entities WHERE project_id = ?", (project_id,)
|
||||
"SELECT name FROM entities WHERE project_id = ?", (project_id,),
|
||||
).fetchall()
|
||||
|
||||
existing_names = {e["name"].lower() for e in existing_entities}
|
||||
@@ -1940,7 +1940,7 @@ class KnowledgeGapDetection:
|
||||
],
|
||||
related_entities=[],
|
||||
metadata={"mention_count": count},
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
conn.close()
|
||||
@@ -2146,7 +2146,7 @@ class SearchManager:
|
||||
|
||||
for t in transcripts:
|
||||
if t["full_text"] and self.semantic_search.index_embedding(
|
||||
t["id"], "transcript", t["project_id"], t["full_text"]
|
||||
t["id"], "transcript", t["project_id"], t["full_text"],
|
||||
):
|
||||
semantic_stats["indexed"] += 1
|
||||
else:
|
||||
@@ -2179,12 +2179,12 @@ class SearchManager:
|
||||
|
||||
# 全文索引统计
|
||||
fulltext_count = conn.execute(
|
||||
f"SELECT COUNT(*) as count FROM search_indexes {where_clause}", params
|
||||
f"SELECT COUNT(*) as count FROM search_indexes {where_clause}", params,
|
||||
).fetchone()["count"]
|
||||
|
||||
# 语义索引统计
|
||||
semantic_count = conn.execute(
|
||||
f"SELECT COUNT(*) as count FROM embeddings {where_clause}", params
|
||||
f"SELECT COUNT(*) as count FROM embeddings {where_clause}", params,
|
||||
).fetchone()["count"]
|
||||
|
||||
# 按类型统计
|
||||
@@ -2225,7 +2225,7 @@ def get_search_manager(db_path: str = "insightflow.db") -> SearchManager:
|
||||
|
||||
|
||||
def fulltext_search(
|
||||
query: str, project_id: str | None = None, limit: int = 20
|
||||
query: str, project_id: str | None = None, limit: int = 20,
|
||||
) -> list[SearchResult]:
|
||||
"""全文搜索便捷函数"""
|
||||
manager = get_search_manager()
|
||||
@@ -2233,7 +2233,7 @@ def fulltext_search(
|
||||
|
||||
|
||||
def semantic_search(
|
||||
query: str, project_id: str | None = None, top_k: int = 10
|
||||
query: str, project_id: str | None = None, top_k: int = 10,
|
||||
) -> list[SemanticSearchResult]:
|
||||
"""语义搜索便捷函数"""
|
||||
manager = get_search_manager()
|
||||
|
||||
@@ -300,22 +300,22 @@ class SecurityManager:
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs(user_id)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_logs_resource "
|
||||
"ON audit_logs(resource_type, resource_id)"
|
||||
"ON audit_logs(resource_type, resource_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action_type)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action_type)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_logs_created ON audit_logs(created_at)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_audit_logs_created ON audit_logs(created_at)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_encryption_project ON encryption_configs(project_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_encryption_project ON encryption_configs(project_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_masking_project ON masking_rules(project_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_masking_project ON masking_rules(project_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_access_policy_project ON data_access_policies(project_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_access_policy_project ON data_access_policies(project_id)",
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
@@ -324,7 +324,7 @@ class SecurityManager:
|
||||
def _generate_id(self) -> str:
|
||||
"""生成唯一ID"""
|
||||
return hashlib.sha256(
|
||||
f"{datetime.now().isoformat()}{secrets.token_hex(16)}".encode()
|
||||
f"{datetime.now().isoformat()}{secrets.token_hex(16)}".encode(),
|
||||
).hexdigest()[:32]
|
||||
|
||||
# ==================== 审计日志 ====================
|
||||
@@ -464,7 +464,7 @@ class SecurityManager:
|
||||
return logs
|
||||
|
||||
def get_audit_stats(
|
||||
self, start_time: str | None = None, end_time: str | None = None
|
||||
self, start_time: str | None = None, end_time: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""获取审计统计"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
@@ -804,7 +804,7 @@ class SecurityManager:
|
||||
description=row[8],
|
||||
created_at=row[9],
|
||||
updated_at=row[10],
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return rules
|
||||
@@ -882,7 +882,7 @@ class SecurityManager:
|
||||
return success
|
||||
|
||||
def apply_masking(
|
||||
self, text: str, project_id: str, rule_types: list[MaskingRuleType] | None = None
|
||||
self, text: str, project_id: str, rule_types: list[MaskingRuleType] | None = None,
|
||||
) -> str:
|
||||
"""应用脱敏规则到文本"""
|
||||
rules = self.get_masking_rules(project_id)
|
||||
@@ -906,7 +906,7 @@ class SecurityManager:
|
||||
return masked_text
|
||||
|
||||
def apply_masking_to_entity(
|
||||
self, entity_data: dict[str, Any], project_id: str
|
||||
self, entity_data: dict[str, Any], project_id: str,
|
||||
) -> dict[str, Any]:
|
||||
"""对实体数据应用脱敏"""
|
||||
masked_data = entity_data.copy()
|
||||
@@ -982,7 +982,7 @@ class SecurityManager:
|
||||
return policy
|
||||
|
||||
def get_access_policies(
|
||||
self, project_id: str, active_only: bool = True
|
||||
self, project_id: str, active_only: bool = True,
|
||||
) -> list[DataAccessPolicy]:
|
||||
"""获取数据访问策略"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
@@ -1015,20 +1015,20 @@ class SecurityManager:
|
||||
is_active=bool(row[10]),
|
||||
created_at=row[11],
|
||||
updated_at=row[12],
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return policies
|
||||
|
||||
def check_access_permission(
|
||||
self, policy_id: str, user_id: str, user_ip: str | None = None
|
||||
self, policy_id: str, user_id: str, user_ip: str | None = None,
|
||||
) -> tuple[bool, str | None]:
|
||||
"""检查访问权限"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"SELECT * FROM data_access_policies WHERE id = ? AND is_active = 1", (policy_id,)
|
||||
"SELECT * FROM data_access_policies WHERE id = ? AND is_active = 1", (policy_id,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
@@ -1163,7 +1163,7 @@ class SecurityManager:
|
||||
return request
|
||||
|
||||
def approve_access_request(
|
||||
self, request_id: str, approved_by: str, expires_hours: int = 24
|
||||
self, request_id: str, approved_by: str, expires_hours: int = 24,
|
||||
) -> AccessRequest | None:
|
||||
"""批准访问请求"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
|
||||
@@ -484,37 +484,37 @@ class SubscriptionManager:
|
||||
|
||||
# 创建索引
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_subscriptions_tenant ON subscriptions(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_subscriptions_tenant ON subscriptions(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_subscriptions_plan ON subscriptions(plan_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_subscriptions_plan ON subscriptions(plan_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_usage_tenant ON usage_records(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_usage_tenant ON usage_records(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_usage_type ON usage_records(resource_type)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_usage_type ON usage_records(resource_type)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_usage_recorded ON usage_records(recorded_at)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_usage_recorded ON usage_records(recorded_at)",
|
||||
)
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_payments_tenant ON payments(tenant_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_payments_status ON payments(status)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_invoices_tenant ON invoices(tenant_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_invoices_status ON invoices(status)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_invoices_number ON invoices(invoice_number)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_invoices_number ON invoices(invoice_number)",
|
||||
)
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_refunds_tenant ON refunds(tenant_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_refunds_status ON refunds(status)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_billing_tenant ON billing_history(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_billing_tenant ON billing_history(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_billing_created ON billing_history(created_at)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_billing_created ON billing_history(created_at)",
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
@@ -588,7 +588,7 @@ class SubscriptionManager:
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT * FROM subscription_plans WHERE tier = ? AND is_active = 1", (tier,)
|
||||
"SELECT * FROM subscription_plans WHERE tier = ? AND is_active = 1", (tier,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
|
||||
@@ -609,7 +609,7 @@ class SubscriptionManager:
|
||||
cursor.execute("SELECT * FROM subscription_plans ORDER BY price_monthly")
|
||||
else:
|
||||
cursor.execute(
|
||||
"SELECT * FROM subscription_plans WHERE is_active = 1 ORDER BY price_monthly"
|
||||
"SELECT * FROM subscription_plans WHERE is_active = 1 ORDER BY price_monthly",
|
||||
)
|
||||
|
||||
rows = cursor.fetchall()
|
||||
@@ -963,7 +963,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def cancel_subscription(
|
||||
self, subscription_id: str, at_period_end: bool = True
|
||||
self, subscription_id: str, at_period_end: bool = True,
|
||||
) -> Subscription | None:
|
||||
"""取消订阅"""
|
||||
conn = self._get_connection()
|
||||
@@ -1017,7 +1017,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def change_plan(
|
||||
self, subscription_id: str, new_plan_id: str, prorate: bool = True
|
||||
self, subscription_id: str, new_plan_id: str, prorate: bool = True,
|
||||
) -> Subscription | None:
|
||||
"""更改订阅计划"""
|
||||
conn = self._get_connection()
|
||||
@@ -1125,7 +1125,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def get_usage_summary(
|
||||
self, tenant_id: str, start_date: datetime | None = None, end_date: datetime | None = None
|
||||
self, tenant_id: str, start_date: datetime | None = None, end_date: datetime | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""获取用量汇总"""
|
||||
conn = self._get_connection()
|
||||
@@ -1268,7 +1268,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def confirm_payment(
|
||||
self, payment_id: str, provider_payment_id: str | None = None
|
||||
self, payment_id: str, provider_payment_id: str | None = None,
|
||||
) -> Payment | None:
|
||||
"""确认支付完成"""
|
||||
conn = self._get_connection()
|
||||
@@ -1361,7 +1361,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def list_payments(
|
||||
self, tenant_id: str, status: str | None = None, limit: int = 100, offset: int = 0
|
||||
self, tenant_id: str, status: str | None = None, limit: int = 100, offset: int = 0,
|
||||
) -> list[Payment]:
|
||||
"""列出支付记录"""
|
||||
conn = self._get_connection()
|
||||
@@ -1501,7 +1501,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def list_invoices(
|
||||
self, tenant_id: str, status: str | None = None, limit: int = 100, offset: int = 0
|
||||
self, tenant_id: str, status: str | None = None, limit: int = 100, offset: int = 0,
|
||||
) -> list[Invoice]:
|
||||
"""列出发票"""
|
||||
conn = self._get_connection()
|
||||
@@ -1581,7 +1581,7 @@ class SubscriptionManager:
|
||||
# ==================== 退款管理 ====================
|
||||
|
||||
def request_refund(
|
||||
self, tenant_id: str, payment_id: str, amount: float, reason: str, requested_by: str
|
||||
self, tenant_id: str, payment_id: str, amount: float, reason: str, requested_by: str,
|
||||
) -> Refund:
|
||||
"""申请退款"""
|
||||
conn = self._get_connection()
|
||||
@@ -1690,7 +1690,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def complete_refund(
|
||||
self, refund_id: str, provider_refund_id: str | None = None
|
||||
self, refund_id: str, provider_refund_id: str | None = None,
|
||||
) -> Refund | None:
|
||||
"""完成退款"""
|
||||
conn = self._get_connection()
|
||||
@@ -1775,7 +1775,7 @@ class SubscriptionManager:
|
||||
conn.close()
|
||||
|
||||
def list_refunds(
|
||||
self, tenant_id: str, status: str | None = None, limit: int = 100, offset: int = 0
|
||||
self, tenant_id: str, status: str | None = None, limit: int = 100, offset: int = 0,
|
||||
) -> list[Refund]:
|
||||
"""列出退款记录"""
|
||||
conn = self._get_connection()
|
||||
@@ -1902,7 +1902,7 @@ class SubscriptionManager:
|
||||
}
|
||||
|
||||
def create_alipay_order(
|
||||
self, tenant_id: str, plan_id: str, billing_cycle: str = "monthly"
|
||||
self, tenant_id: str, plan_id: str, billing_cycle: str = "monthly",
|
||||
) -> dict[str, Any]:
|
||||
"""创建支付宝订单(占位实现)"""
|
||||
# 这里应该集成支付宝 SDK
|
||||
@@ -1919,7 +1919,7 @@ class SubscriptionManager:
|
||||
}
|
||||
|
||||
def create_wechat_order(
|
||||
self, tenant_id: str, plan_id: str, billing_cycle: str = "monthly"
|
||||
self, tenant_id: str, plan_id: str, billing_cycle: str = "monthly",
|
||||
) -> dict[str, Any]:
|
||||
"""创建微信支付订单(占位实现)"""
|
||||
# 这里应该集成微信支付 SDK
|
||||
|
||||
@@ -388,16 +388,16 @@ class TenantManager:
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_tenants_owner ON tenants(owner_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_tenants_status ON tenants(status)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_tenant ON tenant_domains(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_tenant ON tenant_domains(tenant_id)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_domain ON tenant_domains(domain)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_domain ON tenant_domains(domain)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_status ON tenant_domains(status)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_status ON tenant_domains(status)",
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_members_tenant ON tenant_members(tenant_id)"
|
||||
"CREATE INDEX IF NOT EXISTS idx_members_tenant ON tenant_members(tenant_id)",
|
||||
)
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_members_user ON tenant_members(user_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_usage_tenant ON tenant_usage(tenant_id)")
|
||||
@@ -433,7 +433,7 @@ class TenantManager:
|
||||
TenantTier(tier) if tier in [t.value for t in TenantTier] else TenantTier.FREE
|
||||
)
|
||||
resource_limits = self.DEFAULT_LIMITS.get(
|
||||
tier_enum, self.DEFAULT_LIMITS[TenantTier.FREE]
|
||||
tier_enum, self.DEFAULT_LIMITS[TenantTier.FREE],
|
||||
)
|
||||
|
||||
tenant = Tenant(
|
||||
@@ -612,7 +612,7 @@ class TenantManager:
|
||||
conn.close()
|
||||
|
||||
def list_tenants(
|
||||
self, status: str | None = None, tier: str | None = None, limit: int = 100, offset: int = 0
|
||||
self, status: str | None = None, tier: str | None = None, limit: int = 100, offset: int = 0,
|
||||
) -> list[Tenant]:
|
||||
"""列出租户"""
|
||||
conn = self._get_connection()
|
||||
@@ -1103,7 +1103,7 @@ class TenantManager:
|
||||
conn.close()
|
||||
|
||||
def update_member_role(
|
||||
self, tenant_id: str, member_id: str, role: str, permissions: list[str] | None = None
|
||||
self, tenant_id: str, member_id: str, role: str, permissions: list[str] | None = None,
|
||||
) -> bool:
|
||||
"""更新成员角色"""
|
||||
conn = self._get_connection()
|
||||
@@ -1209,7 +1209,7 @@ class TenantManager:
|
||||
**asdict(tenant),
|
||||
"member_role": row["role"],
|
||||
"member_status": row["member_status"],
|
||||
}
|
||||
},
|
||||
)
|
||||
return result
|
||||
|
||||
@@ -1268,7 +1268,7 @@ class TenantManager:
|
||||
conn.close()
|
||||
|
||||
def get_usage_stats(
|
||||
self, tenant_id: str, start_date: datetime | None = None, end_date: datetime | None = None
|
||||
self, tenant_id: str, start_date: datetime | None = None, end_date: datetime | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""获取使用统计"""
|
||||
conn = self._get_connection()
|
||||
@@ -1314,23 +1314,23 @@ class TenantManager:
|
||||
"limits": limits,
|
||||
"usage_percentages": {
|
||||
"storage": self._calc_percentage(
|
||||
row["total_storage"] or 0, limits.get("max_storage_mb", 0) * 1024 * 1024
|
||||
row["total_storage"] or 0, limits.get("max_storage_mb", 0) * 1024 * 1024,
|
||||
),
|
||||
"transcription": self._calc_percentage(
|
||||
row["total_transcription"] or 0,
|
||||
limits.get("max_transcription_minutes", 0) * 60,
|
||||
),
|
||||
"api_calls": self._calc_percentage(
|
||||
row["total_api_calls"] or 0, limits.get("max_api_calls_per_day", 0)
|
||||
row["total_api_calls"] or 0, limits.get("max_api_calls_per_day", 0),
|
||||
),
|
||||
"projects": self._calc_percentage(
|
||||
row["max_projects"] or 0, limits.get("max_projects", 0)
|
||||
row["max_projects"] or 0, limits.get("max_projects", 0),
|
||||
),
|
||||
"entities": self._calc_percentage(
|
||||
row["max_entities"] or 0, limits.get("max_entities", 0)
|
||||
row["max_entities"] or 0, limits.get("max_entities", 0),
|
||||
),
|
||||
"members": self._calc_percentage(
|
||||
row["max_members"] or 0, limits.get("max_team_members", 0)
|
||||
row["max_members"] or 0, limits.get("max_team_members", 0),
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ def test_cache_manager() -> None:
|
||||
|
||||
# 批量操作
|
||||
cache.set_many(
|
||||
{"batch_key_1": "value1", "batch_key_2": "value2", "batch_key_3": "value3"}, ttl=60
|
||||
{"batch_key_1": "value1", "batch_key_2": "value2", "batch_key_3": "value3"}, ttl=60,
|
||||
)
|
||||
print(" ✓ 批量设置缓存")
|
||||
|
||||
@@ -208,7 +208,7 @@ def test_task_queue() -> None:
|
||||
|
||||
# 提交任务
|
||||
task_id = queue.submit(
|
||||
task_type="test_task", payload={"test": "data", "timestamp": time.time()}
|
||||
task_type="test_task", payload={"test": "data", "timestamp": time.time()},
|
||||
)
|
||||
print(" ✓ 提交任务: {task_id}")
|
||||
|
||||
@@ -267,7 +267,7 @@ def test_performance_monitor() -> None:
|
||||
for type_stat in stats.get("by_type", []):
|
||||
print(
|
||||
f" {type_stat['type']}: {type_stat['count']} 次, "
|
||||
f"平均 {type_stat['avg_duration_ms']} ms"
|
||||
f"平均 {type_stat['avg_duration_ms']} ms",
|
||||
)
|
||||
|
||||
print("\n✓ 性能监控测试完成")
|
||||
|
||||
@@ -29,7 +29,7 @@ def test_tenant_management() -> None:
|
||||
# 1. 创建租户
|
||||
print("\n1.1 创建租户...")
|
||||
tenant = manager.create_tenant(
|
||||
name="Test Company", owner_id="user_001", tier="pro", description="A test company tenant"
|
||||
name="Test Company", owner_id="user_001", tier="pro", description="A test company tenant",
|
||||
)
|
||||
print(f"✅ 租户创建成功: {tenant.id}")
|
||||
print(f" - 名称: {tenant.name}")
|
||||
@@ -53,7 +53,7 @@ def test_tenant_management() -> None:
|
||||
# 4. 更新租户
|
||||
print("\n1.4 更新租户信息...")
|
||||
updated = manager.update_tenant(
|
||||
tenant_id=tenant.id, name="Test Company Updated", tier="enterprise"
|
||||
tenant_id=tenant.id, name="Test Company Updated", tier="enterprise",
|
||||
)
|
||||
assert updated is not None, "更新租户失败"
|
||||
print(f"✅ 租户更新成功: {updated.name}, 层级: {updated.tier}")
|
||||
@@ -163,7 +163,7 @@ def test_member_management(tenant_id: str) -> None:
|
||||
# 1. 邀请成员
|
||||
print("\n4.1 邀请成员...")
|
||||
member1 = manager.invite_member(
|
||||
tenant_id=tenant_id, email="admin@test.com", role="admin", invited_by="user_001"
|
||||
tenant_id=tenant_id, email="admin@test.com", role="admin", invited_by="user_001",
|
||||
)
|
||||
print(f"✅ 成员邀请成功: {member1.email}")
|
||||
print(f" - ID: {member1.id}")
|
||||
@@ -171,7 +171,7 @@ def test_member_management(tenant_id: str) -> None:
|
||||
print(f" - 权限: {member1.permissions}")
|
||||
|
||||
member2 = manager.invite_member(
|
||||
tenant_id=tenant_id, email="member@test.com", role="member", invited_by="user_001"
|
||||
tenant_id=tenant_id, email="member@test.com", role="member", invited_by="user_001",
|
||||
)
|
||||
print(f"✅ 成员邀请成功: {member2.email}")
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ def test_subscription_manager() -> None:
|
||||
|
||||
# 更改计划
|
||||
changed = manager.change_plan(
|
||||
subscription_id=subscription.id, new_plan_id=enterprise_plan.id
|
||||
subscription_id=subscription.id, new_plan_id=enterprise_plan.id,
|
||||
)
|
||||
print(f"✓ 更改计划: {changed.plan_id} (Enterprise)")
|
||||
|
||||
|
||||
@@ -181,14 +181,14 @@ async def test_predictions(trend_model_id: str, anomaly_model_id: str) -> None:
|
||||
# 2. 趋势预测
|
||||
print("2. 趋势预测...")
|
||||
trend_result = await manager.predict(
|
||||
trend_model_id, {"historical_values": [10, 12, 15, 14, 18, 20, 22]}
|
||||
trend_model_id, {"historical_values": [10, 12, 15, 14, 18, 20, 22]},
|
||||
)
|
||||
print(f" 预测结果: {trend_result.prediction_data}")
|
||||
|
||||
# 3. 异常检测
|
||||
print("3. 异常检测...")
|
||||
anomaly_result = await manager.predict(
|
||||
anomaly_model_id, {"value": 50, "historical_values": [10, 12, 11, 13, 12, 14, 13]}
|
||||
anomaly_model_id, {"value": 50, "historical_values": [10, 12, 11, 13, 12, 14, 13]},
|
||||
)
|
||||
print(f" 检测结果: {anomaly_result.prediction_data}")
|
||||
|
||||
|
||||
@@ -525,7 +525,7 @@ class TestGrowthManager:
|
||||
|
||||
try:
|
||||
referral = self.manager.generate_referral_code(
|
||||
program_id=program_id, referrer_id="referrer_user_001"
|
||||
program_id=program_id, referrer_id="referrer_user_001",
|
||||
)
|
||||
|
||||
if referral:
|
||||
@@ -551,7 +551,7 @@ class TestGrowthManager:
|
||||
|
||||
try:
|
||||
success = self.manager.apply_referral_code(
|
||||
referral_code=referral_code, referee_id="new_user_001"
|
||||
referral_code=referral_code, referee_id="new_user_001",
|
||||
)
|
||||
|
||||
if success:
|
||||
@@ -579,7 +579,7 @@ class TestGrowthManager:
|
||||
assert "conversion_rate" in stats
|
||||
|
||||
self.log(
|
||||
f"推荐统计: {stats['total_referrals']} 推荐, {stats['conversion_rate']:.2%} 转化率"
|
||||
f"推荐统计: {stats['total_referrals']} 推荐, {stats['conversion_rate']:.2%} 转化率",
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
@@ -618,7 +618,7 @@ class TestGrowthManager:
|
||||
|
||||
try:
|
||||
incentives = self.manager.check_team_incentive_eligibility(
|
||||
tenant_id=self.test_tenant_id, current_tier="free", team_size=5
|
||||
tenant_id=self.test_tenant_id, current_tier="free", team_size=5,
|
||||
)
|
||||
|
||||
self.log(f"找到 {len(incentives)} 个符合条件的激励")
|
||||
@@ -642,7 +642,7 @@ class TestGrowthManager:
|
||||
|
||||
today = dashboard["today"]
|
||||
self.log(
|
||||
f"实时仪表板: 今日 {today['active_users']} 活跃用户, {today['total_events']} 事件"
|
||||
f"实时仪表板: 今日 {today['active_users']} 活跃用户, {today['total_events']} 事件",
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
|
||||
@@ -50,7 +50,7 @@ class TestDeveloperEcosystem:
|
||||
status = "✅" if success else "❌"
|
||||
print(f"{status} {message}")
|
||||
self.test_results.append(
|
||||
{"message": message, "success": success, "timestamp": datetime.now().isoformat()}
|
||||
{"message": message, "success": success, "timestamp": datetime.now().isoformat()},
|
||||
)
|
||||
|
||||
def run_all_tests(self) -> None:
|
||||
@@ -198,7 +198,7 @@ class TestDeveloperEcosystem:
|
||||
try:
|
||||
if self.created_ids["sdk"]:
|
||||
sdk = self.manager.update_sdk_release(
|
||||
self.created_ids["sdk"][0], description="Updated description"
|
||||
self.created_ids["sdk"][0], description="Updated description",
|
||||
)
|
||||
if sdk:
|
||||
self.log(f"Updated SDK: {sdk.name}")
|
||||
@@ -307,7 +307,7 @@ class TestDeveloperEcosystem:
|
||||
try:
|
||||
if self.created_ids["template"]:
|
||||
template = self.manager.approve_template(
|
||||
self.created_ids["template"][0], reviewed_by="admin_001"
|
||||
self.created_ids["template"][0], reviewed_by="admin_001",
|
||||
)
|
||||
if template:
|
||||
self.log(f"Approved template: {template.name}")
|
||||
@@ -496,7 +496,7 @@ class TestDeveloperEcosystem:
|
||||
try:
|
||||
if self.created_ids["developer"]:
|
||||
profile = self.manager.verify_developer(
|
||||
self.created_ids["developer"][0], DeveloperStatus.VERIFIED
|
||||
self.created_ids["developer"][0], DeveloperStatus.VERIFIED,
|
||||
)
|
||||
if profile:
|
||||
self.log(f"Verified developer: {profile.display_name} ({profile.status.value})")
|
||||
@@ -510,7 +510,7 @@ class TestDeveloperEcosystem:
|
||||
self.manager.update_developer_stats(self.created_ids["developer"][0])
|
||||
profile = self.manager.get_developer_profile(self.created_ids["developer"][0])
|
||||
self.log(
|
||||
f"Updated developer stats: {profile.plugin_count} plugins, {profile.template_count} templates"
|
||||
f"Updated developer stats: {profile.plugin_count} plugins, {profile.template_count} templates",
|
||||
)
|
||||
except Exception as e:
|
||||
self.log(f"Failed to update developer stats: {str(e)}", success=False)
|
||||
@@ -584,7 +584,7 @@ console.log('Upload complete:', result.id);
|
||||
example = self.manager.get_code_example(self.created_ids["code_example"][0])
|
||||
if example:
|
||||
self.log(
|
||||
f"Retrieved code example: {example.title} (views: {example.view_count})"
|
||||
f"Retrieved code example: {example.title} (views: {example.view_count})",
|
||||
)
|
||||
except Exception as e:
|
||||
self.log(f"Failed to get code example: {str(e)}", success=False)
|
||||
@@ -651,7 +651,7 @@ console.log('Upload complete:', result.id);
|
||||
try:
|
||||
if self.created_ids["developer"]:
|
||||
summary = self.manager.get_developer_revenue_summary(
|
||||
self.created_ids["developer"][0]
|
||||
self.created_ids["developer"][0],
|
||||
)
|
||||
self.log("Revenue summary for developer:")
|
||||
self.log(f" - Total sales: {summary['total_sales']}")
|
||||
|
||||
@@ -129,7 +129,7 @@ class TestOpsManager:
|
||||
|
||||
# 更新告警规则
|
||||
updated_rule = self.manager.update_alert_rule(
|
||||
rule1.id, threshold=85.0, description="更新后的描述"
|
||||
rule1.id, threshold=85.0, description="更新后的描述",
|
||||
)
|
||||
assert updated_rule.threshold == 85.0
|
||||
self.log(f"Updated alert rule threshold to {updated_rule.threshold}")
|
||||
@@ -421,7 +421,7 @@ class TestOpsManager:
|
||||
|
||||
# 模拟扩缩容评估
|
||||
event = self.manager.evaluate_scaling_policy(
|
||||
policy_id=policy.id, current_instances=3, current_utilization=0.85
|
||||
policy_id=policy.id, current_instances=3, current_utilization=0.85,
|
||||
)
|
||||
|
||||
if event:
|
||||
@@ -439,7 +439,7 @@ class TestOpsManager:
|
||||
with self.manager._get_db() as conn:
|
||||
conn.execute("DELETE FROM scaling_events WHERE tenant_id = ?", (self.tenant_id,))
|
||||
conn.execute(
|
||||
"DELETE FROM auto_scaling_policies WHERE tenant_id = ?", (self.tenant_id,)
|
||||
"DELETE FROM auto_scaling_policies WHERE tenant_id = ?", (self.tenant_id,),
|
||||
)
|
||||
conn.commit()
|
||||
self.log("Cleaned up auto scaling test data")
|
||||
@@ -530,7 +530,7 @@ class TestOpsManager:
|
||||
|
||||
# 发起故障转移
|
||||
event = self.manager.initiate_failover(
|
||||
config_id=config.id, reason="Primary region health check failed"
|
||||
config_id=config.id, reason="Primary region health check failed",
|
||||
)
|
||||
|
||||
if event:
|
||||
@@ -638,7 +638,7 @@ class TestOpsManager:
|
||||
# 生成成本报告
|
||||
now = datetime.now()
|
||||
report = self.manager.generate_cost_report(
|
||||
tenant_id=self.tenant_id, year=now.year, month=now.month
|
||||
tenant_id=self.tenant_id, year=now.year, month=now.month,
|
||||
)
|
||||
|
||||
self.log(f"Generated cost report: {report.id}")
|
||||
@@ -656,7 +656,7 @@ class TestOpsManager:
|
||||
self.log(
|
||||
f" Idle resource: {resource.resource_name} (est. cost: {
|
||||
resource.estimated_monthly_cost
|
||||
}/month)"
|
||||
}/month)",
|
||||
)
|
||||
|
||||
# 生成成本优化建议
|
||||
@@ -666,7 +666,7 @@ class TestOpsManager:
|
||||
for suggestion in suggestions:
|
||||
self.log(f" Suggestion: {suggestion.title}")
|
||||
self.log(
|
||||
f" Potential savings: {suggestion.potential_savings} {suggestion.currency}"
|
||||
f" Potential savings: {suggestion.potential_savings} {suggestion.currency}",
|
||||
)
|
||||
self.log(f" Confidence: {suggestion.confidence}")
|
||||
self.log(f" Difficulty: {suggestion.difficulty}")
|
||||
@@ -691,7 +691,7 @@ class TestOpsManager:
|
||||
)
|
||||
conn.execute("DELETE FROM idle_resources WHERE tenant_id = ?", (self.tenant_id,))
|
||||
conn.execute(
|
||||
"DELETE FROM resource_utilizations WHERE tenant_id = ?", (self.tenant_id,)
|
||||
"DELETE FROM resource_utilizations WHERE tenant_id = ?", (self.tenant_id,),
|
||||
)
|
||||
conn.execute("DELETE FROM cost_reports WHERE tenant_id = ?", (self.tenant_id,))
|
||||
conn.commit()
|
||||
|
||||
@@ -19,7 +19,7 @@ class TingwuClient:
|
||||
raise ValueError("ALI_ACCESS_KEY and ALI_SECRET_KEY required")
|
||||
|
||||
def _sign_request(
|
||||
self, method: str, uri: str, query: str = "", body: str = ""
|
||||
self, method: str, uri: str, query: str = "", body: str = "",
|
||||
) -> dict[str, str]:
|
||||
"""阿里云签名 V3"""
|
||||
timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
@@ -43,7 +43,7 @@ class TingwuClient:
|
||||
from alibabacloud_tingwu20230930.client import Client as TingwuSDKClient
|
||||
|
||||
config = open_api_models.Config(
|
||||
access_key_id=self.access_key, access_key_secret=self.secret_key
|
||||
access_key_id=self.access_key, access_key_secret=self.secret_key,
|
||||
)
|
||||
config.endpoint = "tingwu.cn-beijing.aliyuncs.com"
|
||||
client = TingwuSDKClient(config)
|
||||
@@ -53,8 +53,8 @@ class TingwuClient:
|
||||
input=tingwu_models.Input(source="OSS", file_url=audio_url),
|
||||
parameters=tingwu_models.Parameters(
|
||||
transcription=tingwu_models.Transcription(
|
||||
diarization_enabled=True, sentence_max_length=20
|
||||
)
|
||||
diarization_enabled=True, sentence_max_length=20,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -73,7 +73,7 @@ class TingwuClient:
|
||||
return f"mock_task_{int(time.time())}"
|
||||
|
||||
def get_task_result(
|
||||
self, task_id: str, max_retries: int = 60, interval: int = 5
|
||||
self, task_id: str, max_retries: int = 60, interval: int = 5,
|
||||
) -> dict[str, Any]:
|
||||
"""获取任务结果"""
|
||||
try:
|
||||
@@ -83,7 +83,7 @@ class TingwuClient:
|
||||
from alibabacloud_tingwu20230930.client import Client as TingwuSDKClient
|
||||
|
||||
config = open_api_models.Config(
|
||||
access_key_id=self.access_key, access_key_secret=self.secret_key
|
||||
access_key_id=self.access_key, access_key_secret=self.secret_key,
|
||||
)
|
||||
config.endpoint = "tingwu.cn-beijing.aliyuncs.com"
|
||||
client = TingwuSDKClient(config)
|
||||
@@ -134,7 +134,7 @@ class TingwuClient:
|
||||
"end": sent.end_time / 1000,
|
||||
"text": sent.text,
|
||||
"speaker": f"Speaker {sent.speaker_id}",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return {"full_text": full_text.strip(), "segments": segments}
|
||||
@@ -149,7 +149,7 @@ class TingwuClient:
|
||||
"end": 5.0,
|
||||
"text": "这是一个示例转录文本,包含 Project Alpha 和 K8s 等术语。",
|
||||
"speaker": "Speaker A",
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -234,8 +234,8 @@ class WebhookNotifier:
|
||||
"zh_cn": {
|
||||
"title": message.get("title", ""),
|
||||
"content": message.get("body", []),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
else:
|
||||
@@ -264,7 +264,7 @@ class WebhookNotifier:
|
||||
secret_enc = config.secret.encode("utf-8")
|
||||
string_to_sign = f"{timestamp}\n{config.secret}"
|
||||
hmac_code = hmac.new(
|
||||
secret_enc, string_to_sign.encode("utf-8"), digestmod=hashlib.sha256
|
||||
secret_enc, string_to_sign.encode("utf-8"), digestmod=hashlib.sha256,
|
||||
).digest()
|
||||
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
|
||||
url = f"{config.url}×tamp = {timestamp}&sign = {sign}"
|
||||
@@ -422,7 +422,7 @@ class WorkflowManager:
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Scheduled workflow {workflow.id} ({workflow.name}) with {workflow.schedule_type}"
|
||||
f"Scheduled workflow {workflow.id} ({workflow.name}) with {workflow.schedule_type}",
|
||||
)
|
||||
|
||||
async def _execute_workflow_job(self, workflow_id: str) -> None:
|
||||
@@ -497,7 +497,7 @@ class WorkflowManager:
|
||||
conn.close()
|
||||
|
||||
def list_workflows(
|
||||
self, project_id: str = None, status: str = None, workflow_type: str = None
|
||||
self, project_id: str = None, status: str = None, workflow_type: str = None,
|
||||
) -> list[Workflow]:
|
||||
"""列出工作流"""
|
||||
conn = self.db.get_conn()
|
||||
@@ -518,7 +518,7 @@ class WorkflowManager:
|
||||
where_clause = " AND ".join(conditions) if conditions else "1 = 1"
|
||||
|
||||
rows = conn.execute(
|
||||
f"SELECT * FROM workflows WHERE {where_clause} ORDER BY created_at DESC", params
|
||||
f"SELECT * FROM workflows WHERE {where_clause} ORDER BY created_at DESC", params,
|
||||
).fetchall()
|
||||
|
||||
return [self._row_to_workflow(row) for row in rows]
|
||||
@@ -780,7 +780,7 @@ class WorkflowManager:
|
||||
conn = self.db.get_conn()
|
||||
try:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM webhook_configs WHERE id = ?", (webhook_id,)
|
||||
"SELECT * FROM webhook_configs WHERE id = ?", (webhook_id,),
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
@@ -1159,7 +1159,7 @@ class WorkflowManager:
|
||||
raise
|
||||
|
||||
async def _execute_tasks_with_deps(
|
||||
self, tasks: list[WorkflowTask], input_data: dict, log_id: str
|
||||
self, tasks: list[WorkflowTask], input_data: dict, log_id: str,
|
||||
) -> dict:
|
||||
"""按依赖顺序执行任务"""
|
||||
results = {}
|
||||
@@ -1413,7 +1413,7 @@ class WorkflowManager:
|
||||
# ==================== Notification ====================
|
||||
|
||||
async def _send_workflow_notification(
|
||||
self, workflow: Workflow, results: dict, success: bool = True
|
||||
self, workflow: Workflow, results: dict, success: bool = True,
|
||||
) -> None:
|
||||
"""发送工作流执行通知"""
|
||||
if not workflow.webhook_ids:
|
||||
@@ -1500,8 +1500,8 @@ class WorkflowManager:
|
||||
],
|
||||
"footer": "InsightFlow",
|
||||
"ts": int(datetime.now().timestamp()),
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user