Skip to content

Detected ML Service Misuses #3

@hadil1999-creator

Description

@hadil1999-creator

Hello,
I hope you are doing well.

As part of our analysis, we detected several ML service misuses in your repository. The refactored code snippets provided below were generated with the assistance of an LLM after we identified the misuse and manually validated the refactoring to ensure correctness.

For further details on each misuse definition, please consult our ML Service Misuse Catalog: here

📝 Notes

We did not submit a Pull Request, as we did not want to risk breaking or affecting your project’s workflow or CI/CD pipeline. Instead, we are providing the information below so you can review and integrate the changes at your convenience.

🔍 Detected ML Service Misuses

Below is the list of misuses detected, each with:

  • A short explanation
  • Refactored code examples

1️⃣ Ignoring Monitoring Data Drift

  • Description: No mechanism to detect input/output drift across time.
  • Refactored version:
# ===== File: ImageToText/img2txt.py =====
import os, io
import argparse
from collections import deque
from google.cloud import vision
from google.oauth2 import service_account
def set_args():
    global args
    parser = argparse.ArgumentParser()
    parser.add_argument("path", help="path to image")
    parser.add_argument("--url", action="store_true", help="specify the path for an external image located on the Web (http:// or https://) or in Google Cloud Storage (gs://)")
    parser.add_argument("--document", action="store_true", help="optimized for dense images")
    parser.add_argument("--languages", "--language", help="specify language hints from https://cloud.google.com/vision/docs/languages (comma separated)", default='')
    parser.add_argument("--full", "--verbose", action="store_true", help="show full description (paragraphs, per-word confidence, boundaries...)")
    parser.add_argument("--confidence", type=float, default=0.6, help="display possible mistakes for symbols with low confidence. Default: 0.6")
    parser.add_argument("--key", help="explicitly define the path to your service account JSON credentials")
    args = parser.parse_args()
def f(n, decimals=3):
    return "{:.{}f}".format(n, decimals)
