Skip to content

Commit 88ccd1d

Browse files
committed
Add Gemini API and tests; update routes & docs
Introduce a new /gemini endpoint (app/api/gemini) and accompanying test (tests/test_gemini.py); register the gemini router in app/api/routes.py and expose it in the root metadata. Remove inclusion of several prospects DB utility routers from the main routes. Update README formatting and project overview content, change render.yaml repo to goldlabelapps/python, and relax/adjust assertions in tests/test_prospects.py (including updated meta title expectation). Also minor tweak to root title in app/api/root.py.
1 parent 7b32117 commit 88ccd1d

8 files changed

Lines changed: 52 additions & 43 deletions

File tree

README.md

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
## Python FastAPI/Postgres App
22

3-
**Production-ready, open-source FastAPI application with PostgreSQL and blazing-fast full-text search.**
3+
> Production-ready, open-source FastAPI application with PostgreSQL and blazing-fast full-text search.
44
5-
---
5+
#### Project Overview
66

7-
### 🚀 Features
7+
This project provides a scalable API backend using FastAPI and PostgreSQL, featuring:
8+
9+
- Automatic full-text search on all text fields (via tsvector)
10+
- Endpoints for health checks, product management, prompt handling, and prospect management
11+
- Efficient ingestion and processing of large CSV files
12+
13+
#### 🚀 Features
814

915
- **Python 3.11+**
1016
- **FastAPI** — Modern, high-performance REST API
@@ -13,19 +19,7 @@
1319
- **Uvicorn** — Lightning-fast ASGI server
1420
- **Pytest** — Comprehensive testing
1521

16-
---
17-
18-
## Project Overview
19-
20-
This project provides a scalable API backend using FastAPI and PostgreSQL, featuring:
21-
22-
- Automatic full-text search on all text fields (via tsvector)
23-
- Endpoints for health checks, product management, prompt handling, and prospect management
24-
- Efficient ingestion and processing of large CSV files
25-
26-
---
27-
28-
## Getting Started
22+
#### Getting Started
2923

3024
### 1. Clone & Setup Environment
3125

@@ -38,24 +32,21 @@ source venv/bin/activate
3832
pip install -r requirements.txt
3933
```
4034

41-
### 2. Run the App
35+
#### 2. Run the App
4236

4337
```bash
4438
uvicorn app.main:app --reload
4539
```
4640

4741
Visit [localhost:8000](http://localhost:8000) or [onrender](https://nx-ai.onrender.com)
4842

49-
---
50-
51-
## API Documentation
43+
#### API Documentation
5244

5345
FastAPI auto-generates interactive docs:
5446

5547
- [Swagger UI](https://nx-ai.onrender.com/docs)
5648
- [ReDoc](https://nx-ai.onrender.com/redoc)
5749

58-
---
5950

6051
## Full-Text Search (tsvector)
6152

@@ -69,13 +60,11 @@ SELECT * FROM prospects WHERE search_vector @@ plainto_tsquery('english', 'searc
6960
- On every insert/update, `search_vector` is computed using PostgreSQL's `to_tsvector('english', ...)`.
7061
- The GIN index (`idx_prospects_search_vector`) enables efficient search across large datasets.
7162

72-
---
7363

7464
## Processing Large CSV Files
7565

7666
The `/prospects/process` endpoint supports robust ingestion of large CSVs (e.g., 1300+ rows, 300KB+), following the same normalization and insertion pattern as `/prospects/seed` but optimized for scale.
7767

78-
---
7968

8069
## Directory Structure
8170

@@ -98,13 +87,11 @@ requirements.txt # Python dependencies
9887
render.yaml # Deployment config (Render.com)
9988
```
10089

101-
---
10290

10391
## Contributing
10492

10593
Contributions welcome. Please open issues or submit pull requests.
10694

107-
---
10895

10996
## License
11097

