mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-05 08:55:28 +08:00
feat: Enhance API testing to allow sending actual chat messages to specific accounts and display AI replies.
This commit is contained in:
@@ -287,8 +287,12 @@ async def validate_all_accounts(_: bool = Depends(verify_admin)):
|
||||
# ----------------------------------------------------------------------
|
||||
# 账号 API 测试(实际发送请求)
|
||||
# ----------------------------------------------------------------------
|
||||
async def test_account_api(account: dict, model: str = "deepseek-chat") -> dict:
|
||||
"""测试单个账号的 API 调用能力"""
|
||||
async def test_account_api(account: dict, model: str = "deepseek-chat", message: str = "") -> dict:
|
||||
"""测试单个账号的 API 调用能力
|
||||
|
||||
如果提供了 message,会发送实际请求并返回 AI 回复;
|
||||
否则只测试创建会话(快速测试)。
|
||||
"""
|
||||
from curl_cffi import requests as cffi_requests
|
||||
from core.deepseek import DEEPSEEK_CREATE_SESSION_URL, DEEPSEEK_CHAT_COMPLETION_URL, BASE_HEADERS
|
||||
from core.pow import get_pow_response_direct
|
||||
@@ -300,6 +304,7 @@ async def test_account_api(account: dict, model: str = "deepseek-chat") -> dict:
|
||||
"response_time": 0,
|
||||
"message": "",
|
||||
"model": model,
|
||||
"reply": "", # AI 回复内容
|
||||
}
|
||||
|
||||
import time
|
||||
@@ -339,9 +344,70 @@ async def test_account_api(account: dict, model: str = "deepseek-chat") -> dict:
|
||||
return result
|
||||
|
||||
session_id = session_data["data"]["biz_data"]["id"]
|
||||
|
||||
# 如果没有消息,只测试会话创建
|
||||
if not message:
|
||||
result["success"] = True
|
||||
result["message"] = "API 测试成功"
|
||||
result["response_time"] = round((time.time() - start_time) * 1000)
|
||||
return result
|
||||
|
||||
# 2. 发送实际请求
|
||||
from core.pow import get_pow_response_direct
|
||||
pow_resp = get_pow_response_direct(headers)
|
||||
if not pow_resp:
|
||||
result["message"] = "获取 PoW 失败"
|
||||
return result
|
||||
|
||||
chat_headers = {**headers, "x-ds-pow-response": pow_resp}
|
||||
thinking_enabled = "reasoner" in model.lower()
|
||||
search_enabled = "search" in model.lower()
|
||||
|
||||
payload = {
|
||||
"chat_session_id": session_id,
|
||||
"parent_message_id": None,
|
||||
"prompt": message,
|
||||
"ref_file_ids": [],
|
||||
"thinking_enabled": thinking_enabled,
|
||||
"search_enabled": search_enabled,
|
||||
}
|
||||
|
||||
chat_resp = cffi_requests.post(
|
||||
DEEPSEEK_CHAT_COMPLETION_URL,
|
||||
headers=chat_headers,
|
||||
json=payload,
|
||||
impersonate="safari15_3",
|
||||
timeout=60,
|
||||
stream=True,
|
||||
)
|
||||
|
||||
if chat_resp.status_code != 200:
|
||||
result["message"] = f"对话请求失败: HTTP {chat_resp.status_code}"
|
||||
return result
|
||||
|
||||
# 解析流式响应获取回复
|
||||
reply_text = ""
|
||||
for line in chat_resp.iter_lines():
|
||||
try:
|
||||
line_str = line.decode("utf-8")
|
||||
if line_str.startswith("data:"):
|
||||
data_str = line_str[5:].strip()
|
||||
if data_str == "[DONE]":
|
||||
break
|
||||
import json
|
||||
chunk = json.loads(data_str)
|
||||
choices = chunk.get("choices", [])
|
||||
if choices:
|
||||
delta = choices[0].get("delta", {})
|
||||
if delta.get("type") == "text":
|
||||
reply_text += delta.get("content", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
result["success"] = True
|
||||
result["message"] = "API 测试成功"
|
||||
result["response_time"] = round((time.time() - start_time) * 1000) # ms
|
||||
result["reply"] = reply_text[:500] # 限制长度
|
||||
result["response_time"] = round((time.time() - start_time) * 1000)
|
||||
|
||||
except Exception as e:
|
||||
result["message"] = f"测试失败: {str(e)}"
|
||||
@@ -351,10 +417,15 @@ async def test_account_api(account: dict, model: str = "deepseek-chat") -> dict:
|
||||
|
||||
@router.post("/accounts/test")
|
||||
async def test_single_account(request: Request, _: bool = Depends(verify_admin)):
|
||||
"""测试单个账号的 API 调用"""
|
||||
"""测试单个账号的 API 调用
|
||||
|
||||
如果提供 message,会发送实际请求并返回 AI 回复;
|
||||
否则只快速测试创建会话。
|
||||
"""
|
||||
data = await request.json()
|
||||
identifier = data.get("identifier", "")
|
||||
model = data.get("model", "deepseek-chat")
|
||||
message = data.get("message", "")
|
||||
|
||||
if not identifier:
|
||||
raise HTTPException(status_code=400, detail="需要账号标识(email 或 mobile)")
|
||||
@@ -369,7 +440,7 @@ async def test_single_account(request: Request, _: bool = Depends(verify_admin))
|
||||
if not account:
|
||||
raise HTTPException(status_code=404, detail="账号不存在")
|
||||
|
||||
result = await test_account_api(account, model)
|
||||
result = await test_account_api(account, model, message)
|
||||
|
||||
# 保存可能更新的 token
|
||||
save_config(CONFIG)
|
||||
|
||||
@@ -11,9 +11,13 @@ export default function ApiTester({ config, onMessage }) {
|
||||
const [model, setModel] = useState('deepseek-chat')
|
||||
const [message, setMessage] = useState('你好,请用一句话介绍你自己。')
|
||||
const [apiKey, setApiKey] = useState('')
|
||||
const [selectedAccount, setSelectedAccount] = useState('') // 空为随机
|
||||
const [response, setResponse] = useState(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
// 获取账号列表
|
||||
const accounts = config.accounts || []
|
||||
|
||||
const testApi = async () => {
|
||||
setLoading(true)
|
||||
setResponse(null)
|
||||
@@ -84,6 +88,48 @@ export default function ApiTester({ config, onMessage }) {
|
||||
}
|
||||
}
|
||||
|
||||
// 智能测试:根据是否选择账号决定测试方式
|
||||
const sendTest = async () => {
|
||||
setLoading(true)
|
||||
setResponse(null)
|
||||
|
||||
// 如果选择了指定账号,使用账号测试接口
|
||||
if (selectedAccount) {
|
||||
try {
|
||||
const res = await fetch('/admin/accounts/test', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
identifier: selectedAccount,
|
||||
model,
|
||||
message,
|
||||
}),
|
||||
})
|
||||
const data = await res.json()
|
||||
setResponse({
|
||||
success: data.success,
|
||||
status_code: res.status,
|
||||
response: data,
|
||||
account: selectedAccount,
|
||||
})
|
||||
if (data.success) {
|
||||
onMessage('success', `${selectedAccount}: 测试成功 (${data.response_time}ms)`)
|
||||
} else {
|
||||
onMessage('error', `${selectedAccount}: ${data.message}`)
|
||||
}
|
||||
} catch (e) {
|
||||
onMessage('error', '网络错误: ' + e.message)
|
||||
setResponse({ error: e.message })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 随机账号:使用标准 API
|
||||
directTest()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="section">
|
||||
<div className="card">
|
||||
@@ -102,6 +148,21 @@ export default function ApiTester({ config, onMessage }) {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label">账号(指定测试哪个账号)</label>
|
||||
<select
|
||||
className="form-input"
|
||||
value={selectedAccount}
|
||||
onChange={e => setSelectedAccount(e.target.value)}
|
||||
>
|
||||
<option value="">🎲 随机选择</option>
|
||||
{accounts.map((acc, i) => {
|
||||
const id = acc.email || acc.mobile
|
||||
return <option key={i} value={id}>{id} {acc.has_token ? '✅' : '⚠️'}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label">API Key(留空使用第一个配置的 Key)</label>
|
||||
<input
|
||||
@@ -124,8 +185,9 @@ export default function ApiTester({ config, onMessage }) {
|
||||
</div>
|
||||
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-primary" onClick={directTest} disabled={loading}>
|
||||
{loading ? <span className="loading"></span> : '🚀 发送请求'}
|
||||
<button className="btn btn-primary" onClick={sendTest} disabled={loading}>
|
||||
{loading ? <span className="loading"></span> :
|
||||
selectedAccount ? `🚀 使用 ${selectedAccount} 发送` : '🚀 发送请求'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -155,6 +217,21 @@ export default function ApiTester({ config, onMessage }) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 指定账号测试的回复 */}
|
||||
{response.success && response.response?.reply && (
|
||||
<div style={{ marginTop: '1rem' }}>
|
||||
<div className="form-label">AI 回复 ({response.account}):</div>
|
||||
<div style={{
|
||||
padding: '1rem',
|
||||
background: 'var(--bg-tertiary)',
|
||||
borderRadius: 'var(--radius)',
|
||||
whiteSpace: 'pre-wrap'
|
||||
}}>
|
||||
{response.response.reply}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user