diff --git a/.github/workflows/cat-test-examples.yml b/.github/workflows/cat-test-examples.yml index 012b326..4057262 100644 --- a/.github/workflows/cat-test-examples.yml +++ b/.github/workflows/cat-test-examples.yml @@ -43,21 +43,35 @@ jobs: - name: Check if GOOGLE_CREDENTIALS is set run: | - if [ -z "${{ secrets.GOOGLE_CREDENTIALS }}" ]; then + if [ -z "${{ secrets.GOOGLE_CREDENTIALS }}" ] + then echo "Error: GOOGLE_CREDENTIALS secret is not set!" - exit 1 + exit 121 fi + - name: Check if OPENAI_API_KEY is set + run: | + if [ -z "${{ secrets.OPENAI_API_KEY }}" ] + then + echo "Error: OPENAI_API_KEY secret is not set!" + exit 122 + fi + - name: Authenticate to Google Cloud uses: google-github-actions/auth@v2 with: credentials_json: '${{ secrets.GOOGLE_CREDENTIALS }}' - - name: Run Latest Example tests + - name: Run Graphic Image Example tests + run: uv run pytest --color=yes --showlocals --capture=no examples/graphic_image + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + + - name: Run Latest Example from Team Recommender tests run: > uv run pytest --verbose --verbosity=10 --capture=no --tb=native --color=yes --showlocals - examples/team_recommender/tests + examples/team_recommender env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} diff --git a/.gitignore b/.gitignore index d929ceb..41c4b8d 100644 --- a/.gitignore +++ b/.gitignore @@ -316,5 +316,5 @@ ENV/ # Test artifacts -test_runs/ +**/test_runs/ minio_data/ \ No newline at end of file diff --git a/examples/graphic_image/.env.example b/examples/graphic_image/.env.example new file mode 100644 index 0000000..1cf213d --- /dev/null +++ b/examples/graphic_image/.env.example @@ -0,0 +1,11 @@ +## This is an example of a .env file +## This file should be renamed to .env and filled with the appropriate values + +## This file is used to store environment variables for the project +## This file should not be committed to the repository + +## OpenAI API Key +OPENAI_API_KEY= + +## OpenAI API URL Override (Optional) +OPENAI_API_URL= diff --git a/examples/graphic_image/conftest.py b/examples/graphic_image/conftest.py new file mode 100644 index 0000000..465c7a3 --- /dev/null +++ b/examples/graphic_image/conftest.py @@ -0,0 +1,14 @@ +import sys +from pathlib import Path + +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +root_path = (Path(__file__)).parent +subfolders = ["src", "tests"] +for subfolder in subfolders: + directory = str((root_path / subfolder).resolve()) + print(f"appending folder: {subfolder}, as directory: {directory}") + sys.path.append(directory) diff --git a/examples/graphic_image/readme.md b/examples/graphic_image/readme.md new file mode 100644 index 0000000..ea18b69 --- /dev/null +++ b/examples/graphic_image/readme.md @@ -0,0 +1,37 @@ +# Local Setup + +### Install Package Manager + +- install [uv](https://docs.astral.sh/uv/getting-started/installation) - Python package manager + - `brew install uv` + +### install dependencies + +```shell +uv sync +``` + +### Setup environment + +```shell +cp .env.example .env +``` + +- populate your new `.env` file with required values + +### Running the AI tests + +```shell +uv run pytest +``` + +# Description + +This AI has to produce text descriptions when an image is submitted. All images +used are available under Creative Commons licenses, with source URL information provided below + +OpenAI license keys will be required to run these examples. + +### Licenses + +https://picryl.com/media/an-afghan-local-policeman-monitors-suspicious-activity-7a0054 diff --git a/examples/graphic_image/tests/fixtures/an-afghan-local-policeman-monitors-suspicious-activity.jpg b/examples/graphic_image/tests/fixtures/an-afghan-local-policeman-monitors-suspicious-activity.jpg new file mode 100644 index 0000000..35daf73 Binary files /dev/null and b/examples/graphic_image/tests/fixtures/an-afghan-local-policeman-monitors-suspicious-activity.jpg differ diff --git a/examples/graphic_image/tests/test_describe_image.py b/examples/graphic_image/tests/test_describe_image.py new file mode 100644 index 0000000..ec923cb --- /dev/null +++ b/examples/graphic_image/tests/test_describe_image.py @@ -0,0 +1,71 @@ +import base64 + +import requests +from openai import OpenAI + +from examples.graphic_image.conftest import root_path + + +def describe_image_by_url(image_url): + client = OpenAI() + + user_prompt = """Describe this image in detail. + Do not say: image description or The image shows or The image depicts. + IMPORTANT: Reply with the description of the image. + """ + # Create the API request + response = client.chat.completions.create( + model="gpt-4o-mini", # Or latest vision model + messages=[ + { + "role": "user", + "content": [ + { + "type": "text", + "text": user_prompt, + }, + { + "type": "image_url", + "image_url": {"url": image_url}, + }, + ], + } + ], + max_tokens=1000, + ) + + # Return the description + return response.choices[0].message.content + + +def describe_image(path): + with open(path, "rb") as f: + encoded_image = base64.b64encode(f.read()).decode("utf-8") + return describe_image_by_url(f"data:image/jpeg;base64,{encoded_image}") + + +def test_image_was_described(): + image_url = ( + "https://cdn2.picryl.com/photo/2011/08/31/" + "an-afghan-local-policeman-monitors-suspicious-activity-7a0054-1024.jpg" + ) + local_path = ( + root_path + / "tests" + / "fixtures" + / "an-afghan-local-policeman-monitors-suspicious-activity.jpg" + ) + description = ( + describe_image_by_url(image_url) if check_url(image_url) else describe_image(local_path) + ) + print("image description:", description) + assert description is not None + + +def check_url(image_url) -> bool: + # noinspection PyBroadException + try: + response = requests.head(image_url, timeout=5) + return response.status_code == 200 + except Exception: + return False