V3.3.1 修复:WBS 解析 'Extra data' 报错 + 开工接口空 body 容错
- parse_wbs 改用 raw_decode 提取第一个完整 JSON,忽略尾部杂质/多余 JSON(原按首{到尾}切片,模型输出带第二段JSON或解释文字时解析失败)
- 兼容 subtasks/task_list/items/task 等任务键名
- /autostart 接口 get_json 改 silent 容错(无 body 时不再 400)
This commit is contained in:
@@ -1076,7 +1076,7 @@ def project_autostart(pid):
|
|||||||
return jsonify({'ok': False, 'error': '请先在编辑项目中设置 AI 主管 Worker'}), 400
|
return jsonify({'ok': False, 'error': '请先在编辑项目中设置 AI 主管 Worker'}), 400
|
||||||
if not db.q('SELECT 1 FROM project_team_workers WHERE project_id=?', (pid,)):
|
if not db.q('SELECT 1 FROM project_team_workers WHERE project_id=?', (pid,)):
|
||||||
return jsonify({'ok': False, 'error': '请先在编辑项目中勾选干活团队 Worker(≥1 个)'}), 400
|
return jsonify({'ok': False, 'error': '请先在编辑项目中勾选干活团队 Worker(≥1 个)'}), 400
|
||||||
d = request.get_json(force=True) or {}
|
d = request.get_json(silent=True) or {}
|
||||||
ok = autostart.launch(pid, review_required=1 if d.get('auto_review') else 0)
|
ok = autostart.launch(pid, review_required=1 if d.get('auto_review') else 0)
|
||||||
if not ok:
|
if not ok:
|
||||||
return jsonify({'ok': False, 'error': 'AI 主管正在工作中,请稍候'}), 400
|
return jsonify({'ok': False, 'error': 'AI 主管正在工作中,请稍候'}), 400
|
||||||
|
|||||||
+27
-21
@@ -47,19 +47,39 @@ REVISE_PROMPT = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_wbs(text):
|
def _extract_json(text):
|
||||||
"""从 LLM 输出中稳健提取 JSON 任务列表"""
|
"""用 raw_decode 稳健提取第一个完整 JSON 值(自动忽略尾部杂质/多余 JSON)"""
|
||||||
t = text.strip()
|
t = text.strip()
|
||||||
if t.startswith('```'):
|
if t.startswith('```'):
|
||||||
t = t.strip('`')
|
t = t.strip('`')
|
||||||
if t.startswith('json'):
|
if t.startswith('json'):
|
||||||
t = t[4:]
|
t = t[4:]
|
||||||
t = t.strip()
|
t = t.strip()
|
||||||
start = min([i for i in (t.find('{'), t.find('[')) if i >= 0] or [0])
|
candidates = [i for i in (t.find('{'), t.find('[')) if i >= 0]
|
||||||
end = max(t.rfind('}'), t.rfind(']')) + 1
|
last_err = None
|
||||||
data = json.loads(t[start:end])
|
for i in sorted(candidates):
|
||||||
tasks = data['tasks'] if isinstance(data, dict) else data
|
try:
|
||||||
assert isinstance(tasks, list) and tasks, '任务列表为空'
|
obj, _ = json.JSONDecoder().raw_decode(t[i:])
|
||||||
|
return obj
|
||||||
|
except Exception as e:
|
||||||
|
last_err = e
|
||||||
|
raise last_err or ValueError('未找到 JSON 内容')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_wbs(text):
|
||||||
|
"""从 LLM 输出中稳健提取 JSON 任务列表(兼容任务键名/多余内容)"""
|
||||||
|
try:
|
||||||
|
data = _extract_json(text)
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for key in ('tasks', 'subtasks', 'task_list', 'items', 'task'):
|
||||||
|
if key in data:
|
||||||
|
data = data[key]
|
||||||
|
break
|
||||||
|
tasks = data if isinstance(data, list) else []
|
||||||
|
if not tasks:
|
||||||
|
raise ValueError('任务列表为空或格式不正确')
|
||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
@@ -100,20 +120,6 @@ def _chat_json(worker, messages, max_tokens=3000, temperature=0.3):
|
|||||||
raise last_err or ValueError('JSON 解析失败')
|
raise last_err or ValueError('JSON 解析失败')
|
||||||
|
|
||||||
|
|
||||||
def _extract_json(text):
|
|
||||||
t = text.strip()
|
|
||||||
if t.startswith('```'):
|
|
||||||
t = t.strip('`')
|
|
||||||
if t.startswith('json'):
|
|
||||||
t = t[4:]
|
|
||||||
t = t.strip()
|
|
||||||
start = min([i for i in (t.find('{'), t.find('[')) if i >= 0] or [0])
|
|
||||||
end = max(t.rfind('}'), t.rfind(']')) + 1
|
|
||||||
if end <= start:
|
|
||||||
raise ValueError('未找到 JSON 内容')
|
|
||||||
return json.loads(t[start:end])
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 团队读取
|
# 团队读取
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user