Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
node_modules/
lib/
.vscode/
.venv/
__pycache__/
*.pyc
.DS_Store
.idea
7 changes: 7 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ npm test ||
false;
)

# Check Black (Python formatter)
python -m black backend --check ||
(
echo '🤔 Black Check Failed. Run: python -m black backend';
false;
)

# Check TypeScript
npm run test-compile ||
(
Expand Down
64 changes: 64 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Findable Backend (FastAPI)

This folder contains a minimal FastAPI backend that exposes a stubbed **query-to-facets** endpoint for experimentation and PoC work.

## Requirements

- Python 3.9+
- `pip`

## Set up a Python virtual environment

From the repo root:

```bash
python -m venv backend/.venv
source backend/.venv/bin/activate
python -m pip install --upgrade pip
```

## Install dependencies

From the repo root:

```bash
pip install -r backend/requirements.txt
```

## Run the server

From the repo root:

```bash
uvicorn backend.main:app --reload
```

## Format code

From the repo root:

```bash
python -m black backend
```

The app will start on `http://127.0.0.1:8000` by default.

### Endpoint

- **Method:** `POST`
- **Path:** `/api/v0/facets`
- **Request body:**

```json
{ "query": "string" }
```

- **Example request:**

```bash
curl -sS -X POST "http://127.0.0.1:8000/api/v0/facets" \
-H "Content-Type: application/json" \
-d '{ "query": "public bam files for latino patients with diabetes" }'
```

- **Response:** currently returns a **hard-coded** JSON structure representing resolved facets for the query. This is intentionally stubbed for PoC and will be replaced by a real implementation later.
Empty file added backend/__init__.py
Empty file.
Empty file added backend/controllers/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions backend/controllers/facets_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from fastapi import APIRouter

from backend.services.facets_service import compute_facets_from_query
from backend.services.models import FacetsResponse

from backend.controllers.models import FacetsRequest

router = APIRouter(prefix="/api/v0/facets", tags=["facets"])


@router.post("")
def get_facets(payload: FacetsRequest) -> FacetsResponse:
"""
Return a stubbed query-to-facets response for PoC purposes.
"""
return compute_facets_from_query()
11 changes: 11 additions & 0 deletions backend/controllers/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pydantic import BaseModel


class FacetsRequest(BaseModel):
"""
Request model for the /api/v0/facets endpoint.
Attributes:
query (str): The search query string to retrieve facets for.
"""

query: str
9 changes: 9 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from fastapi import FastAPI

from backend.controllers.facets_controller import router as facets_router


app = FastAPI(title="Findable API")

# Register controller(s)
app.include_router(facets_router)
4 changes: 4 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fastapi==0.115.0
uvicorn[standard]==0.30.0
pydantic==2.9.0
black==24.10.0
Empty file added backend/services/__init__.py
Empty file.
68 changes: 68 additions & 0 deletions backend/services/facets_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from __future__ import annotations

from backend.services.models import FacetsResponse


def compute_facets_from_query() -> FacetsResponse:
"""Return a stubbed query-to-facets response for PoC purposes.

This function will later be replaced by a real implementation that uses
LLM-backed intent parsing and facet resolution.
"""

return FacetsResponse.model_validate(
{
"query": "public bam files for latino foobar patients with diabetes or foobaz",
"facets": [
{
"facet": "Access",
"selectedValues": [
{
"term": "Granted",
"mention": "public",
}
],
},
{
"facet": "Diagnosis",
"selectedValues": [
{
"term": "MONDO:0005015",
"mention": "diabetes", # Mention resolved but value does not exist in dev
},
{
"term": "unknown",
"mention": "foobaz", # Mention resolved to a disease but does not match a known term
},
],
},
{
"facet": "File Format",
"selectedValues": [
{
"term": ".bam",
"mention": "bam",
}
],
},
{
"facet": "Reported Ethnicity",
"selectedValues": [
{
"term": "Hispanic or Latino",
"mention": "latino",
},
],
},
{
"facet": "unknown",
"selectedValues": [
{
"term": "unknown",
"mention": "foobar", # Mention not resolved to a facet or facet term
}
],
},
],
}
)
43 changes: 43 additions & 0 deletions backend/services/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from pydantic import BaseModel


# Model of a selected facet value and its mention (that is, the original query text that resolved to the value).
class SelectedValue(BaseModel):
"""
Represents a selected facet value and its mention in the original query text.

Attributes:
term (str): The resolved facet value.
mention (str): The original query text that resolved to the value.
"""

term: str
mention: str


# Model of a selected facet and its resolved values.
class FacetSelection(BaseModel):
"""
Represents a selected facet and its resolved values.

Attributes:
facet (str): The name of the facet.
selectedValues (list[SelectedValue]): The list of selected values for this facet.
"""

facet: str
selectedValues: list[SelectedValue]


# Model of the selected facets for a given query, resolved and normalized via LLM.
class FacetsResponse(BaseModel):
"""
Represents the selected facets for a given query, resolved and normalized via LLM.

Attributes:
query (str): The original query string.
facets (list[FacetSelection]): The list of selected facets and their values.
"""

query: str
facets: list[FacetSelection]