m97j commited on
Commit
39eaf79
ยท
verified ยท
1 Parent(s): a12f05b

style(ui_components): update code comments

Browse files
README.md CHANGED
@@ -8,7 +8,7 @@ sdk_version: "6.9.0"
8
  app_file: app.py
9
  ---
10
 
11
- # โš™๏ธ Neural Engine (neuro/)
12
 
13
  ์ด Space๋Š” **Cognitive World Interaction Engine์˜ Core Model**์˜ ์ถ”๋ก  API์™€ ๊ฐ„๋‹จํ•œ Web Test์šฉ Gradio UI๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
14
  Hugging Face Hub์— ์—…๋กœ๋“œ๋œ
 
8
  app_file: app.py
9
  ---
10
 
11
+ # โš™๏ธ Neural Engine (neural/)
12
 
13
  ์ด Space๋Š” **Cognitive World Interaction Engine์˜ Core Model**์˜ ์ถ”๋ก  API์™€ ๊ฐ„๋‹จํ•œ Web Test์šฉ Gradio UI๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
14
  Hugging Face Hub์— ์—…๋กœ๋“œ๋œ
app.py CHANGED
@@ -1,18 +1,17 @@
1
- import gradio as gr
2
  from inference import run_inference
3
  from modules.ui_components import build_ui
4
  from webtest_prompt import build_webtest_prompt
5
 
6
 
7
- # Web Test UI ํ˜ธ์ถœ ํ•จ์ˆ˜
8
  def gradio_infer(npc_id, npc_location, player_utt):
9
  prompt = build_webtest_prompt(npc_id, npc_location, player_utt)
10
  result = run_inference(prompt)
11
  return result["npc_output_text"], result["deltas"], result["flags_prob"]
12
 
13
- # ping: ์ƒํƒœ ํ™•์ธ ๋ฐ ๊นจ์šฐ๊ธฐ
14
  def ping():
15
- # ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ, ์—†์œผ๋ฉด ๋กœ๋“œ
16
  global wrapper, tokenizer, model, flags_order
17
  if 'model' not in globals() or model is None:
18
  from model_loader import ModelWrapper
@@ -21,34 +20,6 @@ def ping():
21
  return {"status": "awake"}
22
 
23
 
24
- # with gr.Blocks() as demo:
25
- # gr.Markdown("## CWIE Core Model Inference")
26
-
27
- # with gr.Tab("Web Test UI"):
28
- # npc_id = gr.Textbox(label="NPC ID")
29
- # npc_loc = gr.Textbox(label="NPC Location")
30
- # player_utt = gr.Textbox(label="Player Utterance")
31
- # npc_resp = gr.Textbox(label="NPC Response")
32
- # deltas = gr.JSON(label="Deltas")
33
- # flags = gr.JSON(label="Flags Probabilities")
34
- # btn = gr.Button("Run Inference")
35
-
36
- # # Web Test ์ „์šฉ (api_name ์ œ๊ฑฐ)
37
- # btn.click(
38
- # fn=gradio_infer,
39
- # inputs=[npc_id, npc_loc, player_utt],
40
- # outputs=[npc_resp, deltas, flags]
41
- # )
42
-
43
- # # ping ์—”๋“œํฌ์ธํŠธ (์ƒํƒœ ํ™•์ธ/๊นจ์šฐ๊ธฐ)
44
- # gr.Button("Ping Server").click(
45
- # fn=ping,
46
- # inputs=[],
47
- # outputs=[],
48
- # api_name="ping"
49
- # )
50
-
51
-
52
  if __name__ == "__main__":
53
  demo = build_ui()
54
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
1
  from inference import run_inference
2
  from modules.ui_components import build_ui
3
  from webtest_prompt import build_webtest_prompt
4
 
5
 
6
+ # Web Test UI Call Function
7
  def gradio_infer(npc_id, npc_location, player_utt):
8
  prompt = build_webtest_prompt(npc_id, npc_location, player_utt)
9
  result = run_inference(prompt)
10
  return result["npc_output_text"], result["deltas"], result["flags_prob"]
11
 
12
+ # ping: Check status and wake up
13
  def ping():
14
+ # Check if model is loaded, load if not
15
  global wrapper, tokenizer, model, flags_order
