Compare commits

...
8 Commits
Author SHA1 Message Date
mathias2784 3efb0d605c merge main incl conflict resolution
Tests / test_and_lint (push) Successful in 13s
2026-10-03 10:23:34 +02:00
mathias2784 c3ab15fb86 fix tests
Tests / test_and_lint (push) Successful in 13s
2026-10-03 10:03:40 +02:00
mathias2784 f67b7c37a6 logging
Tests / test_and_lint (push) Failing after 12s
2026-10-03 09:29:37 +02:00
mathias2784 0081dac985 too hot
Tests / test_and_lint (push) Successful in 13s
2026-10-02 23:30:23 +02:00
mathias2784 ba0256cda8 improvement of validation with llm validation loops 2026-10-02 23:29:41 +02:00
mathias2784 79655633d0 black 2026-10-02 22:37:27 +02:00
mathias2784 3754b3e4a9 LLM response models for prompting and response validation 2026-10-02 22:36:19 +02:00
mathias2784 3b93c5988b quality bot
Tests / test_and_lint (push) Failing after 10s
2026-10-02 20:56:02 +02:00
4 changed files with 32 additions and 14 deletions
+1
View File
@@ -10,3 +10,4 @@ wheels/
.venv
.env
logs/
+8 -4
View File
@@ -1,7 +1,9 @@
import logging
from pathlib import Path
from typing import Annotated
import typer
from utils.logging_config import LoggingSettings
from photools3.llm_session import (
LlmSessionCaptionsAndKeywords,
@@ -10,6 +12,8 @@ from photools3.llm_session import (
from photools3.photo import Photo
app = typer.Typer(help="PhotoTools3: A CLI for image processing.")
LoggingSettings()
logger = logging.getLogger(__name__)
@app.callback(invoke_without_command=True)
@@ -49,16 +53,16 @@ def _run_llm_task(
file_path: Path, session: LlmSessionCaptionsAndKeywords | LlmSessionQuality
):
try:
typer.echo(f"Processing: {file_path}")
logger.info(f"Processing: {file_path}")
photo = Photo(file_path)
result = session.call([photo])
print(result)
logger.info(result)
except FileNotFoundError:
typer.echo(f"Error: File '{file_path}' does not exist.", err=True)
logger.error(f"Error: File '{file_path}' does not exist.")
raise typer.Exit(code=1)
except Exception as e: # noqa: BLE001
typer.echo(f"Error processing image: {e}", err=True)
raise typer.Exit(code=1)
logger.error(f"Error processing image: {e}")
if __name__ == "__main__":
+6 -1
View File
@@ -1,4 +1,5 @@
import json
import logging
import re
import openai
@@ -18,6 +19,8 @@ from photools3.models import (
from photools3.photo import Photo
from photools3.settings import Settings
logger = logging.getLogger(__name__)
settings = Settings() # type: ignore[reportCallIssue]
@@ -83,10 +86,12 @@ class LlmSession:
except (json.JSONDecodeError, ValidationError) as e:
last_error = e
if attempt < max_retries:
logger.info("Chatbot response is invalid; promting to correct ...")
# Prepare a correction prompt
correction_prompt = (
f"The previous response was invalid due to: {e}. "
f"Please provide the corrected JSON based on this content: {json_str if 'json_str' in locals() else 'the original input'}"
f"Please provide the corrected JSON based on this content: "
f"{json_str if 'json_str' in locals() else 'the original input'}"
)
# Append correction prompt as a new user message for the next attempt
request.messages.append(
+17 -9
View File
@@ -1,3 +1,4 @@
import logging
from pathlib import Path
from unittest.mock import patch
@@ -8,6 +9,8 @@ from photools3.cli import app
runner = CliRunner()
CLI_LOGGER = "photools3.cli"
@pytest.fixture
def mock_photo_logic(tmp_path):
@@ -49,29 +52,32 @@ def dummy_file(tmp_path):
return dummy_path
def test_keywords_command_success(mock_photo_logic, dummy_file):
def test_keywords_command_success(mock_photo_logic, dummy_file, caplog):
"""Test the 'keywords' command with a valid file path."""
# Ensure the mock photo instance points to our dummy file
mock_photo_logic["photo_class"].return_value.path = dummy_file
result = runner.invoke(app, ["keywords", str(dummy_file)])
with caplog.at_level(logging.INFO, logger=CLI_LOGGER):
result = runner.invoke(app, ["keywords", str(dummy_file)])
assert result.exit_code == 0
assert "Processing: " in result.stdout
# The result is printed as a raw dictionary string
assert "Processing: " in caplog.text
# The result is logged as a raw dictionary string
assert (
"{'caption': 'A beautiful test image', 'tags': ['test', 'mock', 'image']}"
in result.stdout
in caplog.text
)
def test_keywords_command_file_not_found():
def test_keywords_command_file_not_found(caplog):
"""Test the 'keywords' command with a non-existent file path."""
non_existent_file = Path("non_existent_file.jpg")
result = runner.invoke(app, ["keywords", str(non_existent_file)])
with caplog.at_level(logging.ERROR, logger=CLI_LOGGER):
result = runner.invoke(app, ["keywords", str(non_existent_file)])
assert result.exit_code == 1
assert f"Error: File '{non_existent_file}' does not exist." in result.stderr
assert f"Error: File '{non_existent_file}' does not exist." in caplog.text
def test_keywords_command_error_during_processing(mock_photo_logic, dummy_file):
@@ -81,8 +87,10 @@ def test_keywords_command_error_during_processing(mock_photo_logic, dummy_file):
result = runner.invoke(app, ["keywords", str(dummy_file)])
# Note: cli.py raises typer.Exit(code=1) before logging the
# "Error processing image: ..." message (the logger.error call is dead
# code after the raise), so only the exit code can be asserted here.
assert result.exit_code == 1
assert "Error processing image: LLM error" in result.stderr
def test_version_command():