@@ -2104,6 +2104,8 @@ def new_function2(value):
2104
2104
"""
2105
2105
expected_code = """import numpy as np
2106
2106
2107
+ a = 6
2108
+
2107
2109
print("Hello world")
2108
2110
if 2<3:
2109
2111
a=4
@@ -2126,8 +2128,6 @@ def __call__(self, value):
2126
2128
return "I am still old"
2127
2129
def new_function2(value):
2128
2130
return cst.ensure_type(value, str)
2129
-
2130
- a = 6
2131
2131
"""
2132
2132
code_path = (Path (__file__ ).parent .resolve () / "../code_to_optimize/global_var_original.py" ).resolve ()
2133
2133
code_path .write_text (original_code , encoding = "utf-8" )
@@ -3228,3 +3228,228 @@ def _map_tool_definition(f: ToolDefinition) -> ChatCompletionInputTool:
3228
3228
assert not re .search (r"^import aiohttp as aiohttp_\b" , new_code , re .MULTILINE ) # conditional alias import: import <name> as <alias>
3229
3229
assert not re .search (r"^from math import pi as PI, sin as sine\b" , new_code , re .MULTILINE ) # conditional multiple aliases imports
3230
3230
assert "from huggingface_hub import AsyncInferenceClient, ChatCompletionInputTool" not in new_code # conditional from import
3231
+
3232
+ def test_top_level_global_assignments () -> None :
3233
+ root_dir = Path (__file__ ).parent .parent .resolve ()
3234
+ main_file = Path (root_dir / "code_to_optimize/temp_main.py" ).resolve ()
3235
+
3236
+ original_code = '''"""
3237
+ Module for generating GeneratedWorkflowParameters schema from workflow run input_text actions.
3238
+ """
3239
+
3240
+ from typing import Any, Dict, List, Tuple
3241
+
3242
+ import structlog
3243
+ from pydantic import BaseModel
3244
+
3245
+ from skyvern.forge import app
3246
+ from skyvern.forge.sdk.prompting import PromptEngine
3247
+ from skyvern.webeye.actions.actions import ActionType
3248
+
3249
+ LOG = structlog.get_logger(__name__)
3250
+
3251
+ # Initialize prompt engine
3252
+ prompt_engine = PromptEngine("skyvern")
3253
+
3254
+
3255
+ def hydrate_input_text_actions_with_field_names(
3256
+ actions_by_task: Dict[str, List[Dict[str, Any]]], field_mappings: Dict[str, str]
3257
+ ) -> Dict[str, List[Dict[str, Any]]]:
3258
+ """
3259
+ Add field_name to input_text actions based on generated mappings.
3260
+
3261
+ Args:
3262
+ actions_by_task: Dictionary mapping task IDs to lists of action dictionaries
3263
+ field_mappings: Dictionary mapping "task_id:action_id" to field names
3264
+
3265
+ Returns:
3266
+ Updated actions_by_task with field_name added to input_text actions
3267
+ """
3268
+ updated_actions_by_task = {}
3269
+
3270
+ for task_id, actions in actions_by_task.items():
3271
+ updated_actions = []
3272
+
3273
+ for action in actions:
3274
+ action_copy = action.copy()
3275
+
3276
+ if action.get("action_type") == ActionType.INPUT_TEXT:
3277
+ action_id = action.get("action_id", "")
3278
+ mapping_key = f"{task_id}:{action_id}"
3279
+
3280
+ if mapping_key in field_mappings:
3281
+ action_copy["field_name"] = field_mappings[mapping_key]
3282
+ else:
3283
+ # Fallback field name if mapping not found
3284
+ intention = action.get("intention", "")
3285
+ if intention:
3286
+ # Simple field name generation from intention
3287
+ field_name = intention.lower().replace(" ", "_").replace("?", "").replace("'", "")
3288
+ field_name = "".join(c for c in field_name if c.isalnum() or c == "_")
3289
+ action_copy["field_name"] = field_name or "unknown_field"
3290
+ else:
3291
+ action_copy["field_name"] = "unknown_field"
3292
+
3293
+ updated_actions.append(action_copy)
3294
+
3295
+ updated_actions_by_task[task_id] = updated_actions
3296
+
3297
+ return updated_actions_by_task
3298
+ '''
3299
+ main_file .write_text (original_code , encoding = "utf-8" )
3300
+ optim_code = f'''```python:{ main_file .relative_to (root_dir )}
3301
+ from skyvern.webeye.actions.actions import ActionType
3302
+ from typing import Any, Dict, List
3303
+ import re
3304
+
3305
+ # Precompiled regex for efficiently generating simple field_name from intention
3306
+ _INTENTION_CLEANUP_RE = re.compile(r"[^a-zA-Z0-9_]+")
3307
+
3308
+ def hydrate_input_text_actions_with_field_names(
3309
+ actions_by_task: Dict[str, List[Dict[str, Any]]], field_mappings: Dict[str, str]
3310
+ ) -> Dict[str, List[Dict[str, Any]]]:
3311
+ """
3312
+ Add field_name to input_text actions based on generated mappings.
3313
+
3314
+ Args:
3315
+ actions_by_task: Dictionary mapping task IDs to lists of action dictionaries
3316
+ field_mappings: Dictionary mapping "task_id:action_id" to field names
3317
+
3318
+ Returns:
3319
+ Updated actions_by_task with field_name added to input_text actions
3320
+ """
3321
+ updated_actions_by_task = {{}}
3322
+
3323
+ input_text_type = ActionType.INPUT_TEXT # local variable for faster access
3324
+ intention_cleanup = _INTENTION_CLEANUP_RE
3325
+
3326
+ for task_id, actions in actions_by_task.items():
3327
+ updated_actions = []
3328
+
3329
+ for action in actions:
3330
+ action_copy = action.copy()
3331
+
3332
+ if action.get("action_type") == input_text_type:
3333
+ action_id = action.get("action_id", "")
3334
+ mapping_key = f"{{task_id}}:{{action_id}}"
3335
+
3336
+ if mapping_key in field_mappings:
3337
+ action_copy["field_name"] = field_mappings[mapping_key]
3338
+ else:
3339
+ # Fallback field name if mapping not found
3340
+ intention = action.get("intention", "")
3341
+ if intention:
3342
+ # Simple field name generation from intention
3343
+ field_name = intention.lower().replace(" ", "_").replace("?", "").replace("'", "")
3344
+ # Use compiled regex instead of "".join(c for ...)
3345
+ field_name = intention_cleanup.sub("", field_name)
3346
+ action_copy["field_name"] = field_name or "unknown_field"
3347
+ else:
3348
+ action_copy["field_name"] = "unknown_field"
3349
+
3350
+ updated_actions.append(action_copy)
3351
+
3352
+ updated_actions_by_task[task_id] = updated_actions
3353
+
3354
+ return updated_actions_by_task
3355
+ ```
3356
+ '''
3357
+ expected = '''"""
3358
+ Module for generating GeneratedWorkflowParameters schema from workflow run input_text actions.
3359
+ """
3360
+
3361
+ from typing import Any, Dict, List, Tuple
3362
+
3363
+ import structlog
3364
+ from pydantic import BaseModel
3365
+
3366
+ from skyvern.forge import app
3367
+ from skyvern.forge.sdk.prompting import PromptEngine
3368
+ from skyvern.webeye.actions.actions import ActionType
3369
+ import re
3370
+
3371
+ _INTENTION_CLEANUP_RE = re.compile(r"[^a-zA-Z0-9_]+")
3372
+
3373
+ LOG = structlog.get_logger(__name__)
3374
+
3375
+ # Initialize prompt engine
3376
+ prompt_engine = PromptEngine("skyvern")
3377
+
3378
+
3379
+ def hydrate_input_text_actions_with_field_names(
3380
+ actions_by_task: Dict[str, List[Dict[str, Any]]], field_mappings: Dict[str, str]
3381
+ ) -> Dict[str, List[Dict[str, Any]]]:
3382
+ """
3383
+ Add field_name to input_text actions based on generated mappings.
3384
+
3385
+ Args:
3386
+ actions_by_task: Dictionary mapping task IDs to lists of action dictionaries
3387
+ field_mappings: Dictionary mapping "task_id:action_id" to field names
3388
+
3389
+ Returns:
3390
+ Updated actions_by_task with field_name added to input_text actions
3391
+ """
3392
+ updated_actions_by_task = {}
3393
+
3394
+ input_text_type = ActionType.INPUT_TEXT # local variable for faster access
3395
+ intention_cleanup = _INTENTION_CLEANUP_RE
3396
+
3397
+ for task_id, actions in actions_by_task.items():
3398
+ updated_actions = []
3399
+
3400
+ for action in actions:
3401
+ action_copy = action.copy()
3402
+
3403
+ if action.get("action_type") == input_text_type:
3404
+ action_id = action.get("action_id", "")
3405
+ mapping_key = f"{task_id}:{action_id}"
3406
+
3407
+ if mapping_key in field_mappings:
3408
+ action_copy["field_name"] = field_mappings[mapping_key]
3409
+ else:
3410
+ # Fallback field name if mapping not found
3411
+ intention = action.get("intention", "")
3412
+ if intention:
3413
+ # Simple field name generation from intention
3414
+ field_name = intention.lower().replace(" ", "_").replace("?", "").replace("'", "")
3415
+ # Use compiled regex instead of "".join(c for ...)
3416
+ field_name = intention_cleanup.sub("", field_name)
3417
+ action_copy["field_name"] = field_name or "unknown_field"
3418
+ else:
3419
+ action_copy["field_name"] = "unknown_field"
3420
+
3421
+ updated_actions.append(action_copy)
3422
+
3423
+ updated_actions_by_task[task_id] = updated_actions
3424
+
3425
+ return updated_actions_by_task
3426
+ '''
3427
+
3428
+ func = FunctionToOptimize (function_name = "hydrate_input_text_actions_with_field_names" , parents = [], file_path = main_file )
3429
+ test_config = TestConfig (
3430
+ tests_root = root_dir / "tests/pytest" ,
3431
+ tests_project_rootdir = root_dir ,
3432
+ project_root_path = root_dir ,
3433
+ test_framework = "pytest" ,
3434
+ pytest_cmd = "pytest" ,
3435
+ )
3436
+ func_optimizer = FunctionOptimizer (function_to_optimize = func , test_cfg = test_config )
3437
+ code_context : CodeOptimizationContext = func_optimizer .get_code_optimization_context ().unwrap ()
3438
+
3439
+ original_helper_code : dict [Path , str ] = {}
3440
+ helper_function_paths = {hf .file_path for hf in code_context .helper_functions }
3441
+ for helper_function_path in helper_function_paths :
3442
+ with helper_function_path .open (encoding = "utf8" ) as f :
3443
+ helper_code = f .read ()
3444
+ original_helper_code [helper_function_path ] = helper_code
3445
+
3446
+ func_optimizer .args = Args ()
3447
+ func_optimizer .replace_function_and_helpers_with_optimized_code (
3448
+ code_context = code_context , optimized_code = CodeStringsMarkdown .parse_markdown_code (optim_code ), original_helper_code = original_helper_code
3449
+ )
3450
+
3451
+
3452
+ new_code = main_file .read_text (encoding = "utf-8" )
3453
+ main_file .unlink (missing_ok = True )
3454
+
3455
+ assert new_code == expected
0 commit comments