16
  if 'model' not in globals() or model is None:
17
  from model_loader import ModelWrapper
 
20
  return {"status": "awake"}
21
 
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  if __name__ == "__main__":
24
  demo = build_ui()
25
  demo.launch(server_name="0.0.0.0", server_port=7860)
config.py CHANGED
@@ -1,25 +1,26 @@
1
  import os
 
2
  import torch
3
  from dotenv import load_dotenv
4
 
5
- # .env ํŒŒ์ผ ๋กœ๋“œ (๋กœ์ปฌ ๊ฐœ๋ฐœ ์‹œ)
6
  load_dotenv()
7
 
8
- # ๋ชจ๋ธ ๊ฒฝ๋กœ (ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—†์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ)
9
  BASE_MODEL = os.getenv("BASE_MODEL", "Qwen/Qwen2.5-3B-Instruct")
10
  ADAPTERS = os.getenv("ADAPTER_MODEL", "m97j/npc_LoRA-fps")
11
 
12
- # ์žฅ์น˜ ์„ค์ •
13
  DEVICE = os.getenv("DEVICE", "cuda" if torch.cuda.is_available() else "cpu")
14
 
15
- # ํ† ํฌ๋‚˜์ด์ €/๋ชจ๋ธ ๊ณตํ†ต
16
  MAX_LENGTH = int(os.getenv("MAX_LENGTH", 1024))
17
- NUM_FLAGS = int(os.getenv("NUM_FLAGS", 7)) # flags.json ๊ธธ์ด์™€ ์ผ์น˜
18
 
19
- # ์ƒ์„ฑ ํŒŒ๋ผ๋ฏธํ„ฐ
20
  GEN_MAX_NEW_TOKENS = int(os.getenv("GEN_MAX_NEW_TOKENS", 400))
21
  GEN_TEMPERATURE = float(os.getenv("GEN_TEMPERATURE", 0.7))
22
  GEN_TOP_P = float(os.getenv("GEN_TOP_P", 0.9))
23
 
24
- # Hugging Face Token (Private ๋ชจ๋ธ ์ ‘๊ทผ์šฉ)
25
  HF_TOKEN = os.getenv("HF_TOKEN")
 
1
  import os
2
+
3
  import torch
4
  from dotenv import load_dotenv
5
 
6
+ # Load .env file (for local development)
7
  load_dotenv()
8
 
9
+ # Model path (uses default if environment variable is missing)
10
  BASE_MODEL = os.getenv("BASE_MODEL", "Qwen/Qwen2.5-3B-Instruct")
11
  ADAPTERS = os.getenv("ADAPTER_MODEL", "m97j/npc_LoRA-fps")
12
 
13
+ # Device configuration
14
  DEVICE = os.getenv("DEVICE", "cuda" if torch.cuda.is_available() else "cpu")
15
 
16
+ # Tokenizer/Model common parameters
17
  MAX_LENGTH = int(os.getenv("MAX_LENGTH", 1024))
18
+ NUM_FLAGS = int(os.getenv("NUM_FLAGS", 7)) # match withflags.json
19
 
20
+ # Generation parameters (can be overridden at inference time)
21
  GEN_MAX_NEW_TOKENS = int(os.getenv("GEN_MAX_NEW_TOKENS", 400))
22
  GEN_TEMPERATURE = float(os.getenv("GEN_TEMPERATURE", 0.7))
23
  GEN_TOP_P = float(os.getenv("GEN_TOP_P", 0.9))
24
 
25
+ # Hugging Face Token (For Private Model Access)
26
  HF_TOKEN = os.getenv("HF_TOKEN")
inference.py CHANGED
@@ -1,9 +1,10 @@
1
  import torch
2
- from config import DEVICE, MAX_LENGTH, GEN_MAX_NEW_TOKENS, GEN_TEMPERATURE, GEN_TOP_P
 
3
  from model_loader import ModelWrapper
4
 
5
- # ์ „์—ญ ๋กœ๋“œ (์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ 1ํšŒ)
6
- wrapper = ModelWrapper() # ๊ธฐ๋ณธ์€ latest ๋ธŒ๋žœ์น˜
7
  tokenizer, model, flags_order = wrapper.get()
8
 
9
  GEN_PARAMS = {
@@ -18,17 +19,17 @@ def run_inference(prompt: str):
18
  inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=MAX_LENGTH).to(DEVICE)
19
 
20
  with torch.no_grad():
21
- # ํ…์ŠคํŠธ ์ƒ์„ฑ
22
  gen_ids = model.generate(**inputs, **GEN_PARAMS)
23
  generated_text = tokenizer.decode(
24
  gen_ids[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True
25
  )
26
 
27
- # ํžˆ๋“  ์Šคํ…Œ์ดํŠธ ์ถ”์ถœ
28
  outputs = model(**inputs, output_hidden_states=True)
29
  h = outputs.hidden_states[-1]
30
 
31
- # <STATE> ํ† ํฐ ์œ„์น˜ ํ’€๋ง
32
  STATE_ID = tokenizer.convert_tokens_to_ids("<STATE>")
33
  ids = inputs["input_ids"]
34
  mask = (ids == STATE_ID).unsqueeze(-1)
@@ -38,7 +39,7 @@ def run_inference(prompt: str):
38
  else:
39
  pooled = h[:, -1, :]
40
 
41
- # ์ปค์Šคํ…€ ํ—ค๋“œ ์ถ”๋ก 
42
  delta_pred = torch.tanh(model.delta_head(pooled))[0].cpu().tolist()
43
  flag_prob = torch.sigmoid(model.flag_head(pooled))[0].cpu().tolist()
44
  flag_thr = torch.sigmoid(model.flag_threshold_head(pooled))[0].cpu().tolist()
@@ -58,6 +59,6 @@ def run_inference(prompt: str):
58
 
59
  def reload_model(branch="latest"):
60
  global wrapper, tokenizer, model, flags_order
61
- wrapper = ModelWrapper(branch=branch) # branch ์ธ์ž๋กœ latest ์ „๋‹ฌ
62
  tokenizer, model, flags_order = wrapper.get()
63
  print(f"Model reloaded from branch: {branch}")
 
1
  import torch
2
+ from config import (DEVICE, GEN_MAX_NEW_TOKENS, GEN_TEMPERATURE, GEN_TOP_P,
3
+ MAX_LENGTH)
4
  from model_loader import ModelWrapper
5
 
6
+ # Global Load (once at server start)
7
+ wrapper = ModelWrapper()
8
  tokenizer, model, flags_order = wrapper.get()
9
 
10
  GEN_PARAMS = {
 
19
  inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=MAX_LENGTH).to(DEVICE)
20
 
21
  with torch.no_grad():
22
+ # language generation
23
  gen_ids = model.generate(**inputs, **GEN_PARAMS)
24
  generated_text = tokenizer.decode(
25
  gen_ids[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True
26
  )
27
 
28
+ # hidden state extraction
29
  outputs = model(**inputs, output_hidden_states=True)
30
  h = outputs.hidden_states[-1]
31
 
32
+ # <STATE> token position pooling
33
  STATE_ID = tokenizer.convert_tokens_to_ids("<STATE>")
34
  ids = inputs["input_ids"]
35
  mask = (ids == STATE_ID).unsqueeze(-1)
 
39
  else:
40
  pooled = h[:, -1, :]
41
 
42
+ # delta, flag, flag_threshold prediction
43
  delta_pred = torch.tanh(model.delta_head(pooled))[0].cpu().tolist()
44
  flag_prob = torch.sigmoid(model.flag_head(pooled))[0].cpu().tolist()
45
  flag_thr = torch.sigmoid(model.flag_threshold_head(pooled))[0].cpu().tolist()
 
59
 
60
  def reload_model(branch="latest"):
61
  global wrapper, tokenizer, model, flags_order
62
+ wrapper = ModelWrapper(branch=branch)
63
  tokenizer, model, flags_order = wrapper.get()
64
  print(f"Model reloaded from branch: {branch}")
model_loader.py CHANGED
@@ -1,7 +1,10 @@
1
- import os, json, torch
 
 
 
2
  import torch.nn as nn
3
- from transformers import AutoTokenizer, AutoModelForCausalLM
4
  from config import DEVICE, HF_TOKEN
 
5
 
6
  SPECIALS = ["<SYS>", "<CTX>", "<PLAYER>", "<NPC>", "<STATE>", "<RAG>", "<PLAYER_STATE>"]
7
 
@@ -13,18 +16,18 @@ def get_current_branch():
13
 
14
  class ModelWrapper:
15
  def __init__(self):
16
- # Flags ์ •๋ณด
17
  flags_path = os.path.join(os.path.dirname(__file__), "flags.json")
18
  self.flags_order = json.load(open(flags_path, encoding="utf-8"))["ALL_FLAGS"]
19
  self.num_flags = len(self.flags_order)
20
 
21
  branch = get_current_branch()
22
 
23
- # 1) ํ† ํฌ๋‚˜์ด์ € (ํ•™์Šต ๋‹น์‹œ vocab + SPECIALS)
24
  self.tokenizer = AutoTokenizer.from_pretrained(
25
- "m97j/npc_LoRA-fps", # ๋ณ‘ํ•ฉ๋œ ๋ชจ๋ธ์ด ์˜ฌ๋ผ๊ฐ„ repo
26
  revision=branch,
27
- subfolder="testcase_output", # ๋ณ‘ํ•ฉ๋œ ๋ชจ๋ธ์ด ์˜ฌ๋ผ๊ฐ„ ๊ฒฝ๋กœ
28
  use_fast=True,
29
  token=HF_TOKEN,
30
  trust_remote_code=True
@@ -34,24 +37,25 @@ class ModelWrapper:
34
  self.tokenizer.padding_side = "right"
35
  self.tokenizer.add_special_tokens({"additional_special_tokens": SPECIALS})
36
 
37
- # 2) ๋ณ‘ํ•ฉ๋œ ๋ชจ๋ธ ๋กœ๋“œ (์ƒค๋“œ ์ž๋™ ์ธ์‹)
38
  self.model = AutoModelForCausalLM.from_pretrained(
39
- "m97j/npc_LoRA-fps", # ๋ณ‘ํ•ฉ๋œ ๋ชจ๋ธ์ด ์˜ฌ๋ผ๊ฐ„ repo
40
  revision=branch,
41
- subfolder="testcase_output", # ๋ณ‘ํ•ฉ๋œ ๋ชจ๋ธ์ด ์˜ฌ๋ผ๊ฐ„ ๊ฒฝ๋กœ
42
- device_map=None, # ์˜คํ”„๋กœ๋”ฉ ๋น„ํ™œ์„ฑํ™”
43
  low_cpu_mem_usage=False,
44
  trust_remote_code=True,
45
  token=HF_TOKEN
46
  )
47
 
48
- # 3) ์ปค์Šคํ…€ ํ—ค๋“œ ์ถ”๊ฐ€
49
  hidden_size = self.model.config.hidden_size
50
  self.model.delta_head = nn.Linear(hidden_size, 2).to(DEVICE)
51
  self.model.flag_head = nn.Linear(hidden_size, self.num_flags).to(DEVICE)
52
  self.model.flag_threshold_head = nn.Linear(hidden_size, self.num_flags).to(DEVICE)
53
 
54
- # 4) ์ปค์Šคํ…€ ํ—ค๋“œ ๊ฐ€์ค‘์น˜ ๋กœ๋“œ
 
55
  for head_name, file_name in [
56
  ("delta_head", "delta_head.pt"),
57
  ("flag_head", "flag_head.pt"),
@@ -65,7 +69,7 @@ class ModelWrapper:
65
  except Exception as e:
66
  print(f"[WARN] Failed to load {file_name}: {e}")
67
 
68
- # 5) ๋””๋ฐ”์ด์Šค ๋ฐฐ์น˜
69
  self.model.to(DEVICE)
70
  self.model.eval()
71
 
 
1
+ import json
2
+ import os
3
+
4
+ import torch
5
  import torch.nn as nn
 
6
  from config import DEVICE, HF_TOKEN
7
+ from transformers import AutoModelForCausalLM, AutoTokenizer
8
 
9
  SPECIALS = ["<SYS>", "<CTX>", "<PLAYER>", "<NPC>", "<STATE>", "<RAG>", "<PLAYER_STATE>"]
10
 
 
16
 
17
  class ModelWrapper:
18
  def __init__(self):
19
+ # Flags info
20
  flags_path = os.path.join(os.path.dirname(__file__), "flags.json")
21
  self.flags_order = json.load(open(flags_path, encoding="utf-8"))["ALL_FLAGS"]
22
  self.num_flags = len(self.flags_order)
23
 
24
  branch = get_current_branch()
25
 
26
+ # 1) Tokenizer (vocab + SPECIALS at the time of training LoRA)
27
  self.tokenizer = AutoTokenizer.from_pretrained(
28
+ "m97j/npc_LoRA-fps",
29
  revision=branch,
30
+ subfolder="testcase_output",
31
  use_fast=True,
32
  token=HF_TOKEN,
33
  trust_remote_code=True
 
37
  self.tokenizer.padding_side = "right"
38
  self.tokenizer.add_special_tokens({"additional_special_tokens": SPECIALS})
39
 
40
+ # 2) Base model (LoRA model with merged weights, but without custom heads)
41
  self.model = AutoModelForCausalLM.from_pretrained(
42
+ "m97j/npc_LoRA-fps",
43
  revision=branch,
44
+ subfolder="testcase_output",
45
+ device_map=None,
46
  low_cpu_mem_usage=False,
47
  trust_remote_code=True,
48
  token=HF_TOKEN
49
  )
50
 
51
+ # 3) add custom heads (delta, flag, flag_threshold) - architecture only, weights will be loaded separately
52
  hidden_size = self.model.config.hidden_size
53
  self.model.delta_head = nn.Linear(hidden_size, 2).to(DEVICE)
54
  self.model.flag_head = nn.Linear(hidden_size, self.num_flags).to(DEVICE)
55
  self.model.flag_threshold_head = nn.Linear(hidden_size, self.num_flags).to(DEVICE)
56
 
57
+ # 4) Load custom head weights separately (if available)
58
+ # - this is necessary because the LoRA merging process may not include these heads, and they might be trained separately.
59
  for head_name, file_name in [
60
  ("delta_head", "delta_head.pt"),
61
  ("flag_head", "flag_head.pt"),
 
69
  except Exception as e:
70
  print(f"[WARN] Failed to load {file_name}: {e}")
71
 
72
+ # 5) Move model to device and set to eval mode
73
  self.model.to(DEVICE)
74
  self.model.eval()
75
 
modules/case_loader.py CHANGED
@@ -1,15 +1,16 @@
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
- # description์€ input ์•ˆ์— ์žˆ์Œ
13
  return [f"{i+1}. {c['input'].get('description','')}" for i, c in enumerate(TEST_CASES)]
14
 
15
  def load_cases():
 
1
+ import json
2
+ import os
3
+
4
  from inference import run_inference
5
+ from webtest_prompt import build_webtest_prompt
6
 
7
+ BASE_DIR = os.path.dirname(os.path.dirname(__file__))
8
  TEST_CASES_PATH = os.path.join(BASE_DIR, "test_cases.json")
9
 
10
  with open(TEST_CASES_PATH, "r", encoding="utf-8") as f:
11
  TEST_CASES = json.load(f)
12
 
13
  def get_case_names():
 
14
  return [f"{i+1}. {c['input'].get('description','')}" for i, c in enumerate(TEST_CASES)]
15
 
16
  def load_cases():
modules/ui_components.py CHANGED
@@ -1,7 +1,8 @@
1
  import gradio as gr
2
- from .case_loader import load_case, run_case
3
 
4
- # test case names
 
 
5
  CASE_NAMES = [
6
  "ํ๊ณต์žฅ์—์„œ NPC์™€ ๋Œ€ํ™”ํ•˜๋Š” ์žฅ๋ฉด",
7
  "๋งˆ์„ ๋Œ€์žฅ์žฅ์ด์™€ ๋ฌด๊ธฐ ์ˆ˜๋ฆฌ์— ๋Œ€ํ•ด ๋Œ€ํ™”ํ•˜๋Š” ์žฅ๋ฉด",
@@ -35,9 +36,8 @@ def format_case_info(case: dict) -> dict:
35
  def build_ui():
36
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple")) as demo:
37
  gr.Markdown("""
38
- # ๐Ÿ‘พ CWIE Neuro Engine
39
- **CWIE Core ๋ชจ๋ธ ์ถ”๋ก  ์„œ๋ฒ„**
40
- Qwen 3B ๊ธฐ๋ฐ˜ LoRA ํŒŒ์ธํŠœ๋‹ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ NPC ๋Œ€์‚ฌ ์ƒ์„ฑ ๋ฐ ๊ฒŒ์ž„ ์ƒํƒœ๋ณ€ํ™”๋ฅผ ์˜ˆ์ธกํ•ฉ๋‹ˆ๋‹ค.
41
  """)
42
 
43
  with gr.Row():
 
1
  import gradio as gr
 
2
 
3
+ from modules.case_loader import load_case, run_case
4
+
5
+ # test case names (for dropdown display)
6
  CASE_NAMES = [
7
  "ํ๊ณต์žฅ์—์„œ NPC์™€ ๋Œ€ํ™”ํ•˜๋Š” ์žฅ๋ฉด",
8
  "๋งˆ์„ ๋Œ€์žฅ์žฅ์ด์™€ ๋ฌด๊ธฐ ์ˆ˜๋ฆฌ์— ๋Œ€ํ•ด ๋Œ€ํ™”ํ•˜๋Š” ์žฅ๋ฉด",
 
36
  def build_ui():
37
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple")) as demo:
38
  gr.Markdown("""
39
+ # ๐Ÿ‘พ CWIE Neural Engine
40
+ Qwen 3B ๊ธฐ๋ฐ˜ LoRA ํŒŒ์ธํŠœ๋‹ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ NPC ๋Œ€์‚ฌ์ƒ์„ฑ, ๊ฒŒ์ž„ ์ƒํƒœ๋ณ€ํ™” ์˜ˆ์ธก๋“ฑ ์„ธ๊ณ„์™€ ์ƒํ˜ธ์ž‘์šฉ ํ•˜๋Š” ์—”์ง„์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
 
41
  """)
42
 
43
  with gr.Row():
webtest_prompt.py CHANGED
@@ -1,9 +1,10 @@
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
  """
8
  pre = {
9
  "npc_id": npc_id,
@@ -29,15 +30,15 @@ def build_webtest_prompt(npc_id: str, npc_location: str, player_utt: str) -> str
29
 
30
  def _assemble_prompt_for_model(pre: Dict[str, Any]) -> str:
31
  """
32
- Web Test ์ „์šฉ ๋‚ด๋ถ€ ํ•จ์ˆ˜:
33
- pre dict โ†’ ๋ชจ๋ธ ์ž…๋ ฅ ํฌ๋งท ๋ฌธ์ž์—ด(<SYS>~<NPC>)
34
  """
35
 
36
  tags = pre.get("tags", {})
37
  ps = pre.get("player_state", {})
38
  rag_docs = pre.get("rag_main_docs", [])
39
 
40
- # RAG ๋ฌธ์„œ ๋ถ„๋ฆฌ
41
  lore_text = ""
42
  desc_text = ""
43
  for doc in rag_docs:
 
1
+ from typing import Any, Dict
2
+
3
 
4
  def build_webtest_prompt(npc_id: str, npc_location: str, player_utt: str) -> str:
5
  """
6
+ Web Test Only: Generate a prompt string suitable for the model training format
7
+ using minimum input values (NPC ID, Location, Player utterance).
8
  """
9
  pre = {
10
  "npc_id": npc_id,
 
30
 
31
  def _assemble_prompt_for_model(pre: Dict[str, Any]) -> str:
32
  """
33
+ Web Test Only: Internal function for assembling the prompt string for the model.
34
+ pre dict โ†’ Model input format string (<SYS>~<NPC>)
35
  """
36
 
37
  tags = pre.get("tags", {})
38
  ps = pre.get("player_state", {})
39
  rag_docs = pre.get("rag_main_docs", [])
40
 
41
+ # RAG documents are categorized into LORE and DESCRIPTION based on their content.
42
  lore_text = ""
43
  desc_text = ""
44
  for doc in rag_docs: