Spaces:
Sleeping
Sleeping
Reformat SWOT output: concise 3-column table format
Browse filesBackend:
- Update analyzer prompt to output SWOT as tables
- Format: | Ref | Metric | Insight |
- Update revision prompt to use same table format
Frontend:
- Add cleanMarkdown() to strip asterisks and bold markers
- Apply to all SWOT item displays and copy text
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- frontend/src/App.tsx +17 -8
- src/nodes/analyzer.py +35 -30
frontend/src/App.tsx
CHANGED
|
@@ -85,6 +85,15 @@ const defaultLLMStatus: LLMStatus = {
|
|
| 85 |
openrouter: 'idle',
|
| 86 |
}
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
const Index = () => {
|
| 89 |
const [selectedStock, setSelectedStock] = useState<StockResult | null>(null)
|
| 90 |
const [isLoading, setIsLoading] = useState(false)
|
|
@@ -377,16 +386,16 @@ Quality Score: ${analysisResult.score}/10
|
|
| 377 |
Revisions: ${analysisResult.revision_count}
|
| 378 |
|
| 379 |
STRENGTHS:
|
| 380 |
-
${analysisResult.swot_data.strengths.map(s => `- ${s}`).join('\n')}
|
| 381 |
|
| 382 |
WEAKNESSES:
|
| 383 |
-
${analysisResult.swot_data.weaknesses.map(w => `- ${w}`).join('\n')}
|
| 384 |
|
| 385 |
OPPORTUNITIES:
|
| 386 |
-
${analysisResult.swot_data.opportunities.map(o => `- ${o}`).join('\n')}
|
| 387 |
|
| 388 |
THREATS:
|
| 389 |
-
${analysisResult.swot_data.threats.map(t => `- ${t}`).join('\n')}
|
| 390 |
|
| 391 |
QUALITY EVALUATION:
|
| 392 |
${analysisResult.critique}
|
|
@@ -784,7 +793,7 @@ Generated by Instant SWOT Agent`
|
|
| 784 |
{analysisResult.swot_data.strengths.map((item, i) => (
|
| 785 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 786 |
<CheckCircle className="h-4 w-4 text-emerald-500 shrink-0 mt-0.5" />
|
| 787 |
-
<span>{item}</span>
|
| 788 |
</li>
|
| 789 |
))}
|
| 790 |
</ul>
|
|
@@ -800,7 +809,7 @@ Generated by Instant SWOT Agent`
|
|
| 800 |
{analysisResult.swot_data.weaknesses.map((item, i) => (
|
| 801 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 802 |
<XCircle className="h-4 w-4 text-red-500 shrink-0 mt-0.5" />
|
| 803 |
-
<span>{item}</span>
|
| 804 |
</li>
|
| 805 |
))}
|
| 806 |
</ul>
|
|
@@ -816,7 +825,7 @@ Generated by Instant SWOT Agent`
|
|
| 816 |
{analysisResult.swot_data.opportunities.map((item, i) => (
|
| 817 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 818 |
<Zap className="h-4 w-4 text-blue-500 shrink-0 mt-0.5" />
|
| 819 |
-
<span>{item}</span>
|
| 820 |
</li>
|
| 821 |
))}
|
| 822 |
</ul>
|
|
@@ -832,7 +841,7 @@ Generated by Instant SWOT Agent`
|
|
| 832 |
{analysisResult.swot_data.threats.map((item, i) => (
|
| 833 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 834 |
<AlertCircle className="h-4 w-4 text-yellow-500 shrink-0 mt-0.5" />
|
| 835 |
-
<span>{item}</span>
|
| 836 |
</li>
|
| 837 |
))}
|
| 838 |
</ul>
|
|
|
|
| 85 |
openrouter: 'idle',
|
| 86 |
}
|
| 87 |
|
| 88 |
+
// Helper to clean markdown formatting (strip asterisks, bold markers)
|
| 89 |
+
const cleanMarkdown = (text: string): string => {
|
| 90 |
+
return text
|
| 91 |
+
.replace(/\*\*([^*]+)\*\*/g, '$1') // Remove **bold**
|
| 92 |
+
.replace(/\*([^*]+)\*/g, '$1') // Remove *italic*
|
| 93 |
+
.replace(/^\*\s*/gm, '') // Remove bullet asterisks at line start
|
| 94 |
+
.trim()
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
const Index = () => {
|
| 98 |
const [selectedStock, setSelectedStock] = useState<StockResult | null>(null)
|
| 99 |
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
| 386 |
Revisions: ${analysisResult.revision_count}
|
| 387 |
|
| 388 |
STRENGTHS:
|
| 389 |
+
${analysisResult.swot_data.strengths.map(s => `- ${cleanMarkdown(s)}`).join('\n')}
|
| 390 |
|
| 391 |
WEAKNESSES:
|
| 392 |
+
${analysisResult.swot_data.weaknesses.map(w => `- ${cleanMarkdown(w)}`).join('\n')}
|
| 393 |
|
| 394 |
OPPORTUNITIES:
|
| 395 |
+
${analysisResult.swot_data.opportunities.map(o => `- ${cleanMarkdown(o)}`).join('\n')}
|
| 396 |
|
| 397 |
THREATS:
|
| 398 |
+
${analysisResult.swot_data.threats.map(t => `- ${cleanMarkdown(t)}`).join('\n')}
|
| 399 |
|
| 400 |
QUALITY EVALUATION:
|
| 401 |
${analysisResult.critique}
|
|
|
|
| 793 |
{analysisResult.swot_data.strengths.map((item, i) => (
|
| 794 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 795 |
<CheckCircle className="h-4 w-4 text-emerald-500 shrink-0 mt-0.5" />
|
| 796 |
+
<span>{cleanMarkdown(item)}</span>
|
| 797 |
</li>
|
| 798 |
))}
|
| 799 |
</ul>
|
|
|
|
| 809 |
{analysisResult.swot_data.weaknesses.map((item, i) => (
|
| 810 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 811 |
<XCircle className="h-4 w-4 text-red-500 shrink-0 mt-0.5" />
|
| 812 |
+
<span>{cleanMarkdown(item)}</span>
|
| 813 |
</li>
|
| 814 |
))}
|
| 815 |
</ul>
|
|
|
|
| 825 |
{analysisResult.swot_data.opportunities.map((item, i) => (
|
| 826 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 827 |
<Zap className="h-4 w-4 text-blue-500 shrink-0 mt-0.5" />
|
| 828 |
+
<span>{cleanMarkdown(item)}</span>
|
| 829 |
</li>
|
| 830 |
))}
|
| 831 |
</ul>
|
|
|
|
| 841 |
{analysisResult.swot_data.threats.map((item, i) => (
|
| 842 |
<li key={i} className="flex gap-2 text-sm text-foreground">
|
| 843 |
<AlertCircle className="h-4 w-4 text-yellow-500 shrink-0 mt-0.5" />
|
| 844 |
+
<span>{cleanMarkdown(item)}</span>
|
| 845 |
</li>
|
| 846 |
))}
|
| 847 |
</ul>
|
src/nodes/analyzer.py
CHANGED
|
@@ -1348,11 +1348,18 @@ Weighted Score: {critique_details.get('weighted_score', 0):.1f} / 10
|
|
| 1348 |
|
| 1349 |
### OUTPUT INSTRUCTIONS
|
| 1350 |
|
| 1351 |
-
Produce a complete, revised SWOT analysis
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1352 |
|
| 1353 |
Do not:
|
|
|
|
| 1354 |
- Include any preamble about revisions
|
| 1355 |
-
- Apologize or explain what you changed
|
| 1356 |
- Reference the Critic's feedback in your output
|
| 1357 |
|
| 1358 |
Simply output the improved SWOT as a clean, final deliverable."""
|
|
@@ -1397,44 +1404,42 @@ def _build_analyzer_prompt(company: str, ticker: str, formatted_data: str,
|
|
| 1397 |
|
| 1398 |
=== OUTPUT FORMAT ===
|
| 1399 |
|
| 1400 |
-
Produce a SWOT analysis with this exact structure:
|
| 1401 |
|
| 1402 |
## Strengths
|
| 1403 |
-
|
| 1404 |
-
-
|
| 1405 |
-
|
| 1406 |
-
|
|
|
|
|
|
|
| 1407 |
|
| 1408 |
## Weaknesses
|
| 1409 |
-
|
| 1410 |
-
-
|
| 1411 |
-
|
| 1412 |
-
- **Trend:** [Improving/Stable/Deteriorating]
|
| 1413 |
-
- **Remediation Levers:** [What could improve this]
|
| 1414 |
|
| 1415 |
## Opportunities
|
| 1416 |
-
|
| 1417 |
-
-
|
| 1418 |
-
|
| 1419 |
-
- **Execution Requirements:** [What must happen]
|
| 1420 |
|
| 1421 |
## Threats
|
| 1422 |
-
|
| 1423 |
-
-
|
| 1424 |
-
|
| 1425 |
-
- **Impact:** [Potential magnitude]
|
| 1426 |
-
- **Mitigation Options:** [Possible responses]
|
| 1427 |
|
| 1428 |
## Data Quality Notes
|
| 1429 |
-
-
|
| 1430 |
-
-
|
| 1431 |
-
-
|
| 1432 |
-
|
| 1433 |
-
CRITICAL
|
| 1434 |
-
1.
|
| 1435 |
-
2.
|
| 1436 |
-
3.
|
| 1437 |
-
4.
|
|
|
|
| 1438 |
|
| 1439 |
return prompt, metric_lookup, ref_hash
|
| 1440 |
|
|
|
|
| 1348 |
|
| 1349 |
### OUTPUT INSTRUCTIONS
|
| 1350 |
|
| 1351 |
+
Produce a complete, revised SWOT analysis using TABLE format:
|
| 1352 |
+
|
| 1353 |
+
## Strengths
|
| 1354 |
+
| Ref | Metric | Insight |
|
| 1355 |
+
|-----|--------|---------|
|
| 1356 |
+
| M## | Metric: Value | Strategic insight in one sentence |
|
| 1357 |
+
|
| 1358 |
+
(Same table format for Weaknesses, Opportunities, Threats)
|
| 1359 |
|
| 1360 |
Do not:
|
| 1361 |
+
- Use bullet points or **bold** labels - use tables only
|
| 1362 |
- Include any preamble about revisions
|
|
|
|
| 1363 |
- Reference the Critic's feedback in your output
|
| 1364 |
|
| 1365 |
Simply output the improved SWOT as a clean, final deliverable."""
|
|
|
|
| 1404 |
|
| 1405 |
=== OUTPUT FORMAT ===
|
| 1406 |
|
| 1407 |
+
Produce a SWOT analysis using TABLES with this exact structure:
|
| 1408 |
|
| 1409 |
## Strengths
|
| 1410 |
+
| Ref | Metric | Insight |
|
| 1411 |
+
|-----|--------|---------|
|
| 1412 |
+
| M01 | Revenue: $394.3B | Strong market position with substantial scale |
|
| 1413 |
+
| M02 | Net Margin: 24.3% | High profitability indicates pricing power |
|
| 1414 |
+
|
| 1415 |
+
(Include 3-5 rows per section)
|
| 1416 |
|
| 1417 |
## Weaknesses
|
| 1418 |
+
| Ref | Metric | Insight |
|
| 1419 |
+
|-----|--------|---------|
|
| 1420 |
+
| M04 | Debt/Equity: 1.87 | Elevated leverage increases financial risk |
|
|
|
|
|
|
|
| 1421 |
|
| 1422 |
## Opportunities
|
| 1423 |
+
| Ref | Metric | Insight |
|
| 1424 |
+
|-----|--------|---------|
|
| 1425 |
+
| M12 | GDP Growth: 4.3% | Favorable macro environment for expansion |
|
|
|
|
| 1426 |
|
| 1427 |
## Threats
|
| 1428 |
+
| Ref | Metric | Insight |
|
| 1429 |
+
|-----|--------|---------|
|
| 1430 |
+
| M13 | Interest Rate: 3.72% | Higher borrowing costs may impact margins |
|
|
|
|
|
|
|
| 1431 |
|
| 1432 |
## Data Quality Notes
|
| 1433 |
+
- Metrics Used: [List key metrics analyzed]
|
| 1434 |
+
- Data Gaps: [Any unavailable metrics]
|
| 1435 |
+
- Confidence: [High/Medium/Low]
|
| 1436 |
+
|
| 1437 |
+
CRITICAL REQUIREMENTS:
|
| 1438 |
+
1. Use TABLE format as shown above - NOT bullet points
|
| 1439 |
+
2. Every row MUST cite a metric reference [M##] in the Ref column
|
| 1440 |
+
3. Metric column: Name and value (e.g., "Revenue: $394.3B")
|
| 1441 |
+
4. Insight column: One concise sentence explaining strategic implication
|
| 1442 |
+
5. Use EXACT values from the METRIC REFERENCE TABLE - do NOT round"""
|
| 1443 |
|
| 1444 |
return prompt, metric_lookup, ref_hash
|
| 1445 |
|