ak0601 commited on
Commit
6d90ef6
·
verified ·
1 Parent(s): 8898ccb

Update app/summarizer/groq_client.py

Browse files
Files changed (1) hide show
  1. app/summarizer/groq_client.py +111 -101
app/summarizer/groq_client.py CHANGED
@@ -1,101 +1,111 @@
1
- """
2
- ResearchRadar — Groq LLM summarizer.
3
-
4
- Summarizes papers using Groq API (llama-3.1-8b-instant).
5
- Follows user's requested structural (Idea, Method, Results) and
6
- enforces rate limit delays (30 RPM).
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import logging
12
- import time
13
- from typing import List, Optional
14
-
15
- import requests
16
- from app.core.config import (
17
- GROQ_API_KEY, GROQ_BASE_URL, GROQ_MODEL, GROQ_DELAY
18
- )
19
- from app.core.models import Paper
20
-
21
- logger = logging.getLogger(__name__)
22
-
23
- class GroqSummarizer:
24
- """Handles LLM calls to Groq with rate-limiting and structured prompts."""
25
-
26
- def __init__(self, api_key: str = GROQ_API_KEY):
27
- self.api_key = api_key
28
- self.last_call_time = 0.0
29
-
30
- def summarize_paper(self, paper: Paper) -> Optional[str]:
31
- """
32
- Produce a structured summary of the paper.
33
- Structure:
34
- - Idea: The core core concept.
35
- - Method: The approach or architecture.
36
- - Results: The outcome or findings.
37
- """
38
- if not self.api_key:
39
- logger.info("Skip Groq summarization: NO API KEY.")
40
- return None
41
-
42
- # Prepare prompt
43
- prompt = (
44
- f"Please summarize the following research paper abstract into three brief sections:\n"
45
- f"1. Idea: (The core concept)\n"
46
- f"2. Method: (The proposed approach)\n"
47
- f"3. Results: (Key findings)\n\n"
48
- f"Title: {paper.title}\n"
49
- f"Abstract: {paper.abstract}\n\n"
50
- "Keep it concise and professional. Respond in plain text with those three labels."
51
- )
52
-
53
- # Enforce rate limit delay
54
- elapsed = time.time() - self.last_call_time
55
- if elapsed < GROQ_DELAY:
56
- sleep_time = GROQ_DELAY - elapsed
57
- logger.debug(f"Groq Rate Limit: Sleeping for {sleep_time:.2f}s")
58
- time.sleep(sleep_time)
59
-
60
- try:
61
- logger.info(f"Summarizing paper [{paper.paper_id}] via Groq...")
62
- response = requests.post(
63
- GROQ_BASE_URL,
64
- headers={
65
- "Authorization": f"Bearer {self.api_key}",
66
- "Content-Type": "application/json"
67
- },
68
- json={
69
- "model": GROQ_MODEL,
70
- "messages": [
71
- {"role": "system", "content": "You are a scientific research assistant summarizing papers."},
72
- {"role": "user", "content": prompt}
73
- ],
74
- "temperature": 0.3,
75
- "max_tokens": 300
76
- },
77
- timeout=30
78
- )
79
-
80
- self.last_call_time = time.time()
81
-
82
- if response.status_code == 200:
83
- data = response.json()
84
- summary = data['choices'][0]['message']['content'].strip()
85
- return summary
86
- else:
87
- logger.error(f"Groq API error ({response.status_code}): {response.text}")
88
- return None
89
-
90
- except Exception as exc:
91
- logger.exception(f"Unexpected error during Groq summarization: {exc}")
92
- return None
93
-
94
- def summarize_many(self, papers: List[Paper]):
95
- """
96
- Iterate through papers and update their summary_llm field.
97
- """
98
- for p in papers:
99
- # We only summarize if it doesn't already have a summary
100
- if not p.summary_llm:
101
- p.summary_llm = self.summarize_paper(p)
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ResearchRadar — Groq LLM summarizer.
3
+
4
+ Summarizes papers using Groq API (llama-3.1-8b-instant).
5
+ Follows user's requested structural (Idea, Method, Results) and
6
+ enforces rate limit delays (30 RPM).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ import time
13
+ from typing import List, Optional
14
+
15
+ import requests
16
+ from app.core.config import (
17
+ GROQ_API_KEY, GROQ_BASE_URL, GROQ_MODEL, GROQ_DELAY
18
+ )
19
+ from app.core.models import Paper
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ class GroqSummarizer:
24
+ """Handles LLM calls to Groq with rate-limiting and structured prompts."""
25
+
26
+ def __init__(self, api_key: str = GROQ_API_KEY):
27
+ self.api_key = api_key
28
+ self.last_call_time = 0.0
29
+
30
+ def summarize_paper(self, paper: Paper) -> Optional[str]:
31
+ """
32
+ Produce a structured summary with automatic retries for rate limits (429).
33
+ """
34
+ if not self.api_key:
35
+ logger.info("Skip Groq summarization: NO API KEY.")
36
+ return None
37
+
38
+ prompt = (
39
+ f"Summarize this abstract into three brief sections:\n"
40
+ f"1. Idea: (The core concept)\n"
41
+ f"2. Method: (The proposed approach)\n"
42
+ f"3. Results: (Key findings)\n\n"
43
+ f"Title: {paper.title}\n"
44
+ f"Abstract: {paper.abstract}\n"
45
+ )
46
+
47
+ for attempt in range(3): # Try up to 3 times for rate limits
48
+ # RPM Delay
49
+ elapsed = time.time() - self.last_call_time
50
+ if elapsed < GROQ_DELAY:
51
+ time.sleep(GROQ_DELAY - elapsed)
52
+
53
+ try:
54
+ logger.info(f"Summarizing [{paper.paper_id}] (Attempt {attempt+1})...")
55
+ response = requests.post(
56
+ GROQ_BASE_URL,
57
+ headers={"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"},
58
+ json={
59
+ "model": GROQ_MODEL,
60
+ "messages": [
61
+ {"role": "system", "content": "You are a scientific assistant."},
62
+ {"role": "user", "content": prompt}
63
+ ],
64
+ "temperature": 0.2,
65
+ "max_tokens": 250
66
+ },
67
+ timeout=30
68
+ )
69
+ self.last_call_time = time.time()
70
+
71
+ if response.status_code == 200:
72
+ data = response.json()
73
+ return data['choices'][0]['message']['content'].strip()
74
+
75
+ elif response.status_code == 429:
76
+ # Rate limit hit (TPM/RPM)
77
+ wait_time = 5.0 # Default hold
78
+ try:
79
+ # Extract wait time from error message or header
80
+ err_msg = response.json().get('error', {}).get('message', '')
81
+ if 'Please try again in' in err_msg:
82
+ # Parse "850ms" or "1.2s"
83
+ parts = err_msg.split('Please try again in ')[1].split(' ')
84
+ time_str = parts[0]
85
+ if 'ms' in time_str:
86
+ wait_time = float(time_str.replace('ms', '')) / 1000.0 + 0.5
87
+ elif 's' in time_str:
88
+ wait_time = float(time_str.replace('s', '')) + 0.5
89
+ except: pass
90
+
91
+ logger.warning(f"Groq 429! Waiting {wait_time:.2f}s before retry...")
92
+ time.sleep(wait_time)
93
+ continue
94
+ else:
95
+ logger.error(f"Groq API error ({response.status_code}): {response.text}")
96
+ return None
97
+
98
+ except Exception as exc:
99
+ logger.error(f"Groq error: {exc}")
100
+ time.sleep(2)
101
+
102
+ return None
103
+
104
+ def summarize_many(self, papers: List[Paper]):
105
+ """
106
+ Iterate through papers and update their summary_llm field.
107
+ """
108
+ for p in papers:
109
+ # We only summarize if it doesn't already have a summary
110
+ if not p.summary_llm:
111
+ p.summary_llm = self.summarize_paper(p)