Spaces:
Runtime error
Runtime error
import groq | |
import time | |
import os | |
import json | |
import logging | |
logging.basicConfig(level=logging.DEBUG) | |
client = groq.Groq() | |
def parse_json_response(raw_response): | |
""" | |
Разбирает строку JSON и проверяет наличие обязательных ключей: title, content, next_action. | |
""" | |
logging.debug(f"RAW JSON RESPONSE: {raw_response}") | |
try: | |
parsed_response = json.loads(raw_response) | |
except json.JSONDecodeError as e: | |
raise ValueError(f"Invalid JSON: {e}, content: {raw_response}") | |
required_keys = {"title", "content", "next_action"} | |
if required_keys.issubset(parsed_response): | |
return parsed_response | |
else: | |
raise ValueError("JSON missing required keys") | |
def make_api_call(messages, max_tokens, is_final_answer=False, custom_client=None): | |
""" | |
Отправляет API-запрос с заданными сообщениями и возвращает результат. | |
При is_final_answer=True возвращает текст финального ответа. | |
В противном случае ожидается, что ответ будет в формате JSON с ключами "title", "content", "next_action". | |
""" | |
current_client = custom_client if custom_client is not None else client | |
for attempt in range(3): | |
try: | |
if is_final_answer: | |
response = current_client.chat.completions.create( | |
model="llama3-8b-8192", | |
messages=messages, | |
max_tokens=max_tokens, | |
temperature=0.2 | |
) | |
# Возвращаем строку, как и ожидается для финального ответа | |
return response.choices[0].message.content | |
else: | |
response = current_client.chat.completions.create( | |
model="llama3-8b-8192", | |
messages=messages, | |
max_tokens=max_tokens, | |
temperature=0.2, | |
response_format={"type": "json_object"} | |
) | |
raw_response = response.choices[0].message.content | |
return parse_json_response(raw_response) | |
except Exception as e: | |
logging.exception("Attempt %d failed", attempt + 1) | |
if attempt == 2: | |
error_message = f"Failed to generate {'final answer' if is_final_answer else 'step'} after 3 attempts. Error: {str(e)}" | |
if is_final_answer: | |
return f"Error: {error_message}" | |
else: | |
return { | |
"title": "Error", | |
"content": error_message, | |
"next_action": "final_answer" | |
} | |
time.sleep(1) | |
def generate_response(prompt, custom_client=None): | |
""" | |
Генерирует цепочку рассуждений и финальный ответ по заданному запросу. | |
Аргументы: | |
prompt (str): Запрос, на который необходимо сгенерировать ответ. | |
custom_client (object, optional): Альтернативный клиент для API-вызовов. | |
Возвращает: | |
Генератор, yield'ящий кортеж (steps, total_thinking_time), где: | |
- steps: список кортежей (название шага, содержание, время обработки) | |
- total_thinking_time: общее время обработки (либо None до финального шага) | |
""" | |
messages = [ | |
{"role": "system", "content": ( | |
"Вы – интеллектуальный помощник, который анализирует и объясняет свои рассуждения на русском языке шаг за шагом.\n" | |
"### 🔹 Формат ответа\n" | |
"Ваш ответ должен быть строго в JSON-формате без дополнительного текста или форматирования (например, без ```json```).\n" | |
"Обязательные ключи:\n" | |
'- "title" – краткое название шага.\n' | |
'- "content" – описание действий.\n' | |
'- "next_action" – "continue" или "final_answer".\n' | |
"Пример:\n" | |
'{"title": "Анализ задачи", "content": "Выделение ключевых элементов...", "next_action": "continue"}\n' | |
"🔹 Дополнительные требования:\n" | |
"- Используйте русский язык.\n" | |
'- Избегайте Unicode-кодировок (например, писать "Привет", а не "\\u041f\\u0440\\u0438...").' | |
)}, | |
{"role": "user", "content": prompt}, | |
{"role": "assistant", "content": "Спасибо! Начинаю анализ..."} | |
] | |
steps = [] | |
step_count = 1 | |
total_thinking_time = 0 | |
while True: | |
start_time = time.time() | |
step_data = make_api_call(messages, max_tokens=500, custom_client=custom_client) | |
end_time = time.time() | |
thinking_time = end_time - start_time | |
total_thinking_time += thinking_time | |
steps.append((f"Step {step_count}: {step_data.get('title', 'Без названия')}", step_data.get('content', ''), thinking_time)) | |
messages.append({"role": "assistant", "content": json.dumps(step_data, ensure_ascii=False)}) | |
if step_data.get('next_action') == 'final_answer' or step_count >= 25: | |
break | |
step_count += 1 | |
yield steps, None # Возвращаем промежуточные шаги без финального времени | |
messages.append({ | |
"role": "user", | |
"content": "Предоставьте окончательный ответ без формата JSON. Сохранить исходное форматирование из подсказки." | |
}) | |
start_time = time.time() | |
final_data = make_api_call(messages, max_tokens=1200, is_final_answer=True, custom_client=custom_client) | |
end_time = time.time() | |
thinking_time = end_time - start_time | |
total_thinking_time += thinking_time | |
steps.append(("Final Answer", final_data, thinking_time)) | |
yield steps, total_thinking_time | |