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
@@ -0,0 +1,26 @@
1
+ project_id: ""
2
+ project_name: "rlvr-project"
3
+ dataset_id: ""
4
+ dataset_name: "train"
5
+ dataset_type: rl
6
+ organization_id: ""
7
+ data:
8
+ train_file: train.jsonl
9
+ batch_size: 32
10
+ group_size: 16
11
+ model:
12
+ path: Qwen/Qwen3-8B
13
+ qlora_rank: 32
14
+ name: "My RL Model"
15
+ description: "Reinforcement learning fine-tuned model"
16
+ algorithm:
17
+ loss_fn: ppo
18
+ adv_estimator: grpo
19
+ kl_penalty_coef: 0.01
20
+ rollout:
21
+ max_turns: 1
22
+ max_tokens: 16384
23
+ trainer:
24
+ num_epochs: 30
25
+ learning_rate: 0.0001
26
+ save_step: 20
@@ -0,0 +1 @@
1
+ # Add your project dependencies here
File without changes
File without changes
@@ -0,0 +1,27 @@
1
+ project_id: ""
2
+ project_name: "Next.js Code Generation"
3
+ dataset_id: ""
4
+ dataset_name: "nextjs-components"
5
+ dataset_type: rl
6
+ organization_id: ""
7
+ data:
8
+ train_file: train.jsonl
9
+ batch_size: 32
10
+ group_size: 16
11
+ model:
12
+ path: Qwen/Qwen3-8B
13
+ qlora_rank: 32
14
+ name: "Next.js Code Generator"
15
+ description: "RL model for Next.js code generation"
16
+ algorithm:
17
+ loss_fn: ppo
18
+ adv_estimator: grpo
19
+ kl_penalty_coef: 0.01
20
+ rollout:
21
+ max_turns: 1
22
+ max_tokens: 16384
23
+ termination_policy: max_turns
24
+ trainer:
25
+ num_epochs: 10
26
+ learning_rate: 0.0001
27
+ save_step: 833
@@ -0,0 +1,2 @@
1
+ ast-grep-py>=0.29.3
2
+ pydantic>=2.0.0
@@ -0,0 +1,446 @@
1
+ """
2
+ Reward functions for Next.js ast-grep rules using ReinforceNow framework.
3
+ Each reward function checks if the generated code matches the expected ast-grep pattern.
4
+ """
5
+
6
+ import re
7
+
8
+ from ast_grep_py import Config, SgRoot
9
+
10
+ from rnow.core import RewardArgs, reward
11
+
12
+
13
+ @reward(precondition=True)
14
+ def code_block(args: RewardArgs, messages: list) -> float:
15
+ """Precondition: Response must contain a ```typescript/tsx code block."""
16
+ response = messages[-1].get("content", "")
17
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
18
+ return 1.0 if match else 0.0
19
+
20
+
21
+ @reward
22
+ def layout_syntax_1(args: RewardArgs, messages: list) -> float:
23
+ """Reward for correct Next.js layout syntax with children prop."""
24
+ response = messages[-1].get("content", "")
25
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
26
+ if not match:
27
+ return 0.0
28
+
29
+ try:
30
+ root = SgRoot(match.group(1).strip(), "tsx").root()
31
+ matches = root.find_all(
32
+ Config(
33
+ rule={
34
+ "all": [
35
+ {
36
+ "pattern": "function $NAME({ children }: { children: React.ReactNode }) { $$$BODY }"
37
+ },
38
+ {"kind": "function_declaration"},
39
+ {"has": {"pattern": "children", "stopBy": "end"}},
40
+ ]
41
+ }
42
+ )
43
+ )
44
+ return 1.0 if matches else 0.0
45
+ except Exception:
46
+ return 0.0
47
+
48
+
49
+ @reward
50
+ def server_dynamic_segment_1(args: RewardArgs, messages: list) -> float:
51
+ """Reward for correct async param extraction in dynamic segment pages."""
52
+ response = messages[-1].get("content", "")
53
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
54
+ if not match:
55
+ return 0.0
56
+
57
+ try:
58
+ root = SgRoot(match.group(1).strip(), "tsx").root()
59
+ matches = root.find_all(
60
+ Config(
61
+ rule={
62
+ "all": [
63
+ {"pattern": "function $FUNC($$$ARGS) { $$$BODY }"},
64
+ {"kind": "function_declaration"},
65
+ {"has": {"pattern": "const { $VAR2 } = await params", "stopBy": "end"}},
66
+ ]
67
+ }
68
+ )
69
+ )
70
+ return 1.0 if matches else 0.0
71
+ except Exception:
72
+ return 0.0
73
+
74
+
75
+ @reward
76
+ def server_dynamic_segment_2(args: RewardArgs, messages: list) -> float:
77
+ """Reward for generateStaticParams pattern."""
78
+ response = messages[-1].get("content", "")
79
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
80
+ if not match:
81
+ return 0.0
82
+
83
+ try:
84
+ root = SgRoot(match.group(1).strip(), "tsx").root()
85
+ matches = root.find_all(
86
+ Config(
87
+ rule={
88
+ "kind": "program",
89
+ "all": [
90
+ {
91
+ "has": {
92
+ "pattern": "function generateStaticParams() { $$$BODY }",
93
+ "has": {
94
+ "pattern": "return posts.map((post) => ({ slug: post.slug, }))",
95
+ "stopBy": "end",
96
+ },
97
+ "stopBy": "end",
98
+ }
99
+ }
100
+ ],
101
+ }
102
+ )
103
+ )
104
+ return 1.0 if matches else 0.0
105
+ except Exception:
106
+ return 0.0
107
+
108
+
109
+ @reward
110
+ def server_search_params(args: RewardArgs, messages: list) -> float:
111
+ """Reward for correct server searchParams handling."""
112
+ response = messages[-1].get("content", "")
113
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
114
+ if not match:
115
+ return 0.0
116
+
117
+ try:
118
+ root = SgRoot(match.group(1).strip(), "tsx").root()
119
+ matches = root.find_all(
120
+ Config(
121
+ rule={
122
+ "all": [
123
+ {
124
+ "pattern": "async function $FUNC({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) { $$$BODY }"
125
+ },
126
+ {"kind": "function_declaration"},
127
+ {
128
+ "has": {
129
+ "pattern": "const $VAR = (await searchParams).$VAR",
130
+ "stopBy": "end",
131
+ }
132
+ },
133
+ ]
134
+ }
135
+ )
136
+ )
137
+ return 1.0 if matches else 0.0
138
+ except Exception:
139
+ return 0.0
140
+
141
+
142
+ @reward
143
+ def use_client_directive(args: RewardArgs, messages: list) -> float:
144
+ """Reward for correct 'use client' directive placement."""
145
+ response = messages[-1].get("content", "")
146
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
147
+ if not match:
148
+ return 0.0
149
+
150
+ try:
151
+ root = SgRoot(match.group(1).strip(), "tsx").root()
152
+ matches = root.find_all(Config(rule={"kind": "string", "pattern": '"use client"'}))
153
+ return 1.0 if matches else 0.0
154
+ except Exception:
155
+ return 0.0
156
+
157
+
158
+ @reward
159
+ def metadata_export(args: RewardArgs, messages: list) -> float:
160
+ """Reward for valid metadata export."""
161
+ response = messages[-1].get("content", "")
162
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
163
+ if not match:
164
+ return 0.0
165
+
166
+ try:
167
+ root = SgRoot(match.group(1).strip(), "tsx").root()
168
+ matches = root.find_all(Config(rule={"pattern": "export const metadata = { $$$BODY }"}))
169
+ return 1.0 if matches else 0.0
170
+ except Exception:
171
+ return 0.0
172
+
173
+
174
+ @reward
175
+ def error_boundary(args: RewardArgs, messages: list) -> float:
176
+ """Reward for valid error boundary component."""
177
+ response = messages[-1].get("content", "")
178
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
179
+ if not match:
180
+ return 0.0
181
+
182
+ try:
183
+ root = SgRoot(match.group(1).strip(), "tsx").root()
184
+ matches = root.find_all(
185
+ Config(
186
+ rule={
187
+ "all": [
188
+ {
189
+ "pattern": """export default function Error({
190
+ error,
191
+ reset,
192
+ }: {
193
+ error: Error
194
+ reset: () => void
195
+ }) {
196
+ $$$BODY
197
+ }"""
198
+ },
199
+ {"has": {"pattern": "reset()", "stopBy": "end"}},
200
+ ]
201
+ }
202
+ )
203
+ )
204
+ return 1.0 if matches else 0.0
205
+ except Exception:
206
+ return 0.0
207
+
208
+
209
+ @reward
210
+ def not_found_boundary(args: RewardArgs, messages: list) -> float:
211
+ """Reward for not-found boundary component."""
212
+ response = messages[-1].get("content", "")
213
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
214
+ if not match:
215
+ return 0.0
216
+
217
+ try:
218
+ root = SgRoot(match.group(1).strip(), "tsx").root()
219
+ matches = root.find_all(
220
+ Config(rule={"pattern": "export default function NotFound() { $$$BODY }"})
221
+ )
222
+ return 1.0 if matches else 0.0
223
+ except Exception:
224
+ return 0.0
225
+
226
+
227
+ @reward
228
+ def loading_boundary(args: RewardArgs, messages: list) -> float:
229
+ """Reward for loading boundary component."""
230
+ response = messages[-1].get("content", "")
231
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
232
+ if not match:
233
+ return 0.0
234
+
235
+ try:
236
+ root = SgRoot(match.group(1).strip(), "tsx").root()
237
+ matches = root.find_all(
238
+ Config(rule={"pattern": "export default function Loading() { $$$BODY }"})
239
+ )
240
+ return 1.0 if matches else 0.0
241
+ except Exception:
242
+ return 0.0
243
+
244
+
245
+ @reward
246
+ def template_component(args: RewardArgs, messages: list) -> float:
247
+ """Reward for template component."""
248
+ response = messages[-1].get("content", "")
249
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
250
+ if not match:
251
+ return 0.0
252
+
253
+ try:
254
+ root = SgRoot(match.group(1).strip(), "tsx").root()
255
+ matches = root.find_all(
256
+ Config(
257
+ rule={
258
+ "all": [
259
+ {
260
+ "pattern": "export default function Template({ children }: { children: React.ReactNode }) { $$$BODY }"
261
+ },
262
+ {"has": {"pattern": "children", "stopBy": "end"}},
263
+ ]
264
+ }
265
+ )
266
+ )
267
+ return 1.0 if matches else 0.0
268
+ except Exception:
269
+ return 0.0
270
+
271
+
272
+ @reward
273
+ def redirect_usage(args: RewardArgs, messages: list) -> float:
274
+ """Reward for usage of Next.js redirect() helper."""
275
+ response = messages[-1].get("content", "")
276
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
277
+ if not match:
278
+ return 0.0
279
+
280
+ try:
281
+ root = SgRoot(match.group(1).strip(), "tsx").root()
282
+ matches = root.find_all(
283
+ Config(rule={"kind": "program", "has": {"pattern": "redirect(", "stopBy": "end"}})
284
+ )
285
+ return 1.0 if matches else 0.0
286
+ except Exception:
287
+ return 0.0
288
+
289
+
290
+ @reward
291
+ def notfound_function_usage(args: RewardArgs, messages: list) -> float:
292
+ """Reward for usage of Next.js notFound() function."""
293
+ response = messages[-1].get("content", "")
294
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
295
+ if not match:
296
+ return 0.0
297
+
298
+ try:
299
+ root = SgRoot(match.group(1).strip(), "tsx").root()
300
+ matches = root.find_all(
301
+ Config(rule={"kind": "program", "has": {"pattern": "notFound(", "stopBy": "end"}})
302
+ )
303
+ return 1.0 if matches else 0.0
304
+ except Exception:
305
+ return 0.0
306
+
307
+
308
+ @reward
309
+ def generate_metadata_function(args: RewardArgs, messages: list) -> float:
310
+ """Reward for dynamic generateMetadata() function."""
311
+ response = messages[-1].get("content", "")
312
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
313
+ if not match:
314
+ return 0.0
315
+
316
+ try:
317
+ root = SgRoot(match.group(1).strip(), "tsx").root()
318
+ matches = root.find_all(
319
+ Config(rule={"pattern": "export async function generateMetadata() { $$$BODY }"})
320
+ )
321
+ return 1.0 if matches else 0.0
322
+ except Exception:
323
+ return 0.0
324
+
325
+
326
+ @reward
327
+ def generate_metadata_object(args: RewardArgs, messages: list) -> float:
328
+ """Reward for static metadata object export."""
329
+ response = messages[-1].get("content", "")
330
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
331
+ if not match:
332
+ return 0.0
333
+
334
+ try:
335
+ root = SgRoot(match.group(1).strip(), "tsx").root()
336
+ matches = root.find_all(Config(rule={"pattern": "export const metadata = { $$$BODY }"}))
337
+ return 1.0 if matches else 0.0
338
+ except Exception:
339
+ return 0.0
340
+
341
+
342
+ @reward
343
+ def route_handler_get(args: RewardArgs, messages: list) -> float:
344
+ """Reward for GET route handler in Next.js Route Handlers."""
345
+ response = messages[-1].get("content", "")
346
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
347
+ if not match:
348
+ return 0.0
349
+
350
+ try:
351
+ root = SgRoot(match.group(1).strip(), "tsx").root()
352
+ matches = root.find_all(
353
+ Config(rule={"pattern": "export async function GET(request: Request) { $$$BODY }"})
354
+ )
355
+ return 1.0 if matches else 0.0
356
+ except Exception:
357
+ return 0.0
358
+
359
+
360
+ @reward
361
+ def route_handler_post(args: RewardArgs, messages: list) -> float:
362
+ """Reward for POST route handler in Next.js Route Handlers."""
363
+ response = messages[-1].get("content", "")
364
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
365
+ if not match:
366
+ return 0.0
367
+
368
+ try:
369
+ root = SgRoot(match.group(1).strip(), "tsx").root()
370
+ matches = root.find_all(
371
+ Config(rule={"pattern": "export async function POST(request: Request) { $$$BODY }"})
372
+ )
373
+ return 1.0 if matches else 0.0
374
+ except Exception:
375
+ return 0.0
376
+
377
+
378
+ @reward
379
+ def default_page_component(args: RewardArgs, messages: list) -> float:
380
+ """Reward for default page component."""
381
+ response = messages[-1].get("content", "")
382
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
383
+ if not match:
384
+ return 0.0
385
+
386
+ try:
387
+ root = SgRoot(match.group(1).strip(), "tsx").root()
388
+ matches = root.find_all(
389
+ Config(rule={"pattern": "export default function Page() { $$$BODY }"})
390
+ )
391
+ return 1.0 if matches else 0.0
392
+ except Exception:
393
+ return 0.0
394
+
395
+
396
+ @reward
397
+ def client_component_detection(args: RewardArgs, messages: list) -> float:
398
+ """Reward for client components using 'use client' directive."""
399
+ response = messages[-1].get("content", "")
400
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
401
+ if not match:
402
+ return 0.0
403
+
404
+ try:
405
+ root = SgRoot(match.group(1).strip(), "tsx").root()
406
+ matches = root.find_all(
407
+ Config(rule={"all": [{"kind": "string"}, {"pattern": '"use client"'}]})
408
+ )
409
+ return 1.0 if matches else 0.0
410
+ except Exception:
411
+ return 0.0
412
+
413
+
414
+ @reward
415
+ def server_component_detection(args: RewardArgs, messages: list) -> float:
416
+ """Reward for server components (without 'use client')."""
417
+ response = messages[-1].get("content", "")
418
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
419
+ if not match:
420
+ return 0.0
421
+
422
+ try:
423
+ root = SgRoot(match.group(1).strip(), "tsx").root()
424
+ has_function = root.find_all(Config(rule={"kind": "function_declaration"}))
425
+ has_use_client = root.find_all(Config(rule={"kind": "string", "pattern": '"use client"'}))
426
+ return 1.0 if has_function and not has_use_client else 0.0
427
+ except Exception:
428
+ return 0.0
429
+
430
+
431
+ @reward
432
+ def parallel_route_segment(args: RewardArgs, messages: list) -> float:
433
+ """Reward for parallel route segments (e.g., @modal)."""
434
+ response = messages[-1].get("content", "")
435
+ match = re.search(r"```(?:typescript|tsx|ts)\n(.*?)```", response, re.DOTALL)
436
+ if not match:
437
+ return 0.0
438
+
439
+ try:
440
+ root = SgRoot(match.group(1).strip(), "tsx").root()
441
+ matches = root.find_all(
442
+ Config(rule={"kind": "program", "has": {"pattern": "@", "stopBy": "end"}})
443
+ )
444
+ return 1.0 if matches else 0.0
445
+ except Exception:
446
+ return 0.0