iamge genration credits fixing - not fixed
This commit is contained in:
78
CHANGELOG.md
78
CHANGELOG.md
@@ -9,6 +9,7 @@
|
||||
|
||||
| Version | Date | Summary |
|
||||
|---------|------|---------|
|
||||
| 1.7.1 | Jan 10, 2026 | **Image Generation Credits** - Complete credit deduction, verification, and logging for all image generation models (DALL-E 3, Runware, Google); Pre-generation credit check; AITaskLog logging; Notification integration |
|
||||
| 1.7.0 | Jan 10, 2026 | **Major** - Pre-Launch Cleanup Complete (Phases 1, 5, 6): Code cleanup, UX improvements, data backup tools; WordPress plugin distribution system; Template design improvements; AI model fixes |
|
||||
| 1.6.2 | Jan 8, 2026 | **Design Refinements** - Updated marketing site gradients to brand colors (primary + success), reduced shadow weights, simplified automation icons, added Upcoming Features page |
|
||||
| 1.6.1 | Jan 8, 2026 | **Email System** - SMTP configuration, email templates, password reset flow, email verification, unsubscribe functionality |
|
||||
@@ -40,6 +41,83 @@
|
||||
|
||||
---
|
||||
|
||||
## v1.7.1 - January 10, 2026
|
||||
|
||||
### Image Generation Credit System
|
||||
|
||||
This release implements complete credit deduction, verification, and logging for all 3 image generation models, ensuring consistency with other AI functions in the automation pipeline.
|
||||
|
||||
---
|
||||
|
||||
### 💰 Credit System for Image Generation
|
||||
|
||||
**New Credit Check Method:**
|
||||
- Added `CreditService.check_credits_for_image()` in [credit_service.py:307-335](backend/igny8_core/business/billing/services/credit_service.py#L307-L335)
|
||||
- Pre-checks if account has sufficient credits before image generation starts
|
||||
- Uses `credits_per_image` from AIModelConfig (as configured in admin)
|
||||
- Returns required credits or raises `InsufficientCreditsError`
|
||||
|
||||
**Pre-Generation Credit Verification:**
|
||||
- Added credit check before processing images in [tasks.py:290-319](backend/igny8_core/ai/tasks.py#L290-L319)
|
||||
- Checks total credits needed for all images BEFORE starting generation
|
||||
- Returns error with `InsufficientCreditsError` if not enough credits
|
||||
- Gracefully handles missing model configurations for backward compatibility
|
||||
|
||||
**Credit Deduction (Already Existed - Now Verified):**
|
||||
- `deduct_credits_for_image()` called after each successful image in [tasks.py:745-770](backend/igny8_core/ai/tasks.py#L745-L770)
|
||||
- Fixed cost retrieval to use `result.get('cost')` (was incorrectly using `cost_usd`)
|
||||
- Credits per model (from AIModelConfig):
|
||||
- `runware:97@1`: 1 credit/image (basic tier)
|
||||
- `dall-e-3`: 5 credits/image (quality tier)
|
||||
- `google:4@2`: 15 credits/image (premium tier)
|
||||
|
||||
---
|
||||
|
||||
### 📊 Logging & Notifications
|
||||
|
||||
**AITaskLog Integration:**
|
||||
- Added AITaskLog logging in [tasks.py:838-875](backend/igny8_core/ai/tasks.py#L838-L875)
|
||||
- Records function_name (`generate_images`), account, phase, status, cost
|
||||
- Consistent with other AI function logging (clustering, content generation, etc.)
|
||||
|
||||
**Notification Integration:**
|
||||
- Added notification creation in [tasks.py:877-895](backend/igny8_core/ai/tasks.py#L877-L895)
|
||||
- Creates success notifications via `NotificationService.notify_images_complete()`
|
||||
- Creates failure notifications via `NotificationService.notify_images_failed()`
|
||||
- Same pattern as used in AIEngine for other AI functions
|
||||
|
||||
---
|
||||
|
||||
### 🔄 Automation Compatibility
|
||||
|
||||
**Verified automation flow for image generation:**
|
||||
- Stage 6 in automation service calls `process_image_generation_queue` correctly
|
||||
- Credit deduction happens inside the task (same as manual image generation)
|
||||
- Credits tracked via `_get_credits_used()` and stored in `stage_6_result`
|
||||
- Consistent with Stages 1, 2, 4, 5 credit tracking
|
||||
|
||||
---
|
||||
|
||||
### 📁 Files Changed
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `backend/igny8_core/business/billing/services/credit_service.py` | Added `check_credits_for_image()` method |
|
||||
| `backend/igny8_core/ai/tasks.py` | Added credit check, AITaskLog logging, notifications |
|
||||
|
||||
---
|
||||
|
||||
### 📋 Credit Usage Logging Locations
|
||||
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `CreditTransaction` | Financial ledger entry (via `deduct_credits_for_image`) |
|
||||
| `CreditUsageLog` | Detailed usage log with model, cost, credits |
|
||||
| `AITaskLog` | AI task execution tracking |
|
||||
| `Notification` | User notifications for completion/failure |
|
||||
|
||||
---
|
||||
|
||||
## v1.7.0 - January 10, 2026
|
||||
|
||||
### Pre-Launch Cleanup & Plugin Distribution System
|
||||
|
||||
@@ -158,7 +158,8 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
from igny8_core.ai.ai_core import AICore
|
||||
from igny8_core.ai.prompts import PromptRegistry
|
||||
from igny8_core.business.billing.services.credit_service import CreditService
|
||||
|
||||
from igny8_core.business.billing.exceptions import InsufficientCreditsError, CreditCalculationError
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info(f"process_image_generation_queue STARTED")
|
||||
logger.info(f" - Task ID: {self.request.id}")
|
||||
@@ -166,7 +167,7 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
logger.info(f" - Account ID: {account_id}")
|
||||
logger.info(f" - Content ID: {content_id}")
|
||||
logger.info("=" * 80)
|
||||
|
||||
|
||||
account = None
|
||||
if account_id:
|
||||
from igny8_core.auth.models import Account
|
||||
@@ -285,7 +286,38 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
|
||||
# Initialize AICore
|
||||
ai_core = AICore(account=account)
|
||||
|
||||
|
||||
# Credit check before processing images
|
||||
if account:
|
||||
logger.info(f"[process_image_generation_queue] Step 3: Checking credits for {total_images} images")
|
||||
try:
|
||||
required_credits = CreditService.check_credits_for_image(
|
||||
account=account,
|
||||
model_name=model,
|
||||
num_images=total_images
|
||||
)
|
||||
logger.info(f"[process_image_generation_queue] Credit check passed: {required_credits} credits required, {account.credits} available")
|
||||
except InsufficientCreditsError as e:
|
||||
error_msg = str(e)
|
||||
logger.error(f"[process_image_generation_queue] Insufficient credits: {error_msg}")
|
||||
return {
|
||||
'success': False,
|
||||
'error': error_msg,
|
||||
'error_type': 'InsufficientCreditsError',
|
||||
'total_images': total_images,
|
||||
'completed': 0,
|
||||
'failed': total_images,
|
||||
'results': []
|
||||
}
|
||||
except CreditCalculationError as e:
|
||||
# Model not found or no credits_per_image configured - log warning but continue
|
||||
# This allows backward compatibility if model not configured
|
||||
logger.warning(f"[process_image_generation_queue] Credit calculation warning: {e}")
|
||||
logger.warning(f"[process_image_generation_queue] Proceeding without credit check (model may not be configured)")
|
||||
except Exception as e:
|
||||
# Don't fail for unexpected credit check errors - log and continue
|
||||
logger.warning(f"[process_image_generation_queue] Unexpected credit check error: {e}")
|
||||
|
||||
# Process each image sequentially
|
||||
for index, image_id in enumerate(image_ids, 1):
|
||||
try:
|
||||
@@ -712,7 +744,7 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
else:
|
||||
# Deduct credits for successful image generation
|
||||
credits_deducted = 0
|
||||
cost_usd = result.get('cost_usd', 0)
|
||||
cost_usd = result.get('cost', 0) or result.get('cost_usd', 0) # generate_image returns 'cost'
|
||||
if account:
|
||||
try:
|
||||
credits_deducted = CreditService.deduct_credits_for_image(
|
||||
@@ -802,7 +834,66 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
logger.info(f" - Completed: {completed}")
|
||||
logger.info(f" - Failed: {failed}")
|
||||
logger.info("=" * 80)
|
||||
|
||||
|
||||
# Log to AITaskLog for consistency with other AI functions
|
||||
if account:
|
||||
try:
|
||||
from igny8_core.ai.models import AITaskLog
|
||||
import time
|
||||
|
||||
# Calculate total cost from results
|
||||
total_cost = sum(r.get('cost', 0) for r in results if r.get('status') == 'completed')
|
||||
|
||||
AITaskLog.objects.create(
|
||||
task_id=self.request.id,
|
||||
function_name='generate_images',
|
||||
account=account,
|
||||
phase='DONE' if completed > 0 else 'ERROR',
|
||||
message=f'Generated {completed} images ({failed} failed)' if completed > 0 else f'All {total_images} images failed',
|
||||
status='success' if completed > 0 else 'error',
|
||||
duration=0, # Could track actual duration if needed
|
||||
cost=total_cost,
|
||||
tokens=0, # Image generation doesn't use tokens
|
||||
request_steps=[{
|
||||
'phase': 'IMAGE_GENERATION',
|
||||
'status': 'success' if completed > 0 else 'error',
|
||||
'message': f'Processed {total_images} images: {completed} completed, {failed} failed'
|
||||
}],
|
||||
response_steps=[],
|
||||
error=None if completed > 0 else f'All {total_images} images failed',
|
||||
payload={'image_ids': image_ids, 'content_id': content_id},
|
||||
result={
|
||||
'total_images': total_images,
|
||||
'completed': completed,
|
||||
'failed': failed,
|
||||
'model': model,
|
||||
'provider': provider
|
||||
}
|
||||
)
|
||||
logger.info(f"[process_image_generation_queue] AITaskLog entry created")
|
||||
except Exception as log_error:
|
||||
logger.warning(f"[process_image_generation_queue] Failed to create AITaskLog: {log_error}")
|
||||
|
||||
# Create notification for image generation completion
|
||||
if account:
|
||||
try:
|
||||
from igny8_core.business.notifications.services import NotificationService
|
||||
|
||||
if completed > 0:
|
||||
NotificationService.notify_images_complete(
|
||||
account=account,
|
||||
image_count=completed
|
||||
)
|
||||
elif failed > 0:
|
||||
NotificationService.notify_images_failed(
|
||||
account=account,
|
||||
error=f'{failed} images failed to generate',
|
||||
image_count=failed
|
||||
)
|
||||
logger.info(f"[process_image_generation_queue] Notification created")
|
||||
except Exception as notif_error:
|
||||
logger.warning(f"[process_image_generation_queue] Failed to create notification: {notif_error}")
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'total_images': total_images,
|
||||
|
||||
@@ -285,13 +285,13 @@ class CreditService:
|
||||
def check_credits_for_tokens(account, operation_type, estimated_tokens_input, estimated_tokens_output):
|
||||
"""
|
||||
Check if account has sufficient credits based on estimated token usage.
|
||||
|
||||
|
||||
Args:
|
||||
account: Account instance
|
||||
operation_type: Type of operation
|
||||
estimated_tokens_input: Estimated input tokens
|
||||
estimated_tokens_output: Estimated output tokens
|
||||
|
||||
|
||||
Raises:
|
||||
InsufficientCreditsError: If account doesn't have enough credits
|
||||
"""
|
||||
@@ -303,6 +303,36 @@ class CreditService:
|
||||
f"Insufficient credits. Required: {required}, Available: {account.credits}"
|
||||
)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def check_credits_for_image(account, model_name: str, num_images: int = 1):
|
||||
"""
|
||||
Check if account has sufficient credits for image generation.
|
||||
|
||||
Args:
|
||||
account: Account instance
|
||||
model_name: AI model name (e.g., 'dall-e-3', 'runware:97@1')
|
||||
num_images: Number of images to generate
|
||||
|
||||
Raises:
|
||||
InsufficientCreditsError: If account doesn't have enough credits
|
||||
CreditCalculationError: If model not found or has no credits_per_image
|
||||
|
||||
Returns:
|
||||
int: Required credits for the operation
|
||||
"""
|
||||
required = CreditService.calculate_credits_for_image(model_name, num_images)
|
||||
|
||||
if account.credits < required:
|
||||
raise InsufficientCreditsError(
|
||||
f"Insufficient credits for image generation. Required: {required}, Available: {account.credits}"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Credit check passed for image generation: "
|
||||
f"{num_images} images with {model_name} = {required} credits (available: {account.credits})"
|
||||
)
|
||||
return required
|
||||
|
||||
@staticmethod
|
||||
@transaction.atomic
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Billing Module
|
||||
|
||||
**Last Verified:** January 8, 2026
|
||||
**Status:** ✅ Active (Payment System Refactored January 2026)
|
||||
**Last Verified:** January 10, 2026
|
||||
**Status:** ✅ Active (Image Generation Credit System Complete January 2026)
|
||||
**Backend Path:** `backend/igny8_core/modules/billing/` + `backend/igny8_core/business/billing/`
|
||||
**Frontend Path:** `frontend/src/pages/Billing/` + `frontend/src/pages/Account/`
|
||||
|
||||
@@ -208,6 +208,36 @@ credits = CreditService.calculate_credits_for_image(
|
||||
# Returns: 15 (3 images × 5 credits)
|
||||
```
|
||||
|
||||
### Check Credits for Image Generation (NEW v1.7.1)
|
||||
|
||||
```python
|
||||
# Pre-check credits before image generation starts
|
||||
# Raises InsufficientCreditsError if not enough credits
|
||||
required = CreditService.check_credits_for_image(
|
||||
account=account,
|
||||
model_name='dall-e-3', # Model with credits_per_image = 5
|
||||
num_images=3
|
||||
)
|
||||
# Returns: 15 (required credits) if sufficient, raises exception if not
|
||||
```
|
||||
|
||||
### Deduct Credits for Image Generation (v1.7.1 Verified)
|
||||
|
||||
```python
|
||||
# Called after each successful image generation
|
||||
credits_deducted = CreditService.deduct_credits_for_image(
|
||||
account=account,
|
||||
model_name='dall-e-3',
|
||||
num_images=1,
|
||||
description='Image generation: Article Title',
|
||||
metadata={'image_id': 123, 'content_id': 456},
|
||||
cost_usd=0.04,
|
||||
related_object_type='image',
|
||||
related_object_id=123
|
||||
)
|
||||
# Logs to CreditTransaction and CreditUsageLog
|
||||
```
|
||||
|
||||
### Calculate Credits from Tokens by Model (NEW v1.4.0)
|
||||
|
||||
```python
|
||||
@@ -327,5 +357,7 @@ Displays:
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| ~~AI Model Config database~~ | ✅ v1.4.0 | Model pricing moved to AIModelConfig |
|
||||
| Image model quality tiers | ✅ v1.4.0 | credits_per_image per quality tier |
|
||||
| Credit service enhancements | 🔄 v1.5.0 | Model-specific calculation methods |
|
||||
| ~~Image model quality tiers~~ | ✅ v1.4.0 | credits_per_image per quality tier |
|
||||
| ~~Credit service enhancements~~ | ✅ v1.7.1 | Model-specific calculation methods |
|
||||
| ~~Image generation credit check~~ | ✅ v1.7.1 | Pre-generation credit verification |
|
||||
| ~~Image generation logging~~ | ✅ v1.7.1 | AITaskLog + notifications for images |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Credit System
|
||||
|
||||
**Last Verified:** January 5, 2026
|
||||
**Status:** ✅ Simplified (v1.5.0)
|
||||
**Last Verified:** January 10, 2026
|
||||
**Status:** ✅ Complete (v1.7.1 - Image Generation Credits)
|
||||
|
||||
---
|
||||
|
||||
@@ -93,7 +93,7 @@ Credits calculated from actual token usage:
|
||||
| Content Generation | gpt-4o | 1,000 |
|
||||
| Content Optimization | gpt-4o-mini | 10,000 |
|
||||
|
||||
### Fixed-Cost Operations (Image AI)
|
||||
### Fixed-Cost Operations (Image AI) - v1.7.1 Complete
|
||||
|
||||
Credits per image based on quality tier:
|
||||
|
||||
@@ -103,6 +103,13 @@ Credits per image based on quality tier:
|
||||
| Quality | dall-e-3 | 5 |
|
||||
| Premium | google:4@2 | 15 |
|
||||
|
||||
**Image Generation Credit Flow (v1.7.1):**
|
||||
1. Pre-check: `CreditService.check_credits_for_image()` verifies sufficient credits
|
||||
2. Generation: Images generated via `process_image_generation_queue` Celery task
|
||||
3. Post-deduct: `CreditService.deduct_credits_for_image()` called per successful image
|
||||
4. Logging: `CreditUsageLog` + `CreditTransaction` + `AITaskLog` entries created
|
||||
5. Notifications: `NotificationService.notify_images_complete/failed()` called
|
||||
|
||||
### Free Operations
|
||||
|
||||
| Operation | Cost |
|
||||
@@ -452,3 +459,58 @@ All AI operations logged in `CreditUsageLog` with:
|
||||
- Model used
|
||||
- Token counts
|
||||
- Related object metadata
|
||||
|
||||
---
|
||||
|
||||
## Image Generation Credit System (v1.7.1)
|
||||
|
||||
### Implementation Details
|
||||
|
||||
**Files:**
|
||||
- `CreditService.check_credits_for_image()` - [credit_service.py:307-335](../backend/igny8_core/business/billing/services/credit_service.py#L307-L335)
|
||||
- `process_image_generation_queue` credit check - [tasks.py:290-319](../backend/igny8_core/ai/tasks.py#L290-L319)
|
||||
- `deduct_credits_for_image()` - [tasks.py:745-770](../backend/igny8_core/ai/tasks.py#L745-L770)
|
||||
- AITaskLog logging - [tasks.py:838-875](../backend/igny8_core/ai/tasks.py#L838-L875)
|
||||
- Notifications - [tasks.py:877-895](../backend/igny8_core/ai/tasks.py#L877-L895)
|
||||
|
||||
### Credit Flow for Image Generation
|
||||
|
||||
```
|
||||
1. User triggers image generation
|
||||
↓
|
||||
2. CreditService.check_credits_for_image(account, model, num_images)
|
||||
- Calculates: credits_per_image × num_images
|
||||
- Raises InsufficientCreditsError if balance < required
|
||||
↓
|
||||
3. process_image_generation_queue() processes each image
|
||||
↓
|
||||
4. For each successful image:
|
||||
CreditService.deduct_credits_for_image()
|
||||
- Creates CreditUsageLog entry
|
||||
- Creates CreditTransaction entry
|
||||
- Updates account.credits balance
|
||||
↓
|
||||
5. After all images processed:
|
||||
- AITaskLog entry created
|
||||
- Notification created (success or failure)
|
||||
```
|
||||
|
||||
### Logging Locations
|
||||
|
||||
| Table | What's Logged | When |
|
||||
|-------|---------------|------|
|
||||
| CreditTransaction | Credit deduction (financial ledger) | Per image |
|
||||
| CreditUsageLog | Usage details (model, cost, credits) | Per image |
|
||||
| AITaskLog | Task execution summary | After batch |
|
||||
| Notification | User notification | After batch |
|
||||
|
||||
### Automation Compatibility
|
||||
|
||||
Image generation credits work identically for:
|
||||
- Manual image generation (from UI)
|
||||
- Automation Stage 6 (scheduled/manual automation runs)
|
||||
|
||||
Both call `process_image_generation_queue` which handles:
|
||||
- Credit checking before generation
|
||||
- Credit deduction after each successful image
|
||||
- Proper logging to all tables
|
||||
|
||||
Reference in New Issue
Block a user