更新信息查询

This commit is contained in:
zhanggangyong
2026-01-13 13:36:41 +00:00
parent 1b079ed870
commit 8f86d22f00
5 changed files with 496 additions and 36 deletions
-1
View File
@@ -1 +0,0 @@
{"detail":"Not authenticated"}
@@ -0,0 +1,337 @@
# 用户资源信息查询接口文档
> **版本**: 2026-01-13 v1
> **接口路径**: `GET /api/user/resources/info`
> **认证方式**: Bearer Token(JWT)
---
## 📋 接口概述
| 项目 | 值 |
|------|-----|
| **接口路径** | `GET /api/user/resources/info` |
| **接口描述** | 获取用户的 LiteLLM 密钥和已部署 Agent 的详细信息 |
| **认证方式** | Bearer Token(JWT) |
| **Content-Type** | application/json |
### 功能说明
该接口用于获取当前用户的以下资源信息:
1. **LiteLLM 密钥**:解密后的完整 API Key,可直接用于调用 AI 模型
2. **平台 Agent**:已部署的平台 Agent 列表,包含实时 IP 和访问地址
3. **自定义 Agent**:已部署的自定义 Agent 列表,包含实时 IP 和访问地址
---
## 📥 请求参数
### 请求头(Headers)
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|:----:|------|
| `Authorization` | string | ✅ | Bearer Token,格式:`Bearer <JWT Token>` |
### 请求体(Body)
无
### 查询参数(Query)
无
---
## 📤 请求示例
```http
GET /api/user/resources/info HTTP/1.1
Host: api.taiji-ai.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
```
---
## 📊 响应参数
### 响应结构
| 字段 | 类型 | 说明 |
|------|------|------|
| `success` | boolean | 请求是否成功 |
| `message` | string | 响应消息 |
| `data` | object | 响应数据 |
### data 对象
| 字段 | 类型 | 说明 |
|------|------|------|
| `litellmKeys` | array | LiteLLM 密钥列表 |
| `litellmApiBase` | string | LiteLLM 网关地址(全局) |
| `platformAgents` | array | 已部署的平台 Agent 列表 |
| `customAgents` | array | 已部署的自定义 Agent 列表 |
| `summary` | object | 汇总统计信息 |
---
### litellmKeys 数组元素
| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `modelName` | string | 模型名称 | `"azure/gpt-4"` |
| `apiKey` | string | **解密后的完整 API Key**(用于调用模型) | `"sk-xxxxxxxxxxxxxxxx"` |
| `apiBase` | string | LiteLLM 网关地址 | `"https://litellm.xxx.azurecontainerapps.io"` |
| `rpmLimit` | integer | 每分钟请求数限制 | `60` |
| `tpmLimit` | integer | 每分钟 Token 数限制 | `10000` |
| `maxBudget` | number \| null | 最大预算(美元) | `100.0` |
| `budgetDuration` | string | 预算周期 | `"monthly"` |
| `status` | string | 密钥状态 | `"active"` |
| `createdAt` | string | 创建时间(ISO 8601) | `"2026-01-13T10:00:00"` |
| `error` | string | 错误信息(仅解密失败时返回) | `"密钥解密失败"` |
---
### platformAgents / customAgents 数组元素
| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `name` | string | Agent 实例名称 | `"user-123-echo-agent-abc"` |
| `template` | string | Agent 类型/模板 | `"echo_agent"` |
| `templateName` | string | 模板名称 | `"echo_agent"` |
| `status` | string | 运行状态(AKS 实时) | `"Running"` / `"Pending"` / `"Failed"` |
| `healthStatus` | string | 健康状态(AKS 实时) | `"healthy"` / `"unhealthy"` / `"unknown"` |
| `podIp` | string \| null | **Pod IP 地址**(AKS 实时) | `"10.244.1.100"` |
| `accessUrl` | string \| null | **访问 URL**(AKS 实时) | `"http://xxx.ai-agents.svc.cluster.local:8080"` |
| `servicePort` | integer \| null | 服务端口 | `8080` |
| `namespace` | string | K8s 命名空间 | `"ai-agents"` |
| `hostIp` | string \| null | 宿主机 IP(AKS 实时) | `"192.168.1.10"` |
| `nodeName` | string \| null | 节点名称(AKS 实时) | `"aks-agentpool-xxxxx"` |
| `cpu` | string | CPU 配置 | `"100m"` |
| `memory` | string | 内存配置 | `"256Mi"` |
| `replicas` | integer | 副本数量 | `1` |
| `startTime` | string | 启动时间(ISO 8601) | `"2026-01-13T08:00:00"` |
| `runningSeconds` | integer | 已运行秒数 | `7200` |
| `endpoints` | array | 端点列表(可选) | 见下表 |
### endpoints 数组元素(可选)
| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `name` | string | 端点名称 | `"http"` |
| `port` | integer | 端口号 | `8080` |
| `protocol` | string | 协议 | `"TCP"` |
| `targetPort` | integer | 目标端口 | `8080` |
---
### summary 对象
| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `totalLitellmKeys` | integer | LiteLLM 密钥总数 | `2` |
| `totalPlatformAgents` | integer | 已部署的平台 Agent 数量 | `1` |
| `totalCustomAgents` | integer | 已部署的自定义 Agent 数量 | `1` |
---
## 📝 响应示例
### 成功响应
```json
{
"success": true,
"message": "用户资源信息获取成功",
"data": {
"litellmKeys": [
{
"modelName": "azure/gpt-4",
"apiKey": "sk-1234567890abcdef1234567890abcdef",
"apiBase": "https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io",
"rpmLimit": 60,
"tpmLimit": 10000,
"maxBudget": 100.0,
"budgetDuration": "monthly",
"status": "active",
"createdAt": "2026-01-10T10:30:00"
},
{
"modelName": "azure/gpt-3.5-turbo",
"apiKey": "sk-abcdef1234567890abcdef1234567890",
"apiBase": "https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io",
"rpmLimit": 120,
"tpmLimit": 50000,
"maxBudget": 50.0,
"budgetDuration": "monthly",
"status": "active",
"createdAt": "2026-01-10T10:30:00"
}
],
"litellmApiBase": "https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io",
"platformAgents": [
{
"name": "user-abc123-echo-agent-def456",
"template": "echo_agent",
"templateName": "echo_agent",
"status": "Running",
"healthStatus": "healthy",
"podIp": "10.244.1.100",
"accessUrl": "http://user-abc123-echo-agent-def456.ai-agents.svc.cluster.local:8080",
"servicePort": 8080,
"namespace": "ai-agents",
"hostIp": "192.168.1.10",
"nodeName": "aks-agentpool-12345678-vmss000001",
"cpu": "100m",
"memory": "256Mi",
"replicas": 1,
"startTime": "2026-01-13T08:00:00",
"runningSeconds": 7200,
"endpoints": [
{
"name": "http",
"port": 8080,
"protocol": "TCP",
"targetPort": 8080
}
]
}
],
"customAgents": [
{
"name": "user-abc123-mysql-agent-xyz789",
"template": "mysql_agent",
"templateName": "mysql_agent",
"status": "Running",
"healthStatus": "healthy",
"podIp": "10.244.1.101",
"accessUrl": "http://user-abc123-mysql-agent-xyz789.ai-agents.svc.cluster.local:8080",
"servicePort": 8080,
"namespace": "ai-agents",
"hostIp": "192.168.1.11",
"nodeName": "aks-agentpool-12345678-vmss000002",
"cpu": "200m",
"memory": "512Mi",
"replicas": 1,
"startTime": "2026-01-13T09:30:00",
"runningSeconds": 1800,
"endpoints": [
{
"name": "http",
"port": 8080,
"protocol": "TCP",
"targetPort": 8080
}
]
}
],
"summary": {
"totalLitellmKeys": 2,
"totalPlatformAgents": 1,
"totalCustomAgents": 1
}
}
}
```
### 无资源时的响应
```json
{
"success": true,
"message": "用户资源信息获取成功",
"data": {
"litellmKeys": [],
"litellmApiBase": "https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io",
"platformAgents": [],
"customAgents": [],
"summary": {
"totalLitellmKeys": 0,
"totalPlatformAgents": 0,
"totalCustomAgents": 0
}
}
}
```
### 认证失败响应
**HTTP Status Code**: `401 Unauthorized`
```json
{
"detail": "Not authenticated"
}
```
---
## 🔧 使用说明
### LiteLLM 密钥使用方式
获取到的 `apiKey` 可以直接用于调用 AI 模型(OpenAI 兼容格式):
```python
import openai
# 从接口返回的数据中获取
api_key = response["data"]["litellmKeys"][0]["apiKey"]
api_base = response["data"]["litellmKeys"][0]["apiBase"]
client = openai.OpenAI(
api_key=api_key,
base_url=api_base
)
response = client.chat.completions.create(
model="azure/gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
)
```
### Agent 访问方式
通过 `accessUrl` 可以在集群内部访问 Agent 服务:
```bash
# 在集群内部调用 Agent
curl http://user-abc123-echo-agent-def456.ai-agents.svc.cluster.local:8080/api/chat
```
通过 `podIp` 和 `servicePort` 也可以直接访问:
```bash
# 直接通过 Pod IP 访问
curl http://10.244.1.100:8080/api/chat
```
---
## 📌 数据来源说明
| 数据类型 | 来源 | 实时性 |
|---------|------|--------|
| LiteLLM 密钥 | PostgreSQL 数据库 | 静态(创建时存储) |
| Agent 基本信息(名称、模板、CPU/内存) | PostgreSQL 数据库 | 静态(创建时存储) |
| Agent 状态(status, healthStatus) | AKS 集群(通过 Agent Manager) | **实时查询** |
| Agent 网络信息(podIp, accessUrl) | AKS 集群(通过 Agent Manager) | **实时查询** |
### 调用链路
```
前端 → mcp-server → Agent Manager → AKS (Kubernetes API)
↓
查询 Pod/Service 真实状态
```
---
## ⚠️ 注意事项
1. **密钥安全**:返回的 `apiKey` 是完整的解密密钥,请妥善保管,不要泄露
2. **实时性**:Agent 的 `podIp`、`status` 等信息是实时从 AKS 查询的,可能有轻微延迟
3. **服务可用性**:如果 Agent Manager 服务不可用,Agent 的实时信息将显示为 `null` 或 `unknown`
4. **Pod IP 变化**:Pod 重启后 IP 地址会改变,建议使用 `accessUrl`(Service DNS)访问
-12
View File
@@ -38,18 +38,6 @@ scrape_configs:
scrape_interval: 10s
scrape_timeout: 5s
# LiteLLM Gateway (如果有metrics端点)
- job_name: 'litellm-gateway'
static_configs:
- targets: ['litellm-gateway:4000']
labels:
service: 'litellm-gateway'
component: 'gateway'
metrics_path: '/metrics'
scrape_interval: 10s
scrape_timeout: 5s
# 如果端点不存在,Prometheus会记录错误但不会影响其他服务
# PostgreSQL (需要postgres_exporter,当前未部署)
# - job_name: 'postgres'
# static_configs:
-23
View File
@@ -20,28 +20,6 @@ services:
- taiji-network
restart: unless-stopped
# LiteLLM网关服务
litellm-gateway:
build:
context: ./services/model-gateway
dockerfile: Dockerfile
container_name: taiji-litellm-gateway
ports:
- "4000:4000"
environment:
- LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
# OpenRouter 配置
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
- OPENROUTER_BASE_URL=${OPENROUTER_BASE_URL}
volumes:
- ./services/model-gateway/config:/app/config
- ./logs:/app/logs
networks:
- taiji-network
restart: unless-stopped
# 数据接入服务 (Python)
data-ingestion:
build:
@@ -101,7 +79,6 @@ services:
- ./logs:/app/logs
depends_on:
- nats
- litellm-gateway
networks:
- taiji-network
restart: unless-stopped
+159
View File
@@ -3820,3 +3820,162 @@ async def update_user_profile(
"company": getattr(user, "company", None),
}
)
# ============= 用户资源信息综合查询 =============
@router.get("/resources/info", response_model=SuccessResponse)
async def get_user_resources_info(
principal: dict = Depends(require_auth),
db: AsyncSession = Depends(get_db)
):
"""
获取用户的资源信息综合查询
返回:
1. LiteLLM 密钥列表(解密后的完整密钥)
2. 已部署的平台 Agent 列表(包含 IP 地址和访问信息)
3. 已部署的自定义 Agent 列表(包含 IP 地址和访问信息)
注意:
- LiteLLM 密钥用于调用 AI 模型(OpenAI 兼容格式)
- Agent 信息包含实时状态和访问地址
"""
from app.litellm_client import get_litellm_client
from config import settings
user_id = principal.get("user_id")
# ========== 1. 获取 LiteLLM 密钥 ==========
litellm_keys = []
try:
# 查询用户的模型密钥
result = await db.execute(
select(TenantModelKey).where(
and_(
TenantModelKey.tenant_id == user_id,
TenantModelKey.status == "active"
)
)
)
tenant_keys = result.scalars().all()
litellm_client = get_litellm_client()
for key in tenant_keys:
try:
# 解密密钥
decrypted_key = litellm_client.decrypt_key(key.litellm_key_hash)
litellm_keys.append({
"modelName": key.model_name,
"apiKey": decrypted_key,
"apiBase": settings.litellm_url,
"rpmLimit": key.rpm_limit,
"tpmLimit": key.tpm_limit,
"maxBudget": float(key.max_budget) if key.max_budget else None,
"budgetDuration": key.budget_duration,
"status": key.status,
"createdAt": key.created_at.isoformat() if key.created_at else None,
})
except Exception as e:
logger.warning(f"解密 LiteLLM 密钥失败: {e}")
litellm_keys.append({
"modelName": key.model_name,
"apiKey": None,
"apiBase": settings.litellm_url,
"error": "密钥解密失败",
"status": key.status,
})
except Exception as e:
logger.error(f"查询 LiteLLM 密钥失败: {e}")
# ========== 2. 获取已部署的 Agent ==========
platform_agents = []
custom_agents = []
# 查询用户的活跃 Agent(未停止的)
result = await db.execute(
select(AgentBillingRecord).where(
and_(
AgentBillingRecord.user_id == user_id,
AgentBillingRecord.end_time == None # 正在运行
)
)
)
billing_records = result.scalars().all()
# 尝试获取 Agent Manager 客户端
agent_manager_available = False
client = None
try:
from app.agent_manager_client import get_agent_manager_client, AgentManagerError
client = get_agent_manager_client()
agent_manager_available = True
except Exception as e:
logger.warning(f"Agent Manager 客户端不可用: {e}")
for record in billing_records:
agent_info = {
"name": record.agent_name,
"template": record.agent_type,
"templateName": record.template_name,
"status": "unknown",
"healthStatus": "unknown",
"podIp": None,
"accessUrl": None,
"servicePort": None,
"namespace": "ai-agents",
"cpu": record.cpu_used,
"memory": record.memory_used,
"replicas": record.replicas,
"startTime": record.start_time.isoformat() if record.start_time else None,
"runningSeconds": int((datetime.utcnow() - record.start_time).total_seconds()) if record.start_time else 0,
}
# 从 Agent Manager 获取详细状态
if agent_manager_available and client:
try:
agent_status = await client.get_agent_status(record.agent_name)
agent_info["status"] = agent_status.status
agent_info["healthStatus"] = agent_status.health_status
agent_info["podIp"] = agent_status.pod_ip
agent_info["accessUrl"] = agent_status.access_url
agent_info["servicePort"] = agent_status.service_port
agent_info["namespace"] = agent_status.namespace
agent_info["hostIp"] = agent_status.host_ip
agent_info["nodeName"] = agent_status.node_name
# 端点信息
if agent_status.endpoints:
agent_info["endpoints"] = [
{
"name": ep.name,
"port": ep.port,
"protocol": ep.protocol,
"targetPort": ep.target_port,
}
for ep in agent_status.endpoints
]
except Exception as e:
logger.warning(f"获取 Agent {record.agent_name} 状态失败: {e}")
# 分类存储
if record.is_platform_agent:
platform_agents.append(agent_info)
else:
custom_agents.append(agent_info)
return SuccessResponse(
data={
"litellmKeys": litellm_keys,
"litellmApiBase": settings.litellm_url,
"platformAgents": platform_agents,
"customAgents": custom_agents,
"summary": {
"totalLitellmKeys": len(litellm_keys),
"totalPlatformAgents": len(platform_agents),
"totalCustomAgents": len(custom_agents),
}
},
message="用户资源信息获取成功"
)