LLM Generate for Odoo
Unified content generation system with dynamic form generation, streaming responses, and race condition fixes. This module provides a clean, consistent API for generating all types of content (text, images, audio, etc.) across different AI providers.
Module Type: 📦 Infrastructure
Installation
What to Install
This module is typically auto-installed as a dependency of llm_thread or llm_assistant.
For content generation features:
llm_assistant + llm_openai (or other provider)
Auto-Installed Dependencies
These are pulled in automatically:
llm(core infrastructure)llm_assistant(assistant framework)mail(Odoo messaging)
Optional Enhancements
| Module | Adds |
|---|---|
llm_generate_job |
Background job queue for long-running generations |
llm_fal_ai |
Fast image generation |
llm_replicate |
Model marketplace access |
Common Setups Using This Module
| I want to... | Install |
|---|---|
| Generate text with GPT | llm_assistant + llm_openai |
| Generate images | llm_assistant + llm_fal_ai (or llm_replicate) |
| Background generation | Above + llm_generate_job |
Overview
The LLM Generate module serves as the unified interface for all content generation operations in the Odoo LLM ecosystem. It provides a consistent API regardless of the underlying AI provider or content type, with advanced features like dynamic form generation, streaming responses, and comprehensive error handling.
Core Capabilities
- Unified Generation API - Single interface for text, image, audio, and other content types
- Dynamic Form Generation - Automatic UI generation based on model schemas
- Streaming Generation - Real-time content creation with live updates
- Race Condition Fixes - Comprehensive async handling and loading state management
- Schema Handling - Intelligent schema resolution and form field generation
- Queue Management - Background processing for long-running generations
Key Features
Unified Generation Interface
Single Method for All Content Types:
# Text generation
response = thread.generate_response(
user_input="Write a product description",
generation_type="text"
)
# Image generation
image = thread.generate_response(
user_input="A beautiful landscape with mountains",
generation_type="image",
model_id=dalle_model.id
)
# Audio generation
audio = thread.generate_response(
user_input="Convert this text to speech: Hello world",
generation_type="audio"
)
Dynamic Form Generation
Automatic UI Creation from Model Schemas:
The system automatically generates forms based on AI model input schemas:
def get_input_schema(self):
"""Generate form schema with intelligent priority resolution"""
# Priority order:
# 1. Assistant's prompt schema (if assistant selected)
# 2. Thread's direct prompt schema (if prompt directly selected)
# 3. Model's default schema
if self.assistant_id and self.assistant_id.prompt_id:
return self.assistant_id.prompt_id.input_schema_json
elif self.prompt_id:
return self.prompt_id.input_schema_json
else:
return self.model_id.get_default_schema()
Schema-Driven Form Fields:
// Automatic form field generation
get formFields() {
const schema = this.inputSchema;
if (!schema?.properties) return [];
return Object.entries(schema.properties).map(([key, field]) => ({
name: key,
type: this.getFieldType(field),
label: field.title || key,
required: schema.required?.includes(key),
default: field.default,
placeholder: field.description
}));
}
Race Condition Fixes
Comprehensive Async Handling:
Fixed multiple race conditions in form loading and schema computation:
// Before - Race condition prone
async _handleContextChange() {
await this._loadThreadConfiguration();
this._initializeFormValues(); // Could execute before schema loaded
}
// After - Proper async handling
async _handleContextChange() {
this.state.isLoading = true;
try {
await this._loadThreadConfiguration();
this._initializeFormValues();
} finally {
this.state.isLoading = false;
}
}
Loading State Management:
- Proper loading indicators during async operations
- Prevention of premature form rendering
- Smooth user experience without UI flashing
- Consistent form behavior across scenarios
Streaming Generation
Real-time Content Creation:
def generate_response_stream(self, user_input, **kwargs):
"""Generate content with real-time streaming updates"""
# Create placeholder message
message = self.message_post(
body="",
llm_role="assistant"
)
# Stream content generation
stream = self.provider_id.generate_stream(
prompt=user_input,
model=self.model_id.name,
**kwargs
)
# Update message in real-time
for chunk in stream:
message.body += chunk.content
self._notify_message_update(message)
return message
Frontend Streaming Support:
// Real-time UI updates during generation
async _streamGeneration(generationData) {
const response = await this.rpc({
route: '/llm/generate/stream',
params: generationData
});
// Listen for real-time updates
this.env.bus.addEventListener('llm_message_update', (event) => {
this._updateStreamingMessage(event.detail);
});
}
Schema Source Transparency
Clear Schema Source Indication:
get schemaSource() {
if (this.state.isLoading) {
return { type: 'loading', name: 'Loading...' };
}
if (this.thread?.assistant_id?.prompt_id) {
return {
type: 'prompt',
name: this.thread.assistant_id.prompt_id.name,
source: 'Assistant Prompt'
};
}
if (this.thread?.prompt_id) {
return {
type: 'prompt',
name: this.thread.prompt_id.name,
source: 'Thread Prompt'
};
}
if (this.thread?.model_id) {
return {
type: 'model',
name: this.thread.model_id.name,
source: 'Model Default'
};
}
return { type: 'none', name: 'No Schema Available' };
}
Visual Schema Indicators:
- Badge showing schema source type (Prompt/Model/None)
- Clear indication of which configuration is being used
- Warning messages when no schema is available
- Tooltips explaining schema precedence
Content Generation Types
Text Generation
# Simple text generation
text_response = env['llm.thread'].generate_content(
prompt="Write a professional email",
content_type="text",
parameters={
'temperature': 0.7,
'max_tokens': 500
}
)
# Template-based generation
templated_response = env['llm.thread'].generate_from_template(
template_id=email_template.id,
context={
'recipient_name': 'John Doe',
'company_name': 'Acme Corp',
'product_name': 'Enterprise Suite'
}
)
Image Generation
# Image generation with parameters
image_result = env['llm.thread'].generate_content(
prompt="A modern office building at sunset",
content_type="image",
parameters={
'size': '1024x1024',
'style': 'photorealistic',
'quality': 'high'
}
)
# Batch image generation
batch_images = env['llm.thread'].generate_batch(
prompts=[
"Product photo - laptop on desk",
"Product photo - laptop in meeting room",
"Product photo - laptop outdoor setting"
],
content_type="image",
batch_size=3
)
Multi-modal Generation
# Combined text and image generation
multimodal_result = env['llm.thread'].generate_content(
prompt="Create a product listing with description and image",
content_type="multimodal",
context={
'product_name': 'Smart Watch Pro',
'product_features': ['GPS', 'Heart Rate', 'Waterproof']
}
)
Queue Management
Background Processing
# Queue long-running generations
job = env['llm.generation.job'].create({
'thread_id': thread.id,
'prompt': "Generate comprehensive market analysis",
'content_type': "text",
'parameters': {'max_tokens': 4000},
'priority': 'high'
})
# Monitor job progress
while job.state == 'running':
time.sleep(1)
job.refresh()
if job.state == 'completed':
result = job.result_content
Queue Configuration
# Configure provider-specific queues
queue = env['llm.generation.queue'].create({
'name': 'High Priority Text Generation',
'provider_id': openai_provider.id,
'content_types': ['text', 'chat'],
'max_concurrent': 5,
'timeout': 300
})
Form Generation Examples
Dynamic Schema-Based Forms
Automatic Field Generation:
// Schema definition
const schema = {
properties: {
style: {
type: "string",
enum: ["photorealistic", "artistic", "cartoon"],
title: "Image Style",
description: "Choose the visual style",
},
mood: {
type: "string",
title: "Mood",
description: "Describe the desired mood",
},
resolution: {
type: "string",
enum: ["512x512", "1024x1024", "1024x1792"],
default: "1024x1024",
title: "Resolution",
},
},
required: ["style", "mood"],
};
// Automatic form generation creates:
// - Select field for style (with enum options)
// - Text input for mood (with description placeholder)
// - Select field for resolution (with default selected)
// - Required field validation
Custom Form Components
// Custom field types for specific use cases
const customFieldTypes = {
color: ColorPickerField,
slider: SliderField,
file_upload: FileUploadField,
model_selector: ModelSelectorField,
};
// Usage in schema
const advancedSchema = {
properties: {
primary_color: {
type: "color",
title: "Primary Color",
default: "#3498db",
},
creativity: {
type: "slider",
minimum: 0,
maximum: 1,
step: 0.1,
default: 0.7,
title: "Creativity Level",
},
},
};
Error Handling & Validation
Comprehensive Error Management
def generate_with_validation(self, prompt, **kwargs):
"""Generate content with comprehensive error handling"""
try:
# Validate inputs
self._validate_generation_inputs(prompt, **kwargs)
# Check provider availability
if not self.provider_id.is_available():
raise UserError("Provider is currently unavailable")
# Validate content type support
if not self.model_id.supports_content_type(kwargs.get('content_type')):
raise UserError(f"Model doesn't support {kwargs.get('content_type')}")
# Generate content
return self._generate_content(prompt, **kwargs)
except ValidationError as e:
self._log_generation_error(e, 'validation')
raise UserError(f"Validation failed: {e}")
except APIError as e:
self._log_generation_error(e, 'api')
return self._handle_api_error(e)
except Exception as e:
self._log_generation_error(e, 'unknown')
raise UserError("An unexpected error occurred during generation")
Frontend Validation
// Real-time form validation
_validateForm() {
const errors = [];
const values = this.state.formValues;
const schema = this.inputSchema;
// Required field validation
schema.required?.forEach(field => {
if (!values[field]) {
errors.push(`${field} is required`);
}
});
// Type validation
Object.entries(schema.properties).forEach(([key, fieldSchema]) => {
const value = values[key];
if (value && !this._validateFieldType(value, fieldSchema)) {
errors.push(`Invalid value for ${key}`);
}
});
this.state.validationErrors = errors;
return errors.length === 0;
}
Integration Examples
CRM Integration
class CRMLead(models.Model):
_inherit = 'crm.lead'
def generate_follow_up_email(self):
"""Generate personalized follow-up email using AI"""
thread = self.env['llm.thread'].create({
'name': f'Follow-up Generation - {self.name}',
'assistant_id': self.env.ref('crm_ai.follow_up_assistant').id,
'model': self._name,
'res_id': self.id
})
# Generate email content
email_content = thread.generate_response(
user_input="Generate follow-up email",
context={
'lead_name': self.name,
'customer_name': self.partner_name,
'last_contact': self.date_last_stage_update,
'opportunity_value': self.expected_revenue
}
)
return email_content
Product Catalog Integration
class ProductTemplate(models.Model):
_inherit = 'product.template'
def generate_marketing_content(self, content_types=['description', 'image']):
"""Generate marketing content for product"""
results = {}
for content_type in content_types:
if content_type == 'description':
results['description'] = self._generate_description()
elif content_type == 'image':
results['image'] = self._generate_product_image()
elif content_type == 'ad_copy':
results['ad_copy'] = self._generate_ad_copy()
return results
def _generate_product_image(self):
"""Generate product marketing image"""
prompt = f"""
Product photography for {self.name}:
- Category: {self.categ_id.name}
- Key features: {', '.join(self.attribute_line_ids.mapped('display_name'))}
- Professional, commercial style
- Clean background, good lighting
"""
return self.env['llm.thread'].generate_content(
prompt=prompt,
content_type="image",
parameters={
'size': '1024x1024',
'style': 'commercial',
'quality': 'high'
}
)
API Reference
Core Generation Methods
# Basic generation
def generate_response(self, user_input, generation_type="text", **kwargs):
"""Main generation method with unified interface"""
# Streaming generation
def generate_response_stream(self, user_input, **kwargs):
"""Generate with real-time streaming updates"""
# Template-based generation
def generate_from_template(self, template_id, context, **kwargs):
"""Generate using prompt template with context"""
# Batch generation
def generate_batch(self, prompts, content_type, **kwargs):
"""Generate multiple items in batch"""
# Queue-based generation
def generate_async(self, prompt, **kwargs):
"""Queue generation for background processing"""
Schema Methods
# Get input schema for forms
def get_input_schema(self):
"""Get schema for dynamic form generation"""
# Validate schema compatibility
def validate_schema(self, schema):
"""Validate schema format and content"""
# Merge schemas from multiple sources
def merge_schemas(self, *schemas):
"""Combine schemas with intelligent merging"""
Performance Optimizations
Schema Caching
@api.model
@tools.ormcache('model_id', 'prompt_id', 'assistant_id')
def _get_cached_schema(self, model_id, prompt_id, assistant_id):
"""Cache computed schemas for better performance"""
return self._compute_input_schema()
Streaming Optimizations
- Chunked Processing: Process generation in optimal chunks
- UI Debouncing: Prevent excessive UI updates during streaming
- Memory Management: Efficient handling of large content streams
- Connection Pooling: Reuse connections for better performance
Technical Specifications
Module Information
- Name: LLM Generate
- Version: 18.0.2.0.0
- Category: Productivity
- License: LGPL-3
- Dependencies:
llm,llm_assistant,mail - Author: Apexive Solutions LLC
Key Models
llm.generation.job: Background generation job managementllm.generation.queue: Provider-specific queue configuration- Extensions to
llm.thread: Core generation methods
Frontend Components
LLMMediaForm: Dynamic form generation componentLLMGenerationWizard: Generation parameter configurationLLMStreamingDisplay: Real-time content updatesLLMSchemaIndicator: Schema source transparency
Related Modules
llm: Base infrastructure and provider frameworkllm_assistant: Assistant configuration and prompt templatesllm_tool: Function calling and Odoo integrationllm_generate_job: Advanced job queue managementllm_fal_ai: FAL.ai provider with generate endpoint integration
Support & Resources
- Documentation: GitHub Repository
- Architecture Guide: OVERVIEW.md
- API Examples: Generation Examples
- Community: GitHub Discussions
License
This module is licensed under LGPL-3.
© 2025 Apexive Solutions LLC. All rights reserved.
