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:
@@ -14,6 +14,10 @@ from datetime import datetime
|
|||||||
|
|
||||||
DB_PATH = os.getenv("DB_PATH", "/app/data/insightflow.db")
|
DB_PATH = os.getenv("DB_PATH", "/app/data/insightflow.db")
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
UUID_LENGTH = 8 # UUID 截断长度
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Project:
|
class Project:
|
||||||
id: str
|
id: str
|
||||||
@@ -22,6 +26,7 @@ class Project:
|
|||||||
created_at: str = ""
|
created_at: str = ""
|
||||||
updated_at: str = ""
|
updated_at: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Entity:
|
class Entity:
|
||||||
id: str
|
id: str
|
||||||
@@ -42,6 +47,7 @@ class Entity:
|
|||||||
if self.attributes is None:
|
if self.attributes is None:
|
||||||
self.attributes = {}
|
self.attributes = {}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AttributeTemplate:
|
class AttributeTemplate:
|
||||||
"""属性模板定义"""
|
"""属性模板定义"""
|
||||||
@@ -62,6 +68,7 @@ class AttributeTemplate:
|
|||||||
if self.options is None:
|
if self.options is None:
|
||||||
self.options = []
|
self.options = []
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EntityAttribute:
|
class EntityAttribute:
|
||||||
"""实体属性值"""
|
"""实体属性值"""
|
||||||
@@ -82,6 +89,7 @@ class EntityAttribute:
|
|||||||
if self.options is None:
|
if self.options is None:
|
||||||
self.options = []
|
self.options = []
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AttributeHistory:
|
class AttributeHistory:
|
||||||
"""属性变更历史"""
|
"""属性变更历史"""
|
||||||
@@ -95,6 +103,7 @@ class AttributeHistory:
|
|||||||
changed_at: str = ""
|
changed_at: str = ""
|
||||||
change_reason: str = ""
|
change_reason: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EntityMention:
|
class EntityMention:
|
||||||
id: str
|
id: str
|
||||||
@@ -105,6 +114,7 @@ class EntityMention:
|
|||||||
text_snippet: str
|
text_snippet: str
|
||||||
confidence: float = 1.0
|
confidence: float = 1.0
|
||||||
|
|
||||||
|
|
||||||
class DatabaseManager:
|
class DatabaseManager:
|
||||||
def __init__(self, db_path: str = DB_PATH):
|
def __init__(self, db_path: str = DB_PATH):
|
||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
@@ -321,15 +331,14 @@ class DatabaseManager:
|
|||||||
conn.execute(
|
conn.execute(
|
||||||
"""INSERT INTO entity_mentions (id, entity_id, transcript_id, start_pos, end_pos, text_snippet, confidence)
|
"""INSERT INTO entity_mentions (id, entity_id, transcript_id, start_pos, end_pos, text_snippet, confidence)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(
|
(mention.id,
|
||||||
mention.id,
|
mention.entity_id,
|
||||||
mention.entity_id,
|
mention.transcript_id,
|
||||||
mention.transcript_id,
|
mention.start_pos,
|
||||||
mention.start_pos,
|
mention.end_pos,
|
||||||
mention.end_pos,
|
mention.text_snippet,
|
||||||
mention.text_snippet,
|
mention.confidence,
|
||||||
mention.confidence,
|
),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -358,7 +367,12 @@ class DatabaseManager:
|
|||||||
now = datetime.now().isoformat()
|
now = datetime.now().isoformat()
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO transcripts (id, project_id, filename, full_text, type, created_at) VALUES (?, ?, ?, ?, ?, ?)",
|
"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.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -401,7 +415,7 @@ class DatabaseManager:
|
|||||||
transcript_id: str = "",
|
transcript_id: str = "",
|
||||||
):
|
):
|
||||||
conn = self.get_conn()
|
conn = self.get_conn()
|
||||||
relation_id = str(uuid.uuid4())[:8]
|
relation_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
now = datetime.now().isoformat()
|
now = datetime.now().isoformat()
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""INSERT INTO entity_relations
|
"""INSERT INTO entity_relations
|
||||||
@@ -485,7 +499,7 @@ class DatabaseManager:
|
|||||||
conn.close()
|
conn.close()
|
||||||
return existing["id"]
|
return existing["id"]
|
||||||
|
|
||||||
term_id = str(uuid.uuid4())[:8]
|
term_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO glossary (id, project_id, term, pronunciation, frequency) VALUES (?, ?, ?, ?, ?)",
|
"INSERT INTO glossary (id, project_id, term, pronunciation, frequency) VALUES (?, ?, ?, ?, ?)",
|
||||||
(term_id, project_id, term, pronunciation, 1),
|
(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)
|
(id, entity_id, template_id, old_value, new_value, changed_by, changed_at, change_reason)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(
|
(
|
||||||
str(uuid.uuid4())[:8],
|
str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
attr.entity_id,
|
attr.entity_id,
|
||||||
attr.template_id,
|
attr.template_id,
|
||||||
old_value,
|
old_value,
|
||||||
@@ -915,7 +929,7 @@ class DatabaseManager:
|
|||||||
(id, entity_id, template_id, old_value, new_value, changed_by, changed_at, change_reason)
|
(id, entity_id, template_id, old_value, new_value, changed_by, changed_at, change_reason)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(
|
(
|
||||||
str(uuid.uuid4())[:8],
|
str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id,
|
entity_id,
|
||||||
template_id,
|
template_id,
|
||||||
old_row["value"],
|
old_row["value"],
|
||||||
@@ -1387,9 +1401,11 @@ class DatabaseManager:
|
|||||||
conn.close()
|
conn.close()
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
|
|
||||||
# Singleton instance
|
# Singleton instance
|
||||||
_db_manager = None
|
_db_manager = None
|
||||||
|
|
||||||
|
|
||||||
def get_db_manager() -> DatabaseManager:
|
def get_db_manager() -> DatabaseManager:
|
||||||
global _db_manager
|
global _db_manager
|
||||||
if _db_manager is None:
|
if _db_manager is None:
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ class GrowthManager:
|
|||||||
await client.post(
|
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 Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
print(f"Failed to send to Mixpanel: {e}")
|
print(f"Failed to send to Mixpanel: {e}")
|
||||||
|
|
||||||
async def _send_to_amplitude(self, event: AnalyticsEvent):
|
async def _send_to_amplitude(self, event: AnalyticsEvent):
|
||||||
@@ -484,7 +484,7 @@ class GrowthManager:
|
|||||||
json=payload,
|
json=payload,
|
||||||
timeout=10.0,
|
timeout=10.0,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
print(f"Failed to send to Amplitude: {e}")
|
print(f"Failed to send to Amplitude: {e}")
|
||||||
|
|
||||||
async def _update_user_profile(
|
async def _update_user_profile(
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import os
|
|||||||
import uuid
|
import uuid
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
UUID_LENGTH = 8 # UUID 截断长度
|
||||||
|
|
||||||
# 尝试导入图像处理库
|
# 尝试导入图像处理库
|
||||||
try:
|
try:
|
||||||
from PIL import Image, ImageEnhance, ImageFilter
|
from PIL import Image, ImageEnhance, ImageFilter
|
||||||
@@ -369,7 +372,7 @@ class ImageProcessor:
|
|||||||
Returns:
|
Returns:
|
||||||
图片处理结果
|
图片处理结果
|
||||||
"""
|
"""
|
||||||
image_id = image_id or str(uuid.uuid4())[:8]
|
image_id = image_id or str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
|
|
||||||
if not PIL_AVAILABLE:
|
if not PIL_AVAILABLE:
|
||||||
return ImageProcessingResult(
|
return ImageProcessingResult(
|
||||||
|
|||||||
@@ -15,11 +15,13 @@ import httpx
|
|||||||
KIMI_API_KEY = os.getenv("KIMI_API_KEY", "")
|
KIMI_API_KEY = os.getenv("KIMI_API_KEY", "")
|
||||||
KIMI_BASE_URL = os.getenv("KIMI_BASE_URL", "https://api.kimi.com/coding")
|
KIMI_BASE_URL = os.getenv("KIMI_BASE_URL", "https://api.kimi.com/coding")
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ChatMessage:
|
class ChatMessage:
|
||||||
role: str
|
role: str
|
||||||
content: str
|
content: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EntityExtractionResult:
|
class EntityExtractionResult:
|
||||||
name: str
|
name: str
|
||||||
@@ -27,6 +29,7 @@ class EntityExtractionResult:
|
|||||||
definition: str
|
definition: str
|
||||||
confidence: float
|
confidence: float
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RelationExtractionResult:
|
class RelationExtractionResult:
|
||||||
source: str
|
source: str
|
||||||
@@ -34,6 +37,7 @@ class RelationExtractionResult:
|
|||||||
type: str
|
type: str
|
||||||
confidence: float
|
confidence: float
|
||||||
|
|
||||||
|
|
||||||
class LLMClient:
|
class LLMClient:
|
||||||
"""Kimi API 客户端"""
|
"""Kimi API 客户端"""
|
||||||
|
|
||||||
@@ -163,7 +167,7 @@ class LLMClient:
|
|||||||
for r in data.get("relations", [])
|
for r in data.get("relations", [])
|
||||||
]
|
]
|
||||||
return entities, relations
|
return entities, relations
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
print(f"Parse extraction result failed: {e}")
|
print(f"Parse extraction result failed: {e}")
|
||||||
return [], []
|
return [], []
|
||||||
|
|
||||||
@@ -254,9 +258,11 @@ class LLMClient:
|
|||||||
messages = [ChatMessage(role="user", content=prompt)]
|
messages = [ChatMessage(role="user", content=prompt)]
|
||||||
return await self.chat(messages, temperature=0.3)
|
return await self.chat(messages, temperature=0.3)
|
||||||
|
|
||||||
|
|
||||||
# Singleton instance
|
# Singleton instance
|
||||||
_llm_client = None
|
_llm_client = None
|
||||||
|
|
||||||
|
|
||||||
def get_llm_client() -> LLMClient:
|
def get_llm_client() -> LLMClient:
|
||||||
global _llm_client
|
global _llm_client
|
||||||
if _llm_client is None:
|
if _llm_client is None:
|
||||||
|
|||||||
104
backend/main.py
104
backend/main.py
@@ -38,6 +38,14 @@ from pydantic import BaseModel, Field
|
|||||||
# Configure logger
|
# Configure logger
|
||||||
logger = logging.getLogger(__name__)
|
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
|
# Add backend directory to path for imports
|
||||||
backend_dir = os.path.dirname(os.path.abspath(__file__))
|
backend_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
if backend_dir not in sys.path:
|
if backend_dir not in sys.path:
|
||||||
@@ -101,7 +109,7 @@ except ImportError:
|
|||||||
REASONER_AVAILABLE = False
|
REASONER_AVAILABLE = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from export_manager import ExportEntity, ExportRelation, ExportTranscript, get_export_manager
|
from export_manager import get_export_manager
|
||||||
|
|
||||||
EXPORT_AVAILABLE = True
|
EXPORT_AVAILABLE = True
|
||||||
except ImportError:
|
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:
|
if x_api_key and x_api_key == MASTER_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]}"
|
limit_key = f"master:{x_api_key[:16]}"
|
||||||
elif hasattr(request.state, "api_key") and request.state.api_key:
|
elif hasattr(request.state, "api_key") and request.state.api_key:
|
||||||
# 使用 API Key 的限流配置
|
# 使用 API Key 的限流配置
|
||||||
@@ -500,7 +508,7 @@ async def rate_limit_middleware(request: Request, call_next):
|
|||||||
else:
|
else:
|
||||||
# IP 限流(未认证用户)
|
# IP 限流(未认证用户)
|
||||||
client_ip = request.client.host if request.client else "unknown"
|
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}"
|
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 "",
|
ip_address=request.client.host if request.client else "",
|
||||||
user_agent=request.headers.get("User-Agent", ""),
|
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}")
|
print(f"Failed to log API call: {e}")
|
||||||
|
|
||||||
@@ -1062,7 +1070,7 @@ async def create_manual_entity(
|
|||||||
if existing:
|
if existing:
|
||||||
return {"id": existing.id, "name": existing.name, "type": existing.type, "existed": True}
|
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(
|
new_entity = db.create_entity(
|
||||||
Entity(
|
Entity(
|
||||||
id=entity_id,
|
id=entity_id,
|
||||||
@@ -1079,7 +1087,7 @@ async def create_manual_entity(
|
|||||||
if transcript:
|
if transcript:
|
||||||
text = transcript["full_text"]
|
text = transcript["full_text"]
|
||||||
mention = EntityMention(
|
mention = EntityMention(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id=entity_id,
|
entity_id=entity_id,
|
||||||
transcript_id=entity.transcript_id,
|
transcript_id=entity.transcript_id,
|
||||||
start_pos=entity.start_pos,
|
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")
|
raise HTTPException(status_code=500, detail="Database not available")
|
||||||
|
|
||||||
db = get_db_manager()
|
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)
|
p = db.create_project(project_id, project.name, project.description)
|
||||||
return {"id": p.id, "name": p.name, "description": p.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"])
|
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(
|
db.save_transcript(
|
||||||
transcript_id=transcript_id,
|
transcript_id=transcript_id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
@@ -1290,7 +1298,7 @@ async def upload_audio(project_id: str, file: UploadFile = File(...), _=Depends(
|
|||||||
else:
|
else:
|
||||||
new_ent = db.create_entity(
|
new_ent = db.create_entity(
|
||||||
Entity(
|
Entity(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
name=raw_ent["name"],
|
name=raw_ent["name"],
|
||||||
type=raw_ent.get("type", "OTHER"),
|
type=raw_ent.get("type", "OTHER"),
|
||||||
@@ -1313,7 +1321,7 @@ async def upload_audio(project_id: str, file: UploadFile = File(...), _=Depends(
|
|||||||
if pos == -1:
|
if pos == -1:
|
||||||
break
|
break
|
||||||
mention = EntityMention(
|
mention = EntityMention(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id=entity_name_to_id[name],
|
entity_id=entity_name_to_id[name],
|
||||||
transcript_id=transcript_id,
|
transcript_id=transcript_id,
|
||||||
start_pos=pos,
|
start_pos=pos,
|
||||||
@@ -1374,11 +1382,11 @@ async def upload_document(project_id: str, file: UploadFile = File(...), _=Depen
|
|||||||
processor = get_doc_processor()
|
processor = get_doc_processor()
|
||||||
try:
|
try:
|
||||||
result = processor.process(content, file.filename)
|
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)}")
|
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(
|
db.save_transcript(
|
||||||
transcript_id=transcript_id,
|
transcript_id=transcript_id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
@@ -1411,7 +1419,7 @@ async def upload_document(project_id: str, file: UploadFile = File(...), _=Depen
|
|||||||
else:
|
else:
|
||||||
new_ent = db.create_entity(
|
new_ent = db.create_entity(
|
||||||
Entity(
|
Entity(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
name=raw_ent["name"],
|
name=raw_ent["name"],
|
||||||
type=raw_ent.get("type", "OTHER"),
|
type=raw_ent.get("type", "OTHER"),
|
||||||
@@ -1437,7 +1445,7 @@ async def upload_document(project_id: str, file: UploadFile = File(...), _=Depen
|
|||||||
if pos == -1:
|
if pos == -1:
|
||||||
break
|
break
|
||||||
mention = EntityMention(
|
mention = EntityMention(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id=entity_name_to_id[name],
|
entity_id=entity_name_to_id[name],
|
||||||
transcript_id=transcript_id,
|
transcript_id=transcript_id,
|
||||||
start_pos=pos,
|
start_pos=pos,
|
||||||
@@ -2282,7 +2290,7 @@ async def create_attribute_template_endpoint(
|
|||||||
raise HTTPException(status_code=404, detail="Project not found")
|
raise HTTPException(status_code=404, detail="Project not found")
|
||||||
|
|
||||||
new_template = AttributeTemplate(
|
new_template = AttributeTemplate(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
name=template.name,
|
name=template.name,
|
||||||
type=template.type,
|
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)
|
(id, entity_id, attribute_name, old_value, new_value, changed_by, changed_at, change_reason)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(
|
(
|
||||||
str(uuid.uuid4())[:8],
|
str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id,
|
entity_id,
|
||||||
attr.name,
|
attr.name,
|
||||||
existing["value"],
|
existing["value"],
|
||||||
@@ -2444,7 +2452,7 @@ async def set_entity_attribute_endpoint(
|
|||||||
attr_id = existing["id"]
|
attr_id = existing["id"]
|
||||||
else:
|
else:
|
||||||
# 创建
|
# 创建
|
||||||
attr_id = str(uuid.uuid4())[:8]
|
attr_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""INSERT INTO entity_attributes
|
"""INSERT INTO entity_attributes
|
||||||
(id, entity_id, template_id, name, type, value, options, created_at, updated_at)
|
(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)
|
(id, entity_id, attribute_name, old_value, new_value, changed_by, changed_at, change_reason)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(
|
(
|
||||||
str(uuid.uuid4())[:8],
|
str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id,
|
entity_id,
|
||||||
attr.name,
|
attr.name,
|
||||||
None,
|
None,
|
||||||
@@ -2499,7 +2507,7 @@ async def batch_set_entity_attributes_endpoint(
|
|||||||
template = db.get_attribute_template(attr_data.template_id)
|
template = db.get_attribute_template(attr_data.template_id)
|
||||||
if template:
|
if template:
|
||||||
new_attr = EntityAttribute(
|
new_attr = EntityAttribute(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id=entity_id,
|
entity_id=entity_id,
|
||||||
template_id=attr_data.template_id,
|
template_id=attr_data.template_id,
|
||||||
value=attr_data.value,
|
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()
|
reasoner = get_knowledge_reasoner()
|
||||||
summary_result = reasoner.generate_project_summary(project_id, db)
|
summary_result = reasoner.generate_project_summary(project_id, db)
|
||||||
summary = summary_result.get("summary", "")
|
summary = summary_result.get("summary", "")
|
||||||
except Exception:
|
except (RuntimeError, ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
export_mgr = get_export_manager()
|
export_mgr = get_export_manager()
|
||||||
@@ -3113,7 +3121,7 @@ async def neo4j_status(_=Depends(verify_api_key)):
|
|||||||
"uri": manager.uri if connected else None,
|
"uri": manager.uri if connected else None,
|
||||||
"message": "Connected" if connected else "Not connected",
|
"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)}
|
return {"available": True, "connected": False, "message": str(e)}
|
||||||
|
|
||||||
@app.post("/api/v1/neo4j/sync")
|
@app.post("/api/v1/neo4j/sync")
|
||||||
@@ -3658,7 +3666,7 @@ async def create_workflow_endpoint(request: WorkflowCreate, _=Depends(verify_api
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
workflow = Workflow(
|
workflow = Workflow(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
name=request.name,
|
name=request.name,
|
||||||
description=request.description,
|
description=request.description,
|
||||||
workflow_type=request.workflow_type,
|
workflow_type=request.workflow_type,
|
||||||
@@ -3847,7 +3855,7 @@ async def trigger_workflow_endpoint(
|
|||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=404, detail=str(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))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
@app.get(
|
@app.get(
|
||||||
@@ -3924,7 +3932,7 @@ async def create_webhook_endpoint(request: WebhookCreate, _=Depends(verify_api_k
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
webhook = WebhookConfig(
|
webhook = WebhookConfig(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
name=request.name,
|
name=request.name,
|
||||||
webhook_type=request.webhook_type,
|
webhook_type=request.webhook_type,
|
||||||
url=request.url,
|
url=request.url,
|
||||||
@@ -4175,7 +4183,7 @@ async def upload_video_endpoint(
|
|||||||
processor = get_multimodal_processor(frame_interval=extract_interval)
|
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)
|
result = processor.process_video(video_data, file.filename, project_id, video_id)
|
||||||
|
|
||||||
if not result.success:
|
if not result.success:
|
||||||
@@ -4252,7 +4260,7 @@ async def upload_video_endpoint(
|
|||||||
else:
|
else:
|
||||||
new_ent = db.create_entity(
|
new_ent = db.create_entity(
|
||||||
Entity(
|
Entity(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
name=raw_ent["name"],
|
name=raw_ent["name"],
|
||||||
type=raw_ent.get("type", "OTHER"),
|
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)
|
(id, project_id, entity_id, modality, source_id, source_type, text_snippet, confidence, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(
|
(
|
||||||
str(uuid.uuid4())[:8],
|
str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id,
|
project_id,
|
||||||
entity_name_to_id[raw_ent["name"]],
|
entity_name_to_id[raw_ent["name"]],
|
||||||
"video",
|
"video",
|
||||||
@@ -4356,7 +4364,7 @@ async def upload_image_endpoint(
|
|||||||
processor = get_image_processor()
|
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)
|
result = processor.process_image(image_data, file.filename, image_id, detect_type)
|
||||||
|
|
||||||
if not result.success:
|
if not result.success:
|
||||||
@@ -4406,7 +4414,7 @@ async def upload_image_endpoint(
|
|||||||
if not existing:
|
if not existing:
|
||||||
new_ent = db.create_entity(
|
new_ent = db.create_entity(
|
||||||
Entity(
|
Entity(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
name=entity.name,
|
name=entity.name,
|
||||||
type=entity.type,
|
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)
|
(id, project_id, entity_id, modality, source_id, source_type, text_snippet, confidence, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(
|
(
|
||||||
str(uuid.uuid4())[:8],
|
str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id,
|
project_id,
|
||||||
entity_id,
|
entity_id,
|
||||||
"image",
|
"image",
|
||||||
@@ -5156,7 +5164,7 @@ async def create_plugin_endpoint(request: PluginCreate, _=Depends(verify_api_key
|
|||||||
manager = get_plugin_manager_instance()
|
manager = get_plugin_manager_instance()
|
||||||
|
|
||||||
plugin = Plugin(
|
plugin = Plugin(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
name=request.name,
|
name=request.name,
|
||||||
plugin_type=request.plugin_type,
|
plugin_type=request.plugin_type,
|
||||||
project_id=request.project_id,
|
project_id=request.project_id,
|
||||||
@@ -8056,7 +8064,7 @@ async def create_tenant(
|
|||||||
"status": tenant.status,
|
"status": tenant.status,
|
||||||
"created_at": tenant.created_at.isoformat(),
|
"created_at": tenant.created_at.isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants", tags=["Tenants"])
|
@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,
|
"verification_instructions": instructions,
|
||||||
"created_at": domain.created_at.isoformat(),
|
"created_at": domain.created_at.isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/domains", tags=["Tenants"])
|
@app.get("/api/v1/tenants/{tenant_id}/domains", tags=["Tenants"])
|
||||||
@@ -8313,7 +8321,7 @@ async def invite_member(
|
|||||||
"status": member.status,
|
"status": member.status,
|
||||||
"invited_at": member.invited_at.isoformat(),
|
"invited_at": member.invited_at.isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/members", tags=["Tenants"])
|
@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,
|
"trial_end": subscription.trial_end.isoformat() if subscription.trial_end else None,
|
||||||
"created_at": subscription.created_at.isoformat(),
|
"created_at": subscription.created_at.isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/subscription", tags=["Subscriptions"])
|
@app.get("/api/v1/tenants/{tenant_id}/subscription", tags=["Subscriptions"])
|
||||||
@@ -9259,7 +9267,7 @@ async def change_subscription_plan(
|
|||||||
"status": updated.status,
|
"status": updated.status,
|
||||||
"message": "Plan changed successfully",
|
"message": "Plan changed successfully",
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.post("/api/v1/tenants/{tenant_id}/subscription/cancel", tags=["Subscriptions"])
|
@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,
|
"canceled_at": updated.canceled_at.isoformat() if updated.canceled_at else None,
|
||||||
"message": "Subscription cancelled",
|
"message": "Subscription cancelled",
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
# Usage APIs
|
# Usage APIs
|
||||||
@@ -9498,7 +9506,7 @@ async def request_refund(
|
|||||||
"status": refund.status,
|
"status": refund.status,
|
||||||
"requested_at": refund.requested_at.isoformat(),
|
"requested_at": refund.requested_at.isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/refunds", tags=["Subscriptions"])
|
@app.get("/api/v1/tenants/{tenant_id}/refunds", tags=["Subscriptions"])
|
||||||
@@ -9636,7 +9644,7 @@ async def create_stripe_checkout(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return session
|
return session
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.post("/api/v1/tenants/{tenant_id}/checkout/alipay", tags=["Subscriptions"])
|
@app.post("/api/v1/tenants/{tenant_id}/checkout/alipay", tags=["Subscriptions"])
|
||||||
@@ -9658,7 +9666,7 @@ async def create_alipay_order(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return order
|
return order
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.post("/api/v1/tenants/{tenant_id}/checkout/wechat", tags=["Subscriptions"])
|
@app.post("/api/v1/tenants/{tenant_id}/checkout/wechat", tags=["Subscriptions"])
|
||||||
@@ -9680,7 +9688,7 @@ async def create_wechat_order(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return order
|
return order
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
# Webhook Handlers
|
# Webhook Handlers
|
||||||
@@ -9876,7 +9884,7 @@ async def create_sso_config_endpoint(
|
|||||||
"default_role": sso_config.default_role,
|
"default_role": sso_config.default_role,
|
||||||
"created_at": sso_config.created_at.isoformat(),
|
"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))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/sso-configs", tags=["Enterprise"])
|
@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,
|
"sync_interval_minutes": scim_config.sync_interval_minutes,
|
||||||
"created_at": scim_config.created_at.isoformat(),
|
"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))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/scim-configs", tags=["Enterprise"])
|
@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,
|
"expires_at": export.expires_at.isoformat() if export.expires_at else None,
|
||||||
"created_at": export.created_at.isoformat(),
|
"created_at": export.created_at.isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/audit-exports", tags=["Enterprise"])
|
@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,
|
"is_active": new_policy.is_active,
|
||||||
"created_at": new_policy.created_at.isoformat(),
|
"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))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/tenants/{tenant_id}/retention-policies", tags=["Enterprise"])
|
@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()}
|
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))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/analytics/dashboard/{tenant_id}", tags=["Growth & Analytics"])
|
@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,
|
"variants": experiment.variants,
|
||||||
"created_at": experiment.created_at,
|
"created_at": experiment.created_at,
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/experiments", tags=["Growth & Analytics"])
|
@app.get("/api/v1/experiments", tags=["Growth & Analytics"])
|
||||||
@@ -12231,7 +12239,7 @@ async def create_email_template_endpoint(request: CreateEmailTemplateRequest):
|
|||||||
"variables": template.variables,
|
"variables": template.variables,
|
||||||
"created_at": template.created_at,
|
"created_at": template.created_at,
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
@app.get("/api/v1/email/templates", tags=["Growth & Analytics"])
|
@app.get("/api/v1/email/templates", tags=["Growth & Analytics"])
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import uuid
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from difflib import SequenceMatcher
|
from difflib import SequenceMatcher
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
UUID_LENGTH = 8 # UUID 截断长度
|
||||||
|
|
||||||
# 尝试导入embedding库
|
# 尝试导入embedding库
|
||||||
try:
|
try:
|
||||||
NUMPY_AVAILABLE = True
|
NUMPY_AVAILABLE = True
|
||||||
@@ -247,7 +250,7 @@ class MultimodalEntityLinker:
|
|||||||
|
|
||||||
if result and result.matched_entity_id:
|
if result and result.matched_entity_id:
|
||||||
link = EntityLink(
|
link = EntityLink(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
source_entity_id=ent1.get("id"),
|
source_entity_id=ent1.get("id"),
|
||||||
target_entity_id=result.matched_entity_id,
|
target_entity_id=result.matched_entity_id,
|
||||||
@@ -461,7 +464,7 @@ class MultimodalEntityLinker:
|
|||||||
多模态实体记录
|
多模态实体记录
|
||||||
"""
|
"""
|
||||||
return MultimodalEntity(
|
return MultimodalEntity(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
entity_id=entity_id,
|
entity_id=entity_id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
name="", # 将在后续填充
|
name="", # 将在后续填充
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import uuid
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
UUID_LENGTH = 8 # UUID 截断长度
|
||||||
|
|
||||||
# 尝试导入OCR库
|
# 尝试导入OCR库
|
||||||
try:
|
try:
|
||||||
import pytesseract
|
import pytesseract
|
||||||
@@ -340,7 +343,7 @@ class MultimodalProcessor:
|
|||||||
Returns:
|
Returns:
|
||||||
视频处理结果
|
视频处理结果
|
||||||
"""
|
"""
|
||||||
video_id = video_id or str(uuid.uuid4())[:8]
|
video_id = video_id or str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 保存视频文件
|
# 保存视频文件
|
||||||
@@ -375,7 +378,7 @@ class MultimodalProcessor:
|
|||||||
ocr_text, confidence = self.perform_ocr(frame_path)
|
ocr_text, confidence = self.perform_ocr(frame_path)
|
||||||
|
|
||||||
frame = VideoFrame(
|
frame = VideoFrame(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
video_id=video_id,
|
video_id=video_id,
|
||||||
frame_number=frame_number,
|
frame_number=frame_number,
|
||||||
timestamp=timestamp,
|
timestamp=timestamp,
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class Neo4jManager:
|
|||||||
# 验证连接
|
# 验证连接
|
||||||
self._driver.verify_connectivity()
|
self._driver.verify_connectivity()
|
||||||
logger.info(f"Connected to Neo4j at {self.uri}")
|
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}")
|
logger.error(f"Failed to connect to Neo4j: {e}")
|
||||||
self._driver = None
|
self._driver = None
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import json
|
|||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import time
|
import time
|
||||||
import urllib.parse
|
|
||||||
import uuid
|
import uuid
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -20,6 +19,9 @@ from typing import Any
|
|||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
UUID_LENGTH = 8 # UUID 截断长度
|
||||||
|
|
||||||
# WebDAV 支持
|
# WebDAV 支持
|
||||||
try:
|
try:
|
||||||
import webdav4.client as webdav_client
|
import webdav4.client as webdav_client
|
||||||
@@ -318,7 +320,7 @@ class PluginManager:
|
|||||||
)
|
)
|
||||||
config_id = existing["id"]
|
config_id = existing["id"]
|
||||||
else:
|
else:
|
||||||
config_id = str(uuid.uuid4())[:8]
|
config_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""INSERT INTO plugin_configs
|
"""INSERT INTO plugin_configs
|
||||||
(id, plugin_id, config_key, config_value, is_encrypted, created_at, updated_at)
|
(id, plugin_id, config_key, config_value, is_encrypted, created_at, updated_at)
|
||||||
@@ -400,7 +402,7 @@ class ChromeExtensionHandler:
|
|||||||
expires_days: int = None,
|
expires_days: int = None,
|
||||||
) -> ChromeExtensionToken:
|
) -> ChromeExtensionToken:
|
||||||
"""创建 Chrome 扩展令牌"""
|
"""创建 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('=')}"
|
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"}
|
return {"success": False, "error": "Insufficient permissions"}
|
||||||
|
|
||||||
# 创建转录记录(将网页作为文档处理)
|
# 创建转录记录(将网页作为文档处理)
|
||||||
transcript_id = str(uuid.uuid4())[:8]
|
transcript_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
now = datetime.now().isoformat()
|
now = datetime.now().isoformat()
|
||||||
|
|
||||||
# 构建完整文本
|
# 构建完整文本
|
||||||
@@ -604,7 +606,7 @@ class BotHandler:
|
|||||||
secret: str = "",
|
secret: str = "",
|
||||||
) -> BotSession:
|
) -> BotSession:
|
||||||
"""创建机器人会话"""
|
"""创建机器人会话"""
|
||||||
bot_id = str(uuid.uuid4())[:8]
|
bot_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
now = datetime.now().isoformat()
|
now = datetime.now().isoformat()
|
||||||
|
|
||||||
conn = self.pm.db.get_conn()
|
conn = self.pm.db.get_conn()
|
||||||
@@ -932,7 +934,7 @@ class WebhookIntegration:
|
|||||||
trigger_events: list[str] = None,
|
trigger_events: list[str] = None,
|
||||||
) -> WebhookEndpoint:
|
) -> WebhookEndpoint:
|
||||||
"""创建 Webhook 端点"""
|
"""创建 Webhook 端点"""
|
||||||
endpoint_id = str(uuid.uuid4())[:8]
|
endpoint_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
now = datetime.now().isoformat()
|
now = datetime.now().isoformat()
|
||||||
|
|
||||||
conn = self.pm.db.get_conn()
|
conn = self.pm.db.get_conn()
|
||||||
@@ -1155,7 +1157,7 @@ class WebDAVSyncManager:
|
|||||||
sync_interval: int = 3600,
|
sync_interval: int = 3600,
|
||||||
) -> WebDAVSync:
|
) -> WebDAVSync:
|
||||||
"""创建 WebDAV 同步配置"""
|
"""创建 WebDAV 同步配置"""
|
||||||
sync_id = str(uuid.uuid4())[:8]
|
sync_id = str(uuid.uuid4())[:UUID_LENGTH]
|
||||||
now = datetime.now().isoformat()
|
now = datetime.now().isoformat()
|
||||||
|
|
||||||
conn = self.pm.db.get_conn()
|
conn = self.pm.db.get_conn()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RateLimitConfig:
|
class RateLimitConfig:
|
||||||
"""限流配置"""
|
"""限流配置"""
|
||||||
@@ -20,6 +21,7 @@ class RateLimitConfig:
|
|||||||
burst_size: int = 10 # 突发请求数
|
burst_size: int = 10 # 突发请求数
|
||||||
window_size: int = 60 # 窗口大小(秒)
|
window_size: int = 60 # 窗口大小(秒)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RateLimitInfo:
|
class RateLimitInfo:
|
||||||
"""限流信息"""
|
"""限流信息"""
|
||||||
@@ -29,6 +31,7 @@ class RateLimitInfo:
|
|||||||
reset_time: int # 重置时间戳
|
reset_time: int # 重置时间戳
|
||||||
retry_after: int # 需要等待的秒数
|
retry_after: int # 需要等待的秒数
|
||||||
|
|
||||||
|
|
||||||
class SlidingWindowCounter:
|
class SlidingWindowCounter:
|
||||||
"""滑动窗口计数器"""
|
"""滑动窗口计数器"""
|
||||||
|
|
||||||
@@ -60,6 +63,7 @@ class SlidingWindowCounter:
|
|||||||
for k in old_keys:
|
for k in old_keys:
|
||||||
self.requests.pop(k, None)
|
self.requests.pop(k, None)
|
||||||
|
|
||||||
|
|
||||||
class RateLimiter:
|
class RateLimiter:
|
||||||
"""API 限流器"""
|
"""API 限流器"""
|
||||||
|
|
||||||
@@ -155,9 +159,11 @@ class RateLimiter:
|
|||||||
self.counters.clear()
|
self.counters.clear()
|
||||||
self.configs.clear()
|
self.configs.clear()
|
||||||
|
|
||||||
|
|
||||||
# 全局限流器实例
|
# 全局限流器实例
|
||||||
_rate_limiter: RateLimiter | None = None
|
_rate_limiter: RateLimiter | None = None
|
||||||
|
|
||||||
|
|
||||||
def get_rate_limiter() -> RateLimiter:
|
def get_rate_limiter() -> RateLimiter:
|
||||||
"""获取限流器实例"""
|
"""获取限流器实例"""
|
||||||
global _rate_limiter
|
global _rate_limiter
|
||||||
@@ -166,6 +172,8 @@ def get_rate_limiter() -> RateLimiter:
|
|||||||
return _rate_limiter
|
return _rate_limiter
|
||||||
|
|
||||||
# 限流装饰器(用于函数级别限流)
|
# 限流装饰器(用于函数级别限流)
|
||||||
|
|
||||||
|
|
||||||
def rate_limit(requests_per_minute: int = 60, key_func: Callable | None = None) -> None:
|
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
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
class RateLimitExceeded(Exception):
|
class RateLimitExceeded(Exception):
|
||||||
"""限流异常"""
|
"""限流异常"""
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ class TenantManager:
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
logger.info("Tenant tables initialized successfully")
|
logger.info("Tenant tables initialized successfully")
|
||||||
|
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
logger.error(f"Error initializing tenant tables: {e}")
|
logger.error(f"Error initializing tenant tables: {e}")
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
@@ -760,7 +760,7 @@ class TenantManager:
|
|||||||
|
|
||||||
return is_verified
|
return is_verified
|
||||||
|
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
logger.error(f"Error verifying domain: {e}")
|
logger.error(f"Error verifying domain: {e}")
|
||||||
return False
|
return False
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class TingwuClient:
|
class TingwuClient:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.access_key = os.getenv("ALI_ACCESS_KEY", "")
|
self.access_key = os.getenv("ALI_ACCESS_KEY", "")
|
||||||
@@ -67,7 +68,7 @@ class TingwuClient:
|
|||||||
# Fallback: 使用 mock
|
# Fallback: 使用 mock
|
||||||
print("Tingwu SDK not available, using mock")
|
print("Tingwu SDK not available, using mock")
|
||||||
return f"mock_task_{int(time.time())}"
|
return f"mock_task_{int(time.time())}"
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
print(f"Tingwu API error: {e}")
|
print(f"Tingwu API error: {e}")
|
||||||
return f"mock_task_{int(time.time())}"
|
return f"mock_task_{int(time.time())}"
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ class TingwuClient:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
print("Tingwu SDK not available, using mock result")
|
print("Tingwu SDK not available, using mock result")
|
||||||
return self._mock_result()
|
return self._mock_result()
|
||||||
except Exception as e:
|
except (RuntimeError, ValueError, TypeError) as e:
|
||||||
print(f"Get result error: {e}")
|
print(f"Get result error: {e}")
|
||||||
return self._mock_result()
|
return self._mock_result()
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import hashlib
|
|||||||
import hmac
|
import hmac
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import urllib.parse
|
|
||||||
import uuid
|
import uuid
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
@@ -29,6 +28,12 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|||||||
from apscheduler.triggers.cron import CronTrigger
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
from apscheduler.triggers.interval import IntervalTrigger
|
from apscheduler.triggers.interval import IntervalTrigger
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
UUID_LENGTH = 8 # UUID 截断长度
|
||||||
|
DEFAULT_TIMEOUT = 300 # 默认超时时间(秒)
|
||||||
|
DEFAULT_RETRY_COUNT = 3 # 默认重试次数
|
||||||
|
DEFAULT_RETRY_DELAY = 5 # 默认重试延迟(秒)
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -1073,7 +1078,7 @@ class WorkflowManager:
|
|||||||
|
|
||||||
# 创建工作流执行日志
|
# 创建工作流执行日志
|
||||||
log = WorkflowLog(
|
log = WorkflowLog(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
workflow_id=workflow_id,
|
workflow_id=workflow_id,
|
||||||
status=TaskStatus.RUNNING.value,
|
status=TaskStatus.RUNNING.value,
|
||||||
start_time=now,
|
start_time=now,
|
||||||
@@ -1200,7 +1205,7 @@ class WorkflowManager:
|
|||||||
|
|
||||||
# 创建任务日志
|
# 创建任务日志
|
||||||
task_log = WorkflowLog(
|
task_log = WorkflowLog(
|
||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:UUID_LENGTH],
|
||||||
workflow_id=task.workflow_id,
|
workflow_id=task.workflow_id,
|
||||||
task_id=task.id,
|
task_id=task.id,
|
||||||
status=TaskStatus.RUNNING.value,
|
status=TaskStatus.RUNNING.value,
|
||||||
|
|||||||
Reference in New Issue
Block a user