Skip to Content

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

Was this helpful?