T-579 内置默认AI提示词
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
"""Bundled default prompt resources."""
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Bundled default cover prompt templates."""
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
商品标题:{新标题}
|
||||||
|
【核心執行指令:韓系極簡大碼電商主圖】
|
||||||
|
|
||||||
|
1. 畫面風格與氛圍 (視覺基調)
|
||||||
|
|
||||||
|
場景化生活感: 捨棄死板的工廠掛拍或雜亂背景,全面升級為「韓系生活氛圍感」的實穿場景。採用高級「韓系極簡風」,以奶茶色調、莫蘭迪色系、鼠尾草綠等低飽和色彩為主。搭配明亮柔和的自然光影,營造輕鬆的日常氛圍(如質感街道、室內一隅)。
|
||||||
|
|
||||||
|
情緒價值: 營造讓買家在 1 秒內產生共鳴的畫面,傳遞「這件衣服適合我、穿起來修身又優雅」的視覺感受。
|
||||||
|
|
||||||
|
2. 模特兒設定與人體結構防錯 (重點規範)
|
||||||
|
|
||||||
|
精準人體結構 (防錯機制): 【最高權重】確保人體骨骼與四肢結構 100% 符合真實人類解剖學。嚴格禁止生成多餘肢體(如三隻手、三條腿、多根手指)、禁止關節扭曲、肢體融合或殘缺。手部抓握物品(如持咖啡杯)的姿勢必須極度自然逼真。
|
||||||
|
|
||||||
|
目標受眾樣貌: 模特兒限定為 30 歲以上及樂齡世代、具備台灣女性氣質的微胖/大碼身形(能完美展現大至 80 公斤身形的自信與修身美感)。
|
||||||
|
|
||||||
|
動態與多樣性: 展現自然微笑與自信優雅姿態,透過不經意的動態(如轉身、微側身)展現衣服垂墜感。臉部樣貌需具備多樣性,絕對避免每次生成同一張臉。
|
||||||
|
|
||||||
|
3. 產品還原與細節保留 (商品核心)
|
||||||
|
|
||||||
|
100% 實物還原: 完美保留原圖衣服的款式、材質與設計亮點(如紫色螺紋花朵刺繡、新中式盤扣、不規則下擺、藍色暈染字母、V領抽繩褶皺等),禁止擅自改變服裝結構,並承襲原圖的靈魂單品搭配(如小棕包、手鐲、項鍊等配件)。
|
||||||
|
|
||||||
|
版面佔比: 服裝必須佔據整體版面 60% 以上。
|
||||||
|
|
||||||
|
4. 排版、文案與浮水印 (後製規範)
|
||||||
|
|
||||||
|
極簡文案: 畫面保持 90% 以上的純淨度。需要添加核心賣點文字,可以根據對應標題獲取核心詞,但絕對不可遮擋服裝細節。
|
||||||
|
|
||||||
|
隱蔽防盜浮水印: 強制在圖片「正左上角」添加「PAPA」浮水印。字體總寬度嚴格限制在圖片總寬度的 5% 以內,透明度絕對鎖定在 3%。確保其極度低調、無侵入性,絕不破壞 4K 高清細節與畫面留白美感。
|
||||||
|
|
||||||
|
5. 輸出與交付標準 (系統設定)
|
||||||
|
|
||||||
|
免選方案直接輸出: 未來的對話中,接收商品圖與標題後,請自動篩選並「直接輸出」最具點擊率潛力的唯一優化主圖,無需提供多種方案挑選。
|
||||||
|
|
||||||
|
規格限制: 輸出為 4K 高清畫質的 JPEG 格式,比例 1:1(建議解析度 1024x1024 或以上),且單張檔案大小不超過 2MB。
|
||||||
|
|
||||||
|
6.圖片展示【台灣出貨】等時效性詞,请设计一个突出,吸睛的标签。
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
旧标题:{旧标题}
|
||||||
|
蝦皮台灣站 商品標題優化標準(最終梳理版)
|
||||||
|
版本:2026.05 最終優化版
|
||||||
|
適用平台:蝦皮台灣站(Shopee TW)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
一、核心結構(強制執行)
|
||||||
|
================================================================================
|
||||||
|
• 標題開頭固定為:【台灣出貨】
|
||||||
|
• 開頭第二個詞必須是該商品「最核心商品賣點的搜索關鍵詞」
|
||||||
|
• 關鍵詞需符合台灣用戶搜尋習慣,並自然融入商品賣點與風格,痛點解決,受眾與場景,實時熱銷利益詞
|
||||||
|
• 總字數嚴格控制在 54~60 個字符(含中英文、空格、【】、標點),不可超過 60 字符
|
||||||
|
•标题中包含有大码。大尺码等大码女装相关的关键词,增加标题中大码女装的相关关键词密度。
|
||||||
|
•标题中包含有小個子。矮個子等小個子女装相关的关键词,增加标题中小個子穿搭的相关关键词密度。
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
二、絕對禁止事項
|
||||||
|
================================================================================
|
||||||
|
• 禁止使用任何時效承諾詞(現貨、快速出貨、秒發、24H出貨、隔日達等),除非原標題已出現
|
||||||
|
• 禁止出現店名或店鋪相關詞,所有的品牌標籤都是全面禁止的,禁止出現任何品牌名稱
|
||||||
|
• 原標題未出現材質時,嚴禁加入任何材質名詞
|
||||||
|
• 原標題未出現大碼/plus size相關詞時,嚴禁添加大尺碼描述
|
||||||
|
• 禁止醫療、治療、療效相關詞(詳見下方特殊處理)
|
||||||
|
• 請去掉如新港棉,雲朵棉,40支純棉等不會有用戶搜素的材質詞,增加核心指令請確保標題中的所有關鍵詞都是符合用戶搜素習慣,搜素需求的關鍵詞。
|
||||||
|
================================================================================
|
||||||
|
三、高價值詞保留與加入原則
|
||||||
|
================================================================================
|
||||||
|
• 必須保留原標題中的風格詞、版型詞(如:重工刺繡、文藝復古、古典復古、A字裙、大擺裙、寬鬆顯瘦、辣妹風、純欲風等)
|
||||||
|
• 優先加入季節與轉換詞(服飾類):冰絲涼感、抗熱消暑、透氣涼感、親膚舒適、輕薄無負擔
|
||||||
|
• 其他品類依商品特性加入對應利益詞與熱銷類目詞
|
||||||
|
• 服飾類風格需動態匹配原標題:
|
||||||
|
- 原標題偏辣妹/純欲 → 強調設計感、性感氣質、顯瘦曲線
|
||||||
|
- 原標題偏法式/名媛 → 強調優雅、高質感、知性氣質
|
||||||
|
- 原標題偏文藝/復古/媽媽裝 → 強調復古知性、寬鬆顯瘦、實用舒適
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
四、輸出格式 (不要展示思考過程,直接出結果)
|
||||||
|
================================================================================
|
||||||
|
單一標題:
|
||||||
|
直接輸出新標題(只輸出優化後標題)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
輸出範例
|
||||||
|
================================================================================
|
||||||
|
單一標題範例:
|
||||||
|
【台灣出貨】新中式掛鐘 古典復古 簡約現代 繁體漢字 客廳餐廳 個性藝術創意 靜音時鐘 客廳裝飾 藝術時鐘
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
================================================================================
|
||||||
|
五、其他注意事項
|
||||||
|
================================================================================
|
||||||
|
• 相似商品批量生成時,修飾詞重複度需低於 50%(建議輪替同義詞)
|
||||||
|
• 所有標題必須使用繁體中文
|
||||||
|
• 嚴格遵守字數限制(建議目標控制在 55~58 字符,留緩衝空間)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
六、時效詞處理原則(重要補充)
|
||||||
|
================================================================================
|
||||||
|
• 原標題「有」時效詞 → 新標題必須精準保留
|
||||||
|
• 原標題「沒有」時效詞 → 新標題嚴禁添加任何時效承諾詞
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
七、醫療與銀髮族商品特殊處理
|
||||||
|
================================================================================
|
||||||
|
嚴禁使用詞彙:
|
||||||
|
醫療、醫用、醫生推薦、臨床、治療、療效、復健、關節炎、靜脈曲張、腰椎間盤突出、止痛、消炎、殺菌、防褥瘡、降血壓、醫療級、復健車、血氧機、血壓計、助聽器、矯正帶
|
||||||
|
|
||||||
|
銀髮族/樂齡商品安全替代詞建議:
|
||||||
|
• 復健購物車 → 家用休具、休閒買菜車、輕便購物車
|
||||||
|
• 矯正坐墊 → 減壓坐墊、人體工學靠枕、久坐舒適款
|
||||||
|
• 按摩治療儀 → 居家舒壓按摩、放鬆神器、日常舒適好物
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
八、符合台灣用戶搜索習慣的實戰優化建議
|
||||||
|
================================================================================
|
||||||
|
1. 核心關鍵詞選擇優先使用平台即時建議
|
||||||
|
在蝦皮搜尋欄輸入商品類型,觀察自動跳出的熱門詞,優先選用搜尋量高的詞放在「【台灣出貨】」之後第二位。
|
||||||
|
|
||||||
|
2. 利益導向優先
|
||||||
|
台灣用戶特別愛搜:顯瘦、遮肉、寬鬆舒適、親膚、透氣涼感、百搭、日常穿搭、高CP值、輕薄無負擔。
|
||||||
|
建議在核心關鍵詞後盡量放這些「為什麼要買」的利益詞。
|
||||||
|
|
||||||
|
3. 自然流暢可讀(手機優先)
|
||||||
|
標題要像自然中文一樣好讀,手機上掃一眼就能懂。避免硬塞關鍵詞造成斷裂感。
|
||||||
|
|
||||||
|
4. 同義詞輪替策略(批量專用)
|
||||||
|
透氣涼感 / 親膚舒適 / 輕薄透氣 / 抗熱消暑 / 舒適無負擔
|
||||||
|
顯瘦 / 遮肉顯瘦 / 寬鬆顯瘦 / 版型優良
|
||||||
|
這樣可大幅降低重複度,同時維持高相關性。
|
||||||
|
|
||||||
|
5. 強化使用場景詞
|
||||||
|
台灣用戶常搜「日常穿」「居家穿」「出門穿」「媽媽實用」「通勤穿搭」「夏季必備」。適度加入能提升點擊率。
|
||||||
|
|
||||||
|
6. 信任感已由「台灣出貨」建立
|
||||||
|
不需再堆疊「現貨」「快速到貨」。可視情況輕加「品質嚴選」「台灣設計風格」(若原標題有相關元素)。
|
||||||
|
7. 字數精準控制技巧
|
||||||
|
建議目標控制在 55~58 字符,留一點緩衝空間。計算時務必包含空格與【】。
|
||||||
|
|
||||||
|
8. 服飾類風格精準動態匹配
|
||||||
|
- 辣妹/純欲風:設計感、性感氣質、顯瘦曲線
|
||||||
|
- 文藝復古/媽媽裝:復古知性、寬鬆顯瘦、實用舒適
|
||||||
|
- 法式/名媛風:優雅高質感、知性氣質
|
||||||
|
|
||||||
|
9. 定期迭代與數據驗證
|
||||||
|
上架後觀察賣家後台「曝光次數」與「點擊率」,把表現好的熱門關鍵詞保留或加強,表現差的替換。
|
||||||
|
|
||||||
|
10. 避免平台風險
|
||||||
|
嚴格遵守「原標題沒有就不要加」的原則(材質、尺寸、醫療詞),這是長期穩定排名的關鍵。
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
輸出範例
|
||||||
|
================================================================================
|
||||||
|
單一標題範例:
|
||||||
|
【台灣出貨】新中式掛鐘 古典復古 簡約現代 繁體漢字 客廳餐廳 個性藝術創意 靜音時鐘 客廳裝飾 藝術時鐘
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
注意事項總結
|
||||||
|
================================================================================
|
||||||
|
• 嚴格遵守字數 52-60 字符
|
||||||
|
• 繁體中文輸出
|
||||||
|
• 保留原標題風格詞與版型詞
|
||||||
|
• 時效詞「有就留、沒有就絕不加」
|
||||||
|
• 材質與尺寸「原標題沒有就絕不加」
|
||||||
|
• 醫療相關詞一律禁用,銀髮族改用安全替代詞
|
||||||
|
• 批量時修飾詞重複度低於 50%
|
||||||
|
• 建議定期根據賣家後台數據迭代熱門關鍵詞
|
||||||
@@ -764,6 +764,7 @@ class GenerateTab(QWidget):
|
|||||||
self.open_accounts_callback = open_accounts_callback
|
self.open_accounts_callback = open_accounts_callback
|
||||||
self.title_prompt_path = title_prompt_path or appconfig.title_prompt_path(self.config)
|
self.title_prompt_path = title_prompt_path or appconfig.title_prompt_path(self.config)
|
||||||
self.cover_prompts_dir = cover_prompts_dir or appconfig.cover_prompts_dir(self.config)
|
self.cover_prompts_dir = cover_prompts_dir or appconfig.cover_prompts_dir(self.config)
|
||||||
|
prompts.ensure_default_prompts(self.title_prompt_path, self.cover_prompts_dir)
|
||||||
self.current_cover_template = None
|
self.current_cover_template = None
|
||||||
self.generate_worker = None
|
self.generate_worker = None
|
||||||
self.generate_thread = None
|
self.generate_thread = None
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from importlib import resources
|
||||||
|
|
||||||
from . import appconfig
|
from . import appconfig
|
||||||
|
|
||||||
@@ -10,6 +11,8 @@ TITLE_PROMPT_PATH = appconfig.title_prompt_path()
|
|||||||
COVER_PROMPTS_DIR = appconfig.cover_prompts_dir()
|
COVER_PROMPTS_DIR = appconfig.cover_prompts_dir()
|
||||||
TEMPLATE_EXT = ".txt"
|
TEMPLATE_EXT = ".txt"
|
||||||
INVALID_NAME_CHARS = set('\\/:*?"<>|')
|
INVALID_NAME_CHARS = set('\\/:*?"<>|')
|
||||||
|
DEFAULT_PROMPTS_PACKAGE = "app.default_prompts"
|
||||||
|
DEFAULT_COVER_PROMPTS_PACKAGE = "app.default_prompts.cover"
|
||||||
|
|
||||||
|
|
||||||
class PromptError(RuntimeError):
|
class PromptError(RuntimeError):
|
||||||
@@ -35,6 +38,27 @@ def save_title_prompt(text, path=TITLE_PROMPT_PATH) -> None:
|
|||||||
fh.write(str(text or ""))
|
fh.write(str(text or ""))
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_default_prompts(
|
||||||
|
title_prompt_path=TITLE_PROMPT_PATH,
|
||||||
|
cover_prompts_dir=COVER_PROMPTS_DIR,
|
||||||
|
) -> None:
|
||||||
|
"""Seed bundled default prompts into an empty user data directory.
|
||||||
|
|
||||||
|
Existing user prompts are never overwritten. Defaults live in the packaged
|
||||||
|
app and are copied into data/ only when the corresponding user files are
|
||||||
|
missing, or the title prompt file is blank.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not _has_non_empty_file(title_prompt_path):
|
||||||
|
default_title = _read_default_prompt_text("title_prompt.txt")
|
||||||
|
if default_title:
|
||||||
|
save_title_prompt(default_title, title_prompt_path)
|
||||||
|
|
||||||
|
if not list_cover_templates(cover_prompts_dir):
|
||||||
|
for name, text in _iter_default_cover_templates():
|
||||||
|
save_cover_template(name, text, cover_prompts_dir)
|
||||||
|
|
||||||
|
|
||||||
def list_cover_templates(directory=COVER_PROMPTS_DIR):
|
def list_cover_templates(directory=COVER_PROMPTS_DIR):
|
||||||
"""Return cover template names sorted by display name."""
|
"""Return cover template names sorted by display name."""
|
||||||
|
|
||||||
@@ -132,3 +156,40 @@ def _normalize_name(name) -> str:
|
|||||||
if os.path.basename(value) != value:
|
if os.path.basename(value) != value:
|
||||||
raise PromptError(f"封面提示词模板名非法: {value}")
|
raise PromptError(f"封面提示词模板名非法: {value}")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _has_non_empty_file(path) -> bool:
|
||||||
|
try:
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return False
|
||||||
|
with open(path, "r", encoding="utf-8") as fh:
|
||||||
|
return bool(fh.read().strip())
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _read_default_prompt_text(filename) -> str:
|
||||||
|
try:
|
||||||
|
return (
|
||||||
|
resources.files(DEFAULT_PROMPTS_PACKAGE)
|
||||||
|
.joinpath(filename)
|
||||||
|
.read_text(encoding="utf-8")
|
||||||
|
)
|
||||||
|
except (FileNotFoundError, ModuleNotFoundError, OSError):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_default_cover_templates():
|
||||||
|
try:
|
||||||
|
root = resources.files(DEFAULT_COVER_PROMPTS_PACKAGE)
|
||||||
|
except (ModuleNotFoundError, OSError):
|
||||||
|
return
|
||||||
|
for entry in root.iterdir():
|
||||||
|
if not entry.is_file() or not entry.name.lower().endswith(TEMPLATE_EXT):
|
||||||
|
continue
|
||||||
|
name = entry.name[: -len(TEMPLATE_EXT)]
|
||||||
|
try:
|
||||||
|
text = entry.read_text(encoding="utf-8")
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
yield name, text
|
||||||
|
|||||||
+7
-1
@@ -7,13 +7,19 @@ include config files, SQLite databases, Chrome profiles, generated images,
|
|||||||
logs, prompts, or other operator data.
|
logs, prompts, or other operator data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from PyInstaller.utils.hooks import collect_data_files
|
||||||
|
|
||||||
block_cipher = None
|
block_cipher = None
|
||||||
|
default_prompt_datas = collect_data_files(
|
||||||
|
"app.default_prompts",
|
||||||
|
includes=["**/*.txt"],
|
||||||
|
)
|
||||||
|
|
||||||
a = Analysis(
|
a = Analysis(
|
||||||
["main.py"],
|
["main.py"],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[],
|
datas=default_prompt_datas,
|
||||||
hiddenimports=[
|
hiddenimports=[
|
||||||
"PySide6.QtCore",
|
"PySide6.QtCore",
|
||||||
"PySide6.QtGui",
|
"PySide6.QtGui",
|
||||||
|
|||||||
+11
-1
@@ -3,7 +3,7 @@ id: T-579
|
|||||||
title: 打包版内置默认标题和封面提示词,首次运行自动写入用户 data
|
title: 打包版内置默认标题和封面提示词,首次运行自动写入用户 data
|
||||||
phase: 8
|
phase: 8
|
||||||
deps: [T-524, T-538, T-540]
|
deps: [T-524, T-538, T-540]
|
||||||
status: TODO
|
status: DONE
|
||||||
created: 2026-07-10
|
created: 2026-07-10
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -60,3 +60,13 @@ created: 2026-07-10
|
|||||||
- 不提交 `data/config.json`、`data/config/cmhub.json`、`data/cmshopee.db`、`data/images/`、`data/chrome_user_data_dir/`、`data/logs/`。
|
- 不提交 `data/config.json`、`data/config/cmhub.json`、`data/cmshopee.db`、`data/images/`、`data/chrome_user_data_dir/`、`data/logs/`。
|
||||||
- 不改 AI/cmhub 请求协议、生成并发、DB schema、Excel、Shopee/CDP 更新流程。
|
- 不改 AI/cmhub 请求协议、生成并发、DB schema、Excel、Shopee/CDP 更新流程。
|
||||||
- 不在升级时覆盖用户已经修改过的提示词。
|
- 不在升级时覆盖用户已经修改过的提示词。
|
||||||
|
|
||||||
|
## 执行记录
|
||||||
|
|
||||||
|
- 2026-07-10:完成 T-579。
|
||||||
|
- 新增 `app/default_prompts/` 默认提示词资源包,内置当前已验证使用的标题提示词和封面模板 `papa1`;发布前已扫描常见密钥关键词,未发现 API Key、Cookie、token、密码等密钥类内容。
|
||||||
|
- `app/prompts.py` 新增 `ensure_default_prompts()`:用户 `data/title_prompt.txt` 缺失或为空时写入默认标题提示词;用户 `data/prompts/cover/` 没有模板时写入默认封面模板;已有用户提示词或模板时不覆盖。
|
||||||
|
- `app/gui/tabs/generate.py` 在②AI生成页读取提示词前执行默认提示词初始化,保证全新用户首次打开即可看到默认标题提示词和 `papa1` 封面模板。
|
||||||
|
- `cmshopee.spec` 改为通过 `collect_data_files("app.default_prompts")` 收集内置默认提示词文本;仍不打包 release 根目录的 `data/`、`title_prompt.txt`、`prompts/`、DB、配置、图片、日志或登录态。
|
||||||
|
- 测试:`tests/test_prompts.py` 覆盖默认提示词播种和不覆盖用户提示词;`tests/test_gui.py` 覆盖②AI生成页全新用户自动回显默认提示词;`tests/test_packaging.py` 覆盖 PyInstaller spec 收集默认资源。
|
||||||
|
- 验证通过:`py -3.10 -m unittest tests.test_prompts tests.test_gui tests.test_packaging`、`python -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`py -3.10 -m unittest discover -s tests`(319 tests)、`git diff --check`。
|
||||||
|
|||||||
+24
-1
@@ -1857,7 +1857,9 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
self.assertEqual(0, tab.cover_progress_bar.value())
|
self.assertEqual(0, tab.cover_progress_bar.value())
|
||||||
self.assertEqual(1, tab.title_progress_bar.maximum())
|
self.assertEqual(1, tab.title_progress_bar.maximum())
|
||||||
self.assertEqual(1, tab.cover_progress_bar.maximum())
|
self.assertEqual(1, tab.cover_progress_bar.maximum())
|
||||||
self.assertEqual("默认", tab.cover_template_combo.currentText())
|
self.assertIn("蝦皮台灣站", tab.title_prompt_edit.toPlainText())
|
||||||
|
self.assertEqual("papa1", tab.cover_template_combo.currentText())
|
||||||
|
self.assertIn("商品标题:{新标题}", tab.cover_prompt_edit.toPlainText())
|
||||||
self.assertEqual("模板操作", tab.cover_template_actions_button.text())
|
self.assertEqual("模板操作", tab.cover_template_actions_button.text())
|
||||||
self.assertIs(tab.cover_template_actions_menu, tab.cover_template_actions_button.menu())
|
self.assertIs(tab.cover_template_actions_menu, tab.cover_template_actions_button.menu())
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -2234,6 +2236,27 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
|
|
||||||
self.assert_removed(temp_dir)
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
|
def test_generate_tab_seeds_default_prompts_for_new_user_data(self):
|
||||||
|
with self.make_temp_dir() as temp_dir:
|
||||||
|
cfg = self.make_config(temp_dir)
|
||||||
|
title_prompt_path = os.path.join(temp_dir, "title_prompt.txt")
|
||||||
|
cover_prompts_dir = os.path.join(temp_dir, "prompts", "cover")
|
||||||
|
|
||||||
|
tab = GenerateTab(
|
||||||
|
config=cfg,
|
||||||
|
title_prompt_path=title_prompt_path,
|
||||||
|
cover_prompts_dir=cover_prompts_dir,
|
||||||
|
)
|
||||||
|
self.addCleanup(tab.close)
|
||||||
|
|
||||||
|
self.assertIn("蝦皮台灣站", tab.title_prompt_edit.toPlainText())
|
||||||
|
self.assertEqual("papa1", tab.cover_template_combo.currentText())
|
||||||
|
self.assertIn("商品标题:{新标题}", tab.cover_prompt_edit.toPlainText())
|
||||||
|
self.assertTrue(os.path.exists(title_prompt_path))
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(cover_prompts_dir, "papa1.txt")))
|
||||||
|
|
||||||
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
def test_generate_tab_manages_prompt_files_and_preview(self):
|
def test_generate_tab_manages_prompt_files_and_preview(self):
|
||||||
with self.make_temp_dir() as temp_dir:
|
with self.make_temp_dir() as temp_dir:
|
||||||
cfg = self.make_config(temp_dir)
|
cfg = self.make_config(temp_dir)
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ class PackagingTests(unittest.TestCase):
|
|||||||
spec = self.read_text("cmshopee.spec")
|
spec = self.read_text("cmshopee.spec")
|
||||||
normalized = "".join(spec.split())
|
normalized = "".join(spec.split())
|
||||||
|
|
||||||
self.assertIn("datas=[]", normalized)
|
self.assertIn("collect_data_files", spec)
|
||||||
|
self.assertIn('"app.default_prompts"', spec)
|
||||||
|
self.assertIn('includes=["**/*.txt"]', normalized)
|
||||||
|
self.assertIn("datas=default_prompt_datas", normalized)
|
||||||
self.assertIn("COLLECT(", spec)
|
self.assertIn("COLLECT(", spec)
|
||||||
self.assertIn('name="cmshopee"', normalized)
|
self.assertIn('name="cmshopee"', normalized)
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,40 @@ class PromptTests(TempDirMixin, unittest.TestCase):
|
|||||||
|
|
||||||
self.assert_removed(temp_dir)
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
|
def test_ensure_default_prompts_seeds_empty_user_prompt_files(self):
|
||||||
|
with self.make_temp_dir() as temp_dir:
|
||||||
|
title_path = os.path.join(temp_dir, "title_prompt.txt")
|
||||||
|
cover_dir = os.path.join(temp_dir, "prompts", "cover")
|
||||||
|
|
||||||
|
prompts.ensure_default_prompts(title_path, cover_dir)
|
||||||
|
|
||||||
|
self.assertIn("蝦皮台灣站", prompts.load_title_prompt(title_path))
|
||||||
|
self.assertEqual(["papa1"], prompts.list_cover_templates(cover_dir))
|
||||||
|
self.assertIn(
|
||||||
|
"商品标题:{新标题}",
|
||||||
|
prompts.load_cover_template("papa1", cover_dir),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
|
def test_ensure_default_prompts_does_not_overwrite_user_prompts(self):
|
||||||
|
with self.make_temp_dir() as temp_dir:
|
||||||
|
title_path = os.path.join(temp_dir, "title_prompt.txt")
|
||||||
|
cover_dir = os.path.join(temp_dir, "prompts", "cover")
|
||||||
|
prompts.save_title_prompt("用户标题提示词", title_path)
|
||||||
|
prompts.save_cover_template("用户模板", "用户封面提示词", cover_dir)
|
||||||
|
|
||||||
|
prompts.ensure_default_prompts(title_path, cover_dir)
|
||||||
|
|
||||||
|
self.assertEqual("用户标题提示词", prompts.load_title_prompt(title_path))
|
||||||
|
self.assertEqual(["用户模板"], prompts.list_cover_templates(cover_dir))
|
||||||
|
self.assertEqual(
|
||||||
|
"用户封面提示词",
|
||||||
|
prompts.load_cover_template("用户模板", cover_dir),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
def test_render_prompt_replaces_known_variables(self):
|
def test_render_prompt_replaces_known_variables(self):
|
||||||
task = {
|
task = {
|
||||||
"old_title": "舊T恤",
|
"old_title": "舊T恤",
|
||||||
|
|||||||
Reference in New Issue
Block a user