Spaces:
Configuration error
Configuration error
| """ | |
| CLI Runner — test the full pipeline without Gradio. | |
| Usage: python run_cli.py --query "federated learning in healthcare" | |
| """ | |
| import argparse | |
| import json | |
| import logging | |
| import sys | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).parent)) | |
| from workflows.langgraph_workflow import run_research_pipeline | |
| from agents.llm_helper import check_ollama_available | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s | %(levelname)s | %(message)s", | |
| datefmt="%H:%M:%S") | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="AI Research Assistant CLI", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| epilog=""" | |
| Examples: | |
| python run_cli.py --query "federated learning in healthcare" | |
| python run_cli.py --query "LLMs for EHR analysis" --year 2022 --max-papers 15 | |
| python run_cli.py --query "medical imaging segmentation" --no-surveys --output results.json | |
| """ | |
| ) | |
| parser.add_argument("--query", required=True, help="Research query") | |
| parser.add_argument("--year", type=int, default=0, help="Filter papers after this year") | |
| parser.add_argument("--max-papers", type=int, default=20, help="Max papers to retrieve") | |
| parser.add_argument("--no-surveys", action="store_true", help="Exclude survey/review papers") | |
| parser.add_argument("--output", default="", help="Save results to JSON file") | |
| parser.add_argument("--verbose", action="store_true", help="Show debug logs") | |
| args = parser.parse_args() | |
| if args.verbose: | |
| logging.getLogger().setLevel(logging.DEBUG) | |
| if not check_ollama_available(): | |
| print("\n⚠️ WARNING: Ollama is not running. LLM features will be degraded.") | |
| print(" Install: curl https://ollama.ai/install.sh | sh") | |
| print(" Pull: ollama pull phi3:mini") | |
| print(" Start: ollama serve\n") | |
| filters = {"max_papers": args.max_papers} | |
| if args.year > 2000: | |
| filters["year_after"] = args.year | |
| if args.no_surveys: | |
| filters["exclude_surveys"] = True | |
| print(f"\n{'='*60}") | |
| print(f" AI Research Assistant — CLI") | |
| print(f"{'='*60}") | |
| print(f" Query: {args.query}") | |
| print(f" Filters: {filters}") | |
| print(f"{'='*60}\n") | |
| state = run_research_pipeline(query=args.query, filters=filters) | |
| ranked = state.get("ranked_papers", []) | |
| metrics = state.get("metrics", {}) | |
| insights = state.get("insights", {}) | |
| errors = state.get("errors", []) | |
| print(f"\n{'='*60}") | |
| print(f" RESULTS") | |
| print(f"{'='*60}") | |
| print(f" Papers retrieved: {metrics.get('papers_retrieved', 0)}") | |
| print(f" Papers ranked: {len(ranked)}") | |
| print(f" Mean score: {metrics.get('score_mean', 0):.3f}") | |
| print(f" Std deviation: {metrics.get('score_std', 0):.3f}") | |
| print(f" Pipeline time: {metrics.get('query_time_sec', 0):.1f}s") | |
| if errors: | |
| print(f"\n ⚠️ Errors ({len(errors)}):") | |
| for e in errors: | |
| print(f" - {e}") | |
| if ranked: | |
| print(f"\n Top 5 Papers:") | |
| print(f" {'─'*55}") | |
| for i, p in enumerate(ranked[:5], 1): | |
| print(f" {i}. [{p['final_score']:.3f}] {p['title'][:65]}") | |
| print(f" Year: {p.get('published','')[:4]} | " | |
| f"Citations: {p.get('citation_count', 0)} | " | |
| f"Venue: {p.get('venue', 'arXiv')[:25]}") | |
| sb = p.get("score_breakdown", {}) | |
| print(f" Scores → " | |
| f"Citation:{sb.get('citation_score',0):.2f} " | |
| f"Recency:{sb.get('recency_score',0):.2f} " | |
| f"Venue:{sb.get('venue_score',0):.2f} " | |
| f"LLM:{sb.get('llm_quality_score',0):.2f}") | |
| subtopics = state.get("subtopics", []) | |
| if subtopics: | |
| print(f"\n Subtopics identified:") | |
| for t in subtopics: | |
| print(f" • {t}") | |
| if insights.get("research_gaps"): | |
| print(f"\n Research Gaps:") | |
| for line in insights["research_gaps"].split("\n")[:5]: | |
| clean = line.strip().lstrip("•-*0123456789.)> ").strip() | |
| if clean: | |
| print(f" • {clean}") | |
| if insights.get("future_directions"): | |
| print(f"\n Future Directions:") | |
| for line in insights["future_directions"].split("\n")[:4]: | |
| clean = line.strip().lstrip("•-*0123456789.)> ").strip() | |
| if clean: | |
| print(f" → {clean}") | |
| if args.output: | |
| output_data = { | |
| "query": args.query, | |
| "filters": filters, | |
| "metrics": metrics, | |
| "subtopics": state.get("subtopics", []), | |
| "ranked_papers": [ | |
| {k: v for k, v in p.items() if k != "abstract"} | |
| for p in ranked[:20] | |
| ], | |
| "insights": insights, | |
| "knowledge_graph": { | |
| "entities": state.get("knowledge_graph_entities", []), | |
| "edges": state.get("knowledge_graph_edges", []) | |
| } | |
| } | |
| with open(args.output, "w") as f: | |
| json.dump(output_data, f, indent=2) | |
| print(f"\n ✅ Results saved to: {args.output}") | |
| artifacts = state.get("artifacts", {}) | |
| print(f"\n Generated Artifacts:") | |
| for atype in ["report", "bibtex", "related_work"]: | |
| if artifacts.get(atype): | |
| print(f" ✓ {atype}") | |
| print(f" Location: data/artifacts/") | |
| print(f"{'='*60}\n") | |
| if __name__ == "__main__": | |
| main() |