rnow 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. rnow/__init__.py +5 -0
  2. rnow/__main__.py +7 -0
  3. rnow/cli/__init__.py +6 -0
  4. rnow/cli/auth.py +67 -0
  5. rnow/cli/blob.py +98 -0
  6. rnow/cli/commands.py +2311 -0
  7. rnow/cli/common.py +28 -0
  8. rnow/cli/cube.py +255 -0
  9. rnow/cli/main.py +49 -0
  10. rnow/cli/test.py +728 -0
  11. rnow/cli/token_count.py +295 -0
  12. rnow/core/__init__.py +33 -0
  13. rnow/core/reward.py +333 -0
  14. rnow/core/tool.py +494 -0
  15. rnow/models.py +295 -0
  16. rnow/templates/deepseek-aha/config.yml +26 -0
  17. rnow/templates/deepseek-aha/rewards.py +36 -0
  18. rnow/templates/deepseek-aha/train.jsonl +1000 -0
  19. rnow/templates/mcp-tavily/config.yml +29 -0
  20. rnow/templates/mcp-tavily/requirements.txt +1 -0
  21. rnow/templates/mcp-tavily/rewards.py +25 -0
  22. rnow/templates/mcp-tavily/train.jsonl +500 -0
  23. rnow/templates/new/config.yml +26 -0
  24. rnow/templates/new/requirements.txt +1 -0
  25. rnow/templates/new/rewards.py +0 -0
  26. rnow/templates/new/train.jsonl +0 -0
  27. rnow/templates/rl-nextjs/config.yml +27 -0
  28. rnow/templates/rl-nextjs/requirements.txt +2 -0
  29. rnow/templates/rl-nextjs/rewards.py +446 -0
  30. rnow/templates/rl-nextjs/train.jsonl +1000 -0
  31. rnow/templates/rl-single/config.yml +27 -0
  32. rnow/templates/rl-single/requirements.txt +1 -0
  33. rnow/templates/rl-single/rewards.py +14 -0
  34. rnow/templates/rl-single/train.jsonl +1000 -0
  35. rnow/templates/rl-tools/config.yml +27 -0
  36. rnow/templates/rl-tools/env.py +38 -0
  37. rnow/templates/rl-tools/requirements.txt +3 -0
  38. rnow/templates/rl-tools/rewards.py +25 -0
  39. rnow/templates/rl-tools/train.jsonl +500 -0
  40. rnow/templates/sft/config.yml +20 -0
  41. rnow/templates/sft/train.jsonl +100 -0
  42. rnow/templates/tutorial-reward/config.yml +27 -0
  43. rnow/templates/tutorial-reward/requirements.txt +1 -0
  44. rnow/templates/tutorial-reward/rewards.py +15 -0
  45. rnow/templates/tutorial-reward/train.jsonl +1000 -0
  46. rnow/templates/tutorial-tool/config.yml +27 -0
  47. rnow/templates/tutorial-tool/env.py +7 -0
  48. rnow/templates/tutorial-tool/requirements.txt +3 -0
  49. rnow/templates/tutorial-tool/rewards.py +7 -0
  50. rnow/templates/tutorial-tool/train.jsonl +1266 -0
  51. rnow-0.2.4.dist-info/METADATA +135 -0
  52. rnow-0.2.4.dist-info/RECORD +56 -0
  53. rnow-0.2.4.dist-info/WHEEL +5 -0
  54. rnow-0.2.4.dist-info/entry_points.txt +2 -0
  55. rnow-0.2.4.dist-info/licenses/LICENSE +21 -0
  56. rnow-0.2.4.dist-info/top_level.txt +1 -0
