Spaces:
Runtime error
Runtime error
Update g1.py
Browse files
g1.py
CHANGED
@@ -4,58 +4,69 @@ import logging
|
|
4 |
from typing import List, Tuple, Generator, Optional, Any
|
5 |
import groq
|
6 |
|
7 |
-
#
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
11 |
MAX_RETRIES = 3
|
12 |
-
DEFAULT_MODEL = "llama3-8b-8192"
|
13 |
|
14 |
logging.basicConfig(level=logging.INFO)
|
15 |
logger = logging.getLogger(__name__)
|
16 |
|
17 |
class APIError(Exception):
|
18 |
-
"""
|
19 |
pass
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
def parse_json_response(raw_response: str) -> dict:
|
22 |
-
"""Улучшенный парсер с валидацией
|
23 |
-
logger.debug("Parsing JSON response: %s", raw_response[:200])
|
24 |
-
|
25 |
try:
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
return parsed
|
39 |
|
40 |
def make_api_call(
|
41 |
messages: List[dict],
|
42 |
max_tokens: int,
|
43 |
is_final_answer: bool = False,
|
44 |
custom_client: Optional[Any] = None,
|
45 |
-
|
|
|
46 |
) -> Any:
|
47 |
-
"""
|
48 |
client = custom_client or groq.Client()
|
|
|
|
|
49 |
|
50 |
params = {
|
51 |
"model": model,
|
52 |
"messages": messages,
|
53 |
"max_tokens": max_tokens,
|
54 |
"temperature": 0.5,
|
|
|
55 |
}
|
56 |
-
|
57 |
-
if not is_final_answer:
|
58 |
-
params["response_format"] = {"type": "json_object"}
|
59 |
|
60 |
for attempt in range(1, MAX_RETRIES + 1):
|
61 |
try:
|
@@ -63,128 +74,110 @@ def make_api_call(
|
|
63 |
content = response.choices[0].message.content
|
64 |
|
65 |
if response.choices[0].finish_reason == "length":
|
66 |
-
logger.warning("
|
67 |
-
|
68 |
return content if is_final_answer else parse_json_response(content)
|
69 |
-
|
70 |
except groq.APIConnectionError as e:
|
71 |
-
logger.error("
|
72 |
if attempt == MAX_RETRIES:
|
73 |
-
raise
|
74 |
-
time.sleep(RETRY_DELAY * attempt)
|
75 |
-
|
76 |
except groq.APIError as e:
|
77 |
-
logger.error("API
|
78 |
if e.status_code >= 500:
|
79 |
time.sleep(RETRY_DELAY * attempt)
|
80 |
else:
|
81 |
-
raise APIError(
|
82 |
-
|
83 |
-
except ValueError as e:
|
84 |
-
logger.error("Parsing error: %s. Attempt %d/%d", e, attempt, MAX_RETRIES)
|
85 |
-
if attempt == MAX_RETRIES:
|
86 |
-
raise APIError("Failed to parse response after multiple attempts")
|
87 |
-
time.sleep(RETRY_DELAY * attempt)
|
88 |
|
89 |
def generate_response(
|
90 |
prompt: str,
|
91 |
context: Optional[str] = None,
|
92 |
custom_client: Optional[Any] = None,
|
93 |
-
max_steps: int =
|
94 |
-
|
|
|
95 |
) -> Generator[Tuple[List[Tuple[str, str, float]], Optional[float]], None, None]:
|
96 |
-
"""Улучшенная генерация ответа
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
raise ValueError("max_steps must be at least 1")
|
102 |
-
|
103 |
-
system_template = (
|
104 |
-
"Вы – эксперт по Python. Анализируйте вопрос по шагам:\n"
|
105 |
-
"1. Разбейте вопрос на под-вопросы\n"
|
106 |
-
"2. Для каждого сгенерируйте 2-3 варианта рассуждений\n"
|
107 |
-
"3. Оцените варианты по критериям ясности и релевантности\n"
|
108 |
-
"4. Выберите оптимальную цепочку\n"
|
109 |
-
"Формат ответа: JSON с ключами title, content, next_action"
|
110 |
)
|
111 |
|
112 |
-
user_content = f"Context: {context}\n\nQuestion: {prompt}" if context else prompt
|
113 |
messages = [
|
114 |
-
{"role": "system", "content":
|
115 |
-
{"role": "user", "content":
|
116 |
]
|
117 |
-
|
118 |
steps = []
|
119 |
total_time = 0.0
|
120 |
-
|
121 |
-
|
122 |
-
while step_count <= max_steps:
|
123 |
-
start = time.monotonic()
|
124 |
-
|
125 |
-
try:
|
126 |
-
response = make_api_call(
|
127 |
-
messages=messages,
|
128 |
-
max_tokens=DEFAULT_MAX_TOKENS,
|
129 |
-
is_final_answer=False,
|
130 |
-
custom_client=custom_client,
|
131 |
-
model=model
|
132 |
-
)
|
133 |
-
except APIError as e:
|
134 |
-
logger.error("Critical error in step %d: %s", step_count, e)
|
135 |
-
steps.append(("Error", str(e), time.monotonic() - start))
|
136 |
-
break
|
137 |
-
|
138 |
-
elapsed = time.monotonic() - start
|
139 |
-
total_time += elapsed
|
140 |
-
|
141 |
-
step_entry = (
|
142 |
-
f"Step {step_count}: {response['title']}",
|
143 |
-
response["content"],
|
144 |
-
elapsed
|
145 |
-
)
|
146 |
-
steps.append(step_entry)
|
147 |
-
messages.append({"role": "assistant", "content": json.dumps(response, ensure_ascii=False)})
|
148 |
-
|
149 |
-
if response["next_action"] == "final_answer" or step_count == max_steps:
|
150 |
-
break
|
151 |
-
|
152 |
-
step_count += 1
|
153 |
-
yield steps, None
|
154 |
-
|
155 |
-
# Финализация ответа
|
156 |
-
messages.append({
|
157 |
-
"role": "user",
|
158 |
-
"content": "Сформируй окончательный ответ с детальным объяснением и примерами кода, если необходимо."
|
159 |
-
})
|
160 |
-
|
161 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
final_answer = make_api_call(
|
163 |
messages=messages,
|
164 |
-
max_tokens=
|
165 |
is_final_answer=True,
|
166 |
custom_client=custom_client,
|
167 |
-
|
168 |
)
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
|
|
176 |
yield steps, total_time
|
177 |
|
178 |
if __name__ == "__main__":
|
179 |
-
context = "https://console.groq.com/docs/autogen"
|
180 |
-
prompt = "учитывай контекст autogen в ответ"
|
181 |
-
|
182 |
try:
|
183 |
-
for steps, total_time in generate_response(
|
|
|
|
|
|
|
|
|
|
|
184 |
if total_time is not None:
|
185 |
-
print(f"\n
|
186 |
-
print("="*
|
187 |
for title, content, t in steps:
|
188 |
-
print(f"\n{title}
|
189 |
-
|
190 |
-
|
|
|
|
|
|
|
|
4 |
from typing import List, Tuple, Generator, Optional, Any
|
5 |
import groq
|
6 |
|
7 |
+
# Конфигурация
|
8 |
+
DEFAULT_ITER_MODEL = "llama3-70b-8192"
|
9 |
+
DEFAULT_FINAL_MODEL = "mixtral-8x7b-32768"
|
10 |
+
MAX_TOKENS_LIMIT = 8192
|
11 |
+
RETRY_DELAY = 1.5
|
12 |
MAX_RETRIES = 3
|
|
|
13 |
|
14 |
logging.basicConfig(level=logging.INFO)
|
15 |
logger = logging.getLogger(__name__)
|
16 |
|
17 |
class APIError(Exception):
|
18 |
+
"""Базовое исключение для API ошибок"""
|
19 |
pass
|
20 |
|
21 |
+
class APIConnectionError(APIError):
|
22 |
+
"""Ошибка подключения к API"""
|
23 |
+
pass
|
24 |
+
|
25 |
+
def adjust_max_tokens(model: str, max_tokens: int) -> int:
|
26 |
+
"""Автоматическая корректировка max_tokens в зависимости от модели"""
|
27 |
+
model_limits = {
|
28 |
+
"llama3-8b-8192": 8192,
|
29 |
+
"llama3-70b-8192": 8192,
|
30 |
+
"mixtral-8x7b-32768": 32768
|
31 |
+
}
|
32 |
+
return min(max_tokens, model_limits.get(model, 4096))
|
33 |
+
|
34 |
def parse_json_response(raw_response: str) -> dict:
|
35 |
+
"""Улучшенный парсер с полной валидацией"""
|
|
|
|
|
36 |
try:
|
37 |
+
data = json.loads(raw_response)
|
38 |
+
assert isinstance(data, dict), "Ответ должен быть объектом"
|
39 |
+
assert all(k in data for k in ["title", "content", "next_action"]), "Отсутствуют обязательные ключи"
|
40 |
+
assert data["next_action"] in {"continue", "final_answer"}, "Некорректное действие"
|
41 |
+
return data
|
42 |
+
except (json.JSONDecodeError, AssertionError) as e:
|
43 |
+
logger.error(f"Ошибка парсинга: {str(e)}")
|
44 |
+
return {
|
45 |
+
"title": "Ошибка парсинга",
|
46 |
+
"content": str(e),
|
47 |
+
"next_action": "final_answer"
|
48 |
+
}
|
|
|
49 |
|
50 |
def make_api_call(
|
51 |
messages: List[dict],
|
52 |
max_tokens: int,
|
53 |
is_final_answer: bool = False,
|
54 |
custom_client: Optional[Any] = None,
|
55 |
+
iter_model: str = DEFAULT_ITER_MODEL,
|
56 |
+
final_model: str = DEFAULT_FINAL_MODEL
|
57 |
) -> Any:
|
58 |
+
"""Улучшенный метод вызова API"""
|
59 |
client = custom_client or groq.Client()
|
60 |
+
model = final_model if is_final_answer else iter_model
|
61 |
+
max_tokens = adjust_max_tokens(model, max_tokens)
|
62 |
|
63 |
params = {
|
64 |
"model": model,
|
65 |
"messages": messages,
|
66 |
"max_tokens": max_tokens,
|
67 |
"temperature": 0.5,
|
68 |
+
"response_format": {"type": "json_object"} if not is_final_answer else None
|
69 |
}
|
|
|
|
|
|
|
70 |
|
71 |
for attempt in range(1, MAX_RETRIES + 1):
|
72 |
try:
|
|
|
74 |
content = response.choices[0].message.content
|
75 |
|
76 |
if response.choices[0].finish_reason == "length":
|
77 |
+
logger.warning(f"Ответ обрезан! Рекомендуемый max_tokens: {max_tokens * 2}")
|
78 |
+
|
79 |
return content if is_final_answer else parse_json_response(content)
|
80 |
+
|
81 |
except groq.APIConnectionError as e:
|
82 |
+
logger.error(f"Сетевая ошибка (попытка {attempt}/{MAX_RETRIES}): {str(e)}")
|
83 |
if attempt == MAX_RETRIES:
|
84 |
+
raise APIConnectionError(str(e))
|
85 |
+
time.sleep(RETRY_DELAY * (2 ** attempt))
|
86 |
+
|
87 |
except groq.APIError as e:
|
88 |
+
logger.error(f"Ошибка API (код {e.status_code}): {e.message}")
|
89 |
if e.status_code >= 500:
|
90 |
time.sleep(RETRY_DELAY * attempt)
|
91 |
else:
|
92 |
+
raise APIError(e.message)
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
|
94 |
def generate_response(
|
95 |
prompt: str,
|
96 |
context: Optional[str] = None,
|
97 |
custom_client: Optional[Any] = None,
|
98 |
+
max_steps: int = 10,
|
99 |
+
iter_model: str = DEFAULT_ITER_MODEL,
|
100 |
+
final_model: str = DEFAULT_FINAL_MODEL
|
101 |
) -> Generator[Tuple[List[Tuple[str, str, float]], Optional[float]], None, None]:
|
102 |
+
"""Улучшенная генерация ответа"""
|
103 |
+
system_prompt = (
|
104 |
+
"Вы — AI-ассистент для анализа технических вопросов. "
|
105 |
+
"Формат ответа: JSON с ключами title, content, next_action. "
|
106 |
+
"Используйте русский язык и технические термины."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
)
|
108 |
|
|
|
109 |
messages = [
|
110 |
+
{"role": "system", "content": system_prompt},
|
111 |
+
{"role": "user", "content": f"Контекст: {context}\n\nВопрос: {prompt}" if context else prompt}
|
112 |
]
|
113 |
+
|
114 |
steps = []
|
115 |
total_time = 0.0
|
116 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
try:
|
118 |
+
for step in range(1, max_steps + 1):
|
119 |
+
start = time.monotonic()
|
120 |
+
|
121 |
+
try:
|
122 |
+
response = make_api_call(
|
123 |
+
messages=messages,
|
124 |
+
max_tokens=MAX_TOKENS_LIMIT,
|
125 |
+
is_final_answer=False,
|
126 |
+
custom_client=custom_client,
|
127 |
+
iter_model=iter_model
|
128 |
+
)
|
129 |
+
except APIError as e:
|
130 |
+
logger.error("Критическая ошибка: %s", str(e))
|
131 |
+
yield [("Ошибка", str(e), time.monotonic() - start)], None
|
132 |
+
return
|
133 |
+
|
134 |
+
elapsed = time.monotonic() - start
|
135 |
+
total_time += elapsed
|
136 |
+
|
137 |
+
steps.append((f"Шаг {step}: {response['title']}", response["content"], elapsed))
|
138 |
+
messages.append({"role": "assistant", "content": json.dumps(response, ensure_ascii=False)})
|
139 |
+
|
140 |
+
if response["next_action"] == "final_answer":
|
141 |
+
break
|
142 |
+
|
143 |
+
yield steps, None
|
144 |
+
|
145 |
+
# Финализация
|
146 |
+
messages.append({"role": "user", "content": "Сформируйте финальный ответ с примерами кода и пояснениями."})
|
147 |
+
|
148 |
+
final_start = time.monotonic()
|
149 |
final_answer = make_api_call(
|
150 |
messages=messages,
|
151 |
+
max_tokens=MAX_TOKENS_LIMIT,
|
152 |
is_final_answer=True,
|
153 |
custom_client=custom_client,
|
154 |
+
final_model=final_model
|
155 |
)
|
156 |
+
total_time += time.monotonic() - final_start
|
157 |
+
|
158 |
+
steps.append(("Финальный ответ", final_answer, time.monotonic() - final_start))
|
159 |
+
|
160 |
+
except Exception as e:
|
161 |
+
logger.exception("Непредвиденная ошибка:")
|
162 |
+
steps.append(("Критическая ошибка", str(e), 0.0))
|
163 |
+
|
164 |
yield steps, total_time
|
165 |
|
166 |
if __name__ == "__main__":
|
|
|
|
|
|
|
167 |
try:
|
168 |
+
for steps, total_time in generate_response(
|
169 |
+
prompt="Опишите процесс обработки запросов в Groq API",
|
170 |
+
context="Используйте официальную документацию Groq",
|
171 |
+
iter_model="llama3-70b-8192",
|
172 |
+
final_model="mixtral-8x7b-32768"
|
173 |
+
):
|
174 |
if total_time is not None:
|
175 |
+
print(f"\n✅ Готово за {total_time:.2f}s")
|
176 |
+
print("=" * 60)
|
177 |
for title, content, t in steps:
|
178 |
+
print(f"\n🔹 {title} [{t:.2f}s]\n{content}")
|
179 |
+
|
180 |
+
except APIConnectionError as e:
|
181 |
+
print(f"🚨 Ошибка подключения: {str(e)}")
|
182 |
+
except APIError as e:
|
183 |
+
print(f"🚨 Ошибка API: {str(e)}")
|