Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: example on Vision API (image to ad copy) #293

Merged
merged 11 commits into from
Dec 21, 2023

Conversation

lakshyaag
Copy link
Contributor

@lakshyaag lakshyaag commented Dec 21, 2023

This pull request adds the .DS_Store file to the .gitignore file to prevent it from being tracked in the repository. It also includes an example on how to use the GPT-4 Vision API and the Chat API to automatically generate advertising copy from product images.

Summary by CodeRabbit

  • New Features

    • Introduced a guide on using GPT-4 Vision API and Chat API to generate ad copy from images.
    • Implemented a new example script for identifying products in images and creating advertising content.
  • Documentation

    • Added image_to_ad_copy.md to showcase usage and capabilities of the new feature.
  • Chores

    • Updated .gitignore to exclude macOS system files.

Copy link
Contributor

coderabbitai bot commented Dec 21, 2023

Walkthrough

The updates involve enhancing a project with functionalities for processing images and generating advertising copy using AI. A .gitignore file has been updated to exclude macOS system files. New code has been introduced to define classes and functions that work with an AI Vision API to recognize products in images and create corresponding ad copy, streamlining the creation of marketing materials from visual content.

Changes

File Path Change Summary
.gitignore Added .DS_Store to ignore list and appended an extra newline.
docs/examples/image_to_ad_copy.md
examples/vision/image_to_ad_copy.py
Introduced classes and functions for using AI to identify products in images and generate ad copy.

🐇✨
In the den of digital dreams, a rabbit hopped,
Through lines of code and visions cropped.
With a twitch of its nose, the ads took form,
From pixels to prose, a new art was born.
🎩🥕📜

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on X ?


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • You can reply to a review comment made by CodeRabbit.
  • You can tag CodeRabbit on specific lines of code or files in the PR by tagging @coderabbitai in a comment.
  • You can tag @coderabbitai in a PR comment and ask one-off questions about the PR and the codebase. Use quoted replies to pass the context for follow-up questions.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between ffffab7 and 368bd15.
Files selected for processing (3)
  • .gitignore (2 hunks)
  • docs/examples/image_to_ad_copy.md (1 hunks)
  • examples/vision/image_to_ad_copy.py (1 hunks)
Files skipped from review due to trivial changes (1)
  • .gitignore
Additional comments: 19
docs/examples/image_to_ad_copy.md (9)
  • 1-3: The introduction clearly explains the purpose of the document and its relevance to the target audience.

  • 5-5: Ensure the GitHub link points to the correct repository and directory where the full code is available.

  • 9-11: The Product class is well-documented, and the description provides a clear understanding of its purpose.

  • 25-35: The Product class fields are well-defined with descriptions and examples. However, ensure that the key_features field, which is optional, is handled correctly in the code where it's used, as it can be None.

  • 39-48: The generate_prompt method is a good example of how to create a string representation of the Product object for use in prompts. It's clear and concise.

  • 53-53: The IdentifiedProduct class includes an error flag and message, which is a good practice for error handling in data processing.

  • 79-79: The AdCopy class is well-documented, and the fields are descriptive, providing clear guidance on what each field represents.

  • 107-107: The read_images function description is clear, but ensure that the instructor library and the client_image object are properly documented elsewhere, as they are critical to understanding how the API is called.

  • 146-146: Clarify whether the two different clients (client_image and client_copy) are necessary and if they could be combined for simplicity, or if their separation is due to API constraints.

