feat: AI proxy via papa-app (Anthropic from RU), env and docker-compose

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Yuriy 2026-02-16 06:39:48 +03:00
parent 25be90de7b
commit 9d49c9bb6b
4 changed files with 43 additions and 16 deletions

View File

@ -7,3 +7,9 @@ NODE_ENV=development
# AI — Anthropic Claude (единственный AI-провайдер). Бэкенд: ANTHROPIC_API_KEY
# OPENAI_API_KEY не используется
ANTHROPIC_API_KEY=
# Прокси Anthropic через papa-app на Railway (если Anthropic блокирует с РФ IP)
# AI_PROXY_URL=https://papa-app-production.up.railway.app/api/proxy/anthropic
# AI_PROXY_SECRET=klg-refly-proxy-2026
AI_PROXY_URL=
AI_PROXY_SECRET=

View File

@ -21,8 +21,11 @@ class ChatResponse(BaseModel):
@router.post("/chat", response_model=ChatResponse)
async def ai_chat(req: ChatRequest, user=Depends(get_current_user)):
api_key = getattr(settings, "ANTHROPIC_API_KEY", None) or ""
if not api_key or api_key == "":
raise HTTPException(400, "AI assistant not configured")
proxy_url = getattr(settings, "AI_PROXY_URL", "") or ""
proxy_secret = getattr(settings, "AI_PROXY_SECRET", "") or ""
if not proxy_url or not proxy_secret:
if not api_key or api_key == "":
raise HTTPException(400, "AI assistant not configured (set ANTHROPIC_API_KEY or AI_PROXY_URL+AI_PROXY_SECRET)")
db = SessionLocal()
try:
@ -52,21 +55,33 @@ async def ai_chat(req: ChatRequest, user=Depends(get_current_user)):
Отвечай на русском языке. Будь конкретным и профессиональным.
Используй авиационную терминологию где уместно."""
payload = {
"model": getattr(settings, "ANTHROPIC_MODEL", "claude-sonnet-4-20250514"),
"max_tokens": 1024,
"system": system_prompt,
"messages": [{"role": "user", "content": req.message}],
}
async with httpx.AsyncClient(timeout=30.0) as client:
resp = await client.post(
"https://api.anthropic.com/v1/messages",
headers={
"x-api-key": api_key,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
json={
"model": getattr(settings, "ANTHROPIC_MODEL", "claude-sonnet-4-20250514"),
"max_tokens": 1024,
"system": system_prompt,
"messages": [{"role": "user", "content": req.message}],
},
)
if proxy_url and proxy_secret:
resp = await client.post(
proxy_url,
headers={
"x-proxy-secret": proxy_secret,
"content-type": "application/json",
},
json=payload,
)
else:
resp = await client.post(
"https://api.anthropic.com/v1/messages",
headers={
"x-api-key": api_key,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
json=payload,
)
if resp.status_code != 200:
raise HTTPException(502, f"AI service error: {resp.status_code}")

View File

@ -67,6 +67,9 @@ class Settings(BaseSettings):
# AI (Anthropic Claude)
ANTHROPIC_API_KEY: str = ""
ANTHROPIC_MODEL: str = "claude-sonnet-4-20250514"
# Прокси Anthropic через papa-app (Railway) — для обхода блокировки с российских IP
AI_PROXY_URL: str = ""
AI_PROXY_SECRET: str = ""
@property
def database_url(self) -> str:

View File

@ -95,6 +95,9 @@ services:
FGIS_ORG_ID: ${FGIS_ORG_ID:-}
FGIS_API_KEY: ${FGIS_API_KEY:-}
FGIS_CERT_PATH: /etc/ssl/fgis/client.pem
# Прокси Anthropic через papa-app (Railway) — обход блокировки с российских IP
AI_PROXY_URL: ${AI_PROXY_URL:-}
AI_PROXY_SECRET: ${AI_PROXY_SECRET:-}
ports:
- "8000:8000"
volumes: