fix: auto-fix code issues (cron)

- 修复重复导入/字段
- 修复异常处理 (将裸 except Exception 改为具体异常类型)
- 修复PEP8格式问题
- 清理未使用导入
- 添加 UUID_LENGTH 常量替代魔法数字
- 添加 DEFAULT_RATE_LIMIT, MASTER_KEY_RATE_LIMIT, IP_RATE_LIMIT 常量
- 添加 MAX_TEXT_LENGTH, DEFAULT_TIMEOUT 常量

涉及文件:
- backend/main.py
- backend/db_manager.py
- backend/llm_client.py
- backend/neo4j_manager.py
- backend/tingwu_client.py
- backend/tenant_manager.py
- backend/growth_manager.py
- backend/workflow_manager.py
- backend/image_processor.py
- backend/multimodal_entity_linker.py
- backend/multimodal_processor.py
- backend/plugin_manager.py
- backend/rate_limiter.py
This commit is contained in:
OpenClaw Bot
2026-03-01 03:06:06 +08:00
parent ea58b6fe43
commit 1f33d203e8
13 changed files with 141 additions and 85 deletions

View File

@@ -14,6 +14,10 @@ from datetime import datetime
DB_PATH = os.getenv("DB_PATH", "/app/data/insightflow.db")
# Constants
UUID_LENGTH = 8 # UUID 截断长度
@dataclass
class Project:
id: str
@@ -22,6 +26,7 @@ class Project:
created_at: str = ""
updated_at: str = ""
@dataclass
class Entity:
id: str
@@ -42,6 +47,7 @@ class Entity:
if self.attributes is None:
self.attributes = {}
@dataclass
class AttributeTemplate:
"""属性模板定义"""
@@ -62,6 +68,7 @@ class AttributeTemplate:
if self.options is None:
self.options = []
@dataclass
class EntityAttribute:
"""实体属性值"""
@@ -82,6 +89,7 @@ class EntityAttribute:
if self.options is None:
self.options = []
@dataclass
class AttributeHistory:
"""属性变更历史"""
@@ -95,6 +103,7 @@ class AttributeHistory:
changed_at: str = ""
change_reason: str = ""
@dataclass
class EntityMention:
id: str
@@ -105,6 +114,7 @@ class EntityMention:
text_snippet: str
confidence: float = 1.0
class DatabaseManager:
def __init__(self, db_path: str = DB_PATH):
self.db_path = db_path
@@ -321,15 +331,14 @@ class DatabaseManager:
conn.execute(
"""INSERT INTO entity_mentions (id, entity_id, transcript_id, start_pos, end_pos, text_snippet, confidence)
VALUES (?, ?, ?, ?, ?, ?, ?)""",
(
mention.id,
mention.entity_id,
mention.transcript_id,
mention.start_pos,
mention.end_pos,
mention.text_snippet,
mention.confidence,
),
(mention.id,
mention.entity_id,
mention.transcript_id,
mention.start_pos,
mention.end_pos,
mention.text_snippet,
mention.confidence,
),
)
conn.commit()
conn.close()
@@ -358,7 +367,12 @@ class DatabaseManager:
now = datetime.now().isoformat()
conn.execute(
"INSERT INTO transcripts (id, project_id, filename, full_text, type, created_at) VALUES (?, ?, ?, ?, ?, ?)",
(transcript_id, project_id, filename, full_text, transcript_type, now),
(transcript_id,
project_id,
filename,
full_text,
transcript_type,
now),
)
conn.commit()
conn.close()
@@ -401,7 +415,7 @@ class DatabaseManager:
transcript_id: str = "",
):
conn = self.get_conn()
relation_id = str(uuid.uuid4())[:8]
relation_id = str(uuid.uuid4())[:UUID_LENGTH]
now = datetime.now().isoformat()
conn.execute(
"""INSERT INTO entity_relations
@@ -485,7 +499,7 @@ class DatabaseManager:
conn.close()
return existing["id"]
term_id = str(uuid.uuid4())[:8]
term_id = str(uuid.uuid4())[:UUID_LENGTH]
conn.execute(
"INSERT INTO glossary (id, project_id, term, pronunciation, frequency) VALUES (?, ?, ?, ?, ?)",
(term_id, project_id, term, pronunciation, 1),
@@ -836,7 +850,7 @@ class DatabaseManager:
(id, entity_id, template_id, old_value, new_value, changed_by, changed_at, change_reason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(
str(uuid.uuid4())[:8],
str(uuid.uuid4())[:UUID_LENGTH],
attr.entity_id,
attr.template_id,
old_value,
@@ -915,7 +929,7 @@ class DatabaseManager:
(id, entity_id, template_id, old_value, new_value, changed_by, changed_at, change_reason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(
str(uuid.uuid4())[:8],
str(uuid.uuid4())[:UUID_LENGTH],
entity_id,
template_id,
old_row["value"],
@@ -1387,9 +1401,11 @@ class DatabaseManager:
conn.close()
return stats
# Singleton instance
_db_manager = None
def get_db_manager() -> DatabaseManager:
global _db_manager
if _db_manager is None:

View File

@@ -456,7 +456,7 @@ class GrowthManager:
await client.post(
"https://api.mixpanel.com/track", headers=headers, json=[payload], timeout=10.0
)
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
print(f"Failed to send to Mixpanel: {e}")
async def _send_to_amplitude(self, event: AnalyticsEvent):
@@ -484,7 +484,7 @@ class GrowthManager:
json=payload,
timeout=10.0,
)
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
print(f"Failed to send to Amplitude: {e}")
async def _update_user_profile(

View File

@@ -10,6 +10,9 @@ import os
import uuid
from dataclasses import dataclass
# Constants
UUID_LENGTH = 8 # UUID 截断长度
# 尝试导入图像处理库
try:
from PIL import Image, ImageEnhance, ImageFilter
@@ -369,7 +372,7 @@ class ImageProcessor:
Returns:
图片处理结果
"""
image_id = image_id or str(uuid.uuid4())[:8]
image_id = image_id or str(uuid.uuid4())[:UUID_LENGTH]
if not PIL_AVAILABLE:
return ImageProcessingResult(

View File

@@ -15,11 +15,13 @@ import httpx
KIMI_API_KEY = os.getenv("KIMI_API_KEY", "")
KIMI_BASE_URL = os.getenv("KIMI_BASE_URL", "https://api.kimi.com/coding")
@dataclass
class ChatMessage:
role: str
content: str
@dataclass
class EntityExtractionResult:
name: str
@@ -27,6 +29,7 @@ class EntityExtractionResult:
definition: str
confidence: float
@dataclass
class RelationExtractionResult:
source: str
@@ -34,6 +37,7 @@ class RelationExtractionResult:
type: str
confidence: float
class LLMClient:
"""Kimi API 客户端"""
@@ -163,7 +167,7 @@ class LLMClient:
for r in data.get("relations", [])
]
return entities, relations
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
print(f"Parse extraction result failed: {e}")
return [], []
@@ -254,9 +258,11 @@ class LLMClient:
messages = [ChatMessage(role="user", content=prompt)]
return await self.chat(messages, temperature=0.3)
# Singleton instance
_llm_client = None
def get_llm_client() -> LLMClient:
global _llm_client
if _llm_client is None:

View File

@@ -38,6 +38,14 @@ from pydantic import BaseModel, Field
# Configure logger
logger = logging.getLogger(__name__)
# Constants
DEFAULT_RATE_LIMIT = 60 # 默认每分钟请求限制
MASTER_KEY_RATE_LIMIT = 1000 # Master key 限流
IP_RATE_LIMIT = 10 # IP 限流
MAX_TEXT_LENGTH = 3000 # 最大文本长度
UUID_LENGTH = 8 # UUID 截断长度
DEFAULT_TIMEOUT = 60.0 # 默认超时时间
# Add backend directory to path for imports
backend_dir = os.path.dirname(os.path.abspath(__file__))
if backend_dir not in sys.path:
@@ -101,7 +109,7 @@ except ImportError:
REASONER_AVAILABLE = False
try:
from export_manager import ExportEntity, ExportRelation, ExportTranscript, get_export_manager
from export_manager import get_export_manager
EXPORT_AVAILABLE = True
except ImportError:
@@ -490,7 +498,7 @@ async def rate_limit_middleware(request: Request, call_next):
if x_api_key and x_api_key == MASTER_KEY:
# Master key 有更高的限流
config = RateLimitConfig(requests_per_minute=1000)
config = RateLimitConfig(requests_per_minute=MASTER_KEY_RATE_LIMIT)
limit_key = f"master:{x_api_key[:16]}"
elif hasattr(request.state, "api_key") and request.state.api_key:
# 使用 API Key 的限流配置
@@ -500,7 +508,7 @@ async def rate_limit_middleware(request: Request, call_next):
else:
# IP 限流(未认证用户)
client_ip = request.client.host if request.client else "unknown"
config = RateLimitConfig(requests_per_minute=10)
config = RateLimitConfig(requests_per_minute=IP_RATE_LIMIT)
limit_key = f"ip:{client_ip}"
# 检查限流
@@ -547,7 +555,7 @@ async def rate_limit_middleware(request: Request, call_next):
ip_address=request.client.host if request.client else "",
user_agent=request.headers.get("User-Agent", ""),
)
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
# 日志记录失败不应影响主流程
print(f"Failed to log API call: {e}")
@@ -1062,7 +1070,7 @@ async def create_manual_entity(
if existing:
return {"id": existing.id, "name": existing.name, "type": existing.type, "existed": True}
entity_id = str(uuid.uuid4())[:8]
entity_id = str(uuid.uuid4())[:UUID_LENGTH]
new_entity = db.create_entity(
Entity(
id=entity_id,
@@ -1079,7 +1087,7 @@ async def create_manual_entity(
if transcript:
text = transcript["full_text"]
mention = EntityMention(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
entity_id=entity_id,
transcript_id=entity.transcript_id,
start_pos=entity.start_pos,
@@ -1227,7 +1235,7 @@ async def create_project(project: ProjectCreate, _=Depends(verify_api_key)):
raise HTTPException(status_code=500, detail="Database not available")
db = get_db_manager()
project_id = str(uuid.uuid4())[:8]
project_id = str(uuid.uuid4())[:UUID_LENGTH]
p = db.create_project(project_id, project.name, project.description)
return {"id": p.id, "name": p.name, "description": p.description}
@@ -1263,7 +1271,7 @@ async def upload_audio(project_id: str, file: UploadFile = File(...), _=Depends(
raw_entities, raw_relations = extract_entities_with_llm(tw_result["full_text"])
# 保存转录记录
transcript_id = str(uuid.uuid4())[:8]
transcript_id = str(uuid.uuid4())[:UUID_LENGTH]
db.save_transcript(
transcript_id=transcript_id,
project_id=project_id,
@@ -1290,7 +1298,7 @@ async def upload_audio(project_id: str, file: UploadFile = File(...), _=Depends(
else:
new_ent = db.create_entity(
Entity(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
project_id=project_id,
name=raw_ent["name"],
type=raw_ent.get("type", "OTHER"),
@@ -1313,7 +1321,7 @@ async def upload_audio(project_id: str, file: UploadFile = File(...), _=Depends(
if pos == -1:
break
mention = EntityMention(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
entity_id=entity_name_to_id[name],
transcript_id=transcript_id,
start_pos=pos,
@@ -1374,11 +1382,11 @@ async def upload_document(project_id: str, file: UploadFile = File(...), _=Depen
processor = get_doc_processor()
try:
result = processor.process(content, file.filename)
except Exception as e:
except (ValueError, TypeError, RuntimeError, IOError) as e:
raise HTTPException(status_code=400, detail=f"Document processing failed: {str(e)}")
# 保存文档转录记录
transcript_id = str(uuid.uuid4())[:8]
transcript_id = str(uuid.uuid4())[:UUID_LENGTH]
db.save_transcript(
transcript_id=transcript_id,
project_id=project_id,
@@ -1411,7 +1419,7 @@ async def upload_document(project_id: str, file: UploadFile = File(...), _=Depen
else:
new_ent = db.create_entity(
Entity(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
project_id=project_id,
name=raw_ent["name"],
type=raw_ent.get("type", "OTHER"),
@@ -1437,7 +1445,7 @@ async def upload_document(project_id: str, file: UploadFile = File(...), _=Depen
if pos == -1:
break
mention = EntityMention(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
entity_id=entity_name_to_id[name],
transcript_id=transcript_id,
start_pos=pos,
@@ -2282,7 +2290,7 @@ async def create_attribute_template_endpoint(
raise HTTPException(status_code=404, detail="Project not found")
new_template = AttributeTemplate(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
project_id=project_id,
name=template.name,
type=template.type,
@@ -2423,7 +2431,7 @@ async def set_entity_attribute_endpoint(
(id, entity_id, attribute_name, old_value, new_value, changed_by, changed_at, change_reason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(
str(uuid.uuid4())[:8],
str(uuid.uuid4())[:UUID_LENGTH],
entity_id,
attr.name,
existing["value"],
@@ -2444,7 +2452,7 @@ async def set_entity_attribute_endpoint(
attr_id = existing["id"]
else:
# 创建
attr_id = str(uuid.uuid4())[:8]
attr_id = str(uuid.uuid4())[:UUID_LENGTH]
conn.execute(
"""INSERT INTO entity_attributes
(id, entity_id, template_id, name, type, value, options, created_at, updated_at)
@@ -2458,7 +2466,7 @@ async def set_entity_attribute_endpoint(
(id, entity_id, attribute_name, old_value, new_value, changed_by, changed_at, change_reason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(
str(uuid.uuid4())[:8],
str(uuid.uuid4())[:UUID_LENGTH],
entity_id,
attr.name,
None,
@@ -2499,7 +2507,7 @@ async def batch_set_entity_attributes_endpoint(
template = db.get_attribute_template(attr_data.template_id)
if template:
new_attr = EntityAttribute(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
entity_id=entity_id,
template_id=attr_data.template_id,
value=attr_data.value,
@@ -2937,7 +2945,7 @@ async def export_report_pdf_endpoint(project_id: str, _=Depends(verify_api_key))
reasoner = get_knowledge_reasoner()
summary_result = reasoner.generate_project_summary(project_id, db)
summary = summary_result.get("summary", "")
except Exception:
except (RuntimeError, ValueError, TypeError):
pass
export_mgr = get_export_manager()
@@ -3113,7 +3121,7 @@ async def neo4j_status(_=Depends(verify_api_key)):
"uri": manager.uri if connected else None,
"message": "Connected" if connected else "Not connected",
}
except Exception as e:
except (RuntimeError, ValueError, TypeError, ConnectionError) as e:
return {"available": True, "connected": False, "message": str(e)}
@app.post("/api/v1/neo4j/sync")
@@ -3658,7 +3666,7 @@ async def create_workflow_endpoint(request: WorkflowCreate, _=Depends(verify_api
try:
workflow = Workflow(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
name=request.name,
description=request.description,
workflow_type=request.workflow_type,
@@ -3847,7 +3855,7 @@ async def trigger_workflow_endpoint(
)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
except (RuntimeError, TypeError, ConnectionError) as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get(
@@ -3924,7 +3932,7 @@ async def create_webhook_endpoint(request: WebhookCreate, _=Depends(verify_api_k
try:
webhook = WebhookConfig(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
name=request.name,
webhook_type=request.webhook_type,
url=request.url,
@@ -4175,7 +4183,7 @@ async def upload_video_endpoint(
processor = get_multimodal_processor(frame_interval=extract_interval)
# 处理视频
video_id = str(uuid.uuid4())[:8]
video_id = str(uuid.uuid4())[:UUID_LENGTH]
result = processor.process_video(video_data, file.filename, project_id, video_id)
if not result.success:
@@ -4252,7 +4260,7 @@ async def upload_video_endpoint(
else:
new_ent = db.create_entity(
Entity(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
project_id=project_id,
name=raw_ent["name"],
type=raw_ent.get("type", "OTHER"),
@@ -4268,7 +4276,7 @@ async def upload_video_endpoint(
(id, project_id, entity_id, modality, source_id, source_type, text_snippet, confidence, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
str(uuid.uuid4())[:8],
str(uuid.uuid4())[:UUID_LENGTH],
project_id,
entity_name_to_id[raw_ent["name"]],
"video",
@@ -4356,7 +4364,7 @@ async def upload_image_endpoint(
processor = get_image_processor()
# 处理图片
image_id = str(uuid.uuid4())[:8]
image_id = str(uuid.uuid4())[:UUID_LENGTH]
result = processor.process_image(image_data, file.filename, image_id, detect_type)
if not result.success:
@@ -4406,7 +4414,7 @@ async def upload_image_endpoint(
if not existing:
new_ent = db.create_entity(
Entity(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
project_id=project_id,
name=entity.name,
type=entity.type,
@@ -4424,7 +4432,7 @@ async def upload_image_endpoint(
(id, project_id, entity_id, modality, source_id, source_type, text_snippet, confidence, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
str(uuid.uuid4())[:8],
str(uuid.uuid4())[:UUID_LENGTH],
project_id,
entity_id,
"image",
@@ -5156,7 +5164,7 @@ async def create_plugin_endpoint(request: PluginCreate, _=Depends(verify_api_key
manager = get_plugin_manager_instance()
plugin = Plugin(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
name=request.name,
plugin_type=request.plugin_type,
project_id=request.project_id,
@@ -8056,7 +8064,7 @@ async def create_tenant(
"status": tenant.status,
"created_at": tenant.created_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants", tags=["Tenants"])
@@ -8162,7 +8170,7 @@ async def add_domain(tenant_id: str, request: AddDomainRequest, _=Depends(verify
"verification_instructions": instructions,
"created_at": domain.created_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/domains", tags=["Tenants"])
@@ -8313,7 +8321,7 @@ async def invite_member(
"status": member.status,
"invited_at": member.invited_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/members", tags=["Tenants"])
@@ -9193,7 +9201,7 @@ async def create_subscription(
"trial_end": subscription.trial_end.isoformat() if subscription.trial_end else None,
"created_at": subscription.created_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/subscription", tags=["Subscriptions"])
@@ -9259,7 +9267,7 @@ async def change_subscription_plan(
"status": updated.status,
"message": "Plan changed successfully",
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.post("/api/v1/tenants/{tenant_id}/subscription/cancel", tags=["Subscriptions"])
@@ -9288,7 +9296,7 @@ async def cancel_subscription(
"canceled_at": updated.canceled_at.isoformat() if updated.canceled_at else None,
"message": "Subscription cancelled",
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
# Usage APIs
@@ -9498,7 +9506,7 @@ async def request_refund(
"status": refund.status,
"requested_at": refund.requested_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/refunds", tags=["Subscriptions"])
@@ -9636,7 +9644,7 @@ async def create_stripe_checkout(
)
return session
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.post("/api/v1/tenants/{tenant_id}/checkout/alipay", tags=["Subscriptions"])
@@ -9658,7 +9666,7 @@ async def create_alipay_order(
)
return order
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.post("/api/v1/tenants/{tenant_id}/checkout/wechat", tags=["Subscriptions"])
@@ -9680,7 +9688,7 @@ async def create_wechat_order(
)
return order
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
# Webhook Handlers
@@ -9876,7 +9884,7 @@ async def create_sso_config_endpoint(
"default_role": sso_config.default_role,
"created_at": sso_config.created_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/sso-configs", tags=["Enterprise"])
@@ -10036,7 +10044,7 @@ async def create_scim_config_endpoint(
"sync_interval_minutes": scim_config.sync_interval_minutes,
"created_at": scim_config.created_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/scim-configs", tags=["Enterprise"])
@@ -10174,7 +10182,7 @@ async def create_audit_export_endpoint(
"expires_at": export.expires_at.isoformat() if export.expires_at else None,
"created_at": export.created_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/audit-exports", tags=["Enterprise"])
@@ -10309,7 +10317,7 @@ async def create_retention_policy_endpoint(
"is_active": new_policy.is_active,
"created_at": new_policy.created_at.isoformat(),
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/tenants/{tenant_id}/retention-policies", tags=["Enterprise"])
@@ -11885,7 +11893,7 @@ async def track_event_endpoint(request: TrackEventRequest):
)
return {"success": True, "event_id": event.id, "timestamp": event.timestamp.isoformat()}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/v1/analytics/dashboard/{tenant_id}", tags=["Growth & Analytics"])
@@ -12052,7 +12060,7 @@ async def create_experiment_endpoint(request: CreateExperimentRequest, created_b
"variants": experiment.variants,
"created_at": experiment.created_at,
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/experiments", tags=["Growth & Analytics"])
@@ -12231,7 +12239,7 @@ async def create_email_template_endpoint(request: CreateEmailTemplateRequest):
"variables": template.variables,
"created_at": template.created_at,
}
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/api/v1/email/templates", tags=["Growth & Analytics"])

View File

@@ -8,6 +8,9 @@ import uuid
from dataclasses import dataclass
from difflib import SequenceMatcher
# Constants
UUID_LENGTH = 8 # UUID 截断长度
# 尝试导入embedding库
try:
NUMPY_AVAILABLE = True
@@ -247,7 +250,7 @@ class MultimodalEntityLinker:
if result and result.matched_entity_id:
link = EntityLink(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
project_id=project_id,
source_entity_id=ent1.get("id"),
target_entity_id=result.matched_entity_id,
@@ -461,7 +464,7 @@ class MultimodalEntityLinker:
多模态实体记录
"""
return MultimodalEntity(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
entity_id=entity_id,
project_id=project_id,
name="", # 将在后续填充

View File

@@ -12,6 +12,9 @@ import uuid
from dataclasses import dataclass
from pathlib import Path
# Constants
UUID_LENGTH = 8 # UUID 截断长度
# 尝试导入OCR库
try:
import pytesseract
@@ -340,7 +343,7 @@ class MultimodalProcessor:
Returns:
视频处理结果
"""
video_id = video_id or str(uuid.uuid4())[:8]
video_id = video_id or str(uuid.uuid4())[:UUID_LENGTH]
try:
# 保存视频文件
@@ -375,7 +378,7 @@ class MultimodalProcessor:
ocr_text, confidence = self.perform_ocr(frame_path)
frame = VideoFrame(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
video_id=video_id,
frame_number=frame_number,
timestamp=timestamp,

View File

@@ -111,7 +111,7 @@ class Neo4jManager:
# 验证连接
self._driver.verify_connectivity()
logger.info(f"Connected to Neo4j at {self.uri}")
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
logger.error(f"Failed to connect to Neo4j: {e}")
self._driver = None

View File

@@ -11,7 +11,6 @@ import json
import os
import sqlite3
import time
import urllib.parse
import uuid
from dataclasses import dataclass, field
from datetime import datetime
@@ -20,6 +19,9 @@ from typing import Any
import httpx
# Constants
UUID_LENGTH = 8 # UUID 截断长度
# WebDAV 支持
try:
import webdav4.client as webdav_client
@@ -318,7 +320,7 @@ class PluginManager:
)
config_id = existing["id"]
else:
config_id = str(uuid.uuid4())[:8]
config_id = str(uuid.uuid4())[:UUID_LENGTH]
conn.execute(
"""INSERT INTO plugin_configs
(id, plugin_id, config_key, config_value, is_encrypted, created_at, updated_at)
@@ -400,7 +402,7 @@ class ChromeExtensionHandler:
expires_days: int = None,
) -> ChromeExtensionToken:
"""创建 Chrome 扩展令牌"""
token_id = str(uuid.uuid4())[:8]
token_id = str(uuid.uuid4())[:UUID_LENGTH]
# 生成随机令牌
raw_token = f"if_ext_{base64.urlsafe_b64encode(os.urandom(32)).decode('utf-8').rstrip('=')}"
@@ -563,7 +565,7 @@ class ChromeExtensionHandler:
return {"success": False, "error": "Insufficient permissions"}
# 创建转录记录(将网页作为文档处理)
transcript_id = str(uuid.uuid4())[:8]
transcript_id = str(uuid.uuid4())[:UUID_LENGTH]
now = datetime.now().isoformat()
# 构建完整文本
@@ -604,7 +606,7 @@ class BotHandler:
secret: str = "",
) -> BotSession:
"""创建机器人会话"""
bot_id = str(uuid.uuid4())[:8]
bot_id = str(uuid.uuid4())[:UUID_LENGTH]
now = datetime.now().isoformat()
conn = self.pm.db.get_conn()
@@ -932,7 +934,7 @@ class WebhookIntegration:
trigger_events: list[str] = None,
) -> WebhookEndpoint:
"""创建 Webhook 端点"""
endpoint_id = str(uuid.uuid4())[:8]
endpoint_id = str(uuid.uuid4())[:UUID_LENGTH]
now = datetime.now().isoformat()
conn = self.pm.db.get_conn()
@@ -1155,7 +1157,7 @@ class WebDAVSyncManager:
sync_interval: int = 3600,
) -> WebDAVSync:
"""创建 WebDAV 同步配置"""
sync_id = str(uuid.uuid4())[:8]
sync_id = str(uuid.uuid4())[:UUID_LENGTH]
now = datetime.now().isoformat()
conn = self.pm.db.get_conn()

View File

@@ -12,6 +12,7 @@ from collections.abc import Callable
from dataclasses import dataclass
from functools import wraps
@dataclass
class RateLimitConfig:
"""限流配置"""
@@ -20,6 +21,7 @@ class RateLimitConfig:
burst_size: int = 10 # 突发请求数
window_size: int = 60 # 窗口大小(秒)
@dataclass
class RateLimitInfo:
"""限流信息"""
@@ -29,6 +31,7 @@ class RateLimitInfo:
reset_time: int # 重置时间戳
retry_after: int # 需要等待的秒数
class SlidingWindowCounter:
"""滑动窗口计数器"""
@@ -60,6 +63,7 @@ class SlidingWindowCounter:
for k in old_keys:
self.requests.pop(k, None)
class RateLimiter:
"""API 限流器"""
@@ -155,9 +159,11 @@ class RateLimiter:
self.counters.clear()
self.configs.clear()
# 全局限流器实例
_rate_limiter: RateLimiter | None = None
def get_rate_limiter() -> RateLimiter:
"""获取限流器实例"""
global _rate_limiter
@@ -166,6 +172,8 @@ def get_rate_limiter() -> RateLimiter:
return _rate_limiter
# 限流装饰器(用于函数级别限流)
def rate_limit(requests_per_minute: int = 60, key_func: Callable | None = None) -> None:
"""
限流装饰器
@@ -208,5 +216,6 @@ def rate_limit(requests_per_minute: int = 60, key_func: Callable | None = None)
return decorator
class RateLimitExceeded(Exception):
"""限流异常"""

View File

@@ -395,7 +395,7 @@ class TenantManager:
conn.commit()
logger.info("Tenant tables initialized successfully")
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
logger.error(f"Error initializing tenant tables: {e}")
raise
finally:
@@ -760,7 +760,7 @@ class TenantManager:
return is_verified
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
logger.error(f"Error verifying domain: {e}")
return False
finally:

View File

@@ -8,6 +8,7 @@ import time
from datetime import datetime
from typing import Any
class TingwuClient:
def __init__(self):
self.access_key = os.getenv("ALI_ACCESS_KEY", "")
@@ -67,7 +68,7 @@ class TingwuClient:
# Fallback: 使用 mock
print("Tingwu SDK not available, using mock")
return f"mock_task_{int(time.time())}"
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
print(f"Tingwu API error: {e}")
return f"mock_task_{int(time.time())}"
@@ -107,7 +108,7 @@ class TingwuClient:
except ImportError:
print("Tingwu SDK not available, using mock result")
return self._mock_result()
except Exception as e:
except (RuntimeError, ValueError, TypeError) as e:
print(f"Get result error: {e}")
return self._mock_result()

View File

@@ -15,7 +15,6 @@ import hashlib
import hmac
import json
import logging
import urllib.parse
import uuid
from collections.abc import Callable
from dataclasses import dataclass, field
@@ -29,6 +28,12 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger
# Constants
UUID_LENGTH = 8 # UUID 截断长度
DEFAULT_TIMEOUT = 300 # 默认超时时间(秒)
DEFAULT_RETRY_COUNT = 3 # 默认重试次数
DEFAULT_RETRY_DELAY = 5 # 默认重试延迟(秒)
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -1073,7 +1078,7 @@ class WorkflowManager:
# 创建工作流执行日志
log = WorkflowLog(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
workflow_id=workflow_id,
status=TaskStatus.RUNNING.value,
start_time=now,
@@ -1200,7 +1205,7 @@ class WorkflowManager:
# 创建任务日志
task_log = WorkflowLog(
id=str(uuid.uuid4())[:8],
id=str(uuid.uuid4())[:UUID_LENGTH],
workflow_id=task.workflow_id,
task_id=task.id,
status=TaskStatus.RUNNING.value,