diff --git a/client/index.html b/client/index.html index 73f0bb6..b700cd7 100644 --- a/client/index.html +++ b/client/index.html @@ -5,7 +5,7 @@ -
AISysRev demonstrates the capabilities of AI-automated title-abstract screening of systematic reviews (SRs), which is subject - to further research and improvements. This PoC is based on the following + to further research and improvements. This tool is based on the following scientific contributions:
- We support all LLMs hosted by Openrouter,{" "}
+ We support models from Openrouter, OpenAI and local OpenAI SDK{" "}
that support structured JSON response, along with configuring
temperature, seed and top_p parameters.
diff --git a/client/src/pages/ScreeningPage.tsx b/client/src/pages/ScreeningPage.tsx
deleted file mode 100644
index 49a1eb3..0000000
--- a/client/src/pages/ScreeningPage.tsx
+++ /dev/null
@@ -1,526 +0,0 @@
-import { useCallback, useId, useState } from "react";
-import {
- available_models,
- DecisionType,
- default_model,
- isLeft,
- type Criteria,
- type Model,
-} from "../llm/types";
-import {
- exampleCriteria,
- exampleTitle,
- exampleAbstract,
-} from "../example_data";
-import { query_llm } from "../llm/llm";
-import { generatePrompt } from "../llm/prompt";
-import { FileDropArea } from "../components/FileDropArea";
-
-const AUTHORIZATION_TOKEN = "AUTHORIZATION_TOKEN";
-const TEMPERATURE = "TEMPERATURE";
-const SEED = "SEED";
-const AI_MODEL = "AI_MODEL";
-
-const abortController = new AbortController();
-
-function Header() {
- return (
-
- This proof-of-concept (PoC) demonstrates the capabilities of
- AI-automated title-abstract screening of systematic reviews (SRs), which
- is subject to further research and improvements. This PoC is based on a
- conference paper{" "}
-
- "The Promise and Challenges of Using LLMs to Accelerate the Screening
- Process of Systematic Reviews"
- {" "}
- by Huotala et al.
-
- We support all foundational models listed in the Openrouter website.
- CC-BY-ND 4.0 Available by request.
-
-
- By using this Proof of Concept (PoC), you acknowledge and agree that
- it is intended for testing purposes only. This PoC should not be
+ By using the AiSysRev tool, you acknowledge and agree that
+ it is intended for testing purposes only. This tool should not be
considered a final solution and should not be relied upon to fully
replace existing screening process in systematic reviews (SRs). With
- all Large Language Models (LLMs), this PoC may produce erroneous or
+ all Large Language Models (LLMs), this AI tool may produce erroneous or
inaccurate results, and there is a possibility that it may
"hallucinate" — meaning it could generate content that is factually
incorrect, misleading, or irrelevant. Therefore, any decisions or
diff --git a/client/vite.config.ts b/client/vite.config.ts
index 1a481f3..305d97d 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -15,10 +15,8 @@ export default defineConfig({
// This proxies API requests to the backend container
"/api": `http://backend_${appEnv}:8080`,
// Proxying documentation
- "/documentation": {
+ "/docs": {
target: `http://backend_${appEnv}:8080`,
- changeOrigin: true,
- rewrite: (path) => path.replace("/documentation", "/docs"),
},
"/openapi.json": `http://backend_${appEnv}:8080`,
},
diff --git a/server/pyproject.toml b/server/pyproject.toml
index a3a1678..a5e33c1 100644
--- a/server/pyproject.toml
+++ b/server/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "server"
-version = "0.1.0"
+version = "1.0.0"
description = "AiSysRev server"
readme = "README.md"
requires-python = ">=3.14"
diff --git a/server/src/api/controllers/event_queue.py b/server/src/api/controllers/event_queue.py
index dc8de78..2645da4 100644
--- a/server/src/api/controllers/event_queue.py
+++ b/server/src/api/controllers/event_queue.py
@@ -8,7 +8,7 @@
KEEPALIVE_INTERVAL = 10
-@router.get("/event-queue")
+@router.get("/event-queue", tags=["Event queue"])
async def event_bus(request: Request):
async def stream():
yield ": connected\n\n"
diff --git a/server/src/api/controllers/file.py b/server/src/api/controllers/file.py
index 7db2f5b..49180e8 100644
--- a/server/src/api/controllers/file.py
+++ b/server/src/api/controllers/file.py
@@ -22,6 +22,7 @@
"/files/{project_uuid}",
status_code=status.HTTP_200_OK,
response_model=list[FileReadWithPaperCount],
+ tags=["File"],
)
async def list_files(project_uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
try:
@@ -34,7 +35,7 @@ async def list_files(project_uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)
)
-@router.post("/files/upload", status_code=200, response_model=dict)
+@router.post("/files/upload", status_code=200, response_model=dict, tags=["File"])
async def process_csv(
project_uuid: UUID = Form(...),
files: List[UploadFile] = File(...),
diff --git a/server/src/api/controllers/fixture.py b/server/src/api/controllers/fixture.py
index ea01e02..6a00436 100644
--- a/server/src/api/controllers/fixture.py
+++ b/server/src/api/controllers/fixture.py
@@ -7,7 +7,7 @@
router = APIRouter()
-@router.post("/fixtures/reset")
+@router.post("/fixtures/reset", tags=["Fixture"])
async def reset_fixtures():
try:
if settings.APP_ENV != "test":
diff --git a/server/src/api/controllers/health_check.py b/server/src/api/controllers/health_check.py
index 9669e30..ad8551a 100644
--- a/server/src/api/controllers/health_check.py
+++ b/server/src/api/controllers/health_check.py
@@ -8,7 +8,7 @@
router = APIRouter()
-@router.get("/health")
+@router.get("/health", tags=["Health check"])
async def health_check():
if not startup_complete.is_set():
return JSONResponse(
@@ -48,8 +48,8 @@ async def health_check():
}
return JSONResponse(
- status_code=status.HTTP_200_OK
- if is_healthy
- else status.HTTP_503_SERVICE_UNAVAILABLE,
+ status_code=(
+ status.HTTP_200_OK if is_healthy else status.HTTP_503_SERVICE_UNAVAILABLE
+ ),
content=payload,
)
diff --git a/server/src/api/controllers/job.py b/server/src/api/controllers/job.py
index 423a2da..78e2b8a 100644
--- a/server/src/api/controllers/job.py
+++ b/server/src/api/controllers/job.py
@@ -16,7 +16,9 @@
router = APIRouter()
-@router.get("/job", status_code=status.HTTP_200_OK, response_model=list[JobRead])
+@router.get(
+ "/job", status_code=status.HTTP_200_OK, response_model=list[JobRead], tags=["Job"]
+)
async def get_jobs(
project: Optional[UUID] = None, db_ctx: DBContext = Depends(get_db_ctx)
):
@@ -33,7 +35,9 @@ async def get_jobs(
) from e
-@router.get("/job/{uuid}", status_code=status.HTTP_200_OK, response_model=JobRead)
+@router.get(
+ "/job/{uuid}", status_code=status.HTTP_200_OK, response_model=JobRead, tags=["Job"]
+)
async def get_single_job(uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
try:
job_service = create_job_service(db_ctx)
@@ -52,7 +56,7 @@ async def get_single_job(uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
)
-@router.post("/job", status_code=status.HTTP_201_CREATED)
+@router.post("/job", status_code=status.HTTP_201_CREATED, tags=["Job"])
async def create_job(
job_data: JobCreate,
db_ctx: DBContext = Depends(get_db_ctx),
diff --git a/server/src/api/controllers/jobtask.py b/server/src/api/controllers/jobtask.py
index e486e61..c65bedf 100644
--- a/server/src/api/controllers/jobtask.py
+++ b/server/src/api/controllers/jobtask.py
@@ -9,7 +9,7 @@
router = APIRouter()
-@router.get("/jobtask/{uuid}", status_code=status.HTTP_200_OK)
+@router.get("/jobtask/{uuid}", status_code=status.HTTP_200_OK, tags=["Job task"])
async def get_job_tasks(uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
try:
jobtask_service = create_jobtask_service(db_ctx)
@@ -32,9 +32,10 @@ async def get_job_tasks(uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
"/jobtask",
status_code=status.HTTP_200_OK,
response_model=list[JobTaskReadWithLLMConfig],
+ tags=["Job task"],
)
async def get_job_tasks_by_paper(
- paper_uuid: str, db_ctx: DBContext = Depends(get_db_ctx)
+ paper_uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)
):
try:
jobtask_service = create_jobtask_service(db_ctx)
@@ -54,7 +55,7 @@ async def get_job_tasks_by_paper(
)
-@router.patch("/jobtask/{uuid}", status_code=status.HTTP_200_OK)
+@router.patch("/jobtask/{uuid}", status_code=status.HTTP_200_OK, tags=["Job task"])
async def add_job_task_human_result(
uuid: UUID,
result: JobTaskHumanResultUpdate,
diff --git a/server/src/api/controllers/llm.py b/server/src/api/controllers/llm.py
index f5d549a..72e51ec 100644
--- a/server/src/api/controllers/llm.py
+++ b/server/src/api/controllers/llm.py
@@ -12,7 +12,7 @@
router = APIRouter()
-@router.get("/llm/providers", status_code=status.HTTP_200_OK)
+@router.get("/llm/providers", status_code=status.HTTP_200_OK, tags=["LLM"])
async def get_providers() -> list[Provider]:
return [
Provider(
@@ -41,6 +41,7 @@ class ProviderConfigParamsResponse(BaseModel):
"/llm/provider_config_params",
status_code=status.HTTP_200_OK,
response_model=dict[str, ProviderConfigParamsResponse],
+ tags=["LLM"],
)
async def get_provider_config_params():
return {
@@ -53,7 +54,7 @@ async def get_provider_config_params():
}
-@router.post("/llm/{provider}/models", status_code=status.HTTP_200_OK)
+@router.post("/llm/{provider}/models", status_code=status.HTTP_200_OK, tags=["LLM"])
async def get_available_models(
provider: str,
provider_parameters: Optional[Dict[str, Any]] = Body(None),
diff --git a/server/src/api/controllers/paper.py b/server/src/api/controllers/paper.py
index ba05aea..b5fb790 100644
--- a/server/src/api/controllers/paper.py
+++ b/server/src/api/controllers/paper.py
@@ -8,7 +8,7 @@
router = APIRouter()
-@router.get("/paper/{project_uuid}", status_code=status.HTTP_200_OK)
+@router.get("/paper/{project_uuid}", status_code=status.HTTP_200_OK, tags=["Paper"])
async def get_papers(project_uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
papers = create_paper_service(db_ctx)
try:
@@ -24,7 +24,9 @@ async def get_papers(project_uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)
@router.get(
- "/paper/{project_uuid}/with_model_evaluations", status_code=status.HTTP_200_OK
+ "/paper/{project_uuid}/with_model_evaluations",
+ status_code=status.HTTP_200_OK,
+ tags=["Paper"],
)
async def get_project_papers_with_model_evals(
project_uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)
@@ -42,7 +44,7 @@ async def get_project_papers_with_model_evals(
) from e
-@router.patch("/paper/{uuid}", status_code=status.HTTP_200_OK)
+@router.patch("/paper/{uuid}", status_code=status.HTTP_200_OK, tags=["Paper"])
async def add_paper_human_result(
uuid: UUID, result: PaperHumanResultUpdate, db_ctx: DBContext = Depends(get_db_ctx)
):
diff --git a/server/src/api/controllers/project.py b/server/src/api/controllers/project.py
index 6163dc4..663635a 100644
--- a/server/src/api/controllers/project.py
+++ b/server/src/api/controllers/project.py
@@ -11,7 +11,10 @@
@router.get(
- "/project", status_code=status.HTTP_200_OK, response_model=list[ProjectRead]
+ "/project",
+ status_code=status.HTTP_200_OK,
+ response_model=list[ProjectRead],
+ tags=["Project"],
)
async def list_projects(db_ctx: DBContext = Depends(get_db_ctx)):
projects = create_project_service(db_ctx)
@@ -25,7 +28,10 @@ async def list_projects(db_ctx: DBContext = Depends(get_db_ctx)):
@router.get(
- "/project/{uuid}", status_code=status.HTTP_200_OK, response_model=ProjectRead
+ "/project/{uuid}",
+ status_code=status.HTTP_200_OK,
+ response_model=ProjectRead,
+ tags=["Project"],
)
async def get_project(uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
projects = create_project_service(db_ctx)
@@ -45,7 +51,7 @@ async def get_project(uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
)
-@router.post("/project", status_code=status.HTTP_201_CREATED)
+@router.post("/project", status_code=status.HTTP_201_CREATED, tags=["Project"])
async def create_new_project(
project_data: ProjectCreate, db_ctx: DBContext = Depends(get_db_ctx)
):
@@ -64,7 +70,9 @@ async def create_new_project(
)
-@router.delete("/project/{uuid}", status_code=status.HTTP_204_NO_CONTENT)
+@router.delete(
+ "/project/{uuid}", status_code=status.HTTP_204_NO_CONTENT, tags=["Project"]
+)
async def delete_project(uuid: UUID, db_ctx: DBContext = Depends(get_db_ctx)):
projects = create_project_service(db_ctx)
try:
diff --git a/server/src/api/controllers/result.py b/server/src/api/controllers/result.py
index 5473834..f2f7a75 100644
--- a/server/src/api/controllers/result.py
+++ b/server/src/api/controllers/result.py
@@ -10,7 +10,7 @@
router = APIRouter()
-@router.get("/result/download_result_csv", status_code=200)
+@router.get("/result/download_result_csv", status_code=200, tags=["Results"])
async def download_result_csv(
project_uuid: UUID,
db_ctx: DBContext = Depends(get_db_ctx),
@@ -39,7 +39,7 @@ async def download_result_csv(
)
-@router.get("/result/html", status_code=200)
+@router.get("/result/html", status_code=200, tags=["Results"])
async def download_result_html(
project_uuid: UUID,
db_ctx: DBContext = Depends(get_db_ctx),
@@ -78,7 +78,7 @@ async def download_result_html(
)
-@router.get("/result/", status_code=200)
+@router.get("/result/", status_code=200, tags=["Results"])
async def get_result(
project_uuid: UUID,
db_ctx: DBContext = Depends(get_db_ctx),
diff --git a/server/src/api/controllers/setting.py b/server/src/api/controllers/setting.py
index e65e420..03f43cd 100644
--- a/server/src/api/controllers/setting.py
+++ b/server/src/api/controllers/setting.py
@@ -7,7 +7,7 @@
router = APIRouter()
-@router.get("/setting", status_code=status.HTTP_200_OK)
+@router.get("/setting", status_code=status.HTTP_200_OK, tags=["Settings"])
async def get_setting(name: str, db_ctx: DBContext = Depends(get_db_ctx)):
setting_service = create_setting_service(db_ctx)
data = await setting_service.get_setting(name, mask_secret=True)
@@ -22,7 +22,7 @@ class UpsertData(BaseModel):
value: str
-@router.post("/setting", status_code=status.HTTP_201_CREATED)
+@router.post("/setting", status_code=status.HTTP_201_CREATED, tags=["Settings"])
async def upsert_setting(
data: UpsertData,
db_ctx: DBContext = Depends(get_db_ctx),
diff --git a/server/src/main.py b/server/src/main.py
index 91624c2..1af8199 100644
--- a/server/src/main.py
+++ b/server/src/main.py
@@ -52,7 +52,18 @@ async def lifespan(app: FastAPI):
redis_task.cancel()
-app = FastAPI(lifespan=lifespan)
+app = FastAPI(
+ lifespan=lifespan,
+ title="AiSysRev",
+ summary="Research-based title-abstract screening tool.",
+ version="1.0.0",
+ terms_of_service="/terms-and-conditions",
+ license_info={
+ "name": "MIT License",
+ "url": "https://github.com/EvoTestOps/AISysRev/blob/main/LICENSE",
+ },
+ contact={"name": "EvoTestOps", "url": "https://github.com/EvoTestOps"},
+)
v1_router = APIRouter(prefix="/api/v1")
diff --git a/server/src/tests/test_004_jobtask.py b/server/src/tests/test_004_jobtask.py
index 8fc0589..06cd61f 100644
--- a/server/src/tests/test_004_jobtask.py
+++ b/server/src/tests/test_004_jobtask.py
@@ -95,7 +95,6 @@ async def fail_bulk_create(*args, **kwargs):
async def test_async_process_job(
mock_get_structured_response, test_job_data, test_structured_response
):
-
async with DBContext() as db_ctx:
job_crud = db_ctx.crud(JobCrud)
jobtask_crud = db_ctx.crud(JobTaskCrud)
diff --git a/server/src/tools/diagnostics/celery_check.py b/server/src/tools/diagnostics/celery_check.py
index 308424c..75801e6 100644
--- a/server/src/tools/diagnostics/celery_check.py
+++ b/server/src/tools/diagnostics/celery_check.py
@@ -18,13 +18,13 @@ async def check_celery_worker():
raise
-@router.post("/run-test-task")
+@router.post("/run-test-task", tags=["Celery"])
async def run_test_task():
task = test_task.delay("This is FastAPI")
return {"task_id": task.id}
-@router.get("/task-status/{task_id}")
+@router.get("/task-status/{task_id}", tags=["Celery"])
async def get_task_status(task_id: str):
task_result: AsyncResult = AsyncResult(task_id, app=test_task.app)
if not task_result:
diff --git a/server/uv.lock b/server/uv.lock
index 1f8ce2c..bbf8297 100644
--- a/server/uv.lock
+++ b/server/uv.lock
@@ -1351,7 +1351,7 @@ wheels = [
[[package]]
name = "server"
-version = "0.1.0"
+version = "1.0.0"
source = { virtual = "." }
dependencies = [
{ name = "aiohttp" },
- AI-automated title-abstract screening PoC
-
- An error occured
- Introduction
- Supported LLMs
- License
- Source code
- Examples
-
- {[...criterias]
- .sort((a, b) => a.id - b.id)
- .map((c) => {
- return (
-
-
- {response}
-
- )}
- AI-automated title-abstract screening PoC
+ AiSysRev