Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# PostgreSQL
POSTGRES_DB=stackup
POSTGRES_USER=stackup
POSTGRES_PASSWORD=stackup
POSTGRES_PORT=5432

# RabbitMQ
RABBITMQ_USER=stackup
RABBITMQ_PASSWORD=stackup
RABBITMQ_PORT=5672
RABBITMQ_MANAGEMENT_PORT=15672

# MinIO (S3)
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin
MINIO_API_PORT=9000
MINIO_CONSOLE_PORT=9001
MINIO_BUCKET=stackup
14 changes: 7 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
python-version: '3.13'

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Setup Java
uses: actions/setup-java@v4
Expand Down Expand Up @@ -79,14 +81,12 @@ jobs:
# AI (Python)
- name: Install AI dependencies
working-directory: ./ai
run: |
pip install -r requirements.txt
pip install flake8 black pylint
run: uv sync --extra dev

- name: Lint AI (flake8)
working-directory: ./ai
run: flake8 . --max-line-length=120 --exclude=venv,__pycache__
run: uv run flake8 .

- name: Check AI formatting (black)
working-directory: ./ai
run: black --check .
run: uv run black --check .
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.vscode
.claude
680 changes: 680 additions & 0 deletions CLAUDE.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions ai/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__pycache__
*.pyc
.env
.venv/
venv/
.git/
tests/
*.md
15 changes: 15 additions & 0 deletions ai/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Application
DEBUG=false

# RabbitMQ
RABBITMQ_URL=amqp://stackup:stackup@localhost:5672/

# S3 / MinIO
S3_ENDPOINT_URL=http://localhost:9000
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_BUCKET_NAME=stackup

# LLM API Keys
OPENAI_API_KEY=
GOOGLE_API_KEY=
4 changes: 4 additions & 0 deletions ai/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
max-line-length = 500
exclude = venv,.venv,__pycache__
extend-ignore = E203,W503
19 changes: 19 additions & 0 deletions ai/.gitignore

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gitignore는 루트에 하나만 두는것도 괜찮아보입니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Virtual environment
.venv/
venv/

# Python
__pycache__/
*.pyc
*.pyo

# Environment
.env

# pytest
.pytest_cache/

# IDE
.idea/
.vscode/
*.swp
1 change: 1 addition & 0 deletions ai/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
14 changes: 14 additions & 0 deletions ai/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM python:3.13-slim

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app

COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev

COPY src/ src/

EXPOSE 8000

CMD ["uv", "run", "uvicorn", "ai_server.main:app", "--host", "0.0.0.0", "--port", "8000"]
44 changes: 44 additions & 0 deletions ai/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "stackup-ai-server"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
"fastapi>=0.135.2",
"uvicorn[standard]>=0.42.0",
"pydantic>=2.12.5",
"pydantic-settings>=2.13.1",
"httpx>=0.28.1",
"aio-pika>=9.6.2",
"boto3>=1.42.77",
"langchain>=1.2.13",
"langchain-core>=1.2.22",
"langchain-community>=0.4.1",
"structlog>=25.5.0",
]

[project.optional-dependencies]
dev = [
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"black>=26.3.1",
"flake8>=7.3.0",
"pylint>=4.0.5",
]

[tool.hatch.build.targets.wheel]
packages = ["src/ai_server"]

[tool.black]
line-length = 88
target-version = ["py313"]

[tool.pylint.messages_control]
disable = ["C0114", "C0115", "C0116"]

[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
File renamed without changes.
File renamed without changes.
8 changes: 8 additions & 0 deletions ai/src/ai_server/api/health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from fastapi import APIRouter

router = APIRouter(tags=["health"])


@router.get("/health")
async def health_check() -> dict[str, str]:
return {"status": "ok"}
Empty file.
31 changes: 31 additions & 0 deletions ai/src/ai_server/config/settings.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env 파일은 디코나 노션으로 관리하면 좋겠습니다

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
)

# Application
app_name: str = "stackup-ai-server"
app_version: str = "0.1.0"
debug: bool = False

# RabbitMQ
rabbitmq_url: str = "amqp://stackup:stackup@localhost:5672/"

# S3 / MinIO
s3_endpoint_url: str = "http://localhost:9000"
s3_access_key: str = ""
s3_secret_key: str = ""
s3_bucket_name: str = "stackup"

# LLM API Keys
openai_api_key: str = ""
google_api_key: str = ""


def get_settings() -> Settings:
return Settings()
32 changes: 32 additions & 0 deletions ai/src/ai_server/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

from fastapi import FastAPI

from ai_server.api.health import router as health_router
from ai_server.config.settings import Settings, get_settings


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
# Startup
yield
# Shutdown


def create_app(settings: Settings | None = None) -> FastAPI:
if settings is None:
settings = get_settings()

app = FastAPI(
title=settings.app_name,
version=settings.app_version,
lifespan=lifespan,
)

app.include_router(health_router)

return app


app = create_app()
Empty file added ai/tests/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions ai/tests/test_health.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트까지~ 백단 중요로직에 대해서는 테스트도 같이 작성해봅시다

@Jaeho-Site Jaeho-Site Mar 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 인정. 테스트= 명세

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pytest
from httpx import ASGITransport, AsyncClient

from ai_server.main import create_app


@pytest.fixture
def app():
return create_app()


@pytest.mark.asyncio
async def test_health_check(app):
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
Loading
Loading