在2024年使用FastAPI创建LLM动力API(第3部分)

FastAPI + OpenAI

Sure, here is the text translated into simplified Chinese while keeping the HTML structure: ```html 第三部分:用户功能、认证和安全性 ```

Certainly! Here's the translated text in simplified Chinese, keeping the HTML structure: ```html 欢迎来到我们关于构建LLM驱动API系列的最后一部分。在之前的文章中,我们设置了项目的结构,建模了数据库,并创建了API端点。现在,是时候专注于用户功能、认证和安全性了。 ``` This translation maintains the original structure for easy integration into HTML content.

Sure, here is the translation of "Below is an index of the posts in this series:" in simplified Chinese while keeping the HTML structure intact: ```html 以下是本系列文章的索引: ```

  • Sure, here is the text translated into simplified Chinese while keeping the HTML structure intact: ```html

    第一部分。介绍、问题定义和设置

    ```
  • Sure, here's the translation in simplified Chinese while keeping the HTML structure: ```html Part 2: 结构,数据库建模,API端点 ```
  • 在HTML结构中保持不变,将以下英文文本翻译为简体中文: 第三部分:用户功能、身份验证和安全性(您在此处)

在这篇文章中,我们将涵盖以下内容:

  • Sure, here's the translation of "Setting up user authentication and security functions" in simplified Chinese while maintaining HTML structure: ```html 设置用户认证和安全功能。 ```
  • 在HTML结构中保持不变,将以下英文文本翻译为简体中文: 创建用户注册和登录路由。
  • 在HTML结构中,将以下英文文本翻译为简体中文: 确保端点安全,只有经过身份验证的用户才能访问它们。
  • 在 HTML 结构中,将以下英文文本翻译为简体中文: 实现用于记录和审阅生成的电子邮件的端点。

Sure, here's the translation in simplified Chinese, while keeping the HTML structure intact: ```html 逐步教程 ```

Sure, here is the translation of "Users Functionality" in simplified Chinese, while maintaining HTML structure: ```html 用户功能 ``` This HTML snippet ensures the text "用户功能" is displayed in simplified Chinese on a webpage, respecting the language attribute (`lang="zh-CN"`).

Sure, here is the text translated to simplified Chinese while maintaining HTML structure: ```html 现在让我们添加实际的用户创建功能。首先在您的终端上运行: ``` This HTML snippet retains the original formatting and incorporates the translated text in simplified Chinese.

openssl rand -hex 32

To translate "Then write the result to your .env as SECRET_KEY" into simplified Chinese while keeping the HTML structure, you can use the following: ```html 然后将结果写入你的 .env 文件作为 SECRET_KEY ``` This maintains the HTML structure around the translated Chinese text.

# app/.env

OPENAI_API_KEY="my_openai_api_key"
SECRET_KEY="my_secret_key"

To translate the provided English text into simplified Chinese while keeping the HTML structure, you can use the following: ```html 这是我们将用来编码访问令牌的密钥。由于创建实用的安全功能超出了本教程的范围,我将简要地概述一下。 ``` This HTML structure preserves the text content while allowing it to be displayed properly in a web context.

在创建一个名为 helper.py 的文件,并将以下代码粘贴到其中:

# app/helper.py

import os
from datetime import UTC, datetime, timedelta
from typing import Any, Annotated

import bcrypt
from jose import JWTError, jwt
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import SQLModel
from starlette.config import Config

from .database import get_session
from .crud import crud_users

current_file_dir = os.path.dirname(os.path.realpath(__file__))
env_path = os.path.join(current_file_dir, ".env")
config = Config(env_path)


# Security settings
SECRET_KEY = config("SECRET_KEY")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/login")


# Token models
class Token(SQLModel):
access_token: str
token_type: str
class TokenData(SQLModel):
username_or_email: str


# Utility functions
async def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify a plain password against a hashed password."""
return bcrypt.checkpw(plain_password.encode(), hashed_password.encode())

def get_password_hash(password: str) -> str:
"""Hash a password."""
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()

async def create_access_token(
data: dict[str, Any],
expires_delta: timedelta | None = None
) -> str:
"""Create a JWT access token."""
to_encode = data.copy()
if expires_delta:
expire = datetime.now(UTC).replace(tzinfo=None) + expires_delta
else:
expire = datetime.now(UTC).replace(tzinfo=None) + timedelta(minutes=15)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm="HS256")

async def verify_token(token: str, db: AsyncSession) -> TokenData | None:
"""Verify a JWT token and extract the user data."""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
username_or_email: str = payload.get("sub")
if username_or_email is None:
return None
return TokenData(username_or_email=username_or_email)
except JWTError:
return None

async def authenticate_user(username_or_email: str, password: str, db: AsyncSession):
if "@" in username_or_email:
db_user: dict | None = await crud_users.get(db=db, email=username_or_email, is_deleted=False)
else:
db_user = await crud_users.get(db=db, username=username_or_email, is_deleted=False)
if not db_user:
return False
elif not await verify_password(password, db_user["hashed_password"]):
return False
return db_user


# Dependency
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[AsyncSession, Depends(get_session)]
) -> dict[str, Any] | None:
"""Get the current authenticated user."""
token_data = await verify_token(token, db)
if token_data is None:
raise HTTPException(status_code=401, detail="User not authenticated.")

if "@" in token_data.username_or_email:
user = await crud_users.get(
db=db, email=token_data.username_or_email, is_deleted=False
)
else:
user = await crud_users.get(
db=db, username=token_data.username_or_email, is_deleted=False
)

if user:
return user
raise HTTPException(status_code=401, detail="User not authenticated.")

To translate the text "Relevant parts:" to simplified Chinese while keeping the HTML structure intact, you can use the following: ```html 相关部分: ``` This HTML snippet ensures that the text "相关部分:" is displayed in simplified Chinese, maintaining compatibility with HTML standards.

  • Certainly! Here's the translated text in simplified Chinese, while keeping the HTML structure: ```html

    verify_password 用于验证明文密码与哈希密码是否匹配。它用于检查用户提供的密码是否与存储的哈希密码匹配。

    ``` This HTML snippet preserves the structure while presenting the translated text in simplified Chinese.
  • 在将其存储到数据库之前,get_password_hash会对用户提供的密码进行哈希处理。
  • Sure, here's how you could structure the HTML to display the translated text in simplified Chinese: ```html Translation Example

    create_access_token is used to generate a token of the type JWT for authenticated users.

    create_access_token 用于为已认证用户生成 JWT 类型的令牌。

    ``` In the above HTML: - The original English text is included in the first `

    ` tag. - The translated Chinese text is included in the second `

    ` tag, following the original English text. Make sure the `charset` meta tag is set to UTF-8 to properly display Chinese characters. This HTML structure preserves the original English text while displaying its Chinese translation.

  • To translate "verify_token verifies a JWT token and extracts the user data." to simplified Chinese while keeping the HTML structure intact, you can use the following: ```html
    verify_token verifies a JWT token and extracts the user data.
    ``` Translation to simplified Chinese: ```html
    verify_token 验证 JWT 令牌并提取用户数据。
    ``` This HTML structure maintains the blockquote element and specifies the language for translation purposes.
  • To translate "authenticate_user is responsible for authenticating a user based on their username or email and password" into simplified Chinese while keeping the HTML structure intact, you can use the following: ```html

    authenticate_user 负责根据用户的用户名或电子邮件和密码进行身份验证。

    ``` This HTML snippet includes the translated text wrapped in `

    ` tags for paragraph formatting.

  • To translate the given English text "get_current_user is a dependency that retrieves the currently authenticated user based on the provided token" into simplified Chinese while keeping HTML structure, you can use the following: ```html get_current_user 是一个依赖项,根据提供的令牌检索当前认证的用户。 ``` This HTML snippet ensures that the translated Chinese text is properly displayed and maintains the structure of the HTML document.

Sure, here is the translation of the English text to simplified Chinese while keeping the HTML structure: ```html 现在让我们使用这些实用函数来创建用户路由。 ```

# app/routes.py

from datetime import timedelta
from fastapi import APIRouter, Depends, HTTPException

from .database import get_session
from .schemas import UserCreate, UserRead
from .helper import (
get_password_hash,
authenticate_user,
create_access_token,
get_current_user,
Token
)

# ------- user -------
user_router = APIRouter()

@user_router.post("/register", response_model=UserRead)
async def register_user(
user: UserCreate,
db: AsyncSession = Depends(get_session)
):
hashed_password = get_password_hash(user.password)
user_data = user.dict()
user_data["hashed_password"] = hashed_password
del user_data["password"]

new_user = await crud_users.create(
db,
object=UserCreateInternal(**user_data)
)
return new_user

@user_router.post("/login", response_model=Token)
async def login_user(user: UserCreate, db: AsyncSession = Depends(get_session)):
db_user = await crud_users.get(db, email=user.email)
password_verified = await verify_password(
user.password, db_user.hashed_password
)
if not db_user or not password_verified:
raise HTTPException(status_code=400, detail="Invalid credentials")

access_token_expires = timedelta(minutes=30)
access_token = await create_access_token(
data={"sub": user["username"]},
expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}

在我们的应用程序中包含路由器。

# app/main.py

from fastapi import FastAPI

from .database import create_db_and_tables
from .routes import user_router, email_router, log_router

async def lifespan(app):
await create_db_and_tables()
yield

app = FastAPI(lifespan=lifespan)

app.include_router(user_router, prefix="/users", tags=["Users"])
app.include_router(email_router, prefix="/generate", tags=["Email"])
app.include_router(log_router, prefix="/logs", tags=["Logs"])

Sure, here's the translated text in simplified Chinese while keeping the HTML structure intact: ```html 而且最后让我们在 generate_email 终端注入 get_current_user 依赖项,添加需要用户登录才能生成电子邮件的需求,并自动将用户的ID存储在日志中: ``` This translation maintains the technical context and structure suitable for embedding in HTML.

# app/routes.py

...

@email_router.post("/", response_model=EmailResponse)
async def generate_email(
request: EmailRequest,
db: AsyncSession = Depends(get_session),
current_user: dict = Depends(get_current_user)
):
try:
prompt = f"""
Write an email based on the following input:
- User Input: {request.user_input}
- Reply To: {request.reply_to if request.reply_to else 'N/A'}
- Context: {request.context if request.context else 'N/A'}
- Length: {request.length if request.length else 'N/A'} characters
- Tone: {request.tone if request.tone else 'N/A'}
"""

response = open_ai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful email assistant."},
{"role": "user", "content": prompt}
],
max_tokens=request.length
)
generated_email = response.choices[0].message.content

log_entry = EmailLogCreate(
user_id=current_user['id'],
user_input=request.user_input,
reply_to=request.reply_to,
context=request.context,
length=request.length,
tone=request.tone,
generated_email=generated_email,
)
await crud_email_logs.create(db, log_entry)

return EmailResponse(generated_email=generated_email)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

Sure, here's the translated text in simplified Chinese: ```html 如果您现在检查端点,您会看到右边有一个小锁。 ``` This text keeps the HTML structure intact while providing the translation in simplified Chinese.

Certainly! Here's the simplified Chinese translation of the provided text, keeping the HTML structure intact: ```html

为了运行它,您现在需要进行身份验证,方法是点击锁图标并输入有效的用户名和密码(您创建的用户的用户名和密码),位置在这里:

``` In this HTML snippet, the translated Chinese text is wrapped in a `

` (paragraph) tag to maintain the structure as requested.

Sure, here is the HTML structure with the translated simplified Chinese text: ```html

现在让我们还将这个依赖设置到日志端点,同时,让我们使用FastCRUD仅筛选当前用户ID的日志。

```

Sure, here's the translated text in simplified Chinese, keeping the HTML structure intact: 我们可以通过注入 get_current_user 依赖项并将 user_id=current_user[“id”] 传递给 FastCRUD 来实现这一点(当前用户是 get_current_user 返回的内容)。为了更好地理解,请查看 FastCRUD 的文档。

...

# ------- email log -------
log_router = APIRouter()

@log_router.get("/")
async def read_logs(
db: AsyncSession = Depends(get_session),
current_user: dict[str, Any] = Depends(get_current_user)
):
logs = await crud_email_logs.get_multi(db, user_id=current_user["id"])
return logs

@log_router.get("/{log_id}", response_model=EmailLogRead)
async def read_log(
log_id: int,
db: AsyncSession = Depends(get_session),
current_user: dict[str, Any] = Depends(get_current_user)
):
log = await crud_email_logs.get(db, id=log_id, user_id=current_user["id"])
if not log:
raise HTTPException(status_code=404, detail="Log not found")
return log

在保留HTML结构的情况下,将以下英文文本翻译为简体中文: 现在我们只能阅读我们自己的日志,并且只有在登录状态下才能阅读。

这里是最终的路由文件:

# app/routes.py

import os
from typing import Annotated, Any
from datetime import timedelta

from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio.session import AsyncSession
from starlette.config import Config
from openai import OpenAI

from .crud import crud_email_logs, crud_users
from .database import get_session
from .schemas import (
EmailRequest,
EmailResponse,
EmailLogCreate,
EmailLogRead,
UserCreate,
UserRead,
UserCreateInternal,
)
from .helper import (
get_password_hash,
authenticate_user,
create_access_token,
get_current_user,
Token
)

current_file_dir = os.path.dirname(os.path.realpath(__file__))
env_path = os.path.join(current_file_dir, ".env")
config = Config(env_path)

OPENAI_API_KEY = config("OPENAI_API_KEY")

open_ai_client = OpenAI(api_key=OPENAI_API_KEY)


# ------- user -------
user_router = APIRouter()

@user_router.post("/register", response_model=UserRead)
async def register_user(
user: UserCreate,
db: AsyncSession = Depends(get_session)
):
hashed_password = get_password_hash(user.password)
user_data = user.dict()
user_data["hashed_password"] = hashed_password
del user_data["password"]

new_user = await crud_users.create(
db,
object=UserCreateInternal(**user_data)
)
return new_user

@user_router.post("/login", response_model=Token)
async def login_user(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
db: AsyncSession = Depends(get_session)
):
user = await authenticate_user(
username_or_email=form_data.username,
password=form_data.password,
db=db
)
if not user:
raise HTTPException(status_code=400, detail="Invalid credentials")

access_token_expires = timedelta(minutes=30)
access_token = await create_access_token(
data={"sub": user["username"]},
expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}


# ------- email -------
email_router = APIRouter()

@email_router.post("/", response_model=EmailResponse)
async def generate_email(
request: EmailRequest,
db: AsyncSession = Depends(get_session),
current_user: dict = Depends(get_current_user)
):
try:
system_prompt = f"""
You are a helpful email assistant.
You get a prompt to write an email,
you reply with the email and nothing else.
"""
prompt = f"""
Write an email based on the following input:
- User Input: {request.user_input}
- Reply To: {request.reply_to if request.reply_to else 'N/A'}
- Context: {request.context if request.context else 'N/A'}
- Length: {request.length if request.length else 'N/A'} characters
- Tone: {request.tone if request.tone else 'N/A'}
"""

response = open_ai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
],
max_tokens=request.length
)
generated_email = response.choices[0].message.content
log_entry = EmailLogCreate(
user_id=current_user['id'],
user_input=request.user_input,
reply_to=request.reply_to,
context=request.context,
length=request.length,
tone=request.tone,
generated_email=generated_email,
)
await crud_email_logs.create(db, log_entry)
return EmailResponse(generated_email=generated_email)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))


# ------- email log -------
log_router = APIRouter()

@log_router.get("/")
async def read_logs(
db: AsyncSession = Depends(get_session),
current_user: dict[str, Any] = Depends(get_current_user)
):
logs = await crud_email_logs.get_multi(db, user_id=current_user["id"])
return logs

@log_router.get("/{log_id}", response_model=EmailLogRead)
async def read_log(
log_id: int,
db: AsyncSession = Depends(get_session),
current_user: dict[str, Any] = Depends(get_current_user)
):
log = await crud_email_logs.get(db, id=log_id, user_id=current_user["id"])
if not log:
raise HTTPException(status_code=404, detail="Log not found")
return log

To translate "Wrapping Up" into simplified Chinese while keeping the HTML structure, you would use the following: ```html 总结 ``` Here's a breakdown of the HTML structure: - ``: Used to inline style or mark up text without breaking the flow of the paragraph. - `lang="zh"`: Specifies the language of the enclosed text as Chinese. - `总结`: This is the simplified Chinese translation for "Wrapping Up".

Sure, here is the HTML structure with the simplified Chinese translation: ```html

2024年,使用FastAPI和FastCRUD创建基于LLM的API比以往任何时候都更加容易。在本教程中,我们演示了如何构建个性化的电子邮件写作助手,涵盖了项目设置、数据库建模、模式创建以及核心功能实现。要查看结果,请访问这里的Github存储库。

``` In this translation: - "2024年" means "2024". - "使用FastAPI和FastCRUD创建基于LLM的API" means "Creating LLM Powered APIs with FastAPI and FastCRUD". - "比以往任何时候都更加容易" means "has become more accessible than ever". - "在本教程中" means "In this tutorial". - "我们演示了如何构建个性化的电子邮件写作助手" means "we walked through building a personalized email writing assistant". - "涵盖了项目设置、数据库建模、模式创建以及核心功能实现" means "covering project setup, database modeling, schema creation, and core functionality implementation". - "要查看结果,请访问这里的Github存储库" means "To see the result, go to the Github repository here".

这个教程只是一个起点。请随意通过增加更多功能、改进用户体验或与其他服务集成来扩展项目。利用这些基础,创建解决真实世界问题、提升生产力或构建下一个重大成就的AI应用程序。

Sure, the translation of "Happy coding!" in simplified Chinese while keeping HTML structure intact would be: ```html

快乐编码!

```

To translate "Connect with Me" into simplified Chinese while keeping the HTML structure, you would write: ```html 连接我 ``` This HTML code preserves the structure and simply replaces the English text with its simplified Chinese equivalent.

在保持HTML结构的情况下,将以下英文文本翻译为简体中文: 如果您有任何问题,想讨论技术相关话题,或者分享您的反馈意见,请随时通过社交媒体联系我:

  • To translate "GitHub: igorbenav" to simplified Chinese while keeping HTML structure, you would use the following: ```html GitHub: igorbenav ``` This structure ensures that the text "igorbenav" is marked as simplified Chinese within the HTML, allowing browsers and other tools to handle the language appropriately.
  • To translate "X/Twitter: igorbenav" into simplified Chinese while keeping the HTML structure intact, you would write: ```html X/Twitter: igorbenav ``` In simplified Chinese characters, it looks like this: ```html X/Twitter:igorbenav ``` Make sure to use UTF-8 encoding for the HTML document to display Chinese characters correctly.
  • LinkedIn: 伊戈

2024-06-25 05:36:15 AI中文站翻译自原文