cat-stack 0.1.0__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.
- cat_stack/__about__.py +10 -0
- cat_stack/__init__.py +128 -0
- cat_stack/_batch.py +1388 -0
- cat_stack/_category_analysis.py +348 -0
- cat_stack/_chunked.py +424 -0
- cat_stack/_embeddings.py +189 -0
- cat_stack/_formatter.py +169 -0
- cat_stack/_providers.py +1048 -0
- cat_stack/_tiebreaker.py +277 -0
- cat_stack/_utils.py +512 -0
- cat_stack/_web_fetch.py +194 -0
- cat_stack/calls/CoVe.py +287 -0
- cat_stack/calls/__init__.py +25 -0
- cat_stack/calls/all_calls.py +622 -0
- cat_stack/calls/image_CoVe.py +386 -0
- cat_stack/calls/image_stepback.py +210 -0
- cat_stack/calls/pdf_CoVe.py +386 -0
- cat_stack/calls/pdf_stepback.py +210 -0
- cat_stack/calls/stepback.py +180 -0
- cat_stack/calls/top_n.py +217 -0
- cat_stack/classify.py +682 -0
- cat_stack/explore.py +111 -0
- cat_stack/extract.py +218 -0
- cat_stack/image_functions.py +2078 -0
- cat_stack/images/circle.png +0 -0
- cat_stack/images/cube.png +0 -0
- cat_stack/images/diamond.png +0 -0
- cat_stack/images/overlapping_pentagons.png +0 -0
- cat_stack/images/rectangles.png +0 -0
- cat_stack/model_reference_list.py +94 -0
- cat_stack/pdf_functions.py +2087 -0
- cat_stack/summarize.py +290 -0
- cat_stack/text_functions.py +1358 -0
- cat_stack/text_functions_ensemble.py +3644 -0
- cat_stack-0.1.0.dist-info/METADATA +150 -0
- cat_stack-0.1.0.dist-info/RECORD +38 -0
- cat_stack-0.1.0.dist-info/WHEEL +4 -0
- cat_stack-0.1.0.dist-info/licenses/LICENSE +672 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
# openai stepback prompt
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_stepback_insight_openai(
|
|
7
|
+
stepback,
|
|
8
|
+
api_key,
|
|
9
|
+
user_model,
|
|
10
|
+
model_source="openai",
|
|
11
|
+
creativity=None
|
|
12
|
+
):
|
|
13
|
+
"""
|
|
14
|
+
Get stepback insight from OpenAI-compatible APIs.
|
|
15
|
+
Uses direct HTTP requests instead of OpenAI SDK for lighter dependencies.
|
|
16
|
+
"""
|
|
17
|
+
# Determine the base URL based on model source
|
|
18
|
+
if model_source == "huggingface":
|
|
19
|
+
from cat_stack._providers import _detect_huggingface_endpoint
|
|
20
|
+
base_url = _detect_huggingface_endpoint(api_key, user_model)
|
|
21
|
+
elif model_source == "huggingface-together":
|
|
22
|
+
base_url = "https://router.huggingface.co/together/v1"
|
|
23
|
+
elif model_source == "perplexity":
|
|
24
|
+
base_url = "https://api.perplexity.ai"
|
|
25
|
+
elif model_source == "xai":
|
|
26
|
+
base_url = "https://api.x.ai/v1"
|
|
27
|
+
else:
|
|
28
|
+
base_url = "https://api.openai.com/v1"
|
|
29
|
+
|
|
30
|
+
endpoint = f"{base_url}/chat/completions"
|
|
31
|
+
|
|
32
|
+
headers = {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
"Authorization": f"Bearer {api_key}"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
payload = {
|
|
38
|
+
"model": user_model,
|
|
39
|
+
"messages": [{"role": "user", "content": stepback}],
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if creativity is not None:
|
|
43
|
+
payload["temperature"] = creativity
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
response = requests.post(endpoint, headers=headers, json=payload, timeout=120)
|
|
47
|
+
response.raise_for_status()
|
|
48
|
+
result = response.json()
|
|
49
|
+
stepback_insight = result["choices"][0]["message"]["content"]
|
|
50
|
+
|
|
51
|
+
return stepback_insight, True
|
|
52
|
+
|
|
53
|
+
except Exception as e:
|
|
54
|
+
print(f"An error occurred during step-back prompting: {e}")
|
|
55
|
+
return None, False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# claude stepback prompt
|
|
59
|
+
|
|
60
|
+
def get_stepback_insight_anthropic(
|
|
61
|
+
stepback,
|
|
62
|
+
api_key,
|
|
63
|
+
user_model,
|
|
64
|
+
model_source="anthropic",
|
|
65
|
+
creativity=None
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
Get stepback insight from Anthropic Claude.
|
|
69
|
+
|
|
70
|
+
Uses direct HTTP requests instead of Anthropic SDK for lighter dependencies.
|
|
71
|
+
"""
|
|
72
|
+
import requests
|
|
73
|
+
|
|
74
|
+
endpoint = "https://api.anthropic.com/v1/messages"
|
|
75
|
+
|
|
76
|
+
headers = {
|
|
77
|
+
"Content-Type": "application/json",
|
|
78
|
+
"x-api-key": api_key,
|
|
79
|
+
"anthropic-version": "2023-06-01"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
payload = {
|
|
83
|
+
"model": user_model,
|
|
84
|
+
"max_tokens": 4096,
|
|
85
|
+
"messages": [{"role": "user", "content": stepback}],
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if creativity is not None:
|
|
89
|
+
payload["temperature"] = creativity
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
response = requests.post(endpoint, headers=headers, json=payload, timeout=120)
|
|
93
|
+
response.raise_for_status()
|
|
94
|
+
result = response.json()
|
|
95
|
+
|
|
96
|
+
# Parse response - Anthropic returns content as a list
|
|
97
|
+
content = result.get("content", [])
|
|
98
|
+
if content and content[0].get("type") == "text":
|
|
99
|
+
stepback_insight = content[0].get("text", "")
|
|
100
|
+
return stepback_insight, True
|
|
101
|
+
|
|
102
|
+
return None, False
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f"An error occurred during step-back prompting: {e}")
|
|
106
|
+
return None, False
|
|
107
|
+
|
|
108
|
+
# google stepback prompt
|
|
109
|
+
|
|
110
|
+
def get_stepback_insight_google(
|
|
111
|
+
stepback,
|
|
112
|
+
api_key,
|
|
113
|
+
user_model,
|
|
114
|
+
model_source="google",
|
|
115
|
+
creativity=None
|
|
116
|
+
):
|
|
117
|
+
|
|
118
|
+
import requests
|
|
119
|
+
|
|
120
|
+
url = f"https://generativelanguage.googleapis.com/v1beta/models/{user_model}:generateContent?key={api_key}"
|
|
121
|
+
|
|
122
|
+
headers = {
|
|
123
|
+
"Content-Type": "application/json"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
payload = {
|
|
127
|
+
"contents": [{
|
|
128
|
+
"parts": [{"text": stepback}],
|
|
129
|
+
|
|
130
|
+
**({"generationConfig": {"temperature": creativity}} if creativity is not None else {})
|
|
131
|
+
}]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
response = requests.post(url, headers=headers, json=payload)
|
|
136
|
+
response.raise_for_status() # Raise error for bad status codes
|
|
137
|
+
|
|
138
|
+
result = response.json()
|
|
139
|
+
stepback_insight = result['candidates'][0]['content']['parts'][0]['text']
|
|
140
|
+
|
|
141
|
+
return stepback_insight, True
|
|
142
|
+
|
|
143
|
+
except Exception as e:
|
|
144
|
+
print(f"An error occurred during step-back prompting: {e}")
|
|
145
|
+
return None, False
|
|
146
|
+
|
|
147
|
+
# mistral stepback prompt
|
|
148
|
+
|
|
149
|
+
def get_stepback_insight_mistral(
|
|
150
|
+
stepback,
|
|
151
|
+
api_key,
|
|
152
|
+
user_model,
|
|
153
|
+
model_source="mistral",
|
|
154
|
+
creativity=None
|
|
155
|
+
):
|
|
156
|
+
import requests
|
|
157
|
+
|
|
158
|
+
endpoint = "https://api.mistral.ai/v1/chat/completions"
|
|
159
|
+
headers = {
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
"Authorization": f"Bearer {api_key}"
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
payload = {
|
|
165
|
+
"model": user_model,
|
|
166
|
+
"messages": [{'role': 'user', 'content': stepback}],
|
|
167
|
+
}
|
|
168
|
+
if creativity is not None:
|
|
169
|
+
payload["temperature"] = creativity
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
response = requests.post(endpoint, headers=headers, json=payload, timeout=120)
|
|
173
|
+
response.raise_for_status()
|
|
174
|
+
result = response.json()
|
|
175
|
+
stepback_insight = result["choices"][0]["message"]["content"]
|
|
176
|
+
|
|
177
|
+
return stepback_insight, True
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
print(f"An error occurred during step-back prompting: {e}")
|
|
181
|
+
return None, False
|
|
182
|
+
|
|
183
|
+
# openai chain of verification calls
|
|
184
|
+
|
|
185
|
+
def chain_of_verification_openai(
|
|
186
|
+
initial_reply,
|
|
187
|
+
step2_prompt,
|
|
188
|
+
step3_prompt,
|
|
189
|
+
step4_prompt,
|
|
190
|
+
client,
|
|
191
|
+
user_model,
|
|
192
|
+
creativity,
|
|
193
|
+
remove_numbering
|
|
194
|
+
):
|
|
195
|
+
"""
|
|
196
|
+
Execute Chain of Verification (CoVe) process.
|
|
197
|
+
Returns the verified reply or initial reply if error occurs.
|
|
198
|
+
"""
|
|
199
|
+
try:
|
|
200
|
+
# STEP 2: Generate verification questions
|
|
201
|
+
step2_filled = step2_prompt.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
202
|
+
|
|
203
|
+
verification_response = client.chat.completions.create(
|
|
204
|
+
model=user_model,
|
|
205
|
+
messages=[{'role': 'user', 'content': step2_filled}],
|
|
206
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
verification_questions = verification_response.choices[0].message.content
|
|
210
|
+
|
|
211
|
+
# STEP 3: Answer verification questions
|
|
212
|
+
questions_list = [
|
|
213
|
+
remove_numbering(q)
|
|
214
|
+
for q in verification_questions.split('\n')
|
|
215
|
+
if q.strip()
|
|
216
|
+
]
|
|
217
|
+
verification_qa = []
|
|
218
|
+
|
|
219
|
+
# Prompting each question individually
|
|
220
|
+
for question in questions_list:
|
|
221
|
+
step3_filled = step3_prompt.replace('<<QUESTION>>', question)
|
|
222
|
+
|
|
223
|
+
answer_response = client.chat.completions.create(
|
|
224
|
+
model=user_model,
|
|
225
|
+
messages=[{'role': 'user', 'content': step3_filled}],
|
|
226
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
answer = answer_response.choices[0].message.content
|
|
230
|
+
verification_qa.append(f"Q: {question}\nA: {answer}")
|
|
231
|
+
|
|
232
|
+
# STEP 4: Final corrected categorization
|
|
233
|
+
verification_qa_text = "\n\n".join(verification_qa)
|
|
234
|
+
|
|
235
|
+
step4_filled = (step4_prompt
|
|
236
|
+
.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
237
|
+
.replace('<<VERIFICATION_QA>>', verification_qa_text))
|
|
238
|
+
|
|
239
|
+
print(f"Final prompt:\n{step4_filled}\n")
|
|
240
|
+
|
|
241
|
+
final_response = client.chat.completions.create(
|
|
242
|
+
model=user_model,
|
|
243
|
+
messages=[{'role': 'user', 'content': step4_filled}],
|
|
244
|
+
response_format={"type": "json_object"},
|
|
245
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
verified_reply = final_response.choices[0].message.content
|
|
249
|
+
print("Chain of verification completed. Final response generated.\n")
|
|
250
|
+
|
|
251
|
+
return verified_reply
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
print(f"ERROR in Chain of Verification: {str(e)}")
|
|
255
|
+
print("Falling back to initial response.\n")
|
|
256
|
+
return initial_reply
|
|
257
|
+
|
|
258
|
+
# anthropic chain of verification calls
|
|
259
|
+
|
|
260
|
+
def chain_of_verification_anthropic(
|
|
261
|
+
initial_reply,
|
|
262
|
+
step2_prompt,
|
|
263
|
+
step3_prompt,
|
|
264
|
+
step4_prompt,
|
|
265
|
+
client,
|
|
266
|
+
user_model,
|
|
267
|
+
creativity,
|
|
268
|
+
remove_numbering
|
|
269
|
+
):
|
|
270
|
+
"""
|
|
271
|
+
Execute Chain of Verification (CoVe) process for Anthropic Claude.
|
|
272
|
+
Returns the verified reply or initial reply if error occurs.
|
|
273
|
+
"""
|
|
274
|
+
try:
|
|
275
|
+
# STEP 2: Generate verification questions
|
|
276
|
+
step2_filled = step2_prompt.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
277
|
+
|
|
278
|
+
verification_response = client.messages.create(
|
|
279
|
+
model=user_model,
|
|
280
|
+
messages=[{'role': 'user', 'content': step2_filled}],
|
|
281
|
+
max_tokens=4096,
|
|
282
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
verification_questions = verification_response.content[0].text
|
|
286
|
+
|
|
287
|
+
# STEP 3: Answer verification questions
|
|
288
|
+
questions_list = [
|
|
289
|
+
remove_numbering(q)
|
|
290
|
+
for q in verification_questions.split('\n')
|
|
291
|
+
if q.strip()
|
|
292
|
+
]
|
|
293
|
+
print(f"Verification questions:\n{questions_list}\n")
|
|
294
|
+
verification_qa = []
|
|
295
|
+
|
|
296
|
+
# Prompting each question individually
|
|
297
|
+
for question in questions_list:
|
|
298
|
+
step3_filled = step3_prompt.replace('<<QUESTION>>', question)
|
|
299
|
+
|
|
300
|
+
answer_response = client.messages.create(
|
|
301
|
+
model=user_model,
|
|
302
|
+
messages=[{'role': 'user', 'content': step3_filled}],
|
|
303
|
+
max_tokens=4096,
|
|
304
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
answer = answer_response.content[0].text
|
|
308
|
+
verification_qa.append(f"Q: {question}\nA: {answer}")
|
|
309
|
+
|
|
310
|
+
# STEP 4: Final corrected categorization
|
|
311
|
+
verification_qa_text = "\n\n".join(verification_qa)
|
|
312
|
+
|
|
313
|
+
step4_filled = (step4_prompt
|
|
314
|
+
.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
315
|
+
.replace('<<VERIFICATION_QA>>', verification_qa_text))
|
|
316
|
+
|
|
317
|
+
print(f"Final prompt:\n{step4_filled}\n")
|
|
318
|
+
|
|
319
|
+
tools = [{
|
|
320
|
+
"name": "return_categories",
|
|
321
|
+
"description": "Return categorization results as 0 (not present) or 1 (present) for each category",
|
|
322
|
+
"input_schema": {
|
|
323
|
+
"type": "object",
|
|
324
|
+
"properties": properties,
|
|
325
|
+
"required": list(properties.keys()) # All categories required
|
|
326
|
+
}
|
|
327
|
+
}]
|
|
328
|
+
|
|
329
|
+
final_response = client.messages.create(
|
|
330
|
+
model=user_model,
|
|
331
|
+
messages=[{'role': 'user', 'content': step4_filled}],
|
|
332
|
+
max_tokens=4096,
|
|
333
|
+
tools=tools,
|
|
334
|
+
tool_choice={"type": "tool", "name": "return_categories"},
|
|
335
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
result_dict = final_response.content[0].input
|
|
339
|
+
|
|
340
|
+
verified_reply = json.dumps(result_dict)
|
|
341
|
+
print("Chain of verification completed. Final response generated.\n")
|
|
342
|
+
|
|
343
|
+
return verified_reply
|
|
344
|
+
|
|
345
|
+
except Exception as e:
|
|
346
|
+
print(f"ERROR in Chain of Verification: {str(e)}")
|
|
347
|
+
print("Falling back to initial response.\n")
|
|
348
|
+
return initial_reply
|
|
349
|
+
|
|
350
|
+
# google chain of verification calls
|
|
351
|
+
def chain_of_verification_google(
|
|
352
|
+
initial_reply,
|
|
353
|
+
prompt,
|
|
354
|
+
step2_prompt,
|
|
355
|
+
step3_prompt,
|
|
356
|
+
step4_prompt,
|
|
357
|
+
url,
|
|
358
|
+
headers,
|
|
359
|
+
creativity,
|
|
360
|
+
remove_numbering,
|
|
361
|
+
make_google_request
|
|
362
|
+
):
|
|
363
|
+
import time
|
|
364
|
+
"""
|
|
365
|
+
Execute Chain of Verification (CoVe) process for Google Gemini.
|
|
366
|
+
Returns the verified reply or initial reply if error occurs.
|
|
367
|
+
"""
|
|
368
|
+
try:
|
|
369
|
+
# STEP 2: Generate verification questions
|
|
370
|
+
step2_filled = step2_prompt.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
371
|
+
|
|
372
|
+
payload_step2 = {
|
|
373
|
+
"contents": [{
|
|
374
|
+
"parts": [{"text": step2_filled}]
|
|
375
|
+
}],
|
|
376
|
+
**({"generationConfig": {"temperature": creativity}} if creativity is not None else {}),
|
|
377
|
+
**({"thinkingConfig": {"thinkingBudget": thinking_budget}} if thinking_budget is not None else {})
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
result_step2 = make_google_request(url, headers, payload_step2)
|
|
381
|
+
verification_questions = result_step2["candidates"][0]["content"]["parts"][0]["text"]
|
|
382
|
+
|
|
383
|
+
# STEP 3: Answer verification questions
|
|
384
|
+
questions_list = [
|
|
385
|
+
remove_numbering(q)
|
|
386
|
+
for q in verification_questions.split('\n')
|
|
387
|
+
if q.strip()
|
|
388
|
+
]
|
|
389
|
+
verification_qa = []
|
|
390
|
+
|
|
391
|
+
for question in questions_list:
|
|
392
|
+
time.sleep(2) # temporary rate limit handling
|
|
393
|
+
step3_filled = step3_prompt.replace('<<QUESTION>>', question)
|
|
394
|
+
|
|
395
|
+
payload_step3 = {
|
|
396
|
+
"contents": [{
|
|
397
|
+
"parts": [{"text": step3_filled}]
|
|
398
|
+
}],
|
|
399
|
+
**({"generationConfig": {"temperature": creativity}} if creativity is not None else {}),
|
|
400
|
+
**({"thinkingConfig": {"thinkingBudget": thinking_budget}} if thinking_budget is not None else {})
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
result_step3 = make_google_request(url, headers, payload_step3)
|
|
404
|
+
answer = result_step3["candidates"][0]["content"]["parts"][0]["text"]
|
|
405
|
+
verification_qa.append(f"Q: {question}\nA: {answer}")
|
|
406
|
+
|
|
407
|
+
# STEP 4: Final corrected categorization
|
|
408
|
+
verification_qa_text = "\n\n".join(verification_qa)
|
|
409
|
+
|
|
410
|
+
step4_filled = (step4_prompt
|
|
411
|
+
.replace('<<PROMPT>>', prompt)
|
|
412
|
+
.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
413
|
+
.replace('<<VERIFICATION_QA>>', verification_qa_text))
|
|
414
|
+
|
|
415
|
+
payload_step4 = {
|
|
416
|
+
"contents": [{
|
|
417
|
+
"parts": [{"text": step4_filled}]
|
|
418
|
+
}],
|
|
419
|
+
"generationConfig": {
|
|
420
|
+
"responseMimeType": "application/json",
|
|
421
|
+
**({"temperature": creativity} if creativity is not None else {}),
|
|
422
|
+
**({"thinkingConfig": {"thinkingBudget": thinking_budget}} if thinking_budget is not None else {})
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
result_step4 = make_google_request(url, headers, payload_step4)
|
|
427
|
+
verified_reply = result_step4["candidates"][0]["content"]["parts"][0]["text"]
|
|
428
|
+
|
|
429
|
+
print("Chain of verification completed. Final response generated.\n")
|
|
430
|
+
return verified_reply
|
|
431
|
+
|
|
432
|
+
except Exception as e:
|
|
433
|
+
print(f"ERROR in Chain of Verification: {str(e)}")
|
|
434
|
+
print("Falling back to initial response.\n")
|
|
435
|
+
return initial_reply
|
|
436
|
+
|
|
437
|
+
# mistral chain of verification calls
|
|
438
|
+
|
|
439
|
+
def chain_of_verification_mistral(
|
|
440
|
+
initial_reply,
|
|
441
|
+
step2_prompt,
|
|
442
|
+
step3_prompt,
|
|
443
|
+
step4_prompt,
|
|
444
|
+
client,
|
|
445
|
+
user_model,
|
|
446
|
+
creativity,
|
|
447
|
+
remove_numbering
|
|
448
|
+
):
|
|
449
|
+
"""
|
|
450
|
+
Execute Chain of Verification (CoVe) process for Mistral AI.
|
|
451
|
+
Returns the verified reply or initial reply if error occurs.
|
|
452
|
+
"""
|
|
453
|
+
try:
|
|
454
|
+
# STEP 2: Generate verification questions
|
|
455
|
+
step2_filled = step2_prompt.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
456
|
+
|
|
457
|
+
verification_response = client.chat.complete(
|
|
458
|
+
model=user_model,
|
|
459
|
+
messages=[{'role': 'user', 'content': step2_filled}],
|
|
460
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
verification_questions = verification_response.choices[0].message.content
|
|
464
|
+
|
|
465
|
+
# STEP 3: Answer verification questions
|
|
466
|
+
questions_list = [
|
|
467
|
+
remove_numbering(q)
|
|
468
|
+
for q in verification_questions.split('\n')
|
|
469
|
+
if q.strip()
|
|
470
|
+
]
|
|
471
|
+
verification_qa = []
|
|
472
|
+
|
|
473
|
+
# Prompting each question individually
|
|
474
|
+
for question in questions_list:
|
|
475
|
+
step3_filled = step3_prompt.replace('<<QUESTION>>', question)
|
|
476
|
+
|
|
477
|
+
answer_response = client.chat.complete(
|
|
478
|
+
model=user_model,
|
|
479
|
+
messages=[{'role': 'user', 'content': step3_filled}],
|
|
480
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
answer = answer_response.choices[0].message.content
|
|
484
|
+
verification_qa.append(f"Q: {question}\nA: {answer}")
|
|
485
|
+
|
|
486
|
+
# STEP 4: Final corrected categorization
|
|
487
|
+
verification_qa_text = "\n\n".join(verification_qa)
|
|
488
|
+
|
|
489
|
+
step4_filled = (step4_prompt
|
|
490
|
+
.replace('<<INITIAL_REPLY>>', initial_reply)
|
|
491
|
+
.replace('<<VERIFICATION_QA>>', verification_qa_text))
|
|
492
|
+
|
|
493
|
+
final_response = client.chat.complete(
|
|
494
|
+
model=user_model,
|
|
495
|
+
messages=[{'role': 'user', 'content': step4_filled}],
|
|
496
|
+
response_format={"type": "json_object"},
|
|
497
|
+
**({"temperature": creativity} if creativity is not None else {})
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
verified_reply = final_response.choices[0].message.content
|
|
501
|
+
print("Chain of verification completed. Final response generated.\n")
|
|
502
|
+
|
|
503
|
+
return verified_reply
|
|
504
|
+
|
|
505
|
+
except Exception as e:
|
|
506
|
+
print(f"ERROR in Chain of Verification: {str(e)}")
|
|
507
|
+
print("Falling back to initial response.\n")
|
|
508
|
+
return initial_reply
|
|
509
|
+
|
|
510
|
+
# openai explore corpus call
|
|
511
|
+
def get_openai_top_n(
|
|
512
|
+
prompt,
|
|
513
|
+
user_model,
|
|
514
|
+
specificity,
|
|
515
|
+
model_source,
|
|
516
|
+
api_key,
|
|
517
|
+
research_question,
|
|
518
|
+
creativity
|
|
519
|
+
):
|
|
520
|
+
"""
|
|
521
|
+
Get response from OpenAI API with system message.
|
|
522
|
+
Uses direct HTTP requests instead of OpenAI SDK for lighter dependencies.
|
|
523
|
+
"""
|
|
524
|
+
# Determine the base URL based on model source
|
|
525
|
+
if model_source == "huggingface":
|
|
526
|
+
from cat_stack._providers import _detect_huggingface_endpoint
|
|
527
|
+
base_url = _detect_huggingface_endpoint(api_key, user_model)
|
|
528
|
+
elif model_source == "huggingface-together":
|
|
529
|
+
base_url = "https://router.huggingface.co/together/v1"
|
|
530
|
+
elif model_source == "perplexity":
|
|
531
|
+
base_url = "https://api.perplexity.ai"
|
|
532
|
+
elif model_source == "xai":
|
|
533
|
+
base_url = "https://api.x.ai/v1"
|
|
534
|
+
else:
|
|
535
|
+
base_url = "https://api.openai.com/v1"
|
|
536
|
+
|
|
537
|
+
endpoint = f"{base_url}/chat/completions"
|
|
538
|
+
|
|
539
|
+
headers = {
|
|
540
|
+
"Content-Type": "application/json",
|
|
541
|
+
"Authorization": f"Bearer {api_key}"
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
# Build system message
|
|
545
|
+
if research_question:
|
|
546
|
+
system_content = (
|
|
547
|
+
f"You are a helpful assistant that extracts categories from text responses. "
|
|
548
|
+
f"The specific task is to identify {specificity} categories of responses to a text prompt. "
|
|
549
|
+
f"The research question is: {research_question}"
|
|
550
|
+
)
|
|
551
|
+
else:
|
|
552
|
+
system_content = "You are a helpful assistant."
|
|
553
|
+
|
|
554
|
+
payload = {
|
|
555
|
+
"model": user_model,
|
|
556
|
+
"messages": [
|
|
557
|
+
{"role": "system", "content": system_content},
|
|
558
|
+
{"role": "user", "content": prompt}
|
|
559
|
+
],
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if creativity is not None:
|
|
563
|
+
payload["temperature"] = creativity
|
|
564
|
+
|
|
565
|
+
response = requests.post(endpoint, headers=headers, json=payload, timeout=120)
|
|
566
|
+
response.raise_for_status()
|
|
567
|
+
result = response.json()
|
|
568
|
+
|
|
569
|
+
return result["choices"][0]["message"]["content"]
|
|
570
|
+
|
|
571
|
+
def get_anthropic_top_n(
|
|
572
|
+
prompt,
|
|
573
|
+
user_model,
|
|
574
|
+
model_source,
|
|
575
|
+
specificity,
|
|
576
|
+
api_key,
|
|
577
|
+
research_question,
|
|
578
|
+
creativity
|
|
579
|
+
):
|
|
580
|
+
"""
|
|
581
|
+
Get response from Anthropic API with system prompt.
|
|
582
|
+
|
|
583
|
+
Uses direct HTTP requests instead of Anthropic SDK for lighter dependencies.
|
|
584
|
+
"""
|
|
585
|
+
import requests
|
|
586
|
+
|
|
587
|
+
endpoint = "https://api.anthropic.com/v1/messages"
|
|
588
|
+
|
|
589
|
+
headers = {
|
|
590
|
+
"Content-Type": "application/json",
|
|
591
|
+
"x-api-key": api_key,
|
|
592
|
+
"anthropic-version": "2023-06-01"
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
# Build system prompt
|
|
596
|
+
if research_question:
|
|
597
|
+
system_content = (f"You are a helpful assistant that extracts categories from text responses. "
|
|
598
|
+
f"The specific task is to identify {specificity} categories of responses to a text prompt. "
|
|
599
|
+
f"The research question is: {research_question}")
|
|
600
|
+
else:
|
|
601
|
+
system_content = "You are a helpful assistant."
|
|
602
|
+
|
|
603
|
+
payload = {
|
|
604
|
+
"model": user_model,
|
|
605
|
+
"max_tokens": 4096,
|
|
606
|
+
"system": system_content,
|
|
607
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if creativity is not None:
|
|
611
|
+
payload["temperature"] = creativity
|
|
612
|
+
|
|
613
|
+
response = requests.post(endpoint, headers=headers, json=payload, timeout=120)
|
|
614
|
+
response.raise_for_status()
|
|
615
|
+
result = response.json()
|
|
616
|
+
|
|
617
|
+
# Parse response - Anthropic returns content as a list
|
|
618
|
+
content = result.get("content", [])
|
|
619
|
+
if content and content[0].get("type") == "text":
|
|
620
|
+
return content[0].get("text", "")
|
|
621
|
+
|
|
622
|
+
return ""
|