Files
company-ai-platform/app/modules/ai_agent/adapters/common.py
JiuContinent d7db84571d ```
feat: 添加飞书用户模块和订阅功能支持

- 新增feishu_users模块用于处理飞书用户身份验证和权限管理
- 新增subscriptions模块用于处理订阅相关功能
- 新增personalization模块用于个性化服务
- 在alembic迁移配置中注册新的模型模块
- 在API路由器中添加feishu_users和subscriptions路由
- 实现事件调度服务的改进,包括错误处理和状态更新优化
- 添加飞书命令处理的权限检查机制
- 实现飞书应用票据事件处理
- 改进审计日志记录功能
```
2026-07-27 08:02:17 +08:00

110 lines
3.4 KiB
Python

import json
from typing import Any
from fastapi import HTTPException, status
from app.modules.ai_agent.constants import (
COMPANY_MANAGEMENT_SYSTEM_INSTRUCTIONS,
UNEXPECTED_HERMES_RESPONSE,
AIChatRole,
AIContextKey,
AIErrorKey,
AIHttpPayloadKey,
AIResponseKey,
)
def _error_detail(exc: Exception) -> Any:
if isinstance(exc, HTTPException):
return exc.detail
return {AIResponseKey.TYPE: type(exc).__name__, AIResponseKey.MESSAGE: str(exc)}
def _response_payload(response: Any) -> dict[str, Any]:
try:
data = response.json()
except ValueError:
data = {AIResponseKey.TEXT: response.text}
return {AIResponseKey.STATUS_CODE: response.status_code, AIResponseKey.DATA: data}
def _chat_completion_payload(response: Any, error_key: AIErrorKey) -> dict[str, Any]:
try:
return response.json()
except ValueError as exc:
raise HTTPException(
status_code=status.HTTP_502_BAD_GATEWAY,
detail={
error_key: UNEXPECTED_HERMES_RESPONSE,
AIResponseKey.RAW: {AIResponseKey.TEXT: response.text},
},
) from exc
def _chat_messages(prompt: str, context: dict[str, Any] | None = None) -> list[dict[str, str]]:
request_context = context or {}
return [
{
AIHttpPayloadKey.ROLE: AIChatRole.SYSTEM,
AIHttpPayloadKey.CONTENT: COMPANY_MANAGEMENT_SYSTEM_INSTRUCTIONS,
},
{
AIHttpPayloadKey.ROLE: AIChatRole.USER,
AIHttpPayloadKey.CONTENT: _ordered_context(prompt, request_context),
},
]
def _ordered_context(prompt: str, context: dict[str, Any]) -> str:
"""Serialize trusted personalization layers in their required precedence."""
company_rules = context.get(AIContextKey.COMPANY_RULES)
if company_rules is None:
company_rules = context.get(AIContextKey.USER_RULES) or []
controlled_keys = {
AIContextKey.USER_RULES,
AIContextKey.COMPANY_RULES,
AIContextKey.PERSONAL_RULES,
AIContextKey.PREFERENCES,
AIContextKey.INTERESTS,
AIContextKey.LOCAL_MEMORY,
AIContextKey.CONVERSATION_HISTORY,
AIContextKey.PROVIDER_SESSION_ID,
AIContextKey.ALLOW_PROVIDER_MEMORY,
}
current_context = {
str(key): value for key, value in context.items() if key not in controlled_keys
}
sections = [
("公司规则", company_rules),
("个人规则", context.get(AIContextKey.PERSONAL_RULES) or []),
(
"当前请求",
{
"prompt": prompt,
"context": current_context,
},
),
(
"个人偏好与兴趣",
{
"preferences": context.get(AIContextKey.PREFERENCES) or [],
"interests": context.get(AIContextKey.INTERESTS) or [],
},
),
("个人相关记忆", context.get(AIContextKey.LOCAL_MEMORY) or []),
("当前会话历史", context.get(AIContextKey.CONVERSATION_HISTORY) or []),
]
return "\n\n".join(
f"{title}:\n{json.dumps(value, ensure_ascii=False, default=str)}"
for title, value in sections
)
def _service_root(base_url: str, suffix: str) -> str:
root = base_url.rstrip("/")
normalized_suffix = suffix.rstrip("/")
if root.endswith(normalized_suffix):
root = root[: -len(normalized_suffix)]
return root.rstrip("/")