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
5 changes: 2 additions & 3 deletions src/age_and_gender_detection/age_and_gender_detection/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ def predict(inputs: Inputs) -> ResponseBody:
for i, pred in enumerate(predictions):
face_num = i + 1
image_basename = Path(image_path).name

metadata = {
"Image Path": image_path,
"Gender": pred["gender"],
"Age": pred["age"],
"Bounding Box": str(pred["box"]),
#"Face Number": face_num,
# "Face Number": face_num,
}

file_responses.append(
Expand All @@ -100,7 +100,6 @@ def predict(inputs: Inputs) -> ResponseBody:
return ResponseBody(root=TextResponse(value="No faces detected in any images."))

return ResponseBody(root=BatchFileResponse(files=file_responses))



def cli_parser(path: str):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ def __init__(
]
session_options = ort.SessionOptions()
self.runtime_providers = [
"CUDAExecutionProvider", "CPUExecutionProvider",
"CUDAExecutionProvider",
"CPUExecutionProvider",
]
self.genderList = ["Male", "Female"]
self.image_file_extensions = [".jpg", ".jpeg", ".png", ".bmp", ".tiff"]
Expand Down
37 changes: 24 additions & 13 deletions src/age_and_gender_detection/tests/test_main_age_gender.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,24 @@ def test_age_gender_command(self, caplog):
]
# Combine all log messages into one string for easier searching
all_messages = " ".join(caplog.messages)

for expected_file in expected_files:
# Create multiple path representations to check for cross-platform compatibility
posix_path = expected_file.as_posix() # Forward slashes: src/age_and_gender_detection/test_images/gela.jpg
native_path = str(expected_file) # OS-native: src\age_and_gender_detection\test_images\gela.jpg on Windows
escaped_path = native_path.replace("\\", "\\\\") # Double-escaped: src\\\\age_and_gender_detection\\\\test_images\\\\gela.jpg

posix_path = (
expected_file.as_posix()
) # Forward slashes: src/age_and_gender_detection/test_images/gela.jpg
native_path = str(
expected_file
) # OS-native: src\age_and_gender_detection\test_images\gela.jpg on Windows
escaped_path = native_path.replace(
"\\", "\\\\"
) # Double-escaped: src\\\\age_and_gender_detection\\\\test_images\\\\gela.jpg

# Check if any path representation appears in the log messages
assert any(path_repr in all_messages for path_repr in [posix_path, native_path, escaped_path]), \
f"Expected file {expected_file} not found in log messages. Checked: {posix_path}, {native_path}, {escaped_path}"
assert any(
path_repr in all_messages
for path_repr in [posix_path, native_path, escaped_path]
), f"Expected file {expected_file} not found in log messages. Checked: {posix_path}, {native_path}, {escaped_path}"

def test_invalid_path(self):
age_gender_api = f"/{APP_NAME}/predict"
Expand All @@ -123,18 +131,21 @@ def test_age_gender_api(self):
body = ResponseBody(**response.json())
print(f"Response body: {body}")
assert body.root is not None
assert hasattr(body.root, 'files'), "Expected BatchFileResponse with files attribute"

assert hasattr(
body.root, "files"
), "Expected BatchFileResponse with files attribute"

# Convert BatchFileResponse to the old dict format for comparison
files = body.root.files
assert len(files) == 4, f"Expected 4 files, got {len(files)}"

# Check that all expected images are present in the response
returned_paths = {file_resp.path for file_resp in files}
expected_paths = set(EXPECTED_OUTPUT.keys())
assert returned_paths == expected_paths, \
f"Mismatch in returned paths. Expected: {expected_paths}, Got: {returned_paths}"

assert (
returned_paths == expected_paths
), f"Mismatch in returned paths. Expected: {expected_paths}, Got: {returned_paths}"

# Verify each file's predictions match expectations
for file_resp in files:
image_path = file_resp.path
Expand Down
14 changes: 8 additions & 6 deletions src/deepfake-detection/deepfake_detection/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def give_prediction(inputs: Inputs, parameters: Parameters) -> ResponseBody:
model_name = model_results[0]["model_name"]
predictions = model_results[1:]
model_data.append({"name": model_name, "predictions": predictions})

file_responses: List[FileResponse] = []
if model_data and model_data[0]["predictions"]:
num_images = len(model_data[0]["predictions"])
Expand All @@ -217,20 +217,22 @@ def give_prediction(inputs: Inputs, parameters: Parameters) -> ResponseBody:
model_name = m["name"]
row_metadata["Prediction"] = pred
row_metadata["Confidence"] = f"{conf * 100:.0f}%"

file_responses.append(
FileResponse(
file_type="img",
path=full_image_path,
title=f"Prediction for {path_basename}",
metadata=row_metadata
metadata=row_metadata,
)
)
if not file_responses:
return ResponseBody(root=TextResponse(value="No predictions generated or no images found."))

