oceansweep commited on
Commit
09afec6
1 Parent(s): 077a7d5

Upload 9 files

Browse files
App_Function_Libraries/Chat.py CHANGED
@@ -19,53 +19,89 @@ from App_Function_Libraries.LLM_API_Calls import chat_with_openai, chat_with_ant
19
  from App_Function_Libraries.LLM_API_Calls_Local import chat_with_aphrodite, chat_with_local_llm, chat_with_ollama, \
20
  chat_with_kobold, chat_with_llama, chat_with_oobabooga, chat_with_tabbyapi, chat_with_vllm, chat_with_custom_openai
21
  from App_Function_Libraries.DB.SQLite_DB import load_media_content
22
- from App_Function_Libraries.Utils.Utils import generate_unique_filename
 
 
23
  #
24
  ####################################################################################################
25
  #
26
  # Functions:
27
 
 
28
  def chat_api_call(api_endpoint, api_key, input_data, prompt, temp, system_message=None):
29
  if not api_key:
30
  api_key = None
 
31
  try:
32
  logging.info(f"Debug - Chat API Call - API Endpoint: {api_endpoint}")
33
  logging.info(f"Debug - Chat API Call - API Key: {api_key}")
34
  logging.info(f"Debug - Chat chat_api_call - API Endpoint: {api_endpoint}")
35
  if api_endpoint.lower() == 'openai':
36
  response = chat_with_openai(api_key, input_data, prompt, temp, system_message)
37
- elif api_endpoint.lower() == "anthropic":
38
- response = chat_with_anthropic(api_key, input_data, prompt, temp, system_message)
 
 
 
 
 
 
 
 
 
 
 
39
  elif api_endpoint.lower() == "cohere":
40
- response = chat_with_cohere(api_key, input_data, prompt, temp, system_message)
 
 
 
 
 
 
 
 
41
  elif api_endpoint.lower() == "groq":
42
  response = chat_with_groq(api_key, input_data, prompt, temp, system_message)
 
43
  elif api_endpoint.lower() == "openrouter":
44
  response = chat_with_openrouter(api_key, input_data, prompt, temp, system_message)
 
45
  elif api_endpoint.lower() == "deepseek":
46
  response = chat_with_deepseek(api_key, input_data, prompt, temp, system_message)
 
47
  elif api_endpoint.lower() == "mistral":
48
  response = chat_with_mistral(api_key, input_data, prompt, temp, system_message)
 
49
  elif api_endpoint.lower() == "llama.cpp":
50
- response = chat_with_llama(input_data, prompt, temp, system_message)
51
  elif api_endpoint.lower() == "kobold":
52
  response = chat_with_kobold(input_data, api_key, prompt, temp, system_message)
 
53
  elif api_endpoint.lower() == "ooba":
54
  response = chat_with_oobabooga(input_data, api_key, prompt, temp, system_message)
 
55
  elif api_endpoint.lower() == "tabbyapi":
56
  response = chat_with_tabbyapi(input_data, prompt, temp, system_message)
 
57
  elif api_endpoint.lower() == "vllm":
58
  response = chat_with_vllm(input_data, prompt, system_message)
 
59
  elif api_endpoint.lower() == "local-llm":
60
  response = chat_with_local_llm(input_data, prompt, temp, system_message)
 
61
  elif api_endpoint.lower() == "huggingface":
62
  response = chat_with_huggingface(api_key, input_data, prompt, temp) # , system_message)
 
63
  elif api_endpoint.lower() == "ollama":
64
- response = chat_with_ollama(input_data, prompt, temp, system_message)
 
65
  elif api_endpoint.lower() == "aphrodite":
66
  response = chat_with_aphrodite(input_data, prompt, temp, system_message)
 
67
  elif api_endpoint.lower() == "custom-openai-api":
68
  response = chat_with_custom_openai(api_key, input_data, prompt, temp, system_message)
 
69
  else:
70
  raise ValueError(f"Unsupported API endpoint: {api_endpoint}")
71
 
