File size: 10,131 Bytes
cf7f643
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
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