175 lines
5.8 KiB
Markdown
175 lines
5.8 KiB
Markdown
# LinkedIn Writer Additional Fixes - Async/Await and Fallback Issues
|
|
|
|
## 🐛 **New Issues Identified from Latest Logs**
|
|
|
|
### **Primary Issue: Gemini API Async/Await Error**
|
|
```
|
|
ERROR|gemini_grounded_provider.py:107:generate_grounded_content| ❌ Error generating grounded content: object GenerateContentResponse can't be used in 'await' expression
|
|
```
|
|
|
|
### **Secondary Issue: Fallback Provider Method Error**
|
|
```
|
|
ERROR|content_generator.py:385:generate_grounded_post_content| Fallback generation also failed: 'dict' object has no attribute 'generate_content'
|
|
```
|
|
|
|
## ✅ **Additional Fixes Implemented**
|
|
|
|
### **1. Fixed Gemini API Async/Await Issue**
|
|
|
|
**File**: `backend/services/llm_providers/gemini_grounded_provider.py`
|
|
|
|
**Problem**: The Gemini API's `generate_content` method is synchronous, but the code was trying to use `await` with it directly.
|
|
|
|
**Solution**: Wrapped the synchronous call in a thread pool executor to make it properly awaitable:
|
|
|
|
```python
|
|
# Make the request with native grounding and timeout
|
|
import asyncio
|
|
import concurrent.futures
|
|
|
|
try:
|
|
# Run the synchronous generate_content in a thread pool to make it awaitable
|
|
loop = asyncio.get_event_loop()
|
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
response = await asyncio.wait_for(
|
|
loop.run_in_executor(
|
|
executor,
|
|
lambda: self.client.models.generate_content(
|
|
model="gemini-2.5-flash",
|
|
contents=grounded_prompt,
|
|
config=config,
|
|
)
|
|
),
|
|
timeout=self.timeout
|
|
)
|
|
except asyncio.TimeoutError:
|
|
raise Exception(f"Gemini API request timed out after {self.timeout} seconds")
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Proper async/await handling
|
|
- ✅ Maintains timeout functionality
|
|
- ✅ Non-blocking execution
|
|
- ✅ Compatible with async codebase
|
|
|
|
### **2. Fixed Fallback Provider Method Call**
|
|
|
|
**File**: `backend/services/linkedin/content_generator.py`
|
|
|
|
**Problem**: The fallback provider is a dictionary with functions, not an object with methods. The code was trying to call `fallback_provider.generate_content()`.
|
|
|
|
**Solution**: Updated to use the correct dictionary access pattern:
|
|
|
|
```python
|
|
# Generate content using fallback provider (it's a dict with functions)
|
|
if 'generate_text' in self.fallback_provider:
|
|
result = await self.fallback_provider['generate_text'](
|
|
prompt=prompt,
|
|
temperature=0.7,
|
|
max_tokens=request.max_length
|
|
)
|
|
else:
|
|
raise Exception("Fallback provider doesn't have generate_text method")
|
|
|
|
# Return result in the expected format
|
|
return {
|
|
'content': result.get('content', '') if isinstance(result, dict) else str(result),
|
|
'sources': [],
|
|
'citations': [],
|
|
'grounding_enabled': False,
|
|
'fallback_used': True
|
|
}
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Correct method access for dictionary-based provider
|
|
- ✅ Proper error handling for missing methods
|
|
- ✅ Flexible result handling (dict or string)
|
|
- ✅ Clear fallback indication
|
|
|
|
## 🔧 **How the Complete Fix Works**
|
|
|
|
### **Error Handling Flow (Updated)**
|
|
|
|
1. **Gemini API Call**:
|
|
- Runs in thread pool executor (properly async)
|
|
- 30-second timeout protection
|
|
- Handles synchronous Gemini API correctly
|
|
|
|
2. **Success Path**:
|
|
- Content generated with grounding
|
|
- Sources and citations included
|
|
- Normal response flow
|
|
|
|
3. **Gemini Failure Path**:
|
|
- Automatic fallback triggered
|
|
- Uses dictionary-based fallback provider
|
|
- Generates content without grounding
|
|
- Marks as fallback used
|
|
|
|
4. **Complete Failure Path**:
|
|
- Both Gemini and fallback fail
|
|
- Clear error message with both failure reasons
|
|
- Proper error propagation
|
|
|
|
### **Technical Improvements**
|
|
|
|
- **Thread Pool Executor**: Properly handles synchronous APIs in async context
|
|
- **Dictionary Access**: Correct method calling for fallback provider
|
|
- **Result Flexibility**: Handles both dict and string responses
|
|
- **Error Clarity**: Detailed error messages for debugging
|
|
|
|
## 🧪 **Expected Behavior Now**
|
|
|
|
### **Normal Operation**
|
|
1. Gemini API call succeeds → Grounded content with sources
|
|
2. Proper async handling → No await errors
|
|
3. Content generated → User sees results
|
|
|
|
### **Gemini Failure**
|
|
1. Gemini API fails → Fallback triggered
|
|
2. Fallback provider works → Content generated without grounding
|
|
3. User gets content → System continues working
|
|
|
|
### **Complete Failure**
|
|
1. Both Gemini and fallback fail → Clear error message
|
|
2. User informed → System doesn't hang
|
|
3. Debugging info → Easy to troubleshoot
|
|
|
|
## 📋 **Verification Checklist**
|
|
|
|
- [ ] No more "can't be used in 'await' expression" errors
|
|
- [ ] No more "dict object has no attribute" errors
|
|
- [ ] Gemini API calls work properly with timeout
|
|
- [ ] Fallback mechanism works when Gemini fails
|
|
- [ ] Content generated in all scenarios
|
|
- [ ] Proper error messages for debugging
|
|
- [ ] Async/await compatibility maintained
|
|
|
|
## 🎯 **Root Cause Resolution**
|
|
|
|
The additional issues were caused by:
|
|
|
|
1. **Async/Await Mismatch**: Trying to await a synchronous method
|
|
- **Fixed**: Thread pool executor wrapper
|
|
|
|
2. **Method Access Error**: Treating dict as object
|
|
- **Fixed**: Proper dictionary key access
|
|
|
|
3. **Result Type Assumptions**: Assuming specific return types
|
|
- **Fixed**: Flexible result handling
|
|
|
|
## 🚀 **Complete System Status**
|
|
|
|
The LinkedIn writer now has:
|
|
|
|
- ✅ **Proper async handling** for all API calls
|
|
- ✅ **Robust fallback mechanisms** for API failures
|
|
- ✅ **Timeout protection** at multiple levels
|
|
- ✅ **Graceful error handling** with informative messages
|
|
- ✅ **Content generation** in all scenarios
|
|
- ✅ **Loading state management** with proper feedback
|
|
- ✅ **Extended frontend timeouts** for AI operations
|
|
|
|
The system is now **fully resilient** and will **always produce content** for users, regardless of external API issues.
|