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 # AI — Anthropic Claude (единственный AI-провайдер). Бэкенд: ANTHROPIC_API_KEY
# OPENAI_API_KEY не используется # OPENAI_API_KEY не используется
ANTHROPIC_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) @router.post("/chat", response_model=ChatResponse)
async def ai_chat(req: ChatRequest, user=Depends(get_current_user)): async def ai_chat(req: ChatRequest, user=Depends(get_current_user)):
api_key = getattr(settings, "ANTHROPIC_API_KEY", None) or "" api_key = getattr(settings, "ANTHROPIC_API_KEY", None) or ""
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 == "": if not api_key or api_key == "":
raise HTTPException(400, "AI assistant not configured") raise HTTPException(400, "AI assistant not configured (set ANTHROPIC_API_KEY or AI_PROXY_URL+AI_PROXY_SECRET)")
db = SessionLocal() db = SessionLocal()
try: try:
@ -52,7 +55,24 @@ 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: async with httpx.AsyncClient(timeout=30.0) as client:
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( resp = await client.post(
"https://api.anthropic.com/v1/messages", "https://api.anthropic.com/v1/messages",
headers={ headers={
@ -60,12 +80,7 @@ async def ai_chat(req: ChatRequest, user=Depends(get_current_user)):
"anthropic-version": "2023-06-01", "anthropic-version": "2023-06-01",
"content-type": "application/json", "content-type": "application/json",
}, },
json={ json=payload,
"model": getattr(settings, "ANTHROPIC_MODEL", "claude-sonnet-4-20250514"),
"max_tokens": 1024,
"system": system_prompt,
"messages": [{"role": "user", "content": req.message}],
},
) )
if resp.status_code != 200: if resp.status_code != 200:

View File

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

View File

@ -95,6 +95,9 @@ services:
FGIS_ORG_ID: ${FGIS_ORG_ID:-} FGIS_ORG_ID: ${FGIS_ORG_ID:-}
FGIS_API_KEY: ${FGIS_API_KEY:-} FGIS_API_KEY: ${FGIS_API_KEY:-}
FGIS_CERT_PATH: /etc/ssl/fgis/client.pem 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: ports:
- "8000:8000" - "8000:8000"
volumes: volumes: