mirror of
https://github.com/Sosokker/plain-rag.git
synced 2025-12-18 06:34:03 +01:00
initial commit
This commit is contained in:
commit
9985ba430d
19
.env.example
Normal file
19
.env.example
Normal file
@ -0,0 +1,19 @@
|
||||
# API Configuration
|
||||
API_PORT=8001
|
||||
|
||||
# security
|
||||
SECRET_KEY=your-secret-key-here
|
||||
|
||||
# LLM
|
||||
OPENAI_API_KEY=your-openai-key
|
||||
|
||||
|
||||
# Database Configuration
|
||||
DB_PORT=5432
|
||||
POSTGRES_USER=user
|
||||
POSTGRES_PASSWORD=password
|
||||
POSTGRES_DB=mydatabase
|
||||
|
||||
# Environment
|
||||
ENVIRONMENT=production
|
||||
DEBUG=False
|
||||
108
.gitignore
vendored
Normal file
108
.gitignore
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Python template
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
.idea/*
|
||||
84
.trae/rules/project_rules.md
Normal file
84
.trae/rules/project_rules.md
Normal file
@ -0,0 +1,84 @@
|
||||
### 🛠️ Project Tech & Environment Rules
|
||||
|
||||
* **Python Version:** `3.12`
|
||||
* **Dependency Management:** [`uv`](https://github.com/astral-sh/uv) (fast, deterministic, PEP 582-compatible)
|
||||
* **Backend Framework:** [`FastAPI`](https://fastapi.tiangolo.com/)
|
||||
* **ORM:** [`SQLAlchemy 2.x`](https://docs.sqlalchemy.org/en/20/)
|
||||
* **Migrations:** [`Alembic`](https://alembic.sqlalchemy.org/)
|
||||
* **Authentication:** [`fastapi-users`](https://fastapi-users.github.io/fastapi-users/latest/) + [`fastapi-jwt-auth`](https://indominusbyte.github.io/fastapi-jwt-auth/)
|
||||
* **Rate Limiting:** [`fastapi-limiter`](https://github.com/long2ice/fastapi-limiter)
|
||||
* **Caching:** [`fastapi-cache`](https://github.com/long2ice/fastapi-cache)
|
||||
* **Email Service:** [`fastapi-mail`](https://github.com/sabuhish/fastapi-mail)
|
||||
* **Pagination:** [`fastapi-pagination`](https://github.com/uriyyo/fastapi-pagination)
|
||||
* **LLM Layer:** [`litellm`](https://github.com/BerriAI/litellm)
|
||||
* **Embedding Models:** [`Hugging Face Transformers`](https://huggingface.co/models)
|
||||
* **Vector Store:** `pgvector` with PostgreSQL
|
||||
|
||||
Use pure dataclasses between in-app layers (as a transport objects). Use pydantic to validate user (external) input like web APIs.
|
||||
|
||||
---
|
||||
|
||||
### 🧑💻 Python & Backend Code Quality Rules
|
||||
|
||||
#### 📦 Structure & Conventions
|
||||
|
||||
* **Follow modern `SQLAlchemy 2.0` best practices** (use `async engine`, `DeclarativeBase`, `SessionLocal()` pattern).
|
||||
* **Separate concerns clearly:**
|
||||
|
||||
* `models/`: SQLAlchemy models
|
||||
* `schemas/`: Pydantic models
|
||||
* `api/routes/`: FastAPI routers
|
||||
* `services/`: Business logic
|
||||
* `core/`: Settings, config, and utilities
|
||||
* `tests/`: Test suite
|
||||
|
||||
#### 🧹 Clean Code Principles
|
||||
|
||||
1. **Use Meaningful Names:** Functions, classes, variables, and routes should clearly communicate their intent.
|
||||
2. **Avoid Overengineering:** YAGNI (You Aren’t Gonna Need It) — keep your code minimal, testable, and readable.
|
||||
3. **Follow PEP 8 + Black Formatting:** Auto-format with `ruff`, lint with `ruff` or `flake8`.
|
||||
4. **Use Type Hints Everywhere:** Both function arguments and return types must use type annotations.
|
||||
5. **Use Docstrings:**
|
||||
|
||||
* One-liner for simple functions.
|
||||
* Full docstring for public APIs and complex logic.
|
||||
6. **Write Isolated, Testable Logic:** Favor pure functions where possible, especially in `services/`.
|
||||
7. **Handle Exceptions Gracefully:**
|
||||
|
||||
* Use `HTTPException` for expected FastAPI errors.
|
||||
* Log unexpected errors using `structlog`.
|
||||
8. **Use Dependency Injection:** Use `Depends()` for shared logic (e.g., current user, DB session, rate limiter).
|
||||
|
||||
---
|
||||
|
||||
### 🧪 Testing Rules
|
||||
|
||||
* Use `pytest` as your testing framework.
|
||||
* Coverage should include:
|
||||
|
||||
* CRUD operations
|
||||
* API endpoints
|
||||
* Embedding & RAG pipeline logic
|
||||
* Use `pytest-asyncio` for async route testing.
|
||||
* Use fixtures for test data setup.
|
||||
|
||||
---
|
||||
|
||||
### 🔒 Security Practices
|
||||
|
||||
* Never store plaintext passwords — use hashing (`argon2`, `bcrypt` via `fastapi-users`).
|
||||
* Sanitize file uploads & inputs — protect against injection.
|
||||
* Use CORS middleware correctly (`allow_credentials`, `allow_methods`, etc.).
|
||||
* Enable rate limiting on sensitive routes like login & upload.
|
||||
|
||||
---
|
||||
|
||||
### 🚀 Performance & Observability
|
||||
|
||||
* Add `structlog` structured logging to:
|
||||
|
||||
* API entry/exit points
|
||||
* Query vector lookup latency
|
||||
* LLM response times
|
||||
* Cache results where appropriate (`fastapi-cache`) — especially static vector responses.
|
||||
* Stream LLM responses via FastAPI's `StreamingResponse`.
|
||||
88
.windsurf/rules/fastapi-style-guide.md
Normal file
88
.windsurf/rules/fastapi-style-guide.md
Normal file
@ -0,0 +1,88 @@
|
||||
---
|
||||
trigger: always_on
|
||||
---
|
||||
|
||||
### 🛠️ Project Tech & Environment Rules
|
||||
|
||||
* **Python Version:** `3.12`
|
||||
* **Dependency Management:** [`uv`](https://github.com/astral-sh/uv) (fast, deterministic, PEP 582-compatible)
|
||||
* **Backend Framework:** [`FastAPI`](https://fastapi.tiangolo.com/)
|
||||
* **ORM:** [`SQLAlchemy 2.x`](https://docs.sqlalchemy.org/en/20/)
|
||||
* **Migrations:** [`Alembic`](https://alembic.sqlalchemy.org/)
|
||||
* **Authentication:** [`fastapi-users`](https://fastapi-users.github.io/fastapi-users/latest/) + [`fastapi-jwt-auth`](https://indominusbyte.github.io/fastapi-jwt-auth/)
|
||||
* **Rate Limiting:** [`fastapi-limiter`](https://github.com/long2ice/fastapi-limiter)
|
||||
* **Caching:** [`fastapi-cache`](https://github.com/long2ice/fastapi-cache)
|
||||
* **Email Service:** [`fastapi-mail`](https://github.com/sabuhish/fastapi-mail)
|
||||
* **Pagination:** [`fastapi-pagination`](https://github.com/uriyyo/fastapi-pagination)
|
||||
* **LLM Layer:** [`litellm`](https://github.com/BerriAI/litellm)
|
||||
* **Embedding Models:** [`Hugging Face Transformers`](https://huggingface.co/models)
|
||||
* **Vector Store:** `pgvector` with PostgreSQL
|
||||
|
||||
Use pure dataclasses between in-app layers (as a transport objects). Use pydantic to validate user (external) input like web APIs.
|
||||
|
||||
---
|
||||
|
||||
### 🧑💻 Python & Backend Code Quality Rules
|
||||
|
||||
#### 📦 Structure & Conventions
|
||||
|
||||
* **Follow modern `SQLAlchemy 2.0` best practices** (use `async engine`, `DeclarativeBase`, `SessionLocal()` pattern).
|
||||
* **Separate concerns clearly:**
|
||||
|
||||
* `models/`: SQLAlchemy models
|
||||
* `schemas/`: Pydantic models
|
||||
* `api/routes/`: FastAPI routers
|
||||
* `services/`: Business logic
|
||||
* `core/`: Settings, config, and utilities
|
||||
* `tests/`: Test suite
|
||||
|
||||
#### 🧹 Clean Code Principles
|
||||
|
||||
1. **Use Meaningful Names:** Functions, classes, variables, and routes should clearly communicate their intent.
|
||||
2. **Avoid Overengineering:** YAGNI (You Aren’t Gonna Need It) — keep your code minimal, testable, and readable.
|
||||
3. **Follow PEP 8 + Black Formatting:** Auto-format with `ruff`, lint with `ruff` or `flake8`.
|
||||
4. **Use Type Hints Everywhere:** Both function arguments and return types must use type annotations.
|
||||
5. **Use Docstrings:**
|
||||
|
||||
* One-liner for simple functions.
|
||||
* Full docstring for public APIs and complex logic.
|
||||
6. **Write Isolated, Testable Logic:** Favor pure functions where possible, especially in `services/`.
|
||||
7. **Handle Exceptions Gracefully:**
|
||||
|
||||
* Use `HTTPException` for expected FastAPI errors.
|
||||
* Log unexpected errors using `structlog`.
|
||||
8. **Use Dependency Injection:** Use `Depends()` for shared logic (e.g., current user, DB session, rate limiter).
|
||||
|
||||
---
|
||||
|
||||
### 🧪 Testing Rules
|
||||
|
||||
* Use `pytest` as your testing framework.
|
||||
* Coverage should include:
|
||||
|
||||
* CRUD operations
|
||||
* API endpoints
|
||||
* Embedding & RAG pipeline logic
|
||||
* Use `pytest-asyncio` for async route testing.
|
||||
* Use fixtures for test data setup.
|
||||
|
||||
---
|
||||
|
||||
### 🔒 Security Practices
|
||||
|
||||
* Never store plaintext passwords — use hashing (`argon2`, `bcrypt` via `fastapi-users`).
|
||||
* Sanitize file uploads & inputs — protect against injection.
|
||||
* Use CORS middleware correctly (`allow_credentials`, `allow_methods`, etc.).
|
||||
* Enable rate limiting on sensitive routes like login & upload.
|
||||
|
||||
---
|
||||
|
||||
### 🚀 Performance & Observability
|
||||
|
||||
* Add `structlog` structured logging to:
|
||||
|
||||
* API entry/exit points
|
||||
* Query vector lookup latency
|
||||
* LLM response times
|
||||
* Cache results where appropriate (`fastapi-cache`) — especially static vector responses.
|
||||
* Stream LLM responses via FastAPI's `StreamingResponse`.
|
||||
36
Dockerfile
Normal file
36
Dockerfile
Normal file
@ -0,0 +1,36 @@
|
||||
# Start with the official Python 3.12 slim-buster image.
|
||||
# slim-buster is a good choice for production as it's smaller.
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
|
||||
|
||||
# Install the project into `/app`
|
||||
WORKDIR /app
|
||||
|
||||
# Enable bytecode compilation
|
||||
ENV UV_COMPILE_BYTECODE=1
|
||||
|
||||
# Copy from the cache instead of linking since it's a mounted volume
|
||||
ENV UV_LINK_MODE=copy
|
||||
|
||||
# Install the project's dependencies using the lockfile and settings
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
uv sync --locked --no-install-project --no-dev
|
||||
|
||||
# Then, add the rest of the project source code and install it
|
||||
# Installing separately from its dependencies allows optimal layer caching
|
||||
COPY . /app
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
uv sync --locked --no-dev
|
||||
|
||||
# Place executables in the environment at the front of the path
|
||||
ENV PATH="/app/.venv/Lib:$PATH"
|
||||
|
||||
# Reset the entrypoint, don't invoke `uv`
|
||||
ENTRYPOINT []
|
||||
|
||||
# Command to run the application
|
||||
# Use uvicorn to run the FastAPI application.
|
||||
# --host 0.0.0.0 is required to make the app accessible from outside the container
|
||||
# --port 8000 to match the exposed port
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
0
app/api/__init__.py
Normal file
0
app/api/__init__.py
Normal file
0
app/components/__init__.py
Normal file
0
app/components/__init__.py
Normal file
0
app/core/__init__.py
Normal file
0
app/core/__init__.py
Normal file
99
app/core/config.py
Normal file
99
app/core/config.py
Normal file
@ -0,0 +1,99 @@
|
||||
import os
|
||||
import secrets
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import AnyHttpUrl, PostgresDsn, field_validator, model_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
ENV_FILE = ROOT / ".env"
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# Project
|
||||
PROJECT_NAME: str = "Chat Hub"
|
||||
VERSION: str = "0.1.0"
|
||||
API_V1_STR: str = "/api/v1"
|
||||
SECRET_KEY: str = secrets.token_urlsafe(32)
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 # 8 days
|
||||
|
||||
# Security
|
||||
ALGORITHM: str = "HS256"
|
||||
|
||||
# Backend Server
|
||||
BACKEND_CORS_ORIGINS: list[str | AnyHttpUrl] = []
|
||||
|
||||
@field_validator("BACKEND_CORS_ORIGINS", mode="before")
|
||||
def assemble_cors_origins(self, v: str | list[str]) -> list[str] | str:
|
||||
if isinstance(v, str) and not v.startswith("["):
|
||||
return [i.strip() for i in v.split(",")]
|
||||
if isinstance(v, (list, str)):
|
||||
return v
|
||||
raise ValueError(v)
|
||||
|
||||
# Database
|
||||
POSTGRES_SERVER: str = "localhost"
|
||||
POSTGRES_USER: str = "postgres"
|
||||
POSTGRES_PASSWORD: str = ""
|
||||
POSTGRES_DB: str = "chat_hub"
|
||||
POSTGRES_PORT: str = "5432"
|
||||
DATABASE_URI: PostgresDsn | None = None
|
||||
|
||||
@model_validator(mode="after")
|
||||
def assemble_db_connection(self) -> "Settings":
|
||||
if self.DATABASE_URI is None:
|
||||
self.DATABASE_URI = PostgresDsn.build(
|
||||
scheme="postgresql+asyncpg",
|
||||
username=self.POSTGRES_USER,
|
||||
password=self.POSTGRES_PASSWORD,
|
||||
host=self.POSTGRES_SERVER,
|
||||
port=int(self.POSTGRES_PORT),
|
||||
path=f"/{self.POSTGRES_DB or ''}",
|
||||
)
|
||||
return self
|
||||
|
||||
# LLM Configuration
|
||||
OPENAI_API_KEY: str | None = None
|
||||
ANTHROPIC_API_KEY: str | None = None
|
||||
|
||||
# Vector Store
|
||||
VECTOR_STORE_TYPE: str = "pgvector" # or "chroma", "faiss", etc.
|
||||
EMBEDDING_MODEL: str = "sentence-transformers/all-mpnet-base-v2"
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL: str = "INFO"
|
||||
|
||||
# Environment
|
||||
ENVIRONMENT: str = "development"
|
||||
DEBUG: bool = False
|
||||
TESTING: bool = False
|
||||
|
||||
# Rate Limiting
|
||||
RATE_LIMIT: str = "100/minute"
|
||||
|
||||
# Caching
|
||||
CACHE_TTL: int = 300 # 5 minutes
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=ENV_FILE,
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=True,
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_settings() -> Settings:
|
||||
"""
|
||||
Get cached settings instance.
|
||||
This function uses lru_cache to prevent re-reading the environment on each call.
|
||||
"""
|
||||
return Settings()
|
||||
|
||||
|
||||
# Create settings instance
|
||||
settings = get_settings()
|
||||
|
||||
# Set environment variables for third-party libraries
|
||||
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
||||
0
app/core/enums.py
Normal file
0
app/core/enums.py
Normal file
0
app/core/interfaces.py
Normal file
0
app/core/interfaces.py
Normal file
83
app/main.py
Normal file
83
app/main.py
Normal file
@ -0,0 +1,83 @@
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Application metadata
|
||||
APP_NAME = "Chat Hub API"
|
||||
API_VERSION = "0.1.0"
|
||||
APP_DESCRIPTION = """
|
||||
Chat Hub API - A modern chat application with AI capabilities.
|
||||
"""
|
||||
|
||||
|
||||
# Application lifespan
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Startup: Initialize resources
|
||||
print("Starting application...")
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown: Clean up resources
|
||||
print("Shutting down application...")
|
||||
|
||||
|
||||
# Initialize FastAPI application
|
||||
app = FastAPI(
|
||||
title=APP_NAME,
|
||||
description=APP_DESCRIPTION,
|
||||
version=API_VERSION,
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc",
|
||||
openapi_url="/openapi.json",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# Configure CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # In production, replace with specific origins
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# Health check endpoint
|
||||
@app.get("/health", response_model=dict[str, Any])
|
||||
async def health_check() -> dict[str, Any]:
|
||||
"""
|
||||
Health check endpoint to verify the API is running.
|
||||
"""
|
||||
return {"status": "healthy", "service": APP_NAME, "version": API_VERSION}
|
||||
|
||||
|
||||
# Root endpoint
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""
|
||||
Root endpoint that provides basic information about the API.
|
||||
"""
|
||||
return {
|
||||
"message": f"Welcome to {APP_NAME}",
|
||||
"version": API_VERSION,
|
||||
"docs": "/docs",
|
||||
"health_check": "/health",
|
||||
}
|
||||
|
||||
|
||||
# This allows running the app with: uvicorn main:app --reload
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
port = int(os.getenv("API_PORT", 8001))
|
||||
uvicorn.run(
|
||||
"main:app", host="0.0.0.0", port=port, reload=True, reload_dirs=["./app"]
|
||||
)
|
||||
0
app/pipelines/__init__.py
Normal file
0
app/pipelines/__init__.py
Normal file
32
docker-compose.yml
Normal file
32
docker-compose.yml
Normal file
@ -0,0 +1,32 @@
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "${API_PORT:-8001}:8000"
|
||||
volumes:
|
||||
- ./app:/app
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: pgvector/pgvector:pg17
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER:-user}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-mydatabase}
|
||||
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "${DB_PORT:-5432}:5432"
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
14
pyproject.toml
Normal file
14
pyproject.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[project]
|
||||
name = "chat-hub"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"fastapi[standard]>=0.115.13",
|
||||
"litellm>=1.73.0",
|
||||
"pydantic-settings>=2.10.0",
|
||||
"structlog>=25.4.0",
|
||||
"transformers>=4.52.4",
|
||||
"uvicorn>=0.34.3",
|
||||
]
|
||||
Loading…
Reference in New Issue
Block a user