Spaces:
Runtime error
Runtime error
import time | |
import json | |
import logging | |
from typing import List, Tuple, Generator, Optional, Any | |
import groq | |
# Конфигурация | |
DEFAULT_ITER_MODEL = "llama3-70b-8192" | |
DEFAULT_FINAL_MODEL = "mixtral-8x7b-32768" | |
MAX_TOKENS_LIMIT = 8192 | |
RETRY_DELAY = 1.5 | |
MAX_RETRIES = 3 | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
class APIError(Exception): | |
"""Базовое исключение для API ошибок""" | |
pass | |
class APIConnectionError(APIError): | |
"""Ошибка подключения к API""" | |
pass | |
def adjust_max_tokens(model: str, max_tokens: int) -> int: | |
"""Автоматическая корректировка max_tokens в зависимости от модели""" | |
model_limits = { | |
"llama3-8b-8192": 8192, | |
"llama3-70b-8192": 8192, | |
"mixtral-8x7b-32768": 32768 | |
} | |
return min(max_tokens, model_limits.get(model, 4096)) | |
def parse_json_response(raw_response: str) -> dict: | |
"""Улучшенный парсер с полной валидацией""" | |
try: | |
data = json.loads(raw_response) | |
assert isinstance(data, dict), "Ответ должен быть объектом" | |
assert all(k in data for k in ["title", "content", "next_action"]), "Отсутствуют обязательные ключи" | |
assert data["next_action"] in {"continue", "final_answer"}, "Некорректное действие" | |
return data | |
except (json.JSONDecodeError, AssertionError) as e: | |
logger.error(f"Ошибка парсинга: {str(e)}") | |
return { | |
"title": "Ошибка парсинга", | |
"content": str(e), | |
"next_action": "final_answer" | |
} | |
def make_api_call( | |
messages: List[dict], | |
max_tokens: int, | |
is_final_answer: bool = False, | |
custom_client: Optional[Any] = None, | |
iter_model: str = DEFAULT_ITER_MODEL, | |
final_model: str = DEFAULT_FINAL_MODEL | |
) -> Any: | |
"""Улучшенный метод вызова API""" | |
client = custom_client or groq.Client() | |
model = final_model if is_final_answer else iter_model | |
max_tokens = adjust_max_tokens(model, max_tokens) | |
params = { | |
"model": model, | |
"messages": messages, | |
"max_tokens": max_tokens, | |
"temperature": 0.5, | |
"response_format": {"type": "json_object"} if not is_final_answer else None | |
} | |
for attempt in range(1, MAX_RETRIES + 1): | |
try: | |
response = client.chat.completions.create(**params) | |
content = response.choices[0].message.content | |
if response.choices[0].finish_reason == "length": | |
logger.warning(f"Ответ обрезан! Рекомендуемый max_tokens: {max_tokens * 2}") | |
return content if is_final_answer else parse_json_response(content) | |
except groq.APIConnectionError as e: | |
logger.error(f"Сетевая ошибка (попытка {attempt}/{MAX_RETRIES}): {str(e)}") | |
if attempt == MAX_RETRIES: | |
raise APIConnectionError(str(e)) | |
time.sleep(RETRY_DELAY * (2 ** attempt)) | |
except groq.APIError as e: | |
logger.error(f"Ошибка API (код {e.status_code}): {e.message}") | |
if e.status_code >= 500: | |
time.sleep(RETRY_DELAY * attempt) | |
else: | |
raise APIError(e.message) | |
def generate_response( | |
prompt: str, | |
context: Optional[str] = None, | |
custom_client: Optional[Any] = None, | |
max_steps: int = 10, | |
iter_model: str = DEFAULT_ITER_MODEL, | |
final_model: str = DEFAULT_FINAL_MODEL | |
) -> Generator[Tuple[List[Tuple[str, str, float]], Optional[float]], None, None]: | |
"""Улучшенная генерация ответа""" | |
system_prompt = ( | |
"Вы — AI-ассистент для анализа технических вопросов. " | |
"Формат ответа: JSON с ключами title, content, next_action. " | |
"Используйте русский язык и технические термины." | |
) | |
messages = [ | |
{"role": "system", "content": system_prompt}, | |
{"role": "user", "content": f"Контекст: {context}\n\nВопрос: {prompt}" if context else prompt} | |
] | |
steps = [] | |
total_time = 0.0 | |
try: | |
for step in range(1, max_steps + 1): | |
start = time.monotonic() | |
try: | |
response = make_api_call( | |
messages=messages, | |
max_tokens=MAX_TOKENS_LIMIT, | |
is_final_answer=False, | |
custom_client=custom_client, | |
iter_model=iter_model | |
) | |
except APIError as e: | |
logger.error("Критическая ошибка: %s", str(e)) | |
yield [("Ошибка", str(e), time.monotonic() - start)], None | |
return | |
elapsed = time.monotonic() - start | |
total_time += elapsed | |
steps.append((f"Шаг {step}: {response['title']}", response["content"], elapsed)) | |
messages.append({"role": "assistant", "content": json.dumps(response, ensure_ascii=False)}) | |
if response["next_action"] == "final_answer": | |
break | |
yield steps, None | |
# Финализация | |
messages.append({"role": "user", "content": "Сформируйте финальный ответ с примерами кода и пояснениями."}) | |
final_start = time.monotonic() | |
final_answer = make_api_call( | |
messages=messages, | |
max_tokens=MAX_TOKENS_LIMIT, | |
is_final_answer=True, | |
custom_client=custom_client, | |
final_model=final_model | |
) | |
total_time += time.monotonic() - final_start | |
steps.append(("Финальный ответ", final_answer, time.monotonic() - final_start)) | |
except Exception as e: | |
logger.exception("Непредвиденная ошибка:") | |
steps.append(("Критическая ошибка", str(e), 0.0)) | |
yield steps, total_time | |
if __name__ == "__main__": | |
try: | |
for steps, total_time in generate_response( | |
prompt="Опишите процесс обработки запросов в Groq API", | |
context="Используйте официальную документацию Groq", | |
iter_model="llama3-70b-8192", | |
final_model="mixtral-8x7b-32768" | |
): | |
if total_time is not None: | |
print(f"\n✅ Готово за {total_time:.2f}s") | |
print("=" * 60) | |
for title, content, t in steps: | |
print(f"\n🔹 {title} [{t:.2f}s]\n{content}") | |
except APIConnectionError as e: | |
print(f"🚨 Ошибка подключения: {str(e)}") | |
except APIError as e: | |
print(f"🚨 Ошибка API: {str(e)}") |