examples/vision/image_to_ad_copy.py (10)
  • 1-11: The imports and setup of the environment variables are correct. Ensure that the .env file is properly secured and not checked into version control.

  • 15-18: The logger is set up correctly with an appropriate log level. This is good for monitoring the application's behavior.

  • 21-55: The Product class is defined correctly, mirroring the documentation. Ensure that the key_features field is handled properly in the code, as it can be None.

  • 58-79: The IdentifiedProduct class includes an error flag and message, which is good for error handling. The __bool__ method is a nice touch for checking if products were identified.

  • 82-106: The AdCopy class is well-defined with examples for each field. Ensure that the name field is used consistently with the Product class.

  • 109-115: The instantiation of the API clients is done using environment variables, which is secure. However, ensure that the instructor library is properly documented and that its usage is secure and efficient.

  • 118-146: The read_images function correctly uses the Vision API client. Ensure that the model specified is the correct one for the task and that the response_model is properly implemented to handle the API response.

  • 149-167: The generate_ad_copy function is well-documented and uses the Chat API client. Verify that the model and temperature settings are appropriate for the desired output.

  • 170-190: The run function orchestrates the process correctly. Ensure that error handling is robust and that the function gracefully handles cases where no products are identified or ad copy generation fails.

  • 193-229: The main execution block is set up correctly with error handling for command-line arguments. Ensure that the results are written to a secure location and that sensitive data is not exposed.

docs/examples/image_to_ad_copy.md Outdated Show resolved Hide resolved
docs/examples/image_to_ad_copy.md Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 4

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 368bd15 and a0bc201.
Files selected for processing (1)
  • docs/examples/image_to_ad_copy.md (1 hunks)
Additional comments: 4
docs/examples/image_to_ad_copy.md (4)
  • 56-74: The IdentifiedProduct class is well-structured. It's good to see error handling incorporated with the error flag and message. Ensure that the error handling is consistent and integrated throughout the codebase where this class is used.

  • 110-137: The read_images function is clear and seems to align with the PR's objective of using the Vision API. However, ensure that the client_image is properly initialized and authenticated before this function is called.

  • 149-167: The generate_ad_copy function is well-documented and uses a separate client for the Chat API, which is appropriate given the different response formats. Ensure that the client_copy is also properly initialized and authenticated.

  • 170-172: The section on putting it all together is mentioned but lacks a code example or detailed explanation. Providing a complete example or walkthrough, as previously suggested, would be beneficial for users to understand the end-to-end process.

Comment on lines 15 to 48
```python
class Product(BaseModel):
"""
Represents a product extracted from an image using AI.

The product attributes are dynamically determined based on the content
of the image and the AI's interpretation. This class serves as a structured
representation of the identified product characteristics.
"""

name: str = Field(
description="A generic name for the product.", example="Headphones"
)
key_features: Optional[list[str]] = Field(
description="A list of key features of the product that stand out.",
default=None,
)

description: Optional[str] = Field(
description="A description of the product.",
default=None,
)

# Can be customized and automatically generated
def generate_prompt(self):
prompt = f"Product: {self.name}\n"
if self.description:
prompt += f"Description: {self.description}\n"
if self.key_features:
prompt += f"Key Features: {', '.join(self.key_features)}\n"
return prompt

def __repr__(self):
return self.generate_prompt()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Product class is well-documented and includes examples for fields. Consider adding type hints for key_features to specify the type of elements in the list, enhancing type safety and clarity.

- key_features: Optional[list[str]] = Field(
+ key_features: Optional[List[str]] = Field(

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
```python
class Product(BaseModel):
"""
Represents a product extracted from an image using AI.
The product attributes are dynamically determined based on the content
of the image and the AI's interpretation. This class serves as a structured
representation of the identified product characteristics.
"""
name: str = Field(
description="A generic name for the product.", example="Headphones"
)
key_features: Optional[list[str]] = Field(
description="A list of key features of the product that stand out.",
default=None,
)
description: Optional[str] = Field(
description="A description of the product.",
default=None,
)
# Can be customized and automatically generated
def generate_prompt(self):
prompt = f"Product: {self.name}\n"
if self.description:
prompt += f"Description: {self.description}\n"
if self.key_features:
prompt += f"Key Features: {', '.join(self.key_features)}\n"
return prompt
def __repr__(self):
return self.generate_prompt()
class Product(BaseModel):
"""
Represents a product extracted from an image using AI.
The product attributes are dynamically determined based on the content
of the image and the AI's interpretation. This class serves as a structured
representation of the identified product characteristics.
"""
name: str = Field(
description="A generic name for the product.", example="Headphones"
)
key_features: Optional[List[str]] = Field(
description="A list of key features of the product that stand out.",
default=None,
)
description: Optional[str] = Field(
description="A description of the product.",
default=None,
)
# Can be customized and automatically generated
def generate_prompt(self):
prompt = f"Product: {self.name}\n"
if self.description:
prompt += f"Description: {self.description}\n"
if self.key_features:
prompt += f"Key Features: {', '.join(self.key_features)}\n"
return prompt
def __repr__(self):
return self.generate_prompt()