def set_credentials():
    global credentials
    credentials_file = args.key or os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')
    if not credentials_file:
        script_dir = os.path.dirname(os.path.realpath(__file__))
        service_account_json = os.path.join(script_dir, '..', 'service_account.json')
        if os.path.isfile(service_account_json):
            credentials_file = service_account_json
    if credentials_file:
        credentials = service_account.Credentials.from_service_account_file(credentials_file)
    else:
        raise Exception("Missing service_account.json, GOOGLE_APPLICATION_CREDENTIALS or --key\nhttps://github.com/Carleslc/ImageToText
def detect_text(vision_image, language_hints=[], full=False):
    client = vision.ImageAnnotatorClient(credentials=credentials)
    image_context = vision.ImageContext(language_hints=language_hints)
    response = client.text_detection(image=vision_image, image_context=image_context)
    texts = response.text_annotations
    if texts:
        print(f"Language: {texts[0].locale}")
    if full:
        for text in texts:
            print('\n' + text.description)
            vertices = ([f'({vertex.x},{vertex.y})' for vertex in text.bounding_poly.vertices])
            boundaries = ','.join(vertices)
            print(f'bounds: {boundaries}')
    elif texts:
        print()
        print(texts[0].description.strip())
def detect_document_text(vision_image, language_hints=[], full=False):
    client = vision.ImageAnnotatorClient(credentials=credentials)
    image_context = vision.ImageContext(language_hints=language_hints)
    response = client.document_text_detection(image=vision_image, image_context=image_context)
    text = response.text_annotations[0]
    print(f"Language: {text.locale}\n")
    print(text.description.strip())
    if full:
        paragraphs, lines = extract_paragraphs(response.full_text_annotation)
        print('\nSINGLE LINE\n')
        print(' '.join(map(str.strip, lines)))
        print('\nPARAGRAPHS\n\n--')
        print('\n\n'.join(paragraphs) + '\n--')
    for page in response.full_text_annotation.pages:
        has_mistakes = False
        for block in page.blocks:
            if full:
                print(f'\nBlock confidence: {f(block.confidence)}')
            for paragraph in block.paragraphs:
                if full:
                    print('\n' + paragraphs.popleft())
                    print(f'\nParagraph confidence: {f(paragraph.confidence)}\n')
                for word in paragraph.words:
                    word_text = ''.join([symbol.text for symbol in word.symbols])
                    if full:
                        print(f'({f(word.confidence)}) {word_text}')
                    for symbol in word.symbols:
                        if symbol.confidence < args.confidence and not has_mistakes:
                            print(f"Possible mistake: symbol '{symbol.text}' in word '{word_text}' (confidence: {f(symbol.confidence)})")
                            has_mistakes = True
def extract_paragraphs(full_text_annotation):
    breaks = vision.TextAnnotation.DetectedBreak.BreakType
    paragraphs = []
    lines = []
    for page in full_text_annotation.pages:
        for block in page.blocks:
            for paragraph in block.paragraphs:
                para = ""
                line = ""
                for word in paragraph.words:
                    for symbol in word.symbols:
                        line += symbol.text
                        if symbol.property.detected_break.type == breaks.SPACE:
                            line += ' '
                        if symbol.property.detected_break.type == breaks.EOL_SURE_SPACE:
                            line += ' '
                            lines.append(line)
                            para += line
                            line = ''
                        if symbol.property.detected_break.type == breaks.LINE_BREAK:
                            lines.append(line)
                            para += line
                            line = ''
                paragraphs.append(para)
    return deque(paragraphs), lines
def get_image_file(path):
    with io.open(path, 'rb') as image_file:
        content = image_file.read()
    return vision.Image(content=content)
def get_image_uri(uri):
    image = vision.Image()
    image.source.image_uri = uri
    return image
def main():
    set_args()
    convert = detect_document_text if args.document else detect_text
    get_image = get_image_uri if args.url else get_image_file
    language_hints = args.languages.split(',')
    set_credentials()
    convert(get_image(args.path), language_hints, args.full)
if __name__ == "__main__":
    main()

# ===== File: setup.py =====
from setuptools import setup, find_packages
with open('README.md', 'r') as readme_f:
  readme = readme_f.read()
setup(
  name='ImageToText',
  version='1.1',
  author='Carlos Lázaro Costa',
  author_email='lazaro.costa.carles@gmail.com',
  url='https://github.com/Carleslc/ImageToText',
  description="OCR with Google's AI technology (Cloud Vision API)",
  long_description=readme,
  long_description_content_type='text/markdown',
  python_requires='>=3.6',
  packages=find_packages(),
  install_requires=['google-cloud-vision'],
  entry_points={
    'console_scripts': [
      'img2txt = ImageToText.img2txt:main',
    ]
  },
)

2️⃣ Improper Handling of ML API Limits

  • Description: API rate limits or quota usage are not checked or handled.
  • Refactored version:
import os, io
import argparse
from collections import deque
from google.cloud import vision
from google.oauth2 import service_account
def set_args():
    global args
    parser = argparse.ArgumentParser()
    parser.add_argument("path", help="path to image")
    parser.add_argument("--url", action="store_true", help="specify the path for an external image located on the Web (http:// or https://) or in Google Cloud Storage (gs://)")
    parser.add_argument("--document", action="store_true", help="optimized for dense images")
    parser.add_argument("--languages", "--language", help="specify language hints from https://cloud.google.com/vision/docs/languages (comma separated)", default='')
    parser.add_argument("--full", "--verbose", action="store_true", help="show full description (paragraphs, per-word confidence, boundaries...)")
    parser.add_argument("--confidence", type=float, default=0.6, help="display possible mistakes for symbols with low confidence. Default: 0.6")
    parser.add_argument("--key", help="explicitly define the path to your service account JSON credentials")
    args = parser.parse_args()
def f(n, decimals=3):
    return "{:.{}f}".format(n, decimals)
def set_credentials():
    global credentials
    credentials_file = args.key or os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')
    if not credentials_file:
        script_dir = os.path.dirname(os.path.realpath(__file__))
        service_account_json = os.path.join(script_dir, '..', 'service_account.json')
        if os.path.isfile(service_account_json):
            credentials_file = service_account_json
    if credentials_file:
        credentials = service_account.Credentials.from_service_account_file(credentials_file)
    else:
        raise Exception("Missing service_account.json, GOOGLE_APPLICATION_CREDENTIALS or --key\nhttps://github.com/Carleslc/ImageToText
def detect_text(vision_image, language_hints=[], full=False):
    client = vision.ImageAnnotatorClient(credentials=credentials)
    image_context = vision.ImageContext(language_hints=language_hints)
    response = client.text_detection(image=vision_image, image_context=image_context, max_results_per_request=1000)  # Add max_results_per_request parameter to control the number of API requests
    texts = response.text_annotations
    if texts:
        print(f"Language: {texts[0].locale}")
    if full:
        for text in texts:
            print('\n' + text.description)
            vertices = ([f'({vertex.x},{vertex.y})' for vertex in text.bounding_poly.vertices])
            boundaries = ','.join(vertices)
            print(f'bounds: {boundaries}')
    elif texts:
        print()
        print(texts[0].description.strip())
def detect_document_text(vision_image, language_hints=[], full=False):
    client = vision.ImageAnnotatorClient(credentials=credentials)
    image_context = vision.ImageContext(language_hints=language_hints)
    response = client.document_text_detection(image=vision_image, image_context=image_context, max_results_per_request=1000)  # Add max_results_per_request parameter to control the number of API requests
    text = response.text_annotations[0]
    print(f"Language: {text.locale}\n")
    print(text.description.strip())
    if full:
        paragraphs, lines = extract_paragraphs(response.full_text_annotation)
        print('\nSINGLE LINE\n')
        print(' '.join(map(str.strip, lines)))
        print('\nPARAGRAPHS\n\n--')
        print('\n\n'.join(paragraphs) + '\n--')
    for page in response.full_text_annotation.pages:
        has_mistakes = False
        for block in page.blocks:
            if full:
                print(f'\nBlock confidence: {f(block.confidence)}')
            for paragraph in block.paragraphs:
                if full:
                    print('\n' + paragraphs.popleft())
                    print(f'\nParagraph confidence: {f(paragraph.confidence)}\n')
                for word in paragraph.words:
                    word_text = ''.join([symbol.text for symbol in word.symbols])
                    if full:
                        print(f'({f(word.confidence)}) {word_text}')
                    for symbol in word.symbols:
                        if symbol.confidence < args.confidence:
                            if not has_mistakes:
                                has_mistakes = True
                                if not full:
                                    print()
                            print(f"Possible mistake: symbol '{symbol.text}' in word '{word_text}' (confidence: {f(symbol.confidence)})")
def extract_paragraphs(full_text_annotation):
    breaks = vision.TextAnnotation.DetectedBreak.BreakType
    paragraphs = []
    lines = []
    for page in full_text_annotation.pages:
        for block in page.blocks:
            for paragraph in block.paragraphs:
                para = ""
                line = ""
                for word in paragraph.words:
                    for symbol in word.symbols:
                        line += symbol.text
                        if symbol.property.detected_break.type == breaks.SPACE:
                            line += ' '
                        if symbol.property.detected_break.type == breaks.EOL_SURE_SPACE:
                            line += ' '
                            lines.append(line)
                            para += line
                            line = ''
                        if symbol.property.detected_break.type == breaks.LINE_BREAK:
                            lines.append(line)
                            para += line
                            line = ''
                paragraphs.append(para)
    return deque(paragraphs), lines
def get_image_file(path):
    with io.open(path, 'rb') as image_file:
        content = image_file.read()
    return vision.Image(content=content)
def get_image_uri(uri):
    image = vision.Image()
    image.source.image_uri = uri
    return image
def main():
    set_args()
    convert = detect_document_text if args.document else detect_text
    get_image = get_image_uri if args.url else get_image_file
    language_hints = args.languages.split(',')
    set_credentials()
    convert(get_image(args.path), language_hints, args.full)
if __name__ == "__main__":
    main()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions