api_light_hf / apis /base64img2component.py
Renecto's picture
deploy api_light_hf (2026-03-12 12:47:03)
cf7f643
from openai import os
from src.clients.llm_client import LLMClient
import json
import pandas as pd
from pydantic import BaseModel
from enum import Enum
import base64
from io import BytesIO
from PIL import Image
from functools import cache
from datetime import datetime
import pytz
from src.utils.tracer import customtracer
def _ask_raw_hf(messages, model, response_format=None):
"""Compatibility wrapper: routes OpenAI-style messages through HF LLMClient."""
from src.clients.llm_client import LLMClient
import json, re
client = LLMClient()
# Extract system prompt and user content from messages list
system_prompt = None
user_text = ""
images = []
for msg in messages:
role = msg.get("role", "")
c = msg.get("content", "")
if role == "system":
if isinstance(c, str):
system_prompt = c
elif role == "user":
if isinstance(c, str):
user_text = c
elif isinstance(c, list):
for part in c:
if isinstance(part, dict):
if part.get("type") == "text":
user_text += part.get("text", "")
elif part.get("type") == "image_url":
url = part.get("image_url", {}).get("url", "")
if url.startswith("data:"):
images.append(url.split(",", 1)[1] if "," in url else url)
else:
images.append(url)
if response_format is not None and hasattr(response_format, "model_json_schema"):
result = client.call(
prompt=user_text,
schema=response_format,
model=model,
system_prompt=system_prompt,
images=images if images else None,
temperature=0,
)
import json
return json.dumps(result.model_dump(), ensure_ascii=False)
else:
return client.call_raw(
prompt=user_text,
model=model,
system_prompt=system_prompt,
images=images if images else None,
)
class UIoption(str, Enum):
element1 = "バナー/動画"
element2 = "CTA"
element3 = "チE��スチE
element4 = "フォーム"
class Component(BaseModel):
component_large: str
component_middle: str
component_small: list[str]
UIelement: UIoption
class Components(BaseModel):
components: list[Component]
def ask_raw(messages):
client = LLMClient()
# HF: beta.parse not available; use _ask_raw_hf instead
response = client.chat.completions.create(
model='meta-llama/Llama-3.3-70B-Instruct',
messages=messages,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
response_format=Components,
temperature=0
)
return response.choices[0].message.content
@customtracer
def base64img2component(p, image64, openai_key=os.environ.get('OPENAI_KEY')):
"""
input1 (text): 13: ※金融犯罪にご注愁E手口はこちら、E38: ▼ご利用条件はこちら、E77: ピンチ�E時�E、E133: アコム一抁E409: WEB完結カードを作らぁE415: ご契紁E�E翌日から最大30日間��利0冁E421: 借りられめE0刁E�� 644: 今すぐお申し込み 722: 実質年玁E3.0%~18.0%ご融賁E��E1丁E�E~800丁E�E 760: 以前ご利用があっぁE761: ご増額をご希望のお客さまはこちめE784: お客さまはこちめE819: *お申し込み時間めE��査によりご希望に沿えなぁE��合がござぁE��す、E868: お借�E可能かすぐに刁E��めE秒スピ�Eド診断 977: 侁E22 1055: ご年叁E税込) 1067: 侁E250 1146: 他社お借�E顁E1249: 診断開姁E1323: ※クレジチE��カードでのショチE��ング、E��行でのお借�E(銀行カードローン、住宁E��ーン、�E動車ローンなど)を除ぁE��、キャチE��ングめE��ードローンのお借�E状況をご�E力ください、E1498: 借りるなめE1558: アコム一抁E1710: 20刁E��借りられめE1835: アコムなら最短20刁E��お借�Eが可能!※すぐにお��が忁E��とぁE��時�E、本ペ�Eジの申込ボタンから早速お申し込みくだ 1960: ※お申し込み時間めE��査によりご希望に添えなぁE��合がござぁE��す、E2045: カードを作らずWEB完絁E2165: お申し込み〜お借�EまでWEBだけで完結できます。ご希望ぁE��だければカードレスでご契紁E��ただけます、E2354: |30日間��利ぁE冁E2356: 契紁E�E翌日から 2470: はじめてご利用のお客さまは、契紁E�E翌日から最大30日間��利ぁE冁E 2663: たっぁEスチE��チE!(最短20刁E 2757: 申し込みから借りるまでの流れ※お申し込み時間めE��査によりご希望に添えなぁE��合がござぁE��す、E2937: お申し込み・1忁E��書類提出(審査)お申し込みぁE��だぁE��後、忁E��書類を提�EしてぁE��だき審査に進みます、E3131: 2ご契紁E�Eお借�E 3194: 審査結果の冁E��にご同意いただけましたら、契紁E��続きは完亁E��なります。契紁E���E、すぐにお借�EぁE��だけます。ご希望ぁE��だければカードレスでご契紁E��ただけます、E3335: 忁E��書類とは? 3405: 本人確認書顁E免許証など) 3455: (該当する方のみ)+収�E証明書 3488: ※「当社のご利用において50丁E�Eを趁E��るご契紁E��行うお客さま」と「他社を含めたお借�E総額が100丁E�Eを趁E��るお客「さま」につぁE��は、収入証明書も忁E��で 3633: アコムの 3664: よくある質啁E3777: 申し込み編 3892: Q勤務�Eに在籍確認�E電話がかかってきま 3961: 原則、実施しません。※原則、E��話での在籍確認�Eせずに書面めE��申告�E容での確認を実施します。もし実施が忁E��となる場合でも、お客さまの同意を得ずに実施することはありませんので、ご安忁E��ださい、E4135: Q契紁E��ると、忁E��カードが自宁E��郵送さ 4159: れるんですか? 4205: ぁE��え。カードレスでご契紁E��続きぁE��だくことも可能です、E4296: 自宁E��勤務�Eに何か書類が送られてくる 4320: ことはありますか? 4366: 原則、E��付しません、E郵送契紁E��選択された場合や、書面の郵送受け取りを選 4418: んだ場合等を除ぁE 5914: は、ご返済シミュレーションをご利用ぁE5943: ださい、E5992: ペ�Eジ上部に戻る▲ 7671: ご増額をご希望のお客さまはこちめE7671: 以前ご利用があったお客さまはこちめE8033: 今すぐお申し込み
input2 (text): スクショ
input3 (text): default
output1 (json): 頁E��
"""
print(datetime.now(pytz.timezone('Asia/Tokyo')).strftime("%Y-%m-%d %H:%M:%S"), f"base64img2component:", image64[0:30])
if openai_key == "default":
os.environ['OPENAI_API_KEY'] = os.environ.get('OPENAI_KEY')
else:
os.environ['OPENAI_API_KEY'] = openai_key
messages=[
{
"role": "system",
"content": """
■構�E要素名�Eアウト�EチE��サンプル
[
{"component_large":"啁E��/サービスの特徴","component_middle":"アコム", "component_small":[], "UIelement":"チE��スチE},
{"component_large":"FAQ/よくある質啁E,"component_middle":"よくあるご質啁E, "component_small":["自宁E��勤務�Eに何か書類が送られてくることはありますか�E�E,"家族割などの割引�Eありますか�E�E], "UIelement":"表絁E��"}
]
"""
},
{
"role": "user",
"content": [{"type": "text", "text":p}]
},
]
messages[1]["content"].insert(0, {"type": "image_url", "image_url": {"url":"data:image/png;base64,"+image64}})
# OpenAI 側の認証エラーなどを�E示皁E��メチE��ージとして上位に伝搬させめE
try:
return ask_raw(messages)
except openai.AuthenticationError as e:
# API キー / 絁E��設定�E問題を含むエラー冁E��をラチE�Eして投げ直ぁE
# 呼び出し�E�E�EE Origin 側など�E�でこ�EメチE��ージをキャチE��してユーザに表示できる
raise RuntimeError(f"[base64img2component] OpenAI AuthenticationError: {e}") from e