Compare commits
4 Commits
c256012a69
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 95f87c253c | |||
| 32bfec2074 | |||
| a649fb1192 | |||
| 24f4ce118f |
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" project-jdk-name="openjdk-23" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.14 (New-planet-api)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.14 (New-planet-api)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
7
.idea/new-planet-backend.iml
generated
7
.idea/new-planet-backend.iml
generated
@@ -2,8 +2,11 @@
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/new-planet-backend" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.14 (New-planet-api)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
36
logs/app.log
Normal file
36
logs/app.log
Normal file
@@ -0,0 +1,36 @@
|
||||
2025-12-23 21:37:30 - root - INFO - Starting up...
|
||||
2025-12-23 21:37:30 - root - INFO - Starting up...
|
||||
2025-12-23 21:39:37 - root - INFO - Shutting down...
|
||||
2025-12-23 21:39:37 - root - INFO - Shutting down...
|
||||
2025-12-23 21:39:42 - root - INFO - Starting up...
|
||||
2025-12-23 21:39:42 - root - INFO - Starting up...
|
||||
2025-12-23 22:07:40 - root - INFO - Shutting down...
|
||||
2025-12-23 22:07:40 - root - INFO - Shutting down...
|
||||
2025-12-23 22:07:45 - root - INFO - Starting up...
|
||||
2025-12-23 22:07:45 - root - INFO - Starting up...
|
||||
2025-12-23 22:12:42 - root - INFO - Shutting down...
|
||||
2025-12-23 22:12:42 - root - INFO - Shutting down...
|
||||
2025-12-23 22:12:47 - root - INFO - Starting up...
|
||||
2025-12-23 22:12:47 - root - INFO - Starting up...
|
||||
2025-12-23 22:23:22 - root - INFO - Shutting down...
|
||||
2025-12-23 22:23:22 - root - INFO - Shutting down...
|
||||
2025-12-23 22:23:27 - root - INFO - Starting up...
|
||||
2025-12-23 22:23:27 - root - INFO - Starting up...
|
||||
2025-12-23 22:52:47 - root - INFO - Shutting down...
|
||||
2025-12-23 22:52:47 - root - INFO - Shutting down...
|
||||
2025-12-23 22:52:54 - root - INFO - Starting up...
|
||||
2025-12-23 22:52:54 - root - INFO - Starting up...
|
||||
2025-12-23 23:12:01 - root - INFO - Shutting down...
|
||||
2025-12-23 23:12:01 - root - INFO - Shutting down...
|
||||
2025-12-23 23:12:07 - root - INFO - Starting up...
|
||||
2025-12-23 23:12:07 - root - INFO - Starting up...
|
||||
2025-12-23 23:15:05 - root - INFO - Shutting down...
|
||||
2025-12-23 23:15:05 - root - INFO - Shutting down...
|
||||
2025-12-23 23:15:10 - root - INFO - Starting up...
|
||||
2025-12-23 23:15:10 - root - INFO - Starting up...
|
||||
2025-12-24 00:01:39 - root - INFO - Shutting down...
|
||||
2025-12-24 00:01:39 - root - INFO - Shutting down...
|
||||
2025-12-24 00:01:45 - root - INFO - Starting up...
|
||||
2025-12-24 00:01:45 - root - INFO - Starting up...
|
||||
2025-12-24 01:38:58 - root - INFO - Shutting down...
|
||||
2025-12-24 01:38:58 - root - INFO - Shutting down...
|
||||
@@ -1,21 +1,27 @@
|
||||
# Database
|
||||
|
||||
GIGACHAT_CLIENT_ID=019966f0-5781-76e6-a84f-ec7de158188a
|
||||
GIGACHAT_CLIENT_SECRET=MDE5OTY2ZjAtNTc4MS03NmU2LWE4NGYtZWM3ZGUxNTgxODhhOjI3MDMxZjIxLWY3NWYtNGI4NS05MzM1LTI4ZDYyOWM3MmM0MA==
|
||||
GIGACHAT_AUTH_URL=https://ngw.devices.sberbank.ru:9443/api/v2/oauth
|
||||
GIGACHAT_BASE_URL=https://gigachat.devices.sberbank.ru/api/v1
|
||||
GIGACHAT_MODEL_CHAT=GigaChat-2-Lite
|
||||
GIGACHAT_MODEL_SCHEDULE=GigaChat-2-Pro
|
||||
|
||||
# Security
|
||||
SECRET_KEY=3db8542397edddbd6162ad823157e36f8d47232aa646725d4799266229ba7aa4
|
||||
|
||||
# Database
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DB=newplanet
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/newplanet
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_DB=0
|
||||
REDIS_URL=redis://localhost:6379
|
||||
|
||||
# Security
|
||||
SECRET_KEY=jwt-secret-key
|
||||
|
||||
# Storage (MinIO/S3)
|
||||
# Storage
|
||||
STORAGE_ENDPOINT=localhost:9000
|
||||
STORAGE_ACCESS_KEY=minioadmin
|
||||
STORAGE_SECRET_KEY=minioadmin
|
||||
@@ -23,6 +29,5 @@ STORAGE_BUCKET=new-planet-images
|
||||
STORAGE_USE_SSL=false
|
||||
STORAGE_REGION=us-east-1
|
||||
|
||||
# GigaChat API
|
||||
GIGACHAT_CLIENT_ID=gigachat-client-id
|
||||
GIGACHAT_CLIENT_SECRET="gigachat-token-here"
|
||||
# Agents
|
||||
AI_AGENT_BASE_URL=http://localhost:8001
|
||||
@@ -23,12 +23,13 @@ Backend API для мобильного приложения **Новая Пла
|
||||
### Установка
|
||||
|
||||
1. Клонируйте репозиторий
|
||||
2. Установите зависимости:
|
||||
2. Установить окружение ("python -m venv venv")
|
||||
3. Установите зависимости:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Настройте `.env`:
|
||||
4. Настройте `.env`:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
#В целом вам нужно поменять GIGACHAT API секцию, JWT Secret key сгенерить, просто в поисковике генератор на 256 байт сделаете JWT
|
||||
@@ -36,7 +37,7 @@ cp .env.example .env
|
||||
# Отредактируйте .env с вашими настройками
|
||||
```
|
||||
|
||||
4. Запустите инфраструктуру (Docker):
|
||||
5. Запустите инфраструктуру (Docker):
|
||||
```bash
|
||||
docker-compose -f docker/docker-compose.yml up -d
|
||||
# или используйте вариант ниже,но лучше вариант выше для избежания непредвиденного
|
||||
@@ -44,12 +45,12 @@ docker-compose -f docker/docker-compose.yml up -d
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
5. Примените миграции:
|
||||
6. Примените миграции:
|
||||
```bash
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
6. Запустите сервер:
|
||||
7. Запустите сервер:
|
||||
```bash
|
||||
uvicorn app.main:app --reload
|
||||
# если не запустилось, проверяйте есть ли .venv(установлено ли окружение для питона), также попробуйте в венве uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
|
||||
BIN
new-planet-backend/app/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/__pycache__/config.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/__pycache__/main.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/__pycache__/main.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/api/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/api/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/api/__pycache__/deps.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/api/__pycache__/deps.cpython-313.pyc
Normal file
Binary file not shown.
@@ -7,7 +7,7 @@ from app.crud import user as crud_user
|
||||
from app.services.auth_service import auth_service
|
||||
from app.models.user import User
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/v1/auth/login")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
|
||||
Binary file not shown.
BIN
new-planet-backend/app/api/v1/__pycache__/ai.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/api/v1/__pycache__/ai.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/api/v1/__pycache__/auth.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/api/v1/__pycache__/auth.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/api/v1/__pycache__/images.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/api/v1/__pycache__/images.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
new-planet-backend/app/api/v1/__pycache__/router.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/api/v1/__pycache__/router.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
new-planet-backend/app/api/v1/__pycache__/tasks.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/api/v1/__pycache__/tasks.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -3,9 +3,27 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import get_db
|
||||
from app.api.deps import get_current_active_user
|
||||
from app.models.user import User
|
||||
from app.schemas.ai import ChatRequest, ChatResponse, ScheduleGenerateRequest, ScheduleGenerateResponse
|
||||
from app.models.ai_conversation import AIConversation
|
||||
from app.schemas.ai import (
|
||||
ChatRequest,
|
||||
ChatResponse,
|
||||
ScheduleGenerateRequest,
|
||||
ScheduleGenerateResponse,
|
||||
ConversationHistory,
|
||||
ConversationListItem,
|
||||
ScheduleUpdateRequest,
|
||||
ScheduleUpdateResponse,
|
||||
RecommendationRequest,
|
||||
RecommendationResponse,
|
||||
)
|
||||
from app.services.chat_service import chat_service
|
||||
from app.services.schedule_generator import schedule_generator
|
||||
from app.services.cache_service import cache_service
|
||||
from app.services.gigachat_service import gigachat_service
|
||||
from app.crud import schedule as crud_schedule, task as crud_task
|
||||
from app.schemas.task import TaskCreate
|
||||
from app.schemas.schedule import ScheduleUpdate
|
||||
from app.core.config import settings
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -30,7 +48,6 @@ async def chat_with_ai(
|
||||
detail=f"Chat error: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/schedule/generate", response_model=ScheduleGenerateResponse)
|
||||
async def generate_schedule_ai(
|
||||
request: ScheduleGenerateRequest,
|
||||
@@ -58,3 +75,4 @@ async def generate_schedule_ai(
|
||||
detail=f"Failed to generate schedule: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
BIN
new-planet-backend/app/core/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/core/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/core/__pycache__/config.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/core/__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/core/__pycache__/logging.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/core/__pycache__/logging.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/core/__pycache__/security.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/core/__pycache__/security.cpython-313.pyc
Normal file
Binary file not shown.
@@ -1,5 +1,13 @@
|
||||
from pydantic_settings import BaseSettings
|
||||
from typing import Optional
|
||||
from dotenv import load_dotenv
|
||||
from pathlib import Path
|
||||
|
||||
# Загружаем .env файл перед созданием Settings
|
||||
# Ищем .env в корне проекта (на уровень выше от app/)
|
||||
env_path = Path(__file__).parent.parent.parent / ".env"
|
||||
# override=True гарантирует, что переменные из .env перезапишут существующие
|
||||
load_dotenv(dotenv_path=env_path, override=True)
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
@@ -10,15 +18,15 @@ class Settings(BaseSettings):
|
||||
DEBUG: bool = False
|
||||
|
||||
# Security
|
||||
SECRET_KEY: str
|
||||
SECRET_KEY: str = "3db8542397edddbd6162ad823157e36f8d47232aa646725d4799266229ba7aa4"
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
|
||||
|
||||
# Database
|
||||
POSTGRES_USER: str
|
||||
POSTGRES_PASSWORD: str
|
||||
POSTGRES_DB: str
|
||||
POSTGRES_USER: str = "postgres"
|
||||
POSTGRES_PASSWORD: str = "postgres"
|
||||
POSTGRES_DB: str = "newplanet"
|
||||
POSTGRES_HOST: str = "localhost"
|
||||
POSTGRES_PORT: int = 5432
|
||||
DATABASE_URL: Optional[str] = None
|
||||
@@ -43,15 +51,22 @@ class Settings(BaseSettings):
|
||||
|
||||
# Storage (MinIO/S3)
|
||||
STORAGE_ENDPOINT: str = "localhost:9000"
|
||||
STORAGE_ACCESS_KEY: str
|
||||
STORAGE_SECRET_KEY: str
|
||||
STORAGE_ACCESS_KEY: str = "minioadmin"
|
||||
STORAGE_SECRET_KEY: str = "minioadmin"
|
||||
STORAGE_BUCKET: str = "new-planet-images"
|
||||
STORAGE_USE_SSL: bool = False
|
||||
STORAGE_REGION: str = "us-east-1"
|
||||
|
||||
# GigaChat
|
||||
GIGACHAT_CLIENT_ID: str
|
||||
GIGACHAT_CLIENT_SECRET: str
|
||||
# AI Agent Service (внешний сервис для работы с GigaChat)
|
||||
# URL можно переопределить через переменную окружения AI_AGENT_BASE_URL
|
||||
# Для Docker сети используйте: http://ai-agent:8000 (или имя сервиса из docker-compose)
|
||||
# Для локальной разработки используйте: http://localhost:8000
|
||||
AI_AGENT_BASE_URL: str = "http://ai-agent:8000"
|
||||
AI_AGENT_TIMEOUT: int = 120 # Таймаут в секундах
|
||||
|
||||
# GigaChat (оставлено для обратной совместимости, но используется через AI-agent сервис)
|
||||
GIGACHAT_CLIENT_ID: str = "019966f4-1c5c-7382-9006-b84419fbe5d1"
|
||||
GIGACHAT_CLIENT_SECRET: str = "MDE5OTY2ZjQtMWM1Yy03MzgyLTkwMDYtYjg0NDE5ZmJlNWQxOjJjODBmOWE2LWU4YWMtNDE4YS1iOGVkLWE4NTE0YzVkNDAwNw=="
|
||||
GIGACHAT_AUTH_URL: str = "https://ngw.devices.sberbank.ru:9443/api/v2/oauth"
|
||||
GIGACHAT_BASE_URL: str = "https://gigachat.devices.sberbank.ru/api/v1"
|
||||
GIGACHAT_MODEL_CHAT: str = "GigaChat-2-Lite"
|
||||
@@ -65,7 +80,9 @@ class Settings(BaseSettings):
|
||||
RATE_LIMIT_PER_MINUTE: int = 60
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
# Путь к .env файлу относительно корня проекта
|
||||
env_file = str(env_path) if env_path.exists() else ".env"
|
||||
env_file_encoding = "utf-8"
|
||||
case_sensitive = True
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from datetime import datetime, timedelta
|
||||
from typing import Optional, Dict, Any
|
||||
from jose import JWTError, jwt
|
||||
import bcrypt
|
||||
from app.core.config import settings
|
||||
from config import settings
|
||||
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
|
||||
BIN
new-planet-backend/app/crud/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/crud/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/crud/__pycache__/base.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/crud/__pycache__/base.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/crud/__pycache__/schedule.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/crud/__pycache__/schedule.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/crud/__pycache__/task.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/crud/__pycache__/task.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/crud/__pycache__/user.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/crud/__pycache__/user.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/db/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/db/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/db/__pycache__/base.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/db/__pycache__/base.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/db/__pycache__/session.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/db/__pycache__/session.cpython-313.pyc
Normal file
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import declarative_base
|
||||
from sqlalchemy import Column, DateTime, func, String
|
||||
import uuid
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from app.db.base import Base
|
||||
from app.db.session import engine
|
||||
from base import Base
|
||||
from session import engine
|
||||
from app.models import user, schedule, task, reward, ai_conversation
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ from app.core.config import settings
|
||||
|
||||
engine = create_async_engine(
|
||||
settings.database_url,
|
||||
echo=settings.DEBUG,
|
||||
future=True
|
||||
echo=settings.DEBUG
|
||||
)
|
||||
|
||||
AsyncSessionLocal = async_sessionmaker(
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Добавляем корневую директорию проекта в PYTHONPATH при прямом запуске
|
||||
# Это нужно, чтобы Python мог найти модуль 'app'
|
||||
# Проверяем, запускается ли файл напрямую, проверяя имя скрипта
|
||||
if sys.argv and len(sys.argv) > 0:
|
||||
script_path = Path(sys.argv[0]).resolve()
|
||||
current_file = Path(__file__).resolve()
|
||||
# Если скрипт запускается напрямую (не через модуль)
|
||||
if script_path == current_file or script_path.name == current_file.name:
|
||||
project_root = current_file.parent.parent # new-planet-backend
|
||||
if str(project_root) not in sys.path:
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
@@ -73,7 +88,7 @@ if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host="0.0.0.0",
|
||||
host="127.0.0.1",
|
||||
port=8000,
|
||||
reload=settings.DEBUG
|
||||
)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
new-planet-backend/app/models/__pycache__/reward.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/models/__pycache__/reward.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
new-planet-backend/app/models/__pycache__/task.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/models/__pycache__/task.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/models/__pycache__/user.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/models/__pycache__/user.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
new-planet-backend/app/schemas/__pycache__/ai.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/schemas/__pycache__/ai.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
new-planet-backend/app/schemas/__pycache__/task.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/schemas/__pycache__/task.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/schemas/__pycache__/token.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/schemas/__pycache__/token.cpython-313.pyc
Normal file
Binary file not shown.
BIN
new-planet-backend/app/schemas/__pycache__/user.cpython-313.pyc
Normal file
BIN
new-planet-backend/app/schemas/__pycache__/user.cpython-313.pyc
Normal file
Binary file not shown.
@@ -38,3 +38,41 @@ class ConversationHistory(BaseModel):
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ConversationListItem(BaseModel):
|
||||
"""Элемент списка разговоров"""
|
||||
conversation_id: str
|
||||
last_message: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
message_count: int = 0
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ScheduleUpdateRequest(BaseModel):
|
||||
"""Запрос на обновление расписания через ИИ"""
|
||||
user_request: str = Field(..., min_length=1, max_length=1000, description="Описание желаемых изменений")
|
||||
|
||||
|
||||
class ScheduleUpdateResponse(BaseModel):
|
||||
"""Ответ после обновления расписания"""
|
||||
schedule_id: str
|
||||
title: str
|
||||
tasks: List[Dict[str, Any]]
|
||||
tokens_used: Optional[int] = None
|
||||
|
||||
|
||||
class RecommendationRequest(BaseModel):
|
||||
"""Запрос на получение рекомендаций"""
|
||||
preferences: List[str] = Field(default_factory=list, description="Предпочтения пользователя")
|
||||
category: Optional[str] = Field(None, description="Категория заданий")
|
||||
completed_tasks: Optional[List[str]] = Field(default_factory=list, description="Уже выполненные задания")
|
||||
top_k: int = Field(5, ge=1, le=20, description="Количество рекомендаций")
|
||||
|
||||
|
||||
class RecommendationResponse(BaseModel):
|
||||
"""Ответ с рекомендациями"""
|
||||
recommendations: List[Dict[str, Any]]
|
||||
total: int
|
||||
@@ -4,6 +4,7 @@ from app.services.storage_service import storage_service, StorageService
|
||||
from app.services.gigachat_service import gigachat_service, GigaChatService
|
||||
from app.services.chat_service import chat_service, ChatService
|
||||
from app.services.schedule_generator import schedule_generator, ScheduleGenerator
|
||||
from app.services.ai_agent_client import ai_agent_client, AIAgentClient
|
||||
|
||||
__all__ = [
|
||||
"auth_service",
|
||||
@@ -18,5 +19,7 @@ __all__ = [
|
||||
"ChatService",
|
||||
"schedule_generator",
|
||||
"ScheduleGenerator",
|
||||
"ai_agent_client",
|
||||
"AIAgentClient",
|
||||
]
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
117
new-planet-backend/app/services/ai_agent_client.py
Normal file
117
new-planet-backend/app/services/ai_agent_client.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import aiohttp
|
||||
from typing import Optional, List, Dict, Any
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
class AIAgentClient:
|
||||
"""
|
||||
Клиент для взаимодействия с внешним AI-agent сервисом.
|
||||
|
||||
Сервис должен быть доступен в Docker сети и предоставлять следующие endpoints:
|
||||
- POST /api/v1/chat - для чата с ИИ
|
||||
- POST /api/v1/schedule/generate - для генерации расписаний
|
||||
|
||||
Примечание: Структура API endpoints может отличаться в зависимости от реализации
|
||||
внешнего сервиса. При необходимости измените пути в методах этого класса.
|
||||
"""
|
||||
|
||||
def __init__(self, base_url: Optional[str] = None):
|
||||
self.base_url = base_url or settings.AI_AGENT_BASE_URL
|
||||
if not self.base_url.endswith('/'):
|
||||
self.base_url = self.base_url.rstrip('/')
|
||||
|
||||
async def chat(
|
||||
self,
|
||||
message: str,
|
||||
conversation_id: Optional[str] = None,
|
||||
context: Optional[List[Dict[str, Any]]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Отправить сообщение в чат через AI-agent сервис.
|
||||
|
||||
Ожидаемый формат ответа от сервиса:
|
||||
{
|
||||
"response": "текст ответа",
|
||||
"conversation_id": "id беседы",
|
||||
"tokens_used": 100,
|
||||
"model": "модель"
|
||||
}
|
||||
или формат GigaChat API (с полем choices).
|
||||
"""
|
||||
url = f"{self.base_url}/api/v1/chat"
|
||||
|
||||
payload = {
|
||||
"message": message,
|
||||
}
|
||||
|
||||
if conversation_id:
|
||||
payload["conversation_id"] = conversation_id
|
||||
|
||||
if context:
|
||||
payload["context"] = context
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as response:
|
||||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
raise Exception(
|
||||
f"AI-agent service error: HTTP {response.status} - {error_text}"
|
||||
)
|
||||
|
||||
result = await response.json()
|
||||
return result
|
||||
|
||||
async def generate_schedule(
|
||||
self,
|
||||
child_age: int,
|
||||
preferences: List[str],
|
||||
date: str,
|
||||
description: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Сгенерировать расписание через AI-agent сервис"""
|
||||
url = f"{self.base_url}/api/v1/schedule/generate"
|
||||
|
||||
payload = {
|
||||
"child_age": child_age,
|
||||
"preferences": preferences,
|
||||
"date": date
|
||||
}
|
||||
|
||||
if description:
|
||||
payload["description"] = description
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as response:
|
||||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
raise Exception(
|
||||
f"AI-agent service error: HTTP {response.status} - {error_text}"
|
||||
)
|
||||
|
||||
result = await response.json()
|
||||
return result
|
||||
|
||||
async def generate_text(
|
||||
self,
|
||||
prompt: str,
|
||||
model: Optional[str] = None
|
||||
) -> str:
|
||||
"""Генерация текста по промпту через AI-agent сервис"""
|
||||
# Для совместимости с текущим интерфейсом используем chat endpoint
|
||||
result = await self.chat(message=prompt)
|
||||
|
||||
# Извлекаем текст ответа
|
||||
# Предполагаем, что ответ имеет структуру ChatResponse
|
||||
response_text = result.get("response", "")
|
||||
if not response_text:
|
||||
# Если структура другая, пытаемся извлечь из choices (как в GigaChat формате)
|
||||
choices = result.get("choices", [])
|
||||
if choices:
|
||||
response_text = choices[0].get("message", {}).get("content", "")
|
||||
|
||||
return response_text
|
||||
|
||||
|
||||
# Создаем экземпляр клиента
|
||||
ai_agent_client = AIAgentClient()
|
||||
|
||||
@@ -1,26 +1,50 @@
|
||||
import os
|
||||
|
||||
import aiohttp
|
||||
import ssl
|
||||
import base64
|
||||
import uuid
|
||||
import time
|
||||
from urllib.parse import urlencode
|
||||
from typing import Optional, List, Dict, Any
|
||||
from app.core.config import settings
|
||||
|
||||
from app.core.config import settings
|
||||
from app.services.ai_agent_client import ai_agent_client
|
||||
|
||||
class GigaChatService:
|
||||
"""
|
||||
Сервис для работы с GigaChat через внешний AI-agent сервис.
|
||||
Все запросы к GigaChat теперь проходят через внешний сервис.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.access_token: Optional[str] = None
|
||||
self.token_expires_at: Optional[float] = None
|
||||
|
||||
async def _get_token(self) -> str:
|
||||
"""Получить OAuth токен"""
|
||||
"""
|
||||
Получить OAuth токен.
|
||||
|
||||
ВНИМАНИЕ: Этот метод больше не используется, так как все запросы
|
||||
к GigaChat теперь проходят через внешний AI-agent сервис.
|
||||
Метод оставлен для возможной обратной совместимости.
|
||||
"""
|
||||
# Проверяем, не истек ли токен (оставляем запас 60 секунд)
|
||||
if self.access_token and self.token_expires_at:
|
||||
if time.time() < (self.token_expires_at - 60):
|
||||
return self.access_token
|
||||
|
||||
credentials = f"{settings.GIGACHAT_CLIENT_ID}:{settings.GIGACHAT_CLIENT_SECRET}"
|
||||
encoded_credentials = base64.b64encode(credentials.encode()).decode()
|
||||
# Проверяем наличие credentials
|
||||
client_id = os.getenv("GIGACHAT_CLIENT_ID")
|
||||
client_secret = os.getenv("GIGACHAT_CLIENT_SECRET")
|
||||
|
||||
if not client_id or not client_secret:
|
||||
raise Exception(
|
||||
"GigaChat credentials not configured. "
|
||||
"Please set GIGACHAT_CLIENT_ID and GIGACHAT_CLIENT_SECRET in .env file"
|
||||
)
|
||||
|
||||
# Формируем credentials и кодируем в Base64 с явным указанием UTF-8
|
||||
credentials = f"{client_id}:{client_secret}".strip().encode('utf-8') # Обрезаем лишние символы
|
||||
encoded_credentials = base64.b64encode(credentials).decode('utf-8')
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Basic {encoded_credentials}",
|
||||
@@ -29,31 +53,61 @@ class GigaChatService:
|
||||
"RqUID": str(uuid.uuid4())
|
||||
}
|
||||
|
||||
data = {"scope": "GIGACHAT_API_PERS"}
|
||||
# Правильно кодируем данные формы (как в рабочем примере)
|
||||
form_data = {
|
||||
"grant_type": "client_credentials",
|
||||
"scope": "GIGACHAT_API_PERS"
|
||||
}
|
||||
|
||||
# Создаем SSL контекст без проверки сертификата (только для разработки!)
|
||||
ssl_context = ssl.create_default_context()
|
||||
ssl_context.check_hostname = False
|
||||
ssl_context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
||||
# Отключаем проверку SSL (только для разработки!)
|
||||
# Используем ssl=False для полного отключения проверки сертификата
|
||||
connector = aiohttp.TCPConnector(ssl=False)
|
||||
async with aiohttp.ClientSession(connector=connector) as session:
|
||||
async with session.post(
|
||||
settings.GIGACHAT_AUTH_URL,
|
||||
os.getenv("GIGACHAT_BASE_URL"),
|
||||
headers=headers,
|
||||
data=data
|
||||
data=form_data
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Failed to get token: {response.status}")
|
||||
# Получаем детали ошибки из ответа
|
||||
try:
|
||||
error_body = await response.text()
|
||||
# Пытаемся распарсить как JSON, если не получается - возвращаем текст
|
||||
try:
|
||||
error_json = await response.json()
|
||||
error_detail = error_json.get("error_description") or error_json.get("error") or str(error_json)
|
||||
except:
|
||||
error_detail = error_body
|
||||
except:
|
||||
error_detail = "No error details available"
|
||||
|
||||
raise Exception(
|
||||
f"Failed to get token: HTTP {response.status}. "
|
||||
f"Error details: {error_detail}. "
|
||||
f"Check your GIGACHAT_CLIENT_ID and GIGACHAT_CLIENT_SECRET_2 in .env file"
|
||||
)
|
||||
|
||||
result = await response.json()
|
||||
self.access_token = result.get("access_token")
|
||||
expires_in = result.get("expires_at", 1800)
|
||||
# expires_at может быть timestamp или количество секунд
|
||||
if expires_in > 1000000000: # Это timestamp
|
||||
self.token_expires_at = expires_in
|
||||
else: # Это количество секунд
|
||||
if not self.access_token:
|
||||
raise Exception(f"Token not found in response: {result}")
|
||||
|
||||
# Обрабатываем время истечения токена (может быть expires_at или expires_in)
|
||||
expires_at = result.get("expires_at")
|
||||
expires_in = result.get("expires_in")
|
||||
|
||||
if expires_at:
|
||||
# expires_at может быть timestamp или количество секунд
|
||||
if expires_at > 1000000000: # Это timestamp
|
||||
self.token_expires_at = expires_at
|
||||
else: # Это количество секунд
|
||||
self.token_expires_at = time.time() + expires_at
|
||||
elif expires_in:
|
||||
# expires_in - это всегда количество секунд до истечения
|
||||
self.token_expires_at = time.time() + expires_in
|
||||
else:
|
||||
# По умолчанию 30 минут (1800 секунд)
|
||||
self.token_expires_at = time.time() + 1800
|
||||
|
||||
return self.access_token
|
||||
|
||||
@@ -63,52 +117,59 @@ class GigaChatService:
|
||||
context: Optional[List[Dict[str, Any]]] = None,
|
||||
model: str = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Отправить сообщение в GigaChat"""
|
||||
token = await self._get_token()
|
||||
model = model or settings.GIGACHAT_MODEL_CHAT
|
||||
|
||||
messages = context or []
|
||||
messages.append({"role": "user", "content": message})
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": messages,
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 2000
|
||||
}
|
||||
|
||||
# Создаем SSL контекст без проверки сертификата (только для разработки!)
|
||||
ssl_context = ssl.create_default_context()
|
||||
ssl_context.check_hostname = False
|
||||
ssl_context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
||||
async with aiohttp.ClientSession(connector=connector) as session:
|
||||
async with session.post(
|
||||
f"{settings.GIGACHAT_BASE_URL}/chat/completions",
|
||||
headers=headers,
|
||||
json=payload
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
raise Exception(f"GigaChat API error: {response.status} - {error_text}")
|
||||
|
||||
result = await response.json()
|
||||
"""
|
||||
Отправить сообщение в GigaChat через внешний AI-agent сервис.
|
||||
Сохраняет обратную совместимость с форматом ответа GigaChat API.
|
||||
"""
|
||||
try:
|
||||
# Используем внешний AI-agent сервис
|
||||
result = await ai_agent_client.chat(
|
||||
message=message,
|
||||
conversation_id=None, # Если нужен conversation_id, его нужно передавать отдельно
|
||||
context=context
|
||||
)
|
||||
|
||||
# Преобразуем ответ AI-agent сервиса в формат, совместимый с GigaChat API
|
||||
# Предполагаем, что ai_agent_client возвращает структуру ChatResponse или аналогичную
|
||||
if "response" in result:
|
||||
# Если ответ в формате ChatResponse, преобразуем в формат GigaChat
|
||||
return {
|
||||
"model": result.get("model", model or settings.GIGACHAT_MODEL_CHAT or "GigaChat"),
|
||||
"choices": [{
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": result["response"]
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}],
|
||||
"usage": {
|
||||
"total_tokens": result.get("tokens_used", 0),
|
||||
"prompt_tokens": 0,
|
||||
"completion_tokens": result.get("tokens_used", 0)
|
||||
}
|
||||
}
|
||||
else:
|
||||
# Если ответ уже в формате GigaChat, возвращаем как есть
|
||||
return result
|
||||
except Exception as e:
|
||||
# Если внешний сервис недоступен, пробрасываем ошибку
|
||||
raise Exception(f"AI-agent service error: {str(e)}")
|
||||
|
||||
async def generate_text(
|
||||
self,
|
||||
prompt: str,
|
||||
model: str = None
|
||||
) -> str:
|
||||
"""Генерация текста по промпту"""
|
||||
result = await self.chat(prompt, model=model)
|
||||
return result.get("choices", [{}])[0].get("message", {}).get("content", "")
|
||||
"""
|
||||
Генерация текста по промпту через внешний AI-agent сервис.
|
||||
"""
|
||||
try:
|
||||
# Используем метод generate_text из ai_agent_client
|
||||
response_text = await ai_agent_client.generate_text(prompt=prompt, model=model)
|
||||
return response_text
|
||||
except Exception as e:
|
||||
# Если произошла ошибка, пробрасываем её
|
||||
raise Exception(f"AI-agent service error: {str(e)}")
|
||||
|
||||
|
||||
gigachat_service = GigaChatService()
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import json
|
||||
from typing import List, Dict, Any, Optional
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
from app.services.gigachat_service import gigachat_service
|
||||
from app.core.config import settings
|
||||
from app.crud import schedule as crud_schedule, task as crud_task
|
||||
from app.schemas.schedule import ScheduleCreate
|
||||
from app.schemas.task import TaskCreate
|
||||
from app.models.schedule import Schedule
|
||||
from datetime import date
|
||||
|
||||
|
||||
@@ -104,11 +107,17 @@ class ScheduleGenerator:
|
||||
|
||||
await crud_task.create(db, task_create.model_dump())
|
||||
|
||||
await db.refresh(db_schedule)
|
||||
# Загружаем расписание с задачами через selectinload для async корректной работы
|
||||
result = await db.execute(
|
||||
select(Schedule)
|
||||
.where(Schedule.id == db_schedule.id)
|
||||
.options(selectinload(Schedule.tasks))
|
||||
)
|
||||
db_schedule_with_tasks = result.scalar_one()
|
||||
|
||||
return {
|
||||
"schedule_id": db_schedule.id,
|
||||
"title": db_schedule.title,
|
||||
"schedule_id": db_schedule_with_tasks.id,
|
||||
"title": db_schedule_with_tasks.title,
|
||||
"tasks": [
|
||||
{
|
||||
"title": task.title,
|
||||
@@ -117,7 +126,7 @@ class ScheduleGenerator:
|
||||
"category": task.category,
|
||||
"order": task.order
|
||||
}
|
||||
for task in db_schedule.tasks
|
||||
for task in db_schedule_with_tasks.tasks
|
||||
]
|
||||
}
|
||||
except json.JSONDecodeError as e:
|
||||
|
||||
@@ -17,6 +17,8 @@ services:
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- new-planet-network
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
@@ -30,6 +32,8 @@ services:
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- new-planet-network
|
||||
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
@@ -48,9 +52,19 @@ services:
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
networks:
|
||||
- new-planet-network
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
minio_data:
|
||||
|
||||
networks:
|
||||
new-planet-network:
|
||||
driver: bridge
|
||||
# ВАЖНО: Внешний AI-agent сервис (https://git.bro-js.ru/Glevel/New-planet-ai-agent.git)
|
||||
# должен быть запущен в этой же сети для доступа к GigaChat.
|
||||
# Убедитесь, что сервис ai-agent доступен по имени 'ai-agent' в сети new-planet-network.
|
||||
external: false
|
||||
|
||||
|
||||
@@ -1341,3 +1341,254 @@ WHERE users.id = $1::UUID]
|
||||
2025-12-18 13:49:33 - root - INFO - Shutting down...
|
||||
2025-12-18 13:49:38 - root - INFO - Starting up...
|
||||
2025-12-18 13:51:42 - root - INFO - Shutting down...
|
||||
2025-12-18 18:44:49 - root - INFO - Starting up...
|
||||
2025-12-18 19:39:26 - root - INFO - Starting up...
|
||||
2025-12-18 19:40:16 - app.middleware.error_handler - ERROR - Unhandled exception: Error 111 connecting to localhost:6379. Connection refused.
|
||||
+ Exception Group Traceback (most recent call last):
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
|
||||
| yield
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 192, in __call__
|
||||
| async with anyio.create_task_group() as task_group:
|
||||
| File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
|
||||
| raise BaseExceptionGroup(
|
||||
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
|
||||
+-+---------------- 1 ----------------
|
||||
| Traceback (most recent call last):
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
| await self.app(scope, receive, _send)
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 191, in __call__
|
||||
| with recv_stream, send_stream, collapse_excgroups():
|
||||
| File "/usr/local/lib/python3.11/contextlib.py", line 158, in __exit__
|
||||
| self.gen.throw(typ, value, traceback)
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
|
||||
| raise exc
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 193, in __call__
|
||||
| response = await self.dispatch_func(request, call_next)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/app/app/middleware/rate_limiter.py", line 20, in dispatch
|
||||
| current_requests = await cache_service.get(cache_key)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/app/app/services/cache_service.py", line 28, in get
|
||||
| return await self.redis_client.get(key)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/client.py", line 720, in execute_command
|
||||
| conn = self.connection or await pool.get_connection()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1198, in get_connection
|
||||
| await self.ensure_connection(connection)
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1231, in ensure_connection
|
||||
| await connection.connect()
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 298, in connect
|
||||
| await self.connect_check_health(check_health=True)
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 317, in connect_check_health
|
||||
| raise ConnectionError(self._error_message(e))
|
||||
| redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.
|
||||
+------------------------------------
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
await self.app(scope, receive, _send)
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 191, in __call__
|
||||
with recv_stream, send_stream, collapse_excgroups():
|
||||
File "/usr/local/lib/python3.11/contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(typ, value, traceback)
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
|
||||
raise exc
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 193, in __call__
|
||||
response = await self.dispatch_func(request, call_next)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/app/middleware/rate_limiter.py", line 20, in dispatch
|
||||
current_requests = await cache_service.get(cache_key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/app/services/cache_service.py", line 28, in get
|
||||
return await self.redis_client.get(key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/client.py", line 720, in execute_command
|
||||
conn = self.connection or await pool.get_connection()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1198, in get_connection
|
||||
await self.ensure_connection(connection)
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1231, in ensure_connection
|
||||
await connection.connect()
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 298, in connect
|
||||
await self.connect_check_health(check_health=True)
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 317, in connect_check_health
|
||||
raise ConnectionError(self._error_message(e))
|
||||
redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.
|
||||
2025-12-18 19:40:16 - app.middleware.error_handler - ERROR - Unhandled exception: Error 111 connecting to localhost:6379. Connection refused.
|
||||
+ Exception Group Traceback (most recent call last):
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
|
||||
| yield
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 192, in __call__
|
||||
| async with anyio.create_task_group() as task_group:
|
||||
| File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
|
||||
| raise BaseExceptionGroup(
|
||||
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
|
||||
+-+---------------- 1 ----------------
|
||||
| Traceback (most recent call last):
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
| await self.app(scope, receive, _send)
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 191, in __call__
|
||||
| with recv_stream, send_stream, collapse_excgroups():
|
||||
| File "/usr/local/lib/python3.11/contextlib.py", line 158, in __exit__
|
||||
| self.gen.throw(typ, value, traceback)
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
|
||||
| raise exc
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 193, in __call__
|
||||
| response = await self.dispatch_func(request, call_next)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/app/app/middleware/rate_limiter.py", line 20, in dispatch
|
||||
| current_requests = await cache_service.get(cache_key)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/app/app/services/cache_service.py", line 28, in get
|
||||
| return await self.redis_client.get(key)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/client.py", line 720, in execute_command
|
||||
| conn = self.connection or await pool.get_connection()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1198, in get_connection
|
||||
| await self.ensure_connection(connection)
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1231, in ensure_connection
|
||||
| await connection.connect()
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 298, in connect
|
||||
| await self.connect_check_health(check_health=True)
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 317, in connect_check_health
|
||||
| raise ConnectionError(self._error_message(e))
|
||||
| redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.
|
||||
+------------------------------------
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
await self.app(scope, receive, _send)
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 191, in __call__
|
||||
with recv_stream, send_stream, collapse_excgroups():
|
||||
File "/usr/local/lib/python3.11/contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(typ, value, traceback)
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
|
||||
raise exc
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 193, in __call__
|
||||
response = await self.dispatch_func(request, call_next)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/app/middleware/rate_limiter.py", line 20, in dispatch
|
||||
current_requests = await cache_service.get(cache_key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/app/services/cache_service.py", line 28, in get
|
||||
return await self.redis_client.get(key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/client.py", line 720, in execute_command
|
||||
conn = self.connection or await pool.get_connection()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1198, in get_connection
|
||||
await self.ensure_connection(connection)
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1231, in ensure_connection
|
||||
await connection.connect()
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 298, in connect
|
||||
await self.connect_check_health(check_health=True)
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 317, in connect_check_health
|
||||
raise ConnectionError(self._error_message(e))
|
||||
redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.
|
||||
2025-12-18 19:40:16 - app.middleware.error_handler - ERROR - Unhandled exception: Error 111 connecting to localhost:6379. Connection refused.
|
||||
+ Exception Group Traceback (most recent call last):
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
|
||||
| yield
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 192, in __call__
|
||||
| async with anyio.create_task_group() as task_group:
|
||||
| File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
|
||||
| raise BaseExceptionGroup(
|
||||
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
|
||||
+-+---------------- 1 ----------------
|
||||
| Traceback (most recent call last):
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
| await self.app(scope, receive, _send)
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 191, in __call__
|
||||
| with recv_stream, send_stream, collapse_excgroups():
|
||||
| File "/usr/local/lib/python3.11/contextlib.py", line 158, in __exit__
|
||||
| self.gen.throw(typ, value, traceback)
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
|
||||
| raise exc
|
||||
| File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 193, in __call__
|
||||
| response = await self.dispatch_func(request, call_next)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/app/app/middleware/rate_limiter.py", line 20, in dispatch
|
||||
| current_requests = await cache_service.get(cache_key)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/app/app/services/cache_service.py", line 28, in get
|
||||
| return await self.redis_client.get(key)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/client.py", line 720, in execute_command
|
||||
| conn = self.connection or await pool.get_connection()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1198, in get_connection
|
||||
| await self.ensure_connection(connection)
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1231, in ensure_connection
|
||||
| await connection.connect()
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 298, in connect
|
||||
| await self.connect_check_health(check_health=True)
|
||||
| File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 317, in connect_check_health
|
||||
| raise ConnectionError(self._error_message(e))
|
||||
| redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.
|
||||
+------------------------------------
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
await self.app(scope, receive, _send)
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 191, in __call__
|
||||
with recv_stream, send_stream, collapse_excgroups():
|
||||
File "/usr/local/lib/python3.11/contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(typ, value, traceback)
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
|
||||
raise exc
|
||||
File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 193, in __call__
|
||||
response = await self.dispatch_func(request, call_next)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/app/middleware/rate_limiter.py", line 20, in dispatch
|
||||
current_requests = await cache_service.get(cache_key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/app/services/cache_service.py", line 28, in get
|
||||
return await self.redis_client.get(key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/client.py", line 720, in execute_command
|
||||
conn = self.connection or await pool.get_connection()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1198, in get_connection
|
||||
await self.ensure_connection(connection)
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 1231, in ensure_connection
|
||||
await connection.connect()
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 298, in connect
|
||||
await self.connect_check_health(check_health=True)
|
||||
File "/usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py", line 317, in connect_check_health
|
||||
raise ConnectionError(self._error_message(e))
|
||||
redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.
|
||||
2025-12-18 22:55:17 - root - INFO - Starting up...
|
||||
2025-12-18 22:55:17 - root - INFO - Starting up...
|
||||
2025-12-18 22:56:56 - root - INFO - Starting up...
|
||||
2025-12-18 22:56:56 - root - INFO - Shutting down...
|
||||
2025-12-18 22:59:02 - root - INFO - Starting up...
|
||||
2025-12-18 22:59:02 - root - INFO - Shutting down...
|
||||
2025-12-18 22:59:53 - root - INFO - Starting up...
|
||||
2025-12-18 22:59:53 - root - INFO - Starting up...
|
||||
2025-12-18 22:59:53 - root - INFO - Shutting down...
|
||||
2025-12-18 22:59:53 - root - INFO - Shutting down...
|
||||
2025-12-18 20:00:09 - root - INFO - Shutting down...
|
||||
2025-12-18 23:00:15 - root - INFO - Starting up...
|
||||
2025-12-18 23:00:15 - root - INFO - Starting up...
|
||||
2025-12-18 23:00:15 - root - INFO - Shutting down...
|
||||
2025-12-18 23:00:15 - root - INFO - Shutting down...
|
||||
2025-12-23 19:00:39 - root - INFO - Starting up...
|
||||
2025-12-23 19:00:39 - root - INFO - Starting up...
|
||||
2025-12-23 19:52:34 - root - INFO - Shutting down...
|
||||
2025-12-23 19:52:34 - root - INFO - Shutting down...
|
||||
2025-12-23 19:52:58 - root - INFO - Starting up...
|
||||
2025-12-23 19:52:58 - root - INFO - Starting up...
|
||||
2025-12-23 19:54:27 - root - INFO - Shutting down...
|
||||
2025-12-23 19:54:27 - root - INFO - Shutting down...
|
||||
2025-12-23 19:54:34 - root - INFO - Starting up...
|
||||
2025-12-23 19:54:34 - root - INFO - Starting up...
|
||||
2025-12-23 20:00:23 - root - INFO - Shutting down...
|
||||
2025-12-23 20:00:23 - root - INFO - Shutting down...
|
||||
|
||||
@@ -13,3 +13,4 @@ boto3
|
||||
aiohttp
|
||||
websockets
|
||||
python-multipart
|
||||
email-validator
|
||||
Reference in New Issue
Block a user