SAM AI Ecosystem Analysis Report
SAM AI Ecosystem Analysis Report
Generated: 2025-10-13 23:56:51
📊 Executive Summary
Odoo Modules
- Total Modules: 12
- Total Lines of Code: 708,991
- Total Files: 4,246
Claude Agents
- Total Agents: 12
- Total Words: 104,579
- Total Knowledge Files: 62
- Shared Files: 1
📦 Odoo Module Analysis
Module Breakdown
ai_brain
- Total Lines: 16,840
- Total Files: 63
- Code Lines: 6,782
- Commented Lines: 7,403
- Blank Lines: 2,655
- Commented Code Blocks: 101
Files by Type:
- .py: 59 files, 16,670 lines
- .xml: 4 files, 170 lines
ai_sam
- Total Lines: 12,466
- Total Files: 44
- Code Lines: 6,899
- Commented Lines: 3,908
- Blank Lines: 1,659
- Commented Code Blocks: 12
Files by Type:
- .css: 6 files, 2,634 lines
- .html: 1 files, 863 lines
- .js: 13 files, 4,979 lines
- .py: 9 files, 2,307 lines
- .scss: 1 files, 125 lines
- .xml: 14 files, 1,558 lines
ai_sam_creatives
- Total Lines: 2,458
- Total Files: 17
- Code Lines: 1,120
- Commented Lines: 1,027
- Blank Lines: 311
- Commented Code Blocks: 2
Files by Type:
- .css: 1 files, 737 lines
- .js: 5 files, 1,109 lines
- .py: 7 files, 287 lines
- .sql: 1 files, 12 lines
- .xml: 3 files, 313 lines
ai_sam_docs
- Total Lines: 8,113
- Total Files: 21
- Code Lines: 6,979
- Commented Lines: 160
- Blank Lines: 974
- Commented Code Blocks: 2
Files by Type:
- .html: 8 files, 5,789 lines
- .json: 1 files, 178 lines
- .py: 5 files, 375 lines
- .sql: 3 files, 1,260 lines
- .xml: 4 files, 511 lines
ai_sam_intelligence
- Total Lines: 1,312
- Total Files: 11
- Code Lines: 694
- Commented Lines: 428
- Blank Lines: 190
- Commented Code Blocks: 12
Files by Type:
- .py: 6 files, 1,003 lines
- .xml: 5 files, 309 lines
ai_sam_members
- Total Lines: 950
- Total Files: 15
- Code Lines: 769
- Commented Lines: 94
- Blank Lines: 87
- Commented Code Blocks: 1
Files by Type:
- .css: 1 files, 52 lines
- .py: 7 files, 355 lines
- .xml: 7 files, 543 lines
ai_sam_memory
- Total Lines: 3,719
- Total Files: 24
- Code Lines: 1,698
- Commented Lines: 1,521
- Blank Lines: 500
- Commented Code Blocks: 11
Files by Type:
- .js: 3 files, 186 lines
- .py: 12 files, 2,678 lines
- .xml: 9 files, 855 lines
ai_sam_messenger
- Total Lines: 158
- Total Files: 4
- Code Lines: 107
- Commented Lines: 29
- Blank Lines: 22
- Commented Code Blocks: 1
Files by Type:
- .css: 1 files, 56 lines
- .js: 1 files, 64 lines
- .py: 2 files, 38 lines
ai_sam_socializer
- Total Lines: 607
- Total Files: 9
- Code Lines: 408
- Commented Lines: 132
- Blank Lines: 67
- Commented Code Blocks: 0
Files by Type:
- .css: 1 files, 72 lines
- .js: 2 files, 34 lines
- .py: 4 files, 238 lines
- .xml: 2 files, 263 lines
ai_sam_ui
- Total Lines: 733
- Total Files: 8
- Code Lines: 494
- Commented Lines: 147
- Blank Lines: 92
- Commented Code Blocks: 1
Files by Type:
- .html: 1 files, 91 lines
- .js: 1 files, 230 lines
- .py: 3 files, 57 lines
- .scss: 1 files, 274 lines
- .xml: 2 files, 81 lines
ai_sam_workflows
- Total Lines: 661,073
- Total Files: 4,018
- Code Lines: 519,953
- Commented Lines: 138,016
- Blank Lines: 3,104
- Commented Code Blocks: 28
Files by Type:
- .css: 4 files, 2,091 lines
- .html: 1 files, 3,425 lines
- .js: 2646 files, 560,880 lines
- .json: 1331 files, 87,049 lines
- .py: 8 files, 2,037 lines
- .scss: 1 files, 911 lines
- .xml: 27 files, 4,680 lines
github_app
- Total Lines: 562
- Total Files: 12
- Code Lines: 433
- Commented Lines: 64
- Blank Lines: 65
- Commented Code Blocks: 5
Files by Type:
- .html: 1 files, 158 lines
- .py: 7 files, 247 lines
- .xml: 4 files, 157 lines
🤖 Claude Agent Analysis
Agent Breakdown
canvas-core-guardian
- Total Words: 9,968
- Total Files: 6
- Unique Files: 5
- Shared Files: 1
Knowledge Files:
- agent_protocol.md: 1,797 words
- canvas_core_rules.md: 1,610 words
- forbidden_patterns.md: 1,720 words
- naming_standards.md: 1,641 words
- QUICKSTART.md: 1,496 words
- README.md: 1,704 words (shared)
cmo
- Total Words: 8,943
- Total Files: 5
- Unique Files: 5
- Shared Files: 0
Knowledge Files:
- cmo_protocol.md: 2,348 words
- direct_response_mastery.md: 1,858 words
- marketing_strategy_frameworks.md: 2,076 words
- market_positioning_methodology.md: 1,939 words
- sam_ai_product_context.md: 722 words
cto
- Total Words: 12,823
- Total Files: 5
- Unique Files: 5
- Shared Files: 0
Knowledge Files:
- cost_management.md: 2,843 words
- cto_protocol.md: 2,608 words
- infrastructure_strategy.md: 2,091 words
- performance_optimization_playbook.md: 2,487 words
- scaling_roadmap.md: 2,794 words
documentation-master
- Total Words: 5,337
- Total Files: 5
- Unique Files: 5
- Shared Files: 0
Knowledge Files:
- boardroom_context_protocol.md: 987 words
- current_state_rules.md: 983 words
- docs_agent_workflow.md: 1,362 words
- documentation_intelligence.md: 1,215 words
- misalignment_detection.md: 790 words
github
- Total Words: 3,404
- Total Files: 5
- Unique Files: 5
- Shared Files: 0
Knowledge Files:
- commit_message_template.md: 666 words
- github_config.md: 269 words
- github_expertise.md: 1,178 words
- pre_push_checklist.md: 773 words
- workflow_patterns.md: 518 words
odoo-architect
- Total Words: 4,873
- Total Files: 4
- Unique Files: 4
- Shared Files: 0
Knowledge Files:
- brainstorming_framework.md: 1,388 words
- odoo_patterns.md: 918 words
- planning_methodology.md: 1,359 words
- prompt_writing_guide.md: 1,208 words
odoo-audit
- Total Words: 1,978
- Total Files: 4
- Unique Files: 4
- Shared Files: 0
Knowledge Files:
- common_mistakes.md: 553 words
- quality_standards.md: 450 words
- scoring_rubric.md: 364 words
- session_optimization.md: 611 words
odoo-debugger
- Total Words: 11,252
- Total Files: 5
- Unique Files: 5
- Shared Files: 0
Knowledge Files:
- architecture_compliance.md: 1,942 words
- bug_history_protocol.md: 1,798 words
- debug_protocol.md: 3,507 words
- odoo_error_patterns.md: 1,955 words
- qa_tool_guardian.md: 2,050 words
odoo-developer
- Total Words: 7,006
- Total Files: 5
- Unique Files: 5
- Shared Files: 0
Knowledge Files:
- architecture_mastery.md: 1,154 words
- development_standards.md: 1,526 words
- file_management.md: 1,238 words
- odoo_18_error_prevention.md: 2,055 words
- qa_integration.md: 1,033 words
odoo-qa-guardian
- Total Words: 8,042
- Total Files: 5
- Unique Files: 5
- Shared Files: 0
Knowledge Files:
- auto_fix_patterns.md: 1,607 words
- detection_commands.md: 1,606 words
- education_framework.md: 1,697 words
- qa_guardian_protocol.md: 1,580 words
- scoring_rubric.md: 1,552 words
recruiter
- Total Words: 13,612
- Total Files: 6
- Unique Files: 6
- Shared Files: 0
Knowledge Files:
- agent_creation_workflow.md: 2,260 words
- agent_design_patterns.md: 1,977 words
- existing_agents_analysis.md: 2,323 words
- knowledge_extraction.md: 1,773 words
- session_memory.md: 3,711 words
- session_memory_protocol.md: 1,568 words
sam
- Total Words: 17,341
- Total Files: 7
- Unique Files: 7
- Shared Files: 0
Knowledge Files:
- controller_architecture.md: 1,773 words
- graph_memory_protocol.md: 2,482 words
- sam_conversation_engine.md: 2,497 words
- sam_personality_framework.md: 2,483 words
- sam_protocol.md: 3,673 words
- session_history_research_protocol.md: 1,878 words
- specialist_routing.md: 2,555 words
🧹 Cleanup Candidates (Commented/Redundant Code)
Total Commented Code Blocks Found: 176
ai_brain (101 blocks)
ai_artifact_version.py (Lines 39-59)
"""Create a new artifact version from parsed artifact data"""
# Get the latest version for this conversation
latest = self.search([
ai_automator_config.py (Lines 18-27)
"""Load current settings from config parameters"""
res = super().default_get(fields_list)
if 'knowledge_visualizer_enabled' in fields_list:
ai_branches.py (Lines 224-236)
"""Check if the required module is installed"""
for record in self:
if record.module_name:
ai_branches.py (Lines 255-264)
"""Ensure technical name is lowercase and valid"""
for record in self:
if not record.technical_name.islower() or ' ' in record.technical_name:
ai_branches.py (Lines 336-338)
"""
Open canvas creation wizard for this branch type
"""
ai_claude_history_importer.py (Lines 227-271)
"""Import conversations from uploaded JSON file"""
if not self.file_data:
raise ValidationError(_('Please upload a JSON file first'))
ai_context_builder.py (Lines 103-116)
"""Get list of installed modules"""
Module = self.env['ir.module.module']
modules = Module.search([
ai_context_builder.py (Lines 128-135)
"""Format module list for prompt"""
lines = []
for module in modules:
ai_context_builder.py (Lines 196-242)
"""Get important fields from record"""
lines = []
# Get field definitions
ai_conversation.py (Lines 180-186)
"""Check if conversation is shared in any workspace"""
for record in self:
record.is_shared = bool(record.workspace_ids)
ai_conversation_import.py (Lines 153-165)
"""Auto-detect if path is file or directory"""
for record in self:
if record.source_path and os.path.exists(record.source_path):
ai_conversation_import.py (Lines 639-651)
"""Create ai.conversation record"""
# Claude export uses 'name', fallback to 'title' for other formats
conv_name = conv_data.get('name') or conv_data.get('title') or 'Untitled Conversation'
ai_document_extractor.py (Lines 36-50)
"""Register built-in extractors on first use"""
if AIDocumentExtractor._initialized:
return
ai_document_extractor.py (Lines 96-102)
"""
cls._extractors[extension.lower()] = handler
_logger.debug(f"Registered extractor for {extension}")
ai_document_extractor.py (Lines 220-227)
"""Extract JSON files"""
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
ai_document_extractor.py (Lines 237-251)
"""Extract PDF files"""
try:
import PyPDF2
ai_document_extractor.py (Lines 261-278)
"""Extract Excel files"""
try:
import openpyxl
ai_document_extractor.py (Lines 396-397)
"""
prompt = f"""Generate a Python function to extract content from {extension} files.
ai_document_extractor.py (Lines 411-428)
"""
conversation = self.env['ai.conversation'].create({
'name': f'Extractor Generation: {extension}'
ai_graph_service.py (Lines 242-246)
"""
Get the graph structure for a conversation (nodes + relationships)
Returns visualization data for the conversation and its connections
ai_message.py (Lines 100-108)
"""Override create to estimate tokens if not provided"""
for vals in vals_list:
if not vals.get('token_count') and vals.get('content'):
ai_registry_watcher.py (Lines 26-45)
"""Override write to detect module state changes"""
# Store old states
old_states = {module.id: module.state for module in self}
ai_registry_watcher.py (Lines 51-72)
"""
# Log the change
if new_state == 'installed':
ai_registry_watcher.py (Lines 77-99)
"""
# Check if there's a branch registered for this module
Branch = self.env['ai.branch'].sudo()
ai_service.py (Lines 144-185)
"""
if not config.api_key:
_logger.warning("Cannot count tokens: API key not configured")
ai_service.py (Lines 192-217)
"""
total_chars = 0
# Count system prompt
ai_service.py (Lines 223-229)
"""
# Rough approximation: 1 token ≈ 4 characters
# Claude actually uses ~3.5 chars/token on average, but 4 is safer
ai_service.py (Lines 276-290)
"""
# Exponential backoff: base_delay * (2 ^ (attempt - 1))
delay = config.retry_base_delay * (2 ** (attempt - 1))
ai_service.py (Lines 297-331)
"""
# Rate limit errors (429) - always retry with backoff
if status_code == 429:
ai_service.py (Lines 337-360)
"""
try:
# Try to parse JSON error response
ai_service_config.py (Lines 134-148)
"""Set model_name based on provider on create"""
for vals in vals_list:
if 'api_provider' in vals:
ai_service_config.py (Lines 418-435)
"""
config = self.search([('active', '=', True)], limit=1)
if not config:
ai_service_config.py (Lines 518-530)
"""Create default configuration if none exists"""
existing = self.search([], limit=1)
if not existing:
ai_service_config.py (Lines 611-618)
"""Validate token limits"""
for record in self:
if record.daily_token_limit < 0 or record.user_token_limit < 0:
ai_service_config.py (Lines 625-632)
"""Validate max tokens"""
for record in self:
if record.max_tokens < 1 or record.max_tokens > 200000:
ai_service_config.py (Lines 645-654)
"""Validate token counting configuration"""
for record in self:
if record.token_warning_threshold < 0 or record.token_warning_threshold > 200000:
ai_service_provider.py (Lines 172-186)
"""Compute API key status for display"""
for record in self:
if record.api_key and len(record.api_key) > 10:
ai_service_provider.py (Lines 352-359)
"""Validate priority is positive"""
for record in self:
if record.priority < 0:
ai_token_usage.py (Lines 166-172)
"""Calculate total tokens"""
for record in self:
record.total_tokens = record.input_tokens + record.output_tokens
ai_workspace.py (Lines 98-104)
"""Compute member count"""
for workspace in self:
workspace.member_count = len(workspace.member_ids)
api_credentials.py (Lines 88-98)
"""Override create to validate credential data"""
for vals in vals_list:
if 'credential_data' in vals and vals['credential_data']:
business_unit.py (Lines 26-37)
"""Display name with code if available"""
result = []
for record in self:
canvas.py (Lines 113-126)
"""Compute branch_id from branch_type"""
for record in self:
if record.branch_type:
canvas.py (Lines 138-155)
"""Validate JSON structure"""
for record in self:
if record.json_definition:
canvas.py (Lines 245-267)
' """Execute the complete workflow"""',
" try:",
]
canvas.py (Lines 301-307)
"""Generate Python code for a specific node"""
node_type = node.get('type', 'unknown')
node_name = node.get('name', 'Unnamed Node')
canvas.py (Lines 316-318)
"""
elif node_type == 'n8n-nodes-base.emailSend':
return f"""
canvas.py (Lines 325-330)
"""
else:
return f"# Unknown node type: {node_type} ({node_name})"
canvas.py (Lines 745-773)
"""
_logger.info(f'📂 [Canvas Load] Loading canvas state for workflow {workflow_id}')
try:
canvas.py (Lines 776-781)
"""
return self.write_sam_debug_log(log_data)
@api.model
canvas_pan_move.py (Lines 30-36)
"""Override create to set last_saved timestamp"""
for vals in vals_list:
vals['last_saved'] = fields.Datetime.now()
canvas_pan_move.py (Lines 41-62)
"""Save canvas viewport state"""
self.ensure_one()
update_vals = {}
connections.py (Lines 6-9)
"""
Canvas Connections Model for Knowledge Visualizer V2
Stores node-to-node connections for workflow canvas
documentation_manager.py (Lines 60-84)
"""Scan docs folder and update records"""
docs_path = self._get_docs_path()
if not docs_path.exists():
documentation_manager.py (Lines 143-186)
"""Determine category from filename and path"""
filename_lower = filename.lower()
# Architecture & System Design
documentation_manager.py (Lines 210-217)
"""Generate user-friendly title from filename"""
name = file_path.stem
# Convert underscores/hyphens to spaces and title case
documentation_manager.py (Lines 248-254)
"""Get the full file path for the document"""
self.ensure_one()
docs_path = self._get_docs_path()
executions.py (Lines 172-190)
"""Execute workflow nodes"""
# Create node lookup
node_lookup = {node['id']: node for node in nodes}
executions.py (Lines 197-229)
"""Execute a chain of connected nodes"""
node_id = node['id']
# Skip if already executed
executions.py (Lines 326-340)
"""Get nodes connected to the output of given node"""
connected_nodes = []
for connection in connections:
n8n_dynamic_menus.py (Lines 15-62)
"""Remove all dynamically generated N8N menu items that polluted the main menu"""
_logger.info('🧹 CLEANUP: Removing all dynamic N8N menu items from main Odoo menu...')
# Find all menu items with N8N node names or emojis
n8n_node_category.py (Lines 35-35)
# node_l2_ids = fields.Many2many('n8n.nodes.l2', 'n8n_l2_category_rel', 'category_id', 'node_l2_id', string='L2 Nodes')
n8n_node_category.py (Lines 48-48)
# def _compute_node_count(self):
n8n_simple_extractor.py (Lines 2-9)
"""
N8N Node Extractor - Direct from Filesystem Using N8N's Logic
This extracts node data DIRECTLY from the N8N node files and applies
n8n_simple_extractor.py (Lines 21-24)
"""
Wizard to extract N8N nodes directly from filesystem.
Uses N8N's actual logic for categorization.
n8n_simple_extractor.py (Lines 40-48)
"""Load all data for display - suppliers, nodes, and overlay preview"""
for record in self:
record.supplier_ids = self.env['n8n.simple.supplier'].search([])
n8n_simple_extractor.py (Lines 505-656)
"""Generate the new beautiful N8N Node Selection Overlay with REAL DATA"""
_logger.info('Computing new overlay with real N8N data...')
# Get node data from database
n8n_simple_extractor.py (Lines 670-674)
'''
return cards_html
# Simplified version without script tags for Odoo sanitization
n8n_simple_extractor.py (Lines 781-787)
'''
return html
@api.model
n8n_simple_nodes.py (Lines 164-170)
"""Compute node type from is_trigger flag"""
for node in self:
node.node_type = 'Trigger' if node.is_trigger else 'Action'
n8n_simple_nodes.py (Lines 200-246)
"""
# Step 1: Check if Core Nodes with explicit subcategory
if node.is_core_nodes and node.subcategories:
n8n_simple_nodes.py (Lines 260-280)
"""Custom display name"""
result = []
for node in self:
n8n_simple_nodes.py (Lines 320-325)
"""
Parse trigger operations from .node.js files.
For triggers, we return 1 card representing the trigger itself (not config options).
nodes.py (Lines 83-96)
"""Validate connection JSON"""
for record in self:
for field_name in ['input_connections', 'output_connections']:
nodes.py (Lines 106-111)
"""Set parameters from dictionary"""
self.ensure_one()
self.parameters = json.dumps(params_dict, indent=2)
nodes.py (Lines 121-131)
"""Get output connections as list"""
self.ensure_one()
if self.output_connections:
nodes.py (Lines 193-206)
"""Execute trigger node"""
if self.node_type == 'trigger_manual':
return {'status': 'success', 'data': input_data or {}}
nodes.py (Lines 217-228)
"""Execute Odoo-specific node"""
if self.node_type == 'odoo_create_record':
return self._execute_odoo_create(params, input_data)
nodes.py (Lines 239-248)
"""Execute data processing node"""
if self.node_type == 'data_transform':
return self._execute_data_transform(params, input_data)
nodes.py (Lines 293-300)
"""Execute notification"""
message = params.get('message', 'Workflow notification')
self.env.user.notify_info(message)
nodes.py (Lines 314-330)
"""Execute Odoo record update"""
model = params.get('model', '')
record_id = params.get('record_id', 0)
res_config_settings.py (Lines 11-11)
# knowledge_visualizer_enabled = fields.Boolean(
res_config_settings.py (Lines 12-12)
# string='Enable AI Automator Features',
res_config_settings.py (Lines 13-13)
# config_parameter='the_ai_automator.knowledge_visualizer_enabled',
res_config_settings.py (Lines 14-14)
# help='Enable the AI Automator workflow and visualization features',
res_config_settings.py (Lines 15-15)
# groups='base.group_no_one' # Hide from main settings menu
sam_brain_modes.py (Lines 451-501)
"""
message_lower = user_message.lower()
# Check for explicit brain requests
sam_brain_modes.py (Lines 511-543)
"""
# Get user context
user_ctx = user_profile.get_user_context_for_sam()
sam_chat_session.py (Lines 2-8)
"""
SAM AI Chat Session & Messages
================================
sam_knowledge_doc.py (Lines 269-282)
"""Compute file size and type from uploaded file"""
for record in self:
if record.file_data and record.file_name:
sam_knowledge_doc.py (Lines 291-305)
"""Generate preview from content"""
for record in self:
if record.content:
sam_mode_context.py (Lines 319-319)
# server_url = self.env['ir.config_parameter'].sudo().get_param('sam.power_prompts_server')
sam_personality.py (Lines 247-263)
prompt += f"""
YOUR BOUNDARIES WITH {name}:
- Personal topics: {'✅ Allowed' if user_ctx['boundaries']['can_discuss_personal'] else '❌ Keep professional'}
sam_personality.py (Lines 268-284)
"""Add SAM's context to the message before sending to AI"""
enriched = f"{system_prompt}\n\n"
# Add Odoo context if available
sam_personality.py (Lines 293-310)
"""Extract what actions user is requesting"""
actions = []
message_lower = message.lower()
sam_user_profile.py (Lines 242-257)
"""How SAM addresses the user"""
for record in self:
if record.preferred_name:
sam_user_profile.py (Lines 390-426)
"""
self.ensure_one()
pending = []
sam_user_profile.py (Lines 470-488)
"""Record an interaction and update trust score"""
self.interaction_count += 1
self.last_interaction = fields.Datetime.now()
sam_user_profile.py (Lines 494-516)
"""
self.ensure_one()
approved = []
sam_user_profile.py (Lines 524-545)
"""
self.ensure_one()
if not self.approved_file_paths:
workflow_templates.py (Lines 80-106)
"""Validate template JSON structure"""
for record in self:
if record.json_definition:
ai_sam (12 blocks)
canvas_controller.py (Lines 39-60)
"""Get platform configuration for dynamic loading"""
if not platform_id:
return {'error': 'platform_id is required'}
canvas_controller.py (Lines 72-95)
"""Render canvas container with platform"""
canvas = request.env['canvas'].browse(int(canvas_id))
if not canvas.exists():
canvas_controller.py (Lines 107-154)
"""Load nodes for a canvas based on its platform"""
import logging
_logger = logging.getLogger(__name__)
sam_developer_mode.py (Lines 23-26)
"""Controller for SAM AI Developer Mode features."""
def _get_tool_paths(self):
"""
sam_orchestrator.py (Lines 167-188)
"""Get existing session or create new one"""
Session = request.env['sam.chat.session'].sudo()
if session_id:
sam_session_controller.py (Lines 17-21)
"""Controller for SAM AI chat session management."""
@http.route('/sam/session/get_history', type='json', auth='user')
def get_session_history(self, mode=None, limit=20, **kwargs):
sam_ai_chat_interface.js (Lines 1236-1256)
/**
* Get relative time string (e.g., "2 hours ago", "Yesterday")
*/
sam_permission_handler.js (Lines 1-6)
/** @odoo-module **/
import { Component, useState } from "@odoo/owl";
import { rpc } from "@web/core/network/rpc";
sam_permission_handler.js (Lines 90-102)
/**
* Get icon for permission type
*/
sam_profile_settings.js (Lines 1-6)
/** @odoo-module **/
import { Component, useState, onMounted } from "@odoo/owl";
import { rpc } from "@web/core/network/rpc";
sam_ai_artifacts_manager.js (Lines 47-93)
/**
* Detect artifact type based on language and content
*/
sam_code_mode_button.js (Lines 1-6)
/** @odoo-module **/
import { Component, useState, onWillStart } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
ai_sam_creatives (2 blocks)
creatives_controller.py (Lines 11-14)
"""
Creatives Platform Controller
Handles all /creatives/* routes for the SAM Creative platform.
creatives_landing_card.py (Lines 2-9)
"""
Placeholder file for ai_sam_creatives module.
The actual creatives.landing.card model is defined in ai_brain module
ai_sam_docs (2 blocks)
documentation_controller.py (Lines 68-107)
"""Open file directly in browser"""
try:
doc = request.env['ai.automator.documentation'].browse(doc_id)
documentation_controller.py (Lines 139-145)
"""Get full path to documentation file"""
from odoo.modules.module import get_module_path
module_path = Path(get_module_path('the_ai_automator'))
ai_sam_intelligence (12 blocks)
ai_agent_knowledge.py (Lines 44-53)
"""Show first 500 characters as preview"""
for record in self:
if record.content:
ai_agent_knowledge.py (Lines 62-70)
"""Compute MD5 hash for change detection"""
for record in self:
if record.content:
ai_agent_registry.py (Lines 93-102)
"""Build rich system prompt from agent knowledge for SAM chat"""
for agent in self:
# Build knowledge summary
ai_agent_registry.py (Lines 120-124)
"""
@api.depends('archetype', 'category')
def _compute_capabilities(self):
ai_agent_registry.py (Lines 208-225)
"""Determine agent archetype from name and config"""
name_lower = agent_name.lower()
if any(x in name_lower for x in ['architect', 'cto', 'cmo', 'cfo', 'cos']):
ai_agent_registry.py (Lines 234-255)
"""Determine slash command from agent name"""
# Map known agents to their slash commands
command_map = {
ai_agent_registry.py (Lines 265-309)
"""
self.ensure_one()
if not self.knowledge_file_ids:
documentation_intelligence.py (Lines 68-76)
"""Get SAM AI base path"""
# Traverse up from this module to ai_sam_odoo directory
current_file = os.path.abspath(__file__)
documentation_intelligence.py (Lines 79-105)
"""
base_path = self._get_base_path()
modules = []
documentation_intelligence.py (Lines 138-140)
Scan models/*.py for Odoo model definitions (_name = 'model.name')
Returns list of model names
"""
documentation_intelligence.py (Lines 284-294)
"""Load previous state from JSON file"""
if os.path.exists(self.state_file_path):
try:
documentation_intelligence.py (Lines 459-464)
""")
return analysis
def action_view_report(self):
ai_sam_members (1 blocks)
portal.py (Lines 123-140)
"""SAM AI Chat Portal (paid members only)"""
user = request.env.user
# Check if user is paid member
ai_sam_memory (11 blocks)
memory_graph_controller.py (Lines 16-19)
"""
Get graph visualization data from ai.conversation
Returns nodes and edges for vis.js rendering
ai_conversation_import.py (Lines 153-165)
"""Auto-detect if path is file or directory"""
for record in self:
if record.source_path and os.path.exists(record.source_path):
ai_conversation_import.py (Lines 639-651)
"""Create ai.conversation record"""
# Claude export uses 'name', fallback to 'title' for other formats
conv_name = conv_data.get('name') or conv_data.get('title') or 'Untitled Conversation'
ai_document_extractor.py (Lines 36-50)
"""Register built-in extractors on first use"""
if AIDocumentExtractor._initialized:
return
ai_document_extractor.py (Lines 96-102)
"""
cls._extractors[extension.lower()] = handler
_logger.debug(f"Registered extractor for {extension}")
ai_document_extractor.py (Lines 220-227)
"""Extract JSON files"""
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
ai_document_extractor.py (Lines 237-251)
"""Extract PDF files"""
try:
import PyPDF2
ai_document_extractor.py (Lines 261-278)
"""Extract Excel files"""
try:
import openpyxl
ai_document_extractor.py (Lines 396-397)
"""
prompt = f"""Generate a Python function to extract content from {extension} files.
ai_document_extractor.py (Lines 411-428)
"""
conversation = self.env['ai.conversation'].create({
'name': f'Extractor Generation: {extension}'
ai_graph_service.py (Lines 242-246)
"""
Get the graph structure for a conversation (nodes + relationships)
Returns visualization data for the conversation and its connections
ai_sam_messenger (1 blocks)
messenger_toggle.js (Lines 1-7)
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { FormController } from "@web/views/form/form_controller";
ai_sam_ui (1 blocks)
000.js (Lines 1-6)
/** @odoo-module **/
import publicWidget from "@web/legacy/js/public/public_widget";
import { rpc } from "@web/core/network/rpc";
ai_sam_workflows (28 blocks)
branch_api.py (Lines 22-26)
"""API endpoints for branch/canvas type selection"""
@http.route('/canvas/api/branches/available', type='http', auth='user', methods=['GET'])
def get_available_branches(self, **kwargs):
documentation_controller.py (Lines 68-107)
"""Open file directly in browser"""
try:
doc = request.env['ai.automator.documentation'].browse(doc_id)
documentation_controller.py (Lines 139-145)
"""Get full path to documentation file"""
from odoo.modules.module import get_module_path
module_path = Path(get_module_path('the_ai_automator'))
transition_control.py (Lines 172-175)
"""
Save/update canvas connections for a workflow
JSON API endpoint for AJAX calls
transition_control.py (Lines 216-219)
"""
Load canvas connections for a workflow
JSON API endpoint for AJAX calls
transition_control.py (Lines 348-351)
"""
Get available Odoo apps for canvas menu dropdown
Returns simplified menu data for Service Bridge
transition_control.py (Lines 422-425)
"""
Get N8N nodes from n8n.simple.node model grouped by UI placement
HTTP API endpoint for the node selector overlay - Uses new simplified model
transition_control.py (Lines 570-573)
"""
Get operations for a specific n8n.simple.node
Returns triggers/actions parsed from Description.js files on-demand
transition_control.py (Lines 788-792)
"""
🎯 JSON Node Structure API - Get triggers and actions for nodes with has_node_json=True
Called when user clicks a parent node that has actual .node.json files
transition_control.py (Lines 1012-1015)
"""
📄 Get metadata for a single N8N node
Used for detailed node information on-demand
transition_control.py (Lines 1062-1066)
"""
📁 Get L1 children for hierarchical nodes like Google, Microsoft
Called when user clicks a parent node that has sub-folders (has_node_json=False)
connection_system.js (Lines 713-727)
/**
* Find node by name (for loading connections from database)
*/
FunctionItem.node.js (Lines 84-91)
/** @deprecated for removal - replaced by getBinaryDataAsync() */
getBinaryData: () => {
if (mode === 'manual') {
GoogleSheet.js (Lines 111-133)
/**
* Returns the given sheet data in a structured way
*/
Onfleet.js (Lines 62-63)
/* -------------------------------------------------------------------------- */
/* Get fields for create and update a destination */
Onfleet.js (Lines 106-107)
/* -------------------------------------------------------------------------- */
/* Get fields for create admin */
Onfleet.js (Lines 119-120)
/* Get fields for update admin */
/* -------------------------------------------------------------------------- */
Onfleet.js (Lines 140-141)
/* Get fields for create hub */
/* -------------------------------------------------------------------------- */
Onfleet.js (Lines 151-152)
/* -------------------------------------------------------------------------- */
/* Get fields for update hub */
Onfleet.js (Lines 175-176)
/* -------------------------------------------------------------------------- */
/* Get fields for create worker */
Onfleet.js (Lines 194-195)
/* Get fields for update worker */
/* -------------------------------------------------------------------------- */
Onfleet.js (Lines 218-219)
/* -------------------------------------------------------------------------- */
/* Get fields for get and getAll workers */
Onfleet.js (Lines 269-270)
/* Get fields for create webhook */
/* -------------------------------------------------------------------------- */
Onfleet.js (Lines 376-391)
/* -------------------------------------------------------------------------- */
const updateFields = this.getNodeParameter('updateFields', item);
const taskData = {};
Onfleet.js (Lines 426-450)
/* -------------------------------------------------------------------------- */
const filters = this.getNodeParameter('filters', item);
const listTaskData = {};
Onfleet.js (Lines 497-498)
/* Get driver time estimates for tasks that haven't been created yet */
/* -------------------------------------------------------------------------- */
Onfleet.js (Lines 1181-1182)
/* -------------------------------------------------------------------------- */
/* Get driver time estimates for tasks that haven't been created yet */
utils.js (Lines 40-51)
/** Parses the given value in a number if it is one else returns a string */
function getParsedValue(value) {
if (value.match(/^[\d\.]+$/) === null) {
github_app (5 blocks)
github_view_app.py (Lines 5-5)
# from github import Github
github_view_app.py (Lines 50-50)
# print(repo.remotes.origin.pull())
github_view_app.py (Lines 52-52)
# print(repo.remotes.origin.push())
github_view_settings.py (Lines 103-103)
# print(repo.remotes.origin.pull())
github_view_settings.py (Lines 105-105)
# print(repo.remotes.origin.push())
📈 Statistics Summary
Code Distribution by File Type
- .js: 2671 files, 567,482 lines
- .json: 1332 files, 87,227 lines
- .py: 129 files, 26,292 lines
- .html: 12 files, 10,326 lines
- .xml: 81 files, 9,440 lines
- .css: 14 files, 5,642 lines
- .scss: 3 files, 1,310 lines
- .sql: 4 files, 1,272 lines
🎯 Key Insights
Codebase Health
- Code Density: 167.0 lines per file (average)
- Comment Ratio: 21.6% of lines are comments
- Cleanup Opportunity: 176 commented code blocks identified for review
Agent Knowledge Base
- Knowledge Density: 1687 words per file (average)
- Total Knowledge: 104,579 words of specialized agent instructions
- Agents per Module: 1.00 (agent-to-module ratio)
Report Generated by SAM AI Ecosystem Analyzer
Tool Location: C:\Working With AI\ai_sam\ai_toolbox\ecosystem_analyzer.py