api_light_hf / apis /html2variants.py
Renecto's picture
deploy api_light_hf (2026-03-12 12:47:03)
cf7f643
"""
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=[])