feat(idea): create group auto-generates personas; channel removed; product->product/idea

Per product-idea focus:
- GroupBuilder: 'สินค้า/บริการ/ไอเดีย' label; removed the channel select (channel is now
  chosen at chat time as a scenario, not at persona create). Create now auto-runs analyze
  so personas are generated immediately (no separate Analyze button).
- GroupEdit: analyze button becomes 'สร้างบุคคลต้นแบบเพิ่มเติม' which APPENDS more personas
  (backend analyze?append=true reuses sales kit + keeps existing instead of replacing).
- i18n product label updated.
- User-journey test asserts append adds personas (15->30) and existing kept.
All 9 backend suites pass. Rebuilt dist.
This commit is contained in:
Macky
2026-08-09 10:12:40 +07:00
parent 675cc5c73f
commit 0b92430aab
33 changed files with 119 additions and 93 deletions

View File

@@ -170,9 +170,13 @@ def list_groups():
@require_auth
@require_roles("admin")
def analyze_group(gid: str):
"""Run analysis: sales kit + 15 personas. Synchronous for v1 (replaces gen)."""
"""Run analysis: sales kit + 15 personas. Synchronous for v1 (replaces gen).
?append=true generates MORE personas and appends to existing ones instead of
replacing them (used by 'create more personas')."""
s = _stores()
group = _get_owned_group(s, gid)
append = request.args.get("append") == "true"
inp = group.get("input", {})
if not s["llm"]:
@@ -181,20 +185,33 @@ def analyze_group(gid: str):
from ..services.analyzer import Analyzer
from ..services.persona_generator import PersonaGenerator
s["groups"].update(gid, status="analyzing", error=None)
# If appending, reuse the existing sales kit; else re-run the full analysis.
sales_kit = group.get("sales_kit")
if not append or not sales_kit:
s["groups"].update(gid, status="analyzing", error=None)
try:
sales_kit = Analyzer(s["llm"]).analyze(
product=inp.get("product", ""),
segment=inp.get("segment", ""),
description=inp.get("description", ""),
file_text=inp.get("file_text", ""),
channel=inp.get("channel", "facebook"),
)
except Exception as exc:
s["groups"].update(gid, status="failed", error=str(exc))
raise ApiError(f"analysis failed: {exc}", 500)
s["groups"].update(gid, sales_kit=sales_kit, status="ready", error=None)
existing = []
if append:
existing = s["groups"].get_or_none(gid).get("personas", []) or []
try:
sales_kit = Analyzer(s["llm"]).analyze(
product=inp.get("product", ""),
segment=inp.get("segment", ""),
description=inp.get("description", ""),
file_text=inp.get("file_text", ""),
channel=inp.get("channel", "facebook"),
)
personas = PersonaGenerator(s["llm"]).generate(
sales_kit=sales_kit,
language=inp.get("language", "th"),
channel=inp.get("channel", "facebook"),
)
personas = existing + personas
except Exception as exc:
s["groups"].update(gid, status="failed", error=str(exc))
raise ApiError(f"analysis failed: {exc}", 500)

View File

@@ -85,4 +85,12 @@ for hidden in ("pains","tolerance","negotiation_levers","opener","income","backg
assert hidden not in pfirst, f"trainee should not see '{hidden}' before chat"
print("[ok] trainee sees only revealable persona (latent hidden)")
# 11. 'create more personas' = append (does not replace existing ones)
before = len(C.get(f"/api/groups/{gid}/personas", headers=AH).get_json()["personas"])
r = C.post(f"/api/groups/{gid}/analyze?append=true", headers=AH)
assert r.status_code == 200, r.get_json()
after = len(r.get_json()["personas"])
assert after > before, f"append should add personas (before={before}, after={after})"
print(f"[ok] append adds personas ({before} -> {after}); existing kept")
print("ALL USER-JOURNEY FLOW TESTS PASSED")