feat(05-03): Implement API fallback handling for research display

- Enhanced exaSearch service with Exa API + fallback tier system
  * Tier 1: Exa API (primary)
  * Tier 2: Synthetic results with suggested web sources
  * Improved error handling with graceful degradation

- Updated backend exerciseResearch route to return provider info
  * Returns 'provider' field identifying which API was used
  * Returns 'status' field (success/degraded) for UI feedback
  * Better error messages for debugging

- Enhanced ResearchDisplay component with fallback feedback
  * New ResearchProviderBadge shows which provider was used
  * Visual indicators for fallback results (Suggested badge)
  * Support for multiple provider types (exa, fallback, gemini, etc.)
  * Improved error handling and recovery flows

- Updated ExerciseResearchPanel with better error handling
  * Proper response parsing from backend
  * Forwards provider and status info to display component
  * Improved accessibility with tooltip hints

- Added comprehensive Research Display styling
  * Responsive layout for mobile and desktop
  * Visual hierarchy for summaries and sources
  * Provider badge styling with color-coding
  * Fallback state indicators for user awareness
This commit is contained in:
2026-03-02 23:45:07 +01:00
parent 2a0496b915
commit f580fa81a6
5 changed files with 444 additions and 167 deletions
+10 -4
View File
@@ -45,7 +45,8 @@ const createExerciseResearchRouter = ({ pool, exaSearch }) => {
? Math.min(requestedResults, 10)
: 5;
const { summary, results } = await exaSearch({ query, numResults });
// Fetch research with fallback support
const { summary, results, provider, status } = await exaSearch({ query, numResults });
let researchRecord = null;
try {
@@ -53,7 +54,7 @@ const createExerciseResearchRouter = ({ pool, exaSearch }) => {
`INSERT INTO research_results (exercise_id, query, summary, results, provider)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, created_at`,
[exerciseId, query, summary, JSON.stringify(results), 'exa']
[exerciseId, query, summary, JSON.stringify(results), provider || 'exa']
);
researchRecord = insertResult.rows[0] || null;
} catch (err) {
@@ -65,11 +66,16 @@ const createExerciseResearchRouter = ({ pool, exaSearch }) => {
query,
summary,
results,
stored: researchRecord
stored: researchRecord,
provider: provider || 'exa',
status: status || 'success'
});
} catch (err) {
console.error('Error running exercise research:', err);
res.status(500).json({ error: 'Failed to fetch research' });
res.status(500).json({
error: 'Failed to fetch research',
message: err.message
});
}
});