tower-learns-you / tools /verify_local_agent.py
vknt's picture
Deploy The Tower Learns You (custom gr.Server frontend, hf_inference + mock fallback)
22a027e verified
Raw
History Blame Contribute Delete
6.33 kB
from __future__ import annotations
import argparse
import json
import sys
from collections import Counter
from pathlib import Path
from time import perf_counter
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from tower_game.ai import LocalOpenAIGateway, MockGateway, load_model_config
from tower_game.engine import TowerGame
def prepared_state(game: TowerGame, seed: int = 7001):
state, _ = game.new_character(seed=seed)
game.confirm_allocation(state, [6, 6, 6, 6])
game.prepare_run(state)
game.choose_starter_skill(state, 0)
return state
def record(rows, operation, gateway, started, fallback=False):
metrics = dict(gateway.agent.last_metrics)
rows.append(
{
"operation": operation,
"latency_ms": round((perf_counter() - started) * 1000),
"fallback": fallback,
"last_error": getattr(gateway, "_last_error", ""),
**metrics,
}
)
def repeated_verification(gateway: LocalOpenAIGateway, repetitions: int):
game = TowerGame(gateway)
state = prepared_state(game)
state["current_floor"] = 10
state["boss_deck"] = [
move.model_dump()
for move in MockGateway().boss_deck(state, final=True).moves
]
rows = []
for index in range(repetitions):
operations = [
("run_setup", lambda: gateway.run_setup(state)),
("class_evolution", lambda: gateway.evolution(state, 1 + index % 2)),
(
"boss_package",
lambda: gateway.boss_package(
state, game.assets.ids("boss"), final=bool(index % 2)
),
),
("boss_adjustment", lambda: gateway.boss_adjustment(state)),
("boss_turn_decision", lambda: gateway.boss_turn_decision(state)),
("ascension", lambda: gateway.ascension(state)),
]
for name, operation in operations:
started = perf_counter()
operation()
record(rows, name, gateway, started, bool(getattr(gateway, "_last_error", "")))
return rows
def full_run(gateway: LocalOpenAIGateway):
print("AI Ascension: generating run setup...", flush=True)
game = TowerGame(gateway)
state = prepared_state(game, seed=8101)
guard = 0
last_phase = ""
while state["game_phase"] != "ascension" and guard < 200:
guard += 1
phase = state["game_phase"]
if phase != last_phase:
print(
f"AI Ascension: {phase} / floor {state.get('current_floor')}",
flush=True,
)
last_phase = phase
if phase == "combat":
state["enemy_evasion"] = 0
state["enemy_hp"] = 1
game.act(state, "strike")
if state.get("boss_thinking") and state["game_phase"] == "combat":
decision, status = game.generate_boss_decision(state)
game.apply_boss_decision(state, decision, status)
elif phase == "victory":
if state["victory_step"] == "level_skill":
game.choose_level_skill(state, 0)
if state["game_phase"] == "skill_replacement":
game.replace_skill(state, 0)
elif state["victory_step"] == "loot":
while not state["proceed_ready"]:
index = next(
i
for i, reward in enumerate(state["pending_loot"])
if not reward.get("claimed")
)
game.choose_loot(state, index)
game.proceed(state)
else:
game.proceed(state)
elif phase == "evolution_loading":
evolution, status = game.generate_evolution(state)
game.complete_evolution_generation(state, evolution, status)
elif phase == "evolution_reveal":
game.embrace_evolution(state)
elif phase == "evolution_healing":
game.heal_choice(state, True)
elif phase in {"skill_replacement", "evolution_skill_replace"}:
game.replace_skill(state, 0)
elif phase == "boss_loading":
package, decision, status = game.generate_boss_intro(state)
game.complete_boss_intro(state, package, decision, status)
elif phase == "ascension_loading":
passive, status = game.generate_ascension(state)
game.complete_ascension(state, passive, status)
elif phase == "defeat":
raise RuntimeError("AI-enabled verification run was defeated")
else:
raise RuntimeError(f"unexpected phase during verification: {phase}")
if state["game_phase"] != "ascension":
raise RuntimeError("AI-enabled run did not reach Ascension")
return {
"floor": state["current_floor"],
"ascension": state["ascension_level"],
"fallbacks": state["agent_status"]["fallback_count"],
}
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--repetitions", type=int, default=20)
parser.add_argument("--skip-full-run", action="store_true")
parser.add_argument(
"--output", type=Path, default=Path(".local/llama.cpp/verification.json")
)
args = parser.parse_args()
config = load_model_config()
config["backend"] = "local_openai"
gateway = LocalOpenAIGateway(config)
rows = repeated_verification(gateway, args.repetitions)
summary = {
"calls": len(rows),
"fallbacks": sum(1 for row in rows if row["fallback"]),
"operations": dict(Counter(row["operation"] for row in rows)),
"average_latency_ms": round(
sum(row["latency_ms"] for row in rows) / max(1, len(rows))
),
"results": rows,
}
if not args.skip_full_run:
summary["full_run"] = full_run(gateway)
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(summary, indent=2), encoding="utf-8")
print(json.dumps({key: value for key, value in summary.items() if key != "results"}, indent=2))
if __name__ == "__main__":
main()