Skip to content

Commit 520cf4b

Browse files
jekalminjekalmin
jekalmin
authored andcommitted
[#140] Make local image to be attached in query_image service
1 parent 1b20b56 commit 520cf4b

File tree

1 file changed

+40
-4
lines changed
  • custom_components/extended_openai_conversation

1 file changed

+40
-4
lines changed

custom_components/extended_openai_conversation/services.py

+40-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
import base64
12
import logging
3+
import mimetypes
4+
from pathlib import Path
5+
from urllib.parse import urlparse
26

3-
import voluptuous as vol
47
from openai import AsyncOpenAI
58
from openai._exceptions import OpenAIError
9+
from openai.types.chat.chat_completion_content_part_image_param import (
10+
ChatCompletionContentPartImageParam,
11+
)
12+
import voluptuous as vol
613

714
from homeassistant.core import (
815
HomeAssistant,
@@ -11,8 +18,8 @@
1118
SupportsResponse,
1219
)
1320
from homeassistant.exceptions import HomeAssistantError
21+
from homeassistant.helpers import config_validation as cv, selector
1422
from homeassistant.helpers.typing import ConfigType
15-
from homeassistant.helpers import selector, config_validation as cv
1623

1724
from .const import DOMAIN, SERVICE_QUERY_IMAGE
1825

@@ -25,7 +32,7 @@
2532
),
2633
vol.Required("model", default="gpt-4-vision-preview"): cv.string,
2734
vol.Required("prompt"): cv.string,
28-
vol.Required("images"): vol.All(cv.ensure_list, [{"url": cv.url}]),
35+
vol.Required("images"): vol.All(cv.ensure_list, [{"url": cv.string}]),
2936
vol.Optional("max_tokens", default=300): cv.positive_int,
3037
}
3138
)
@@ -41,7 +48,7 @@ async def query_image(call: ServiceCall) -> ServiceResponse:
4148
try:
4249
model = call.data["model"]
4350
images = [
44-
{"type": "image_url", "image_url": image}
51+
{"type": "image_url", "image_url": to_image_param(hass, image)}
4552
for image in call.data["images"]
4653
]
4754

@@ -74,3 +81,32 @@ async def query_image(call: ServiceCall) -> ServiceResponse:
7481
schema=QUERY_IMAGE_SCHEMA,
7582
supports_response=SupportsResponse.ONLY,
7683
)
84+
85+
86+
def to_image_param(hass: HomeAssistant, image) -> ChatCompletionContentPartImageParam:
87+
"""Convert url to base64 encoded image if local."""
88+
url = image["url"]
89+
90+
if urlparse(url).scheme in cv.EXTERNAL_URL_PROTOCOL_SCHEMA_LIST:
91+
return image
92+
93+
if not hass.config.is_allowed_path(url):
94+
raise HomeAssistantError(
95+
f"Cannot read `{url}`, no access to path; "
96+
"`allowlist_external_dirs` may need to be adjusted in "
97+
"`configuration.yaml`"
98+
)
99+
if not Path(url).exists():
100+
raise HomeAssistantError(f"`{url}` does not exist")
101+
mime_type, _ = mimetypes.guess_type(url)
102+
if mime_type is None or not mime_type.startswith("image"):
103+
raise HomeAssistantError(f"`{url}` is not an image")
104+
105+
image["url"] = f"data:{mime_type};base64,{encode_image(url)}"
106+
return image
107+
108+
109+
def encode_image(image_path):
110+
"""Convert to base64 encoded image."""
111+
with open(image_path, "rb") as image_file:
112+
return base64.b64encode(image_file.read()).decode("utf-8")

0 commit comments

Comments
 (0)