@@ -97,12 +133,10 @@ def chat(message, history, media_content, selected_parts, api_endpoint, api_key,
97
  # logging.debug(f"Debug - Chat Function - Combined Content: {combined_content[:500]}...")
98
 
99
  # Prepare the input for the API
100
- if not history:
101
- input_data = f"{combined_content}\n\nUser: {message}\n"
102
- else:
103
- input_data = f"User: {message}\n"
104
- # Print first 500 chars
105
- # logging.info(f"Debug - Chat Function - Input Data: {input_data[:500]}...")
106
 
107
  if system_message:
108
  print(f"System message: {system_message}")
@@ -110,7 +144,7 @@ def chat(message, history, media_content, selected_parts, api_endpoint, api_key,
110
  temperature = float(temperature) if temperature else 0.7
111
  temp = temperature
112
 
113
- logging.debug("Debug - Chat Function - Temperature: {temperature}")
114
  logging.debug(f"Debug - Chat Function - API Key: {api_key[:10]}")
115
  logging.debug(f"Debug - Chat Function - Prompt: {prompt}")
116
 
@@ -124,13 +158,13 @@ def chat(message, history, media_content, selected_parts, api_endpoint, api_key,
124
 
125
 
126
 
127
- def save_chat_history_to_db_wrapper(chatbot, conversation_id, media_content):
128
  logging.info(f"Attempting to save chat history. Media content type: {type(media_content)}")
129
  try:
130
  # Extract the media_id and media_name from the media_content
131
  media_id = None
132
- media_name = None
133
  if isinstance(media_content, dict):
 
134
  logging.debug(f"Media content keys: {media_content.keys()}")
135
  if 'content' in media_content:
136
  try:
@@ -168,7 +202,7 @@ def save_chat_history_to_db_wrapper(chatbot, conversation_id, media_content):
168
 
169
  # Generate a unique conversation name using media_id and current timestamp
170
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
171
- conversation_name = f"Chat_{media_id}_{timestamp}"
172
 
173
  new_conversation_id = save_chat_history_to_database(chatbot, conversation_id, media_id, media_name,
174
  conversation_name)
 
19
  from App_Function_Libraries.LLM_API_Calls_Local import chat_with_aphrodite, chat_with_local_llm, chat_with_ollama, \
20
  chat_with_kobold, chat_with_llama, chat_with_oobabooga, chat_with_tabbyapi, chat_with_vllm, chat_with_custom_openai
21
  from App_Function_Libraries.DB.SQLite_DB import load_media_content
22
+ from App_Function_Libraries.Utils.Utils import generate_unique_filename, load_and_log_configs
23
+
24
+
25
  #
26
  ####################################################################################################
27
  #
28
  # Functions:
29
 
30
+
31
  def chat_api_call(api_endpoint, api_key, input_data, prompt, temp, system_message=None):
32
  if not api_key:
33
  api_key = None
34
+ model = None
35
  try:
36
  logging.info(f"Debug - Chat API Call - API Endpoint: {api_endpoint}")
37
  logging.info(f"Debug - Chat API Call - API Key: {api_key}")
38
  logging.info(f"Debug - Chat chat_api_call - API Endpoint: {api_endpoint}")
39
  if api_endpoint.lower() == 'openai':
40
  response = chat_with_openai(api_key, input_data, prompt, temp, system_message)
41
+
42
+ elif api_endpoint.lower() == 'anthropic':
43
+ # Retrieve the model from config
44
+ loaded_config_data = load_and_log_configs()
45
+ model = loaded_config_data['models']['anthropic'] if loaded_config_data else None
46
+ response = chat_with_anthropic(
47
+ api_key=api_key,
48
+ input_data=input_data,
49
+ model=model,
50
+ custom_prompt_arg=prompt,
51
+ system_prompt=system_message
52
+ )
53
+
54
  elif api_endpoint.lower() == "cohere":
55
+ response = chat_with_cohere(
56
+ api_key,
57
+ input_data,
58
+ model=model,
59
+ custom_prompt_arg=prompt,
60
+ system_prompt=system_message,
61
+ temp=temp
62
+ )
63
+
64
  elif api_endpoint.lower() == "groq":
65
  response = chat_with_groq(api_key, input_data, prompt, temp, system_message)
66
+
67
  elif api_endpoint.lower() == "openrouter":
68
  response = chat_with_openrouter(api_key, input_data, prompt, temp, system_message)
69
+
70
  elif api_endpoint.lower() == "deepseek":
71
  response = chat_with_deepseek(api_key, input_data, prompt, temp, system_message)
72
+
73
  elif api_endpoint.lower() == "mistral":
74
  response = chat_with_mistral(api_key, input_data, prompt, temp, system_message)
75
+
76
  elif api_endpoint.lower() == "llama.cpp":
77
+ response = chat_with_llama(input_data, prompt, temp, None, api_key, system_message)
78
  elif api_endpoint.lower() == "kobold":
79
  response = chat_with_kobold(input_data, api_key, prompt, temp, system_message)
80
+
81
  elif api_endpoint.lower() == "ooba":
82
  response = chat_with_oobabooga(input_data, api_key, prompt, temp, system_message)
83
+
84
  elif api_endpoint.lower() == "tabbyapi":
85
  response = chat_with_tabbyapi(input_data, prompt, temp, system_message)
86
+
87
  elif api_endpoint.lower() == "vllm":
88
  response = chat_with_vllm(input_data, prompt, system_message)
89
+
90
  elif api_endpoint.lower() == "local-llm":
91
  response = chat_with_local_llm(input_data, prompt, temp, system_message)
92
+
93
  elif api_endpoint.lower() == "huggingface":
94
  response = chat_with_huggingface(api_key, input_data, prompt, temp) # , system_message)
95
+
96
  elif api_endpoint.lower() == "ollama":
97
+ response = chat_with_ollama(input_data, prompt, None, api_key, temp, system_message)
98
+
99
  elif api_endpoint.lower() == "aphrodite":
100
  response = chat_with_aphrodite(input_data, prompt, temp, system_message)
101
+
102
  elif api_endpoint.lower() == "custom-openai-api":
103
  response = chat_with_custom_openai(api_key, input_data, prompt, temp, system_message)
104
+
105
  else:
106
  raise ValueError(f"Unsupported API endpoint: {api_endpoint}")
107
 
 
133
  # logging.debug(f"Debug - Chat Function - Combined Content: {combined_content[:500]}...")
134
 
135
  # Prepare the input for the API
136
+ input_data = f"{combined_content}\n\n" if combined_content else ""
137
+ for old_message, old_response in history:
138
+ input_data += f"{old_message}\nAssistant: {old_response}\n\n"
139
+ input_data += f"{message}\n"
 
 
140
 
141
  if system_message:
142
  print(f"System message: {system_message}")
 
144
  temperature = float(temperature) if temperature else 0.7
145
  temp = temperature
146
 
147
+ logging.debug(f"Debug - Chat Function - Temperature: {temperature}")
148
  logging.debug(f"Debug - Chat Function - API Key: {api_key[:10]}")
149
  logging.debug(f"Debug - Chat Function - Prompt: {prompt}")
150
 
 
158
 
159
 
160
 
161
+ def save_chat_history_to_db_wrapper(chatbot, conversation_id, media_content, media_name=None):
162
  logging.info(f"Attempting to save chat history. Media content type: {type(media_content)}")
163
  try:
164
  # Extract the media_id and media_name from the media_content
165
  media_id = None
 
166
  if isinstance(media_content, dict):
167
+ media_id = None
168
  logging.debug(f"Media content keys: {media_content.keys()}")
169
  if 'content' in media_content:
170
  try:
 
202
 
203
  # Generate a unique conversation name using media_id and current timestamp
204
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
205
+ conversation_name = f"{media_name}_{timestamp}"
206
 
207
  new_conversation_id = save_chat_history_to_database(chatbot, conversation_id, media_id, media_name,
208
  conversation_name)
App_Function_Libraries/Chunk_Lib.py CHANGED
@@ -32,8 +32,13 @@ from App_Function_Libraries.Utils.Utils import load_comprehensive_config
32
  #
33
  # FIXME - Make sure it only downloads if it already exists, and does a check first.
34
  # Ensure NLTK data is downloaded
35
- def ntlk_prep():
36
- nltk.download('punkt')
 
 
 
 
 
37
  #
38
  # Load GPT2 tokenizer
39
  tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
@@ -57,6 +62,34 @@ openai_api_key = config.get('API', 'openai_api_key')
57
  #
58
  # Functions:
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  def detect_language(text: str) -> str:
61
  try:
62
  return detect(text)
@@ -65,13 +98,13 @@ def detect_language(text: str) -> str:
65
  return 'en'
66
 
67
 
68
- def load_document(file_path):
69
- with open(file_path, 'r') as file:
70
  text = file.read()
71
- return re.sub('\\s+', ' ', text).strip()
72
 
73
 
74
- def improved_chunking_process(text: str, custom_chunk_options: Dict[str, Any] = None) -> List[Dict[str, Any]]:
75
  logging.debug("Improved chunking process started...")
76
 
77
  # Extract JSON metadata if present
@@ -92,9 +125,9 @@ def improved_chunking_process(text: str, custom_chunk_options: Dict[str, Any] =
92
  text = text[len(header_text):].strip()
93
  logging.debug(f"Extracted header text: {header_text}")
94
 
95
- options = chunk_options.copy()
96
- if custom_chunk_options:
97
- options.update(custom_chunk_options)
98
 
99
  chunk_method = options.get('method', 'words')
100
  max_size = options.get('max_size', 2000)
@@ -104,32 +137,39 @@ def improved_chunking_process(text: str, custom_chunk_options: Dict[str, Any] =
104
  if language is None:
105
  language = detect_language(text)
106
 
107
- chunks = chunk_text(text, chunk_method, max_size, overlap, language)
 
 
 
108
 
109
  chunks_with_metadata = []
110
  total_chunks = len(chunks)
111
  for i, chunk in enumerate(chunks):
112
  metadata = {
113
- 'chunk_index': i,
114
  'total_chunks': total_chunks,
115
  'chunk_method': chunk_method,
116
  'max_size': max_size,
117
  'overlap': overlap,
118
  'language': language,
119
- 'relative_position': i / total_chunks
120
  }
121
  metadata.update(json_content) # Add the extracted JSON content to metadata
122
  metadata['header_text'] = header_text # Add the header text to metadata
123
 
 
 
 
 
 
124
  chunks_with_metadata.append({
125
- 'text': chunk,
126
  'metadata': metadata
127
  })
128
 
129
  return chunks_with_metadata
130
 
131
 
132
-
133
  def multi_level_chunking(text: str, method: str, max_size: int, overlap: int, language: str) -> List[str]:
134
  logging.debug("Multi-level chunking process started...")
135
  # First level: chunk by paragraphs
@@ -139,35 +179,34 @@ def multi_level_chunking(text: str, method: str, max_size: int, overlap: int, la
139
  chunks = []
140
  for para in paragraphs:
141
  if method == 'words':
142
- chunks.extend(chunk_text_by_words(para, max_size, overlap, language))
143
  elif method == 'sentences':
144
- chunks.extend(chunk_text_by_sentences(para, max_size, overlap, language))
145
  else:
146
  chunks.append(para)
147
 
148
  return chunks
149
 
150
 
151
-
152
  # FIXME - ensure language detection occurs in each chunk function
153
- def chunk_text(text: str, method: str, max_size: int, overlap: int, language: str=None) -> List[str]:
154
-
155
  if method == 'words':
156
  logging.debug("Chunking by words...")
157
- return chunk_text_by_words(text, max_size, overlap, language)
158
  elif method == 'sentences':
159
  logging.debug("Chunking by sentences...")
160
- return chunk_text_by_sentences(text, max_size, overlap, language)
161
  elif method == 'paragraphs':
162
  logging.debug("Chunking by paragraphs...")
163
- return chunk_text_by_paragraphs(text, max_size, overlap)
164
  elif method == 'tokens':
165
  logging.debug("Chunking by tokens...")
166
- return chunk_text_by_tokens(text, max_size, overlap)
167
  elif method == 'semantic':
168
  logging.debug("Chunking by semantic similarity...")
169
- return semantic_chunking(text, max_size)
170
  else:
 
171
  return [text]
172
 
173
  def determine_chunk_position(relative_position: float) -> str:
@@ -206,22 +245,37 @@ def chunk_text_by_sentences(text: str, max_sentences: int = 10, overlap: int = 0
206
  if language is None:
207
  language = detect_language(text)
208
 
209
- nltk.download('punkt', quiet=True)
210
-
211
  if language.startswith('zh'): # Chinese
212
  import jieba
213
- sentences = list(jieba.cut(text, cut_all=False))
 
 
 
 
214
  elif language == 'ja': # Japanese
215
  import fugashi
216
  tagger = fugashi.Tagger()
217
- sentences = [word.surface for word in tagger(text) if word.feature.pos1 in ['記号', '補助記号'] and word.surface.strip()]
 
 
218
  else: # Default to NLTK for other languages
219
- sentences = sent_tokenize(text, language=language)
 
 
 
 
220
 
221
  chunks = []
 
 
222
  for i in range(0, len(sentences), max_sentences - overlap):
223
- chunk = ' '.join(sentences[i:i + max_sentences])
 
 
 
224
  chunks.append(chunk)
 
 
225
  return post_process_chunks(chunks)
226
 
227
 
@@ -258,6 +312,16 @@ def chunk_text_by_tokens(text: str, max_tokens: int = 1000, overlap: int = 0) ->
258
  chunks.append(' '.join(current_chunk))
259
 
260
  return post_process_chunks(chunks)
 
 
 
 
 
 
 
 
 
 
261
 
262
 
263
  def post_process_chunks(chunks: List[str]) -> List[str]:
@@ -266,35 +330,35 @@ def post_process_chunks(chunks: List[str]) -> List[str]:
266
 
267
  # FIXME - F
268
  def get_chunk_metadata(chunk: str, full_text: str, chunk_type: str = "generic",
269
- chapter_number: Optional[int] = None,
270
- chapter_pattern: Optional[str] = None,
271
- language: str = None) -> Dict[str, Any]:
272
- try:
273
- logging.debug("get_chunk_metadata...")
274
- start_index = full_text.index(chunk)
275
- end_index = start_index + len(chunk)
276
- # Calculate a hash for the chunk
277
- chunk_hash = hashlib.md5(chunk.encode()).hexdigest()
278
-
279
- metadata = {
280
- 'start_index': start_index,
281
- 'end_index': end_index,
282
- 'word_count': len(chunk.split()),
283
- 'char_count': len(chunk),
284
- 'chunk_type': chunk_type,
285
- 'language': language,
286
- 'chunk_hash': chunk_hash,
287
- 'relative_position': start_index / len(full_text)
288
- }
 
 
 
289
 
290
- if chunk_type == "chapter":
291
- metadata['chapter_number'] = chapter_number
292
- metadata['chapter_pattern'] = chapter_pattern
293
 
294
- return metadata
295
- except ValueError as e:
296
- logging.error(f"Chunk not found in full_text: {chunk[:50]}... Full text length: {len(full_text)}")
297
- raise
298
 
299
 
300
  def process_document_with_metadata(text: str, chunk_options: Dict[str, Any],
@@ -308,27 +372,33 @@ def process_document_with_metadata(text: str, chunk_options: Dict[str, Any],
308
 
309
 
310
  # Hybrid approach, chunk each sentence while ensuring total token size does not exceed a maximum number
311
- def chunk_text_hybrid(text, max_tokens=1000):
312
  logging.debug("chunk_text_hybrid...")
313
- sentences = nltk.tokenize.sent_tokenize(text)
314
  chunks = []
315
  current_chunk = []
316
  current_length = 0
317
 
318
  for sentence in sentences:
319
  tokens = tokenizer.encode(sentence)
320
- if current_length + len(tokens) <= max_tokens:
321
- current_chunk.append(sentence)
322
- current_length += len(tokens)
323
- else:
324
  chunks.append(' '.join(current_chunk))
325
- current_chunk = [sentence]
326
- current_length = len(tokens)
 
 
 
 
 
 
 
 
 
327
 
328
  if current_chunk:
329
  chunks.append(' '.join(current_chunk))
330
 
331
- return chunks
332
 
333
 
334
  # Thanks openai
@@ -340,21 +410,22 @@ def chunk_on_delimiter(input_string: str,
340
  combined_chunks, _, dropped_chunk_count = combine_chunks_with_no_minimum(
341
  chunks, max_tokens, chunk_delimiter=delimiter, add_ellipsis_for_overflow=True)
342
  if dropped_chunk_count > 0:
343
- print(f"Warning: {dropped_chunk_count} chunks were dropped due to exceeding the token limit.")
344
  combined_chunks = [f"{chunk}{delimiter}" for chunk in combined_chunks]
345
  return combined_chunks
346
 
347
 
348
 
349
 
350
- # ????FIXME
351
- def recursive_summarize_chunks(chunks, summarize_func, custom_prompt, temp=None, system_prompt=None):
 
352
  logging.debug("recursive_summarize_chunks...")
353
  summarized_chunks = []
354
  current_summary = ""
355
 
356
- logging.debug(f"recursive_summarize_chunks: Summarizing {len(chunks)} chunks recursively...")
357
- logging.debug(f"recursive_summarize_chunks: temperature is @ {temp}")
358
  for i, chunk in enumerate(chunks):
359
  if i == 0:
360
  current_summary = summarize_func(chunk, custom_prompt, temp, system_prompt)
@@ -406,20 +477,20 @@ Natural language processing has its roots in the 1950s. Already in 1950, Alan Tu
406
  #
407
 
408
  # Chunk text into segments based on semantic similarity
409
- def count_units(text, unit='words'):
410
  if unit == 'words':
411
  return len(text.split())
412
  elif unit == 'tokens':
413
- return len(word_tokenize(text))
414
  elif unit == 'characters':
415
  return len(text)
416
  else:
417
  raise ValueError("Invalid unit. Choose 'words', 'tokens', or 'characters'.")
418
 
419
 
420
- def semantic_chunking(text, max_chunk_size=2000, unit='words'):
 
421
  logging.debug("semantic_chunking...")
422
- nltk.download('punkt', quiet=True)
423
  sentences = sent_tokenize(text)
424
  vectorizer = TfidfVectorizer()
425
  sentence_vectors = vectorizer.fit_transform(sentences)
@@ -432,9 +503,9 @@ def semantic_chunking(text, max_chunk_size=2000, unit='words'):
432
  sentence_size = count_units(sentence, unit)
433
  if current_size + sentence_size > max_chunk_size and current_chunk:
434
  chunks.append(' '.join(current_chunk))
435
- overlap_size = count_units(' '.join(current_chunk[-3:]), unit) # Use last 3 sentences for overlap
436
- current_chunk = current_chunk[-3:] # Keep last 3 sentences for overlap
437
- current_size = overlap_size
438
 
439
  current_chunk.append(sentence)
440
  current_size += sentence_size
@@ -445,9 +516,8 @@ def semantic_chunking(text, max_chunk_size=2000, unit='words'):
445
  similarity = cosine_similarity(current_vector, next_vector)[0][0]
446
  if similarity < 0.5 and current_size >= max_chunk_size // 2:
447
  chunks.append(' '.join(current_chunk))
448
- overlap_size = count_units(' '.join(current_chunk[-3:]), unit)
449
  current_chunk = current_chunk[-3:]
450
- current_size = overlap_size
451
 
452
  if current_chunk:
453
  chunks.append(' '.join(current_chunk))
@@ -455,7 +525,7 @@ def semantic_chunking(text, max_chunk_size=2000, unit='words'):
455
  return chunks
456
 
457
 
458
- def semantic_chunk_long_file(file_path, max_chunk_size=1000, overlap=100, unit='words'):
459
  logging.debug("semantic_chunk_long_file...")
460
  try:
461
  with open(file_path, 'r', encoding='utf-8') as file:
@@ -510,6 +580,162 @@ def chunk_for_embedding(text: str, file_name: str, custom_chunk_options: Dict[st
510
  #######################################################################################################################
511
 
512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  #######################################################################################################################
514
  #
515
  # OpenAI Rolling Summarization
@@ -530,45 +756,38 @@ def get_chat_completion(messages, model='gpt-4-turbo'):
530
  def combine_chunks_with_no_minimum(
531
  chunks: List[str],
532
  max_tokens: int,
533
- chunk_delimiter="\n\n",
534
  header: Optional[str] = None,
535
- add_ellipsis_for_overflow=False,
536
  ) -> Tuple[List[str], List[List[int]], int]:
537
  dropped_chunk_count = 0
538
  output = [] # list to hold the final combined chunks
539
  output_indices = [] # list to hold the indices of the final combined chunks
540
- candidate = (
541
- [] if header is None else [header]
542
- ) # list to hold the current combined chunk candidate
543
  candidate_indices = []
544
  for chunk_i, chunk in enumerate(chunks):
545
- chunk_with_header = [chunk] if header is None else [header, chunk]
546
- # FIXME MAKE NOT OPENAI SPECIFIC
547
- if len(openai_tokenize(chunk_delimiter.join(chunk_with_header))) > max_tokens:
548
- print(f"warning: chunk overflow")
549
- if (
550
- add_ellipsis_for_overflow
551
- # FIXME MAKE NOT OPENAI SPECIFIC
552
- and len(openai_tokenize(chunk_delimiter.join(candidate + ["..."]))) <= max_tokens
553
- ):
554
- candidate.append("...")
 
 
 
 
 
 
555
  dropped_chunk_count += 1
556
- continue # this case would break downstream assumptions
557
- # estimate token count with the current chunk added
558
- # FIXME MAKE NOT OPENAI SPECIFIC
559
- extended_candidate_token_count = len(openai_tokenize(chunk_delimiter.join(candidate + [chunk])))
560
- # If the token count exceeds max_tokens, add the current candidate to output and start a new candidate
561
- if extended_candidate_token_count > max_tokens:
562
- output.append(chunk_delimiter.join(candidate))
563
- output_indices.append(candidate_indices)
564
- candidate = chunk_with_header # re-initialize candidate
565
- candidate_indices = [chunk_i]
566
- # otherwise keep extending the candidate
567
  else:
568
- candidate.append(chunk)
569
  candidate_indices.append(chunk_i)
570
- # add the remaining candidate to output if it's not empty
571
- if (header is not None and len(candidate) > 1) or (header is None and len(candidate) > 0):
572
  output.append(chunk_delimiter.join(candidate))
573
  output_indices.append(candidate_indices)
574
  return output, output_indices, dropped_chunk_count
@@ -576,27 +795,25 @@ def combine_chunks_with_no_minimum(
576
 
577
  def rolling_summarize(text: str,
578
  detail: float = 0,
579
- model: str = 'gpt-4-turbo',
580
  additional_instructions: Optional[str] = None,
581
  minimum_chunk_size: Optional[int] = 500,
582
  chunk_delimiter: str = ".",
583
- summarize_recursively=False,
584
- verbose=False):
585
  """
586
  Summarizes a given text by splitting it into chunks, each of which is summarized individually.
587
  The level of detail in the summary can be adjusted, and the process can optionally be made recursive.
588
 
589
  Parameters:
590
  - text (str): The text to be summarized.
591
- - detail (float, optional): A value between 0 and 1
592
- indicating the desired level of detail in the summary. 0 leads to a higher level summary, and 1 results in a more
593
- detailed summary. Defaults to 0.
594
- - additional_instructions (Optional[str], optional): Additional instructions to provide to the
595
- model for customizing summaries. - minimum_chunk_size (Optional[int], optional): The minimum size for text
596
- chunks. Defaults to 500.
597
- - chunk_delimiter (str, optional): The delimiter used to split the text into chunks. Defaults to ".".
598
- - summarize_recursively (bool, optional): If True, summaries are generated recursively, using previous summaries for context.
599
  - verbose (bool, optional): If True, prints detailed information about the chunking process.
 
600
  Returns:
601
  - str: The final compiled summary of the text.
602
 
@@ -606,31 +823,29 @@ def rolling_summarize(text: str,
606
  summarization process. The function returns a compiled summary of all chunks.
607
  """
608
 
609
- # check detail is set correctly
610
- assert 0 <= detail <= 1
611
 
612
- # interpolate the number of chunks based to get specified level of detail
613
- max_chunks = len(chunk_on_delimiter(text, minimum_chunk_size, chunk_delimiter))
 
614
  min_chunks = 1
615
  num_chunks = int(min_chunks + detail * (max_chunks - min_chunks))
616
 
617
- # adjust chunk_size based on interpolated number of chunks
618
- # FIXME MAKE NOT OPENAI SPECIFIC
619
- document_length = len(openai_tokenize(text))
620
- chunk_size = max(minimum_chunk_size, document_length // num_chunks)
621
  text_chunks = chunk_on_delimiter(text, chunk_size, chunk_delimiter)
622
  if verbose:
623
  print(f"Splitting the text into {len(text_chunks)} chunks to be summarized.")
624
- # FIXME MAKE NOT OPENAI SPECIFIC
625
- print(f"Chunk lengths are {[len(openai_tokenize(x)) for x in text_chunks]}")
626
 
627
- # set system message - FIXME
628
  system_message_content = "Rewrite this text in summarized form."
629
- if additional_instructions is not None:
630
  system_message_content += f"\n\n{additional_instructions}"
631
 
632
  accumulated_summaries = []
633
- for i, chunk in enumerate(tqdm(text_chunks)):
634
  if summarize_recursively and accumulated_summaries:
635
  # Combine previous summary with current chunk for recursive summarization
636
  combined_text = accumulated_summaries[-1] + "\n\n" + chunk
@@ -658,8 +873,8 @@ def rolling_summarize(text: str,
658
 
659
  def chunk_ebook_by_chapters(text: str, chunk_options: Dict[str, Any]) -> List[Dict[str, Any]]:
660
  logging.debug("chunk_ebook_by_chapters")
661
- max_chunk_size = chunk_options.get('max_size', 300)
662
- overlap = chunk_options.get('overlap', 0)
663
  custom_pattern = chunk_options.get('custom_chapter_pattern', None)
664
 
665
  # List of chapter heading patterns to try, in order
@@ -685,7 +900,13 @@ def chunk_ebook_by_chapters(text: str, chunk_options: Dict[str, Any]) -> List[Di
685
 
686
  # If no chapters found, return the entire content as one chunk
687
  if not chapter_positions:
688
- return [{'text': text, 'metadata': get_chunk_metadata(text, text, chunk_type="whole_document")}]
 
 
 
 
 
 
689
 
690
  # Split content into chapters
691
  chunks = []
@@ -696,7 +917,7 @@ def chunk_ebook_by_chapters(text: str, chunk_options: Dict[str, Any]) -> List[Di
696
 
697
  # Apply overlap if specified
698
  if overlap > 0 and i > 0:
699
- overlap_start = max(0, start - overlap)
700
  chapter = text[overlap_start:end]
701
 
702
  chunks.append(chapter)
@@ -705,52 +926,19 @@ def chunk_ebook_by_chapters(text: str, chunk_options: Dict[str, Any]) -> List[Di
705
  processed_chunks = post_process_chunks(chunks)
706
 
707
  # Add metadata to chunks
708
- return [{'text': chunk, 'metadata': get_chunk_metadata(chunk, text, chunk_type="chapter", chapter_number=i + 1,
709
- chapter_pattern=used_pattern)}
710
- for i, chunk in enumerate(processed_chunks)]
711
-
 
 
 
 
 
 
 
712
 
713
- # # Example usage
714
- # if __name__ == "__main__":
715
- # sample_ebook_content = """
716
- # # Chapter 1: Introduction
717
- #
718
- # This is the introduction.
719
- #
720
- # ## Section 1.1
721
- #
722
- # Some content here.
723
- #
724
- # # Chapter 2: Main Content
725
- #
726
- # This is the main content.
727
- #
728
- # ## Section 2.1
729
- #
730
- # More content here.
731
- #
732
- # CHAPTER THREE
733
- #
734
- # This is the third chapter.
735
- #
736
- # 4. Fourth Chapter
737
- #
738
- # This is the fourth chapter.
739
- # """
740
- #
741
- # chunk_options = {
742
- # 'method': 'chapters',
743
- # 'max_size': 500,
744
- # 'overlap': 50,
745
- # 'custom_chapter_pattern': r'^CHAPTER\s+[A-Z]+' # Custom pattern for 'CHAPTER THREE' style
746
- # }
747
- #
748
- # chunked_chapters = improved_chunking_process(sample_ebook_content, chunk_options)
749
- #
750
- # for i, chunk in enumerate(chunked_chapters, 1):
751
- # print(f"Chunk {i}:")
752
- # print(chunk['text'])
753
- # print(f"Metadata: {chunk['metadata']}\n")
754
 
755
  #
756
  # End of ebook chapter chunking
@@ -761,13 +949,14 @@ def chunk_ebook_by_chapters(text: str, chunk_options: Dict[str, Any]) -> List[Di
761
  # Functions for adapative chunking:
762
 
763
  # FIXME - punkt
764
- def adaptive_chunk_size(text: str, base_size: int = 1000, min_size: int = 500, max_size: int = 2000) -> int:
765
- # Ensure NLTK data is downloaded
766
- nltk.download('punkt', quiet=True)
767
 
 
768
  # Tokenize the text into sentences
769
  sentences = sent_tokenize(text)
770
 
 
 
 
771
  # Calculate average sentence length
772
  avg_sentence_length = sum(len(s.split()) for s in sentences) / len(sentences)
773
 
 
32
  #
33
  # FIXME - Make sure it only downloads if it already exists, and does a check first.
34
  # Ensure NLTK data is downloaded
35
+ def ensure_nltk_data():
36
+ try:
37
+ nltk.data.find('tokenizers/punkt')
38
+ except LookupError:
39
+ nltk.download('punkt')
40
+ ensure_nltk_data()
41
+
42
  #
43
  # Load GPT2 tokenizer
44
  tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
 
62
  #
63
  # Functions:
64
 
65
+ # Create a chunking class for refactoring FIXME
66
+ # class Chunker:
67
+ # def __init__(self, tokenizer: GPT2Tokenizer):
68
+ # self.tokenizer = tokenizer
69
+ #
70
+ # def detect_language(self, text: str) -> str:
71
+ # try:
72
+ # return detect(text)
73
+ # except:
74
+ # return 'en'
75
+ #
76
+ # def chunk_text(self, text: str, method: str, max_size: int, overlap: int, language: str = None) -> List[str]:
77
+ # if language is None:
78
+ # language = self.detect_language(text)
79
+ #
80
+ # if method == 'words':
81
+ # return self.chunk_text_by_words(text, max_size, overlap, language)
82
+ # elif method == 'sentences':
83
+ # return self.chunk_text_by_sentences(text, max_size, overlap, language)
84
+ # elif method == 'paragraphs':
85
+ # return self.chunk_text_by_paragraphs(text, max_size, overlap)
86
+ # elif method == 'tokens':
87
+ # return self.chunk_text_by_tokens(text, max_size, overlap, language)
88
+ # elif method == 'semantic':
89
+ # return self.semantic_chunking(text, max_size)
90
+ # else:
91
+ # return [text]
92
+
93
  def detect_language(text: str) -> str:
94
  try:
95
  return detect(text)
 
98
  return 'en'
99
 
100
 
101
+ def load_document(file_path: str) -> str:
102
+ with open(file_path, 'r', encoding='utf-8') as file:
103
  text = file.read()
104
+ return re.sub(r'\s+', ' ', text).strip()
105
 
106
 
107
+ def improved_chunking_process(text: str, chunk_options: Dict[str, Any] = None) -> List[Dict[str, Any]]:
108
  logging.debug("Improved chunking process started...")
109
 
110
  # Extract JSON metadata if present
 
125
  text = text[len(header_text):].strip()
126
  logging.debug(f"Extracted header text: {header_text}")
127
 
128
+ options = chunk_options.copy() if chunk_options else {}
129
+ if chunk_options:
130
+ options.update(chunk_options)
131
 
132
  chunk_method = options.get('method', 'words')
133
  max_size = options.get('max_size', 2000)
 
137
  if language is None:
138
  language = detect_language(text)
139
 
140
+ if chunk_method == 'json':
141
+ chunks = chunk_text_by_json(text, max_size=max_size, overlap=overlap)
142
+ else:
143
+ chunks = chunk_text(text, chunk_method, max_size, overlap, language)
144
 
145
  chunks_with_metadata = []
146
  total_chunks = len(chunks)
147
  for i, chunk in enumerate(chunks):
148
  metadata = {
149
+ 'chunk_index': i + 1,
150
  'total_chunks': total_chunks,
151
  'chunk_method': chunk_method,
152
  'max_size': max_size,
153
  'overlap': overlap,
154
  'language': language,
155
+ 'relative_position': (i + 1) / total_chunks
156
  }
157
  metadata.update(json_content) # Add the extracted JSON content to metadata
158
  metadata['header_text'] = header_text # Add the header text to metadata
159
 
160
+ if chunk_method == 'json':
161
+ chunk_text_content = json.dumps(chunk['json'], ensure_ascii=False)
162
+ else:
163
+ chunk_text_content = chunk
164
+
165
  chunks_with_metadata.append({
166
+ 'text': chunk_text_content,
167
  'metadata': metadata
168
  })
169
 
170
  return chunks_with_metadata
171
 
172
 
 
173
  def multi_level_chunking(text: str, method: str, max_size: int, overlap: int, language: str) -> List[str]:
174
  logging.debug("Multi-level chunking process started...")
175
  # First level: chunk by paragraphs
 
179
  chunks = []
180
  for para in paragraphs:
181
  if method == 'words':
182
+ chunks.extend(chunk_text_by_words(para, max_words=max_size, overlap=overlap, language=language))
183
  elif method == 'sentences':
184
+ chunks.extend(chunk_text_by_sentences(para, max_sentences=max_size, overlap=overlap, language=language))
185
  else:
186
  chunks.append(para)
187
 
188
  return chunks
189
 
190
 
 
191
  # FIXME - ensure language detection occurs in each chunk function
192
+ def chunk_text(text: str, method: str, max_size: int, overlap: int, language: str = None) -> List[str]:
 
193
  if method == 'words':
194
  logging.debug("Chunking by words...")
195
+ return chunk_text_by_words(text, max_words=max_size, overlap=overlap, language=language)
196
  elif method == 'sentences':
197
  logging.debug("Chunking by sentences...")
198
+ return chunk_text_by_sentences(text, max_sentences=max_size, overlap=overlap, language=language)
199
  elif method == 'paragraphs':
200
  logging.debug("Chunking by paragraphs...")
201
+ return chunk_text_by_paragraphs(text, max_paragraphs=max_size, overlap=overlap)
202
  elif method == 'tokens':
203
  logging.debug("Chunking by tokens...")
204
+ return chunk_text_by_tokens(text, max_tokens=max_size, overlap=overlap)
205
  elif method == 'semantic':
206
  logging.debug("Chunking by semantic similarity...")
207
+ return semantic_chunking(text, max_chunk_size=max_size)
208
  else:
209
+ logging.warning(f"Unknown chunking method '{method}'. Returning full text as a single chunk.")
210
  return [text]
211
 
212
  def determine_chunk_position(relative_position: float) -> str:
 
245
  if language is None:
246
  language = detect_language(text)
247
 
 
 
248
  if language.startswith('zh'): # Chinese
249
  import jieba
250
+ # Use jieba to perform sentence segmentation
251
+ # jieba does not support sentence segmentation out of the box
252
+ # Use punctuation as delimiters
253
+ sentences = re.split(r'[。!?;]', text)
254
+ sentences = [s.strip() for s in sentences if s.strip()]
255
  elif language == 'ja': # Japanese
256
  import fugashi
257
  tagger = fugashi.Tagger()
258
+ # Simple sentence segmentation based on punctuation
259
+ sentences = re.split(r'[。!?]', text)
260
+ sentences = [s.strip() for s in sentences if s.strip()]
261
  else: # Default to NLTK for other languages
262
+ try:
263
+ sentences = sent_tokenize(text, language=language)
264
+ except LookupError:
265
+ logging.warning(f"Punkt tokenizer not found for language '{language}'. Using default 'english'.")
266
+ sentences = sent_tokenize(text, language='english')
267
 
268
  chunks = []
269
+ previous_overlap = []
270
+
271
  for i in range(0, len(sentences), max_sentences - overlap):
272
+ current_sentences = sentences[i:i + max_sentences]
273
+ if overlap > 0 and previous_overlap:
274
+ current_sentences = previous_overlap + current_sentences
275
+ chunk = ' '.join(current_sentences)
276
  chunks.append(chunk)
277
+ previous_overlap = sentences[i + max_sentences - overlap:i + max_sentences] if overlap > 0 else []
278
+
279
  return post_process_chunks(chunks)
280
 
281
 
 
312
  chunks.append(' '.join(current_chunk))
313
 
314
  return post_process_chunks(chunks)
315
+ # def chunk_text_by_tokens(text: str, max_tokens: int = 1000, overlap: int = 0) -> List[str]:
316
+ # logging.debug("chunk_text_by_tokens...")
317
+ # # Use GPT2 tokenizer for tokenization
318
+ # tokens = tokenizer.encode(text)
319
+ # chunks = []
320
+ # for i in range(0, len(tokens), max_tokens - overlap):
321
+ # chunk_tokens = tokens[i:i + max_tokens]
322
+ # chunk = tokenizer.decode(chunk_tokens)
323
+ # chunks.append(chunk)
324
+ # return post_process_chunks(chunks)
325
 
326
 
327
  def post_process_chunks(chunks: List[str]) -> List[str]:
 
330
 
331
  # FIXME - F
332
  def get_chunk_metadata(chunk: str, full_text: str, chunk_type: str = "generic",
333
+ chapter_number: Optional[int] = None,
334
+ chapter_pattern: Optional[str] = None,
335
+ language: str = None) -> Dict[str, Any]:
336
+ """
337
+ Generate metadata for a chunk based on its position in the full text.
338
+ """
339
+ chunk_length = len(chunk)
340
+ start_index = full_text.find(chunk)
341
+ end_index = start_index + chunk_length if start_index != -1 else None
342
+
343
+ # Calculate a hash for the chunk
344
+ chunk_hash = hashlib.md5(chunk.encode()).hexdigest()
345
+
346
+ metadata = {
347
+ 'start_index': start_index,
348
+ 'end_index': end_index,
349
+ 'word_count': len(chunk.split()),
350
+ 'char_count': chunk_length,
351
+ 'chunk_type': chunk_type,
352
+ 'language': language,
353
+ 'chunk_hash': chunk_hash,
354
+ 'relative_position': start_index / len(full_text) if len(full_text) > 0 and start_index != -1 else 0
355
+ }
356
 
357
+ if chunk_type == "chapter":
358
+ metadata['chapter_number'] = chapter_number
359
+ metadata['chapter_pattern'] = chapter_pattern
360
 
361
+ return metadata
 
 
 
362
 
363
 
364
  def process_document_with_metadata(text: str, chunk_options: Dict[str, Any],
 
372
 
373
 
374
  # Hybrid approach, chunk each sentence while ensuring total token size does not exceed a maximum number
375
+ def chunk_text_hybrid(text: str, max_tokens: int = 1000, overlap: int = 0) -> List[str]:
376
  logging.debug("chunk_text_hybrid...")
377
+ sentences = sent_tokenize(text)
378
  chunks = []
379
  current_chunk = []
380
  current_length = 0
381
 
382
  for sentence in sentences:
383
  tokens = tokenizer.encode(sentence)
384
+ if current_length + len(tokens) > max_tokens and current_chunk:
 
 
 
385
  chunks.append(' '.join(current_chunk))
386
+ # Handle overlap
387
+ if overlap > 0:
388
+ overlap_tokens = tokenizer.encode(' '.join(current_chunk[-overlap:]))
389
+ current_chunk = current_chunk[-overlap:]
390
+ current_length = len(overlap_tokens)
391
+ else:
392
+ current_chunk = []
393
+ current_length = 0
394
+
395
+ current_chunk.append(sentence)
396
+ current_length += len(tokens)
397
 
398
  if current_chunk:
399
  chunks.append(' '.join(current_chunk))
400
 
401
+ return post_process_chunks(chunks)
402
 
403
 
404
  # Thanks openai
 
410
  combined_chunks, _, dropped_chunk_count = combine_chunks_with_no_minimum(
411
  chunks, max_tokens, chunk_delimiter=delimiter, add_ellipsis_for_overflow=True)
412
  if dropped_chunk_count > 0:
413
+ logging.warning(f"Warning: {dropped_chunk_count} chunks were dropped due to exceeding the token limit.")
414
  combined_chunks = [f"{chunk}{delimiter}" for chunk in combined_chunks]
415
  return combined_chunks
416
 
417
 
418
 
419
 
420
+ # FIXME
421
+ def recursive_summarize_chunks(chunks: List[str], summarize_func, custom_prompt: Optional[str] = None,
422
+ temp: Optional[float] = None, system_prompt: Optional[str] = None) -> List[str]:
423
  logging.debug("recursive_summarize_chunks...")
424
  summarized_chunks = []
425
  current_summary = ""
426
 
427
+ logging.debug(f"Summarizing {len(chunks)} chunks recursively...")
428
+ logging.debug(f"Temperature is set to {temp}")
429
  for i, chunk in enumerate(chunks):
430
  if i == 0:
431
  current_summary = summarize_func(chunk, custom_prompt, temp, system_prompt)
 
477
  #
478
 
479
  # Chunk text into segments based on semantic similarity
480
+ def count_units(text: str, unit: str = 'words') -> int:
481
  if unit == 'words':
482
  return len(text.split())
483
  elif unit == 'tokens':
484
+ return len(tokenizer.encode(text))
485
  elif unit == 'characters':
486
  return len(text)
487
  else:
488
  raise ValueError("Invalid unit. Choose 'words', 'tokens', or 'characters'.")
489
 
490
 
491
+
492
+ def semantic_chunking(text: str, max_chunk_size: int = 2000, unit: str = 'words') -> List[str]:
493
  logging.debug("semantic_chunking...")
 
494
  sentences = sent_tokenize(text)
495
  vectorizer = TfidfVectorizer()
496
  sentence_vectors = vectorizer.fit_transform(sentences)
 
503
  sentence_size = count_units(sentence, unit)
504
  if current_size + sentence_size > max_chunk_size and current_chunk:
505
  chunks.append(' '.join(current_chunk))
506
+ # Use last 3 sentences for overlap
507
+ current_chunk = current_chunk[-3:]
508
+ current_size = count_units(' '.join(current_chunk), unit)
509
 
510
  current_chunk.append(sentence)
511
  current_size += sentence_size
 
516
  similarity = cosine_similarity(current_vector, next_vector)[0][0]
517
  if similarity < 0.5 and current_size >= max_chunk_size // 2:
518
  chunks.append(' '.join(current_chunk))
 
519
  current_chunk = current_chunk[-3:]
520
+ current_size = count_units(' '.join(current_chunk), unit)
521
 
522
  if current_chunk:
523
  chunks.append(' '.join(current_chunk))
 
525
  return chunks
526
 
527
 
528
+ def semantic_chunk_long_file(file_path: str, max_chunk_size: int = 1000, overlap: int = 100, unit: str = 'words') -> Optional[List[str]]:
529
  logging.debug("semantic_chunk_long_file...")
530
  try:
531
  with open(file_path, 'r', encoding='utf-8') as file:
 
580
  #######################################################################################################################
581
 
582
 
583
+ #######################################################################################################################
584
+ #
585
+ # JSON Chunking
586
+
587
+ # FIXME
588
+ def chunk_text_by_json(text: str, max_size: int = 1000, overlap: int = 0) -> List[Dict[str, Any]]:
589
+ """
590
+ Chunk JSON-formatted text into smaller JSON chunks while preserving structure.
591
+
592
+ Parameters:
593
+ - text (str): The JSON-formatted text to be chunked.
594
+ - max_size (int): Maximum number of items or characters per chunk.
595
+ - overlap (int): Number of items or characters to overlap between chunks.
596
+
597
+ Returns:
598
+ - List[Dict[str, Any]]: A list of chunks with their metadata.
599
+ """
600
+ logging.debug("chunk_text_by_json started...")
601
+ try:
602
+ json_data = json.loads(text)
603
+ except json.JSONDecodeError as e:
604
+ logging.error(f"Invalid JSON data: {e}")
605
+ raise ValueError(f"Invalid JSON data: {e}")
606
+
607
+ # Determine if JSON data is a list or a dict
608
+ if isinstance(json_data, list):
609
+ return chunk_json_list(json_data, max_size, overlap)
610
+ elif isinstance(json_data, dict):
611
+ return chunk_json_dict(json_data, max_size, overlap)
612
+ else:
613
+ logging.error("Unsupported JSON structure. Only JSON objects and arrays are supported.")
614
+ raise ValueError("Unsupported JSON structure. Only JSON objects and arrays are supported.")
615
+
616
+
617
+ def chunk_json_list(json_list: List[Any], max_size: int, overlap: int) -> List[Dict[str, Any]]:
618
+ """
619
+ Chunk a JSON array into smaller chunks.
620
+
621
+ Parameters:
622
+ - json_list (List[Any]): The JSON array to be chunked.
623
+ - max_size (int): Maximum number of items per chunk.
624
+ - overlap (int): Number of items to overlap between chunks.
625
+
626
+ Returns:
627
+ - List[Dict[str, Any]]: A list of JSON chunks with metadata.
628
+ """
629
+ logging.debug("chunk_json_list started...")
630
+ chunks = []
631
+ total_items = len(json_list)
632
+ step = max_size - overlap
633
+ if step <= 0:
634
+ raise ValueError("max_size must be greater than overlap.")
635
+
636
+ for i in range(0, total_items, step):
637
+ chunk = json_list[i:i + max_size]
638
+ metadata = {
639
+ 'chunk_index': i // step + 1,
640
+ 'total_chunks': (total_items + step - 1) // step,
641
+ 'chunk_method': 'json_list',
642
+ 'max_size': max_size,
643
+ 'overlap': overlap,
644
+ 'relative_position': i / total_items
645
+ }
646
+ chunks.append({
647
+ 'json': chunk,
648
+ 'metadata': metadata
649
+ })
650
+
651
+ logging.debug(f"chunk_json_list created {len(chunks)} chunks.")
652
+ return chunks
653
+
654
+
655
+
656
+ def chunk_json_dict(json_dict: Dict[str, Any], max_size: int, overlap: int) -> List[Dict[str, Any]]:
657
+ """
658
+ Chunk a JSON object into smaller chunks based on its 'data' key while preserving other keys like 'metadata'.
659
+
660
+ Parameters:
661
+ - json_dict (Dict[str, Any]): The JSON object to be chunked.
662
+ - max_size (int): Maximum number of key-value pairs per chunk in the 'data' section.
663
+ - overlap (int): Number of key-value pairs to overlap between chunks.
664
+
665
+ Returns:
666
+ - List[Dict[str, Any]]: A list of JSON chunks with metadata.
667
+ """
668
+ logging.debug("chunk_json_dict started...")
669
+
670
+ # Preserve non-chunked sections
671
+ preserved_keys = ['metadata']
672
+ preserved_data = {key: value for key, value in json_dict.items() if key in preserved_keys}
673
+
674
+ # Identify the chunkable section
675
+ chunkable_key = 'data'
676
+ if chunkable_key not in json_dict or not isinstance(json_dict[chunkable_key], dict):
677
+ logging.error("No chunkable 'data' section found in JSON dictionary.")
678
+ raise ValueError("No chunkable 'data' section found in JSON dictionary.")
679
+
680
+ chunkable_data = json_dict[chunkable_key]
681
+ data_keys = list(chunkable_data.keys())
682
+ total_keys = len(data_keys)
683
+ chunks = []
684
+ step = max_size - overlap
685
+ if step <= 0:
686
+ raise ValueError("max_size must be greater than overlap.")
687
+
688
+ # Adjust the loop to prevent creating an extra chunk
689
+ for i in range(0, total_keys, step):
690
+ chunk_keys = data_keys[i:i + max_size]
691
+
692
+ # Handle overlap
693
+ if i != 0 and overlap > 0:
694
+ overlap_keys = data_keys[i - overlap:i]
695
+ chunk_keys = overlap_keys + chunk_keys
696
+
697
+ # Remove duplicate keys caused by overlap
698
+ unique_chunk_keys = []
699
+ seen_keys = set()
700
+ for key in chunk_keys:
701
+ if key not in seen_keys:
702
+ unique_chunk_keys.append(key)
703
+ seen_keys.add(key)
704
+
705
+ chunk_data = {key: chunkable_data[key] for key in unique_chunk_keys}
706
+
707
+ metadata = {
708
+ 'chunk_index': (i // step) + 1,
709
+ 'total_chunks': (total_keys + step - 1) // step,
710
+ 'chunk_method': 'json_dict',
711
+ 'max_size': max_size,
712
+ 'overlap': overlap,
713
+ 'language': 'english', # Assuming English; modify as needed
714
+ 'relative_position': (i // step + 1) / ((total_keys + step - 1) // step)
715
+ }
716
+
717
+ # Merge preserved data into metadata
718
+ metadata.update(preserved_data.get('metadata', {}))
719
+
720
+ # Create the chunk with preserved data
721
+ chunk = {
722
+ 'metadata': preserved_data,
723
+ 'data': chunk_data
724
+ }
725
+
726
+ chunks.append({
727
+ 'json': chunk,
728
+ 'metadata': metadata
729
+ })
730
+
731
+ logging.debug(f"chunk_json_dict created {len(chunks)} chunks.")
732
+ return chunks
733
+
734
+
735
+ #
736
+ # End of JSON Chunking
737
+ #######################################################################################################################
738
+
739
  #######################################################################################################################
740
  #
741
  # OpenAI Rolling Summarization
 
756
  def combine_chunks_with_no_minimum(
757
  chunks: List[str],
758
  max_tokens: int,
759
+ chunk_delimiter: str = "\n\n",
760
  header: Optional[str] = None,
761
+ add_ellipsis_for_overflow: bool = False,
762
  ) -> Tuple[List[str], List[List[int]], int]:
763
  dropped_chunk_count = 0
764
  output = [] # list to hold the final combined chunks
765
  output_indices = [] # list to hold the indices of the final combined chunks
766
+ candidate = [header] if header else [] # list to hold the current combined chunk candidate
 
 
767
  candidate_indices = []
768
  for chunk_i, chunk in enumerate(chunks):
769
+ chunk_with_header = [chunk] if not header else [header, chunk]
770
+ combined_text = chunk_delimiter.join(candidate + chunk_with_header)
771
+ token_count = len(tokenizer.encode(combined_text))
772
+ if token_count > max_tokens:
773
+ if add_ellipsis_for_overflow and len(candidate) > 0:
774
+ ellipsis_text = chunk_delimiter.join(candidate + ["..."])
775
+ if len(tokenizer.encode(ellipsis_text)) <= max_tokens:
776
+ candidate = candidate + ["..."]
777
+ dropped_chunk_count += 1
778
+ if len(candidate) > 0:
779
+ output.append(chunk_delimiter.join(candidate))
780
+ output_indices.append(candidate_indices)
781
+ candidate = chunk_with_header
782
+ candidate_indices = [chunk_i]
783
+ else:
784
+ logging.warning(f"Single chunk at index {chunk_i} exceeds max_tokens and will be dropped.")
785
  dropped_chunk_count += 1
 
 
 
 
 
 
 
 
 
 
 
786
  else:
787
+ candidate.extend(chunk_with_header)
788
  candidate_indices.append(chunk_i)
789
+
790
+ if candidate:
791
  output.append(chunk_delimiter.join(candidate))
792
  output_indices.append(candidate_indices)
793
  return output, output_indices, dropped_chunk_count
 
795
 
796
  def rolling_summarize(text: str,
797
  detail: float = 0,
798
+ model: str = 'gpt-4o',
799
  additional_instructions: Optional[str] = None,
800
  minimum_chunk_size: Optional[int] = 500,
801
  chunk_delimiter: str = ".",
802
+ summarize_recursively: bool = False,
803
+ verbose: bool = False) -> str:
804
  """
805
  Summarizes a given text by splitting it into chunks, each of which is summarized individually.
806
  The level of detail in the summary can be adjusted, and the process can optionally be made recursive.
807
 
808
  Parameters:
809
  - text (str): The text to be summarized.
810
+ - detail (float, optional): A value between 0 and 1 indicating the desired level of detail in the summary.
811
+ - additional_instructions (Optional[str], optional): Additional instructions for the model.
812
+ - minimum_chunk_size (Optional[int], optional): The minimum size for text chunks.
813
+ - chunk_delimiter (str, optional): The delimiter used to split the text into chunks.
814
+ - summarize_recursively (bool, optional): If True, summaries are generated recursively.
 
 
 
815
  - verbose (bool, optional): If True, prints detailed information about the chunking process.
816
+
817
  Returns:
818
  - str: The final compiled summary of the text.
819
 
 
823
  summarization process. The function returns a compiled summary of all chunks.
824
  """
825
 
826
+ # Check detail is set correctly
827
+ assert 0 <= detail <= 1, "Detail must be between 0 and 1."
828
 
829
+ # Interpolate the number of chunks based on the detail parameter
830
+ text_length = len(tokenizer.encode(text))
831
+ max_chunks = text_length // minimum_chunk_size if minimum_chunk_size else 10
832
  min_chunks = 1
833
  num_chunks = int(min_chunks + detail * (max_chunks - min_chunks))
834
 
835
+ # Adjust chunk_size based on interpolated number of chunks
836
+ chunk_size = max(minimum_chunk_size, text_length // num_chunks) if num_chunks else text_length
 
 
837
  text_chunks = chunk_on_delimiter(text, chunk_size, chunk_delimiter)
838
  if verbose:
839
  print(f"Splitting the text into {len(text_chunks)} chunks to be summarized.")
840
+ print(f"Chunk lengths are {[len(tokenizer.encode(x)) for x in text_chunks]} tokens.")
 
841
 
842
+ # Set system message
843
  system_message_content = "Rewrite this text in summarized form."
844
+ if additional_instructions:
845
  system_message_content += f"\n\n{additional_instructions}"
846
 
847
  accumulated_summaries = []
848
+ for i, chunk in enumerate(tqdm(text_chunks, desc="Summarizing chunks")):
849
  if summarize_recursively and accumulated_summaries:
850
  # Combine previous summary with current chunk for recursive summarization
851
  combined_text = accumulated_summaries[-1] + "\n\n" + chunk
 
873
 
874
  def chunk_ebook_by_chapters(text: str, chunk_options: Dict[str, Any]) -> List[Dict[str, Any]]:
875
  logging.debug("chunk_ebook_by_chapters")
876
+ max_chunk_size = int(chunk_options.get('max_size', 300))
877
+ overlap = int(chunk_options.get('overlap', 0))
878
  custom_pattern = chunk_options.get('custom_chapter_pattern', None)
879
 
880
  # List of chapter heading patterns to try, in order
 
900
 
901
  # If no chapters found, return the entire content as one chunk
902
  if not chapter_positions:
903
+ metadata = get_chunk_metadata(
904
+ chunk=text,
905
+ full_text=text,
906
+ chunk_type="whole_document",
907
+ language=chunk_options.get('language', 'english')
908
+ )
909
+ return [{'text': text, 'metadata': metadata}]
910
 
911
  # Split content into chapters
912
  chunks = []
 
917
 
918
  # Apply overlap if specified
919
  if overlap > 0 and i > 0:
920
+ overlap_start = max(0, chapter_positions[i] - overlap)
921
  chapter = text[overlap_start:end]
922
 
923
  chunks.append(chapter)
 
926
  processed_chunks = post_process_chunks(chunks)
927
 
928
  # Add metadata to chunks
929
+ chunks_with_metadata = []
930
+ for i, chunk in enumerate(processed_chunks):
931
+ metadata = get_chunk_metadata(
932
+ chunk=chunk,
933
+ full_text=text,
934
+ chunk_type="chapter",
935
+ chapter_number=i + 1,
936
+ chapter_pattern=used_pattern,
937
+ language=chunk_options.get('language', 'english')
938
+ )
939
+ chunks_with_metadata.append({'text': chunk, 'metadata': metadata})
940
 
941
+ return chunks_with_metadata
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
942
 
943
  #
944
  # End of ebook chapter chunking
 
949
  # Functions for adapative chunking:
950
 
951
  # FIXME - punkt
 
 
 
952
 
953
+ def adaptive_chunk_size(text: str, base_size: int = 1000, min_size: int = 500, max_size: int = 2000) -> int:
954
  # Tokenize the text into sentences
955
  sentences = sent_tokenize(text)
956
 
957
+ if not sentences:
958
+ return base_size
959
+
960
  # Calculate average sentence length
961
  avg_sentence_length = sum(len(s.split()) for s in sentences) / len(sentences)
962
 
App_Function_Libraries/LLM_API_Calls.py CHANGED
@@ -1,966 +1,1109 @@
1
- # Summarization_General_Lib.py
2
- #########################################
3
- # General Summarization Library
4
- # This library is used to perform summarization.
5
- #
6
- ####
7
- ####################
8
- # Function List
9
- #
10
- # 1. extract_text_from_segments(segments: List[Dict]) -> str
11
- # 2. chat_with_openai(api_key, file_path, custom_prompt_arg)
12
- # 3. chat_with_anthropic(api_key, file_path, model, custom_prompt_arg, max_retries=3, retry_delay=5)
13
- # 4. chat_with_cohere(api_key, file_path, model, custom_prompt_arg)
14
- # 5. chat_with_groq(api_key, input_data, custom_prompt_arg, system_prompt=None):
15
- # 6. chat_with_openrouter(api_key, input_data, custom_prompt_arg, system_prompt=None)
16
- # 7. chat_with_huggingface(api_key, input_data, custom_prompt_arg, system_prompt=None)
17
- # 8. chat_with_deepseek(api_key, input_data, custom_prompt_arg, system_prompt=None)
18
- # 9. chat_with_vllm(input_data, custom_prompt_input, api_key=None, vllm_api_url="http://127.0.0.1:8000/v1/chat/completions", system_prompt=None)
19
- #
20
- #
21
- ####################
22
- #
23
- # Import necessary libraries
24
- import json
25
- import logging
26
- import os
27
- import time
28
- from typing import List
29
-
30
- import requests
31
- #
32
- # Import 3rd-Party Libraries
33
- from requests import RequestException
34
- #
35
- # Import Local libraries
36
- from App_Function_Libraries.Utils.Utils import load_and_log_configs
37
- #
38
- #######################################################################################################################
39
- # Function Definitions
40
- #
41
-
42
- #FIXME: Update to include full arguments
43
-
44
- def extract_text_from_segments(segments):
45
- logging.debug(f"Segments received: {segments}")
46
- logging.debug(f"Type of segments: {type(segments)}")
47
-
48
- text = ""
49
-
50
- if isinstance(segments, list):
51
- for segment in segments:
52
- logging.debug(f"Current segment: {segment}")
53
- logging.debug(f"Type of segment: {type(segment)}")
54
- if 'Text' in segment:
55
- text += segment['Text'] + " "
56
- else:
57
- logging.warning(f"Skipping segment due to missing 'Text' key: {segment}")
58
- else:
59
- logging.warning(f"Unexpected type of 'segments': {type(segments)}")
60
-
61
- return text.strip()
62
-
63
-
64
-
65
- def get_openai_embeddings(input_data: str, model: str) -> List[float]:
66
- """
67
- Get embeddings for the input text from OpenAI API.
68
-
69
- Args:
70
- input_data (str): The input text to get embeddings for.
71
- model (str): The model to use for generating embeddings.
72
-
73
- Returns:
74
- List[float]: The embeddings generated by the API.
75
- """
76
- loaded_config_data = load_and_log_configs()
77
- api_key = loaded_config_data['api_keys']['openai']
78
-
79
- if not api_key:
80
- logging.error("OpenAI: API key not found or is empty")
81
- raise ValueError("OpenAI: API Key Not Provided/Found in Config file or is empty")
82
-
83
- logging.debug(f"OpenAI: Using API Key: {api_key[:5]}...{api_key[-5:]}")
84
- logging.debug(f"OpenAI: Raw input data (first 500 chars): {str(input_data)[:500]}...")
85
- logging.debug(f"OpenAI: Using model: {model}")
86
-
87
- headers = {
88
- 'Authorization': f'Bearer {api_key}',
89
- 'Content-Type': 'application/json'
90
- }
91
-
92
- request_data = {
93
- "input": input_data,
94
- "model": model,
95
- }
96
-
97
- try:
98
- logging.debug("OpenAI: Posting request to embeddings API")
99
- response = requests.post('https://api.openai.com/v1/embeddings', headers=headers, json=request_data)
100
- logging.debug(f"Full API response data: {response}")
101
- if response.status_code == 200:
102
- response_data = response.json()
103
- if 'data' in response_data and len(response_data['data']) > 0:
104
- embedding = response_data['data'][0]['embedding']
105
- logging.debug("OpenAI: Embeddings retrieved successfully")
106
- return embedding
107
- else:
108
- logging.warning("OpenAI: Embedding data not found in the response")
109
- raise ValueError("OpenAI: Embedding data not available in the response")
110
- else:
111
- logging.error(f"OpenAI: Embeddings request failed with status code {response.status_code}")
112
- logging.error(f"OpenAI: Error response: {response.text}")
113
- raise ValueError(f"OpenAI: Failed to retrieve embeddings. Status code: {response.status_code}")
114
- except requests.RequestException as e:
115
- logging.error(f"OpenAI: Error making API request: {str(e)}", exc_info=True)
116
- raise ValueError(f"OpenAI: Error making API request: {str(e)}")
117
- except Exception as e:
118
- logging.error(f"OpenAI: Unexpected error: {str(e)}", exc_info=True)
119
- raise ValueError(f"OpenAI: Unexpected error occurred: {str(e)}")
120
-
121
-
122
- def chat_with_openai(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
123
- loaded_config_data = load_and_log_configs()
124
- openai_api_key = api_key
125
- try:
126
- # API key validation
127
- if not openai_api_key:
128
- logging.info("OpenAI: API key not provided as parameter")
129
- logging.info("OpenAI: Attempting to use API key from config file")
130
- openai_api_key = loaded_config_data['api_keys']['openai']
131
-
132
- if not openai_api_key:
133
- logging.error("OpenAI: API key not found or is empty")
134
- return "OpenAI: API Key Not Provided/Found in Config file or is empty"
135
-
136
- logging.debug(f"OpenAI: Using API Key: {openai_api_key[:5]}...{openai_api_key[-5:]}")
137
-
138
- # Input data handling
139
- logging.debug(f"OpenAI: Raw input data type: {type(input_data)}")
140
- logging.debug(f"OpenAI: Raw input data (first 500 chars): {str(input_data)[:500]}...")
141
-
142
- if isinstance(input_data, str):
143
- if input_data.strip().startswith('{'):
144
- # It's likely a JSON string
145
- logging.debug("OpenAI: Parsing provided JSON string data for summarization")
146
- try:
147
- data = json.loads(input_data)
148
- except json.JSONDecodeError as e:
149
- logging.error(f"OpenAI: Error parsing JSON string: {str(e)}")
150
- return f"OpenAI: Error parsing JSON input: {str(e)}"
151
- elif os.path.isfile(input_data):
152
- logging.debug("OpenAI: Loading JSON data from file for summarization")
153
- with open(input_data, 'r') as file:
154
- data = json.load(file)
155
- else:
156
- logging.debug("OpenAI: Using provided string data for summarization")
157
- data = input_data
158
- else:
159
- data = input_data
160
-
161
- logging.debug(f"OpenAI: Processed data type: {type(data)}")
162
- logging.debug(f"OpenAI: Processed data (first 500 chars): {str(data)[:500]}...")
163
-
164
- # Text extraction
165
- if isinstance(data, dict):
166
- if 'summary' in data:
167
- logging.debug("OpenAI: Summary already exists in the loaded data")
168
- return data['summary']
169
- elif 'segments' in data:
170
- text = extract_text_from_segments(data['segments'])
171
- else:
172
- text = json.dumps(data) # Convert dict to string if no specific format
173
- elif isinstance(data, list):
174
- text = extract_text_from_segments(data)
175
- elif isinstance(data, str):
176
- text = data
177
- else:
178
- raise ValueError(f"OpenAI: Invalid input data format: {type(data)}")
179
-
180
- logging.debug(f"OpenAI: Extracted text (first 500 chars): {text[:500]}...")
181
- logging.debug(f"OpenAI: Custom prompt: {custom_prompt_arg}")
182
-
183
- openai_model = loaded_config_data['models']['openai'] or "gpt-4o"
184
- logging.debug(f"OpenAI: Using model: {openai_model}")
185
-
186
- headers = {
187
- 'Authorization': f'Bearer {openai_api_key}',
188
- 'Content-Type': 'application/json'
189
- }
190
-
191
- logging.debug(
192
- f"OpenAI API Key: {openai_api_key[:5]}...{openai_api_key[-5:] if openai_api_key else None}")
193
- logging.debug("openai: Preparing data + prompt for submittal")
194
- openai_prompt = f"{text} \n\n\n\n{custom_prompt_arg}"
195
- if temp is None:
196
- temp = 0.7
197
- if system_message is None:
198
- system_message = "You are a helpful AI assistant who does whatever the user requests."
199
- temp = float(temp)
200
- data = {
201
- "model": openai_model,
202
- "messages": [
203
- {"role": "system", "content": system_message},
204
- {"role": "user", "content": openai_prompt}
205
- ],
206
- "max_tokens": 4096,
207
- "temperature": temp
208
- }
209
-
210
- logging.debug("OpenAI: Posting request")
211
- response = requests.post('https://api.openai.com/v1/chat/completions', headers=headers, json=data)
212
- logging.debug(f"Full API response data: {response}")
213
- if response.status_code == 200:
214
- response_data = response.json()
215
- logging.debug(response_data)
216
- if 'choices' in response_data and len(response_data['choices']) > 0:
217
- chat_response = response_data['choices'][0]['message']['content'].strip()
218
- logging.debug("openai: Chat Sent successfully")
219
- logging.debug(f"openai: Chat response: {chat_response}")
220
- return chat_response
221
- else:
222
- logging.warning("openai: Chat response not found in the response data")
223
- return "openai: Chat not available"
224
- else:
225
- logging.error(f"OpenAI: Chat request failed with status code {response.status_code}")
226
- logging.error(f"OpenAI: Error response: {response.text}")
227
- return f"OpenAI: Failed to process chat response. Status code: {response.status_code}"
228
- except json.JSONDecodeError as e:
229
- logging.error(f"OpenAI: Error decoding JSON: {str(e)}", exc_info=True)
230
- return f"OpenAI: Error decoding JSON input: {str(e)}"
231
- except requests.RequestException as e:
232
- logging.error(f"OpenAI: Error making API request: {str(e)}", exc_info=True)
233
- return f"OpenAI: Error making API request: {str(e)}"
234
- except Exception as e:
235
- logging.error(f"OpenAI: Unexpected error: {str(e)}", exc_info=True)
236
- return f"OpenAI: Unexpected error occurred: {str(e)}"
237
-
238
-
239
- def chat_with_anthropic(api_key, input_data, model, custom_prompt_arg, max_retries=3, retry_delay=5, system_prompt=None):
240
- try:
241
- loaded_config_data = load_and_log_configs()
242
- global anthropic_api_key
243
- anthropic_api_key = api_key
244
- # API key validation
245
- if not api_key:
246
- logging.info("Anthropic: API key not provided as parameter")
247
- logging.info("Anthropic: Attempting to use API key from config file")
248
- anthropic_api_key = loaded_config_data['api_keys']['anthropic']
249
-
250
- if not api_key or api_key.strip() == "":
251
- logging.error("Anthropic: API key not found or is empty")
252
- return "Anthropic: API Key Not Provided/Found in Config file or is empty"
253
-
254
- logging.debug(f"Anthropic: Using API Key: {api_key[:5]}...{api_key[-5:]}")
255
-
256
- if system_prompt is not None:
257
- logging.debug("Anthropic: Using provided system prompt")
258
- pass
259
- else:
260
- system_prompt = "You are a helpful assistant"
261
-
262
- logging.debug(f"AnthropicAI: Loaded data: {input_data}")
263
- logging.debug(f"AnthropicAI: Type of data: {type(input_data)}")
264
-
265
- anthropic_model = loaded_config_data['models']['anthropic']
266
-
267
- headers = {
268
- 'x-api-key': anthropic_api_key,
269
- 'anthropic-version': '2023-06-01',
270
- 'Content-Type': 'application/json'
271
- }
272
-
273
- anthropic_user_prompt = custom_prompt_arg
274
- logging.debug(f"Anthropic: User Prompt is {anthropic_user_prompt}")
275
- user_message = {
276
- "role": "user",
277
- "content": f"{input_data} \n\n\n\n{anthropic_user_prompt}"
278
- }
279
-
280
- data = {
281
- "model": model,
282
- "max_tokens": 4096, # max _possible_ tokens to return
283
- "messages": [user_message],
284
- "stop_sequences": ["\n\nHuman:"],
285
- "temperature": 0.1,
286
- "top_k": 0,
287
- "top_p": 1.0,
288
- "metadata": {
289
- "user_id": "example_user_id",
290
- },
291
- "stream": False,
292
- "system": f"{system_prompt}"
293
- }
294
-
295
- for attempt in range(max_retries):
296
- try:
297
- logging.debug("anthropic: Posting request to API")
298
- response = requests.post('https://api.anthropic.com/v1/messages', headers=headers, json=data)
299
- logging.debug(f"Full API response data: {response}")
300
- # Check if the status code indicates success
301
- if response.status_code == 200:
302
- logging.debug("anthropic: Post submittal successful")
303
- response_data = response.json()
304
- try:
305
- chat_response = response_data['content'][0]['text'].strip()
306
- logging.debug("anthropic: Chat request successful")
307
- print("Chat request processed successfully.")
308
- return chat_response
309
- except (IndexError, KeyError) as e:
310
- logging.debug("anthropic: Unexpected data in response")
311
- print("Unexpected response format from Anthropic API:", response.text)
312
- return None
313
- elif response.status_code == 500: # Handle internal server error specifically
314
- logging.debug("anthropic: Internal server error")
315
- print("Internal server error from API. Retrying may be necessary.")
316
- time.sleep(retry_delay)
317
- else:
318
- logging.debug(
319
- f"anthropic: Failed to process chat request, status code {response.status_code}: {response.text}")
320
- print(f"Failed to process chat request, status code {response.status_code}: {response.text}")
321
- return None
322
-
323
- except RequestException as e:
324
- logging.error(f"anthropic: Network error during attempt {attempt + 1}/{max_retries}: {str(e)}")
325
- if attempt < max_retries - 1:
326
- time.sleep(retry_delay)
327
- else:
328
- return f"anthropic: Network error: {str(e)}"
329
- except Exception as e:
330
- logging.error(f"anthropic: Error in processing: {str(e)}")
331
- return f"anthropic: Error occurred while processing summary with Anthropic: {str(e)}"
332
-
333
-
334
- # Summarize with Cohere
335
- def chat_with_cohere(api_key, input_data, model, custom_prompt_arg, system_prompt=None):
336
- loaded_config_data = load_and_log_configs()
337
- if api_key is not None:
338
- logging.debug(f"Cohere Chat: API Key from parameter: {api_key[:3]}...{api_key[-3:]}")
339
- logging.debug(f"Cohere Chat: Cohere API Key from config: {loaded_config_data['api_keys']['cohere']}")
340
- try:
341
- # API key validation
342
- if api_key is None:
343
- logging.info("Cohere Chat: API key not provided as parameter")
344
- logging.info("Cohere Chat: Attempting to use API key from config file")
345
- cohere_api_key = loaded_config_data.get('api_keys', {}).get('cohere')
346
- if not cohere_api_key:
347
- logging.error("Cohere Chat: API key not found or is empty")
348
- return "Cohere Chat: API Key Not Provided/Found in Config file or is empty"
349
-
350
- logging.debug(f"Cohere Chat: Using API Key: {cohere_api_key[:3]}...{cohere_api_key[-3:]}")
351
-
352
- logging.debug(f"Cohere Chat: Loaded data: {input_data}")
353
- logging.debug(f"Cohere Chat: Type of data: {type(input_data)}")
354
-
355
- # Ensure model is set
356
- if not model:
357
- model = loaded_config_data['models']['cohere']
358
- logging.debug(f"Cohere Chat: Using model: {model}")
359
-
360
- headers = {
361
- 'accept': 'application/json',
362
- 'content-type': 'application/json',
363
- 'Authorization': f'Bearer {cohere_api_key}'
364
- }
365
-
366
- # Ensure system_prompt is set
367
- if not system_prompt:
368
- system_prompt = "You are a helpful assistant"
369
- logging.debug(f"Cohere Chat: System Prompt being sent is: '{system_prompt}'")
370
-
371
- cohere_prompt = input_data
372
- if custom_prompt_arg:
373
- cohere_prompt += f"\n\n{custom_prompt_arg}"
374
- logging.debug(f"Cohere Chat: User Prompt being sent is: '{cohere_prompt}'")
375
-
376
- data = {
377
- "chat_history": [
378
- {"role": "SYSTEM", "message": system_prompt},
379
- ],
380
- "message": cohere_prompt,
381
- "model": model,
382
- "connectors": [{"id": "web-search"}]
383
- }
384
- logging.debug(f"Cohere Chat: Request data: {json.dumps(data, indent=2)}")
385
-
386
- logging.debug("cohere chat: Submitting request to API endpoint")
387
- print("cohere chat: Submitting request to API endpoint")
388
-
389
- try:
390
- response = requests.post('https://api.cohere.ai/v1/chat', headers=headers, json=data)
391
- logging.debug(f"Cohere Chat: Raw API response: {response.text}")
392
- except requests.RequestException as e:
393
- logging.error(f"Cohere Chat: Error making API request: {str(e)}")
394
- return f"Cohere Chat: Error making API request: {str(e)}"
395
-
396
- if response.status_code == 200:
397
- try:
398
- response_data = response.json()
399
- except json.JSONDecodeError:
400
- logging.error("Cohere Chat: Failed to decode JSON response")
401
- return "Cohere Chat: Failed to decode JSON response"
402
-
403
- if response_data is None:
404
- logging.error("Cohere Chat: No response data received.")
405
- return "Cohere Chat: No response data received."
406
-
407
- logging.debug(f"cohere chat: Full API response data: {json.dumps(response_data, indent=2)}")
408
-
409
- if 'text' in response_data:
410
- chat_response = response_data['text'].strip()
411
- logging.debug("Cohere Chat: Chat request successful")
412
- print("Cohere Chat request processed successfully.")
413
- return chat_response
414
- else:
415
- logging.error("Cohere Chat: Expected 'text' key not found in API response.")
416
- return "Cohere Chat: Expected data not found in API response."
417
- else:
418
- logging.error(f"Cohere Chat: API request failed with status code {response.status_code}: {response.text}")
419
- print(f"Cohere Chat: Failed to process chat response, status code {response.status_code}: {response.text}")
420
- return f"Cohere Chat: API request failed: {response.text}"
421
-
422
- except Exception as e:
423
- logging.error(f"Cohere Chat: Error in processing: {str(e)}", exc_info=True)
424
- return f"Cohere Chat: Error occurred while processing chat request with Cohere: {str(e)}"
425
-
426
-
427
- # https://console.groq.com/docs/quickstart
428
- def chat_with_groq(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
429
- logging.debug("Groq: Summarization process starting...")
430
- try:
431
- logging.debug("Groq: Loading and validating configurations")
432
- loaded_config_data = load_and_log_configs()
433
- if loaded_config_data is None:
434
- logging.error("Failed to load configuration data")
435
- groq_api_key = None
436
- else:
437
- # Prioritize the API key passed as a parameter
438
- if api_key and api_key.strip():
439
- groq_api_key = api_key
440
- logging.info("Groq: Using API key provided as parameter")
441
- else:
442
- # If no parameter is provided, use the key from the config
443
- groq_api_key = loaded_config_data['api_keys'].get('groq')
444
- if groq_api_key:
445
- logging.info("Groq: Using API key from config file")
446
- else:
447
- logging.warning("Groq: No API key found in config file")
448
-
449
- # Final check to ensure we have a valid API key
450
- if not groq_api_key or not groq_api_key.strip():
451
- logging.error("Anthropic: No valid API key available")
452
- # You might want to raise an exception here or handle this case as appropriate for your application
453
- # For example: raise ValueError("No valid Anthropic API key available")
454
-
455
- logging.debug(f"Groq: Using API Key: {groq_api_key[:5]}...{groq_api_key[-5:]}")
456
-
457
- # Transcript data handling & Validation
458
- if isinstance(input_data, str) and os.path.isfile(input_data):
459
- logging.debug("Groq: Loading json data for summarization")
460
- with open(input_data, 'r') as file:
461
- data = json.load(file)
462
- else:
463
- logging.debug("Groq: Using provided string data for summarization")
464
- data = input_data
465
-
466
- # DEBUG - Debug logging to identify sent data
467
- logging.debug(f"Groq: Loaded data: {data[:500]}...(snipped to first 500 chars)")
468
- logging.debug(f"Groq: Type of data: {type(data)}")
469
-
470
- if isinstance(data, dict) and 'summary' in data:
471
- # If the loaded data is a dictionary and already contains a summary, return it
472
- logging.debug("Groq: Summary already exists in the loaded data")
473
- return data['summary']
474
-
475
- # If the loaded data is a list of segment dictionaries or a string, proceed with summarization
476
- if isinstance(data, list):
477
- segments = data
478
- text = extract_text_from_segments(segments)
479
- elif isinstance(data, str):
480
- text = data
481
- else:
482
- raise ValueError("Groq: Invalid input data format")
483
-
484
- # Set the model to be used
485
- groq_model = loaded_config_data['models']['groq']
486
-
487
- if temp is None:
488
- temp = 0.2
489
- temp = float(temp)
490
- if system_message is None:
491
- system_message = "You are a helpful AI assistant who does whatever the user requests."
492
-
493
- headers = {
494
- 'Authorization': f'Bearer {groq_api_key}',
495
- 'Content-Type': 'application/json'
496
- }
497
-
498
- groq_prompt = f"{text} \n\n\n\n{custom_prompt_arg}"
499
- logging.debug("groq: Prompt being sent is {groq_prompt}")
500
-
501
- data = {
502
- "messages": [
503
- {
504
- "role": "system",
505
- "content": system_message,
506
- },
507
- {
508
- "role": "user",
509
- "content": groq_prompt,
510
- }
511
- ],
512
- "model": groq_model,
513
- "temperature": temp
514
- }
515
-
516
- logging.debug("groq: Submitting request to API endpoint")
517
- print("groq: Submitting request to API endpoint")
518
- response = requests.post('https://api.groq.com/openai/v1/chat/completions', headers=headers, json=data)
519
-
520
- response_data = response.json()
521
- logging.debug(f"Full API response data: {response_data}")
522
-
523
- if response.status_code == 200:
524
- logging.debug(response_data)
525
- if 'choices' in response_data and len(response_data['choices']) > 0:
526
- summary = response_data['choices'][0]['message']['content'].strip()
527
- logging.debug("groq: Chat request successful")
528
- print("Groq: Chat request successful.")
529
- return summary
530
- else:
531
- logging.error("Groq(chat): Expected data not found in API response.")
532
- return "Groq(chat): Expected data not found in API response."
533
- else:
534
- logging.error(f"groq: API request failed with status code {response.status_code}: {response.text}")
535
- return f"groq: API request failed: {response.text}"
536
-
537
- except Exception as e:
538
- logging.error("groq: Error in processing: %s", str(e))
539
- return f"groq: Error occurred while processing summary with groq: {str(e)}"
540
-
541
-
542
- def chat_with_openrouter(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
543
- import requests
544
- import json
545
- global openrouter_model, openrouter_api_key
546
- try:
547
- logging.debug("OpenRouter: Loading and validating configurations")
548
- loaded_config_data = load_and_log_configs()
549
- if loaded_config_data is None:
550
- logging.error("Failed to load configuration data")
551
- openrouter_api_key = None
552
- else:
553
- # Prioritize the API key passed as a parameter
554
- if api_key and api_key.strip():
555
- openrouter_api_key = api_key
556
- logging.info("OpenRouter: Using API key provided as parameter")
557
- else:
558
- # If no parameter is provided, use the key from the config
559
- openrouter_api_key = loaded_config_data['api_keys'].get('openrouter')
560
- if openrouter_api_key:
561
- logging.info("OpenRouter: Using API key from config file")
562
- else:
563
- logging.warning("OpenRouter: No API key found in config file")
564
-
565
- # Model Selection validation
566
- logging.debug("OpenRouter: Validating model selection")
567
- loaded_config_data = load_and_log_configs()
568
- openrouter_model = loaded_config_data['models']['openrouter']
569
- logging.debug(f"OpenRouter: Using model from config file: {openrouter_model}")
570
-
571
- # Final check to ensure we have a valid API key
572
- if not openrouter_api_key or not openrouter_api_key.strip():
573
- logging.error("OpenRouter: No valid API key available")
574
- raise ValueError("No valid Anthropic API key available")
575
- except Exception as e:
576
- logging.error("OpenRouter: Error in processing: %s", str(e))
577
- return f"OpenRouter: Error occurred while processing config file with OpenRouter: {str(e)}"
578
-
579
- logging.debug(f"OpenRouter: Using API Key: {openrouter_api_key[:5]}...{openrouter_api_key[-5:]}")
580
-
581
- logging.debug(f"OpenRouter: Using Model: {openrouter_model}")
582
-
583
- if isinstance(input_data, str) and os.path.isfile(input_data):
584
- logging.debug("OpenRouter: Loading json data for summarization")
585
- with open(input_data, 'r') as file:
586
- data = json.load(file)
587
- else:
588
- logging.debug("OpenRouter: Using provided string data for summarization")
589
- data = input_data
590
-
591
- # DEBUG - Debug logging to identify sent data
592
- logging.debug(f"OpenRouter: Loaded data: {data[:500]}...(snipped to first 500 chars)")
593
- logging.debug(f"OpenRouter: Type of data: {type(data)}")
594
-
595
- if isinstance(data, dict) and 'summary' in data:
596
- # If the loaded data is a dictionary and already contains a summary, return it
597
- logging.debug("OpenRouter: Summary already exists in the loaded data")
598
- return data['summary']
599
-
600
- # If the loaded data is a list of segment dictionaries or a string, proceed with summarization
601
- if isinstance(data, list):
602
- segments = data
603
- text = extract_text_from_segments(segments)
604
- elif isinstance(data, str):
605
- text = data
606
- else:
607
- raise ValueError("OpenRouter: Invalid input data format")
608
-
609
- openrouter_prompt = f"{input_data} \n\n\n\n{custom_prompt_arg}"
610
- logging.debug(f"openrouter: User Prompt being sent is {openrouter_prompt}")
611
-
612
- if temp is None:
613
- temp = 0.1
614
- temp = float(temp)
615
- if system_message is None:
616
- system_message = "You are a helpful AI assistant who does whatever the user requests."
617
-
618
- try:
619
- logging.debug("OpenRouter: Submitting request to API endpoint")
620
- print("OpenRouter: Submitting request to API endpoint")
621
- response = requests.post(
622
- url="https://openrouter.ai/api/v1/chat/completions",
623
- headers={
624
- "Authorization": f"Bearer {openrouter_api_key}",
625
- },
626
- data=json.dumps({
627
- "model": openrouter_model,
628
- "messages": [
629
- {"role": "system", "content": system_message},
630
- {"role": "user", "content": openrouter_prompt}
631
- ],
632
- "temperature": temp
633
- })
634
- )
635
-
636
- response_data = response.json()
637
- logging.debug("Full API Response Data: %s", response_data)
638
-
639
- if response.status_code == 200:
640
- if 'choices' in response_data and len(response_data['choices']) > 0:
641
- summary = response_data['choices'][0]['message']['content'].strip()
642
- logging.debug("openrouter: Chat request successful")
643
- print("openrouter: Chat request successful.")
644
- return summary
645
- else:
646
- logging.error("openrouter: Expected data not found in API response.")
647
- return "openrouter: Expected data not found in API response."
648
- else:
649
- logging.error(f"openrouter: API request failed with status code {response.status_code}: {response.text}")
650
- return f"openrouter: API request failed: {response.text}"
651
- except Exception as e:
652
- logging.error("openrouter: Error in processing: %s", str(e))
653
- return f"openrouter: Error occurred while processing chat request with openrouter: {str(e)}"
654
-
655
-
656
- # FIXME: This function is not yet implemented properly
657
- def chat_with_huggingface(api_key, input_data, custom_prompt_arg, system_prompt=None, temp=None):
658
- loaded_config_data = load_and_log_configs()
659
- logging.debug(f"huggingface Chat: Chat request process starting...")
660
- try:
661
- huggingface_api_key = global_huggingface_api_key
662
-
663
- headers = {
664
- "Authorization": f"Bearer {huggingface_api_key}"
665
- }
666
-
667
- # Setup model
668
- huggingface_model = loaded_config_data['models']['huggingface']
669
-
670
- API_URL = f"https://api-inference.huggingface.co/models/{huggingface_model}/v1/chat/completions"
671
- if temp is None:
672
- temp = 1.0
673
- temp = float(temp)
674
- huggingface_prompt = f"{custom_prompt_arg}\n\n\n{input_data}"
675
- logging.debug(f"HuggingFace chat: Prompt being sent is {huggingface_prompt}")
676
- data = {
677
- "model": f"{huggingface_model}",
678
- "messages": [{"role": "user", "content": f"{huggingface_prompt}"}],
679
- "max_tokens": 4096,
680
- "stream": False,
681
- "temperature": temp
682
- }
683
-
684
- logging.debug("HuggingFace Chat: Submitting request...")
685
- response = requests.post(API_URL, headers=headers, json=data)
686
- logging.debug(f"Full API response data: {response.text}")
687
-
688
- if response.status_code == 200:
689
- response_json = response.json()
690
- if "choices" in response_json and len(response_json["choices"]) > 0:
691
- generated_text = response_json["choices"][0]["message"]["content"]
692
- logging.debug("HuggingFace Chat: Chat request successful")
693
- print("HuggingFace Chat: Chat request successful.")
694
- return generated_text.strip()
695
- else:
696
- logging.error("HuggingFace Chat: No generated text in the response")
697
- return "HuggingFace Chat: No generated text in the response"
698
- else:
699
- logging.error(
700
- f"HuggingFace Chat: Chat request failed with status code {response.status_code}: {response.text}")
701
- return f"HuggingFace Chat: Failed to process chat request, status code {response.status_code}: {response.text}"
702
- except Exception as e:
703
- logging.error(f"HuggingFace Chat: Error in processing: {str(e)}")
704
- print(f"HuggingFace Chat: Error occurred while processing chat request with huggingface: {str(e)}")
705
- return None
706
-
707
-
708
- def chat_with_deepseek(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
709
- logging.debug("DeepSeek: Summarization process starting...")
710
- try:
711
- logging.debug("DeepSeek: Loading and validating configurations")
712
- loaded_config_data = load_and_log_configs()
713
- if loaded_config_data is None:
714
- logging.error("Failed to load configuration data")
715
- deepseek_api_key = None
716
- else:
717
- # Prioritize the API key passed as a parameter
718
- if api_key and api_key.strip():
719
- deepseek_api_key = api_key
720
- logging.info("DeepSeek: Using API key provided as parameter")
721
- else:
722
- # If no parameter is provided, use the key from the config
723
- deepseek_api_key = loaded_config_data['api_keys'].get('deepseek')
724
- if deepseek_api_key:
725
- logging.info("DeepSeek: Using API key from config file")
726
- else:
727
- logging.warning("DeepSeek: No API key found in config file")
728
-
729
- # Final check to ensure we have a valid API key
730
- if not deepseek_api_key or not deepseek_api_key.strip():
731
- logging.error("DeepSeek: No valid API key available")
732
- # You might want to raise an exception here or handle this case as appropriate for your application
733
- # For example: raise ValueError("No valid deepseek API key available")
734
-
735
-
736
- logging.debug(f"DeepSeek: Using API Key: {deepseek_api_key[:5]}...{deepseek_api_key[-5:]}")
737
-
738
- # Input data handling
739
- if isinstance(input_data, str) and os.path.isfile(input_data):
740
- logging.debug("DeepSeek: Loading json data for summarization")
741
- with open(input_data, 'r') as file:
742
- data = json.load(file)
743
- else:
744
- logging.debug("DeepSeek: Using provided string data for summarization")
745
- data = input_data
746
-
747
- # DEBUG - Debug logging to identify sent data
748
- logging.debug(f"DeepSeek: Loaded data: {data[:500]}...(snipped to first 500 chars)")
749
- logging.debug(f"DeepSeek: Type of data: {type(data)}")
750
-
751
- if isinstance(data, dict) and 'summary' in data:
752
- # If the loaded data is a dictionary and already contains a summary, return it
753
- logging.debug("DeepSeek: Summary already exists in the loaded data")
754
- return data['summary']
755
-
756
- # Text extraction
757
- if isinstance(data, list):
758
- segments = data
759
- text = extract_text_from_segments(segments)
760
- elif isinstance(data, str):
761
- text = data
762
- else:
763
- raise ValueError("DeepSeek: Invalid input data format")
764
-
765
- deepseek_model = loaded_config_data['models']['deepseek'] or "deepseek-chat"
766
-
767
- if temp is None:
768
- temp = 0.1
769
- temp = float(temp)
770
- if system_message is None:
771
- system_message = "You are a helpful AI assistant who does whatever the user requests."
772
-
773
- headers = {
774
- 'Authorization': f'Bearer {api_key}',
775
- 'Content-Type': 'application/json'
776
- }
777
-
778
- logging.debug(
779
- f"Deepseek API Key: {api_key[:5]}...{api_key[-5:] if api_key else None}")
780
- logging.debug("DeepSeek: Preparing data + prompt for submittal")
781
- deepseek_prompt = f"{text} \n\n\n\n{custom_prompt_arg}"
782
- data = {
783
- "model": deepseek_model,
784
- "messages": [
785
- {"role": "system", "content": system_message},
786
- {"role": "user", "content": deepseek_prompt}
787
- ],
788
- "stream": False,
789
- "temperature": temp
790
- }
791
-
792
- logging.debug("DeepSeek: Posting request")
793
- response = requests.post('https://api.deepseek.com/chat/completions', headers=headers, json=data)
794
- logging.debug(f"Full API response data: {response}")
795
- if response.status_code == 200:
796
- response_data = response.json()
797
- logging.debug(response_data)
798
- if 'choices' in response_data and len(response_data['choices']) > 0:
799
- summary = response_data['choices'][0]['message']['content'].strip()
800
- logging.debug("DeepSeek: Chat request successful")
801
- return summary
802
- else:
803
- logging.warning("DeepSeek: Chat response not found in the response data")
804
- return "DeepSeek: Chat response not available"
805
- else:
806
- logging.error(f"DeepSeek: Chat request failed with status code {response.status_code}")
807
- logging.error(f"DeepSeek: Error response: {response.text}")
808
- return f"DeepSeek: Failed to chat request summary. Status code: {response.status_code}"
809
- except Exception as e:
810
- logging.error(f"DeepSeek: Error in processing: {str(e)}", exc_info=True)
811
- return f"DeepSeek: Error occurred while processing chat request: {str(e)}"
812
-
813
-
814
- def chat_with_mistral(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
815
- logging.debug("Mistral: Chat request made")
816
- try:
817
- logging.debug("Mistral: Loading and validating configurations")
818
- loaded_config_data = load_and_log_configs()
819
- if loaded_config_data is None:
820
- logging.error("Failed to load configuration data")
821
- mistral_api_key = None
822
- else:
823
- # Prioritize the API key passed as a parameter
824
- if api_key and api_key.strip():
825
- mistral_api_key = api_key
826
- logging.info("Mistral: Using API key provided as parameter")
827
- else:
828
- # If no parameter is provided, use the key from the config
829
- mistral_api_key = loaded_config_data['api_keys'].get('mistral')
830
- if mistral_api_key:
831
- logging.info("Mistral: Using API key from config file")
832
- else:
833
- logging.warning("Mistral: No API key found in config file")
834
-
835
- # Final check to ensure we have a valid API key
836
- if not mistral_api_key or not mistral_api_key.strip():
837
- logging.error("Mistral: No valid API key available")
838
- return "Mistral: No valid API key available"
839
-
840
- logging.debug(f"Mistral: Using API Key: {mistral_api_key[:5]}...{mistral_api_key[-5:]}")
841
-
842
- logging.debug("Mistral: Using provided string data")
843
- data = input_data
844
-
845
- # Text extraction
846
- if isinstance(input_data, list):
847
- text = extract_text_from_segments(input_data)
848
- elif isinstance(input_data, str):
849
- text = input_data
850
- else:
851
- raise ValueError("Mistral: Invalid input data format")
852
-
853
- mistral_model = loaded_config_data['models'].get('mistral', "mistral-large-latest")
854
-
855
- temp = float(temp) if temp is not None else 0.2
856
- if system_message is None:
857
- system_message = "You are a helpful AI assistant who does whatever the user requests."
858
-
859
- headers = {
860
- 'Authorization': f'Bearer {mistral_api_key}',
861
- 'Content-Type': 'application/json'
862
- }
863
-
864
- logging.debug(
865
- f"Deepseek API Key: {mistral_api_key[:5]}...{mistral_api_key[-5:] if mistral_api_key else None}")
866
- logging.debug("Mistral: Preparing data + prompt for submittal")
867
- mistral_prompt = f"{custom_prompt_arg}\n\n\n\n{text} "
868
- data = {
869
- "model": mistral_model,
870
- "messages": [
871
- {"role": "system",
872
- "content": system_message},
873
- {"role": "user",
874
- "content": mistral_prompt}
875
- ],
876
- "temperature": temp,
877
- "top_p": 1,
878
- "max_tokens": 4096,
879
- "stream": False,
880
- "safe_prompt": False
881
- }
882
-
883
- logging.debug("Mistral: Posting request")
884
- response = requests.post('https://api.mistral.ai/v1/chat/completions', headers=headers, json=data)
885
- logging.debug(f"Full API response data: {response}")
886
- if response.status_code == 200:
887
- response_data = response.json()
888
- logging.debug(response_data)
889
- if 'choices' in response_data and len(response_data['choices']) > 0:
890
- summary = response_data['choices'][0]['message']['content'].strip()
891
- logging.debug("Mistral: request successful")
892
- return summary
893
- else:
894
- logging.warning("Mistral: Chat response not found in the response data")
895
- return "Mistral: Chat response not available"
896
- else:
897
- logging.error(f"Mistral: Chat request failed with status code {response.status_code}")
898
- logging.error(f"Mistral: Error response: {response.text}")
899
- return f"Mistral: Failed to process summary. Status code: {response.status_code}. Error: {response.text}"
900
- except Exception as e:
901
- logging.error(f"Mistral: Error in processing: {str(e)}", exc_info=True)
902
- return f"Mistral: Error occurred while processing Chat: {str(e)}"
903
-
904
-
905
-
906
- # Stashed in here since OpenAI usage.... #FIXME
907
- # FIXME - https://docs.vllm.ai/en/latest/getting_started/quickstart.html .... Great docs.
908
- # def chat_with_vllm(input_data, custom_prompt_input, api_key=None, vllm_api_url="http://127.0.0.1:8000/v1/chat/completions", system_prompt=None):
909
- # loaded_config_data = load_and_log_configs()
910
- # llm_model = loaded_config_data['models']['vllm']
911
- # # API key validation
912
- # if api_key is None:
913
- # logging.info("vLLM: API key not provided as parameter")
914
- # logging.info("vLLM: Attempting to use API key from config file")
915
- # api_key = loaded_config_data['api_keys']['llama']
916
- #
917
- # if api_key is None or api_key.strip() == "":
918
- # logging.info("vLLM: API key not found or is empty")
919
- # vllm_client = OpenAI(
920
- # base_url=vllm_api_url,
921
- # api_key=custom_prompt_input
922
- # )
923
- #
924
- # if isinstance(input_data, str) and os.path.isfile(input_data):
925
- # logging.debug("vLLM: Loading json data for summarization")
926
- # with open(input_data, 'r') as file:
927
- # data = json.load(file)
928
- # else:
929
- # logging.debug("vLLM: Using provided string data for summarization")
930
- # data = input_data
931
- #
932
- # logging.debug(f"vLLM: Loaded data: {data}")
933
- # logging.debug(f"vLLM: Type of data: {type(data)}")
934
- #
935
- # if isinstance(data, dict) and 'summary' in data:
936
- # # If the loaded data is a dictionary and already contains a summary, return it
937
- # logging.debug("vLLM: Summary already exists in the loaded data")
938
- # return data['summary']
939
- #
940
- # # If the loaded data is a list of segment dictionaries or a string, proceed with summarization
941
- # if isinstance(data, list):
942
- # segments = data
943
- # text = extract_text_from_segments(segments)
944
- # elif isinstance(data, str):
945
- # text = data
946
- # else:
947
- # raise ValueError("Invalid input data format")
948
- #
949
- #
950
- # custom_prompt = custom_prompt_input
951
- #
952
- # completion = client.chat.completions.create(
953
- # model=llm_model,
954
- # messages=[
955
- # {"role": "system", "content": f"{system_prompt}"},
956
- # {"role": "user", "content": f"{text} \n\n\n\n{custom_prompt}"}
957
- # ]
958
- # )
959
- # vllm_summary = completion.choices[0].message.content
960
- # return vllm_summary
961
-
962
-
963
-
964
- #
965
- #
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
966
  #######################################################################################################################
 
1
+ # Summarization_General_Lib.py
2
+ #########################################
3
+ # General Summarization Library
4
+ # This library is used to perform summarization.
5
+ #
6
+ ####
7
+ ####################
8
+ # Function List
9
+ #
10
+ # 1. extract_text_from_segments(segments: List[Dict]) -> str
11
+ # 2. chat_with_openai(api_key, file_path, custom_prompt_arg)
12
+ # 3. chat_with_anthropic(api_key, file_path, model, custom_prompt_arg, max_retries=3, retry_delay=5)
13
+ # 4. chat_with_cohere(api_key, file_path, model, custom_prompt_arg)
14
+ # 5. chat_with_groq(api_key, input_data, custom_prompt_arg, system_prompt=None):
15
+ # 6. chat_with_openrouter(api_key, input_data, custom_prompt_arg, system_prompt=None)
16
+ # 7. chat_with_huggingface(api_key, input_data, custom_prompt_arg, system_prompt=None)
17
+ # 8. chat_with_deepseek(api_key, input_data, custom_prompt_arg, system_prompt=None)
18
+ # 9. chat_with_vllm(input_data, custom_prompt_input, api_key=None, vllm_api_url="http://127.0.0.1:8000/v1/chat/completions", system_prompt=None)
19
+ #
20
+ #
21
+ ####################
22
+ #
23
+ # Import necessary libraries
24
+ import json
25
+ import logging
26
+ import os
27
+ import time
28
+ from typing import List
29
+
30
+ import requests
31
+ #
32
+ # Import 3rd-Party Libraries
33
+ #
34
+ # Import Local libraries
35
+ from App_Function_Libraries.Utils.Utils import load_and_log_configs
36
+ #
37
+ #######################################################################################################################
38
+ # Function Definitions
39
+ #
40
+
41
+ #FIXME: Update to include full arguments
42
+
43
+ def extract_text_from_segments(segments):
44
+ logging.debug(f"Segments received: {segments}")
45
+ logging.debug(f"Type of segments: {type(segments)}")
46
+
47
+ text = ""
48
+
49
+ if isinstance(segments, list):
50
+ for segment in segments:
51
+ logging.debug(f"Current segment: {segment}")
52
+ logging.debug(f"Type of segment: {type(segment)}")
53
+ if 'Text' in segment:
54
+ text += segment['Text'] + " "
55
+ else:
56
+ logging.warning(f"Skipping segment due to missing 'Text' key: {segment}")
57
+ else:
58
+ logging.warning(f"Unexpected type of 'segments': {type(segments)}")
59
+
60
+ return text.strip()
61
+
62
+
63
+
64
+ def get_openai_embeddings(input_data: str, model: str) -> List[float]:
65
+ """
66
+ Get embeddings for the input text from OpenAI API.
67
+
68
+ Args:
69
+ input_data (str): The input text to get embeddings for.
70
+ model (str): The model to use for generating embeddings.
71
+
72
+ Returns:
73
+ List[float]: The embeddings generated by the API.
74
+ """
75
+ loaded_config_data = load_and_log_configs()
76
+ api_key = loaded_config_data['api_keys']['openai']
77
+
78
+ if not api_key:
79
+ logging.error("OpenAI: API key not found or is empty")
80
+ raise ValueError("OpenAI: API Key Not Provided/Found in Config file or is empty")
81
+
82
+ logging.debug(f"OpenAI: Using API Key: {api_key[:5]}...{api_key[-5:]}")
83
+ logging.debug(f"OpenAI: Raw input data (first 500 chars): {str(input_data)[:500]}...")
84
+ logging.debug(f"OpenAI: Using model: {model}")
85
+
86
+ headers = {
87
+ 'Authorization': f'Bearer {api_key}',
88
+ 'Content-Type': 'application/json'
89
+ }
90
+
91
+ request_data = {
92
+ "input": input_data,
93
+ "model": model,
94
+ }
95
+
96
+ try:
97
+ logging.debug("OpenAI: Posting request to embeddings API")
98
+ response = requests.post('https://api.openai.com/v1/embeddings', headers=headers, json=request_data)
99
+ logging.debug(f"Full API response data: {response}")
100
+ if response.status_code == 200:
101
+ response_data = response.json()
102
+ if 'data' in response_data and len(response_data['data']) > 0:
103
+ embedding = response_data['data'][0]['embedding']
104
+ logging.debug("OpenAI: Embeddings retrieved successfully")
105
+ return embedding
106
+ else:
107
+ logging.warning("OpenAI: Embedding data not found in the response")
108
+ raise ValueError("OpenAI: Embedding data not available in the response")
109
+ else:
110
+ logging.error(f"OpenAI: Embeddings request failed with status code {response.status_code}")
111
+ logging.error(f"OpenAI: Error response: {response.text}")
112
+ raise ValueError(f"OpenAI: Failed to retrieve embeddings. Status code: {response.status_code}")
113
+ except requests.RequestException as e:
114
+ logging.error(f"OpenAI: Error making API request: {str(e)}", exc_info=True)
115
+ raise ValueError(f"OpenAI: Error making API request: {str(e)}")
116
+ except Exception as e:
117
+ logging.error(f"OpenAI: Unexpected error: {str(e)}", exc_info=True)
118
+ raise ValueError(f"OpenAI: Unexpected error occurred: {str(e)}")
119
+
120
+
121
+ def chat_with_openai(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
122
+ loaded_config_data = load_and_log_configs()
123
+ openai_api_key = api_key
124
+ try:
125
+ # API key validation
126
+ if not openai_api_key:
127
+ logging.info("OpenAI: API key not provided as parameter")
128
+ logging.info("OpenAI: Attempting to use API key from config file")
129
+ openai_api_key = loaded_config_data['api_keys']['openai']
130
+
131
+ if not openai_api_key:
132
+ logging.error("OpenAI: API key not found or is empty")
133
+ return "OpenAI: API Key Not Provided/Found in Config file or is empty"
134
+
135
+ logging.debug(f"OpenAI: Using API Key: {openai_api_key[:5]}...{openai_api_key[-5:]}")
136
+
137
+ # Input data handling
138
+ logging.debug(f"OpenAI: Raw input data type: {type(input_data)}")
139
+ logging.debug(f"OpenAI: Raw input data (first 500 chars): {str(input_data)[:500]}...")
140
+
141
+ if isinstance(input_data, str):
142
+ if input_data.strip().startswith('{'):
143
+ # It's likely a JSON string
144
+ logging.debug("OpenAI: Parsing provided JSON string data for summarization")
145
+ try:
146
+ data = json.loads(input_data)
147
+ except json.JSONDecodeError as e:
148
+ logging.error(f"OpenAI: Error parsing JSON string: {str(e)}")
149
+ return f"OpenAI: Error parsing JSON input: {str(e)}"
150
+ elif os.path.isfile(input_data):
151
+ logging.debug("OpenAI: Loading JSON data from file for summarization")
152
+ with open(input_data, 'r') as file:
153
+ data = json.load(file)
154
+ else:
155
+ logging.debug("OpenAI: Using provided string data for summarization")
156
+ data = input_data
157
+ else:
158
+ data = input_data
159
+
160
+ logging.debug(f"OpenAI: Processed data type: {type(data)}")
161
+ logging.debug(f"OpenAI: Processed data (first 500 chars): {str(data)[:500]}...")
162
+
163
+ # Text extraction
164
+ if isinstance(data, dict):
165
+ if 'summary' in data:
166
+ logging.debug("OpenAI: Summary already exists in the loaded data")
167
+ return data['summary']
168
+ elif 'segments' in data:
169
+ text = extract_text_from_segments(data['segments'])
170
+ else:
171
+ text = json.dumps(data) # Convert dict to string if no specific format
172
+ elif isinstance(data, list):
173
+ text = extract_text_from_segments(data)
174
+ elif isinstance(data, str):
175
+ text = data
176
+ else:
177
+ raise ValueError(f"OpenAI: Invalid input data format: {type(data)}")
178
+
179
+ logging.debug(f"OpenAI: Extracted text (first 500 chars): {text[:500]}...")
180
+ logging.debug(f"OpenAI: Custom prompt: {custom_prompt_arg}")
181
+
182
+ openai_model = loaded_config_data['models']['openai'] or "gpt-4o"
183
+ logging.debug(f"OpenAI: Using model: {openai_model}")
184
+
185
+ headers = {
186
+ 'Authorization': f'Bearer {openai_api_key}',
187
+ 'Content-Type': 'application/json'
188
+ }
189
+
190
+ logging.debug(
191
+ f"OpenAI API Key: {openai_api_key[:5]}...{openai_api_key[-5:] if openai_api_key else None}")
192
+ logging.debug("openai: Preparing data + prompt for submittal")
193
+ openai_prompt = f"{text} \n\n\n\n{custom_prompt_arg}"
194
+ if temp is None:
195
+ temp = 0.7
196
+ if system_message is None:
197
+ system_message = "You are a helpful AI assistant who does whatever the user requests."
198
+ temp = float(temp)
199
+ data = {
200
+ "model": openai_model,
201
+ "messages": [
202
+ {"role": "system", "content": system_message},
203
+ {"role": "user", "content": openai_prompt}
204
+ ],
205
+ "max_tokens": 4096,
206
+ "temperature": temp
207
+ }
208
+
209
+ logging.debug("OpenAI: Posting request")
210
+ response = requests.post('https://api.openai.com/v1/chat/completions', headers=headers, json=data)
211
+ logging.debug(f"Full API response data: {response}")
212
+ if response.status_code == 200:
213
+ response_data = response.json()
214
+ logging.debug(response_data)
215
+ if 'choices' in response_data and len(response_data['choices']) > 0:
216
+ chat_response = response_data['choices'][0]['message']['content'].strip()
217
+ logging.debug("openai: Chat Sent successfully")
218
+ logging.debug(f"openai: Chat response: {chat_response}")
219
+ return chat_response
220
+ else:
221
+ logging.warning("openai: Chat response not found in the response data")
222
+ return "openai: Chat not available"
223
+ else:
224
+ logging.error(f"OpenAI: Chat request failed with status code {response.status_code}")
225
+ logging.error(f"OpenAI: Error response: {response.text}")
226
+ return f"OpenAI: Failed to process chat response. Status code: {response.status_code}"
227
+ except json.JSONDecodeError as e:
228
+ logging.error(f"OpenAI: Error decoding JSON: {str(e)}", exc_info=True)
229
+ return f"OpenAI: Error decoding JSON input: {str(e)}"
230
+ except requests.RequestException as e:
231
+ logging.error(f"OpenAI: Error making API request: {str(e)}", exc_info=True)
232
+ return f"OpenAI: Error making API request: {str(e)}"
233
+ except Exception as e:
234
+ logging.error(f"OpenAI: Unexpected error: {str(e)}", exc_info=True)
235
+ return f"OpenAI: Unexpected error occurred: {str(e)}"
236
+
237
+
238
+ def chat_with_anthropic(api_key, input_data, model, custom_prompt_arg, max_retries=3, retry_delay=5, system_prompt=None, temp=None):
239
+ try:
240
+ loaded_config_data = load_and_log_configs()
241
+
242
+ # Check if config was loaded successfully
243
+ if loaded_config_data is None:
244
+ logging.error("Anthropic: Failed to load configuration data.")
245
+ return "Anthropic: Failed to load configuration data."
246
+
247
+ # Initialize the API key
248
+ anthropic_api_key = api_key
249
+
250
+ # API key validation
251
+ if not api_key:
252
+ logging.info("Anthropic: API key not provided as parameter")
253
+ logging.info("Anthropic: Attempting to use API key from config file")
254
+ # Ensure 'api_keys' and 'anthropic' keys exist
255
+ try:
256
+ anthropic_api_key = loaded_config_data['api_keys']['anthropic']
257
+ logging.debug(f"Anthropic: Loaded API Key from config: {anthropic_api_key[:5]}...{anthropic_api_key[-5:]}")
258
+ except (KeyError, TypeError) as e:
259
+ logging.error(f"Anthropic: Error accessing API key from config: {str(e)}")
260
+ return "Anthropic: API Key Not Provided/Found in Config file or is empty"
261
+
262
+ if not anthropic_api_key or anthropic_api_key == "":
263
+ logging.error("Anthropic: API key not found or is empty")
264
+ return "Anthropic: API Key Not Provided/Found in Config file or is empty"
265
+
266
+ if anthropic_api_key:
267
+ logging.debug(f"Anthropic: Using API Key: {anthropic_api_key[:5]}...{anthropic_api_key[-5:]}")
268
+ else:
269
+ logging.debug(f"Anthropic: Using API Key: {api_key[:5]}...{api_key[-5:]}")
270
+
271
+ if system_prompt is not None:
272
+ logging.debug("Anthropic: Using provided system prompt")
273
+ pass
274
+ else:
275
+ system_prompt = "You are a helpful assistant"
276
+ logging.debug("Anthropic: Using default system prompt")
277
+
278
+ logging.debug(f"AnthropicAI: Loaded data: {input_data}")
279
+ logging.debug(f"AnthropicAI: Type of data: {type(input_data)}")
280
+
281
+ # Retrieve the model from config if not provided
282
+ if not model:
283
+ try:
284
+ anthropic_model = loaded_config_data['models']['anthropic']
285
+ logging.debug(f"Anthropic: Loaded model from config: {anthropic_model}")
286
+ except (KeyError, TypeError) as e:
287
+ logging.error(f"Anthropic: Error accessing model from config: {str(e)}")
288
+ return "Anthropic: Model configuration not found."
289
+ else:
290
+ anthropic_model = model
291
+ logging.debug(f"Anthropic: Using provided model: {anthropic_model}")
292
+
293
+ if temp is None:
294
+ temp = 1.0
295
+ logging.debug(f"Anthropic: Using default temperature: {temp}")
296
+
297
+ headers = {
298
+ 'x-api-key': anthropic_api_key,
299
+ 'anthropic-version': '2023-06-01',
300
+ 'Content-Type': 'application/json'
301
+ }
302
+
303
+ anthropic_user_prompt = custom_prompt_arg if custom_prompt_arg else ""
304
+ logging.debug(f"Anthropic: User Prompt is '{anthropic_user_prompt}'")
305
+ user_message = {
306
+ "role": "user",
307
+ "content": f"{input_data} \n\n\n\n{anthropic_user_prompt}"
308
+ }
309
+
310
+ data = {
311
+ "model": anthropic_model,
312
+ "max_tokens": 4096, # max possible tokens to return
313
+ "messages": [user_message],
314
+ "stop_sequences": ["\n\nHuman:"],
315
+ "temperature": temp,
316
+ "top_k": 0,
317
+ "top_p": 1.0,
318
+ "metadata": {
319
+ "user_id": "example_user_id",
320
+ },
321
+ "stream": False,
322
+ "system": system_prompt
323
+ }
324
+
325
+ for attempt in range(max_retries):
326
+ try:
327
+ logging.debug("Anthropic: Posting request to API")
328
+ response = requests.post('https://api.anthropic.com/v1/messages', headers=headers, json=data)
329
+ logging.debug(f"Anthropic: Full API response data: {response}")
330
+
331
+ # Check if the status code indicates success
332
+ if response.status_code == 200:
333
+ logging.debug("Anthropic: Post submittal successful")
334
+ response_data = response.json()
335
+
336
+ # Corrected path to access the assistant's reply
337
+ if 'content' in response_data and isinstance(response_data['content'], list) and len(response_data['content']) > 0:
338
+ chat_response = response_data['content'][0]['text'].strip()
339
+ logging.debug("Anthropic: Chat request successful")
340
+ print("Chat request processed successfully.")
341
+ return chat_response
342
+ else:
343
+ logging.error("Anthropic: Unexpected data structure in response.")
344
+ print("Unexpected response format from Anthropic API:", response.text)
345
+ return "Anthropic: Unexpected response format from API."
346
+ elif response.status_code == 500: # Handle internal server error specifically
347
+ logging.debug("Anthropic: Internal server error")
348
+ print("Internal server error from API. Retrying may be necessary.")
349
+ time.sleep(retry_delay)
350
+ else:
351
+ logging.debug(
352
+ f"Anthropic: Failed to process chat request, status code {response.status_code}: {response.text}")
353
+ print(f"Failed to process chat request, status code {response.status_code}: {response.text}")
354
+ return f"Anthropic: Failed to process chat request, status code {response.status_code}: {response.text}"
355
+
356
+ except requests.RequestException as e:
357
+ logging.error(f"Anthropic: Network error during attempt {attempt + 1}/{max_retries}: {str(e)}")
358
+ if attempt < max_retries - 1:
359
+ logging.debug(f"Anthropic: Retrying in {retry_delay} seconds...")
360
+ time.sleep(retry_delay)
361
+ else:
362
+ return f"Anthropic: Network error: {str(e)}"
363
+
364
+ except Exception as e:
365
+ logging.error(f"Anthropic: Error in processing: {str(e)}")
366
+ return f"Anthropic: Error occurred while processing summary with Anthropic: {str(e)}"
367
+
368
+
369
+ # Summarize with Cohere
370
+ def chat_with_cohere(api_key, input_data, model=None, custom_prompt_arg=None, system_prompt=None, temp=None):
371
+ loaded_config_data = load_and_log_configs()
372
+ cohere_api_key = None
373
+
374
+ try:
375
+ # API key validation
376
+ if api_key:
377
+ logging.info(f"Cohere Chat: API Key from parameter: {api_key[:3]}...{api_key[-3:]}")
378
+ cohere_api_key = api_key
379
+ else:
380
+ logging.info("Cohere Chat: API key not provided as parameter")
381
+ logging.info("Cohere Chat: Attempting to use API key from config file")
382
+ logging.debug(f"Cohere Chat: Cohere API Key from config: {loaded_config_data['api_keys']['cohere']}")
383
+ cohere_api_key = loaded_config_data['api_keys']['cohere']
384
+ if cohere_api_key:
385
+ logging.debug(f"Cohere Chat: Cohere API Key from config: {cohere_api_key[:3]}...{cohere_api_key[-3:]}")
386
+ else:
387
+ logging.error("Cohere Chat: API key not found or is empty")
388
+ return "Cohere Chat: API Key Not Provided/Found in Config file or is empty"
389
+
390
+ logging.debug(f"Cohere Chat: Loaded data: {input_data}")
391
+ logging.debug(f"Cohere Chat: Type of data: {type(input_data)}")
392
+
393
+ # Ensure model is set
394
+ if not model:
395
+ model = loaded_config_data['models']['cohere']
396
+ logging.debug(f"Cohere Chat: Using model: {model}")
397
+
398
+ if temp is None:
399
+ temp = 0.3
400
+ else:
401
+ try:
402
+ temp = float(temp)
403
+ except ValueError:
404
+ logging.warning(f"Cohere Chat: Invalid temperature value '{temp}', defaulting to 0.3")
405
+ temp = 0.3
406
+
407
+ headers = {
408
+ 'accept': 'application/json',
409
+ 'content-type': 'application/json',
410
+ 'Authorization': f'Bearer {cohere_api_key}'
411
+ }
412
+
413
+ # Ensure system_prompt is set
414
+ if not system_prompt:
415
+ system_prompt = "You are a helpful assistant"
416
+ logging.debug(f"Cohere Chat: System Prompt being sent is: '{system_prompt}'")
417
+
418
+ cohere_prompt = input_data
419
+ if custom_prompt_arg:
420
+ cohere_prompt += f"\n\n{custom_prompt_arg}"
421
+ logging.debug(f"Cohere Chat: User Prompt being sent is: '{cohere_prompt}'")
422
+
423
+ data = {
424
+ "model" : model,
425
+ "temperature": temp,
426
+ "messages": [
427
+ {
428
+ "role": "system",
429
+ "content": system_prompt
430
+ },
431
+ {
432
+ "role": "user",
433
+ "content": cohere_prompt,
434
+ }
435
+ ],
436
+ }
437
+ logging.debug(f"Cohere Chat: Request data: {json.dumps(data, indent=2)}")
438
+
439
+ logging.debug("cohere chat: Submitting request to API endpoint")
440
+ print("cohere chat: Submitting request to API endpoint")
441
+
442
+ try:
443
+ response = requests.post('https://api.cohere.ai/v2/chat', headers=headers, json=data)
444
+ logging.debug(f"Cohere Chat: Raw API response: {response.text}")
445
+ except requests.RequestException as e:
446
+ logging.error(f"Cohere Chat: Error making API request: {str(e)}")
447
+ return f"Cohere Chat: Error making API request: {str(e)}"
448
+
449
+ if response.status_code == 200:
450
+ try:
451
+ response_data = response.json()
452
+ except json.JSONDecodeError:
453
+ logging.error("Cohere Chat: Failed to decode JSON response")
454
+ return "Cohere Chat: Failed to decode JSON response"
455
+
456
+ if response_data is None:
457
+ logging.error("Cohere Chat: No response data received.")
458
+ return "Cohere Chat: No response data received."
459
+
460
+ logging.debug(f"cohere chat: Full API response data: {json.dumps(response_data, indent=2)}")
461
+
462
+ if 'message' in response_data and 'content' in response_data['message']:
463
+ content = response_data['message']['content']
464
+ if isinstance(content, list) and len(content) > 0:
465
+ # Extract text from the first content block
466
+ text = content[0].get('text', '').strip()
467
+ if text:
468
+ logging.debug("Cohere Chat: Chat request successful")
469
+ print("Cohere Chat request processed successfully.")
470
+ return text
471
+ else:
472
+ logging.error("Cohere Chat: 'text' field is empty in response content.")
473
+ return "Cohere Chat: 'text' field is empty in response content."
474
+ else:
475
+ logging.error("Cohere Chat: 'content' field is not a list or is empty.")
476
+ return "Cohere Chat: 'content' field is not a list or is empty."
477
+ else:
478
+ logging.error("Cohere Chat: 'message' or 'content' field not found in API response.")
479
+ return "Cohere Chat: 'message' or 'content' field not found in API response."
480
+
481
+ elif response.status_code == 401:
482
+ error_message = "Cohere Chat: Unauthorized - Invalid API key"
483
+ logging.warning(error_message)
484
+ print(error_message)
485
+ return error_message
486
+
487
+ else:
488
+ logging.error(f"Cohere Chat: API request failed with status code {response.status_code}: {response.text}")
489
+ print(f"Cohere Chat: Failed to process chat response, status code {response.status_code}: {response.text}")
490
+ return f"Cohere Chat: API request failed: {response.text}"
491
+
492
+ except Exception as e:
493
+ logging.error(f"Cohere Chat: Error in processing: {str(e)}", exc_info=True)
494
+ return f"Cohere Chat: Error occurred while processing chat request with Cohere: {str(e)}"
495
+
496
+
497
+ # https://console.groq.com/docs/quickstart
498
+ def chat_with_groq(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
499
+ logging.debug("Groq: Summarization process starting...")
500
+ try:
501
+ logging.debug("Groq: Loading and validating configurations")
502
+ loaded_config_data = load_and_log_configs()
503
+ if loaded_config_data is None:
504
+ logging.error("Failed to load configuration data")
505
+ groq_api_key = None
506
+ else:
507
+ # Prioritize the API key passed as a parameter
508
+ if api_key and api_key.strip():
509
+ groq_api_key = api_key
510
+ logging.info("Groq: Using API key provided as parameter")
511
+ else:
512
+ # If no parameter is provided, use the key from the config
513
+ groq_api_key = loaded_config_data['api_keys'].get('groq')
514
+ if groq_api_key:
515
+ logging.info("Groq: Using API key from config file")
516
+ else:
517
+ logging.warning("Groq: No API key found in config file")
518
+
519
+ # Final check to ensure we have a valid API key
520
+ if not groq_api_key or not groq_api_key.strip():
521
+ logging.error("Anthropic: No valid API key available")
522
+ # You might want to raise an exception here or handle this case as appropriate for your application
523
+ # For example: raise ValueError("No valid Anthropic API key available")
524
+
525
+ logging.debug(f"Groq: Using API Key: {groq_api_key[:5]}...{groq_api_key[-5:]}")
526
+
527
+ # Transcript data handling & Validation
528
+ if isinstance(input_data, str) and os.path.isfile(input_data):
529
+ logging.debug("Groq: Loading json data for summarization")
530
+ with open(input_data, 'r') as file:
531
+ data = json.load(file)
532
+ else:
533
+ logging.debug("Groq: Using provided string data for summarization")
534
+ data = input_data
535
+
536
+ # DEBUG - Debug logging to identify sent data
537
+ logging.debug(f"Groq: Loaded data: {data[:500]}...(snipped to first 500 chars)")
538
+ logging.debug(f"Groq: Type of data: {type(data)}")
539
+
540
+ if isinstance(data, dict) and 'summary' in data:
541
+ # If the loaded data is a dictionary and already contains a summary, return it
542
+ logging.debug("Groq: Summary already exists in the loaded data")
543
+ return data['summary']
544
+
545
+ # If the loaded data is a list of segment dictionaries or a string, proceed with summarization
546
+ if isinstance(data, list):
547
+ segments = data
548
+ text = extract_text_from_segments(segments)
549
+ elif isinstance(data, str):
550
+ text = data
551
+ else:
552
+ raise ValueError("Groq: Invalid input data format")
553
+
554
+ # Set the model to be used
555
+ groq_model = loaded_config_data['models']['groq']
556
+
557
+ if temp is None:
558
+ temp = 0.2
559
+ temp = float(temp)
560
+ if system_message is None:
561
+ system_message = "You are a helpful AI assistant who does whatever the user requests."
562
+
563
+ headers = {
564
+ 'Authorization': f'Bearer {groq_api_key}',
565
+ 'Content-Type': 'application/json'
566
+ }
567
+
568
+ groq_prompt = f"{text} \n\n\n\n{custom_prompt_arg}"
569
+ logging.debug("groq: Prompt being sent is {groq_prompt}")
570
+
571
+ data = {
572
+ "messages": [
573
+ {
574
+ "role": "system",
575
+ "content": system_message,
576
+ },
577
+ {
578
+ "role": "user",
579
+ "content": groq_prompt,
580
+ }
581
+ ],
582
+ "model": groq_model,
583
+ "temperature": temp
584
+ }
585
+
586
+ logging.debug("groq: Submitting request to API endpoint")
587
+ print("groq: Submitting request to API endpoint")
588
+ response = requests.post('https://api.groq.com/openai/v1/chat/completions', headers=headers, json=data)
589
+
590
+ response_data = response.json()
591
+ logging.debug(f"Full API response data: {response_data}")
592
+
593
+ if response.status_code == 200:
594
+ logging.debug(response_data)
595
+ if 'choices' in response_data and len(response_data['choices']) > 0:
596
+ summary = response_data['choices'][0]['message']['content'].strip()
597
+ logging.debug("groq: Chat request successful")
598
+ print("Groq: Chat request successful.")
599
+ return summary
600
+ else:
601
+ logging.error("Groq(chat): Expected data not found in API response.")
602
+ return "Groq(chat): Expected data not found in API response."
603
+ else:
604
+ logging.error(f"groq: API request failed with status code {response.status_code}: {response.text}")
605
+ return f"groq: API request failed: {response.text}"
606
+
607
+ except Exception as e:
608
+ logging.error("groq: Error in processing: %s", str(e))
609
+ return f"groq: Error occurred while processing summary with groq: {str(e)}"
610
+
611
+
612
+ def chat_with_openrouter(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
613
+ import requests
614
+ import json
615
+ global openrouter_model, openrouter_api_key
616
+ try:
617
+ logging.debug("OpenRouter: Loading and validating configurations")
618
+ loaded_config_data = load_and_log_configs()
619
+ if loaded_config_data is None:
620
+ logging.error("Failed to load configuration data")
621
+ openrouter_api_key = None
622
+ else:
623
+ # Prioritize the API key passed as a parameter
624
+ if api_key and api_key.strip():
625
+ openrouter_api_key = api_key
626
+ logging.info("OpenRouter: Using API key provided as parameter")
627
+ else:
628
+ # If no parameter is provided, use the key from the config
629
+ openrouter_api_key = loaded_config_data['api_keys'].get('openrouter')
630
+ if openrouter_api_key:
631
+ logging.info("OpenRouter: Using API key from config file")
632
+ else:
633
+ logging.warning("OpenRouter: No API key found in config file")
634
+
635
+ # Model Selection validation
636
+ logging.debug("OpenRouter: Validating model selection")
637
+ loaded_config_data = load_and_log_configs()
638
+ openrouter_model = loaded_config_data['models']['openrouter']
639
+ logging.debug(f"OpenRouter: Using model from config file: {openrouter_model}")
640
+
641
+ # Final check to ensure we have a valid API key
642
+ if not openrouter_api_key or not openrouter_api_key.strip():
643
+ logging.error("OpenRouter: No valid API key available")
644
+ raise ValueError("No valid Anthropic API key available")
645
+ except Exception as e:
646
+ logging.error("OpenRouter: Error in processing: %s", str(e))
647
+ return f"OpenRouter: Error occurred while processing config file with OpenRouter: {str(e)}"
648
+
649
+ logging.debug(f"OpenRouter: Using API Key: {openrouter_api_key[:5]}...{openrouter_api_key[-5:]}")
650
+
651
+ logging.debug(f"OpenRouter: Using Model: {openrouter_model}")
652
+
653
+ if isinstance(input_data, str) and os.path.isfile(input_data):
654
+ logging.debug("OpenRouter: Loading json data for summarization")
655
+ with open(input_data, 'r') as file:
656
+ data = json.load(file)
657
+ else:
658
+ logging.debug("OpenRouter: Using provided string data for summarization")
659
+ data = input_data
660
+
661
+ # DEBUG - Debug logging to identify sent data
662
+ logging.debug(f"OpenRouter: Loaded data: {data[:500]}...(snipped to first 500 chars)")
663
+ logging.debug(f"OpenRouter: Type of data: {type(data)}")
664
+
665
+ if isinstance(data, dict) and 'summary' in data:
666
+ # If the loaded data is a dictionary and already contains a summary, return it
667
+ logging.debug("OpenRouter: Summary already exists in the loaded data")
668
+ return data['summary']
669
+
670
+ # If the loaded data is a list of segment dictionaries or a string, proceed with summarization
671
+ if isinstance(data, list):
672
+ segments = data
673
+ text = extract_text_from_segments(segments)
674
+ elif isinstance(data, str):
675
+ text = data
676
+ else:
677
+ raise ValueError("OpenRouter: Invalid input data format")
678
+
679
+ openrouter_prompt = f"{input_data} \n\n\n\n{custom_prompt_arg}"
680
+ logging.debug(f"openrouter: User Prompt being sent is {openrouter_prompt}")
681
+
682
+ if temp is None:
683
+ temp = 0.1
684
+ temp = float(temp)
685
+ if system_message is None:
686
+ system_message = "You are a helpful AI assistant who does whatever the user requests."
687
+
688
+ try:
689
+ logging.debug("OpenRouter: Submitting request to API endpoint")
690
+ print("OpenRouter: Submitting request to API endpoint")
691
+ response = requests.post(
692
+ url="https://openrouter.ai/api/v1/chat/completions",
693
+ headers={
694
+ "Authorization": f"Bearer {openrouter_api_key}",
695
+ },
696
+ data=json.dumps({
697
+ "model": openrouter_model,
698
+ "messages": [
699
+ {"role": "system", "content": system_message},
700
+ {"role": "user", "content": openrouter_prompt}
701
+ ],
702
+ "temperature": temp
703
+ })
704
+ )
705
+
706
+ response_data = response.json()
707
+ logging.debug("Full API Response Data: %s", response_data)
708
+
709
+ if response.status_code == 200:
710
+ if 'choices' in response_data and len(response_data['choices']) > 0:
711
+ summary = response_data['choices'][0]['message']['content'].strip()
712
+ logging.debug("openrouter: Chat request successful")
713
+ print("openrouter: Chat request successful.")
714
+ return summary
715
+ else:
716
+ logging.error("openrouter: Expected data not found in API response.")
717
+ return "openrouter: Expected data not found in API response."
718
+ else:
719
+ logging.error(f"openrouter: API request failed with status code {response.status_code}: {response.text}")
720
+ return f"openrouter: API request failed: {response.text}"
721
+ except Exception as e:
722
+ logging.error("openrouter: Error in processing: %s", str(e))
723
+ return f"openrouter: Error occurred while processing chat request with openrouter: {str(e)}"
724
+
725
+
726
+ # FIXME: This function is not yet implemented properly
727
+ def chat_with_huggingface(api_key, input_data, custom_prompt_arg, system_prompt=None, temp=None):
728
+ loaded_config_data = load_and_log_configs()
729
+ logging.debug(f"huggingface Chat: Chat request process starting...")
730
+ try:
731
+ # API key validation
732
+ if not api_key or api_key.strip() == "":
733
+ logging.info("HuggingFace Chat: API key not provided as parameter")
734
+ logging.info("HuggingFace Chat: Attempting to use API key from config file")
735
+
736
+ huggingface_api_key = loaded_config_data['api_keys'].get('huggingface')
737
+ logging.debug(f"HuggingFace Chat: API key from config: {huggingface_api_key[:5]}...{huggingface_api_key[-5:]}")
738
+
739
+ if huggingface_api_key is None or huggingface_api_key.strip() == "":
740
+ logging.error("HuggingFace Chat: API key not found or is empty")
741
+ return "HuggingFace Chat: API Key Not Provided/Found in Config file or is empty"
742
+ if huggingface_api_key:
743
+ logging.info("HuggingFace Chat: Using API key from config file")
744
+ headers = {
745
+ "Authorization": f"Bearer {huggingface_api_key}"
746
+ }
747
+
748
+ # Setup model
749
+ huggingface_model = loaded_config_data['models']['huggingface']
750
+
751
+ API_URL = f"https://api-inference.huggingface.co/models/{huggingface_model}/v1/chat/completions"
752
+ if temp is None:
753
+ temp = 1.0
754
+ temp = float(temp)
755
+ huggingface_prompt = f"{custom_prompt_arg}\n\n\n{input_data}"
756
+ logging.debug(f"HuggingFace chat: Prompt being sent is {huggingface_prompt}")
757
+ data = {
758
+ "model": f"{huggingface_model}",
759
+ "messages": [{"role": "user", "content": f"{huggingface_prompt}"}],
760
+ "max_tokens": 4096,
761
+ "stream": False,
762
+ "temperature": temp
763
+ }
764
+
765
+ logging.debug("HuggingFace Chat: Submitting request...")
766
+ response = requests.post(API_URL, headers=headers, json=data)
767
+ logging.debug(f"Full API response data: {response.text}")
768
+
769
+ if response.status_code == 200:
770
+ response_json = response.json()
771
+ if "choices" in response_json and len(response_json["choices"]) > 0:
772
+ generated_text = response_json["choices"][0]["message"]["content"]
773
+ logging.debug("HuggingFace Chat: Chat request successful")
774
+ print("HuggingFace Chat: Chat request successful.")
775
+ return generated_text.strip()
776
+ else:
777
+ logging.error("HuggingFace Chat: No generated text in the response")
778
+ return "HuggingFace Chat: No generated text in the response"
779
+ else:
780
+ logging.error(
781
+ f"HuggingFace Chat: Chat request failed with status code {response.status_code}: {response.text}")
782
+ return f"HuggingFace Chat: Failed to process chat request, status code {response.status_code}: {response.text}"
783
+ except Exception as e:
784
+ logging.error(f"HuggingFace Chat: Error in processing: {str(e)}")
785
+ print(f"HuggingFace Chat: Error occurred while processing chat request with huggingface: {str(e)}")
786
+ return None
787
+
788
+
789
+ def chat_with_deepseek(api_key, input_data, custom_prompt_arg, temp=0.1, system_message="You are a helpful AI assistant who does whatever the user requests.", max_retries=3, retry_delay=5):
790
+ """
791
+ Interacts with the DeepSeek API to generate summaries based on input data.
792
+
793
+ Parameters:
794
+ api_key (str): DeepSeek API key. If not provided, the key from the config is used.
795
+ input_data (str or list): The data to summarize. Can be a string or a list of segments.
796
+ custom_prompt_arg (str): Custom prompt to append to the input data.
797
+ temp (float, optional): Temperature setting for the model. Defaults to 0.1.
798
+ system_message (str, optional): System prompt for the assistant. Defaults to a helpful assistant message.
799
+ max_retries (int, optional): Maximum number of retries for failed API calls. Defaults to 3.
800
+ retry_delay (int, optional): Delay between retries in seconds. Defaults to 5.
801
+
802
+ Returns:
803
+ str: The summary generated by DeepSeek or an error message.
804
+ """
805
+ logging.debug("DeepSeek: Summarization process starting...")
806
+ try:
807
+ logging.debug("DeepSeek: Loading and validating configurations")
808
+ loaded_config_data = load_and_log_configs()
809
+ if loaded_config_data is None:
810
+ logging.error("DeepSeek: Failed to load configuration data")
811
+ return "DeepSeek: Failed to load configuration data."
812
+
813
+ # Prioritize the API key passed as a parameter
814
+ if api_key and api_key.strip():
815
+ deepseek_api_key = api_key.strip()
816
+ logging.info("DeepSeek: Using API key provided as parameter")
817
+ else:
818
+ # If no parameter is provided, use the key from the config
819
+ deepseek_api_key = loaded_config_data['api_keys'].get('deepseek')
820
+ if deepseek_api_key and deepseek_api_key.strip():
821
+ deepseek_api_key = deepseek_api_key.strip()
822
+ logging.info("DeepSeek: Using API key from config file")
823
+ else:
824
+ logging.error("DeepSeek: No valid API key available")
825
+ return "DeepSeek: API Key Not Provided/Found in Config file or is empty"
826
+
827
+ logging.debug("DeepSeek: Using API Key")
828
+
829
+ # Input data handling
830
+ if isinstance(input_data, str) and os.path.isfile(input_data):
831
+ logging.debug("DeepSeek: Loading JSON data for summarization")
832
+ with open(input_data, 'r', encoding='utf-8') as file:
833
+ try:
834
+ data = json.load(file)
835
+ except json.JSONDecodeError as e:
836
+ logging.error(f"DeepSeek: JSON decoding failed: {str(e)}")
837
+ return f"DeepSeek: Invalid JSON file. Error: {str(e)}"
838
+ else:
839
+ logging.debug("DeepSeek: Using provided string data for summarization")
840
+ data = input_data
841
+
842
+ # DEBUG - Debug logging to identify sent data
843
+ if isinstance(data, str):
844
+ snipped_data = data[:500] + "..." if len(data) > 500 else data
845
+ logging.debug(f"DeepSeek: Loaded data (snipped to first 500 chars): {snipped_data}")
846
+ elif isinstance(data, list):
847
+ snipped_data = json.dumps(data[:2], indent=2) + "..." if len(data) > 2 else json.dumps(data, indent=2)
848
+ logging.debug(f"DeepSeek: Loaded data (snipped to first 2 segments): {snipped_data}")
849
+ else:
850
+ logging.debug(f"DeepSeek: Loaded data: {data}")
851
+
852
+ logging.debug(f"DeepSeek: Type of data: {type(data)}")
853
+
854
+ if isinstance(data, dict) and 'summary' in data:
855
+ # If the loaded data is a dictionary and already contains a summary, return it
856
+ logging.debug("DeepSeek: Summary already exists in the loaded data")
857
+ return data['summary']
858
+
859
+ # Text extraction
860
+ if isinstance(data, list):
861
+ segments = data
862
+ try:
863
+ text = extract_text_from_segments(segments)
864
+ logging.debug("DeepSeek: Extracted text from segments")
865
+ except Exception as e:
866
+ logging.error(f"DeepSeek: Error extracting text from segments: {str(e)}")
867
+ return f"DeepSeek: Error extracting text from segments: {str(e)}"
868
+ elif isinstance(data, str):
869
+ text = data
870
+ logging.debug("DeepSeek: Using string data directly")
871
+ else:
872
+ raise ValueError("DeepSeek: Invalid input data format")
873
+
874
+ # Retrieve the model from config if not provided
875
+ deepseek_model = loaded_config_data['models'].get('deepseek', "deepseek-chat")
876
+ logging.debug(f"DeepSeek: Using model: {deepseek_model}")
877
+
878
+ # Ensure temperature is a float within acceptable range
879
+ try:
880
+ temp = float(temp)
881
+ if not (0.0 <= temp <= 1.0):
882
+ logging.warning("DeepSeek: Temperature out of bounds (0.0 - 1.0). Setting to default 0.1")
883
+ temp = 0.1
884
+ except (ValueError, TypeError):
885
+ logging.warning("DeepSeek: Invalid temperature value. Setting to default 0.1")
886
+ temp = 0.1
887
+
888
+ # Set default system prompt if not provided
889
+ if system_message is not None:
890
+ logging.debug("DeepSeek: Using provided system prompt")
891
+ else:
892
+ system_message = "You are a helpful AI assistant who does whatever the user requests."
893
+ logging.debug("DeepSeek: Using default system prompt")
894
+
895
+ headers = {
896
+ 'Authorization': f'Bearer {deepseek_api_key}',
897
+ 'Content-Type': 'application/json'
898
+ }
899
+
900
+ logging.debug("DeepSeek: Preparing data and prompt for submittal")
901
+ deepseek_prompt = f"{text}\n\n\n\n{custom_prompt_arg}"
902
+ payload = {
903
+ "model": deepseek_model,
904
+ "messages": [
905
+ {"role": "system", "content": system_message},
906
+ {"role": "user", "content": deepseek_prompt}
907
+ ],
908
+ "stream": False,
909
+ "temperature": temp
910
+ }
911
+
912
+ logging.debug("DeepSeek: Posting request to API")
913
+ for attempt in range(1, max_retries + 1):
914
+ try:
915
+ response = requests.post('https://api.deepseek.com/chat/completions', headers=headers, json=payload, timeout=30)
916
+ logging.debug(f"DeepSeek: Full API response: {response.status_code} - {response.text}")
917
+
918
+ if response.status_code == 200:
919
+ response_data = response.json()
920
+ logging.debug(f"DeepSeek: Response JSON: {json.dumps(response_data, indent=2)}")
921
+
922
+ # Adjust parsing based on actual API response structure
923
+ if 'choices' in response_data:
924
+ if len(response_data['choices']) > 0:
925
+ summary = response_data['choices'][0]['message']['content'].strip()
926
+ logging.debug("DeepSeek: Chat request successful")
927
+ return summary
928
+ else:
929
+ logging.error("DeepSeek: 'choices' key is empty in response")
930
+ else:
931
+ logging.error("DeepSeek: 'choices' key missing in response")
932
+ return "DeepSeek: Unexpected response format from API."
933
+ elif 500 <= response.status_code < 600:
934
+ logging.error(f"DeepSeek: Server error (status code {response.status_code}). Attempt {attempt} of {max_retries}. Retrying in {retry_delay} seconds...")
935
+ else:
936
+ logging.error(f"DeepSeek: Request failed with status code {response.status_code}. Response: {response.text}")
937
+ return f"DeepSeek: Failed to process chat request. Status code: {response.status_code}"
938
+
939
+ except requests.Timeout:
940
+ logging.error(f"DeepSeek: Request timed out. Attempt {attempt} of {max_retries}. Retrying in {retry_delay} seconds...")
941
+ except requests.RequestException as e:
942
+ logging.error(f"DeepSeek: Request exception occurred: {str(e)}. Attempt {attempt} of {max_retries}. Retrying in {retry_delay} seconds...")
943
+
944
+ if attempt < max_retries:
945
+ time.sleep(retry_delay)
946
+ else:
947
+ logging.error("DeepSeek: Max retries reached. Failed to get a successful response.")
948
+ return "DeepSeek: Failed to get a successful response from API after multiple attempts."
949
+
950
+ except Exception as e:
951
+ logging.error(f"DeepSeek: Unexpected error in processing: {str(e)}", exc_info=True)
952
+ return f"DeepSeek: Error occurred while processing chat request: {str(e)}"
953
+
954
+
955
+
956
+
957
+ def chat_with_mistral(api_key, input_data, custom_prompt_arg, temp=None, system_message=None):
958
+ logging.debug("Mistral: Chat request made")
959
+ try:
960
+ logging.debug("Mistral: Loading and validating configurations")
961
+ loaded_config_data = load_and_log_configs()
962
+ if loaded_config_data is None:
963
+ logging.error("Failed to load configuration data")
964
+ mistral_api_key = None
965
+ else:
966
+ # Prioritize the API key passed as a parameter
967
+ if api_key and api_key.strip():
968
+ mistral_api_key = api_key
969
+ logging.info("Mistral: Using API key provided as parameter")
970
+ else:
971
+ # If no parameter is provided, use the key from the config
972
+ mistral_api_key = loaded_config_data['api_keys'].get('mistral')
973
+ if mistral_api_key:
974
+ logging.info("Mistral: Using API key from config file")
975
+ else:
976
+ logging.warning("Mistral: No API key found in config file")
977
+
978
+ # Final check to ensure we have a valid API key
979
+ if not mistral_api_key or not mistral_api_key.strip():
980
+ logging.error("Mistral: No valid API key available")
981
+ return "Mistral: No valid API key available"
982
+
983
+ logging.debug(f"Mistral: Using API Key: {mistral_api_key[:5]}...{mistral_api_key[-5:]}")
984
+
985
+ logging.debug("Mistral: Using provided string data")
986
+ data = input_data
987
+
988
+ # Text extraction
989
+ if isinstance(input_data, list):
990
+ text = extract_text_from_segments(input_data)
991
+ elif isinstance(input_data, str):
992
+ text = input_data
993
+ else:
994
+ raise ValueError("Mistral: Invalid input data format")
995
+
996
+ mistral_model = loaded_config_data['models'].get('mistral', "mistral-large-latest")
997
+
998
+ temp = float(temp) if temp is not None else 0.2
999
+ if system_message is None:
1000
+ system_message = "You are a helpful AI assistant who does whatever the user requests."
1001
+
1002
+ headers = {
1003
+ 'Authorization': f'Bearer {mistral_api_key}',
1004
+ 'Content-Type': 'application/json'
1005
+ }
1006
+
1007
+ logging.debug(
1008
+ f"Deepseek API Key: {mistral_api_key[:5]}...{mistral_api_key[-5:] if mistral_api_key else None}")
1009
+ logging.debug("Mistral: Preparing data + prompt for submittal")
1010
+ mistral_prompt = f"{custom_prompt_arg}\n\n\n\n{text} "
1011
+ data = {
1012
+ "model": mistral_model,
1013
+ "messages": [
1014
+ {"role": "system",
1015
+ "content": system_message},
1016
+ {"role": "user",
1017
+ "content": mistral_prompt}
1018
+ ],
1019
+ "temperature": temp,
1020
+ "top_p": 1,
1021
+ "max_tokens": 4096,
1022
+ "stream": False,
1023
+ "safe_prompt": False
1024
+ }
1025
+
1026
+ logging.debug("Mistral: Posting request")
1027
+ response = requests.post('https://api.mistral.ai/v1/chat/completions', headers=headers, json=data)
1028
+ logging.debug(f"Full API response data: {response}")
1029
+ if response.status_code == 200:
1030
+ response_data = response.json()
1031
+ logging.debug(response_data)
1032
+ if 'choices' in response_data and len(response_data['choices']) > 0:
1033
+ summary = response_data['choices'][0]['message']['content'].strip()
1034
+ logging.debug("Mistral: request successful")
1035
+ return summary
1036
+ else:
1037
+ logging.warning("Mistral: Chat response not found in the response data")
1038
+ return "Mistral: Chat response not available"
1039
+ else:
1040
+ logging.error(f"Mistral: Chat request failed with status code {response.status_code}")
1041
+ logging.error(f"Mistral: Error response: {response.text}")
1042
+ return f"Mistral: Failed to process summary. Status code: {response.status_code}. Error: {response.text}"
1043
+ except Exception as e:
1044
+ logging.error(f"Mistral: Error in processing: {str(e)}", exc_info=True)
1045
+ return f"Mistral: Error occurred while processing Chat: {str(e)}"
1046
+
1047
+
1048
+
1049
+ # Stashed in here since OpenAI usage.... #FIXME
1050
+ # FIXME - https://docs.vllm.ai/en/latest/getting_started/quickstart.html .... Great docs.
1051
+ # def chat_with_vllm(input_data, custom_prompt_input, api_key=None, vllm_api_url="http://127.0.0.1:8000/v1/chat/completions", system_prompt=None):
1052
+ # loaded_config_data = load_and_log_configs()
1053
+ # llm_model = loaded_config_data['models']['vllm']
1054
+ # # API key validation
1055
+ # if api_key is None:
1056
+ # logging.info("vLLM: API key not provided as parameter")
1057
+ # logging.info("vLLM: Attempting to use API key from config file")
1058
+ # api_key = loaded_config_data['api_keys']['llama']
1059
+ #
1060
+ # if api_key is None or api_key.strip() == "":
1061
+ # logging.info("vLLM: API key not found or is empty")
1062
+ # vllm_client = OpenAI(
1063
+ # base_url=vllm_api_url,
1064
+ # api_key=custom_prompt_input
1065
+ # )
1066
+ #
1067
+ # if isinstance(input_data, str) and os.path.isfile(input_data):
1068
+ # logging.debug("vLLM: Loading json data for summarization")
1069
+ # with open(input_data, 'r') as file:
1070
+ # data = json.load(file)
1071
+ # else:
1072
+ # logging.debug("vLLM: Using provided string data for summarization")
1073
+ # data = input_data
1074
+ #
1075
+ # logging.debug(f"vLLM: Loaded data: {data}")
1076
+ # logging.debug(f"vLLM: Type of data: {type(data)}")
1077
+ #
1078
+ # if isinstance(data, dict) and 'summary' in data:
1079
+ # # If the loaded data is a dictionary and already contains a summary, return it
1080
+ # logging.debug("vLLM: Summary already exists in the loaded data")
1081
+ # return data['summary']
1082
+ #
1083
+ # # If the loaded data is a list of segment dictionaries or a string, proceed with summarization
1084
+ # if isinstance(data, list):
1085
+ # segments = data
1086
+ # text = extract_text_from_segments(segments)
1087
+ # elif isinstance(data, str):
1088
+ # text = data
1089
+ # else:
1090
+ # raise ValueError("Invalid input data format")
1091
+ #
1092
+ #
1093
+ # custom_prompt = custom_prompt_input
1094
+ #
1095
+ # completion = client.chat.completions.create(
1096
+ # model=llm_model,
1097
+ # messages=[
1098
+ # {"role": "system", "content": f"{system_prompt}"},
1099
+ # {"role": "user", "content": f"{text} \n\n\n\n{custom_prompt}"}
1100
+ # ]
1101
+ # )
1102
+ # vllm_summary = completion.choices[0].message.content
1103
+ # return vllm_summary
1104
+
1105
+
1106
+
1107
+ #
1108
+ #
1109
  #######################################################################################################################
App_Function_Libraries/LLM_API_Calls_Local.py CHANGED
@@ -4,6 +4,7 @@
4
  # This library is used to perform summarization with a 'local' inference engine.
5
  #
6
  ####
 
7
  from typing import Union
8
 
9
  ####################
@@ -99,7 +100,8 @@ def chat_with_local_llm(input_data, custom_prompt_arg, temp, system_message=None
99
  print("Error occurred while processing Chat request with Local LLM:", str(e))
100
  return "Local LLM: Error occurred while processing Chat response"
101
 
102
- def chat_with_llama(input_data, custom_prompt, api_url="http://127.0.0.1:8080/completion", api_key=None, system_prompt=None):
 
103
  loaded_config_data = load_and_log_configs()
104
  try:
105
  # API key validation
@@ -113,6 +115,15 @@ def chat_with_llama(input_data, custom_prompt, api_url="http://127.0.0.1:8080/co
113
 
114
  logging.debug(f"llama.cpp: Using API Key: {api_key[:5]}...{api_key[-5:]}")
115
 
 
 
 
 
 
 
 
 
 
116
  headers = {
117
  'accept': 'application/json',
118
  'content-type': 'application/json',
@@ -132,7 +143,29 @@ def chat_with_llama(input_data, custom_prompt, api_url="http://127.0.0.1:8080/co
132
 
133
  data = {
134
  "prompt": f"{llama_prompt}",
135
- "system_prompt": f"{system_prompt}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  }
137
 
138
  logging.debug("llama: Submitting request to API endpoint")
@@ -400,10 +433,19 @@ def chat_with_aphrodite(input_data, custom_prompt_input, api_key=None, api_IP="h
400
  return "Error summarizing with Aphrodite."
401
 
402
 
403
- # FIXME
404
- def chat_with_ollama(input_data, custom_prompt, api_url="http://127.0.0.1:11434/api/generate", api_key=None, temp=None, system_message=None, model=None):
 
 
 
 
 
 
 
 
 
405
  try:
406
- logging.debug("ollama: Loading and validating configurations")
407
  loaded_config_data = load_and_log_configs()
408
  if loaded_config_data is None:
409
  logging.error("Failed to load configuration data")
@@ -421,7 +463,19 @@ def chat_with_ollama(input_data, custom_prompt, api_url="http://127.0.0.1:11434/
421
  else:
422
  logging.warning("Ollama: No API key found in config file")
423
 
424
- model = loaded_config_data['models']['ollama']
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
  # Load transcript
427
  logging.debug("Ollama: Loading JSON data")
@@ -454,48 +508,88 @@ def chat_with_ollama(input_data, custom_prompt, api_url="http://127.0.0.1:11434/
454
  'accept': 'application/json',
455
  'content-type': 'application/json',
456
  }
457
- if len(ollama_api_key) > 5:
458
  headers['Authorization'] = f'Bearer {ollama_api_key}'
459
 
460
- ollama_prompt = f"{custom_prompt} \n\n\n\n{text}"
461
- if system_message is None:
462
- system_message = "You are a helpful AI assistant."
463
- logging.debug(f"llama: Prompt being sent is {ollama_prompt}")
464
  if system_message is None:
465
  system_message = "You are a helpful AI assistant."
 
466
 
467
- data = {
468
  "model": model,
469
  "messages": [
470
- {"role": "system",
471
- "content": system_message
472
- },
473
- {"role": "user",
474
- "content": ollama_prompt
475
- }
 
 
476
  ],
477
  }
478
 
479
- logging.debug("Ollama: Submitting request to API endpoint")
480
- print("Ollama: Submitting request to API endpoint")
481
- response = requests.post(api_url, headers=headers, json=data)
482
- response_data = response.json()
483
- logging.debug("API Response Data: %s", response_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
 
485
- if response.status_code == 200:
486
- # if 'X' in response_data:
487
- logging.debug(response_data)
488
- summary = response_data['content'].strip()
489
- logging.debug("Ollama: Chat request successful")
490
- print("\n\nChat request successful.")
491
- return summary
492
- else:
493
- logging.error(f"\n\nOllama: API request failed with status code {response.status_code}: {response.text}")
494
- return f"Ollama: API request failed: {response.text}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
 
496
  except Exception as e:
497
  logging.error("\n\nOllama: Error in processing: %s", str(e))
498
- return f"Ollama: Error occurred while processing summary with ollama: {str(e)}"
 
499
 
500
  def chat_with_vllm(
501
  input_data: Union[str, dict, list],
 
4
  # This library is used to perform summarization with a 'local' inference engine.
5
  #
6
  ####
7
+ import logging
8
  from typing import Union
9
 
10
  ####################
 
100
  print("Error occurred while processing Chat request with Local LLM:", str(e))
101
  return "Local LLM: Error occurred while processing Chat response"
102
 
103
+ # FIXME
104
+ def chat_with_llama(input_data, custom_prompt, temp, api_url="http://127.0.0.1:8080/completion", api_key=None, system_prompt=None):
105
  loaded_config_data = load_and_log_configs()
106
  try:
107
  # API key validation
 
115
 
116
  logging.debug(f"llama.cpp: Using API Key: {api_key[:5]}...{api_key[-5:]}")
117
 
118
+ if api_url is None:
119
+ logging.info("llama.cpp: API URL not provided as parameter")
120
+ logging.info("llama.cpp: Attempting to use API URL from config file")
121
+ api_url = loaded_config_data['local_api_ip']['llama']
122
+
123
+ if api_url is None or api_url.strip() == "":
124
+ logging.info("llama.cpp: API URL not found or is empty")
125
+ return "llama.cpp: API URL not found or is empty"
126
+
127
  headers = {
128
  'accept': 'application/json',
129
  'content-type': 'application/json',
 
143
 
144
  data = {
145
  "prompt": f"{llama_prompt}",
146
+ "system_prompt": f"{system_prompt}",
147
+ 'temperature': temp,
148
+ #'top_k': '40',
149
+ #'top_p': '0.95',
150
+ #'min_p': '0.05',
151
+ #'n_predict': '-1',
152
+ #'n_keep': '0',
153
+ 'stream': 'True',
154
+ #'stop': '["\n"]',
155
+ #'tfs_z': '1.0',
156
+ #'repeat_penalty': '1.1',
157
+ #'repeat_last_n': '64',
158
+ #'presence_penalty': '0.0',
159
+ #'frequency_penalty': '0.0',
160
+ #'mirostat': '0',
161
+ #'grammar': '0',
162
+ #'json_schema': '0',
163
+ #'ignore_eos': 'false',
164
+ #'logit_bias': [],
165
+ #'n_probs': '0',
166
+ #'min_keep': '0',
167
+ #'samplers': '["top_k", "tfs_z", "typical_p", "top_p", "min_p", "temperature"]',
168
+
169
  }
170
 
171
  logging.debug("llama: Submitting request to API endpoint")
 
433
  return "Error summarizing with Aphrodite."
434
 
435
 
436
+ def chat_with_ollama(
437
+ input_data,
438
+ custom_prompt,
439
+ api_url="http://127.0.0.1:11434/v1/chat/completions",
440
+ api_key=None,
441
+ temp=None,
442
+ system_message=None,
443
+ model=None,
444
+ max_retries=5,
445
+ retry_delay=20
446
+ ):
447
  try:
448
+ logging.debug("Ollama: Loading and validating configurations")
449
  loaded_config_data = load_and_log_configs()
450
  if loaded_config_data is None:
451
  logging.error("Failed to load configuration data")
 
463
  else:
464
  logging.warning("Ollama: No API key found in config file")
465
 
466
+ # Set model from parameter or config
467
+ if model is None:
468
+ model = loaded_config_data['models'].get('ollama')
469
+ if model is None:
470
+ logging.error("Ollama: Model not found in config file")
471
+ return "Ollama: Model not found in config file"
472
+
473
+ # Set api_url from parameter or config
474
+ if api_url is None:
475
+ api_url = loaded_config_data['local_api_ip'].get('ollama')
476
+ if api_url is None:
477
+ logging.error("Ollama: API URL not found in config file")
478
+ return "Ollama: API URL not found in config file"
479
 
480
  # Load transcript
481
  logging.debug("Ollama: Loading JSON data")
 
508
  'accept': 'application/json',
509
  'content-type': 'application/json',
510
  }
511
+ if ollama_api_key and len(ollama_api_key) > 5:
512
  headers['Authorization'] = f'Bearer {ollama_api_key}'
513
 
514
+ ollama_prompt = f"{custom_prompt}\n\n{text}"
 
 
 
515
  if system_message is None:
516
  system_message = "You are a helpful AI assistant."
517
+ logging.debug(f"Ollama: Prompt being sent is: {ollama_prompt}")
518
 
519
+ data_payload = {
520
  "model": model,
521
  "messages": [
522
+ {
523
+ "role": "system",
524
+ "content": system_message
525
+ },
526
+ {
527
+ "role": "user",
528
+ "content": ollama_prompt
529
+ }
530
  ],
531
  }
532
 
533
+ for attempt in range(1, max_retries + 1):
534
+ logging.debug("Ollama: Submitting request to API endpoint")
535
+ print("Ollama: Submitting request to API endpoint")
536
+ try:
537
+ response = requests.post(api_url, headers=headers, json=data_payload, timeout=30)
538
+ response.raise_for_status() # Raises HTTPError for bad responses
539
+ response_data = response.json()
540
+ except requests.exceptions.Timeout:
541
+ logging.error("Ollama: Request timed out.")
542
+ return "Ollama: Request timed out."
543
+ except requests.exceptions.HTTPError as http_err:
544
+ logging.error(f"Ollama: HTTP error occurred: {http_err}")
545
+ return f"Ollama: HTTP error occurred: {http_err}"
546
+ except requests.exceptions.RequestException as req_err:
547
+ logging.error(f"Ollama: Request exception: {req_err}")
548
+ return f"Ollama: Request exception: {req_err}"
549
+ except json.JSONDecodeError:
550
+ logging.error("Ollama: Failed to decode JSON response")
551
+ return "Ollama: Failed to decode JSON response."
552
+ except Exception as e:
553
+ logging.error(f"Ollama: An unexpected error occurred: {str(e)}")
554
+ return f"Ollama: An unexpected error occurred: {str(e)}"
555
+
556
+ logging.debug(f"API Response Data: {response_data}")
557
 
558
+ if response.status_code == 200:
559
+ # Inspect available keys
560
+ available_keys = list(response_data.keys())
561
+ logging.debug(f"Ollama: Available keys in response: {available_keys}")
562
+
563
+ # Attempt to retrieve 'response'
564
+ summary = None
565
+ if 'response' in response_data and response_data['response']:
566
+ summary = response_data['response'].strip()
567
+ elif 'choices' in response_data and len(response_data['choices']) > 0:
568
+ choice = response_data['choices'][0]
569
+ if 'message' in choice and 'content' in choice['message']:
570
+ summary = choice['message']['content'].strip()
571
+
572
+ if summary:
573
+ logging.debug("Ollama: Chat request successful")
574
+ print("\n\nChat request successful.")
575
+ return summary
576
+ elif response_data.get('done_reason') == 'load':
577
+ logging.warning(f"Ollama: Model is loading. Attempt {attempt} of {max_retries}. Retrying in {retry_delay} seconds...")
578
+ time.sleep(retry_delay)
579
+ else:
580
+ logging.error("Ollama: API response does not contain 'response' or 'choices'.")
581
+ return "Ollama: API response does not contain 'response' or 'choices'."
582
+ else:
583
+ logging.error(f"Ollama: API request failed with status code {response.status_code}: {response.text}")
584
+ return f"Ollama: API request failed: {response.text}"
585
+
586
+ logging.error("Ollama: Maximum retry attempts reached. Model is still loading.")
587
+ return "Ollama: Maximum retry attempts reached. Model is still loading."
588
 
589
  except Exception as e:
590
  logging.error("\n\nOllama: Error in processing: %s", str(e))
591
+ return f"Ollama: Error occurred while processing summary with Ollama: {str(e)}"
592
+
593
 
594
  def chat_with_vllm(
595
  input_data: Union[str, dict, list],
App_Function_Libraries/Prompt_Handling.py CHANGED
@@ -5,6 +5,8 @@ import tempfile
5
  import zipfile
6
  import re
7
 
 
 
8
 
9
  def import_prompt_from_file(file):
10
  if file is None:
@@ -78,7 +80,7 @@ def import_prompt_data(name, details, system, user):
78
  return "Name and System fields are required."
79
 
80
  try:
81
- conn = sqlite3.connect('prompts.db')
82
  cursor = conn.cursor()
83
  cursor.execute('''
84
  INSERT INTO Prompts (name, details, system, user)
 
5
  import zipfile
6
  import re
7
 
8
+ from App_Function_Libraries.Utils.Utils import get_database_path
9
+
10
 
11
  def import_prompt_from_file(file):
12
  if file is None:
 
80
  return "Name and System fields are required."
81
 
82
  try:
83
+ conn = sqlite3.connect(get_database_path('prompts.db'))
84
  cursor = conn.cursor()
85
  cursor.execute('''
86
  INSERT INTO Prompts (name, details, system, user)