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