init
This commit is contained in:
BIN
app/__pycache__/api.cpython-311.pyc
Normal file
BIN
app/__pycache__/api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/crud.cpython-311.pyc
Normal file
BIN
app/__pycache__/crud.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/database.cpython-311.pyc
Normal file
BIN
app/__pycache__/database.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/main.cpython-311.pyc
Normal file
BIN
app/__pycache__/main.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/openrouter_client.cpython-311.pyc
Normal file
BIN
app/__pycache__/openrouter_client.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/schemas.cpython-311.pyc
Normal file
BIN
app/__pycache__/schemas.cpython-311.pyc
Normal file
Binary file not shown.
101
app/api.py
Normal file
101
app/api.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# from fastapi import APIRouter, Depends
|
||||
# from sqlalchemy.ext.asyncio import AsyncSession
|
||||
# from .database import async_session
|
||||
# from . import crud, openrouter_client
|
||||
|
||||
# router = APIRouter()
|
||||
|
||||
# async def get_db():
|
||||
# async with async_session() as db:
|
||||
# yield db
|
||||
|
||||
# @router.get("/history/{telegram_id}")
|
||||
# async def get_history(telegram_id: str, db: AsyncSession = Depends(get_db)):
|
||||
# user = await crud.get_or_create_user(db, telegram_id)
|
||||
# history = await crud.get_conversation(db, user, limit=20)
|
||||
# return [{"role": m.role, "content": m.content} for m in history]
|
||||
|
||||
# @router.post("/chat/{telegram_id}")
|
||||
# async def chat(telegram_id: str, payload: dict, db: AsyncSession = Depends(get_db)):
|
||||
# user = await crud.get_or_create_user(db, telegram_id)
|
||||
# user_msg = payload.get("message", "")
|
||||
# if not user_msg:
|
||||
# return {"error": "Empty message"}
|
||||
|
||||
# await crud.add_message(db, user, "user", user_msg)
|
||||
|
||||
# history = await crud.get_conversation(db, user, limit=10)
|
||||
# messages_payload = [{"role": m.role, "content": m.content} for m in history]
|
||||
|
||||
# response = await openrouter_client.generate_response(messages_payload)
|
||||
# await crud.add_message(db, user, "assistant", response)
|
||||
|
||||
# return {"response": response}
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.future import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
from app import models, schemas
|
||||
from .database import async_session
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# --- Dependency ---
|
||||
async def get_db() -> AsyncSession:
|
||||
async with async_session() as db:
|
||||
yield db
|
||||
|
||||
|
||||
# 📌 Получить профиль пользователя (берём первого для примера)
|
||||
@router.get("/profile", response_model=schemas.UserOut)
|
||||
async def get_profile(db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(models.User).options(
|
||||
selectinload(models.User.chats).selectinload(models.Chat.messages)
|
||||
)
|
||||
)
|
||||
user = result.scalars().first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
return user
|
||||
|
||||
|
||||
# 📌 Получить список чатов
|
||||
@router.get("/chats", response_model=list[schemas.ChatOut])
|
||||
async def get_chats(db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(models.Chat).options(selectinload(models.Chat.messages))
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
# 📌 Сообщения конкретного чата
|
||||
@router.get("/chats/{chat_id}/messages", response_model=list[schemas.MessageOut])
|
||||
async def get_chat_messages(chat_id: int, db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(models.Chat)
|
||||
.options(selectinload(models.Chat.messages))
|
||||
.filter_by(id=chat_id)
|
||||
)
|
||||
chat = result.scalars().first()
|
||||
if not chat:
|
||||
raise HTTPException(status_code=404, detail="Chat not found")
|
||||
return chat.messages
|
||||
|
||||
|
||||
# 📌 Добавить новое сообщение
|
||||
@router.post("/chats/{chat_id}/messages", response_model=schemas.MessageOut)
|
||||
async def add_message(chat_id: int, msg: schemas.MessageBase, db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(models.Chat).filter_by(id=chat_id)
|
||||
)
|
||||
chat = result.scalars().first()
|
||||
if not chat:
|
||||
raise HTTPException(status_code=404, detail="Chat not found")
|
||||
|
||||
new_msg = models.Message(chat_id=chat_id, role=msg.role, content=msg.content)
|
||||
db.add(new_msg)
|
||||
await db.commit()
|
||||
await db.refresh(new_msg)
|
||||
return new_msg
|
||||
29
app/crud.py
Normal file
29
app/crud.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from sqlalchemy.future import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from . import models
|
||||
|
||||
async def get_or_create_user(db: AsyncSession, telegram_id: str):
|
||||
result = await db.execute(select(models.User).where(models.User.telegram_id == telegram_id))
|
||||
user = result.scalar_one_or_none()
|
||||
if not user:
|
||||
user = models.User(telegram_id=telegram_id)
|
||||
db.add(user)
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
return user
|
||||
|
||||
async def add_message(db: AsyncSession, user, role, content):
|
||||
msg = models.Message(user_id=user.id, role=role, content=content)
|
||||
db.add(msg)
|
||||
await db.commit()
|
||||
await db.refresh(msg)
|
||||
return msg
|
||||
|
||||
async def get_conversation(db: AsyncSession, user, limit=10):
|
||||
result = await db.execute(
|
||||
select(models.Message)
|
||||
.where(models.Message.user_id == user.id)
|
||||
.order_by(models.Message.timestamp.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
return list(reversed(result.scalars().all()))
|
||||
9
app/database.py
Normal file
9
app/database.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker, declarative_base
|
||||
import os
|
||||
|
||||
DATABASE_URL = os.getenv("DATABASE_URL")
|
||||
|
||||
engine = create_async_engine(DATABASE_URL, echo=True, future=True)
|
||||
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
Base = declarative_base()
|
||||
18
app/main.py
Normal file
18
app/main.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from fastapi import FastAPI
|
||||
from . import database, models, api
|
||||
|
||||
from app.api import router as api_router
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.include_router(api_router, prefix="/api")
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
async with database.engine.begin() as conn:
|
||||
await conn.run_sync(models.Base.metadata.create_all)
|
||||
|
||||
@app.get("/{full_path:path}")
|
||||
async def serve_spa(full_path: str):
|
||||
index_path = os.path.join(dist_path, "index.html")
|
||||
return FileResponse(index_path)
|
||||
33
app/models.py
Normal file
33
app/models.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, func
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.database import Base
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
telegram_id = Column(String, unique=True, index=True)
|
||||
username = Column(String, nullable=True)
|
||||
|
||||
chats = relationship("Chat", back_populates="owner")
|
||||
|
||||
|
||||
class Chat(Base):
|
||||
__tablename__ = "chats"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String, default="Новый чат")
|
||||
|
||||
owner_id = Column(Integer, ForeignKey("users.id"))
|
||||
owner = relationship("User", back_populates="chats")
|
||||
|
||||
messages = relationship("Message", back_populates="chat", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class Message(Base):
|
||||
__tablename__ = "messages"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
role = Column(String) # 'user' | 'assistant' | 'system'
|
||||
content = Column(Text)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
chat_id = Column(Integer, ForeignKey("chats.id"))
|
||||
chat = relationship("Chat", back_populates="messages")
|
||||
33
app/openrouter_client.py
Normal file
33
app/openrouter_client.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
|
||||
SITE_URL = os.getenv("WEBAPP_URL", "https://llm.byte-mate.xyz")
|
||||
SITE_NAME = "Telegram LLM Chat"
|
||||
|
||||
# инициализируем клиента
|
||||
client = OpenAI(
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
api_key=OPENROUTER_API_KEY,
|
||||
)
|
||||
|
||||
async def generate_response(messages):
|
||||
"""
|
||||
messages: список сообщений [{'role': 'user'|'assistant'|'system', 'content': str}]
|
||||
"""
|
||||
try:
|
||||
completion = client.chat.completions.create(
|
||||
# дополнительные заголовки для OpenRouter (не обязательно, но полезно)
|
||||
extra_headers={
|
||||
"HTTP-Referer": SITE_URL,
|
||||
"X-Title": SITE_NAME,
|
||||
},
|
||||
model="qwen/qwen3-235b-a22b:free", # ✅ используй модель, которая точно доступна твоему API‑ключу
|
||||
messages=messages,
|
||||
)
|
||||
return completion.choices[0].message.content
|
||||
|
||||
except Exception as e:
|
||||
# Логи и возврат ошибки
|
||||
print(f"❌ Ошибка вызова OpenRouter API: {e}")
|
||||
return "Извините, LLM сейчас недоступен. Попробуйте позже."
|
||||
35
app/schemas.py
Normal file
35
app/schemas.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
class MessageBase(BaseModel):
|
||||
role: str
|
||||
content: str
|
||||
|
||||
class MessageOut(MessageBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class ChatBase(BaseModel):
|
||||
title: str
|
||||
|
||||
class ChatOut(ChatBase):
|
||||
id: int
|
||||
messages: List[MessageOut] = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class UserOut(BaseModel):
|
||||
id: int
|
||||
telegram_id: str
|
||||
username: Optional[str]
|
||||
chats: List[ChatOut] = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
Reference in New Issue
Block a user