Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.
Merged
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
20 changes: 12 additions & 8 deletions backend/app/analysis/video_stt.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ def _extract_wav_segment(video_path: str, *, start_sec: float, clip_sec: float)
check=False,
)
if proc.returncode != 0:
err = (proc.stderr or b"").decode("utf-8", errors="replace")[:400]
err_raw = (proc.stderr or b"").decode("utf-8", errors="replace")
# 头部多为版本信息,真实错误常在尾部;输出尾部更利于定位。
err = err_raw[-1000:] if err_raw else ""
logger.warning("VIDEO_STT: ffmpeg 片段提取失败 rc=%s %s", proc.returncode, err)
return b""
with open(wav_path, "rb") as wf:
Expand Down Expand Up @@ -274,17 +276,18 @@ async def _transcribe_single_wav(
return _transcription_text_from_response(resp)


async def transcribe_video_with_whisper(video_bytes: bytes, container_suffix: str) -> str:
async def transcribe_video_with_whisper(video_bytes: bytes, container_suffix: str) -> tuple[str, str]:
"""
异步:线程池提取 WAV + AsyncOpenAI Whisper 转写。
@returns 转写文本,失败或未开启时为空字符串
@returns (转写文本, 状态码)
status: ok | disabled | missing_config | extract_failed | api_error | empty_text
"""
if not _stt_enabled():
return ""
return "", "disabled"

key, base = _resolve_whisper_client_config()
if not key or not base:
return ""
return "", "missing_config"

model = (os.getenv("WHISPER_MODEL") or "whisper-1").strip()

Expand All @@ -293,7 +296,7 @@ async def transcribe_video_with_whisper(video_bytes: bytes, container_suffix: st
logger.warning(
"VIDEO_STT: WAV 片段为空,未调用转写 API(检查 ffmpeg、视频是否含音轨、上方 stderr 日志)",
)
return ""
return "", "extract_failed"

import httpx
from openai import AsyncOpenAI
Expand Down Expand Up @@ -336,12 +339,13 @@ async def transcribe_video_with_whisper(video_bytes: bytes, container_suffix: st
merged = _join_transcript_parts(texts)
if merged:
logger.info("VIDEO_STT: 全片转写成功 chunks=%s total_len=%s model=%s", total, len(merged), model)
return merged, "ok"
else:
logger.warning("VIDEO_STT: 全片转写为空,请核对 ASR 模型与音频内容")
return merged
return "", "empty_text"
except Exception as e:
logger.warning("VIDEO_STT: 转写 API 失败 %s", e)
return ""
return "", "api_error"
finally:
await http_client.aclose()

Expand Down
46 changes: 45 additions & 1 deletion backend/app/api/comments_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"""
import json
import logging
import time
from pydantic import BaseModel
from fastapi import APIRouter
from fastapi import APIRouter, HTTPException

from app.agents.base_agent import BaseAgent, MODEL_FAST

Expand Down Expand Up @@ -68,8 +69,51 @@ async def generate_comments(req: GenerateCommentsRequest):
result = await agent.call_llm(user_msg, max_tokens=2000)

result.pop("_meta", None)

if result.get("dimension") == "error":
issues = result.get("issues") or []
detail = issues[0] if issues else result.get("reasoning", "评论生成失败")
# #region agent log
try:
with open("/Users/yxz/Desktop/projects/小红书黑客松/noterx/.cursor/debug-7caaea.log", "a", encoding="utf-8") as _df:
_df.write(json.dumps({
"sessionId": "7caaea",
"runId": "post-fix",
"hypothesisId": "B",
"location": "comments_api.py:generate_comments:error",
"message": "llm error raised to client",
"data": {"detail": str(detail)[:200]},
"timestamp": int(time.time() * 1000),
}, ensure_ascii=False) + "\n")
except Exception:
pass
# #endregion
raise HTTPException(status_code=503, detail=str(detail))

comments = result.get("comments", [])

# #region agent log
try:
with open("/Users/yxz/Desktop/projects/小红书黑客松/noterx/.cursor/debug-7caaea.log", "a", encoding="utf-8") as _df:
_df.write(json.dumps({
"sessionId": "7caaea",
"runId": "pre-fix",
"hypothesisId": "A,B",
"location": "comments_api.py:generate_comments",
"message": "generate_comments llm result",
"data": {
"existing_count": req.existing_count,
"title_len": len(req.title or ""),
"raw_keys": list(result.keys())[:12],
"has_error_dimension": result.get("dimension") == "error",
"comments_count": len(comments) if isinstance(comments, list) else -1,
},
"timestamp": int(time.time() * 1000),
}, ensure_ascii=False) + "\n")
except Exception:
pass
# #endregion

formatted = []
for c in comments:
if not isinstance(c, dict):
Expand Down
Loading
Loading