1
1
#!/usr/bin/env python3
2
2
"""
3
- Research Finder CLI - Initial Skeleton
4
- """
3
+ Research Finder CLI - A tool to research topics or questions using Perplexity's Sonar API.
4
+ """
5
5
6
6
import argparse
7
- import sys
7
+ import json
8
8
import os
9
+ import sys
9
10
from pathlib import Path
10
- from typing import Optional , Dict , Any # Added imports
11
+ from typing import Dict , Optional , Any
12
+
13
+ import requests # Added requests import
14
+ from requests .exceptions import RequestException
15
+
16
+ class ResearchAssistant :
17
+ """A class to interact with Perplexity Sonar API for research."""
18
+
19
+ API_URL = "https://api.perplexity.ai/chat/completions"
20
+ DEFAULT_MODEL = "sonar-pro"
21
+ PROMPT_FILE = "system_prompt.md" # Default prompt filename
22
+
23
+ def __init__ (self , api_key : Optional [str ] = None , prompt_file : Optional [str ] = None ):
24
+ """
25
+ Initialize the ResearchAssistant with API key and system prompt.
26
+ """
27
+ self .api_key = api_key or self ._get_api_key ()
28
+ if not self .api_key :
29
+ raise ValueError (
30
+ "API key not found. Please provide via argument, environment variable (PPLX_API_KEY), or key file."
31
+ )
32
+
33
+ script_dir = Path (__file__ ).parent
34
+ prompt_path = Path (prompt_file ) if prompt_file else script_dir / self .PROMPT_FILE
35
+ if not prompt_path .is_absolute () and prompt_file :
36
+ prompt_path = Path .cwd () / prompt_file
37
+ elif not prompt_path .is_absolute ():
38
+ prompt_path = script_dir / self .PROMPT_FILE
39
+
40
+ self .system_prompt = self ._load_system_prompt (prompt_path )
41
+ print (f"System prompt loaded from: { prompt_path } " , file = sys .stderr ) # Debug print
11
42
12
- def research_topic (query : str , model : str ):
13
- """Placeholder for research function."""
14
- print (f"Researching: '{ query } ' using model '{ model } '..." )
15
- # TODO: Implement API call
16
- return {"summary" : "Placeholder summary" , "sources" : ["placeholder source" ]}
43
+ def _get_api_key (self ) -> str :
44
+ """
45
+ Try to get API key from environment or from a file.
46
+ """
47
+ api_key = os .environ .get ("PPLX_API_KEY" , "" )
48
+ if api_key :
49
+ return api_key
17
50
18
- def display_results (results : dict ):
51
+ search_dirs = [Path .cwd (), Path (__file__ ).parent ]
52
+ key_filenames = ["pplx_api_key" , ".pplx_api_key" , "PPLX_API_KEY" , ".PPLX_API_KEY" ]
53
+
54
+ for directory in search_dirs :
55
+ for key_file in key_filenames :
56
+ key_path = directory / key_file
57
+ if key_path .exists ():
58
+ try :
59
+ return key_path .read_text ().strip ()
60
+ except Exception :
61
+ pass
62
+ return ""
63
+
64
+ def _load_system_prompt (self , prompt_path : Path ) -> str :
65
+ """
66
+ Load the system prompt from a file.
67
+ """
68
+ try :
69
+ with open (prompt_path , 'r' , encoding = 'utf-8' ) as f :
70
+ return f .read ().strip ()
71
+ except FileNotFoundError :
72
+ print (f"Warning: Prompt file not found at { prompt_path } " , file = sys .stderr )
73
+ except Exception as e :
74
+ print (f"Warning: Could not load system prompt from { prompt_path } : { e } " , file = sys .stderr )
75
+
76
+ print ("Using fallback default system prompt." , file = sys .stderr )
77
+ return (
78
+ "You are an AI research assistant. Your task is to research the user's query, "
79
+ "provide a concise summary, and list the sources used."
80
+ )
81
+
82
+ def research_topic (self , query : str , model : str = DEFAULT_MODEL ) -> Dict [str , Any ]:
83
+ """Placeholder for research function."""
84
+ print (f"Researching (placeholder): '{ query } ' using model '{ model } '..." )
85
+ # TODO: Implement actual API call here
86
+ return {"summary" : "Placeholder summary" , "sources" : ["placeholder source" ], "raw_response" : "" }
87
+
88
+
89
+ def display_results (results : Dict [str , Any ]):
19
90
"""Placeholder for displaying results."""
20
91
print ("\n --- Results ---" )
21
92
print (f"Summary: { results .get ('summary' )} " )
@@ -26,14 +97,27 @@ def main():
26
97
"""Main entry point."""
27
98
parser = argparse .ArgumentParser (description = "Research Finder CLI" )
28
99
parser .add_argument ("query" , type = str , help = "The research question or topic." )
29
- parser .add_argument ("-m" , "--model" , type = str , default = "sonar-pro" , help = "Perplexity model to use." )
30
- # TODO: Add API key, prompt file, JSON args later
100
+ parser .add_argument ("-m" , "--model" , type = str , default = ResearchAssistant .DEFAULT_MODEL , help = f"Perplexity model (default: { ResearchAssistant .DEFAULT_MODEL } )" )
101
+ parser .add_argument ("-k" , "--api-key" , type = str , help = "Perplexity API key (overrides env var/file)" )
102
+ parser .add_argument ("-p" , "--prompt-file" , type = str , help = f"Path to system prompt (default: { ResearchAssistant .PROMPT_FILE } )" )
103
+ # TODO: Add JSON output arg later
31
104
32
105
args = parser .parse_args ()
33
106
34
- print (f"Query: { args .query } " , file = sys .stderr )
35
- results = research_topic (args .query , args .model )
36
- display_results (results )
107
+ try :
108
+ print (f"Initializing research assistant for query: \" { args .query } \" " , file = sys .stderr )
109
+ assistant = ResearchAssistant (api_key = args .api_key , prompt_file = args .prompt_file )
110
+
111
+ print ("Researching in progress..." , file = sys .stderr )
112
+ results = assistant .research_topic (args .query , model = args .model )
113
+ display_results (results )
114
+
115
+ except ValueError as e : # Catch API key error
116
+ print (f"Configuration Error: { e } " , file = sys .stderr )
117
+ sys .exit (1 )
118
+ except Exception as e :
119
+ print (f"An unexpected error occurred in main: { e } " , file = sys .stderr )
120
+ sys .exit (1 )
37
121
38
122
sys .exit (0 )
39
123
0 commit comments