return ResponseBody(
root=TextResponse(value="No predictions generated or no images found.")
)

return ResponseBody(root=BatchFileResponse(files=file_responses))


# ----------------------------
# Server Setup Below
Expand Down
12 changes: 9 additions & 3 deletions src/deepfake-detection/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ def test_cli_predict(self, run_models_mock, defaultDataset_mock, caplog, tmp_pat
# Verify the structured response in the log output
# The CLI logs the BatchFileResponse which should contain our prediction
assert "BatchFileResponse" in caplog.text, "Expected BatchFileResponse in logs"
assert "'Prediction': 'fake'" in caplog.text, "Expected prediction 'fake' in metadata"
assert "'Confidence': '100%'" in caplog.text, "Expected confidence '100%' in metadata"
assert (
"'Prediction': 'fake'" in caplog.text
), "Expected prediction 'fake' in metadata"
assert (
"'Confidence': '100%'" in caplog.text
), "Expected confidence '100%' in metadata"
assert "img1.jpg" in caplog.text, "Expected img1.jpg in response"

def test_invalid_path(self):
Expand Down Expand Up @@ -112,7 +116,9 @@ def test_api_predict(self, run_models_mock, defaultDataset_mock, tmp_path):
response = self.client.post(predict_api, json=payload)
assert response.status_code == 200
body = ResponseBody(**response.json())
assert hasattr(body.root, 'files'), "Expected BatchFileResponse with files attribute"
assert hasattr(
body.root, "files"
), "Expected BatchFileResponse with files attribute"
files = body.root.files
assert len(files) == 1, f"Expected 1 file response, got {len(files)}"
file_resp = files[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,21 @@ def find_face_bulk_endpoint(
file_type="img",
path=matched_path,
title=f"Match for {query_img_name}: {matched_filename}",
metadata={"query_image": query_img_name}
metadata={"query_image": query_img_name},
)
)

if not status or not file_responses:
# If results is a string (e.g., an error message), use it directly
# Otherwise, convert the dictionary to a string representation
error_message = str(results) if isinstance(results, str) else json.dumps(results, indent=2)
error_message = (
str(results) if isinstance(results, str) else json.dumps(results, indent=2)
)
return ResponseBody(root=TextResponse(value=error_message))

return ResponseBody(root=BatchFileResponse(files=file_responses))


server.add_ml_service(
rule="/findfacebulk",
ml_function=find_face_bulk_endpoint,
Expand Down
9 changes: 5 additions & 4 deletions src/face-detection-recognition/test/test_app_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,15 +349,16 @@ def test_06_find_face_bulk_endpoint(self):
assert response.status_code == 200
body = ResponseBody(**response.json())

assert isinstance(body.root, BatchFileResponse), \
f"Expected BatchFileResponse with matches, got {type(body.root)}"
assert isinstance(
body.root, BatchFileResponse
), f"Expected BatchFileResponse with matches, got {type(body.root)}"
# BatchFileResponse with matches
print(f"Find face bulk result: {len(body.root.files)} matches found")
# Verify the structure of file responses
for file_resp in body.root.files:
assert file_resp.file_type.value == "img"
assert hasattr(file_resp, 'metadata')
assert 'query_image' in file_resp.metadata
assert hasattr(file_resp, "metadata")
assert "query_image" in file_resp.metadata
print(f" - {file_resp.title}")

# @pytest.mark.skipif(not has_test_images, reason="Test images not available")
Expand Down
3 changes: 2 additions & 1 deletion src/rb-api/rb/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ class FileResponse(BaseModel):
path: str
title: Optional[str] = None
subtitle: Optional[str] = None
metadata: Optional[Dict[str, Any]] = None # additional columns like prediction
metadata: Optional[Dict[str, Any]] = None # additional columns like prediction


class DirectoryResponse(BaseModel):
model_config = ConfigDict(
Expand Down
2 changes: 1 addition & 1 deletion src/rb-api/rb/api/routes/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def is_get_request(command: typer.models.CommandInfo) -> bool:
endpoint=command_callback(command),
methods=["POST"],
name=command.callback.__name__,
response_model=Any, # for internal use
response_model=Any, # for internal use
)
logger.debug(
f"Registering FastAPI route for {plugin.name} command: {command.callback.__name__}"
Expand Down
3 changes: 2 additions & 1 deletion src/rb-lib/rb/lib/typer.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@ def get_inputs_from_signature(
result.append(data)
return result


def typer_app_to_tree(app: typer.Typer) -> dict:
# Create root node
root = Node("rescuebox", command=None, is_group=True)
schema_commands = {}

def add_commands_to_node(typer_app: typer.Typer, parent_node: Node):

for group in getattr(typer_app, "registered_groups", []):
group_node = Node(
group.name,
Expand Down