更新agent域名

This commit is contained in:
zhanggangyong
2026-01-14 13:42:36 +00:00
parent 72ed8c1d29
commit 31ba6fb8f1
11 changed files with 1569 additions and 2403 deletions
@@ -183,6 +183,12 @@ class AgentStatusResult:
conditions: Optional[List[Dict[str, Any]]] = None
# Compatible with old fields
template: Optional[str] = None
# ========== 访问信息字段(从 access_info 解析) ==========
external_ip: Optional[str] = None # 外网 IP 地址
domain: Optional[str] = None # 域名
domain_url: Optional[str] = None # 域名访问地址
ip_url: Optional[str] = None # IP 访问地址
# ======================================================
@property
def is_healthy(self) -> bool:
@@ -1024,6 +1030,16 @@ class AgentManagerClient:
# New format: list
endpoints = endpoints_data
# ========== 解析 access_info(域名和外网IP) ==========
access_info = data.get("access_info", {})
external_ip = access_info.get("external_ip")
domain = access_info.get("domain")
domain_url = access_info.get("domain_url")
ip_url = access_info.get("ip_url")
# 优先使用 access_info 中的推荐地址,否则使用旧的 access_url
access_url = access_info.get("recommended") or data.get("access_url")
# ======================================================
return AgentStatusResult(
name=data["name"],
namespace=data["namespace"],
@@ -1035,13 +1051,18 @@ class AgentManagerClient:
node_name=data.get("node_name"),
labels=data.get("labels"),
service_port=data.get("service_port"),
access_url=data.get("access_url"),
access_url=access_url,
containers=containers,
resources=data.get("resources"),
endpoints=endpoints,
conditions=data.get("conditions"),
# Extract template from labels (for compatibility)
template=data.get("labels", {}).get("template") if data.get("labels") else None
template=data.get("labels", {}).get("template") if data.get("labels") else None,
# ========== 新增:访问信息字段 ==========
external_ip=external_ip,
domain=domain,
domain_url=domain_url,
ip_url=ip_url,
)
async def get_agent_metrics(self, agent_name: str) -> AgentMetricsResult:
+70 -21
View File
@@ -1457,7 +1457,8 @@ async def deploy_agent(
)
db.add(agent)
# 记录计费
# 记录计费(包含访问信息)
access_info = result.access_info or {}
billing_record = AgentBillingRecord(
user_id=user_id,
channel_id=channel_id,
@@ -1470,6 +1471,13 @@ async def deploy_agent(
cpu_used=quota.cpu_per_pod or "100m",
memory_used=quota.memory_per_pod or "256Mi",
replicas=req.instances,
# ========== 保存访问信息 ==========
external_ip=access_info.get("external_ip"),
domain=access_info.get("domain"),
domain_url=access_info.get("domain_url"),
access_url=access_info.get("recommended") or access_info.get("domain_url"),
service_port=result.service_port,
namespace=result.namespace,
)
db.add(billing_record)
@@ -2400,7 +2408,8 @@ async def deploy_platform_agent(
)
db.add(agent)
# 记录计费
# 记录计费(包含访问信息)
access_info = result.access_info or {}
billing_record = AgentBillingRecord(
user_id=user_id,
channel_id=channel_id,
@@ -2413,6 +2422,13 @@ async def deploy_platform_agent(
cpu_used=quota.cpu_per_pod or "100m",
memory_used=quota.memory_per_pod or "256Mi",
replicas=1,
# ========== 保存访问信息 ==========
external_ip=access_info.get("external_ip"),
domain=access_info.get("domain"),
domain_url=access_info.get("domain_url"),
access_url=access_info.get("recommended") or access_info.get("domain_url"),
service_port=result.service_port,
namespace=result.namespace,
)
db.add(billing_record)
@@ -2425,6 +2441,8 @@ async def deploy_platform_agent(
"status": result.status,
"servicePort": result.service_port,
"accessInfo": result.access_info,
"domain": access_info.get("domain"),
"domainUrl": access_info.get("domain_url"),
"quotaRemaining": quota.pod_quota - quota.pod_used,
},
message=f"平台 Agent {req.agentType} 部署成功"
@@ -2521,7 +2539,8 @@ async def use_platform_agent(
# 更新配额使用量
quota.pod_used += 1
# 记录计费
# 记录计费(包含访问信息)
access_info = result.access_info or {}
billing_record = AgentBillingRecord(
user_id=user_id,
channel_id=channel_id,
@@ -2534,6 +2553,13 @@ async def use_platform_agent(
cpu_used=quota.cpu_per_pod or "100m",
memory_used=quota.memory_per_pod or "256Mi",
replicas=1,
# ========== 保存访问信息 ==========
external_ip=access_info.get("external_ip"),
domain=access_info.get("domain"),
domain_url=access_info.get("domain_url"),
access_url=access_info.get("recommended") or access_info.get("domain_url"),
service_port=result.service_port,
namespace=result.namespace,
)
db.add(billing_record)
@@ -2546,6 +2572,8 @@ async def use_platform_agent(
"status": result.status,
"servicePort": result.service_port,
"accessInfo": result.access_info,
"domain": access_info.get("domain"),
"domainUrl": access_info.get("domain_url"),
"quotaRemaining": quota.pod_quota - quota.pod_used,
},
message=f"平台 Agent {req.agentType} 启动成功"
@@ -3162,7 +3190,8 @@ async def create_custom_agent(
quota.memory_used = memory_used + memory_request
quota.agent_count = (quota.agent_count or 0) + 1
# 记录计费
# 记录计费(包含访问信息)
access_info = result.access_info or {}
billing_record = AgentBillingRecord(
user_id=user_id,
channel_id=channel_id,
@@ -3174,6 +3203,13 @@ async def create_custom_agent(
cpu_used=req.cpuRequest,
memory_used=req.memoryRequest,
tools_used=req.tools if req.tools else [],
# ========== 保存访问信息 ==========
external_ip=access_info.get("external_ip"),
domain=access_info.get("domain"),
domain_url=access_info.get("domain_url"),
access_url=access_info.get("recommended") or access_info.get("domain_url"),
service_port=result.service_port,
namespace=result.namespace,
)
db.add(billing_record)
@@ -3203,6 +3239,8 @@ async def create_custom_agent(
"status": result.status,
"servicePort": result.service_port,
"accessInfo": result.access_info,
"domain": access_info.get("domain"),
"domainUrl": access_info.get("domain_url"),
"modelInjected": model_name is not None,
"quotaRemaining": {
"cpu": remaining_cpu - cpu_request,
@@ -3951,9 +3989,16 @@ async def get_user_agents_info(
"status": "unknown",
"healthStatus": "unknown",
"podIp": None,
"accessUrl": None,
"servicePort": None,
"namespace": "ai-agents",
# ========== 访问信息(优先使用数据库存储的值) ==========
"externalIp": record.external_ip,
"domain": record.domain,
"domainUrl": record.domain_url,
"accessUrl": record.access_url,
"servicePort": record.service_port,
"namespace": record.namespace or "ai-agents",
# ======================================================
"hostIp": None,
"nodeName": None,
"cpu": record.cpu_used,
"memory": record.memory_used,
"replicas": record.replicas,
@@ -3961,30 +4006,34 @@ async def get_user_agents_info(
"runningSeconds": int((datetime.utcnow() - record.start_time).total_seconds()) if record.start_time else 0,
}
# 从 Agent Manager 获取详细状态
# 从 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
# 端点信息
# ========== 更新访问信息(如果 Agent Manager 返回了最新值) ==========
if agent_status.external_ip:
agent_info["externalIp"] = agent_status.external_ip
if agent_status.domain:
agent_info["domain"] = agent_status.domain
if agent_status.domain_url:
agent_info["domainUrl"] = agent_status.domain_url
if agent_status.access_url:
agent_info["accessUrl"] = agent_status.access_url
if agent_status.service_port:
agent_info["servicePort"] = agent_status.service_port
if agent_status.namespace:
agent_info["namespace"] = agent_status.namespace
# ==================================================================
# 端点信息(字符串列表,如 ["http://10.244.1.100:8080"])
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
]
agent_info["endpoints"] = agent_status.endpoints
except Exception as e:
logger.warning(f"获取 Agent {record.agent_name} 状态失败: {e}")
@@ -0,0 +1,61 @@
-- Migration 016: Add Agent Access Info Fields
-- Description: 为 agent_billing_records 表添加访问信息字段(域名、外网IP等)
-- Date: 2026-01-14
-- ========== 添加访问信息字段 ==========
-- 外网 IP 地址
ALTER TABLE agent_billing_records
ADD COLUMN IF NOT EXISTS external_ip VARCHAR(45);
-- 域名
ALTER TABLE agent_billing_records
ADD COLUMN IF NOT EXISTS domain VARCHAR(255);
-- 域名访问地址
ALTER TABLE agent_billing_records
ADD COLUMN IF NOT EXISTS domain_url VARCHAR(500);
-- 推荐访问地址
ALTER TABLE agent_billing_records
ADD COLUMN IF NOT EXISTS access_url VARCHAR(500);
-- 服务端口
ALTER TABLE agent_billing_records
ADD COLUMN IF NOT EXISTS service_port INTEGER;
-- K8s 命名空间
ALTER TABLE agent_billing_records
ADD COLUMN IF NOT EXISTS namespace VARCHAR(100);
-- ========== 添加索引 ==========
-- 按域名查询索引(用于根据域名查找 Agent)
CREATE INDEX IF NOT EXISTS idx_agent_billing_domain
ON agent_billing_records(domain);
-- ========== 添加注释 ==========
COMMENT ON COLUMN agent_billing_records.external_ip IS '外网 IP 地址(Agent Manager 分配)';
COMMENT ON COLUMN agent_billing_records.domain IS '域名(如 my-agent.taijiagent.com)';
COMMENT ON COLUMN agent_billing_records.domain_url IS '域名访问地址(如 http://my-agent.taijiagent.com)';
COMMENT ON COLUMN agent_billing_records.access_url IS '推荐访问地址(域名优先)';
COMMENT ON COLUMN agent_billing_records.service_port IS '服务端口';
COMMENT ON COLUMN agent_billing_records.namespace IS 'Kubernetes 命名空间';
-- ========== 验证迁移 ==========
-- 检查字段是否添加成功
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'agent_billing_records'
AND column_name = 'domain'
) THEN
RAISE NOTICE 'Migration 016 completed successfully: domain field added';
ELSE
RAISE EXCEPTION 'Migration 016 failed: domain field not found';
END IF;
END $$;
@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""
Migration 016: Add Agent Access Info Fields
为 agent_billing_records 表添加访问信息字段(域名、外网IP等)
Usage:
python migrations/run_016_migration.py
"""
import asyncio
import os
import sys
# 添加项目根目录到 Python 路径
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sqlalchemy import text
from database import engine
async def run_migration():
"""运行迁移"""
print("=" * 60)
print("Migration 016: Add Agent Access Info Fields")
print("=" * 60)
# 读取 SQL 文件
sql_file = os.path.join(os.path.dirname(__file__), "016_add_agent_access_info_fields.sql")
with open(sql_file, "r", encoding="utf-8") as f:
sql_content = f.read()
# 分割 SQL 语句
statements = []
current_stmt = []
in_do_block = False
for line in sql_content.split("\n"):
line_stripped = line.strip()
# 跳过注释和空行(但保留 DO 块中的内容)
if not in_do_block:
if line_stripped.startswith("--") or not line_stripped:
continue
# 检测 DO 块开始
if line_stripped.upper().startswith("DO $$"):
in_do_block = True
current_stmt.append(line)
continue
# 检测 DO 块结束
if in_do_block and line_stripped == "END $$;":
current_stmt.append(line)
statements.append("\n".join(current_stmt))
current_stmt = []
in_do_block = False
continue
if in_do_block:
current_stmt.append(line)
continue
# 普通语句处理
current_stmt.append(line)
if line_stripped.endswith(";"):
stmt = "\n".join(current_stmt)
if stmt.strip():
statements.append(stmt)
current_stmt = []
# 执行迁移
async with engine.begin() as conn:
for i, stmt in enumerate(statements, 1):
try:
# 打印语句摘要
stmt_preview = stmt.strip()[:80].replace("\n", " ")
if len(stmt.strip()) > 80:
stmt_preview += "..."
print(f"\n[{i}/{len(statements)}] Executing: {stmt_preview}")
await conn.execute(text(stmt))
print(f" ✓ Success")
except Exception as e:
error_msg = str(e)
# 忽略 "column already exists" 错误
if "already exists" in error_msg.lower():
print(f" ⚠ Skipped (already exists)")
else:
print(f" ✗ Error: {error_msg}")
raise
print("\n" + "=" * 60)
print("Migration 016 completed successfully!")
print("=" * 60)
async def verify_migration():
"""验证迁移结果"""
print("\nVerifying migration...")
async with engine.begin() as conn:
# 检查新字段是否存在
result = await conn.execute(text("""
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_name = 'agent_billing_records'
AND column_name IN ('external_ip', 'domain', 'domain_url', 'access_url', 'service_port', 'namespace')
ORDER BY column_name
"""))
columns = result.fetchall()
print(f"\nNew columns in agent_billing_records table:")
print("-" * 50)
for col in columns:
col_name, data_type, max_len = col
type_info = f"{data_type}({max_len})" if max_len else data_type
print(f" ✓ {col_name}: {type_info}")
expected_columns = {'external_ip', 'domain', 'domain_url', 'access_url', 'service_port', 'namespace'}
found_columns = {col[0] for col in columns}
if found_columns == expected_columns:
print(f"\n✓ All {len(expected_columns)} columns verified successfully!")
else:
missing = expected_columns - found_columns
if missing:
print(f"\n✗ Missing columns: {missing}")
return False
return True
if __name__ == "__main__":
try:
asyncio.run(run_migration())
asyncio.run(verify_migration())
except KeyboardInterrupt:
print("\nMigration cancelled.")
sys.exit(1)
except Exception as e:
print(f"\nMigration failed: {e}")
sys.exit(1)
+10
View File
@@ -1198,6 +1198,15 @@ class AgentBillingRecord(BaseModel, Base):
tools_used = Column(JSON, nullable=True) # 使用的工具列表
request_id = Column(String(100), nullable=True) # 请求 ID
# ========== 访问信息字段(Agent Manager 返回) ==========
external_ip = Column(String(45), nullable=True) # 外网 IP 地址
domain = Column(String(255), nullable=True) # 域名
domain_url = Column(String(500), nullable=True) # 域名访问地址
access_url = Column(String(500), nullable=True) # 推荐访问地址
service_port = Column(Integer, nullable=True) # 服务端口
namespace = Column(String(100), nullable=True) # K8s 命名空间
# ======================================================
# 关联关系
user = relationship("User")
channel = relationship("Channel")
@@ -1207,6 +1216,7 @@ class AgentBillingRecord(BaseModel, Base):
Index("idx_agent_billing_channel", channel_id),
Index("idx_agent_billing_period", period_start, period_end),
Index("idx_agent_billing_type", agent_type),
Index("idx_agent_billing_domain", domain), # 按域名查询索引
)