import requests import json import logging from typing import Dict, Optional, List logger = logging.getLogger(__name__) class LLMClient: def __init__(self, endpoint: str = 'http://localhost:11434', model: str = 'llama3', use_local: bool = True, lm_studio_host: str = None): self.endpoint = endpoint self.model = model self.use_local = use_local self.lm_studio_endpoints = { 'plato': {'url': 'http://192.168.1.74:1234', 'model': 'openai/gpt-oss-20b'}, 'postgres': {'url': 'http://192.168.1.159:1234', 'model': 'mistralai/devstral-small-2-2512'}, 'local': {'url': 'http://localhost:11434', 'model': 'llama3'} } self.lm_studio_host = lm_studio_host or 'postgres' studio_config = self.lm_studio_endpoints.get(self.lm_studio_host, self.lm_studio_endpoints['postgres']) self.lm_studio_endpoint = studio_config['url'] self.lm_studio_model = studio_config['model'] def summarize(self, text: str, max_length: int = 200) -> Dict: prompt = f"Summarize this concisely in under {max_length} characters:\n\n{text[:3000]}" return self._query(prompt) def extract_topics(self, text: str) -> Dict: prompt = f"Extract 5-10 key topics/tags. Return ONLY comma-separated words:\n\n{text[:3000]}" result = self._query(prompt) if result.get('success'): topics = [t.strip() for t in result['text'].split(',')] result['topics'] = topics[:10] return result def extract_intent(self, text: str) -> Dict: prompt = f"What is the main purpose/intent of this code/document? Answer in 1-2 sentences:\n\n{text[:3000]}" return self._query(prompt) def detect_project_type(self, text: str, file_list: List[str]) -> Dict: files_str = ', '.join(file_list[:20]) prompt = f"Based on these files: {files_str}\nAnd this content:\n{text[:2000]}\n\nWhat type of project is this? (e.g. web app, ml/ai, transcription, data processing, etc.)" return self._query(prompt) def _query(self, prompt: str, timeout: int = 30) -> Dict: try: if self.use_local: response = requests.post( f'{self.endpoint}/api/generate', json={'model': self.model, 'prompt': prompt, 'stream': False}, timeout=timeout ) if response.status_code == 200: data = response.json() return {'success': True, 'text': data.get('response', '').strip()} else: response = requests.post( f'{self.lm_studio_endpoint}/v1/chat/completions', json={ 'model': self.lm_studio_model, 'messages': [{'role': 'user', 'content': prompt}], 'max_tokens': 500, 'temperature': 0.7 }, timeout=timeout ) if response.status_code == 200: data = response.json() return {'success': True, 'text': data['choices'][0]['message']['content'].strip()} return {'success': False, 'error': f'HTTP {response.status_code}'} except requests.Timeout: logger.warning(f'LLM request timeout after {timeout}s') return {'success': False, 'error': 'timeout'} except Exception as e: logger.error(f'LLM query failed: {e}') return {'success': False, 'error': str(e)}