This commit is contained in:
xds
2026-03-22 22:31:11 +03:00
parent d9dad0f7d9
commit 9e630fb5d2
4 changed files with 30 additions and 18 deletions

4
.env
View File

@@ -10,4 +10,6 @@ MINIO_SECRET_KEY=SuperSecretPassword123!
MINIO_BUCKET=filam3d
MINIO_SECURE=false
AI_PROXY_URL=http://82.22.174.14:8001
AI_PROXY_SALT=AbVJUkwTPcUWJWhPzmjXb5p4SYyKmYC5m1uVW7Dhi7o
AI_PROXY_SALT=AbVJUkwTPcUWJWhPzmjXb5p4SYyKmYC5m1uVW7Dhi7o
QWEN_API_KEY=sk-991942d15b424cc89513498bb2946045
QWEN_MODEL=qwen3.5-plus

View File

@@ -4,6 +4,9 @@ from pydantic_settings import BaseSettings
class Settings(BaseSettings):
DATABASE_URL: str = "postgresql+asyncpg://print3d:P3D_PASSWORD@31.59.58.220:5432/print3d"
GOOGLE_API_KEY: str = ""
QWEN_API_KEY: str = ""
QWEN_MODEL: str = "qwen-plus"
QWEN_BASE_URL: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
AI_PROXY_URL: str = "http://82.22.174.14:8001"
AI_PROXY_SALT: str = "change_me_in_production"
TELEGRAM_BOT_TOKEN: str = ""

View File

@@ -75,25 +75,32 @@ async def _call_via_proxy(proxy_url: str, proxy_salt: str, messages: list[dict])
async def _call_direct(api_key: str, system: str, user_message: str) -> str:
"""Call Google GenAI directly via REST (no SDK dependency)."""
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={api_key}"
"""Call Qwen via OpenAI-compatible DashScope API."""
url = f"{settings.QWEN_BASE_URL.rstrip('/')}/chat/completions"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
payload = {
"system_instruction": {"parts": [{"text": system}]},
"contents": [{"role": "user", "parts": [{"text": user_message}]}],
"generationConfig": {"temperature": 0.3, "maxOutputTokens": 1024},
"model": settings.QWEN_MODEL,
"messages": [
{"role": "system", "content": system},
{"role": "user", "content": user_message},
],
"temperature": 0.3,
"max_tokens": 1024,
}
async with httpx.AsyncClient(timeout=60.0) as client:
resp = await client.post(url, json=payload)
resp = await client.post(url, json=payload, headers=headers)
if resp.status_code != 200:
logger.error("Google API error %d: %s", resp.status_code, resp.text[:500])
raise ValueError(f"Google API ошибка: {resp.status_code}")
logger.error("Qwen API error %d: %s", resp.status_code, resp.text[:500])
raise ValueError(f"Qwen API ошибка: {resp.status_code}")
data = resp.json()
candidates = data.get("candidates", [])
if not candidates:
raise ValueError("Google API вернул пустой ответ")
parts = candidates[0].get("content", {}).get("parts", [])
return parts[0].get("text", "") if parts else ""
choices = data.get("choices", [])
if not choices:
raise ValueError("Qwen API вернул пустой ответ")
return choices[0].get("message", {}).get("content", "")
def _parse_json_response(text: str) -> dict:
@@ -122,7 +129,7 @@ async def get_material_recommendation(
use_proxy = True
proxy_url = settings.AI_PROXY_URL
proxy_salt = settings.AI_PROXY_SALT
direct_api_key = settings.GOOGLE_API_KEY
direct_api_key = settings.QWEN_API_KEY
if db:
use_proxy_str = await _get_setting(db, "ai_use_proxy", "true")
@@ -153,9 +160,9 @@ async def get_material_recommendation(
messages = [{"role": "user", "content": system + "\n\n" + user_message}]
response_text = await _call_via_proxy(proxy_url, proxy_salt, messages)
else:
logger.info("Mode: DIRECT (Google API)")
logger.info("Mode: DIRECT (Qwen API, model=%s)", settings.QWEN_MODEL)
if not direct_api_key:
raise ValueError("Google API Key не настроен")
raise ValueError("Qwen API Key не настроен (QWEN_API_KEY)")
response_text = await _call_direct(direct_api_key, system, user_message)
elapsed = time.time() - start_time

View File

@@ -116,7 +116,7 @@ const settingsGroups = [
{ key: 'ai_use_proxy', label: 'Использовать AI-прокси (true/false)', placeholder: 'true' },
{ key: 'ai_proxy_url', label: 'URL прокси', placeholder: 'http://82.22.174.14:8001' },
{ key: 'ai_proxy_salt', label: 'Секретная соль прокси', placeholder: '' },
{ key: 'ai_direct_api_key', label: 'Google API Key (прямое подключение)', placeholder: '' },
{ key: 'ai_direct_api_key', label: 'Qwen API Key (DashScope)', placeholder: '' },
],
},
{