File size: 6,230 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
"""
HTMLバリアント生成API
允E�EHTMLと変更点を受け取り、Eつの新しいHTMLを提案すめE
"""

import os
from src.clients.llm_client import LLMClient
import json
import re
from typing import List
from pydantic import BaseModel, Field
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,
        )



class HtmlVariant(BaseModel):
    html : str
    description: str
    changes: list[str]

class HtmlVariantsResponse(BaseModel):
    variants : list[HtmlVariant]
    
def get_openai_request(messages, format, openai_key):
    """OpenAI APIを呼び出ぁ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.7  # バリエーションを�Eすため少し高めに設宁E
    )
    return response


@customtracer
def html2variants(original_html: str, change_points: str, openai_key=os.environ.get('OPENAI_KEY')):
    """
    input1 (text): <h1>title</h1>
    input2 (text): タイトルに下線を表示
    input3 (text): default
    output1 (json): html
    """
    print(datetime.now(pytz.timezone('Asia/Tokyo')).strftime("%Y-%m-%d %H:%M:%S"), __name__)
    
    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
    
    # HTMLの構造を簡潔に要紁E��長すぎる場合�E要紁E��E
    html_summary = original_html[:5000]  # 最初�E5000斁E��を使用
    if len(original_html) > 5000:
        html_summary += "\n\n[以下省略...]"
    
    # プロンプトを構篁E
    prompt = f"""以下�E允E�EHTMLと変更点の説明を基に、指定数のHTMLバリアントを生�Eしてください、E

【�EのHTML、E
{html_summary}

【変更点の説明、E
{change_points}

【要件、E
1. 允E�EHTMLの構造とスタイルを可能な限り維持すめE
2. 変更点の説明に基づぁE��、指定数のHTMLバリアントを生�Eする
3. バリアント�E完�EなHTMLドキュメントとして返す�E�E!DOCTYPE html>から</html>まで�E�E
4. 画像パス、CSSパス、JavaScriptパスなどは允E�EHTMLからそ�Eまま維持すめE
5. 変更するのは主にチE��ストコンチE��チE��、その周辺のチE��イン�E�フォントサイズ、色、レイアウト、余白、スタイルなど�E�でぁE
6. 変更点周辺のチE��インを変更することで、より効果的な表現を実現してください
7. ただし、変更点以外�E部刁E��ロゴ、ナビゲーション、フチE��ー、その他�Eセクション�E��EチE��インは維持してください
8. 允E�EHTMLのすべての要素�E�ESS、画像、JavaScript、メタタグなど�E�を完�Eに保持する

【�E力形式、E
- variants: 持E��数のHtmlVariantオブジェクト�EリスチE
- HtmlVariantには以下を含める:
  - html: 完�EなHTMLドキュメンチE
  - description: こ�Eバリアント�E説明(変更点の要紁E��E0斁E��程度�E�E
  - changes: 具体的な変更点のリスト(各頁E��は30斁E��程度�E�E
"""

    messages = [
        {
            "role": "system",
            "content": """あなた�EHTMLの専門家です。�EのHTMLと変更点の説明を基に、指定数のHTMLバリアントを生�Eしてください、E
バリアント�E完�EなHTMLドキュメントとして返し、�EのHTMLの構造とスタイルを可能な限り維持しながら、変更点を反映してください、E
変更点周辺のチE��イン�E�フォントサイズ、色、レイアウト、余白、スタイルなど�E�も変更可能です、E"",
        },
        {
            "role": "user",
            "content": prompt
        },
    ]
    
    try:
        result = get_openai_request(messages, HtmlVariantsResponse, openai_key)
        return result
    except Exception as e:
        print(f"[html2variants] エラー: {e}")
        import traceback
        print(traceback.format_exc())
        # エラー時�E空のレスポンスを返す
        return HtmlVariantsResponse(variants=[])