Skip to content

Commit 9d6ced6

Browse files
Merge pull request #84 from goldlabelapps/staging
Document /prompt and /resend endpoints in README
2 parents 151aa0a + b340236 commit 9d6ced6

20 files changed

Lines changed: 115 additions & 133 deletions

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
This project provides a scalable API backend using FastAPI and PostgreSQL, featuring:
1010

1111
- Automatic full-text search on all text fields (via tsvector)
12-
- Endpoints for health checks, product management, prompt handling, and prospect management
12+
- Endpoints for health checks, product management, prompt handling (via `/prompt`), resend email, and prospect management
1313
- Efficient ingestion and processing of large CSV files
1414

1515
#### 🚀 Features
@@ -21,7 +21,7 @@ This project provides a scalable API backend using FastAPI and PostgreSQL, featu
2121
- **Uvicorn** — Lightning-fast ASGI server
2222
- **Pytest** — Comprehensive testing
2323

24-
#### Getting Started
24+
#### Install & Use
2525

2626
### 1. Clone & Setup Environment
2727

@@ -42,13 +42,21 @@ uvicorn app.main:app --reload
4242

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

45+
4546
#### API Documentation
4647

4748
FastAPI auto-generates interactive docs:
4849

4950
- [Swagger UI](https://nx-ai.onrender.com/docs)
5051
- [ReDoc](https://nx-ai.onrender.com/redoc)
5152

53+
#### Notable Endpoints
54+
55+
- `GET /health` — Health check
56+
- `GET/POST /prompt` — LLM prompt completion (formerly `/llm`)
57+
- `GET/POST /resend` — Send email via Resend API (see implementation in `app/utils/notify/resend.py`)
58+
- `GET /prospects` — Paginated prospects
59+
- `POST /prospects/process` — Bulk CSV ingestion
5260

5361
## Full-Text Search (tsvector)
5462

app/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""Python - FastAPI, Postgres, tsvector"""
22

33
# Current Version
4-
__version__ = "2.2.2"
4+
__version__ = "2.2.3"

app/api/prompt/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
"""LLM Routes"""
1+
"""Prompt Routes"""
22

3-
from .prompt import router as llm_router
3+
from .prompt import router as prompt_router
4+
from .linkedin import router as linkedin_router

app/api/prompt/linkedin.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from fastapi import APIRouter, Depends, HTTPException
2+
3+
from app.utils.api_key_auth import get_api_key
4+
from app.utils.db import get_db_connection_direct
5+
from app.utils.make_meta import make_meta
6+
7+
router = APIRouter()
8+
9+
10+
@router.post("/prompt/linkedin")
11+
def linkedin_prompt_success(payload: dict, api_key: str = Depends(get_api_key)) -> dict:
12+
"""POST /prompt/linkedin: return cached completion for linkedinUrl when available."""
13+
linkedin_url = (payload.get("linkedinUrl") or "").strip()
14+
if not linkedin_url:
15+
raise HTTPException(status_code=400, detail="Missing 'linkedinUrl' in request body.")
16+
17+
conn = None
18+
cur = None
19+
try:
20+
conn = get_db_connection_direct()
21+
cur = conn.cursor()
22+
cur.execute(
23+
"""
24+
SELECT id, completion, time, model, data
25+
FROM prompt
26+
WHERE (data->>'linkedinUrl' = %s OR prompt ILIKE %s)
27+
ORDER BY id DESC
28+
LIMIT 1;
29+
""",
30+
(linkedin_url, f"%{linkedin_url}%"),
31+
)
32+
row = cur.fetchone()
33+
34+
if row:
35+
return {
36+
"meta": make_meta("success", "LinkedIn URL already analysed"),
37+
"data": {
38+
"cached": True,
39+
"id": row[0],
40+
"linkedinUrl": linkedin_url,
41+
"completion": row[1],
42+
"time": row[2].isoformat() if row[2] else None,
43+
"model": row[3],
44+
"record_data": row[4],
45+
},
46+
}
47+
48+
return {
49+
"meta": make_meta("warning", "LinkedIn URL not analysed yet"),
50+
"data": {
51+
"cached": False,
52+
"linkedinUrl": linkedin_url,
53+
"completion": None,
54+
},
55+
}
56+
except HTTPException:
57+
raise
58+
except Exception as e:
59+
return {
60+
"meta": make_meta("error", f"DB error: {str(e)}"),
61+
"data": {},
62+
}
63+
finally:
64+
if cur:
65+
cur.close()
66+
if conn:
67+
conn.close()

app/api/prompt/prompt.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ def get_prompt_records(
2121
if prospect_id is not None:
2222
# No pagination for single prospect_id lookup
2323
select_query = """
24-
SELECT id, prompt, completion, duration, time, data, model, prospect_id, search_vector
25-
FROM llm
24+
SELECT id, prompt, completion, duration, time, data, model, prospect_id
25+
FROM prompt
2626
WHERE prospect_id = %s
2727
ORDER BY id DESC
2828
"""
@@ -38,7 +38,6 @@ def get_prompt_records(
3838
"data": row[5],
3939
"model": row[6],
4040
"prospect_id": row[7],
41-
"search_vector": str(row[8]) if row[8] is not None else None,
4241
}
4342
for row in rows
4443
]
@@ -58,12 +57,12 @@ def get_prompt_records(
5857
}
5958
else:
6059
offset = (page - 1) * page_size
61-
cur.execute("SELECT COUNT(*) FROM llm;")
60+
cur.execute("SELECT COUNT(*) FROM prompt;")
6261
count_row = cur.fetchone()
6362
total = count_row[0] if count_row and count_row[0] is not None else 0
6463
cur.execute("""
65-
SELECT id, prompt, completion, duration, time, data, model, prospect_id, search_vector
66-
FROM llm
64+
SELECT id, prompt, completion, duration, time, data, model, prospect_id
65+
FROM prompt
6766
ORDER BY id DESC
6867
LIMIT %s OFFSET %s;
6968
""", (page_size, offset))
@@ -77,13 +76,12 @@ def get_prompt_records(
7776
"data": row[5],
7877
"model": row[6],
7978
"prospect_id": row[7],
80-
"search_vector": str(row[8]) if row[8] is not None else None,
8179
}
8280
for row in cur.fetchall()
8381
]
8482
cur.close()
8583
conn.close()
86-
meta = make_meta("success", f"LLM {len(records)} records (page {page})")
84+
meta = make_meta("success", f"Prompt {len(records)} records (page {page})")
8785
return {
8886
"meta": meta,
8987
"data": {
@@ -140,22 +138,21 @@ def llm_post(payload: dict) -> dict:
140138
if not completion:
141139
error_details = " | ".join([f"{k}: {v}" for k, v in errors.items()])
142140
raise Exception(f"No available Gemini model succeeded for generate_content with your API key. Details: {error_details}")
143-
# Insert record into llm table
141+
# Insert record into prompt table
144142
record_id = None
145143
try:
146144
import json
147145
from app import __version__
148146
data_blob = json.dumps({"version": __version__})
149147
conn = get_db_connection_direct()
150148
cur = conn.cursor()
151-
# Generate tsvector from prompt and completion
152149
cur.execute(
153150
"""
154-
INSERT INTO llm (prompt, completion, duration, data, model, prospect_id, search_vector)
155-
VALUES (%s, %s, %s, %s, %s, %s, to_tsvector('english', %s || ' ' || %s))
151+
INSERT INTO prompt (prompt, completion, duration, data, model, prospect_id)
152+
VALUES (%s, %s, %s, %s, %s, %s)
156153
RETURNING id;
157154
""",
158-
(prompt, completion, duration, data_blob, used_model, prospect_id, prompt, completion)
155+
(prompt, completion, duration, data_blob, used_model, prospect_id)
159156
)
160157
record_id_row = cur.fetchone()
161158
record_id = record_id_row[0] if record_id_row else None
@@ -164,7 +161,7 @@ def llm_post(payload: dict) -> dict:
164161
conn.close()
165162
except Exception as db_exc:
166163
# Log DB error but do not fail the API response
167-
logging.error(f"Failed to insert llm record: {db_exc}")
164+
logging.error(f"Failed to insert prompt record: {db_exc}")
168165
meta = make_meta("success", f"Gemini completion received from {used_model}")
169166
return {"meta": meta, "data": {"id": record_id, "prompt": prompt, "completion": completion}}
170167
except Exception as e:

app/api/prompt/sql/alter_add_prompt_code.sql

Lines changed: 0 additions & 2 deletions
This file was deleted.

app/api/prompt/sql/alter_add_prospect_id.sql

Lines changed: 0 additions & 2 deletions
This file was deleted.

app/api/prompt/sql/alter_add_search_vector.sql

Lines changed: 0 additions & 5 deletions
This file was deleted.

app/api/prompt/sql/alter_add_type_column.sql

Lines changed: 0 additions & 2 deletions
This file was deleted.

app/api/prompt/sql/create_table.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
CREATE TABLE IF NOT EXISTS llm (
2+
CREATE TABLE IF NOT EXISTS prompt (
33
id SERIAL PRIMARY KEY,
44
vector vector(1536),
55
prompt TEXT NOT NULL,

0 commit comments

Comments
 (0)