Spaces:
Sleeping
Sleeping
Update ui.py
Browse files
ui.py
CHANGED
|
@@ -22,13 +22,16 @@ from app.storage import (
|
|
| 22 |
from app.bandit import ThompsonBandit
|
| 23 |
from app.forecast import SeasonalityModel
|
| 24 |
from app.compliance import rule_based_check, llm_check_and_fix
|
| 25 |
-
from app.openai_client import openai_chat_json
|
| 26 |
-
|
| 27 |
|
| 28 |
# 初期化
|
| 29 |
init_db()
|
| 30 |
_seasonality_cache: Dict[str, SeasonalityModel] = {}
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
# JSONモード前提の厳格プロンプト
|
| 33 |
GEN_SYSTEM = """
|
| 34 |
あなたは日本語広告コピーのプロフェッショナルコピーライターです。
|
|
@@ -88,7 +91,6 @@ async def ui_generate(campaign_id: str, brand: str, product: str, target: str, t
|
|
| 88 |
k=k_variants,
|
| 89 |
)
|
| 90 |
|
| 91 |
-
# ← JSONモードを使用
|
| 92 |
data = await openai_chat_json(
|
| 93 |
[
|
| 94 |
{"role": "system", "content": GEN_SYSTEM},
|
|
@@ -98,13 +100,9 @@ async def ui_generate(campaign_id: str, brand: str, product: str, target: str, t
|
|
| 98 |
max_tokens=1200,
|
| 99 |
)
|
| 100 |
|
| 101 |
-
# 許容: {"variants":[...] } もしくはリストそのもの(保険)
|
| 102 |
items = data.get("variants", data if isinstance(data, list) else [])
|
| 103 |
-
if not isinstance(items, list) or not items:
|
| 104 |
-
raise gr.Error("JSON応答の 'variants' が取得できませんでした。もう一度お試しください。")
|
| 105 |
-
|
| 106 |
rows = []
|
| 107 |
-
for it in items[:k_variants]:
|
| 108 |
headline = (it.get("headline") or "").strip()
|
| 109 |
body = (it.get("body") or "").strip()
|
| 110 |
text = f"{headline}\n{body}".strip()
|
|
@@ -134,7 +132,8 @@ async def ui_generate(campaign_id: str, brand: str, product: str, target: str, t
|
|
| 134 |
"text": text,
|
| 135 |
})
|
| 136 |
|
| 137 |
-
|
|
|
|
| 138 |
return df
|
| 139 |
|
| 140 |
def ui_serve(campaign_id: str, hour: int, segment: str):
|
|
@@ -180,7 +179,8 @@ def ui_report(campaign_id: str):
|
|
| 180 |
"cvr": round(cvr, 4),
|
| 181 |
"expected_value": round(ev, 6),
|
| 182 |
})
|
| 183 |
-
|
|
|
|
| 184 |
return df
|
| 185 |
|
| 186 |
def ui_check(text: str):
|
|
@@ -211,7 +211,7 @@ with gr.Blocks(title="AdCopy MAB Optimizer", fill_height=True) as demo:
|
|
| 211 |
tone = gr.Textbox(label="トーン", value="エビデンス重視で安心感")
|
| 212 |
ng_words = gr.Textbox(label="NGワード(改行区切り)", value="治る\n奇跡")
|
| 213 |
btn_gen = gr.Button("広告案を生成&審査&保存")
|
| 214 |
-
table_gen = gr.Dataframe(headers=
|
| 215 |
btn_gen.click(ui_generate, [campaign_id, brand, product, target, tone, k_variants, ng_words, value_per_conv], [table_gen])
|
| 216 |
|
| 217 |
with gr.Tab("2) Serve & Feedback"):
|
|
@@ -234,7 +234,7 @@ with gr.Blocks(title="AdCopy MAB Optimizer", fill_height=True) as demo:
|
|
| 234 |
with gr.Tab("3) Report"):
|
| 235 |
campaign_id3 = gr.Textbox(label="campaign_id", value="cmp-demo")
|
| 236 |
btn_rep = gr.Button("更新")
|
| 237 |
-
table_rep = gr.Dataframe(headers=
|
| 238 |
btn_rep.click(ui_report, [campaign_id3], [table_rep])
|
| 239 |
|
| 240 |
with gr.Tab("4) Compliance Check"):
|
|
|
|
| 22 |
from app.bandit import ThompsonBandit
|
| 23 |
from app.forecast import SeasonalityModel
|
| 24 |
from app.compliance import rule_based_check, llm_check_and_fix
|
| 25 |
+
from app.openai_client import openai_chat_json
|
|
|
|
| 26 |
|
| 27 |
# 初期化
|
| 28 |
init_db()
|
| 29 |
_seasonality_cache: Dict[str, SeasonalityModel] = {}
|
| 30 |
|
| 31 |
+
# 固定カラム(常にこの形で返す)
|
| 32 |
+
GENERATE_COLUMNS = ["variant_id", "status", "rejection_reason", "text"]
|
| 33 |
+
REPORT_COLUMNS = ["variant_id","impressions","clicks","conversions","ctr","cvr","expected_value"]
|
| 34 |
+
|
| 35 |
# JSONモード前提の厳格プロンプト
|
| 36 |
GEN_SYSTEM = """
|
| 37 |
あなたは日本語広告コピーのプロフェッショナルコピーライターです。
|
|
|
|
| 91 |
k=k_variants,
|
| 92 |
)
|
| 93 |
|
|
|
|
| 94 |
data = await openai_chat_json(
|
| 95 |
[
|
| 96 |
{"role": "system", "content": GEN_SYSTEM},
|
|
|
|
| 100 |
max_tokens=1200,
|
| 101 |
)
|
| 102 |
|
|
|
|
| 103 |
items = data.get("variants", data if isinstance(data, list) else [])
|
|
|
|
|
|
|
|
|
|
| 104 |
rows = []
|
| 105 |
+
for it in (items[:k_variants] if isinstance(items, list) else []):
|
| 106 |
headline = (it.get("headline") or "").strip()
|
| 107 |
body = (it.get("body") or "").strip()
|
| 108 |
text = f"{headline}\n{body}".strip()
|
|
|
|
| 132 |
"text": text,
|
| 133 |
})
|
| 134 |
|
| 135 |
+
# 常に固定カラムで返す(空でもカラムを持つDataFrame)
|
| 136 |
+
df = pd.DataFrame(rows, columns=GENERATE_COLUMNS)
|
| 137 |
return df
|
| 138 |
|
| 139 |
def ui_serve(campaign_id: str, hour: int, segment: str):
|
|
|
|
| 179 |
"cvr": round(cvr, 4),
|
| 180 |
"expected_value": round(ev, 6),
|
| 181 |
})
|
| 182 |
+
# 常に固定カラムで返す(空でもカラムを持つDataFrame)
|
| 183 |
+
df = pd.DataFrame(rows, columns=REPORT_COLUMNS)
|
| 184 |
return df
|
| 185 |
|
| 186 |
def ui_check(text: str):
|
|
|
|
| 211 |
tone = gr.Textbox(label="トーン", value="エビデンス重視で安心感")
|
| 212 |
ng_words = gr.Textbox(label="NGワード(改行区切り)", value="治る\n奇跡")
|
| 213 |
btn_gen = gr.Button("広告案を生成&審査&保存")
|
| 214 |
+
table_gen = gr.Dataframe(headers=GENERATE_COLUMNS, interactive=False)
|
| 215 |
btn_gen.click(ui_generate, [campaign_id, brand, product, target, tone, k_variants, ng_words, value_per_conv], [table_gen])
|
| 216 |
|
| 217 |
with gr.Tab("2) Serve & Feedback"):
|
|
|
|
| 234 |
with gr.Tab("3) Report"):
|
| 235 |
campaign_id3 = gr.Textbox(label="campaign_id", value="cmp-demo")
|
| 236 |
btn_rep = gr.Button("更新")
|
| 237 |
+
table_rep = gr.Dataframe(headers=REPORT_COLUMNS, interactive=False)
|
| 238 |
btn_rep.click(ui_report, [campaign_id3], [table_rep])
|
| 239 |
|
| 240 |
with gr.Tab("4) Compliance Check"):
|