rnow/cli/common.py ADDED
@@ -0,0 +1,28 @@
1
+ # reinforcenow/cli/common.py
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from rnow.cli import auth
8
+
9
+
10
+ def get_active_organization():
11
+ """Get active organization ID (config > credentials)."""
12
+ # First check config
13
+ org_id = auth.get_active_org_from_config()
14
+ if org_id:
15
+ return org_id
16
+
17
+ # Fall back to credentials
18
+ try:
19
+ with open(auth.CREDS_FILE) as f:
20
+ return json.load(f).get("organization_id")
21
+ except (FileNotFoundError, json.JSONDecodeError, KeyError):
22
+ return None
23
+
24
+
25
+ def require_auth():
26
+ """Ensure authenticated."""
27
+ if not auth.is_authenticated():
28
+ raise click.ClickException("Not authenticated. Run 'reinforcenow login'")
rnow/cli/cube.py ADDED
@@ -0,0 +1,255 @@
1
+ # reinforcenow/cli/cube.py
2
+ """ASCII 3D rotating cube animation for loading states.
3
+
4
+ Inspired by https://github.com/leonmavr/retrovoxel
5
+ Voxel-style cube rendering with depth-based shading.
6
+ """
7
+
8
+ import contextlib
9
+ import math
10
+ import os
11
+ import sys
12
+ import threading
13
+ import time
14
+
15
+ # Display settings
16
+ WIDTH = 50
17
+ HEIGHT = 18
18
+ FRAME_DELAY = 0.04
19
+
20
+ # ReinforceNow teal: #14B8A6
21
+ TEAL_RGB = (20, 184, 166)
22
+ TEAL = "\033[38;2;20;184;166m"
23
+ RESET = "\033[0m"
24
+
25
+ # Cube faces with shading characters (front to back)
26
+ CHARS = ".,-~:;=!*#$@"
27
+
28
+
29
+ class VoxelCube:
30
+ """Voxel-style 3D cube renderer."""
31
+
32
+ def __init__(self, width=WIDTH, height=HEIGHT):
33
+ self.width = width
34
+ self.height = height
35
+ self.A = 0 # Rotation angle X
36
+ self.B = 0 # Rotation angle Y
37
+ self.C = 0 # Rotation angle Z
38
+ self.cube_size = 8
39
+ self.distance = 40
40
+ self.k1 = 20 # Projection constant
41
+
42
+ def calculate_x(self, i, j, k):
43
+ """Calculate rotated X coordinate."""
44
+ return (
45
+ j * math.sin(self.A) * math.sin(self.B) * math.cos(self.C)
46
+ - k * math.cos(self.A) * math.sin(self.B) * math.cos(self.C)
47
+ + j * math.cos(self.A) * math.sin(self.C)
48
+ + k * math.sin(self.A) * math.sin(self.C)
49
+ + i * math.cos(self.B) * math.cos(self.C)
50
+ )
51
+
52
+ def calculate_y(self, i, j, k):
53
+ """Calculate rotated Y coordinate."""
54
+ return (
55
+ j * math.cos(self.A) * math.cos(self.C)
56
+ + k * math.sin(self.A) * math.cos(self.C)
57
+ - j * math.sin(self.A) * math.sin(self.B) * math.sin(self.C)
58
+ + k * math.cos(self.A) * math.sin(self.B) * math.sin(self.C)
59
+ - i * math.cos(self.B) * math.sin(self.C)
60
+ )
61
+
62
+ def calculate_z(self, i, j, k):
63
+ """Calculate rotated Z coordinate."""
64
+ return (
65
+ k * math.cos(self.A) * math.cos(self.B)
66
+ - j * math.sin(self.A) * math.cos(self.B)
67
+ + i * math.sin(self.B)
68
+ )
69
+
70
+ def render_frame(self):
71
+ """Render a single frame of the rotating cube."""
72
+ output = [[" " for _ in range(self.width)] for _ in range(self.height)]
73
+ z_buffer = [[0 for _ in range(self.width)] for _ in range(self.height)]
74
+
75
+ # Render each face of the cube
76
+ cube_size = self.cube_size
77
+ increment = 0.6
78
+
79
+ for face in range(6):
80
+ # Generate points for each face
81
+ if face == 0: # Front
82
+ points = [
83
+ (i, j, -cube_size)
84
+ for i in self._frange(-cube_size, cube_size, increment)
85
+ for j in self._frange(-cube_size, cube_size, increment)
86
+ ]
87
+ char_idx = 0
88
+ elif face == 1: # Back
89
+ points = [
90
+ (i, j, cube_size)
91
+ for i in self._frange(-cube_size, cube_size, increment)
92
+ for j in self._frange(-cube_size, cube_size, increment)
93
+ ]
94
+ char_idx = 2
95
+ elif face == 2: # Left
96
+ points = [
97
+ (-cube_size, j, k)
98
+ for j in self._frange(-cube_size, cube_size, increment)
99
+ for k in self._frange(-cube_size, cube_size, increment)
100
+ ]
101
+ char_idx = 4
102
+ elif face == 3: # Right
103
+ points = [
104
+ (cube_size, j, k)
105
+ for j in self._frange(-cube_size, cube_size, increment)
106
+ for k in self._frange(-cube_size, cube_size, increment)
107
+ ]
108
+ char_idx = 6
109
+ elif face == 4: # Top
110
+ points = [
111
+ (i, -cube_size, k)
112
+ for i in self._frange(-cube_size, cube_size, increment)
113
+ for k in self._frange(-cube_size, cube_size, increment)
114
+ ]
115
+ char_idx = 8
116
+ else: # Bottom
117
+ points = [
118
+ (i, cube_size, k)
119
+ for i in self._frange(-cube_size, cube_size, increment)
120
+ for k in self._frange(-cube_size, cube_size, increment)
121
+ ]
122
+ char_idx = 10
123
+
124
+ for i, j, k in points:
125
+ x = self.calculate_x(i, j, k)
126
+ y = self.calculate_y(i, j, k)
127
+ z = self.calculate_z(i, j, k) + self.distance
128
+
129
+ ooz = 1 / z if z != 0 else 0
130
+
131
+ xp = int(self.width / 2 + self.k1 * ooz * x * 2)
132
+ yp = int(self.height / 2 + self.k1 * ooz * y)
133
+
134
+ if 0 <= xp < self.width and 0 <= yp < self.height and ooz > z_buffer[yp][xp]:
135
+ z_buffer[yp][xp] = ooz
136
+ output[yp][xp] = CHARS[char_idx % len(CHARS)]
137
+
138
+ return "\n".join("".join(row) for row in output)
139
+
140
+ def _frange(self, start, stop, step):
141
+ """Float range generator."""
142
+ vals = []
143
+ val = start
144
+ while val < stop:
145
+ vals.append(val)
146
+ val += step
147
+ return vals
148
+
149
+ def rotate(self, da=0.04, db=0.02, dc=0.01):
150
+ """Update rotation angles."""
151
+ self.A += da
152
+ self.B += db
153
+ self.C += dc
154
+
155
+
156
+ class CubeSpinner:
157
+ """Animated ASCII 3D rotating cube spinner."""
158
+
159
+ def __init__(self):
160
+ self.cube = VoxelCube()
161
+ self.running = False
162
+ self.thread = None
163
+ self.message = ""
164
+ self.lines_printed = 0
165
+
166
+ # Enable Windows ANSI support
167
+ if os.name == "nt":
168
+ os.system("")
169
+
170
+ def _animation_loop(self):
171
+ """Main animation loop."""
172
+ first_frame = True
173
+ while self.running:
174
+ frame = self.cube.render_frame()
175
+ self.cube.rotate()
176
+
177
+ # Strip trailing newlines to get accurate line count
178
+ frame = frame.rstrip("\n")
179
+ colored_frame = f"{TEAL}{frame}{RESET}"
180
+
181
+ # Count actual lines in this frame
182
+ lines_this_frame = frame.count("\n") + 1
183
+
184
+ # Move back to top of previous frame (no clearing - new frame overwrites old)
185
+ if not first_frame and self.lines_printed > 0:
186
+ sys.stdout.write(f"\033[{self.lines_printed}A")
187
+ sys.stdout.write("\r")
188
+
189
+ # Print the new frame with newline so cursor ends below
190
+ sys.stdout.write(colored_frame + "\n")
191
+ sys.stdout.flush()
192
+
193
+ self.lines_printed = lines_this_frame
194
+ first_frame = False
195
+ time.sleep(FRAME_DELAY)
196
+
197
+ def start(self, message=""):
198
+ """Start the animation."""
199
+ self.message = message
200
+ self.running = True
201
+ self.lines_printed = 0
202
+ self.thread = threading.Thread(target=self._animation_loop, daemon=True)
203
+ self.thread.start()
204
+
205
+ def update_message(self, message):
206
+ """Update the message."""
207
+ self.message = message
208
+
209
+ def stop(self, keep_visible=False):
210
+ """Stop the animation.
211
+
212
+ Args:
213
+ keep_visible: If True, leave the cube frozen in place.
214
+ If False (default), clear the cube from output.
215
+ """
216
+ self.running = False
217
+ if self.thread:
218
+ self.thread.join(timeout=0.1)
219
+
220
+ if keep_visible:
221
+ # Leave the cube visible, just move cursor below it
222
+ sys.stdout.write("\n")
223
+ sys.stdout.flush()
224
+ elif self.lines_printed > 0:
225
+ # Cursor is currently one line below the cube
226
+ # Move back up to the first line of the cube
227
+ sys.stdout.write(f"\033[{self.lines_printed}A")
228
+ sys.stdout.write("\r")
229
+
230
+ # Clear exactly self.lines_printed lines (not the whole screen)
231
+ for i in range(self.lines_printed):
232
+ sys.stdout.write("\033[2K") # Clear current line only
233
+ if i < self.lines_printed - 1:
234
+ sys.stdout.write("\n")
235
+
236
+ # Move cursor back up to where the first line was
237
+ if self.lines_printed > 1:
238
+ sys.stdout.write(f"\033[{self.lines_printed - 1}A")
239
+ sys.stdout.write("\r")
240
+ sys.stdout.flush()
241
+
242
+
243
+ if __name__ == "__main__":
244
+ spinner = CubeSpinner()
245
+ spinner.start("Uploading...")
246
+ with contextlib.suppress(KeyboardInterrupt):
247
+ time.sleep(3)
248
+ spinner.stop(keep_visible=True)
249
+ print(f"Run started successfully {TEAL}✅{RESET}")
250
+ print(" Project: My Project")
251
+ print(" Model: Qwen/Qwen3-8B")
252
+ print(" Dataset: train.jsonl")
253
+ print()
254
+ print("View your experiment here:")
255
+ print(f"{TEAL}https://reinforcenow.ai/run/abc123{RESET}")
rnow/cli/main.py ADDED
@@ -0,0 +1,49 @@
1
+ # reinforcenow/cli/main.py
2
+
3
+ import click
4
+
5
+ from rnow.cli.commands import download, init, login, logout, orgs, run, status, stop
6
+ from rnow.cli.test import test
7
+
8
+ try:
9
+ from importlib.metadata import version as get_version
10
+
11
+ __version__ = get_version("rnow")
12
+ except Exception:
13
+ __version__ = "unknown"
14
+
15
+
16
+ @click.group(context_settings={"help_option_names": ["-h", "--help"]})
17
+ @click.option("--api-url", default="https://www.reinforcenow.ai/api", hidden=True)
18
+ @click.option("--debug", is_flag=True, hidden=True)
19
+ @click.version_option(version=__version__, prog_name="rnow")
20
+ @click.pass_context
21
+ def cli(ctx, api_url, debug):
22
+ """
23
+ \b
24
+ \033[1m\033[38;2;20;184;166mTrain language models with reinforcement learning.\033[0m
25
+ """
26
+ ctx.ensure_object(dict)
27
+ ctx.obj["api_url"] = api_url
28
+ ctx.obj["debug"] = debug
29
+
30
+
31
+ # Add commands
32
+ cli.add_command(login)
33
+ cli.add_command(logout)
34
+ cli.add_command(status)
35
+ cli.add_command(orgs)
36
+ cli.add_command(init)
37
+ cli.add_command(run)
38
+ cli.add_command(stop)
39
+ cli.add_command(download)
40
+ cli.add_command(test)
41
+
42
+
43
+ def main():
44
+ """Entry point."""
45
+ cli(auto_envvar_prefix="REINFORCE")
46
+
47
+
48
+ if __name__ == "__main__":
49
+ main()