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
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.PHONY: lint

lint:
cd frontend && npx prettier --write .
cd frontend && npm run lint --fix .
cd backend && poetry run black .
cd backend && poetry run isort .
cd backend && poetry run autoflake --in-place --remove-all-unused-imports --recursive .

start-backend:
cd backend && poetry run uvicorn app.api.main:app --reload --host 0.0.0.0 --port 8080 --env-file .env

start-frontend:
cd frontend && npm run dev
Empty file added backend/app/__init__.py
Empty file.
Empty file added backend/app/api/__init__.py
Empty file.
64 changes: 64 additions & 0 deletions backend/app/api/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import logging
from contextlib import asynccontextmanager

from fastapi import FastAPI, status
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

from app.api.routes import router

logging.basicConfig(
level=logging.INFO,
format="%(name)s - %(message)s",
)
logging.getLogger("httpx").setLevel(logging.WARNING)
log = logging.getLogger(__name__)
log.addHandler(logging.StreamHandler())


@asynccontextmanager
async def lifespan(app: FastAPI):
"""Entry point lifecycle event. Runs before the server starts"""
try:
log.info("Starting up server...")
yield
except Exception as e:
log.exception("Failed to initialize Raise and Rage server: %s", e)
raise e
finally:
log.info("Shutting down server...")


def create_app() -> FastAPI:
"""Create FastAPI app with all routes."""
try:
app = FastAPI(lifespan=lifespan, debug=True)

# TODO: This is a quick fix to bypass CORS error. We need to ensure that the origin is shared in production / whitelist specific origins explicitly.
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(router)

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc: RequestValidationError):
exc_str = f"{exc}".replace("\n", " ").replace(" ", " ")
log.error(f"{request}: {exc_str}")

content = {"status_code": 10422, "message": exc_str, "data": None}
return JSONResponse(
content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY
)

return app
except Exception as e:
log.exception("Failed to create backend server: %s", e)
raise e


app = create_app()
33 changes: 33 additions & 0 deletions backend/app/api/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import logging

from fastapi import APIRouter

from app.controllers.messages import MessagesController
from app.services.messages import MessagesService

log = logging.getLogger(__name__)

router = APIRouter()

### Health check


@router.get("/status")
async def status():
log.info("Status endpoint called")
return {"status": "ok"}


### Messages


def get_messages_controller_router():
service = MessagesService()
return MessagesController(service=service).router


router.include_router(
get_messages_controller_router(),
tags=["messages"],
prefix="/api/messages",
)
Empty file.
28 changes: 28 additions & 0 deletions backend/app/controllers/messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import logging

from fastapi import APIRouter

from app.models.messages import MessageRequest, MessageResponse
from app.services.messages import MessagesService

log = logging.getLogger(__name__)


class MessagesController:
def __init__(self, service: MessagesService):
self.router = APIRouter()
self.service = service
self.setup_routes()

def setup_routes(self):
router = self.router

@router.post(
"",
response_model=MessageResponse,
)
async def chat(input: MessageRequest) -> MessageResponse:
log.info(f"Sending message of id {input.id} to assistant...")
response: MessageResponse = await self.service.chat(input=input)
log.info("Message to be sent back to user: %s", response.content)
return response
Empty file added backend/app/models/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions backend/app/models/messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Optional

from pydantic import BaseModel, Field


class MessageRequest(BaseModel):
id: Optional[str] = Field(
description="The ID of the chat history which this message belongs to. It is None if this is a new chat."
)
content: str = Field(
description="The content of the user message to be sent to the assistant."
)


class MessageResponse(BaseModel):
id: str = Field(
description="The ID of the chat history which this message belongs to. This should be passed back to the server during the next request."
)
content: str = Field(
description="The response from the assistant to be sent back to the user."
)
Empty file.
10 changes: 10 additions & 0 deletions backend/app/services/messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import logging

from app.models.messages import MessageRequest, MessageResponse

log = logging.getLogger(__name__)


class MessagesService:
async def chat(self, input: MessageRequest) -> MessageResponse:
return MessageResponse(id="123", content="Hello, world!")
5 changes: 3 additions & 2 deletions backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ authors = [
{name = "jinyang628",email = "chenjinyang4192@gmail.com"}
]
readme = "README.md"
requires-python = ">=3.13"
requires-python = ">=3.12 <4.0"
dependencies = [
"pydantic (>=2.11.5,<3.0.0)",
"fastapi (>=0.115.12,<0.116.0)",
Expand Down
41 changes: 41 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
18 changes: 18 additions & 0 deletions frontend/.prettierrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
printWidth: 100,
trailingComma: 'all',
singleQuote: true,
importOrder: [
'^(next)|(next/(.*))$',
'^(react)|(react/(.*))$',
'<THIRD_PARTY_MODULES>',
'^@/components/(.*)$',
'^@/types/(.*)$',
'^@/lib/(.*)$',
'^[./]',
],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
importOrderCaseInsensitive: false,
plugins: ['@trivago/prettier-plugin-sort-imports', 'prettier-plugin-tailwindcss'],
};
2 changes: 1 addition & 1 deletion frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ npm install
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Add [ShadCn](https://ui.shadcn.com/docs/components/accordion) components
21 changes: 21 additions & 0 deletions frontend/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
14 changes: 14 additions & 0 deletions frontend/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FlatCompat } from '@eslint/eslintrc';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});

const eslintConfig = [...compat.extends('next/core-web-vitals', 'next/typescript')];

export default eslintConfig;
7 changes: 7 additions & 0 deletions frontend/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
/* config options here */
};

export default nextConfig;
Loading