app/api/gemini/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Gemini Routes"""
2+
3+
from .gemini import router as gemini_router

app/api/gemini/gemini.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from fastapi import APIRouter
2+
3+
router = APIRouter()
4+
5+
@router.get("/gemini")
6+
def root() -> dict:
7+
"""GET /gemini endpoint."""
8+
return {"message": "Welcome to the Gemini API!"}
9+

app/api/root.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ def root() -> dict:
1313
base_url = os.getenv("BASE_URL", "http://localhost:8000")
1414
epoch = int(time.time() * 1000)
1515
meta = {
16-
"title": "NX-AI",
16+
"title": "Python",
1717
"severity": "success",
1818
"version": __version__,
1919
"base_url": base_url,
2020
"time": epoch,
2121
}
2222
endpoints = [
23+
{"name": "gemini", "url": f"{base_url}/gemini"},
2324
{"name": "docs", "url": f"{base_url}/docs"},
2425
{"name": "resend", "url": f"{base_url}/resend"},
2526
{"name": "health", "url": f"{base_url}/health"},

app/api/routes.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
from app import __version__
21
"""API routes"""
3-
2+
from app import __version__
43
from dotenv import load_dotenv
5-
64
from fastapi import APIRouter, Depends
7-
85
from app.utils.db import get_db_connection
96
from app.api.schemas import EchoRequest, EchoResponse
107

@@ -16,18 +13,13 @@
1613
from app.api.prompts.prompts import router as prompts_router
1714
from app.api.prospects.prospects import router as prospects_router
1815
from app.api.prospects.search import router as prospects_search_router
19-
from app.utils.prospects.database.alter import router as prospects_alter_router
20-
from app.utils.prospects.database.seed import router as prospects_seed_router
21-
from app.utils.prospects.database.empty import router as prospects_empty_router
22-
from app.utils.prospects.database.process import router as prospects_process_router
16+
from app.api.gemini.gemini import router as gemini_router
2317

2418
router.include_router(root_router)
2519
router.include_router(resend_router)
2620
router.include_router(health_router)
2721
router.include_router(prompts_router)
2822
router.include_router(prospects_search_router)
2923
router.include_router(prospects_router)
30-
router.include_router(prospects_alter_router)
31-
router.include_router(prospects_seed_router)
32-
router.include_router(prospects_empty_router)
33-
router.include_router(prospects_process_router)
24+
router.include_router(gemini_router)
25+

render.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ projects:
88
- type: web
99
name: nx-ai
1010
runtime: python
11-
repo: https://github.com/goldlabelapps/nx-ai
11+
repo: https://github.com/goldlabelapps/python
1212
plan: free
1313
region: oregon
1414
buildCommand: pip install -r requirements.txt

tests/test_gemini.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import sys
2+
import os
3+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../")))
4+
import pytest
5+
from fastapi.testclient import TestClient
6+
from app.main import app
7+
8+
client = TestClient(app)
9+
10+
11+
def test_gemini_endpoint():
12+
response = client.get("/gemini")
13+
assert response.status_code == 200
14+
assert response.json() == {"message": "Welcome to the Gemini API!"}

tests/test_prospects.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ def test_get_prospects_root():
1111
assert "meta" in data
1212
assert "data" in data
1313
assert isinstance(data["data"], list)
14-
# Check that the expected keys are present in the data list
15-
assert any("init" in item for item in data["data"])
16-
assert any("search" in item for item in data["data"])
14+
# Check that the expected keys are present in the data list (if not empty)
15+
if data["data"]:
16+
first_item = data["data"][0]
17+
# Adjust these keys to match your actual prospect schema
18+
assert "id" in first_item
19+
assert "first_name" in first_item or "last_name" in first_item
1720
# Meta checks
1821
meta = data["meta"]
1922
assert meta["severity"] == "success"
20-
assert meta["title"] == "Prospects endpoint"
23+
assert meta["title"] == "Read paginated prospects"
2124

2225
def test_prospects_returns_list():
2326
response = client.get("/prospects")

0 commit comments

Comments
 (0)