cite-agent 1.0.5__py3-none-any.whl → 1.2.3__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.
Potentially problematic release.
This version of cite-agent might be problematic. Click here for more details.
- cite_agent/cli.py +374 -39
- cite_agent/cli_workflow.py +276 -0
- cite_agent/enhanced_ai_agent.py +527 -80
- cite_agent/session_manager.py +215 -0
- cite_agent/updater.py +50 -17
- cite_agent/workflow.py +427 -0
- cite_agent/workflow_integration.py +275 -0
- cite_agent-1.2.3.dist-info/METADATA +442 -0
- {cite_agent-1.0.5.dist-info → cite_agent-1.2.3.dist-info}/RECORD +13 -9
- cite_agent-1.0.5.dist-info/METADATA +0 -235
- {cite_agent-1.0.5.dist-info → cite_agent-1.2.3.dist-info}/WHEEL +0 -0
- {cite_agent-1.0.5.dist-info → cite_agent-1.2.3.dist-info}/entry_points.txt +0 -0
- {cite_agent-1.0.5.dist-info → cite_agent-1.2.3.dist-info}/licenses/LICENSE +0 -0
- {cite_agent-1.0.5.dist-info → cite_agent-1.2.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enhanced CLI with workflow integration features
|
|
3
|
+
Reduces context switching for scholars
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Any, Optional
|
|
11
|
+
try:
|
|
12
|
+
import structlog
|
|
13
|
+
except ImportError:
|
|
14
|
+
import logging
|
|
15
|
+
structlog = logging
|
|
16
|
+
|
|
17
|
+
from .enhanced_ai_agent import EnhancedNocturnalAgent, ChatRequest
|
|
18
|
+
from .workflow_integration import WorkflowIntegration
|
|
19
|
+
|
|
20
|
+
logger = structlog.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
class WorkflowCLI:
|
|
23
|
+
"""Enhanced CLI with workflow integration"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self.agent = None
|
|
27
|
+
self.workflow = WorkflowIntegration()
|
|
28
|
+
self.session_id = f"workflow_{os.getpid()}"
|
|
29
|
+
|
|
30
|
+
async def initialize(self):
|
|
31
|
+
"""Initialize the agent and workflow"""
|
|
32
|
+
self.agent = EnhancedNocturnalAgent()
|
|
33
|
+
await self.agent.initialize()
|
|
34
|
+
|
|
35
|
+
async def close(self):
|
|
36
|
+
"""Clean up resources"""
|
|
37
|
+
if self.agent:
|
|
38
|
+
await self.agent.close()
|
|
39
|
+
|
|
40
|
+
async def search_and_save(self, query: str, user_id: str = "default") -> Dict[str, Any]:
|
|
41
|
+
"""Search papers and save to library"""
|
|
42
|
+
try:
|
|
43
|
+
# Search for papers
|
|
44
|
+
request = ChatRequest(
|
|
45
|
+
question=f"Find academic papers about: {query}",
|
|
46
|
+
user_id=user_id,
|
|
47
|
+
conversation_id=self.session_id
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
response = await self.agent.process_request(request)
|
|
51
|
+
|
|
52
|
+
# Extract papers from response
|
|
53
|
+
papers = self._extract_papers_from_response(response)
|
|
54
|
+
|
|
55
|
+
# Save papers to library
|
|
56
|
+
saved_papers = []
|
|
57
|
+
for paper in papers:
|
|
58
|
+
paper_id = self.workflow.save_paper_to_library(paper, user_id)
|
|
59
|
+
saved_papers.append({
|
|
60
|
+
"id": paper_id,
|
|
61
|
+
"title": paper.get("title", "Unknown Title")
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
# Save session
|
|
65
|
+
session_id = self.workflow.save_session_history(
|
|
66
|
+
user_id,
|
|
67
|
+
query,
|
|
68
|
+
{
|
|
69
|
+
"response": response.response,
|
|
70
|
+
"papers": papers,
|
|
71
|
+
"tools_used": response.tools_used
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
"success": True,
|
|
77
|
+
"papers_found": len(papers),
|
|
78
|
+
"papers_saved": len(saved_papers),
|
|
79
|
+
"session_id": session_id,
|
|
80
|
+
"saved_papers": saved_papers
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
except Exception as e:
|
|
84
|
+
logger.error("Error in search_and_save", error=str(e))
|
|
85
|
+
return {"success": False, "error": str(e)}
|
|
86
|
+
|
|
87
|
+
def _extract_papers_from_response(self, response) -> List[Dict[str, Any]]:
|
|
88
|
+
"""Extract paper data from agent response"""
|
|
89
|
+
papers = []
|
|
90
|
+
|
|
91
|
+
# Try to extract from execution_results
|
|
92
|
+
if hasattr(response, 'execution_results') and response.execution_results:
|
|
93
|
+
for result in response.execution_results.values():
|
|
94
|
+
if isinstance(result, dict) and 'papers' in result:
|
|
95
|
+
papers.extend(result['papers'])
|
|
96
|
+
|
|
97
|
+
# Try to extract from api_results
|
|
98
|
+
if hasattr(response, 'api_results') and response.api_results:
|
|
99
|
+
for result in response.api_results.values():
|
|
100
|
+
if isinstance(result, dict) and 'papers' in result:
|
|
101
|
+
papers.extend(result['papers'])
|
|
102
|
+
|
|
103
|
+
return papers
|
|
104
|
+
|
|
105
|
+
async def export_library(self, user_id: str, format: str = "bibtex") -> str:
|
|
106
|
+
"""Export user's library in specified format"""
|
|
107
|
+
try:
|
|
108
|
+
library = self.workflow.get_user_library(user_id)
|
|
109
|
+
papers = [paper_data.get('paper', {}) for paper_data in library]
|
|
110
|
+
|
|
111
|
+
if format == "bibtex":
|
|
112
|
+
filename = f"library_{user_id}.bib"
|
|
113
|
+
file_path = self.workflow.export_to_bibtex(papers, filename)
|
|
114
|
+
elif format == "markdown":
|
|
115
|
+
filename = f"library_{user_id}.md"
|
|
116
|
+
file_path = self.workflow.export_to_markdown(papers, filename)
|
|
117
|
+
else:
|
|
118
|
+
raise ValueError(f"Unsupported format: {format}")
|
|
119
|
+
|
|
120
|
+
return file_path
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error("Error exporting library", error=str(e))
|
|
124
|
+
raise
|
|
125
|
+
|
|
126
|
+
def search_library(self, user_id: str, query: str) -> List[Dict[str, Any]]:
|
|
127
|
+
"""Search user's saved library"""
|
|
128
|
+
return self.workflow.search_library(user_id, query)
|
|
129
|
+
|
|
130
|
+
def get_library_stats(self, user_id: str) -> Dict[str, Any]:
|
|
131
|
+
"""Get library statistics"""
|
|
132
|
+
library = self.workflow.get_user_library(user_id)
|
|
133
|
+
sessions = self.workflow.get_session_history(user_id, 100)
|
|
134
|
+
|
|
135
|
+
# Calculate stats
|
|
136
|
+
total_papers = len(library)
|
|
137
|
+
|
|
138
|
+
# Papers by year
|
|
139
|
+
papers_by_year = {}
|
|
140
|
+
for paper_data in library:
|
|
141
|
+
paper = paper_data.get('paper', {})
|
|
142
|
+
year = paper.get('year', 'Unknown')
|
|
143
|
+
papers_by_year[year] = papers_by_year.get(year, 0) + 1
|
|
144
|
+
|
|
145
|
+
# Most used tools
|
|
146
|
+
tools_used = {}
|
|
147
|
+
for session in sessions:
|
|
148
|
+
for tool in session.get('tools_used', []):
|
|
149
|
+
tools_used[tool] = tools_used.get(tool, 0) + 1
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
"total_papers": total_papers,
|
|
153
|
+
"total_sessions": len(sessions),
|
|
154
|
+
"papers_by_year": papers_by_year,
|
|
155
|
+
"most_used_tools": dict(sorted(tools_used.items(), key=lambda x: x[1], reverse=True)[:5])
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async def get_citation_suggestions(self, paper: Dict[str, Any]) -> List[str]:
|
|
159
|
+
"""Get citation suggestions for a paper"""
|
|
160
|
+
return self.workflow.generate_citation_suggestions(paper)
|
|
161
|
+
|
|
162
|
+
def get_session_history(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
|
|
163
|
+
"""Get recent session history"""
|
|
164
|
+
return self.workflow.get_session_history(user_id, limit)
|
|
165
|
+
|
|
166
|
+
async def interactive_workflow(self, user_id: str = "default"):
|
|
167
|
+
"""Interactive workflow mode"""
|
|
168
|
+
print("🔬 Cite-Agent Workflow Mode")
|
|
169
|
+
print("=" * 50)
|
|
170
|
+
print("Commands:")
|
|
171
|
+
print(" search <query> - Search and save papers")
|
|
172
|
+
print(" library - Show saved papers")
|
|
173
|
+
print(" export <format> - Export library (bibtex/markdown)")
|
|
174
|
+
print(" stats - Show library statistics")
|
|
175
|
+
print(" history - Show recent searches")
|
|
176
|
+
print(" suggest <title> - Get citation suggestions")
|
|
177
|
+
print(" quit - Exit workflow mode")
|
|
178
|
+
print()
|
|
179
|
+
|
|
180
|
+
while True:
|
|
181
|
+
try:
|
|
182
|
+
command = input("workflow> ").strip()
|
|
183
|
+
|
|
184
|
+
if command.lower() in ['quit', 'exit', 'q']:
|
|
185
|
+
break
|
|
186
|
+
elif command.startswith('search '):
|
|
187
|
+
query = command[7:].strip()
|
|
188
|
+
if query:
|
|
189
|
+
result = await self.search_and_save(query, user_id)
|
|
190
|
+
if result['success']:
|
|
191
|
+
print(f"✅ Found {result['papers_found']} papers, saved {result['papers_saved']}")
|
|
192
|
+
else:
|
|
193
|
+
print(f"❌ Error: {result.get('error', 'Unknown error')}")
|
|
194
|
+
else:
|
|
195
|
+
print("❌ Please provide a search query")
|
|
196
|
+
|
|
197
|
+
elif command == 'library':
|
|
198
|
+
library = self.workflow.get_user_library(user_id)
|
|
199
|
+
if library:
|
|
200
|
+
print(f"📚 Library ({len(library)} papers):")
|
|
201
|
+
for i, paper_data in enumerate(library[:10], 1):
|
|
202
|
+
paper = paper_data.get('paper', {})
|
|
203
|
+
title = paper.get('title', 'Unknown Title')[:60]
|
|
204
|
+
year = paper.get('year', 'Unknown')
|
|
205
|
+
print(f" {i}. {title}... ({year})")
|
|
206
|
+
if len(library) > 10:
|
|
207
|
+
print(f" ... and {len(library) - 10} more")
|
|
208
|
+
else:
|
|
209
|
+
print("📚 Library is empty")
|
|
210
|
+
|
|
211
|
+
elif command.startswith('export '):
|
|
212
|
+
format = command[7:].strip().lower()
|
|
213
|
+
if format in ['bibtex', 'markdown']:
|
|
214
|
+
try:
|
|
215
|
+
file_path = await self.export_library(user_id, format)
|
|
216
|
+
print(f"✅ Exported to: {file_path}")
|
|
217
|
+
except Exception as e:
|
|
218
|
+
print(f"❌ Export error: {e}")
|
|
219
|
+
else:
|
|
220
|
+
print("❌ Supported formats: bibtex, markdown")
|
|
221
|
+
|
|
222
|
+
elif command == 'stats':
|
|
223
|
+
stats = self.get_library_stats(user_id)
|
|
224
|
+
print("📊 Library Statistics:")
|
|
225
|
+
print(f" Total papers: {stats['total_papers']}")
|
|
226
|
+
print(f" Total sessions: {stats['total_sessions']}")
|
|
227
|
+
print(" Papers by year:")
|
|
228
|
+
for year, count in sorted(stats['papers_by_year'].items()):
|
|
229
|
+
print(f" {year}: {count}")
|
|
230
|
+
print(" Most used tools:")
|
|
231
|
+
for tool, count in stats['most_used_tools'].items():
|
|
232
|
+
print(f" {tool}: {count}")
|
|
233
|
+
|
|
234
|
+
elif command == 'history':
|
|
235
|
+
history = self.get_session_history(user_id, 5)
|
|
236
|
+
if history:
|
|
237
|
+
print("🕒 Recent Searches:")
|
|
238
|
+
for i, session in enumerate(history, 1):
|
|
239
|
+
query = session.get('query', 'Unknown')[:50]
|
|
240
|
+
timestamp = session.get('timestamp', 'Unknown')[:19]
|
|
241
|
+
papers = session.get('papers_found', 0)
|
|
242
|
+
print(f" {i}. {query}... ({papers} papers) - {timestamp}")
|
|
243
|
+
else:
|
|
244
|
+
print("🕒 No search history")
|
|
245
|
+
|
|
246
|
+
elif command.startswith('suggest '):
|
|
247
|
+
title = command[8:].strip()
|
|
248
|
+
if title:
|
|
249
|
+
# Find paper in library
|
|
250
|
+
library = self.workflow.get_user_library(user_id)
|
|
251
|
+
paper = None
|
|
252
|
+
for paper_data in library:
|
|
253
|
+
if title.lower() in paper_data.get('paper', {}).get('title', '').lower():
|
|
254
|
+
paper = paper_data.get('paper', {})
|
|
255
|
+
break
|
|
256
|
+
|
|
257
|
+
if paper:
|
|
258
|
+
suggestions = await self.get_citation_suggestions(paper)
|
|
259
|
+
print(f"💡 Suggestions for '{paper.get('title', 'Unknown')}':")
|
|
260
|
+
for suggestion in suggestions:
|
|
261
|
+
print(f" • {suggestion}")
|
|
262
|
+
else:
|
|
263
|
+
print(f"❌ Paper not found in library: {title}")
|
|
264
|
+
else:
|
|
265
|
+
print("❌ Please provide a paper title")
|
|
266
|
+
|
|
267
|
+
else:
|
|
268
|
+
print("❌ Unknown command. Type 'quit' to exit.")
|
|
269
|
+
|
|
270
|
+
except KeyboardInterrupt:
|
|
271
|
+
print("\n👋 Goodbye!")
|
|
272
|
+
break
|
|
273
|
+
except Exception as e:
|
|
274
|
+
print(f"❌ Error: {e}")
|
|
275
|
+
|
|
276
|
+
print("👋 Workflow mode ended")
|