ai-logmaster 1.0.0__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.
@@ -0,0 +1,10 @@
1
+ """
2
+ AI LogMaster - Smart Error Analysis with AI-Powered Solutions
3
+ """
4
+
5
+ __version__ = "1.0.0"
6
+ __author__ = "Your Name"
7
+
8
+ from .config import get_config, init_config
9
+
10
+ __all__ = ["get_config", "init_config"]
ai_logmaster/agent.py ADDED
@@ -0,0 +1,356 @@
1
+ """
2
+ LangGraph Agent for Smart Error Analysis
3
+ - Decides when to fetch documentation vs use cached knowledge
4
+ - Optimizes API usage to stay within free quotas
5
+ - Uses DuckDuckGo search for documentation when needed
6
+ """
7
+ import os
8
+ from typing import TypedDict, Annotated, Literal
9
+ from dotenv import load_dotenv
10
+
11
+ load_dotenv()
12
+
13
+ # State definition
14
+ class AgentState(TypedDict):
15
+ error_context: str
16
+ error_type: str
17
+ needs_docs: bool
18
+ documentation: str
19
+ analysis: dict
20
+ api_calls: int
21
+
22
+ def create_agent():
23
+ """Create the LangGraph agent"""
24
+ try:
25
+ from langgraph.graph import StateGraph, END
26
+ from langchain_openai import ChatOpenAI
27
+ from langchain_community.tools import DuckDuckGoSearchRun
28
+ from langchain_core.prompts import ChatPromptTemplate
29
+
30
+ # Initialize LLM (we'll use it sparingly)
31
+ llm = ChatOpenAI(
32
+ model="mistralai/mistral-small-3.1-24b-instruct-2503",
33
+ base_url="https://integrate.api.nvidia.com/v1",
34
+ api_key=os.environ.get("NVIDIA_API_KEY", ""),
35
+ temperature=0.1, # Lower temperature for consistency
36
+ )
37
+
38
+ # Initialize search tool
39
+ search = DuckDuckGoSearchRun()
40
+
41
+ # Node 1: Classify Error (Pattern-based, no API call)
42
+ def classify_error(state: AgentState) -> AgentState:
43
+ """Classify error type using pattern matching (free)"""
44
+ print("[AGENT] Classifying error type...")
45
+
46
+ context = state["error_context"]
47
+ error_type = "unknown"
48
+ needs_docs = False
49
+
50
+ # Pattern-based classification (no API needed)
51
+ patterns = {
52
+ "connection": ["ConnectionRefused", "Connection refused", "ECONNREFUSED"],
53
+ "import": ["ModuleNotFoundError", "ImportError", "No module named"],
54
+ "memory": ["MemoryError", "Out of memory", "OOM"],
55
+ "timeout": ["TimeoutError", "timed out", "Read timed out"],
56
+ "permission": ["PermissionError", "Permission denied"],
57
+ "syntax": ["SyntaxError", "IndentationError"],
58
+ "type": ["TypeError", "AttributeError"],
59
+ "value": ["ValueError", "KeyError"],
60
+ }
61
+
62
+ for err_type, keywords in patterns.items():
63
+ if any(kw in context for kw in keywords):
64
+ error_type = err_type
65
+ # Only fetch docs for complex/uncommon errors
66
+ needs_docs = err_type in ["syntax", "type", "value", "unknown"]
67
+ break
68
+
69
+ state["error_type"] = error_type
70
+ state["needs_docs"] = needs_docs
71
+ state["api_calls"] = 0
72
+
73
+ print(f"[AGENT] Error type: {error_type}, Needs docs: {needs_docs}")
74
+ return state
75
+
76
+ # Node 2: Fetch Documentation (conditional)
77
+ def fetch_documentation(state: AgentState) -> AgentState:
78
+ """Fetch relevant documentation if needed"""
79
+ if not state["needs_docs"]:
80
+ print("[AGENT] Using cached knowledge, skipping doc fetch")
81
+ state["documentation"] = ""
82
+ return state
83
+
84
+ print("[AGENT] Fetching documentation from web...")
85
+
86
+ # Extract error message for search
87
+ lines = state["error_context"].split('\n')
88
+ error_msg = ""
89
+ for line in lines:
90
+ if "Error:" in line or "Exception:" in line:
91
+ error_msg = line.strip()
92
+ break
93
+
94
+ if not error_msg:
95
+ error_msg = f"{state['error_type']} error python"
96
+
97
+ try:
98
+ # Search for documentation
99
+ query = f"{error_msg} official documentation solution"
100
+ docs = search.run(query)
101
+ state["documentation"] = docs[:1000] # Limit to 1000 chars
102
+ print(f"[AGENT] ✓ Fetched {len(docs)} chars of documentation")
103
+ except Exception as e:
104
+ print(f"[AGENT] ✗ Doc fetch failed: {e}")
105
+ state["documentation"] = ""
106
+
107
+ return state
108
+
109
+ # Node 3: Analyze with AI (only if needed)
110
+ def analyze_with_ai(state: AgentState) -> AgentState:
111
+ """Use AI for analysis (costs API calls)"""
112
+ print("[AGENT] Analyzing with AI...")
113
+
114
+ # Build context-aware prompt
115
+ if state["documentation"]:
116
+ prompt_template = """You are a debugging expert. Analyze this error using the documentation.
117
+
118
+ Documentation Context:
119
+ {docs}
120
+
121
+ Error:
122
+ {error}
123
+
124
+ Provide in this EXACT format:
125
+ TYPE: <error type>
126
+ CAUSE: <root cause>
127
+ FIX1: <specific fix>
128
+ FIX2: <alternative fix>
129
+ FIX3: <prevention tip>"""
130
+
131
+ prompt = ChatPromptTemplate.from_template(prompt_template)
132
+ chain = prompt | llm
133
+
134
+ response = chain.invoke({
135
+ "docs": state["documentation"],
136
+ "error": state["error_context"]
137
+ })
138
+ else:
139
+ # Simpler prompt for known errors
140
+ prompt_template = """Analyze this {error_type} error and provide fixes.
141
+
142
+ Error:
143
+ {error}
144
+
145
+ Format:
146
+ TYPE: <type>
147
+ CAUSE: <cause>
148
+ FIX1: <fix>
149
+ FIX2: <fix>
150
+ FIX3: <fix>"""
151
+
152
+ prompt = ChatPromptTemplate.from_template(prompt_template)
153
+ chain = prompt | llm
154
+
155
+ response = chain.invoke({
156
+ "error_type": state["error_type"],
157
+ "error": state["error_context"][:500] # Limit context to save tokens
158
+ })
159
+
160
+ # Parse response
161
+ content = response.content
162
+ analysis = parse_response(content)
163
+ analysis["method"] = "AI + Docs" if state["documentation"] else "AI"
164
+ analysis["confidence"] = 0.90 if state["documentation"] else 0.75
165
+
166
+ state["analysis"] = analysis
167
+ state["api_calls"] += 1
168
+
169
+ print(f"[AGENT] ✓ Analysis complete (API calls: {state['api_calls']})")
170
+ return state
171
+
172
+ # Node 4: Use Cached Solution (no API call)
173
+ def use_cached_solution(state: AgentState) -> AgentState:
174
+ """Use pre-defined solutions for common errors"""
175
+ print("[AGENT] Using cached solution...")
176
+
177
+ solutions = {
178
+ "connection": {
179
+ "type": "Connection Error",
180
+ "cause": "Service is not running or unreachable",
181
+ "fixes": [
182
+ "Check if the service is running",
183
+ "Verify host and port configuration",
184
+ "Check firewall and network settings"
185
+ ]
186
+ },
187
+ "import": {
188
+ "type": "Import Error",
189
+ "cause": "Required package is not installed",
190
+ "fixes": [
191
+ "Install package: pip install <package-name>",
192
+ "Activate virtual environment",
193
+ "Check package name spelling"
194
+ ]
195
+ },
196
+ "memory": {
197
+ "type": "Memory Error",
198
+ "cause": "Insufficient memory available",
199
+ "fixes": [
200
+ "Reduce batch size or data volume",
201
+ "Use generators instead of lists",
202
+ "Increase system memory"
203
+ ]
204
+ },
205
+ "timeout": {
206
+ "type": "Timeout Error",
207
+ "cause": "Operation took too long",
208
+ "fixes": [
209
+ "Increase timeout value",
210
+ "Optimize slow operations",
211
+ "Check network latency"
212
+ ]
213
+ },
214
+ "permission": {
215
+ "type": "Permission Error",
216
+ "cause": "Insufficient file/resource permissions",
217
+ "fixes": [
218
+ "Check file permissions: ls -la",
219
+ "Run with appropriate privileges",
220
+ "Verify user access rights"
221
+ ]
222
+ }
223
+ }
224
+
225
+ error_type = state["error_type"]
226
+ if error_type in solutions:
227
+ state["analysis"] = {
228
+ **solutions[error_type],
229
+ "method": "Cached",
230
+ "confidence": 0.80
231
+ }
232
+ else:
233
+ # Fallback
234
+ state["analysis"] = {
235
+ "type": "Unknown Error",
236
+ "cause": "Unable to classify",
237
+ "fixes": [
238
+ "Read error message carefully",
239
+ "Search error online",
240
+ "Check recent code changes"
241
+ ],
242
+ "method": "Generic",
243
+ "confidence": 0.40
244
+ }
245
+
246
+ print(f"[AGENT] ✓ Using cached solution for {error_type}")
247
+ return state
248
+
249
+ # Decision function: Should we use AI?
250
+ def should_use_ai(state: AgentState) -> Literal["ai", "cached"]:
251
+ """Decide whether to use AI or cached solution"""
252
+ # Use AI only for complex/unknown errors or when docs are available
253
+ if state["needs_docs"] or state["error_type"] == "unknown":
254
+ return "ai"
255
+ return "cached"
256
+
257
+ # Build the graph
258
+ workflow = StateGraph(AgentState)
259
+
260
+ # Add nodes
261
+ workflow.add_node("classify", classify_error)
262
+ workflow.add_node("fetch_docs", fetch_documentation)
263
+ workflow.add_node("ai_analysis", analyze_with_ai)
264
+ workflow.add_node("cached_solution", use_cached_solution)
265
+
266
+ # Add edges
267
+ workflow.set_entry_point("classify")
268
+ workflow.add_edge("classify", "fetch_docs")
269
+ workflow.add_conditional_edges(
270
+ "fetch_docs",
271
+ should_use_ai,
272
+ {
273
+ "ai": "ai_analysis",
274
+ "cached": "cached_solution"
275
+ }
276
+ )
277
+ workflow.add_edge("ai_analysis", END)
278
+ workflow.add_edge("cached_solution", END)
279
+
280
+ # Compile
281
+ app = workflow.compile()
282
+
283
+ print("[AGENT] ✓ LangGraph agent initialized")
284
+ return app
285
+
286
+ except Exception as e:
287
+ print(f"[AGENT] Failed to create agent: {e}")
288
+ return None
289
+
290
+ def parse_response(content: str) -> dict:
291
+ """Parse AI response"""
292
+ import re
293
+
294
+ result = {
295
+ "type": "Unknown",
296
+ "cause": "Not determined",
297
+ "fixes": []
298
+ }
299
+
300
+ for line in content.split('\n'):
301
+ line = line.strip()
302
+ if line.startswith('TYPE:'):
303
+ result["type"] = line.replace('TYPE:', '').strip()
304
+ elif line.startswith('CAUSE:'):
305
+ result["cause"] = line.replace('CAUSE:', '').strip()
306
+ elif line.startswith('FIX'):
307
+ fix = re.sub(r'^FIX\d+:\s*', '', line)
308
+ if fix:
309
+ result["fixes"].append(fix)
310
+
311
+ return result
312
+
313
+ # Global agent instance
314
+ _agent = None
315
+
316
+ def analyze_with_agent(error_context: str) -> dict:
317
+ """Analyze error using LangGraph agent"""
318
+ global _agent
319
+
320
+ if _agent is None:
321
+ _agent = create_agent()
322
+
323
+ if _agent is None:
324
+ # Fallback if agent creation failed
325
+ return {
326
+ "type": "Error",
327
+ "cause": "Agent initialization failed",
328
+ "fixes": ["Check dependencies", "Review error logs"],
329
+ "method": "Fallback",
330
+ "confidence": 0.30
331
+ }
332
+
333
+ # Run the agent
334
+ initial_state = {
335
+ "error_context": error_context,
336
+ "error_type": "",
337
+ "needs_docs": False,
338
+ "documentation": "",
339
+ "analysis": {},
340
+ "api_calls": 0
341
+ }
342
+
343
+ try:
344
+ result = _agent.invoke(initial_state)
345
+ analysis = result["analysis"]
346
+ analysis["api_calls_used"] = result["api_calls"]
347
+ return analysis
348
+ except Exception as e:
349
+ print(f"[AGENT] Execution failed: {e}")
350
+ return {
351
+ "type": "Analysis Error",
352
+ "cause": str(e),
353
+ "fixes": ["Check agent configuration"],
354
+ "method": "Error",
355
+ "confidence": 0.20
356
+ }