Skip to content

Commit c93f1f2

Browse files
authored
Add formal mathematics example with RL and SFT (#733)
1 parent 490660a commit c93f1f2

File tree

7 files changed

+993
-0
lines changed

7 files changed

+993
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Usage
2+
3+
For the minimal demo:
4+
5+
```shell
6+
# install dependencies
7+
apt update && apt install -y docker-cli
8+
pip install kimina-client polars
9+
10+
# prepare data
11+
python examples/formal_math/single_round/prepare_data.py --output-name minimal_demo
12+
13+
# prepare ray, model, test dataset, etc
14+
# normally just use this script, but here we want to demonstrate run_minimal.py, thus skip ray-submit part
15+
SLIME_SCRIPT_ENABLE_RAY_SUBMIT=0 python examples/formal_math/single_round/run.py
16+
17+
# run
18+
python examples/formal_math/single_round/run_minimal.py
19+
```
20+
21+
The code also support more complicated cases, e.g.:
22+
23+
* SFT + RL
24+
* Data filter + RL
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import datetime
2+
import os
3+
import random
4+
import time
5+
from typing import List
6+
7+
import ray
8+
import requests
9+
from kimina_client import AsyncKiminaClient, CheckResponse
10+
from ray.util.scheduling_strategies import NodeAffinitySchedulingStrategy
11+
12+
from slime.utils.misc import exec_command, get_free_port
13+
14+
# TODO handle docker stop more gracefully later
15+
_KILL_PREVIOUS_KIMINA_DOCKER = bool(int(os.environ.get("SLIME_KILL_PREVIOUS_KIMINA_DOCKER", "1")))
16+
17+
18+
class KiminaServerAndClientCluster:
19+
def __init__(self):
20+
self._servers = _create_actor_per_node(actor_cls=_KiminaServerActor)
21+
self._client_cluster = _KiminaClientCluster(self._servers)
22+
23+
async def check(self, *args, **kwargs) -> CheckResponse:
24+
return await self._client_cluster.check(*args, **kwargs)
25+
26+
27+
class _KiminaClientCluster:
28+
def __init__(self, servers: List["_KiminaServerActor"]):
29+
self._clients = [AsyncKiminaClient(api_url=ray.get(server.get_api_url.remote())) for server in servers]
30+
self._next_client_index = 0
31+
32+
async def check(self, *args, **kwargs):
33+
client = self._clients[self._next_client_index]
34+
self._next_client_index = (self._next_client_index + 1) % len(self._clients)
35+
return await client.check(*args, **kwargs)
36+
37+
38+
def _create_actor_per_node(actor_cls) -> List:
39+
# for simplicity, we use all available nodes
40+
nodes = [n for n in ray.nodes() if n.get("Alive")]
41+
assert len(nodes) > 0
42+
43+
actors = []
44+
for node in nodes:
45+
actors.append(
46+
actor_cls.options(
47+
name=None,
48+
lifetime="detached",
49+
scheduling_strategy=NodeAffinitySchedulingStrategy(node_id=node["NodeID"], soft=False),
50+
num_cpus=0.001,
51+
).remote()
52+
)
53+
54+
return actors
55+
56+
57+
@ray.remote
58+
class _KiminaServerActor:
59+
def __init__(self):
60+
self.addr = _get_current_node_host_ip()
61+
self.port = get_free_port()
62+
63+
if _KILL_PREVIOUS_KIMINA_DOCKER:
64+
_docker_stop_all()
65+
66+
_docker_start(port=self.port)
67+
_wait_server_ready(base_url=self.get_api_url())
68+
69+
def get_api_url(self):
70+
return f"http://{self.addr}:{self.port}"
71+
72+
73+
def _docker_start(port: int):
74+
name = f"kimina_lean_server_auto_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}-{random.randint(0, 1000000)}"
75+
exec_command(
76+
"docker run "
77+
"-d "
78+
f"--name {name} "
79+
"--restart unless-stopped "
80+
# "--env-file .env " # do not use env yet
81+
f"-p {port}:8000 "
82+
f"projectnumina/kimina-lean-server:2.0.0"
83+
)
84+
85+
86+
def _wait_server_ready(base_url: str):
87+
with requests.Session() as session:
88+
while True:
89+
try:
90+
response = session.get(f"{base_url}/health")
91+
if response.status_code == 200:
92+
break
93+
except requests.RequestException:
94+
pass
95+
print(f"Wait kimina server ready ({base_url})...")
96+
time.sleep(2)
97+
98+
99+
def _docker_stop_all():
100+
exec_command(
101+
'ids=$(docker ps -a --filter "name=kimina_lean_server_auto" -q); '
102+
'[ -n "$ids" ] && docker stop $ids && docker rm $ids; '
103+
"true"
104+
)
105+
106+
107+
def _get_current_node_host_ip():
108+
# when RL container uses network=host
109+
return "127.0.0.1"
110+
111+
# when RL container does not use network=host
112+
# https://stackoverflow.com/questions/22944631
113+
# out = exec_command("ip route show default | awk '/default/ {print $3}'", capture_output=True)
114+
# return out.strip()

0 commit comments

Comments
 (0)