api_light_hf / apis /format2ecinfos.py
Renecto's picture
deploy api_light_hf (2026-03-12 12:47:03)
cf7f643
import os
from src.clients.llm_client import LLMClient
import json
import base64
from io import BytesIO
from PIL import Image
import re
from pydantic import BaseModel
import numpy as np
from enum import Enum
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 as _json
client = LLMClient()
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,
)
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,
)
"""
EC用の褁E��バリアント生成API�E�Eormat2ecinfo.pyに依存しなぁE��立実裁E��E
baseimg2ecinfo_rect.pyのpageInfo構造に準拠
"""
# スキーマ定義�E�Eormat2ecinfo.pyから独立!E
class Category(str, Enum):
ビジネス = "ビジネス�E�EaaS・法人支援�E�E
ヘルスケア = "ヘルスケア�E�美容・健康�E�E
ヒューマンリソース = "ヒューマンリソース�E�求人・紹介!E
コマ�Eス = "コマ�Eス�E�趣味・食品・衣類!E
ファイナンス = "ファイナンス�E���融�E保険・不動産�E�E
インフラ = "インフラ�E�電気�E通信・ガス・住屁E��E
ライフイベンチE= "ライフイベント(教育・結婚�E相諁E��E
class CategoryMiddle(str, Enum):
# ビジネス
ITソフトウェア = "IT・ソフトウェア"
マ�Eケ支援コンサル = "マ�Eケ支援・コンサル"
オフィス機器用品E= "オフィス・機器用品E
# ヘルスケア
健康食品器具 = "健康食品・器具"
美容医療クリニック = "美容・医療クリニック"
美容コスメ = "美容コスメ"
フィチE��ネスジム = "フィチE��ネスジム"
# ヒューマンリソース
求人惁E�� = "求人惁E��"
人材紹仁E= "人材紹仁E
人材派遣 = "人材派遣"
# コマ�Eス
動画アニメゲーム = "動画・アニメ・ゲーム"
リユースリサイクル = "リユース・リサイクル"
旁E���EチE��レジャー = "旁E���Eホテル・レジャー"
趣味交隁E= "趣味・交隁E
新聞雑誌メチE��ア = "新聞�E雑誌�E惁E��メチE��ア"
自動車レンタカー用品E= "自動車�Eレンタカー・用品E
飲料食品生活用品E= "飲料食品・生活用品E
家電パソコン = "家電・パソコン"
ファチE��ョン = "ファチE��ョン"
# ファイナンス
不動産 = "不動産"
保険 = "保険"
ローン = "ローン"
クレカ電子決渁E= "クレカ・電子決渁E
証券FX先物 = "証券・FX・先物"
銀衁E= "銀衁E
# インフラ
ネット通信サービス = "ネット�E通信サービス"
電気ガス = "電気�Eガス"
住宁E��備リフォーム = "住宁E��備�Eリフォーム"
# ライフイベンチE
士業相諁E= "士業・相諁E
学習スクール = "学習�Eスクール"
結婚�E会い = "結婚�E出会い"
葬儀墓地 = "葬儀・墓地"
引越し介護 = "引越し・介護"
class Meta(BaseModel):
会社吁E str
業畁E Category
中刁E��E CategoryMiddle
サービス: str
啁E��: str
タイトル: str
構�Eの意図: str
訴求テーチE list[str]
class cood(BaseModel):
x: int
y: int
class str_with_rect(BaseModel):
text: str
html: str
rect: list[cood]
class pageInfo(BaseModel):
# ペ�Eジ共送E
メタ: Meta
ロゴ: list[str_with_rect]
グローバル検索バ�E: list[str_with_rect]
ハンバ�Eガーメニューアイコン: list[str_with_rect]
カートアイコン: list[str_with_rect]
ユーザーメニュー: list[str_with_rect]
# ナビゲーション
ブレチE��クラム: list[str_with_rect]
ペ�Eジネ�Eション: list[str_with_rect]
タブ�E替: list[str_with_rect]
# トップ�Eージ
メインビジュアル: list[str_with_rect]
プロモーションバナー: list[str_with_rect]
カチE��リカーチE list[str_with_rect]
# 啁E��一覧ペ�Eジ
啁E��一覧: list[str_with_rect]
フィルタ: list[str_with_rect]
ソーチE list[str_with_rect]
ペ�Eジャー: list[str_with_rect]
クイチE��ビューアイコン: list[str_with_rect]
# 啁E��詳細ペ�Eジ
啁E��吁E list[str_with_rect]
価格: list[str_with_rect]
ブランチE list[str_with_rect]
サムネイル: list[str_with_rect]
画像ギャラリー: list[str_with_rect]
カラースウォチE��: list[str_with_rect]
サイズセレクタ: list[str_with_rect]
在庫スチE�Eタス: list[str_with_rect]
配送情報: list[str_with_rect]
ボタン_カート追加: list[str_with_rect]
ボタン_今すぐ購入: list[str_with_rect]
レビューサマリー: list[str_with_rect]
レビューボタン: list[str_with_rect]
QnAリンク: list[str_with_rect]
バッジタグ: list[str_with_rect]
関連啁E��カルーセル: list[str_with_rect]
# カート�Eージ
カート商品リスチE list[str_with_rect]
数量セレクタ: list[str_with_rect]
削除アイコン: list[str_with_rect]
クーポン入劁E list[str_with_rect]
注斁E��計サマリー: list[str_with_rect]
チェチE��アウト�Eタン: list[str_with_rect]
# 共通下部
フッターリンク: list[str_with_rect]
SNSアイコン: list[str_with_rect]
カスタマ�Eサポ�Eトリンク: list[str_with_rect]
def get_openai_request(messages, format, n=1):
"""
OpenAI API呼び出し!Eパラメータ対応、常にリストを返す�E�E
Args:
messages: メチE��ージリスチE
format: レスポンスフォーマッチE
n: 生�Eする候補数�E�デフォルチE 1�E�E
Returns:
list[str]: 常にリストで返却�E�E=1でも長ぁEのリスト!E
"""
client = LLMClient())
response = _ask_raw_hf([{"role":"user","content":p}], model,
model="meta-llama/Llama-3.3-70B-Instruct",
messages=messages,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
response_format=format,
temperature=0,
n=n
)
# 常にリストで返す�E�E=1でも統一�E�E
return [choice.message.content for choice in response.choices]
@customtracer
def format2ecinfos(p, openai_key=os.environ.get('OPENAI_KEY'), n=1):
"""
input1 (text): プロンプト�E�Eormat2ecinfoと同様�E形式!E
input2 (text): default
input3 (number): 1
output1 (json): pageInfo形式�EJSONの配�E
"""
print(datetime.now(pytz.timezone('Asia/Tokyo')).strftime("%Y-%m-%d %H:%M:%S"), __name__, f"n={n}")
if openai_key == "default" or not openai_key:
openai_key = os.environ.get('OPENAI_KEY', '')
if openai_key:
os.environ['OPENAI_API_KEY'] = openai_key
# n を整数に変換し、篁E��チェチE��
try:
n = int(n)
if n < 1:
print(f"Warning: n={n} is invalid, using n=1")
n = 1
elif n > 10:
print(f"Warning: n={n} is too large, capping at 10")
n = 10
except (TypeError, ValueError):
print(f"Warning: n={n} is invalid, using n=1")
n = 1
messages=[
{
"role": "system",
"content": """提供したフォーマットデータから、ECサイト向け�Eペ�Eジ惁E��を生成してください。baseimg2ecinfo_rect.pyのpageInfo構造に準拠し、ECサイト�E特性�E�商品比輁E��カチE��リ一覧、賁E��請求など�E�を老E�Eして、E��刁E��要素を生成してください。各要素はstr_with_rect形式!Eext, html, rect�E�で記述してください、E"",
},
{
"role": "user",
"content": [{"type": "text", "text":p}]
},
]
# get_openai_requestは常にリストを返すので、そのまま使用
result = get_openai_request(messages, pageInfo, n=n)
print(f"Generated {len(result)} EC variants")
# リストをJSON斁E���Eとして返す
#return json.dumps(result, ensure_ascii=False)
return result