Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions backend/app/api/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,8 @@ def get_simulation_config_realtime(simulation_id: str):
is_generating = False
generation_stage = None
config_generated = False

state_data = None

state_file = os.path.join(sim_dir, "state.json")
if os.path.exists(state_file):
try:
Expand All @@ -1205,9 +1206,11 @@ def get_simulation_config_realtime(simulation_id: str):
status = state_data.get("status", "")
is_generating = status == "preparing"
config_generated = state_data.get("config_generated", False)

# 判断当前阶段
if is_generating:
if status == "failed":
generation_stage = "failed"
elif is_generating:
if state_data.get("profiles_generated", False):
generation_stage = "generating_config"
else:
Expand All @@ -1217,6 +1220,12 @@ def get_simulation_config_realtime(simulation_id: str):
except Exception:
pass

# 如果状态为失败,提取错误信息
failed = generation_stage == "failed"
error_message = None
if failed and state_data:
error_message = state_data.get("error", None)

# 构建返回数据
response_data = {
"simulation_id": simulation_id,
Expand All @@ -1225,7 +1234,9 @@ def get_simulation_config_realtime(simulation_id: str):
"is_generating": is_generating,
"generation_stage": generation_stage,
"config_generated": config_generated,
"config": config
"config": config,
"failed": failed,
"error": error_message
}

# 如果配置存在,提取一些关键统计信息
Expand Down
24 changes: 22 additions & 2 deletions frontend/src/components/Step2EnvSetup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,11 @@ const fetchProfilesRealtime = async () => {
}

// 配置轮询
let configErrorCount = 0
const MAX_CONFIG_ERRORS = 10

const startConfigPolling = () => {
configErrorCount = 0
configTimer = setInterval(fetchConfigRealtime, 2000)
}

Expand All @@ -972,7 +976,17 @@ const fetchConfigRealtime = async () => {

if (res.success && res.data) {
const data = res.data

configErrorCount = 0

// 检测是否失败
if (data.failed || data.generation_stage === 'failed') {
const errorMsg = data.error || t('common.unknownError')
addLog(`❌ ${t('log.configGenerationFailed') || '配置生成失败'}: ${errorMsg}`)
stopConfigPolling()
emit('update-status', 'failed')
return
}

// 输出配置生成阶段日志(避免重复)
if (data.generation_stage && data.generation_stage !== lastLoggedConfigStage) {
lastLoggedConfigStage = data.generation_stage
Expand All @@ -982,7 +996,7 @@ const fetchConfigRealtime = async () => {
addLog(t('log.generatingLLMConfig'))
}
}

// 如果配置已生成
if (data.config_generated && data.config) {
simulationConfig.value = data.config
Expand Down Expand Up @@ -1017,6 +1031,12 @@ const fetchConfigRealtime = async () => {
}
} catch (err) {
console.warn('获取 Config 失败:', err)
configErrorCount++
if (configErrorCount >= MAX_CONFIG_ERRORS) {
addLog(`❌ ${t('log.configGenerationFailed') || '配置生成失败'}: ${t('log.pollErrorLimit') || '连续多次获取状态失败,已停止轮询'}`)
stopConfigPolling()
emit('update-status', 'failed')
}
}
}

Expand Down
24 changes: 21 additions & 3 deletions frontend/src/components/Step3Simulation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,11 @@ const handleStopSimulation = async () => {
// 轮询状态
let statusTimer = null
let detailTimer = null
let statusErrorCount = 0
const MAX_STATUS_ERRORS = 10

const startStatusPolling = () => {
statusErrorCount = 0
statusTimer = setInterval(fetchRunStatus, 2000)
}

Expand Down Expand Up @@ -497,7 +500,7 @@ const fetchRunStatus = async () => {

if (res.success && res.data) {
const data = res.data

statusErrorCount = 0
runStatus.value = data

// 分别检测各平台的轮次变化并输出日志
Expand All @@ -511,13 +514,22 @@ const fetchRunStatus = async () => {
prevRedditRound.value = data.reddit_current_round
}

// 检测模拟是否失败
if (data.runner_status === 'failed') {
const errorMsg = data.error || t('common.unknownError')
addLog(`❌ ${t('log.simFailed') || '模拟运行失败'}: ${errorMsg}`)
stopPolling()
emit('update-status', 'failed')
return
}

// 检测模拟是否已完成(通过 runner_status 或平台完成状态判断)
const isCompleted = data.runner_status === 'completed' || data.runner_status === 'stopped'

// 额外检查:如果后端还没来得及更新 runner_status,但平台已经报告完成
// 通过检测 twitter_completed 和 reddit_completed 状态判断
const platformsCompleted = checkPlatformsCompleted(data)

if (isCompleted || platformsCompleted) {
if (platformsCompleted && !isCompleted) {
addLog(t('log.allPlatformsCompleted'))
Expand All @@ -530,6 +542,12 @@ const fetchRunStatus = async () => {
}
} catch (err) {
console.warn('获取运行状态失败:', err)
statusErrorCount++
if (statusErrorCount >= MAX_STATUS_ERRORS) {
addLog(`❌ ${t('log.simFailed') || '模拟运行失败'}: ${t('log.pollErrorLimit') || '连续多次获取状态失败,已停止轮询'}`)
stopPolling()
emit('update-status', 'failed')
}
}
}

Expand Down