| """ |
| Reasoning Classifier Module |
| Uses DeepSeek-R1 with Chain of Thought for AI intelligence classification |
| """ |
|
|
| import json |
| import asyncio |
| from typing import Dict, List, Optional |
| from datetime import datetime, timedelta |
| from functools import lru_cache |
| import hashlib |
| import re |
|
|
|
|
| class ReasoningClassifier: |
| """Reasoning-based classifier using DeepSeek-R1 with Chain of Thought""" |
| |
| def __init__(self, cache_ttl: int = 86400): |
| self.cache_ttl = cache_ttl |
| self.cache = {} |
| self.system_instruction = ( |
| "You are an AI Research Scientist specializing in AGI, ASI, and ACI taxonomies. " |
| "Your task is to analyze research paper abstracts and categorize them.\n\n" |
| "CRITERIA:\n" |
| "- AGI (Artificial General Intelligence): Focus on cross-domain reasoning, System 2 thinking, and 'generality.'\n" |
| "- ASI (Artificial Superintelligence): Focus on recursive self-improvement, alignment at scale, and superhuman capabilities.\n" |
| "- ACI (Artificial Collective Intelligence): Focus on multi-agent systems, swarm intelligence, and human-AI collaboration.\n" |
| "- Narrow AI: Focus on specific, single-domain optimizations (e.g., just 'faster vision' or 'better LLM weights').\n\n" |
| "OUTPUT FORMAT (JSON):\n" |
| "{\n" |
| ' "category": "AGI | ASI | ACI | Narrow AI",\n' |
| ' "confidence_score": 0-100,\n' |
| ' "analysis": "A brief technical justification of why this fits the category based on architectural depth.",\n' |
| ' "aci_potential": "High/Low"\n' |
| "}\n" |
| ) |
| |
| def _construct_fallback_response(self, content: str) -> Optional[Dict]: |
| """ |
| Construct a fallback response from incomplete JSON by extracting available information |
| |
| Args: |
| content: The content that failed JSON parsing |
| |
| Returns: |
| Dictionary with extracted information or None if extraction fails |
| """ |
| try: |
| |
| category_match = re.search(r'"category"\s*:\s*"([^"]+)"', content) |
| category = category_match.group(1) if category_match else "Unknown" |
| |
| |
| confidence_match = re.search(r'"confidence_score"\s*:\s*(\d+)', content) |
| confidence = int(confidence_match.group(1)) if confidence_match else 50 |
| |
| |
| analysis_match = re.search(r'"analysis"\s*:\s*"([^"]*)', content) |
| analysis = analysis_match.group(1) if analysis_match else "Could not extract full analysis" |
| |
| |
| aci_match = re.search(r'"aci_potential"\s*:\s*"([^"]+)"', content) |
| aci_potential = aci_match.group(1) if aci_match else "Unknown" |
| |
| print(f"DEBUG: Fallback extraction - category: {category}, confidence: {confidence}") |
| |
| return { |
| 'category': category, |
| 'confidence_score': confidence, |
| 'analysis': analysis + " (Extracted from incomplete response)", |
| 'aci_potential': aci_potential, |
| 'model_used': 'deepseek-r1', |
| 'classification_timestamp': datetime.now().isoformat(), |
| 'fallback': True |
| } |
| except Exception as e: |
| print(f"DEBUG: Fallback extraction failed: {e}") |
| return None |
| |
| def _get_cache_key(self, title: str, summary: str) -> str: |
| """Generate cache key from paper content""" |
| content = f"{title}:{summary}" |
| return hashlib.md5(content.encode()).hexdigest() |
| |
| def _get_from_cache(self, cache_key: str) -> Optional[Dict]: |
| """Get classification result from cache if available and not expired""" |
| if cache_key in self.cache: |
| result, timestamp = self.cache[cache_key] |
| if (datetime.now() - timestamp).seconds < self.cache_ttl: |
| return result |
| return None |
| |
| def _set_cache(self, cache_key: str, result: Dict): |
| """Store classification result in cache""" |
| self.cache[cache_key] = (result, datetime.now()) |
| |
| def classify_paper(self, paper_data: Dict, use_cache: bool = True) -> Dict: |
| """ |
| Classify a paper using reasoning-based approach |
| |
| Args: |
| paper_data: Dictionary containing paper information (title, summary, etc.) |
| use_cache: Whether to use cached results if available |
| |
| Returns: |
| Dictionary with classification results |
| """ |
| title = paper_data.get('title', '') |
| summary = paper_data.get('summary', '') |
| |
| |
| cache_key = self._get_cache_key(title, summary) |
| if use_cache: |
| cached_result = self._get_from_cache(cache_key) |
| if cached_result: |
| cached_result['cached'] = True |
| return cached_result |
| |
| |
| result = self._classify_with_reasoning(title, summary) |
| |
| |
| if use_cache: |
| self._set_cache(cache_key, result) |
| |
| result['cached'] = False |
| return result |
| |
| def _classify_with_reasoning(self, title: str, summary: str) -> Dict: |
| """ |
| Perform actual reasoning classification using DeepSeek-R1 |
| |
| Args: |
| title: Paper title |
| summary: Paper summary |
| |
| Returns: |
| Classification result dictionary |
| """ |
| try: |
| from huggingface_hub import InferenceClient |
| import os |
| |
| |
| api_key = os.getenv("HUGGINGFACE_API_KEY") |
| |
| |
| print(f"DEBUG: HUGGINGFACE_API_KEY found: {bool(api_key)}") |
| if not api_key: |
| print("DEBUG: HUGGINGFACE_API_KEY not set - will try without authentication (may fail)") |
| |
| |
| if api_key: |
| client = InferenceClient("deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", token=api_key) |
| else: |
| client = InferenceClient("deepseek-ai/DeepSeek-R1-Distill-Qwen-7B") |
| |
| user_input = f"Analyze this abstract: {title}. {summary}" |
| |
| response = client.chat_completion( |
| messages=[ |
| {"role": "system", "content": self.system_instruction}, |
| {"role": "user", "content": user_input} |
| ], |
| max_tokens=1000, |
| temperature=0.1 |
| ) |
| |
| content = response.choices[0].message.content |
| |
| |
| print(f"DEBUG: Raw model response length: {len(content)}") |
| print(f"DEBUG: Raw response preview: {content[:200]}") |
| |
| |
| try: |
| |
| if "<|begin_of_thought|>" in content and "<|end_of_thought|>" in content: |
| |
| content = content.split("<|end_of_thought|>")[-1].strip() |
| elif "```json" in content: |
| |
| content = content.split("```json")[-1].split("```")[0].strip() |
| elif "```" in content: |
| |
| content = content.split("```")[-1].split("```")[0].strip() |
| |
| elif "{" in content and "}" in content: |
| |
| start_idx = content.find("{") |
| end_idx = content.rfind("}") + 1 |
| if start_idx != -1 and end_idx > start_idx: |
| content = content[start_idx:end_idx] |
| |
| print(f"DEBUG: Content after tag stripping length: {len(content)}") |
| print(f"DEBUG: Content preview: {content[:200]}") |
| except Exception as e: |
| |
| print(f"DEBUG: Tag stripping failed: {e}, using raw content") |
| pass |
| |
| |
| try: |
| result = json.loads(content) |
| except json.JSONDecodeError as e: |
| |
| print(f"DEBUG: Initial JSON parse failed: {e}") |
| print(f"DEBUG: Attempting to extract JSON from mixed content") |
| |
| |
| |
| json_pattern = r'\{(?:[^{}]|(?:\{[^{}]*\}))*\}' |
| matches = re.findall(json_pattern, content, re.DOTALL) |
| |
| if matches: |
| |
| matches.sort(key=len, reverse=True) |
| for match in matches: |
| try: |
| result = json.loads(match) |
| print(f"DEBUG: Successfully extracted JSON from mixed content") |
| print(f"DEBUG: Extracted JSON length: {len(match)}") |
| break |
| except: |
| continue |
| else: |
| |
| print(f"DEBUG: All JSON matches failed, attempting fallback") |
| |
| fallback_result = self._construct_fallback_response(content) |
| if fallback_result: |
| return fallback_result |
| raise e |
| else: |
| print(f"DEBUG: No JSON patterns found in content") |
| |
| fallback_result = self._construct_fallback_response(content) |
| if fallback_result: |
| return fallback_result |
| raise e |
| |
| |
| result['model_used'] = 'deepseek-r1' |
| result['classification_timestamp'] = datetime.now().isoformat() |
| |
| return result |
| |
| except json.JSONDecodeError as e: |
| |
| print(f"DEBUG: Final JSON parsing error: {e}") |
| return { |
| 'category': 'Error', |
| 'confidence_score': 0, |
| 'analysis': f'JSON parsing error: {str(e)}', |
| 'aci_potential': 'Unknown', |
| 'model_used': 'deepseek-r1', |
| 'classification_timestamp': datetime.now().isoformat(), |
| 'error': str(e) |
| } |
| except Exception as e: |
| |
| error_msg = str(e) |
| print(f"DEBUG: General exception: {error_msg}") |
| if "api_key" in error_msg.lower() or "api key" in error_msg.lower(): |
| error_msg = "HUGGINGFACE_API_KEY not configured or invalid. Please configure it in Space Settings or use keyword mode instead." |
| return { |
| 'category': 'Error', |
| 'confidence_score': 0, |
| 'analysis': f'API error: {error_msg}', |
| 'aci_potential': 'Unknown', |
| 'model_used': 'deepseek-r1', |
| 'classification_timestamp': datetime.now().isoformat(), |
| 'error': error_msg |
| } |
| |
| async def classify_paper_async(self, paper_data: Dict, use_cache: bool = True) -> Dict: |
| """ |
| Async version of classify_paper for batch processing |
| |
| Args: |
| paper_data: Dictionary containing paper information |
| use_cache: Whether to use cached results if available |
| |
| Returns: |
| Classification result dictionary |
| """ |
| |
| |
| loop = asyncio.get_event_loop() |
| return await loop.run_in_executor(None, self.classify_paper, paper_data, use_cache) |
| |
| async def batch_classify_async(self, papers: List[Dict], use_cache: bool = True, |
| max_concurrent: int = 5) -> List[Dict]: |
| """ |
| Classify multiple papers asynchronously with concurrency control |
| |
| Args: |
| papers: List of paper dictionaries |
| use_cache: Whether to use cached results |
| max_concurrent: Maximum number of concurrent API calls |
| |
| Returns: |
| List of classification results |
| """ |
| semaphore = asyncio.Semaphore(max_concurrent) |
| |
| async def classify_with_semaphore(paper): |
| async with semaphore: |
| return await self.classify_paper_async(paper, use_cache) |
| |
| tasks = [classify_with_semaphore(paper) for paper in papers] |
| results = await asyncio.gather(*tasks, return_exceptions=True) |
| |
| |
| processed_results = [] |
| for i, result in enumerate(results): |
| if isinstance(result, Exception): |
| processed_results.append({ |
| 'category': 'Error', |
| 'confidence_score': 0, |
| 'analysis': f'Batch processing error: {str(result)}', |
| 'aci_potential': 'Unknown', |
| 'model_used': 'deepseek-r1', |
| 'classification_timestamp': datetime.now().isoformat(), |
| 'error': str(result) |
| }) |
| else: |
| processed_results.append(result) |
| |
| return processed_results |
| |
| def batch_classify(self, papers: List[Dict], use_cache: bool = True) -> List[Dict]: |
| """ |
| Synchronous batch classification |
| |
| Args: |
| papers: List of paper dictionaries |
| use_cache: Whether to use cached results |
| |
| Returns: |
| List of classification results |
| """ |
| results = [] |
| for paper in papers: |
| result = self.classify_paper(paper, use_cache) |
| results.append(result) |
| return results |
| |
| def clear_cache(self): |
| """Clear the classification cache""" |
| self.cache.clear() |
| |
| def get_cache_stats(self) -> Dict: |
| """Get cache statistics""" |
| total_entries = len(self.cache) |
| valid_entries = 0 |
| expired_entries = 0 |
| |
| for cache_key, (result, timestamp) in self.cache.items(): |
| if (datetime.now() - timestamp).seconds < self.cache_ttl: |
| valid_entries += 1 |
| else: |
| expired_entries += 1 |
| |
| return { |
| 'total_entries': total_entries, |
| 'valid_entries': valid_entries, |
| 'expired_entries': expired_entries, |
| 'cache_ttl_hours': self.cache_ttl / 3600 |
| } |
| |
| def get_supported_categories(self) -> List[str]: |
| """Get list of supported classification categories""" |
| return ['AGI', 'ASI', 'ACI', 'Narrow AI', 'Not Related', 'Error'] |
|
|
|
|
| |
| if __name__ == "__main__": |
| classifier = ReasoningClassifier() |
| |
| |
| test_papers = [ |
| { |
| 'title': 'Neural Computers: A New Computing Paradigm', |
| 'summary': 'Researchers propose Neural Computers that unify computation, memory, and I/O in a single learned runtime state, potentially leading to artificial general intelligence.' |
| }, |
| { |
| 'title': 'Multi-Agent Reinforcement Learning for Swarm Coordination', |
| 'summary': 'A novel approach to coordinating large swarms of autonomous agents using decentralized reinforcement learning and emergent collective intelligence.' |
| }, |
| { |
| 'title': 'Image Classification with Deep Learning', |
| 'summary': 'A new approach to image classification using convolutional neural networks.' |
| } |
| ] |
| |
| print("Testing Reasoning Classifier...") |
| for i, paper in enumerate(test_papers, 1): |
| print(f"\nPaper {i}: {paper['title']}") |
| result = classifier.classify_paper(paper) |
| print(f"Category: {result['category']}") |
| print(f"Confidence: {result['confidence_score']}") |
| print(f"Analysis: {result['analysis']}") |
| print(f"ACI Potential: {result['aci_potential']}") |
| print(f"Cached: {result['cached']}") |