{
    "sourceFile": "llm_prototype/llm_classifier.py",
    "activeCommit": 0,
    "commits": [
        {
            "activePatchIndex": 9,
            "patches": [
                {
                    "date": 1745578462882,
                    "content": "Index: \n===================================================================\n--- \n+++ \n"
                },
                {
                    "date": 1745578943977,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -61,47 +61,69 @@\n \r\n     return prompt\r\n \r\n def call_llm_api(prompt, config):\r\n-    \"\"\"Calls the LLM API with the formatted prompt.\"\"\"\r\n+    \"\"\"\r\n+    Calls the LLM API with the formatted prompt.\r\n+    Handles API key, headers, and basic error checking.\r\n+    \"\"\"\r\n     print(f\"Calling LLM API at: {config.LLM_API_ENDPOINT}\")\r\n \r\n     api_key = os.getenv(config.LLM_API_KEY_ENV_VAR)\r\n-    if not api_key and \"openai.com\" in config.LLM_API_ENDPOINT:\r\n-         print(f\"Warning: {config.LLM_API_KEY_ENV_VAR} environment variable not set. API call may fail.\")\r\n-         # For local LLMs (like LM Studio), API key might not be needed.\r\n-         # We'll allow the call to proceed, but warn if it looks like an OpenAI endpoint without a key.\r\n-\r\n     headers = {\r\n         \"Content-Type\": \"application/json\",\r\n     }\r\n+\r\n+    # Add Authorization header if API key is provided\r\n     if api_key:\r\n         headers[\"Authorization\"] = f\"Bearer {api_key}\"\r\n+    elif \"openai.com\" in config.LLM_API_ENDPOINT.lower():\r\n+         print(f\"Warning: {config.LLM_API_KEY_ENV_VAR} environment variable not set for OpenAI endpoint. API call may fail.\")\r\n \r\n+\r\n     payload = {\r\n         \"model\": config.LLM_MODEL_NAME if config.LLM_MODEL_NAME else \"gpt-3.5-turbo\", # Use a default if model name is empty\r\n         \"messages\": [\r\n-            {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, # Basic system message\r\n+            {\"role\": \"system\", \"content\": \"You are a helpful assistant that outputs JSON.\"}, # System message updated\r\n             {\"role\": \"user\", \"content\": prompt}\r\n         ],\r\n-        \"response_format\": {\"type\": \"json_object\"} # Request JSON object output if supported\r\n+        # Request JSON object output if supported by the API\r\n+        # Note: Some local LLMs might not support this.\r\n+        \"response_format\": {\"type\": \"json_object\"}\r\n     }\r\n \r\n     try:\r\n-        # Note: Some local LLMs might not support response_format or specific models.\r\n-        # We might need to adjust payload based on endpoint in the future.\r\n         response = requests.post(config.LLM_API_ENDPOINT, headers=headers, json=payload)\r\n         response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)\r\n-        return response.json()\r\n+\r\n+        # Check if the response is directly a JSON object (as requested by response_format)\r\n+        # Some APIs might return the JSON directly if response_format is supported and successful.\r\n+        try:\r\n+            json_response = response.json()\r\n+            # Check if the JSON response looks like a chat completion response\r\n+            if 'choices' in json_response and len(json_response['choices']) > 0:\r\n+                 return json_response # It's a standard chat completion response\r\n+            else:\r\n+                 # It might be the raw JSON output if the API returned it directly\r\n+                 print(\"Received direct JSON response (not standard chat completion format).\")\r\n+                 # Wrap it in a structure similar to chat completion for consistent processing\r\n+                 return {\"choices\": [{\"message\": {\"content\": json.dumps(json_response)}}]}\r\n+        except json.JSONDecodeError:\r\n+            # If it's not JSON, assume it's plain text content\r\n+            print(\"Received non-JSON response. Treating as plain text.\")\r\n+            return {\"choices\": [{\"message\": {\"content\": response.text}}]}\r\n+\r\n+\r\n     except requests.exceptions.RequestException as e:\r\n         print(f\"Error calling LLM API: {e}\")\r\n         sys.exit(1)\r\n \r\n def extract_json_from_response(response_data):\r\n-    \"\"\"Extracts the JSON list part from the LLM's response.\"\"\"\r\n+    \"\"\"\r\n+    Extracts the JSON list part from the LLM's response content.\r\n+    Handles responses that might include a thinking block before the JSON.\r\n+    \"\"\"\r\n     print(\"Extracting JSON from LLM response...\")\r\n-    # Assuming the LLM response structure is like: <thinking>...</thinking>\\n[...JSON...]\r\n-    # We need to find the start of the JSON list.\r\n \r\n     # Look for the content of the first message from the assistant\r\n     assistant_message_content = \"\"\r\n     if 'choices' in response_data and len(response_data['choices']) > 0:\r\n@@ -110,30 +132,35 @@\n \r\n     if not assistant_message_content:\r\n         print(\"Error: LLM response content is empty or unexpected format.\")\r\n         print(f\"Full response: {response_data}\")\r\n-        sys.exit(1)\r\n+        # Attempt to return empty list for validation to catch this\r\n+        return []\r\n \r\n-    # Find the start of the JSON list (first '[')\r\n+    # Try to find the start of the JSON list.\r\n+    # We'll look for the first '[' character.\r\n     json_start_index = assistant_message_content.find('[')\r\n \r\n     if json_start_index == -1:\r\n-        print(\"Error: Could not find the start of the JSON list in the LLM response.\")\r\n-        print(f\"Response content: {assistant_message_content}\")\r\n-        sys.exit(1)\r\n+        print(\"Error: Could not find the start of the JSON list ('[') in the LLM response content.\")\r\n+        print(f\"Response content snippet: {assistant_message_content[:500]}...\") # Print snippet\r\n+        # Attempt to return empty list for validation to catch this\r\n+        return []\r\n \r\n     json_string = assistant_message_content[json_start_index:]\r\n \r\n+    # Attempt to parse the extracted string as JSON\r\n     try:\r\n-        # Attempt to parse the extracted string as JSON\r\n         parsed_json = json.loads(json_string)\r\n         return parsed_json\r\n     except json.JSONDecodeError as e:\r\n         print(f\"Error: Could not decode extracted JSON from LLM response: {e}\")\r\n-        print(f\"Attempted to parse: {json_string}\")\r\n-        sys.exit(1)\r\n+        print(f\"Attempted to parse (snippet): {json_string[:500]}...\") # Print snippet\r\n+        # Attempt to return empty list for validation to catch this\r\n+        return []\r\n \r\n \r\n+\r\n def validate_llm_output(llm_output, config):\r\n     \"\"\"Validates the structure and content of the parsed LLM output JSON.\"\"\"\r\n     print(\"Validating LLM output...\")\r\n \r\n"
                },
                {
                    "date": 1745579722828,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -90,8 +90,12 @@\n         # Note: Some local LLMs might not support this.\r\n         \"response_format\": {\"type\": \"json_object\"}\r\n     }\r\n \r\n+    print(\"\\n--- JSON Payload Sent to LLM API ---\")\r\n+    print(json.dumps(payload, indent=2))\r\n+    print(\"------------------------------------\\n\")\r\n+\r\n     try:\r\n         response = requests.post(config.LLM_API_ENDPOINT, headers=headers, json=payload)\r\n         response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)\r\n \r\n"
                },
                {
                    "date": 1745580658080,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -87,9 +87,9 @@\n             {\"role\": \"user\", \"content\": prompt}\r\n         ],\r\n         # Request JSON object output if supported by the API\r\n         # Note: Some local LLMs might not support this.\r\n-        \"response_format\": {\"type\": \"json_object\"}\r\n+        # \"response_format\": {\"type\": \"json_object\"} # Removed as per suggestion\r\n     }\r\n \r\n     print(\"\\n--- JSON Payload Sent to LLM API ---\")\r\n     print(json.dumps(payload, indent=2))\r\n"
                },
                {
                    "date": 1745580751639,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -120,12 +120,14 @@\n     except requests.exceptions.RequestException as e:\r\n         print(f\"Error calling LLM API: {e}\")\r\n         sys.exit(1)\r\n \r\n+import re # Import the regex module\r\n+\r\n def extract_json_from_response(response_data):\r\n     \"\"\"\r\n-    Extracts the JSON list part from the LLM's response content.\r\n-    Handles responses that might include a thinking block before the JSON.\r\n+    Extracts the JSON list part from the LLM's response content using regex.\r\n+    Handles responses that might include a thinking block or other text before/after the JSON.\r\n     \"\"\"\r\n     print(\"Extracting JSON from LLM response...\")\r\n \r\n     # Look for the content of the first message from the assistant\r\n@@ -139,19 +141,19 @@\n         print(f\"Full response: {response_data}\")\r\n         # Attempt to return empty list for validation to catch this\r\n         return []\r\n \r\n-    # Try to find the start of the JSON list.\r\n-    # We'll look for the first '[' character.\r\n-    json_start_index = assistant_message_content.find('[')\r\n+    # Use regex to find the first occurrence of a JSON array [...]\r\n+    # This is a basic pattern and might need refinement depending on LLM output variations.\r\n+    json_match = re.search(r'\\[\\s*\\{.*?\\}\\s*\\]', assistant_message_content, re.DOTALL)\r\n \r\n-    if json_start_index == -1:\r\n-        print(\"Error: Could not find the start of the JSON list ('[') in the LLM response content.\")\r\n+    if not json_match:\r\n+        print(\"Error: Could not find a JSON list structure in the LLM response content.\")\r\n         print(f\"Response content snippet: {assistant_message_content[:500]}...\") # Print snippet\r\n         # Attempt to return empty list for validation to catch this\r\n         return []\r\n \r\n-    json_string = assistant_message_content[json_start_index:]\r\n+    json_string = json_match.group(0)\r\n \r\n     # Attempt to parse the extracted string as JSON\r\n     try:\r\n         parsed_json = json.loads(json_string)\r\n@@ -163,8 +165,9 @@\n         return []\r\n \r\n \r\n \r\n+\r\n def validate_llm_output(llm_output, config):\r\n     \"\"\"Validates the structure and content of the parsed LLM output JSON.\"\"\"\r\n     print(\"Validating LLM output...\")\r\n \r\n"
                },
                {
                    "date": 1745581133877,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -84,12 +84,10 @@\n         \"model\": config.LLM_MODEL_NAME if config.LLM_MODEL_NAME else \"gpt-3.5-turbo\", # Use a default if model name is empty\r\n         \"messages\": [\r\n             {\"role\": \"system\", \"content\": \"You are a helpful assistant that outputs JSON.\"}, # System message updated\r\n             {\"role\": \"user\", \"content\": prompt}\r\n-        ],\r\n-        # Request JSON object output if supported by the API\r\n-        # Note: Some local LLMs might not support this.\r\n-        # \"response_format\": {\"type\": \"json_object\"} # Removed as per suggestion\r\n+        ]\r\n+        # Removed \"response_format\": {\"type\": \"json_object\"} as we expect mixed output (text + JSON)\r\n     }\r\n \r\n     print(\"\\n--- JSON Payload Sent to LLM API ---\")\r\n     print(json.dumps(payload, indent=2))\r\n"
                },
                {
                    "date": 1745581427149,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -118,13 +118,12 @@\n     except requests.exceptions.RequestException as e:\r\n         print(f\"Error calling LLM API: {e}\")\r\n         sys.exit(1)\r\n \r\n-import re # Import the regex module\r\n-\r\n def extract_json_from_response(response_data):\r\n     \"\"\"\r\n-    Extracts the JSON list part from the LLM's response content using regex.\r\n+    Extracts the JSON list part from the LLM's response content by finding\r\n+    the first '[' and last ']' and parsing the content between them.\r\n     Handles responses that might include a thinking block or other text before/after the JSON.\r\n     \"\"\"\r\n     print(\"Extracting JSON from LLM response...\")\r\n \r\n@@ -139,19 +138,20 @@\n         print(f\"Full response: {response_data}\")\r\n         # Attempt to return empty list for validation to catch this\r\n         return []\r\n \r\n-    # Use regex to find the first occurrence of a JSON array [...]\r\n-    # This is a basic pattern and might need refinement depending on LLM output variations.\r\n-    json_match = re.search(r'\\[\\s*\\{.*?\\}\\s*\\]', assistant_message_content, re.DOTALL)\r\n+    # Find the index of the first '[' and the last ']'\r\n+    first_bracket_index = assistant_message_content.find('[')\r\n+    last_bracket_index = assistant_message_content.rfind(']')\r\n \r\n-    if not json_match:\r\n-        print(\"Error: Could not find a JSON list structure in the LLM response content.\")\r\n+    if first_bracket_index == -1 or last_bracket_index == -1 or last_bracket_index < first_bracket_index:\r\n+        print(\"Error: Could not find a valid JSON list structure (matching '[' and ']') in the LLM response content.\")\r\n         print(f\"Response content snippet: {assistant_message_content[:500]}...\") # Print snippet\r\n         # Attempt to return empty list for validation to catch this\r\n         return []\r\n \r\n-    json_string = json_match.group(0)\r\n+    # Extract the potential JSON string between the first '[' and last ']'\r\n+    json_string = assistant_message_content[first_bracket_index : last_bracket_index + 1]\r\n \r\n     # Attempt to parse the extracted string as JSON\r\n     try:\r\n         parsed_json = json.loads(json_string)\r\n"
                },
                {
                    "date": 1745583064778,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -261,8 +261,13 @@\n     # print(\"------------------------\\n\")\r\n \r\n     llm_output_json = extract_json_from_response(llm_response_data)\r\n \r\n+    # Add check for empty extracted JSON\r\n+    if not llm_output_json:\r\n+        print(\"\\nError: Extracted JSON list is empty. LLM response or extraction failed.\")\r\n+        sys.exit(1)\r\n+\r\n     if validate_llm_output(llm_output_json, config):\r\n         print(\"\\n--- Validated LLM Output JSON ---\")\r\n         print(json.dumps(llm_output_json, indent=2))\r\n         print(\"---------------------------------\\n\")\r\n"
                },
                {
                    "date": 1745584263659,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -132,8 +132,12 @@\n     if 'choices' in response_data and len(response_data['choices']) > 0:\r\n         message = response_data['choices'][0].get('message', {})\r\n         assistant_message_content = message.get('content', '')\r\n \r\n+    print(\"\\n--- Raw Assistant Message Content ---\")\r\n+    print(assistant_message_content)\r\n+    print(\"-------------------------------------\\n\")\r\n+\r\n     if not assistant_message_content:\r\n         print(\"Error: LLM response content is empty or unexpected format.\")\r\n         print(f\"Full response: {response_data}\")\r\n         # Attempt to return empty list for validation to catch this\r\n"
                },
                {
                    "date": 1745584508971,
                    "content": "Index: \n===================================================================\n--- \n+++ \n@@ -132,11 +132,17 @@\n     if 'choices' in response_data and len(response_data['choices']) > 0:\r\n         message = response_data['choices'][0].get('message', {})\r\n         assistant_message_content = message.get('content', '')\r\n \r\n-    print(\"\\n--- Raw Assistant Message Content ---\")\r\n+    # Strip markdown code fences if present\r\n+    if assistant_message_content.strip().startswith(\"```json\"):\r\n+        assistant_message_content = assistant_message_content.strip()[len(\"```json\"):].strip()\r\n+    if assistant_message_content.strip().endswith(\"```\"):\r\n+        assistant_message_content = assistant_message_content.strip()[:-len(\"```\")].strip()\r\n+\r\n+    print(\"\\n--- Processed Assistant Message Content (after stripping fences) ---\")\r\n     print(assistant_message_content)\r\n-    print(\"-------------------------------------\\n\")\r\n+    print(\"-------------------------------------------------------------------\\n\")\r\n \r\n     if not assistant_message_content:\r\n         print(\"Error: LLM response content is empty or unexpected format.\")\r\n         print(f\"Full response: {response_data}\")\r\n"
                }
            ],
            "date": 1745578462882,
            "name": "Commit-0",
            "content": "# llm_prototype/llm_classifier.py\r\n\r\nimport os\r\nimport json\r\nimport requests\r\nimport sys\r\n\r\n# Add the prototype directory to the Python path to import config_llm\r\nsys.path.append(os.path.dirname(__file__))\r\nimport config_llm\r\n\r\ndef load_config():\r\n    \"\"\"Loads configuration from config_llm.py.\"\"\"\r\n    print(\"Loading configuration...\")\r\n    return config_llm\r\n\r\ndef load_input_files(input_json_path):\r\n    \"\"\"Loads the list of files from an input JSON file.\"\"\"\r\n    print(f\"Loading input file list from: {input_json_path}\")\r\n    try:\r\n        with open(input_json_path, 'r') as f:\r\n            data = json.load(f)\r\n            if \"files\" not in data or not isinstance(data[\"files\"], list):\r\n                raise ValueError(\"Input JSON must contain a 'files' key with a list of strings.\")\r\n            return data[\"files\"]\r\n    except FileNotFoundError:\r\n        print(f\"Error: Input file not found at {input_json_path}\")\r\n        sys.exit(1)\r\n    except json.JSONDecodeError:\r\n        print(f\"Error: Could not decode JSON from {input_json_path}\")\r\n        sys.exit(1)\r\n    except ValueError as e:\r\n        print(f\"Error in input file format: {e}\")\r\n        sys.exit(1)\r\n\r\ndef load_prompt_template(config):\r\n    \"\"\"Loads the prompt template from the specified file.\"\"\"\r\n    print(f\"Loading prompt template from: {config.PROMPT_TEMPLATE_PATH}\")\r\n    try:\r\n        with open(config.PROMPT_TEMPLATE_PATH, 'r', encoding='utf-8') as f:\r\n            return f.read()\r\n    except FileNotFoundError:\r\n        print(f\"Error: Prompt template file not found at {config.PROMPT_TEMPLATE_PATH}\")\r\n        sys.exit(1)\r\n    except Exception as e:\r\n        print(f\"Error loading prompt template: {e}\")\r\n        sys.exit(1)\r\n\r\n\r\ndef format_prompt(template, file_list, config):\r\n    \"\"\"Formats the prompt template with dynamic values.\"\"\"\r\n    print(\"Formatting prompt...\")\r\n    file_list_json = json.dumps(file_list, indent=2)\r\n\r\n    # Replace placeholders in the template\r\n    prompt = template.replace(config.FILE_LIST_PLACEHOLDER, file_list_json)\r\n    prompt = prompt.replace(config.MAP_TYPES_PLACEHOLDER, json.dumps(config.EXPECTED_MAP_TYPES))\r\n    prompt = prompt.replace(config.CATEGORIES_PLACEHOLDER, json.dumps(config.EXPECTED_CATEGORIES))\r\n    prompt = prompt.replace(config.CLASSIFICATIONS_PLACEHOLDER, json.dumps(config.EXPECTED_CLASSIFICATIONS))\r\n    prompt = prompt.replace(config.OUTPUT_SCHEMA_PLACEHOLDER, config.OUTPUT_SCHEMA.strip())\r\n\r\n    return prompt\r\n\r\ndef call_llm_api(prompt, config):\r\n    \"\"\"Calls the LLM API with the formatted prompt.\"\"\"\r\n    print(f\"Calling LLM API at: {config.LLM_API_ENDPOINT}\")\r\n\r\n    api_key = os.getenv(config.LLM_API_KEY_ENV_VAR)\r\n    if not api_key and \"openai.com\" in config.LLM_API_ENDPOINT:\r\n         print(f\"Warning: {config.LLM_API_KEY_ENV_VAR} environment variable not set. API call may fail.\")\r\n         # For local LLMs (like LM Studio), API key might not be needed.\r\n         # We'll allow the call to proceed, but warn if it looks like an OpenAI endpoint without a key.\r\n\r\n    headers = {\r\n        \"Content-Type\": \"application/json\",\r\n    }\r\n    if api_key:\r\n        headers[\"Authorization\"] = f\"Bearer {api_key}\"\r\n\r\n    payload = {\r\n        \"model\": config.LLM_MODEL_NAME if config.LLM_MODEL_NAME else \"gpt-3.5-turbo\", # Use a default if model name is empty\r\n        \"messages\": [\r\n            {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, # Basic system message\r\n            {\"role\": \"user\", \"content\": prompt}\r\n        ],\r\n        \"response_format\": {\"type\": \"json_object\"} # Request JSON object output if supported\r\n    }\r\n\r\n    try:\r\n        # Note: Some local LLMs might not support response_format or specific models.\r\n        # We might need to adjust payload based on endpoint in the future.\r\n        response = requests.post(config.LLM_API_ENDPOINT, headers=headers, json=payload)\r\n        response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)\r\n        return response.json()\r\n    except requests.exceptions.RequestException as e:\r\n        print(f\"Error calling LLM API: {e}\")\r\n        sys.exit(1)\r\n\r\ndef extract_json_from_response(response_data):\r\n    \"\"\"Extracts the JSON list part from the LLM's response.\"\"\"\r\n    print(\"Extracting JSON from LLM response...\")\r\n    # Assuming the LLM response structure is like: <thinking>...</thinking>\\n[...JSON...]\r\n    # We need to find the start of the JSON list.\r\n\r\n    # Look for the content of the first message from the assistant\r\n    assistant_message_content = \"\"\r\n    if 'choices' in response_data and len(response_data['choices']) > 0:\r\n        message = response_data['choices'][0].get('message', {})\r\n        assistant_message_content = message.get('content', '')\r\n\r\n    if not assistant_message_content:\r\n        print(\"Error: LLM response content is empty or unexpected format.\")\r\n        print(f\"Full response: {response_data}\")\r\n        sys.exit(1)\r\n\r\n    # Find the start of the JSON list (first '[')\r\n    json_start_index = assistant_message_content.find('[')\r\n\r\n    if json_start_index == -1:\r\n        print(\"Error: Could not find the start of the JSON list in the LLM response.\")\r\n        print(f\"Response content: {assistant_message_content}\")\r\n        sys.exit(1)\r\n\r\n    json_string = assistant_message_content[json_start_index:]\r\n\r\n    try:\r\n        # Attempt to parse the extracted string as JSON\r\n        parsed_json = json.loads(json_string)\r\n        return parsed_json\r\n    except json.JSONDecodeError as e:\r\n        print(f\"Error: Could not decode extracted JSON from LLM response: {e}\")\r\n        print(f\"Attempted to parse: {json_string}\")\r\n        sys.exit(1)\r\n\r\n\r\ndef validate_llm_output(llm_output, config):\r\n    \"\"\"Validates the structure and content of the parsed LLM output JSON.\"\"\"\r\n    print(\"Validating LLM output...\")\r\n\r\n    if not isinstance(llm_output, list):\r\n        print(\"Validation Error: LLM output is not a JSON list.\")\r\n        return False\r\n\r\n    if not llm_output:\r\n        print(\"Validation Warning: LLM output list is empty.\")\r\n        # Depending on requirements, an empty list might be valid if no assets were found.\r\n        # For now, we'll allow it but warn.\r\n\r\n    required_asset_keys = [\"asset_name\", \"asset_category\", \"asset_archetype\", \"file_classifications\"]\r\n    required_file_keys = [\"input_path\", \"classification\", \"map_type\"]\r\n\r\n    for i, asset in enumerate(llm_output):\r\n        if not isinstance(asset, dict):\r\n            print(f\"Validation Error: Asset item at index {i} is not a dictionary.\")\r\n            return False\r\n\r\n        # Validate required asset keys\r\n        for key in required_asset_keys:\r\n            if key not in asset:\r\n                print(f\"Validation Error: Asset item at index {i} is missing required key: '{key}'.\")\r\n                return False\r\n\r\n        # Validate asset_category value\r\n        if asset[\"asset_category\"] not in config.EXPECTED_CATEGORIES:\r\n             print(f\"Validation Error: Asset item at index {i} has invalid asset_category: '{asset['asset_category']}'. Expected one of: {config.EXPECTED_CATEGORIES}\")\r\n             return False\r\n\r\n        # Validate file_classifications list\r\n        if not isinstance(asset[\"file_classifications\"], list):\r\n            print(f\"Validation Error: 'file_classifications' for asset at index {i} is not a list.\")\r\n            return False\r\n\r\n        for j, file_info in enumerate(asset[\"file_classifications\"]):\r\n            if not isinstance(file_info, dict):\r\n                print(f\"Validation Error: File classification item at asset index {i}, file index {j} is not a dictionary.\")\r\n                return False\r\n\r\n            # Validate required file keys\r\n            for key in required_file_keys:\r\n                if key not in file_info:\r\n                    print(f\"Validation Error: File classification item at asset index {i}, file index {j} is missing required key: '{key}'.\")\r\n                    return False\r\n\r\n            # Validate classification value\r\n            if file_info[\"classification\"] not in config.EXPECTED_CLASSIFICATIONS:\r\n                 print(f\"Validation Error: File classification item at asset index {i}, file index {j} has invalid classification: '{file_info['classification']}'. Expected one of: {config.EXPECTED_CLASSIFICATIONS}\")\r\n                 return False\r\n\r\n            # Validate map_type value if classification is \"Map\"\r\n            if file_info[\"classification\"] == \"Map\":\r\n                if file_info[\"map_type\"] is None or file_info[\"map_type\"] not in config.EXPECTED_MAP_TYPES:\r\n                     print(f\"Validation Error: File classification item at asset index {i}, file index {j} is classified as 'Map' but has invalid or missing map_type: '{file_info['map_type']}'. Expected one of: {config.EXPECTED_MAP_TYPES}\")\r\n                     return False\r\n            elif file_info[\"map_type\"] is not None:\r\n                 print(f\"Validation Warning: File classification item at asset index {i}, file index {j} is not classified as 'Map' but has a map_type: '{file_info['map_type']}'. map_type should be null.\")\r\n                 # This is a warning, not a strict error, as some LLMs might include it.\r\n\r\n            # Validate input_path is a string\r\n            if not isinstance(file_info[\"input_path\"], str) or not file_info[\"input_path\"]:\r\n                 print(f\"Validation Error: File classification item at asset index {i}, file index {j} has invalid or empty input_path: '{file_info['input_path']}'.\")\r\n                 return False\r\n\r\n\r\n    print(\"LLM output validation successful.\")\r\n    return True\r\n\r\n\r\ndef main():\r\n    \"\"\"Main function to run the LLM classifier prototype.\"\"\"\r\n    if len(sys.argv) != 2:\r\n        print(\"Usage: python llm_classifier.py <path_to_input_json>\")\r\n        sys.exit(1)\r\n\r\n    input_json_path = sys.argv[1]\r\n\r\n    config = load_config()\r\n    file_list = load_input_files(input_json_path)\r\n    prompt_template = load_prompt_template(config)\r\n    prompt = format_prompt(prompt_template, file_list, config)\r\n\r\n    # print(\"\\n--- Generated Prompt ---\")\r\n    # print(prompt)\r\n    # print(\"------------------------\\n\")\r\n\r\n    llm_response_data = call_llm_api(prompt, config)\r\n\r\n    # print(\"\\n--- Raw LLM Response ---\")\r\n    # print(json.dumps(llm_response_data, indent=2))\r\n    # print(\"------------------------\\n\")\r\n\r\n    llm_output_json = extract_json_from_response(llm_response_data)\r\n\r\n    if validate_llm_output(llm_output_json, config):\r\n        print(\"\\n--- Validated LLM Output JSON ---\")\r\n        print(json.dumps(llm_output_json, indent=2))\r\n        print(\"---------------------------------\\n\")\r\n        print(\"Prototype run complete. LLM output is valid.\")\r\n    else:\r\n        print(\"\\nPrototype run failed due to LLM output validation errors.\")\r\n        sys.exit(1)\r\n\r\nif __name__ == \"__main__\":\r\n    main()"
        }
    ]
}