Comment on lines 82 to 100
class AdCopy(BaseModel):
"""
Represents a generated ad copy.
"""

headline: str = Field(
description="A short, catchy, and memorable headline for the given product. The headline should invoke curiosity and interest in the product.",
)
ad_copy: str = Field(
description="A long-form advertisement copy for the given product. This will be used in campaigns to promote the product with a persuasive message and a call-to-action with the objective of driving sales.",
)
name: str = Field(
description="The name of the product being advertised.", )

def __str__(self):
return f"{self.name}: \n" + "-" * 100 + f"{self.headline}\n{self.ad_copy}"

def __repr__(self):
return str(self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AdCopy class includes a name field, which might be redundant if it's meant to represent the same product name as in the Product class. If the intention is to reference the product, consider using a reference or a foreign key instead of duplicating the name.

@lakshyaag lakshyaag changed the title [docs] Example on Vision API (image to ad copy) docs: example on Vision API (image to ad copy) Dec 21, 2023
@jxnl
Copy link
Owner

jxnl commented Dec 21, 2023

Can you include the example output the same way I'd did in the vision post?

name: str = Field(
description="The name of the product being advertised.", )

def __str__(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for str

usually model_dumps() is enough



# Define clients
client_image = instructor.patch(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see, we shuold work on being able to overload the default mode in the create call. in the future

logger.info("Writing results to file...")

with open("results.json", "w") as f:
json.dump(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does a single products.model_dump() not work?

Co-authored-by: Jason Liu <jxnl@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between a0bc201 and 1024004.
Files selected for processing (1)
  • docs/examples/image_to_ad_copy.md (1 hunks)
Additional comments: 5
docs/examples/image_to_ad_copy.md (5)
  • 56-75: The IdentifiedProduct class is well-structured to hold identified products and error information. It's good practice to include error handling within data models for robust processing.

  • 82-101:

  • 110-137: The read_images function is correctly documented and uses the OpenAI vision model to identify products in images. The use of the instructor library to patch the OpenAI client is a good approach for extending functionality.

  • 149-167: The generate_ad_copy function is well-documented and correctly uses a separate OpenAI client to generate advertising copy, acknowledging the incompatibility between different models.

  • 174-181:

Comment on lines 47 to 48
def __repr__(self):
return self.generate_prompt()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __repr__ method in the Product class is unnecessary if it only calls another method (generate_prompt) that is not intended for debugging purposes. Consider removing it to follow Python's convention of using __repr__ for unambiguous object representations.

-    def __repr__(self):
-        return self.generate_prompt()

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def __repr__(self):
return self.generate_prompt()

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 6

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 1024004 and 385d8b4.
Files ignored due to filter (1)
  • mkdocs.yml
Files selected for processing (2)
  • docs/examples/image_to_ad_copy.md (1 hunks)
  • examples/vision/image_to_ad_copy.py (1 hunks)
Additional comments: 12
docs/examples/image_to_ad_copy.md (6)
  • 1-3: The introduction provides a clear overview of the purpose of the document.

  • 7-45: The Product class is well-documented with a clear description and examples for each field.

  • 48-71: The IdentifiedProduct class includes an error flag and message, which is good for error handling.

  • 76-92: The AdCopy class is well-documented and includes examples, which will be helpful for users.

  • 166-168: The input file section is brief and has been previously commented on for improvement. Since the previous comment is still valid, skip generating a similar comment.

  • 175-227: The example output is a good addition, providing a clear idea of what the user can expect from using the code.

examples/vision/image_to_ad_copy.py (6)
  • 21-52: The Product class is well-defined with clear documentation and examples.

  • 55-76: The IdentifiedProduct class includes an error flag and message, which is good for error handling. The __bool__ method is a nice touch for easy truthiness checks.

  • 79-97: The AdCopy class is well-documented with examples, which will be helpful for users.

  • 100-106: The global variables client_image and client_copy are introduced for managing API client instances. Ensure that these instances are used securely and that the API key is not exposed.

Verification successful

The verification process did not find any instances of the OPENAI_API_KEY being exposed through print or log statements in the Python files of the codebase. The use of environment variables to handle the API key is secure, and the absence of the key in any print or log statements suggests that it is not being inadvertently exposed.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the API key is not exposed in the codebase
rg --type py "OPENAI_API_KEY"

Length of output: 609



Script:

#!/bin/bash
# Check for logging or print statements that might inadvertently expose the API key
rg --type py -e "print.*OPENAI_API_KEY" -e "log.*OPENAI_API_KEY"

Length of output: 66

* 184-222: The main execution block is well-structured and includes error handling. Ensure that the `results.json` file is written to a secure location and that sensitive data is not exposed.
  • 223-271: The example output provided in the docstring is a good addition, providing a clear idea of what the user can expect from using the code.

Comment on lines +99 to +129
The `read_images` function uses OpenAI's vision model to process a list of image URLs and identify products in each of them. We utilize the `instructor` library to patch the OpenAI client for this purpose.

```python
def read_images(image_urls: List[str]) -> IdentifiedProduct:
"""
Given a list of image URLs, identify the products in the images.
"""

logger.info(f"Identifying products in images... {len(image_urls)} images")

return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024, # can be changed
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The read_images function documentation should specify the expected return type for clarity.

- def read_images(image_urls: List[str]) -> IdentifiedProduct:
+ def read_images(image_urls: List[str]) -> List[IdentifiedProduct]:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
The `read_images` function uses OpenAI's vision model to process a list of image URLs and identify products in each of them. We utilize the `instructor` library to patch the OpenAI client for this purpose.
```python
def read_images(image_urls: List[str]) -> IdentifiedProduct:
"""
Given a list of image URLs, identify the products in the images.
"""
logger.info(f"Identifying products in images... {len(image_urls)} images")
return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024, # can be changed
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)
def read_images(image_urls: List[str]) -> List[IdentifiedProduct]:
"""
Given a list of image URLs, identify the products in the images.
"""
logger.info(f"Identifying products in images... {len(image_urls)} images")
return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024, # can be changed
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)

Comment on lines 138 to 158
Two clients are defined for the two different models. This is because the `gpt-4-vision-preview` model is not compatible with the `gpt-4-1106-preview` model in terms of their response format.

```python
def generate_ad_copy(product: Product):
"""
Given a product, generate an ad copy for the product.
"""

logger.info(f"Generating ad copy for product: {product.name}")

return client_copy.chat.completions.create(
model="gpt-4-1106-preview",
response_model=AdCopy,
temperature=0.3,
messages=[
{
"role": "system",
"content": "You are an expert marketing assistant for all products. Your task is to generate an advertisement copy for a product using the name, description, and key features.",
},
{"role": "user", "content": product.generate_prompt()},
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generate_ad_copy function is well-documented, but it would be beneficial to include the expected return type in the documentation.

- def generate_ad_copy(product: Product):
+ def generate_ad_copy(product: Product) -> AdCopy:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
Two clients are defined for the two different models. This is because the `gpt-4-vision-preview` model is not compatible with the `gpt-4-1106-preview` model in terms of their response format.
```python
def generate_ad_copy(product: Product):
"""
Given a product, generate an ad copy for the product.
"""
logger.info(f"Generating ad copy for product: {product.name}")
return client_copy.chat.completions.create(
model="gpt-4-1106-preview",
response_model=AdCopy,
temperature=0.3,
messages=[
{
"role": "system",
"content": "You are an expert marketing assistant for all products. Your task is to generate an advertisement copy for a product using the name, description, and key features.",
},
{"role": "user", "content": product.generate_prompt()},
],
def generate_ad_copy(product: Product) -> AdCopy:
"""
Given a product, generate an ad copy for the product.
"""
logger.info(f"Generating ad copy for product: {product.name}")
return client_copy.chat.completions.create(
model="gpt-4-1106-preview",
response_model=AdCopy,
temperature=0.3,
messages=[
{
"role": "system",
"content": "You are an expert marketing assistant for all products. Your task is to generate an advertisement copy for a product using the name, description, and key features.",
},
{"role": "user", "content": product.generate_prompt()},
],


### Putting it all together

Finally, we can put it all together in a single function that takes a list of image URLs and generates advertising copy for the products identified in the images. Please refer to the [full code](https://github.com/jxnl/instructor/examples/vision/image_to_ad_copy.py) for the complete implementation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The summary of the run function is clear, but it would be helpful to include the return type in the documentation for consistency.

- def run(images: list[str]):
+ def run(images: list[str]) -> Tuple[List[Product], List[AdCopy]]:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
Finally, we can put it all together in a single function that takes a list of image URLs and generates advertising copy for the products identified in the images. Please refer to the [full code](https://github.com/jxnl/instructor/examples/vision/image_to_ad_copy.py) for the complete implementation.
Finally, we can put it all together in a single function that takes a list of image URLs and generates advertising copy for the products identified in the images. Please refer to the [full code](https://github.com/jxnl/instructor/examples/vision/image_to_ad_copy.py) for the complete implementation.

Comment on lines 110 to 137
def read_images(image_urls: List[str]):
"""
Given a list of image URLs, identify the products in the images.
"""

logger.info(f"Identifying products in images... {len(image_urls)} images")

return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024,
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The read_images function should specify the expected return type for clarity.

- def read_images(image_urls: List[str]):
+ def read_images(image_urls: List[str]) -> IdentifiedProduct:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def read_images(image_urls: List[str]):
"""
Given a list of image URLs, identify the products in the images.
"""
logger.info(f"Identifying products in images... {len(image_urls)} images")
return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024,
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)
def read_images(image_urls: List[str]) -> IdentifiedProduct:
"""
Given a list of image URLs, identify the products in the images.
"""
logger.info(f"Identifying products in images... {len(image_urls)} images")
return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024,
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)

examples/vision/image_to_ad_copy.py Outdated Show resolved Hide resolved
Comment on lines 161 to 181
def run(images: list[str]):
"""
Given a list of images, identify the products in the images and generate ad copy for each product.
"""

identified_products: IdentifiedProduct = read_images(images)
ad_copies = []

if identified_products.error:
rprint(f"[red]Error: {identified_products.message}[/red]")
return []

if not identified_products:
rprint("[yellow]No products identified.[/yellow]")
return []

for product in identified_products.products:
ad_copy: AdCopy = generate_ad_copy(product)
ad_copies.append(ad_copy)

return identified_products.products, ad_copies
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The run function is well-documented, but it would be helpful to include the return type in the documentation for consistency.

- def run(images: list[str]):
+ def run(images: list[str]) -> Tuple[List[Product], List[AdCopy]]:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def run(images: list[str]):
"""
Given a list of images, identify the products in the images and generate ad copy for each product.
"""
identified_products: IdentifiedProduct = read_images(images)
ad_copies = []
if identified_products.error:
rprint(f"[red]Error: {identified_products.message}[/red]")
return []
if not identified_products:
rprint("[yellow]No products identified.[/yellow]")
return []
for product in identified_products.products:
ad_copy: AdCopy = generate_ad_copy(product)
ad_copies.append(ad_copy)
return identified_products.products, ad_copies
def run(images: list[str]) -> Tuple[List[Product], List[AdCopy]]:
"""
Given a list of images, identify the products in the images and generate ad copy for each product.
"""
identified_products: IdentifiedProduct = read_images(images)
ad_copies = []
if identified_products.error:
rprint(f"[red]Error: {identified_products.message}[/red]")
return []
if not identified_products:
rprint("[yellow]No products identified.[/yellow]")
return []
for product in identified_products.products:
ad_copy: AdCopy = generate_ad_copy(product)
ad_copies.append(ad_copy)
return identified_products.products, ad_copies

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 385d8b4 and 471cf88.
Files selected for processing (2)
  • docs/examples/image_to_ad_copy.md (1 hunks)
  • examples/vision/image_to_ad_copy.py (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • docs/examples/image_to_ad_copy.md
Additional comments: 8
examples/vision/image_to_ad_copy.py (8)
  • 22-53: The Product class is well-defined with appropriate type annotations and default values. The generate_prompt method concatenates product information into a prompt string. Ensure that the method is used in the context where a prompt is required.

  • 56-77: The IdentifiedProduct class includes an error field and a message field for error handling. The __bool__ method is implemented to quickly check if any products are identified. Verify that the error handling mechanism is consistent with the rest of the codebase.

  • 80-98: The AdCopy class is defined with fields for the headline, ad copy, and product name. The descriptions and examples provided in the Field declarations are helpful for understanding the intended use of each field.

  • 110-138: The read_images function uses the client_image API client to identify products in images. Ensure that the model and response_model parameters are correctly configured and that the function properly handles the response from the API.

  • 141-159: The generate_ad_copy function uses the client_copy API client to generate ad copy for a product. The function should handle the response from the API and return an AdCopy instance. Verify that the model parameter is correctly set and that the temperature is appropriate for the desired creativity in the ad copy.

  • 162-182: The run function orchestrates the process of reading images and generating ad copy. It includes error handling and uses the IdentifiedProduct and AdCopy classes. Ensure that the error messages are clear and that the function returns the expected tuple of products and ad copies.

  • 185-222: The main execution block includes command-line argument handling, file operations, and function calls to process images and generate ad copy. Ensure that the file operations are robust and that exceptions are handled appropriately. Also, verify that the results.json file is written to the correct location and contains the expected output format.

  • 223-273: The example output JSON at the end of the file provides a clear example of the expected output structure. This is helpful for understanding the output format and for testing purposes. Ensure that the actual output matches this structure.

Comment on lines +1 to +19
import json
import logging
import os
import sys
from typing import List, Optional, Tuple

from dotenv import find_dotenv, load_dotenv
from openai import OpenAI
from pydantic import BaseModel, Field
from rich import print as rprint

import instructor

load_dotenv(find_dotenv())

# Add logger
logging.basicConfig()
logger = logging.getLogger("app")
logger.setLevel("INFO")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider grouping standard library imports (json, logging, os, sys) and separating them from third-party library imports (dotenv, openai, pydantic, rich) with a newline for better readability.

Comment on lines +101 to +107
# Define clients
client_image = instructor.patch(
OpenAI(api_key=os.getenv("OPENAI_API_KEY")), mode=instructor.Mode.MD_JSON
)
client_copy = instructor.patch(
OpenAI(api_key=os.getenv("OPENAI_API_KEY")), mode=instructor.Mode.FUNCTIONS
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Global variables client_image and client_copy are initialized with API clients. It's generally not recommended to use global variables for state management. Consider encapsulating these within a class or a function to avoid potential side effects and improve testability.

@lakshyaag lakshyaag requested a review from jxnl December 21, 2023 17:17
@jxnl
Copy link
Owner

jxnl commented Dec 21, 2023

the images don't look like i see a desert and water, not the products

@lakshyaag
Copy link
Contributor Author

lakshyaag commented Dec 21, 2023

the images don't look like i see a desert and water, not the products

Oh yes, I didn't upload the actual image links because I thought there would be some sort of licensing restrictions.

EDIT: I've updated the image links in the example. It should correspond with the results now.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 4

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 471cf88 and eefd24e.
Files selected for processing (1)
  • docs/examples/image_to_ad_copy.md (1 hunks)
Additional comments: 5
docs/examples/image_to_ad_copy.md (5)
  • 1-3: The introduction provides a clear overview of the document's purpose.

  • 16-45: The Product class is well-defined with clear documentation and a method to generate a prompt for the Chat API.

  • 79-92: The AdCopy class is clearly documented and structured, providing a good format for the generated advertising copy.

  • 141-159: The generate_ad_copy function is well-documented and seems to correctly utilize the Chat API to generate advertising copy.

  • 177-229: The output section provides a clear example of what the user can expect when running the code, which is helpful for understanding the functionality.


This post demonstrates how to use GPT-4 Vision API and the Chat API to automatically generate advertising copy from product images. This method can be useful for marketing and advertising teams, as well as for e-commerce platforms.

The full code is available on [GitHub](https://github.com/jxnl/instructor/examples/vision/image_to_ad_copy.py).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GitHub link is still incorrect and should be updated to point to the official repository path.

- [GitHub](https://github.com/jxnl/instructor/examples/vision/image_to_ad_copy.py)
+ [GitHub](https://github.com/<official-repo>/examples/vision/image_to_ad_copy.py)

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
The full code is available on [GitHub](https://github.com/jxnl/instructor/examples/vision/image_to_ad_copy.py).
The full code is available on [GitHub](https://github.com/<official-repo>/examples/vision/image_to_ad_copy.py).

Comment on lines +53 to +71
class IdentifiedProduct(BaseModel):
"""
Represents a list of products identified in the images.
"""

products: Optional[List[Product]] = Field(
description="A list of products identified by the AI.",
example=[
Product(
name="Headphones",
description="Wireless headphones with noise cancellation.",
key_features=["Wireless", "Noise Cancellation"],
)
],
default=None,
)

error: bool = Field(default=False)
message: Optional[str] = Field(default=None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IdentifiedProduct class is well-structured, but it's not clear why it has an error flag and message. Ensure that the error handling is consistent with the rest of the application.

Comment on lines +102 to +129
def read_images(image_urls: List[str]) -> IdentifiedProduct:
"""
Given a list of image URLs, identify the products in the images.
"""

logger.info(f"Identifying products in images... {len(image_urls)} images")

return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024, # can be changed
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type of the read_images function should be a list of IdentifiedProduct objects, as one function call may process multiple images.

- def read_images(image_urls: List[str]) -> IdentifiedProduct:
+ def read_images(image_urls: List[str]) -> List[IdentifiedProduct]:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def read_images(image_urls: List[str]) -> IdentifiedProduct:
"""
Given a list of image URLs, identify the products in the images.
"""
logger.info(f"Identifying products in images... {len(image_urls)} images")
return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024, # can be changed
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)
def read_images(image_urls: List[str]) -> List[IdentifiedProduct]:
"""
Given a list of image URLs, identify the products in the images.
"""
logger.info(f"Identifying products in images... {len(image_urls)} images")
return client_image.chat.completions.create(
model="gpt-4-vision-preview",
response_model=IdentifiedProduct,
max_tokens=1024, # can be changed
temperature=0,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify products using the given images and generate key features for each product.",
},
*[
{"type": "image_url", "image_url": {"url": url}}
for url in image_urls
],
],
}
],
)

Comment on lines +166 to +168
## Input file

The input file is currently a list of image URLs, but this trivial to change to any required format.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input file section remains brief. Providing a more detailed explanation or an example of the expected format would be beneficial for users.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between eefd24e and a159cda.
Files selected for processing (1)
  • docs/examples/image_to_ad_copy.md (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • docs/examples/image_to_ad_copy.md

@jxnl jxnl merged commit 89a6702 into jxnl:main Dec 21, 2023
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants