arionxiv 1.0.32__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. arionxiv/__init__.py +40 -0
  2. arionxiv/__main__.py +10 -0
  3. arionxiv/arxiv_operations/__init__.py +0 -0
  4. arionxiv/arxiv_operations/client.py +225 -0
  5. arionxiv/arxiv_operations/fetcher.py +173 -0
  6. arionxiv/arxiv_operations/searcher.py +122 -0
  7. arionxiv/arxiv_operations/utils.py +293 -0
  8. arionxiv/cli/__init__.py +4 -0
  9. arionxiv/cli/commands/__init__.py +1 -0
  10. arionxiv/cli/commands/analyze.py +587 -0
  11. arionxiv/cli/commands/auth.py +365 -0
  12. arionxiv/cli/commands/chat.py +714 -0
  13. arionxiv/cli/commands/daily.py +482 -0
  14. arionxiv/cli/commands/fetch.py +217 -0
  15. arionxiv/cli/commands/library.py +295 -0
  16. arionxiv/cli/commands/preferences.py +426 -0
  17. arionxiv/cli/commands/search.py +254 -0
  18. arionxiv/cli/commands/settings_unified.py +1407 -0
  19. arionxiv/cli/commands/trending.py +41 -0
  20. arionxiv/cli/commands/welcome.py +168 -0
  21. arionxiv/cli/main.py +407 -0
  22. arionxiv/cli/ui/__init__.py +1 -0
  23. arionxiv/cli/ui/global_theme_manager.py +173 -0
  24. arionxiv/cli/ui/logo.py +127 -0
  25. arionxiv/cli/ui/splash.py +89 -0
  26. arionxiv/cli/ui/theme.py +32 -0
  27. arionxiv/cli/ui/theme_system.py +391 -0
  28. arionxiv/cli/utils/__init__.py +54 -0
  29. arionxiv/cli/utils/animations.py +522 -0
  30. arionxiv/cli/utils/api_client.py +583 -0
  31. arionxiv/cli/utils/api_config.py +505 -0
  32. arionxiv/cli/utils/command_suggestions.py +147 -0
  33. arionxiv/cli/utils/db_config_manager.py +254 -0
  34. arionxiv/github_actions_runner.py +206 -0
  35. arionxiv/main.py +23 -0
  36. arionxiv/prompts/__init__.py +9 -0
  37. arionxiv/prompts/prompts.py +247 -0
  38. arionxiv/rag_techniques/__init__.py +8 -0
  39. arionxiv/rag_techniques/basic_rag.py +1531 -0
  40. arionxiv/scheduler_daemon.py +139 -0
  41. arionxiv/server.py +1000 -0
  42. arionxiv/server_main.py +24 -0
  43. arionxiv/services/__init__.py +73 -0
  44. arionxiv/services/llm_client.py +30 -0
  45. arionxiv/services/llm_inference/__init__.py +58 -0
  46. arionxiv/services/llm_inference/groq_client.py +469 -0
  47. arionxiv/services/llm_inference/llm_utils.py +250 -0
  48. arionxiv/services/llm_inference/openrouter_client.py +564 -0
  49. arionxiv/services/unified_analysis_service.py +872 -0
  50. arionxiv/services/unified_auth_service.py +457 -0
  51. arionxiv/services/unified_config_service.py +456 -0
  52. arionxiv/services/unified_daily_dose_service.py +823 -0
  53. arionxiv/services/unified_database_service.py +1633 -0
  54. arionxiv/services/unified_llm_service.py +366 -0
  55. arionxiv/services/unified_paper_service.py +604 -0
  56. arionxiv/services/unified_pdf_service.py +522 -0
  57. arionxiv/services/unified_prompt_service.py +344 -0
  58. arionxiv/services/unified_scheduler_service.py +589 -0
  59. arionxiv/services/unified_user_service.py +954 -0
  60. arionxiv/utils/__init__.py +51 -0
  61. arionxiv/utils/api_helpers.py +200 -0
  62. arionxiv/utils/file_cleanup.py +150 -0
  63. arionxiv/utils/ip_helper.py +96 -0
  64. arionxiv-1.0.32.dist-info/METADATA +336 -0
  65. arionxiv-1.0.32.dist-info/RECORD +69 -0
  66. arionxiv-1.0.32.dist-info/WHEEL +5 -0
  67. arionxiv-1.0.32.dist-info/entry_points.txt +4 -0
  68. arionxiv-1.0.32.dist-info/licenses/LICENSE +21 -0
  69. arionxiv-1.0.32.dist-info/top_level.txt +1 -0
