feat(frontend): add always-visible '重新生成分析' button per module\nfix(backend): inject dependency context for single-module generation (final_conclusion placeholders)

This commit is contained in:
xucheng 2025-10-31 03:09:43 +00:00
parent 1e904eb7f4
commit 8b5d5f5777
3 changed files with 79 additions and 1 deletions

View File

@ -669,13 +669,77 @@ async def generate_analysis(
# Initialize analysis client with configured model
client = AnalysisClient(api_key=api_key, base_url=base_url, model=model)
# Prepare dependency context for single-module generation
# If the requested module declares dependencies, generate them first and inject their outputs
context = {}
try:
dependencies = analysis_cfg.get("dependencies", []) or []
if dependencies:
# Load full modules config to resolve dependency graph
analysis_config_full = load_analysis_config()
modules_config = analysis_config_full.get("analysis_modules", {})
# Collect all transitive dependencies
all_required = set()
def collect_all_deps(mod_name: str):
for dep in modules_config.get(mod_name, {}).get("dependencies", []) or []:
if dep not in all_required:
all_required.add(dep)
collect_all_deps(dep)
for dep in dependencies:
all_required.add(dep)
collect_all_deps(dep)
# Build subgraph and topologically sort
graph = {name: [d for d in (modules_config.get(name, {}).get("dependencies", []) or []) if d in all_required] for name in all_required}
in_degree = {u: 0 for u in graph}
for u, deps in graph.items():
for v in deps:
in_degree[v] += 1
queue = [u for u, deg in in_degree.items() if deg == 0]
order = []
while queue:
u = queue.pop(0)
order.append(u)
for v in graph.get(u, []):
in_degree[v] -= 1
if in_degree[v] == 0:
queue.append(v)
if len(order) != len(graph):
# Fallback: if cycle detected, just use any order
order = list(all_required)
# Generate dependencies in order
completed = {}
for mod in order:
cfg = modules_config.get(mod, {})
dep_ctx = {d: completed.get(d, "") for d in (cfg.get("dependencies", []) or [])}
dep_client = AnalysisClient(api_key=api_key, base_url=base_url, model=cfg.get("model", model))
dep_result = await dep_client.generate_analysis(
analysis_type=mod,
company_name=company_name,
ts_code=ts_code,
prompt_template=cfg.get("prompt_template", ""),
financial_data=financial_data,
context=dep_ctx,
)
completed[mod] = dep_result.get("content", "") if dep_result.get("success") else ""
context = {dep: completed.get(dep, "") for dep in dependencies}
except Exception:
# Best-effort context; if anything goes wrong, continue without it
context = {}
# Generate analysis
result = await client.generate_analysis(
analysis_type=analysis_type,
company_name=company_name,
ts_code=ts_code,
prompt_template=prompt_template,
financial_data=financial_data
financial_data=financial_data,
context=context,
)
logger.info(f"[API] Analysis generation completed, success={result.get('success')}")

View File

@ -1491,6 +1491,7 @@ export default function ReportPage() {
: '待开始'}
</div>
</div>
{/* 失败时的“重新分析”按钮(兼容原逻辑) */}
{state.error && !state.loading && (
<Button
variant="outline"
@ -1502,6 +1503,18 @@ export default function ReportPage() {
</Button>
)}
{/* 新增:始终可见的“重新生成分析”按钮 */}
{!state.loading && (
<Button
variant="ghost"
size="sm"
onClick={() => retryAnalysis(analysisType)}
disabled={currentAnalysisTask !== null}
>
<RotateCw className="size-4" />
</Button>
)}
</div>
{state.error && (

View File

@ -8,3 +8,4 @@ echo "All pm2 applications stopped."
echo "Deleting all pm2 processes..."
pm2 delete all
echo "All pm2 processes deleted."