m97j commited on
Commit
2249ab6
Β·
1 Parent(s): 69e85d6

update app tab structure and live test

Browse files
app.py CHANGED
@@ -1,29 +1,23 @@
1
  import gradio as gr
2
- from inference import run_inference, reload_model # reload_model은 λͺ¨λΈ μž¬λ‘œλ”© ν•¨μˆ˜
3
- from utils_prompt import build_webtest_prompt
4
 
5
- # UIμ—μ„œ ν˜ΈμΆœν•  ν•¨μˆ˜
6
  def gradio_infer(npc_id, npc_location, player_utt):
7
  prompt = build_webtest_prompt(npc_id, npc_location, player_utt)
8
  result = run_inference(prompt)
9
  return result["npc_output_text"], result["deltas"], result["flags_prob"]
10
 
11
- # API 호좜용 ν•¨μˆ˜
12
- def api_infer(session_id, npc_id, prompt, max_tokens=200):
13
- result = run_inference(prompt)
14
- return {
15
- "session_id": session_id,
16
- "npc_id": npc_id,
17
- "npc_response": result["npc_output_text"],
18
- "deltas": result["deltas"],
19
- "flags": result["flags_prob"],
20
- "thresholds": result["flags_thr"]
21
- }
22
-
23
- # λͺ¨λΈ μž¬λ‘œλ”©μš© ν•¨μˆ˜
24
- def ping_reload():
25
- reload_model(branch="latest") # latest λΈŒλžœμΉ˜μ—μ„œ μž¬λ‹€μš΄λ‘œλ“œ & λ‘œλ“œ
26
- return {"status": "reloaded"}
27
 
28
  with gr.Blocks() as demo:
29
  gr.Markdown("## NPC Main Model Inference")
@@ -37,21 +31,20 @@ with gr.Blocks() as demo:
37
  flags = gr.JSON(label="Flags Probabilities")
38
  btn = gr.Button("Run Inference")
39
 
40
- # UI λ²„νŠΌ 클릭 μ‹œ API μ—”λ“œν¬μΈνŠΈλ„ μžλ™ 생성
41
  btn.click(
42
  fn=gradio_infer,
43
  inputs=[npc_id, npc_loc, player_utt],
44
- outputs=[npc_resp, deltas, flags],
45
- api_name="predict_main" # /api/predict_main μ—”λ“œν¬μΈνŠΈ 생성
46
  )
47
 
48
- # λ³„λ„μ˜ UI 없이 API만 μ œκ³΅ν•˜λŠ” μ—”λ“œν¬μΈνŠΈ
49
- gr.Button("Reload Model").click(
50
- fn=ping_reload,
51
  inputs=[],
52
  outputs=[],
53
- api_name="ping_reload" # /api/ping_reload μ—”λ“œν¬μΈνŠΈ 생성
54
  )
55
 
56
  if __name__ == "__main__":
57
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import gradio as gr
2
+ from inference import run_inference
3
+ from webtest_prompt import build_webtest_prompt
4
 
5
+ # Web Test UI 호좜 ν•¨μˆ˜
6
  def gradio_infer(npc_id, npc_location, player_utt):
7
  prompt = build_webtest_prompt(npc_id, npc_location, player_utt)
8
  result = run_inference(prompt)
9
  return result["npc_output_text"], result["deltas"], result["flags_prob"]
10
 
11
+ # ping: μƒνƒœ 확인 및 깨우기
12
+ def ping():
13
+ # λͺ¨λΈμ΄ λ‘œλ“œλ˜μ–΄ μžˆλŠ”μ§€ 확인, μ—†μœΌλ©΄ λ‘œλ“œ
14
+ global wrapper, tokenizer, model, flags_order
15
+ if 'model' not in globals() or model is None:
16
+ from model_loader import ModelWrapper
17
+ wrapper = ModelWrapper()
18
+ tokenizer, model, flags_order = wrapper.get()
19
+ return {"status": "awake"}
20
+
 
 
 
 
 
 
21
 