@@ -0,0 +1,366 @@
1
+ # LLM client for AI-powered paper analysis
2
+ from typing import Dict, Any, List, Optional
3
+ import logging
4
+ import json
5
+ import asyncio
6
+ import os
7
+ from datetime import datetime
8
+ from groq import AsyncGroq
9
+ from dotenv import load_dotenv
10
+
11
+ load_dotenv()
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ class UnifiedLLMService:
16
+ """Client for LLM-based paper analysis using Groq"""
17
+
18
+ def __init__(self):
19
+ # Groq LLM configuration - lazy loaded
20
+ self._api_key = None
21
+ self._api_key_checked = False
22
+ self.model = os.getenv("DEFAULT_ANALYSIS_MODEL", "llama-3.3-70b-versatile")
23
+ self.timeout = 60
24
+ self._client = None
25
+ self._client_initialized = False
26
+
27
+ @property
28
+ def api_key(self):
29
+ """Lazy load API key"""
30
+ if not self._api_key_checked:
31
+ self._api_key = os.getenv("GROQ_API_KEY")
32
+ self._api_key_checked = True
33
+ return self._api_key
34
+
35
+ @property
36
+ def client(self):
37
+ """Lazy initialize Groq client"""
38
+ if not self._client_initialized:
39
+ self._client_initialized = True
40
+ if self.api_key:
41
+ try:
42
+ self._client = AsyncGroq(api_key=self.api_key)
43
+ logger.debug("Groq LLM client initialized", extra={"model": self.model})
44
+ except Exception as e:
45
+ logger.error(f"Failed to initialize Groq client: {e}")
46
+ self._client = None
47
+ return self._client
48
+
49
+ async def analyze_paper(self, content: str) -> Dict[str, Any]:
50
+ """Analyze a single paper using Groq LLM"""
51
+ try:
52
+ if not content.strip():
53
+ return {"analysis": "No content provided for analysis"}
54
+
55
+ if not self.client:
56
+ logger.error("LLM client not configured - API key missing")
57
+ raise ValueError("GROQ_API_KEY is not configured. Please set your Groq API key in the .env file to use paper analysis.")
58
+
59
+ # Create comprehensive analysis prompt with enhanced instructions
60
+ from ..prompts import format_prompt
61
+ prompt = format_prompt("enhanced_paper_analysis", content=content)
62
+
63
+ # Make API call to Groq with optimized settings for quality
64
+ response = await self.client.chat.completions.create(
65
+ model=self.model,
66
+ messages=[
67
+ {"role": "system", "content": "You are an elite research analyst known for producing exceptionally thorough, insightful, and high-quality paper analyses. Your analyses are comprehensive, technically precise, and highly valued by researchers worldwide."},
68
+ {"role": "user", "content": prompt}
69
+ ],
70
+ temperature=0.3, # Balanced temperature for quality and creativity
71
+ max_tokens=8000 # Significantly increased token limit for comprehensive analysis
72
+ )
73
+
74
+ # Parse the response
75
+ response_content = response.choices[0].message.content
76
+
77
+ try:
78
+ # Clean response content - remove markdown code blocks
79
+ clean_content = response_content.strip()
80
+ if clean_content.startswith("```json"):
81
+ clean_content = clean_content[7:]
82
+ elif clean_content.startswith("```"):
83
+ clean_content = clean_content[3:]
84
+ if clean_content.endswith("```"):
85
+ clean_content = clean_content[:-3]
86
+ clean_content = clean_content.strip()
87
+
88
+ # Try to parse as JSON
89
+ analysis = json.loads(clean_content)
90
+ logger.info("Successfully analyzed paper with LLM")
91
+ return analysis
92
+ except json.JSONDecodeError:
93
+ # If JSON parsing fails, create structured response from raw text
94
+ logger.warning("LLM response was not valid JSON, creating structured response")
95
+
96
+ # Try to extract meaningful content from the response
97
+ lines = response_content.split('\n')
98
+ summary_lines = []
99
+ key_findings = []
100
+ methodology_lines = []
101
+ current_section = None
102
+
103
+ for line in lines:
104
+ line = line.strip()
105
+ if not line:
106
+ continue
107
+
108
+ # Try to identify sections
109
+ if any(word in line.lower() for word in ['summary', 'abstract', 'overview']):
110
+ current_section = 'summary'
111
+ if ':' in line:
112
+ summary_lines.append(line.split(':', 1)[1].strip())
113
+ continue
114
+ elif any(word in line.lower() for word in ['finding', 'result', 'contribution']):
115
+ current_section = 'findings'
116
+ if ':' in line:
117
+ key_findings.append(line.split(':', 1)[1].strip())
118
+ continue
119
+ elif any(word in line.lower() for word in ['method', 'approach', 'technique']):
120
+ current_section = 'methodology'
121
+ if ':' in line:
122
+ methodology_lines.append(line.split(':', 1)[1].strip())
123
+ continue
124
+
125
+ # Add content based on current section
126
+ if current_section == 'summary' and len(summary_lines) < 3:
127
+ summary_lines.append(line)
128
+ elif current_section == 'findings' and len(key_findings) < 5:
129
+ key_findings.append(line)
130
+ elif current_section == 'methodology' and len(methodology_lines) < 3:
131
+ methodology_lines.append(line)
132
+
133
+ # Fallback if sections not found - use first part as summary
134
+ if not summary_lines:
135
+ summary_lines = lines[:3] if len(lines) >= 3 else lines
136
+ if not key_findings:
137
+ key_findings = lines[3:8] if len(lines) > 3 else ["Analysis completed successfully"]
138
+ if not methodology_lines:
139
+ methodology_lines = lines[8:11] if len(lines) > 8 else ["LLM-based analysis methodology"]
140
+
141
+ return {
142
+ "summary": ' '.join(summary_lines) if summary_lines else "Comprehensive research paper analysis completed.",
143
+ "key_findings": key_findings if key_findings else ["Significant research contributions identified", "Novel methodological approaches presented", "Important findings documented"],
144
+ "methodology": ' '.join(methodology_lines) if methodology_lines else "Advanced analytical methodology applied to research content.",
145
+ "strengths": ["Comprehensive research approach", "Strong methodological foundation", "Clear presentation of results", "Significant contributions to field"],
146
+ "limitations": ["Specific scope limitations may apply", "Further validation may be beneficial", "Future research directions identified"],
147
+ "technical_details": response_content[:500] + "..." if len(response_content) > 500 else response_content,
148
+ "broader_impact": "This research contributes to advancing knowledge in the field with potential applications and future research directions.",
149
+ "confidence_score": 0.75,
150
+ "relevance_tags": ["research", "analysis", "academic"],
151
+ "technical_level": "intermediate",
152
+ "raw_response": response_content
153
+ }
154
+
155
+ except Exception as e:
156
+ logger.error("Paper analysis failed", error=str(e))
157
+ raise
158
+
159
+ async def generate_insights(self, papers: List[Dict[str, Any]]) -> Dict[str, Any]:
160
+ """Generate cross-paper insights using Groq LLM"""
161
+ try:
162
+ if not papers:
163
+ return {"message": "No papers provided for insight generation"}
164
+
165
+ if not self.client:
166
+ logger.error("LLM client not configured - API key missing")
167
+ raise ValueError("GROQ_API_KEY is not configured. Please set your Groq API key in the .env file to generate insights.")
168
+
169
+ # Prepare papers summary for analysis
170
+ papers_summary = []
171
+ for i, paper in enumerate(papers[:10]): # Limit to 10 papers to avoid token limits
172
+ summary = f"Paper {i+1}: {paper.get('title', 'Unknown')} - {paper.get('abstract', 'No abstract')[:200]}..."
173
+ papers_summary.append(summary)
174
+
175
+ from ..prompts import format_prompt
176
+ papers_data = f"Papers analyzed ({len(papers)} total, showing first {min(len(papers), 10)}):\n{chr(10).join(papers_summary)}"
177
+ prompt = format_prompt("enhanced_trend_analysis", papers_data=papers_data)
178
+
179
+ response = await self.client.chat.completions.create(
180
+ model=self.model,
181
+ messages=[
182
+ {"role": "user", "content": prompt}
183
+ ],
184
+ temperature=0.4,
185
+ max_tokens=2500
186
+ )
187
+
188
+ response_content = response.choices[0].message.content
189
+
190
+ try:
191
+ insights = json.loads(response_content)
192
+ logger.info("Successfully generated insights with LLM", paper_count=len(papers))
193
+ return insights
194
+ except json.JSONDecodeError as json_err:
195
+ logger.error("LLM insights response was not valid JSON", error=str(json_err))
196
+ raise ValueError(f"Failed to parse LLM response: {str(json_err)}")
197
+
198
+ except Exception as e:
199
+ logger.error("Insight generation failed", error=str(e))
200
+ raise
201
+
202
+ async def summarize_collection(self, papers: List[Dict[str, Any]]) -> str:
203
+ """Generate a summary of a collection of papers using Groq LLM"""
204
+ try:
205
+ if not papers:
206
+ return "No papers provided for summarization"
207
+
208
+ if not self.client:
209
+ logger.error("LLM client not configured - API key missing")
210
+ raise ValueError("GROQ_API_KEY is not configured. Please set your Groq API key in the .env file to generate summaries.")
211
+
212
+ # Create summary for papers
213
+ papers_info = []
214
+ for paper in papers[:15]: # Limit to avoid token limits
215
+ info = f"- {paper.get('title', 'Unknown')}: {paper.get('abstract', 'No abstract')[:150]}..."
216
+ papers_info.append(info)
217
+
218
+ from ..prompts import format_prompt
219
+ papers_data = chr(10).join(papers_info)
220
+ prompt = format_prompt("paper_summary", papers_data=papers_data)
221
+
222
+ response = await self.client.chat.completions.create(
223
+ model=self.model,
224
+ messages=[
225
+ {"role": "user", "content": prompt}
226
+ ],
227
+ temperature=0.3,
228
+ max_tokens=300
229
+ )
230
+
231
+ summary = response.choices[0].message.content.strip()
232
+ logger.info("Successfully generated collection summary with LLM")
233
+ return summary
234
+
235
+ except Exception as e:
236
+ logger.error("Collection summarization failed", error=str(e))
237
+ return f"Collection of {len(papers)} papers covering diverse topics in machine learning and AI. Summarization failed: {str(e)}"
238
+
239
+ async def generate_research_recommendations(self, user_history: List[Dict[str, Any]]) -> Dict[str, Any]:
240
+ """Generate personalized research recommendations using Groq LLM"""
241
+ try:
242
+ if not user_history:
243
+ return {"message": "No user history available for recommendations"}
244
+
245
+ if not self.client:
246
+ logger.error("LLM client not configured - API key missing")
247
+ raise ValueError("GROQ_API_KEY is not configured. Please set your Groq API key in the .env file to generate recommendations.")
248
+
249
+ # Prepare user history summary
250
+ history_summary = []
251
+ for item in user_history[:10]: # Limit to recent history
252
+ summary = f"- {item.get('title', 'Unknown')}: {item.get('action', 'viewed')} on {item.get('date', 'unknown date')}"
253
+ history_summary.append(summary)
254
+
255
+ from ..prompts import format_prompt
256
+ user_profile = chr(10).join(history_summary)
257
+ prompt = format_prompt("personalized_recommendations",
258
+ user_profile=user_profile,
259
+ recent_activity="See user history above")
260
+
261
+ response = await self.client.chat.completions.create(
262
+ model=self.model,
263
+ messages=[
264
+ {"role": "user", "content": prompt}
265
+ ],
266
+ temperature=0.4,
267
+ max_tokens=1500
268
+ )
269
+
270
+ response_content = response.choices[0].message.content
271
+
272
+ try:
273
+ recommendations = json.loads(response_content)
274
+ logger.info("Successfully generated recommendations with LLM")
275
+ return recommendations
276
+ except json.JSONDecodeError as json_err:
277
+ logger.error("LLM recommendations response was not valid JSON", error=str(json_err))
278
+ raise ValueError(f"Failed to parse LLM response: {str(json_err)}")
279
+
280
+ except Exception as e:
281
+ logger.error("Recommendation generation failed", error=str(e))
282
+ raise
283
+
284
+ async def get_completion(self, prompt: str, temperature: float = 0.7, max_tokens: int = 2500, system_message: str = None) -> Dict[str, Any]:
285
+ """Get completion from LLM for chat purposes with enhanced quality"""
286
+ try:
287
+ if not self.client:
288
+ logger.error("LLM client not configured - API key missing")
289
+ return {
290
+ "success": False,
291
+ "error": "GROQ_API_KEY is not configured. Please set your Groq API key in the .env file.",
292
+ "content": "",
293
+ "model": "none"
294
+ }
295
+
296
+ # Prepare messages with optional system message for better quality
297
+ messages = []
298
+ if system_message:
299
+ messages.append({"role": "system", "content": system_message})
300
+ else:
301
+ # Default system message for research assistance
302
+ messages.append({
303
+ "role": "system",
304
+ "content": "You are an elite AI research assistant with deep expertise in scientific literature. Provide thorough, accurate, and insightful responses. Use markdown formatting (bold, italics, lists, code blocks) for better readability. Be comprehensive yet clear in your explanations."
305
+ })
306
+
307
+ messages.append({"role": "user", "content": prompt})
308
+
309
+ # Make API call to Groq with enhanced settings
310
+ response = await self.client.chat.completions.create(
311
+ model=self.model,
312
+ messages=messages,
313
+ temperature=temperature,
314
+ max_tokens=max_tokens
315
+ )
316
+
317
+ # Extract response content
318
+ content = response.choices[0].message.content
319
+
320
+ return {
321
+ "success": True,
322
+ "content": content,
323
+ "model": self.model,
324
+ "usage": {
325
+ "prompt_tokens": response.usage.prompt_tokens,
326
+ "completion_tokens": response.usage.completion_tokens,
327
+ "total_tokens": response.usage.total_tokens
328
+ }
329
+ }
330
+
331
+ except Exception as e:
332
+ logger.error("LLM completion failed", error=str(e))
333
+ return {
334
+ "success": False,
335
+ "error": str(e),
336
+ "content": "Sorry, I encountered an error while processing your request. Please try again.",
337
+ "model": self.model
338
+ }
339
+
340
+ def configure(self, api_key: str, model: str = None):
341
+ """Configure LLM client with API credentials"""
342
+ self.api_key = api_key
343
+ if model:
344
+ self.model = model
345
+
346
+ # Reinitialize client with new API key
347
+ if self.api_key:
348
+ self.client = AsyncGroq(api_key=self.api_key)
349
+ logger.info("LLM client reconfigured", model=self.model)
350
+
351
+ def get_status(self) -> Dict[str, Any]:
352
+ """Get LLM client status"""
353
+ return {
354
+ "configured": self.api_key is not None and self.client is not None,
355
+ "model": self.model,
356
+ "api_service": "Groq",
357
+ "timeout": self.timeout,
358
+ "has_api_key": self.client is not None
359
+ }
360
+
361
+
362
+ # Global instance
363
+ unified_llm_service = UnifiedLLMService()
364
+ llm_service = unified_llm_service
365
+
366
+ __all__ = ['UnifiedLLMService', 'unified_llm_service', 'llm_service']