Skip to content

Commit 0fef67a

Browse files
committed
feat: en version
1 parent 6051d53 commit 0fef67a

File tree

12 files changed

+765
-190
lines changed

12 files changed

+765
-190
lines changed

demos/AOTAI_Hike/backend/aotai_hike/adapters/companion.py

Lines changed: 161 additions & 93 deletions
Large diffs are not rendered by default.

demos/AOTAI_Hike/backend/aotai_hike/router.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@
3232
from aotai_hike.services.game_service import GameService
3333
from aotai_hike.stores.session_store import InMemorySessionStore
3434
from aotai_hike.utils.share_image import generate_share_image
35-
from aotai_hike.world.map_data import get_graph
35+
from aotai_hike.world.map_data import (
36+
AOTAI_EDGE_LABELS_EN,
37+
AOTAI_NODE_NAMES_EN,
38+
get_graph,
39+
)
3640

3741

3842
router = APIRouter(prefix="/api/demo/ao-tai", tags=["AoTai Demo"])
@@ -49,10 +53,11 @@
4953
# Default 3 roles per theme (server-owned config). One map = one set of default personas.
5054
# Each set has zh and en versions (name + persona).
5155

52-
# 鳌太线 (AoTai): 阿鳌 / 太白 / 小山 — zh & en persona
56+
# 鳌太线 (AoTai): 阿鳌/太白/小山 — zh & en name + persona
5357
_AOTAI_ROLES: list[dict] = [
5458
{
5559
"name": "阿鳌",
60+
"name_en": "Ao",
5661
"avatar_key": "green",
5762
"persona_zh": "阿鳌:持灯的领路者,熟知鳌太古道与太白山脉。谨慎、稳重,誓要带队抵达太白之巅。",
5863
"persona_en": "Ao: The guide with the lamp. Knows the AoTai trail and Taibai range. Cautious and steady, determined to lead the team to the summit.",
@@ -66,6 +71,7 @@
6671
},
6772
{
6873
"name": "太白",
74+
"name_en": "Taibai",
6975
"avatar_key": "blue",
7076
"persona_zh": '太白:表面是器材与数据的虔信者,经验丰厚、言辞克制。暗闻2800下撤口藏有金矿,欲借"体力不支"脱队潜行。',
7177
"persona_en": "Taibai: On the surface a believer in gear and data, experienced and reserved. Secretly heard of a stash at the evacuation point and plans to slip away by feigning exhaustion.",
@@ -79,6 +85,7 @@
7985
},
8086
{
8187
"name": "小山",
88+
"name_en": "Xiaoshan",
8289
"avatar_key": "red",
8390
"persona_zh": "小山:笑容背后的新人徒步者,乐观只是外壳。多年前真主在2800下撤口埋下金矿,此行只为取回;若同伴相助便分金,不助则将其永远留在此地。",
8491
"persona_en": "Xiaoshan: A newcomer behind the smile; optimism is just a shell. Years ago something was left at the evacuation point; this trek is to retrieve it. Help and share; refuse and be left behind forever.",
@@ -155,7 +162,7 @@ def _get_default_roles(theme: str | None, lang: str | None) -> list[dict]:
155162
use_zh = lang != "en"
156163
return [
157164
{
158-
"name": r["name"],
165+
"name": r["name_en"] if lang == "en" else r["name"],
159166
"avatar_key": r["avatar_key"],
160167
"persona": r["persona_zh"] if use_zh else r["persona_en"],
161168
"attrs": r["attrs"],
@@ -172,9 +179,22 @@ def _get_ws(session_id: str) -> WorldState:
172179

173180

174181
@router.get("/map", response_model=MapResponse)
175-
def get_map(theme: str | None = None):
182+
def get_map(theme: str | None = None, lang: str | None = None):
176183
graph = get_graph(theme)
177-
return MapResponse(start_node_id=graph.start_node_id, nodes=graph.nodes(), edges=graph.edges())
184+
nodes = list(graph.nodes())
185+
edges = list(graph.edges())
186+
# When theme is aotai and lang is en, return English node names and edge labels
187+
if theme == "aotai" and lang == "en":
188+
nodes = [
189+
n.model_copy(update={"name": AOTAI_NODE_NAMES_EN.get(n.node_id, n.name)}) for n in nodes
190+
]
191+
edges = [
192+
e.model_copy(
193+
update={"label": AOTAI_EDGE_LABELS_EN.get((e.from_node_id, e.to_node_id), e.label)}
194+
)
195+
for e in edges
196+
]
197+
return MapResponse(start_node_id=graph.start_node_id, nodes=nodes, edges=edges)
178198

179199

180200
@router.get("/background/{scene_id}", response_model=BackgroundAsset)

demos/AOTAI_Hike/backend/aotai_hike/services/game_service.py

Lines changed: 77 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@
3333
event_observe_label,
3434
event_observe_phrases,
3535
event_rest_phrases,
36+
mem_memory_action_map,
37+
mem_memory_event_action_label,
38+
mem_memory_event_day_line,
39+
mem_memory_event_dialogue_label,
40+
mem_memory_event_recent_label,
41+
mem_memory_query_action_label,
42+
mem_memory_query_events_label,
43+
mem_memory_query_persona_label,
44+
mem_memory_time_map,
45+
mem_memory_weather_map,
46+
mem_teammate_label,
3647
prompt_memory_tag_by_theme,
3748
sys_advance_km_arrived,
3849
sys_advance_km_en_route,
@@ -71,7 +82,7 @@
7182
sys_vote_result_keep,
7283
sys_you_choose_rest,
7384
)
74-
from aotai_hike.world.map_data import get_graph
85+
from aotai_hike.world.map_data import get_graph, get_node_display_name
7586
from loguru import logger
7687

7788

@@ -405,15 +416,13 @@ def _apply_decision(
405416
(r.name for r in world_state.roles if r.role_id == world_state.leader_role_id),
406417
"未知",
407418
)
408-
node_name = (
409-
get_graph(getattr(world_state, "theme", "aotai"))
410-
.get_node(world_state.current_node_id)
411-
.name
412-
)
419+
theme = getattr(world_state, "theme", "aotai")
420+
lang = _lang(world_state)
421+
node_name = get_node_display_name(theme, lang, world_state.current_node_id)
413422
plan_name = (
414-
get_graph(getattr(world_state, "theme", "aotai")).get_node(next_id).name
423+
get_node_display_name(theme, lang, next_id)
415424
if next_id
416-
else "(未选择)"
425+
else ("(not selected)" if lang == "en" else "(未选择)")
417426
)
418427
ev = f"营地共识:在 {node_name},共识路线=去 {plan_name},锁强度={world_state.lock_strength},次日团长={who}"
419428
self._push_event(world_state, ev)
@@ -627,7 +636,11 @@ def _enter_camp_meeting(
627636
# Very light mock proposal: pick one option (or "stay" if none)
628637
if opts:
629638
pick = self._rng.choice(opts)
630-
dest = get_graph(getattr(world_state, "theme", "aotai")).get_node(pick).name
639+
dest = get_node_display_name(
640+
getattr(world_state, "theme", "aotai"),
641+
_lang(world_state),
642+
pick,
643+
)
631644
text = sys_camp_proposal_dest(_lang(world_state), dest)
632645
else:
633646
text = sys_camp_proposal_rest(_lang(world_state))
@@ -690,7 +703,11 @@ def _auto_camp_meeting(
690703
continue
691704
if opts:
692705
pick = self._rng.choice(opts)
693-
dest = get_graph(getattr(world_state, "theme", "aotai")).get_node(pick).name
706+
dest = get_node_display_name(
707+
getattr(world_state, "theme", "aotai"),
708+
_lang(world_state),
709+
pick,
710+
)
694711
text = sys_camp_proposal_dest(_lang(world_state), dest)
695712
else:
696713
text = sys_camp_proposal_rest(_lang(world_state))
@@ -838,9 +855,6 @@ def _apply_action(
838855
if req.action == ActionType.CONTINUE:
839856
req.action = ActionType.MOVE_FORWARD
840857

841-
node = get_graph(getattr(world_state, "theme", "aotai")).get_node(
842-
world_state.current_node_id
843-
)
844858
# UI-only; we keep it empty in the auto-run flow unless we explicitly need a user choice.
845859
world_state.available_next_node_ids = []
846860

@@ -850,7 +864,11 @@ def _apply_action(
850864
kind="system",
851865
content=sys_location_weather_time(
852866
_lang(world_state),
853-
node.name,
867+
get_node_display_name(
868+
getattr(world_state, "theme", "aotai"),
869+
_lang(world_state),
870+
world_state.current_node_id,
871+
),
854872
world_state.weather,
855873
world_state.day,
856874
world_state.time_of_day,
@@ -973,8 +991,10 @@ def _apply_action(
973991

974992
ev = self._rng.choice(event_arrived_phrases(_lang(world_state)))
975993
self._push_event(world_state, ev)
976-
node_name = (
977-
get_graph(getattr(world_state, "theme", "aotai")).get_node(next_id).name
994+
node_name = get_node_display_name(
995+
getattr(world_state, "theme", "aotai"),
996+
_lang(world_state),
997+
next_id,
978998
)
979999
messages.append(
9801000
Message(
@@ -1068,9 +1088,11 @@ def _apply_action(
10681088
content=sys_at_junction_leader_chose(
10691089
_lang(world_state),
10701090
leader_name,
1071-
get_graph(getattr(world_state, "theme", "aotai"))
1072-
.get_node(next_edge.to_node_id)
1073-
.name,
1091+
get_node_display_name(
1092+
getattr(world_state, "theme", "aotai"),
1093+
_lang(world_state),
1094+
next_edge.to_node_id,
1095+
),
10741096
),
10751097
timestamp_ms=now_ms,
10761098
)
@@ -1153,7 +1175,9 @@ def _apply_action(
11531175

11541176
ev = self._rng.choice(event_arrived_phrases_start(_lang(world_state)))
11551177
self._push_event(world_state, ev)
1156-
node_name = get_graph(getattr(world_state, "theme", "aotai")).get_node(next_id).name
1178+
node_name = get_node_display_name(
1179+
getattr(world_state, "theme", "aotai"), _lang(world_state), next_id
1180+
)
11571181
messages.append(
11581182
Message(
11591183
message_id=f"sys-{uuid.uuid4().hex[:8]}",
@@ -1167,10 +1191,10 @@ def _apply_action(
11671191
return f"MOVE_FORWARD:arrive:{next_id}"
11681192

11691193
left = max(0.0, world_state.in_transit_total_km - world_state.in_transit_progress_km)
1170-
to_name = (
1171-
get_graph(getattr(world_state, "theme", "aotai"))
1172-
.get_node(next_edge.to_node_id)
1173-
.name
1194+
to_name = get_node_display_name(
1195+
getattr(world_state, "theme", "aotai"),
1196+
_lang(world_state),
1197+
next_edge.to_node_id,
11741198
)
11751199
messages.append(
11761200
Message(
@@ -1322,66 +1346,55 @@ def _format_memory_event(
13221346
user_action_desc: str,
13231347
messages: list[Message],
13241348
) -> str:
1325-
time_map = {
1326-
"morning": "早晨",
1327-
"noon": "中午",
1328-
"afternoon": "下午",
1329-
"evening": "傍晚",
1330-
"night": "夜晚",
1331-
}
1332-
weather_map = {
1333-
"sunny": "晴",
1334-
"cloudy": "多云",
1335-
"windy": "有风",
1336-
"rainy": "雨",
1337-
"snowy": "雪",
1338-
"foggy": "雾",
1339-
}
1349+
lang = _lang(world_state)
1350+
time_map = mem_memory_time_map(lang)
1351+
weather_map = mem_memory_weather_map(lang)
1352+
action_map = mem_memory_action_map(lang)
13401353

13411354
action_name = getattr(req.action, "name", str(req.action))
1342-
action_cn_map = {
1343-
"SAY": "发言",
1344-
"MOVE_FORWARD": "前进",
1345-
"REST": "休息",
1346-
"CAMP": "扎营",
1347-
"OBSERVE": "观察",
1348-
"DECIDE": "决策",
1349-
}
1350-
action_cn = action_cn_map.get(action_name, action_name)
1355+
action_str = action_map.get(action_name, action_name)
13511356

13521357
day = world_state.day
1353-
tod_cn = time_map.get(world_state.time_of_day, str(world_state.time_of_day))
1354-
weather_cn = weather_map.get(world_state.weather, str(world_state.weather))
1358+
tod = time_map.get(world_state.time_of_day, str(world_state.time_of_day))
1359+
weather = weather_map.get(world_state.weather, str(world_state.weather))
13551360
node_name = getattr(node, "name", "")
13561361

1362+
pieces: list[str] = []
1363+
pieces.append(mem_memory_event_day_line(lang, day, tod, weather, node_name))
1364+
pieces.append(mem_memory_event_action_label(lang) + action_str)
1365+
13571366
recent_events = world_state.recent_events[-3:] if world_state.recent_events else []
1358-
recent_events_txt = ";".join(recent_events)
1367+
if recent_events:
1368+
sep = "; " if lang == "en" else ";"
1369+
pieces.append(mem_memory_event_recent_label(lang) + sep.join(recent_events) + ".")
13591370

13601371
dialogue_msgs = [m for m in messages if m.kind != "system"]
13611372
dialogue_parts: list[str] = []
1373+
teammate = mem_teammate_label(lang)
13621374
for m in dialogue_msgs[:3]:
1363-
speaker = m.role_name or m.role_id or "队员"
1364-
dialogue_parts.append(f"{speaker}{m.content[:40]}")
1365-
dialogue_txt = ";".join(dialogue_parts)
1366-
1367-
pieces: list[str] = []
1368-
pieces.append(f"第{day}天,{tod_cn},天气{weather_cn},位置:{node_name}。")
1369-
pieces.append(f"动作:{action_cn}")
1370-
if recent_events_txt:
1371-
pieces.append(f"最近事件:{recent_events_txt}。")
1372-
if dialogue_txt:
1373-
pieces.append(f"关键对话:{dialogue_txt}。")
1375+
speaker = m.role_name or m.role_id or teammate
1376+
dialogue_parts.append(
1377+
f"{speaker}: {m.content[:40]}" if lang == "en" else f"{speaker}{m.content[:40]}"
1378+
)
1379+
if dialogue_parts:
1380+
sep = "; " if lang == "en" else ";"
1381+
pieces.append(mem_memory_event_dialogue_label(lang) + sep.join(dialogue_parts) + ".")
13741382

13751383
return " ".join(pieces)
13761384

13771385
def _build_memory_query(
13781386
self, world_state: WorldState, req: ActRequest, node, user_action_desc: str
13791387
) -> str:
1388+
lang = _lang(world_state)
13801389
active = self._get_active_role(world_state)
13811390
persona = (active.persona if active else "")[:80]
1382-
ev = ";".join(world_state.recent_events[-3:])
1391+
sep = "; " if lang == "en" else ";"
1392+
ev = sep.join(world_state.recent_events[-3:])
13831393
tag = prompt_memory_tag_by_theme(_theme(world_state))
1384-
return f"{tag} {node.name} {world_state.weather} {world_state.time_of_day} 动作:{req.action} {user_action_desc} 事件:{ev} 人设:{persona}"
1394+
action_label = mem_memory_query_action_label(lang)
1395+
events_label = mem_memory_query_events_label(lang)
1396+
persona_label = mem_memory_query_persona_label(lang)
1397+
return f"{tag} {node.name} {world_state.weather} {world_state.time_of_day} {action_label}{req.action} {user_action_desc} {events_label}{ev} {persona_label}{persona}"
13851398

13861399
def _append_chat_history(self, world_state: WorldState, messages: list[Message]) -> None:
13871400
if not messages:

0 commit comments

Comments
 (0)