22
  with gr.Blocks() as demo:
23
  gr.Markdown("## NPC Main Model Inference")
 
31
  flags = gr.JSON(label="Flags Probabilities")
32
  btn = gr.Button("Run Inference")
33
 
34
+ # Web Test μ „μš© (api_name 제거)
35
  btn.click(
36
  fn=gradio_infer,
37
  inputs=[npc_id, npc_loc, player_utt],
38
+ outputs=[npc_resp, deltas, flags]
 
39
  )
40
 
41
+ # ping μ—”λ“œν¬μΈνŠΈ (μƒνƒœ 확인/깨우기)
42
+ gr.Button("Ping Server").click(
43
+ fn=ping,
44
  inputs=[],
45
  outputs=[],
46
+ api_name="ping"
47
  )
48
 
49
  if __name__ == "__main__":
50
+ demo.launch(server_name="0.0.0.0", server_port=7860)
flags.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "ALL_FLAGS": [
3
- "give_item",
4
- "end_npc_main_story",
5
- "quest_stage_change",
6
- "change_game_state",
7
- "change_player_state",
8
- "npc_action",
9
- "unlock_hidden_path"
10
- ]
11
- }
 
 
 
 
 
 
 
 
 
 
 
 
modules/case_loader.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json
2
+ from webtest_prompt import build_webtest_prompt
3
+ from inference import run_inference
4
+
5
+ BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # modules/ μƒμœ„ 폴더
6
+ TEST_CASES_PATH = os.path.join(BASE_DIR, "test_cases.json")
7
+
8
+ with open(TEST_CASES_PATH, "r", encoding="utf-8") as f:
9
+ TEST_CASES = json.load(f)
10
+
11
+ def get_case_names():
12
+ return [f"{i+1}. {c['description']}" for i, c in enumerate(TEST_CASES)]
13
+
14
+ def load_case(idx):
15
+ case = TEST_CASES[idx]
16
+ return json.dumps(case, ensure_ascii=False, indent=2), case["player_utterance"]
17
+
18
+ def run_case(idx, player_utt):
19
+ case = TEST_CASES[idx].copy()
20
+ case["player_utterance"] = player_utt
21
+ prompt = build_webtest_prompt(case["npc_id"], case["npc_location"], player_utt)
22
+ result = run_inference(prompt)
23
+ return result["npc_output_text"], result["deltas"], result["flags_prob"]
modules/ui_components.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from case_loader import get_case_names, load_case, run_case
3
+
4
+ def build_ui():
5
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple")) as demo:
6
+ # 상단 μ†Œκ°œ
7
+ gr.Markdown("""
8
+ # πŸ‘Ύ PersonaChatEngine HF-Serve
9
+ **κ²Œμž„ λ‚΄ NPC 메인 λͺ¨λΈ μΆ”λ‘  μ„œλ²„**
10
+ Qwen 3B 기반 LoRA νŒŒμΈνŠœλ‹ λͺ¨λΈμ„ μ‚¬μš©ν•˜μ—¬ NPC λŒ€μ‚¬λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
11
+ """)
12
+
13
+ with gr.Row():
14
+ gr.Button("πŸ“„ 상세 λ¬Έμ„œ 보기",
15
+ link="https://huggingface.co/spaces/m97j/PersonaChatEngine_HF-serve/blob/main/README.md")
16
+ gr.Button("πŸ’» Colab ν…ŒμŠ€νŠΈ μ—΄κΈ°",
17
+ link="https://colab.research.google.com/drive/1_-qH8kdoU2Jj58TdaSnswHex-BFefInq?usp=sharing#scrollTo=cFJGv8BJ8oPD")
18
+
19
+ gr.Markdown("### 🎯 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ 기반 간단 μ‹€ν–‰")
20
+
21
+ with gr.Row():
22
+ case_dropdown = gr.Dropdown(choices=get_case_names(), label="ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ 선택", value=get_case_names()[0])
23
+ load_btn = gr.Button("μΌ€μ΄μŠ€ 뢈러였기")
24
+
25
+ case_info = gr.Textbox(label="μΌ€μ΄μŠ€ 정보", lines=10)
26
+ player_input = gr.Textbox(label="Player Utterance μˆ˜μ •", lines=2)
27
+
28
+ run_btn = gr.Button("πŸš€ Run Inference", variant="primary")
29
+ npc_resp = gr.Textbox(label="NPC Response")
30
+ deltas = gr.JSON(label="Deltas")
31
+ flags = gr.JSON(label="Flags Probabilities")
32
+
33
+ load_btn.click(
34
+ fn=lambda name: load_case(get_case_names().index(name)),
35
+ inputs=[case_dropdown],
36
+ outputs=[case_info, player_input]
37
+ )
38
+
39
+ run_btn.click(
40
+ fn=lambda name, utt: run_case(get_case_names().index(name), utt),
41
+ inputs=[case_dropdown, player_input],
42
+ outputs=[npc_resp, deltas, flags]
43
+ )
44
+
45
+ gr.Markdown("""
46
+ ---
47
+ ⚠️ **μ‹€μ œ κ²Œμž„ νŒŒμ΄ν”„λΌμΈ ν…ŒμŠ€νŠΈ**λŠ” [ai-server Swagger](https://huggingface.co/spaces/m97j/PersonaChatEngine_ai_server)μ—μ„œ μ§„ν–‰ν•˜μ„Έμš”.
48
+ """)
49
+
50
+ return demo
test_cases.json ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "case1",
4
+ "npc_id": "mother_abandoned_factory",
5
+ "npc_location": "map1",
6
+ "description": "폐곡μž₯μ—μ„œ NPC와 λŒ€ν™”ν•˜λŠ” μž₯λ©΄",
7
+ "player_utterance": "μ•„! 머리가!!! κ°‘μžκΈ° 기얡이 λ– μ˜¬λžμ–΄μš”...",
8
+ "tags": {
9
+ "quest_stage": "in_progress",
10
+ "relationship": 0.35,
11
+ "trust": 0.35,
12
+ "npc_mood": "grief",
13
+ "player_reputation": "helpful",
14
+ "style": "emotional"
15
+ },
16
+ "lore": "이 곡μž₯은 μˆ˜μ‹­ λ…„ μ „ ν™”μž¬λ‘œ νμ‡„λ˜μ—ˆλ‹€.",
17
+ "context": [
18
+ {"role": "player", "text": "사싀 이 곡μž₯을 λŒμ•„λ‹€λ‹ˆλ©΄μ„œ..."},
19
+ {"role": "npc", "text": "ν˜Ήμ‹œ κ·Έ νŒŒν‹°μ— Jason도 μžˆμ—ˆλ‚˜μš”..."}
20
+ ]
21
+ },
22
+ {
23
+ "id": "case2",
24
+ "npc_id": "blacksmith_village_center",
25
+ "npc_location": "village_square",
26
+ "description": "λ§ˆμ„ λŒ€μž₯μž₯이와 무기 μˆ˜λ¦¬μ— λŒ€ν•΄ λŒ€ν™”ν•˜λŠ” μž₯λ©΄",
27
+ "player_utterance": "이 검을 λ‹€μ‹œ μ“Έ 수 있게 고쳐쀄 수 μžˆλ‚˜μš”?",
28
+ "tags": {
29
+ "quest_stage": "not_started",
30
+ "relationship": 0.2,
31
+ "trust": 0.4,
32
+ "npc_mood": "neutral",
33
+ "player_reputation": "unknown",
34
+ "style": "direct"
35
+ },
36
+ "lore": "λ§ˆμ„μ˜ λŒ€μž₯μž₯μ΄λŠ” μ„ΈλŒ€λ₯Ό 이어 무기λ₯Ό μ œμž‘ν•΄μ™”λ‹€.",
37
+ "context": [
38
+ {"role": "npc", "text": "였, μ—¬ν–‰μžκ΅°. 무슨 일둜 μ™”λ‚˜?"}
39
+ ]
40
+ },
41
+ {
42
+ "id": "case3",
43
+ "npc_id": "forest_hermit",
44
+ "npc_location": "deep_forest",
45
+ "description": "μˆ²μ† μ€λ‘”μžμ™€ 희귀 μ•½μ΄ˆμ— λŒ€ν•΄ λŒ€ν™”ν•˜λŠ” μž₯λ©΄",
46
+ "player_utterance": "ν˜Ήμ‹œ 이 κ·Όμ²˜μ—μ„œ ν‘Έλ₯ΈλΉ› μ•½μ΄ˆλ₯Ό λ³Έ 적 μžˆλ‚˜μš”?",
47
+ "tags": {
48
+ "quest_stage": "in_progress",
49
+ "relationship": 0.5,
50
+ "trust": 0.6,
51
+ "npc_mood": "curious",
52
+ "player_reputation": "friendly",
53
+ "style": "polite"
54
+ },
55
+ "lore": "μ€λ‘”μžλŠ” μˆ²μ† κΉŠμ€ κ³³μ—μ„œ μ•½μ΄ˆμ™€ 버섯을 μ—°κ΅¬ν•œλ‹€.",
56
+ "context": [
57
+ {"role": "player", "text": "μ•ˆλ…•ν•˜μ„Έμš”, ν˜Ήμ‹œ μž μ‹œ 이야기 λ‚˜λˆŒ 수 μžˆμ„κΉŒμš”?"},
58
+ {"role": "npc", "text": "μ—¬κΈ°κΉŒμ§€ μ˜€λŠ” μ‚¬λžŒμ€ λ“œλ¬Όμ§€μš”."}
59
+ ]
60
+ },
61
+ {
62
+ "id": "case4",
63
+ "npc_id": "captain_port_authority",
64
+ "npc_location": "harbor",
65
+ "description": "항ꡬ 관리관과 μΆœν•­ ν—ˆκ°€μ— λŒ€ν•΄ λŒ€ν™”ν•˜λŠ” μž₯λ©΄",
66
+ "player_utterance": "이 λ°°λ₯Ό 였늘 μ•ˆμ— μΆœν•­μ‹œμΌœμ•Ό ν•©λ‹ˆλ‹€. ν—ˆκ°€λ₯Ό λΆ€νƒλ“œλ¦½λ‹ˆλ‹€.",
67
+ "tags": {
68
+ "quest_stage": "urgent",
69
+ "relationship": 0.45,
70
+ "trust": 0.3,
71
+ "npc_mood": "suspicious",
72
+ "player_reputation": "neutral",
73
+ "style": "persuasive"
74
+ },
75
+ "lore": "항ꡬ 관리관은 λͺ¨λ“  μ„ λ°•μ˜ μΆœν•­μ„ μ—„κ²©νžˆ κ΄€λ¦¬ν•œλ‹€.",
76
+ "context": [
77
+ {"role": "npc", "text": "μ„œλ₯˜λŠ” λ‹€ μ€€λΉ„λλ‚˜?"}
78
+ ]
79
+ },
80
+ {
81
+ "id": "case5",
82
+ "npc_id": "young_apprentice_mage",
83
+ "npc_location": "mage_tower_library",
84
+ "description": "λ§ˆλ²•μ‚¬ κ²¬μŠ΅μƒκ³Ό κ³ λŒ€ μ£Όλ¬Έμ„œμ— λŒ€ν•΄ λŒ€ν™”ν•˜λŠ” μž₯λ©΄",
85
+ "player_utterance": "이 μ£Όλ¬Έμ„œμ— 적힌 λ¬Έμž₯을 해석할 수 μžˆλ‚˜μš”?",
86
+ "tags": {
87
+ "quest_stage": "research",
88
+ "relationship": 0.6,
89
+ "trust": 0.7,
90
+ "npc_mood": "excited",
91
+ "player_reputation": "scholar",
92
+ "style": "inquisitive"
93
+ },
94
+ "lore": "λ§ˆλ²•μ‚¬ νƒ‘μ˜ λ„μ„œκ΄€μ—λŠ” 수백 λ…„ 된 κ³ μ„œλ“€μ΄ λ³΄κ΄€λ˜μ–΄ μžˆλ‹€.",
95
+ "context": [
96
+ {"role": "player", "text": "이 책은 ꡉμž₯히 였래된 것 κ°™μ•„μš”."},
97
+ {"role": "npc", "text": "λ§žμ•„μš”! 이런 건 정말 λ“œλ¬Όμ£ ."}
98
+ ]
99
+ }
100
+ ]
utils_prompt.py β†’ webtest_prompt.py RENAMED
@@ -1,7 +1,11 @@
1
  from typing import Dict, Any
2
 
3
  def build_webtest_prompt(npc_id: str, npc_location: str, player_utt: str) -> str:
4
- # μ›Ή ν…ŒμŠ€νŠΈμ—μ„œλŠ” μ΅œμ†Œ ν•„λ“œλ§Œ μ±„μš΄ pre dict 생성
 
 
 
 
5
  pre = {
6
  "tags": {
7
  "npc_id": npc_id,
@@ -14,15 +18,18 @@ def build_webtest_prompt(npc_id: str, npc_location: str, player_utt: str) -> str
14
  "style": ""
15
  },
16
  "player_state": {},
17
- "rag_main_docs": [], # μ›Ή ν…ŒμŠ€νŠΈμ—μ„œλŠ” RAG λ¬Έμ„œ μ—†μŒ
18
- "context": [], # λŒ€ν™” νžˆμŠ€ν† λ¦¬ μ—†μŒ
19
  "player_utterance": player_utt
20
  }
21
- # session_idλŠ” μ›Ή ν…ŒμŠ€νŠΈμ—μ„œλŠ” 의미 μ—†μœΌλ‹ˆ 빈 κ°’
22
- return build_main_prompt(pre, session_id="", npc_id=npc_id)
23
 
 
 
 
 
 
24
 
25
- def build_main_prompt(pre: Dict[str, Any], session_id: str, npc_id: str) -> str:
26
  tags = pre.get("tags", {})
27
  ps = pre.get("player_state", {})
28
  rag_docs = pre.get("rag_main_docs", [])
 
1
  from typing import Dict, Any
2
 
3
  def build_webtest_prompt(npc_id: str, npc_location: str, player_utt: str) -> str:
4
+ """
5
+ Web Test μ „μš©: μ΅œμ†Œ μž…λ ₯κ°’(NPC ID, Location, Player λ°œν™”)으둜
6
+ λͺ¨λΈ ν•™μŠ΅ 포맷에 λ§žλŠ” prompt λ¬Έμžμ—΄μ„ 생성.
7
+ μ‹€μ œ API/κ²Œμž„ μ„œλΉ„μŠ€ κ²½λ‘œμ—μ„œλŠ” μ‚¬μš©ν•˜μ§€ μ•ŠμŒ.
8
+ """
9
  pre = {
10
  "tags": {
11
  "npc_id": npc_id,
 
18
  "style": ""
19
  },
20
  "player_state": {},
21
+ "rag_main_docs": [],
22
+ "context": [],
23
  "player_utterance": player_utt
24
  }
25
+ return _assemble_prompt_for_model(pre)
 
26
 
27
+ def _assemble_prompt_for_model(pre: Dict[str, Any]) -> str:
28
+ """
29
+ Web Test μ „μš© λ‚΄λΆ€ ν•¨μˆ˜:
30
+ pre dict β†’ λͺ¨λΈ μž…λ ₯ 포맷 λ¬Έμžμ—΄(<SYS>~<NPC>)
31
+ """
32
 
 
33
  tags = pre.get("tags", {})
34
  ps = pre.get("player_state", {})
35
  rag_docs = pre.get("rag_main_docs", [])