Skip to content

Commit 137e712

Browse files
committed
feat: Implement class structure, API key and prompt loading
1 parent 4738084 commit 137e712

File tree

1 file changed

+99
-15
lines changed

1 file changed

+99
-15
lines changed

sonar-use-cases/research_finder/research_finder.py

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,92 @@
11
#!/usr/bin/env python3
22
"""
3-
Research Finder CLI - Initial Skeleton
4-
"""
3+
Research Finder CLI - A tool to research topics or questions using Perplexity's Sonar API.
4+
"""
55

66
import argparse
7-
import sys
7+
import json
88
import os
9+
import sys
910
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
1142

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
1750

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]):
1990
"""Placeholder for displaying results."""
2091
print("\n--- Results ---")
2192
print(f"Summary: {results.get('summary')}")
@@ -26,14 +97,27 @@ def main():
2697
"""Main entry point."""
2798
parser = argparse.ArgumentParser(description="Research Finder CLI")
2899
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
31104

32105
args = parser.parse_args()
33106

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)
37121

38122
sys.exit(0)
39123

0 commit comments

Comments
 (0)