awx-zipline-ai 0.0.32__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 (96) hide show
  1. __init__.py +0 -0
  2. agent/__init__.py +1 -0
  3. agent/constants.py +15 -0
  4. agent/ttypes.py +1684 -0
  5. ai/__init__.py +0 -0
  6. ai/chronon/__init__.py +0 -0
  7. ai/chronon/airflow_helpers.py +248 -0
  8. ai/chronon/cli/__init__.py +0 -0
  9. ai/chronon/cli/compile/__init__.py +0 -0
  10. ai/chronon/cli/compile/column_hashing.py +336 -0
  11. ai/chronon/cli/compile/compile_context.py +173 -0
  12. ai/chronon/cli/compile/compiler.py +183 -0
  13. ai/chronon/cli/compile/conf_validator.py +742 -0
  14. ai/chronon/cli/compile/display/__init__.py +0 -0
  15. ai/chronon/cli/compile/display/class_tracker.py +102 -0
  16. ai/chronon/cli/compile/display/compile_status.py +95 -0
  17. ai/chronon/cli/compile/display/compiled_obj.py +12 -0
  18. ai/chronon/cli/compile/display/console.py +3 -0
  19. ai/chronon/cli/compile/display/diff_result.py +111 -0
  20. ai/chronon/cli/compile/fill_templates.py +35 -0
  21. ai/chronon/cli/compile/parse_configs.py +134 -0
  22. ai/chronon/cli/compile/parse_teams.py +242 -0
  23. ai/chronon/cli/compile/serializer.py +109 -0
  24. ai/chronon/cli/compile/version_utils.py +42 -0
  25. ai/chronon/cli/git_utils.py +145 -0
  26. ai/chronon/cli/logger.py +59 -0
  27. ai/chronon/constants.py +3 -0
  28. ai/chronon/group_by.py +692 -0
  29. ai/chronon/join.py +580 -0
  30. ai/chronon/logger.py +23 -0
  31. ai/chronon/model.py +40 -0
  32. ai/chronon/query.py +126 -0
  33. ai/chronon/repo/__init__.py +39 -0
  34. ai/chronon/repo/aws.py +284 -0
  35. ai/chronon/repo/cluster.py +136 -0
  36. ai/chronon/repo/compile.py +62 -0
  37. ai/chronon/repo/constants.py +164 -0
  38. ai/chronon/repo/default_runner.py +269 -0
  39. ai/chronon/repo/explore.py +418 -0
  40. ai/chronon/repo/extract_objects.py +134 -0
  41. ai/chronon/repo/gcp.py +586 -0
  42. ai/chronon/repo/gitpython_utils.py +15 -0
  43. ai/chronon/repo/hub_runner.py +261 -0
  44. ai/chronon/repo/hub_uploader.py +109 -0
  45. ai/chronon/repo/init.py +60 -0
  46. ai/chronon/repo/join_backfill.py +119 -0
  47. ai/chronon/repo/run.py +296 -0
  48. ai/chronon/repo/serializer.py +133 -0
  49. ai/chronon/repo/team_json_utils.py +46 -0
  50. ai/chronon/repo/utils.py +481 -0
  51. ai/chronon/repo/zipline.py +35 -0
  52. ai/chronon/repo/zipline_hub.py +277 -0
  53. ai/chronon/resources/__init__.py +0 -0
  54. ai/chronon/resources/gcp/__init__.py +0 -0
  55. ai/chronon/resources/gcp/group_bys/__init__.py +0 -0
  56. ai/chronon/resources/gcp/group_bys/test/__init__.py +0 -0
  57. ai/chronon/resources/gcp/group_bys/test/data.py +30 -0
  58. ai/chronon/resources/gcp/joins/__init__.py +0 -0
  59. ai/chronon/resources/gcp/joins/test/__init__.py +0 -0
  60. ai/chronon/resources/gcp/joins/test/data.py +26 -0
  61. ai/chronon/resources/gcp/sources/__init__.py +0 -0
  62. ai/chronon/resources/gcp/sources/test/__init__.py +0 -0
  63. ai/chronon/resources/gcp/sources/test/data.py +26 -0
  64. ai/chronon/resources/gcp/teams.py +58 -0
  65. ai/chronon/source.py +86 -0
  66. ai/chronon/staging_query.py +226 -0
  67. ai/chronon/types.py +58 -0
  68. ai/chronon/utils.py +510 -0
  69. ai/chronon/windows.py +48 -0
  70. awx_zipline_ai-0.0.32.dist-info/METADATA +197 -0
  71. awx_zipline_ai-0.0.32.dist-info/RECORD +96 -0
  72. awx_zipline_ai-0.0.32.dist-info/WHEEL +5 -0
  73. awx_zipline_ai-0.0.32.dist-info/entry_points.txt +2 -0
  74. awx_zipline_ai-0.0.32.dist-info/top_level.txt +4 -0
  75. gen_thrift/__init__.py +0 -0
  76. gen_thrift/api/__init__.py +1 -0
  77. gen_thrift/api/constants.py +15 -0
  78. gen_thrift/api/ttypes.py +3754 -0
  79. gen_thrift/common/__init__.py +1 -0
  80. gen_thrift/common/constants.py +15 -0
  81. gen_thrift/common/ttypes.py +1814 -0
  82. gen_thrift/eval/__init__.py +1 -0
  83. gen_thrift/eval/constants.py +15 -0
  84. gen_thrift/eval/ttypes.py +660 -0
  85. gen_thrift/fetcher/__init__.py +1 -0
  86. gen_thrift/fetcher/constants.py +15 -0
  87. gen_thrift/fetcher/ttypes.py +127 -0
  88. gen_thrift/hub/__init__.py +1 -0
  89. gen_thrift/hub/constants.py +15 -0
  90. gen_thrift/hub/ttypes.py +1109 -0
  91. gen_thrift/observability/__init__.py +1 -0
  92. gen_thrift/observability/constants.py +15 -0
  93. gen_thrift/observability/ttypes.py +2355 -0
  94. gen_thrift/planner/__init__.py +1 -0
  95. gen_thrift/planner/constants.py +15 -0
  96. gen_thrift/planner/ttypes.py +1967 -0
@@ -0,0 +1,173 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, List, Optional, Type
4
+
5
+ from gen_thrift.api.ttypes import ConfType, GroupBy, Join, MetaData, Model, StagingQuery, Team
6
+
7
+ import ai.chronon.cli.compile.parse_teams as teams
8
+ from ai.chronon.cli.compile.conf_validator import ConfValidator
9
+ from ai.chronon.cli.compile.display.compile_status import CompileStatus
10
+ from ai.chronon.cli.compile.display.compiled_obj import CompiledObj
11
+ from ai.chronon.cli.compile.serializer import file2thrift
12
+ from ai.chronon.cli.logger import get_logger, require
13
+
14
+ logger = get_logger()
15
+
16
+
17
+ @dataclass
18
+ class ConfigInfo:
19
+ folder_name: str
20
+ cls: Type
21
+ config_type: Optional[ConfType]
22
+
23
+
24
+ @dataclass
25
+ class CompileContext:
26
+ def __init__(self, ignore_python_errors: bool = False):
27
+ self.chronon_root: str = os.getenv("CHRONON_ROOT", os.getcwd())
28
+ self.teams_dict: Dict[str, Team] = teams.load_teams(self.chronon_root)
29
+ self.compile_dir: str = "compiled"
30
+ self.ignore_python_errors: bool = ignore_python_errors
31
+
32
+ self.config_infos: List[ConfigInfo] = [
33
+ ConfigInfo(folder_name="joins", cls=Join, config_type=ConfType.JOIN),
34
+ ConfigInfo(
35
+ folder_name="group_bys",
36
+ cls=GroupBy,
37
+ config_type=ConfType.GROUP_BY,
38
+ ),
39
+ ConfigInfo(
40
+ folder_name="staging_queries",
41
+ cls=StagingQuery,
42
+ config_type=ConfType.STAGING_QUERY,
43
+ ),
44
+ ConfigInfo(folder_name="models", cls=Model, config_type=ConfType.MODEL),
45
+ ConfigInfo(
46
+ folder_name="teams_metadata", cls=MetaData, config_type=None
47
+ ), # only for team metadata
48
+ ]
49
+
50
+ self.compile_status = CompileStatus(use_live=False)
51
+
52
+ self.existing_confs: Dict[Type, Dict[str, Any]] = {}
53
+ for config_info in self.config_infos:
54
+ cls = config_info.cls
55
+ self.existing_confs[cls] = self._parse_existing_confs(cls)
56
+
57
+ self.validator: ConfValidator = ConfValidator(
58
+ input_root=self.chronon_root,
59
+ output_root=self.compile_dir,
60
+ existing_gbs=self.existing_confs[GroupBy],
61
+ existing_joins=self.existing_confs[Join],
62
+ existing_staging_queries=self.existing_confs[StagingQuery],
63
+ )
64
+
65
+ def input_dir(self, cls: type) -> str:
66
+ """
67
+ - eg., input: group_by class
68
+ - eg., output: root/group_bys/
69
+ """
70
+ config_info = self.config_info_for_class(cls)
71
+ return os.path.join(self.chronon_root, config_info.folder_name)
72
+
73
+ def staging_output_dir(self, cls: type = None) -> str:
74
+ """
75
+ - eg., input: group_by class
76
+ - eg., output: root/compiled_staging/group_bys/
77
+ """
78
+ if cls is None:
79
+ return os.path.join(self.chronon_root, self.compile_dir + "_staging")
80
+ else:
81
+ config_info = self.config_info_for_class(cls)
82
+ return os.path.join(
83
+ self.chronon_root,
84
+ self.compile_dir + "_staging",
85
+ config_info.folder_name,
86
+ )
87
+
88
+ def output_dir(self, cls: type = None) -> str:
89
+ """
90
+ - eg., input: group_by class
91
+ - eg., output: root/compiled/group_bys/
92
+ """
93
+ if cls is None:
94
+ return os.path.join(self.chronon_root, self.compile_dir)
95
+ else:
96
+ config_info = self.config_info_for_class(cls)
97
+ return os.path.join(self.chronon_root, self.compile_dir, config_info.folder_name)
98
+
99
+ def staging_output_path(self, compiled_obj: CompiledObj):
100
+ """
101
+ - eg., input: group_by with name search.clicks.features.v1
102
+ - eg., output: root/compiled_staging/group_bys/search/clicks.features.v1
103
+ """
104
+
105
+ output_dir = self.staging_output_dir(compiled_obj.obj.__class__) # compiled/joins
106
+
107
+ team, rest = compiled_obj.name.split(".", 1) # search, clicks.features.v1
108
+
109
+ return os.path.join(
110
+ output_dir,
111
+ team,
112
+ rest,
113
+ )
114
+
115
+ def config_info_for_class(self, cls: type) -> ConfigInfo:
116
+ for info in self.config_infos:
117
+ if info.cls == cls:
118
+ return info
119
+
120
+ require(False, f"Class {cls} not found in CONFIG_INFOS")
121
+
122
+ def _parse_existing_confs(self, obj_class: type) -> Dict[str, object]:
123
+ result = {}
124
+
125
+ output_dir = self.output_dir(obj_class)
126
+
127
+ # Check if output_dir exists before walking
128
+ if not os.path.exists(output_dir):
129
+ return result
130
+
131
+ for sub_root, _sub_dirs, sub_files in os.walk(output_dir):
132
+ for f in sub_files:
133
+ if f.startswith("."): # ignore hidden files - such as .DS_Store
134
+ continue
135
+
136
+ full_path = os.path.join(sub_root, f)
137
+
138
+ try:
139
+ obj = file2thrift(full_path, obj_class)
140
+
141
+ if obj:
142
+ if hasattr(obj, "metaData"):
143
+ result[obj.metaData.name] = obj
144
+ compiled_obj = CompiledObj(
145
+ name=obj.metaData.name,
146
+ obj=obj,
147
+ file=obj.metaData.sourceFile,
148
+ errors=None,
149
+ obj_type=obj_class.__name__,
150
+ tjson=open(full_path).read(),
151
+ )
152
+ self.compile_status.add_existing_object_update_display(compiled_obj)
153
+ elif isinstance(obj, MetaData):
154
+ team_metadata_name = ".".join(
155
+ full_path.split("/")[-2:]
156
+ ) # use the name of the file as team metadata won't have name
157
+ result[team_metadata_name] = obj
158
+ compiled_obj = CompiledObj(
159
+ name=team_metadata_name,
160
+ obj=obj,
161
+ file=obj.sourceFile,
162
+ errors=None,
163
+ obj_type=obj_class.__name__,
164
+ tjson=open(full_path).read(),
165
+ )
166
+ self.compile_status.add_existing_object_update_display(compiled_obj)
167
+ else:
168
+ logger.errors(f"Parsed object from {full_path} has no metaData attribute")
169
+
170
+ except Exception as e:
171
+ print(f"Failed to parse file {full_path}: {str(e)}", e)
172
+
173
+ return result
@@ -0,0 +1,183 @@
1
+ import os
2
+ import shutil
3
+ import traceback
4
+ from dataclasses import dataclass
5
+ from typing import Any, Dict, List, Optional, Tuple
6
+
7
+ from gen_thrift.api.ttypes import ConfType
8
+
9
+ import ai.chronon.cli.compile.display.compiled_obj
10
+ import ai.chronon.cli.compile.parse_configs as parser
11
+ import ai.chronon.cli.logger as logger
12
+ from ai.chronon.cli.compile import serializer
13
+ from ai.chronon.cli.compile.compile_context import CompileContext, ConfigInfo
14
+ from ai.chronon.cli.compile.display.compiled_obj import CompiledObj
15
+ from ai.chronon.cli.compile.display.console import console
16
+ from ai.chronon.cli.compile.parse_teams import merge_team_execution_info
17
+ from ai.chronon.types import MetaData
18
+
19
+ logger = logger.get_logger()
20
+
21
+
22
+ @dataclass
23
+ class CompileResult:
24
+ config_info: ConfigInfo
25
+ obj_dict: Dict[str, Any]
26
+ error_dict: Dict[str, List[BaseException]]
27
+
28
+
29
+ class Compiler:
30
+ def __init__(self, compile_context: CompileContext):
31
+ self.compile_context = compile_context
32
+
33
+ def compile(self) -> Dict[ConfType, CompileResult]:
34
+ # Clean staging directory at the start to ensure fresh compilation
35
+ staging_dir = self.compile_context.staging_output_dir()
36
+ if os.path.exists(staging_dir):
37
+ shutil.rmtree(staging_dir)
38
+
39
+ config_infos = self.compile_context.config_infos
40
+
41
+ compile_results = {}
42
+ all_compiled_objects = [] # Collect all compiled objects for change validation
43
+
44
+ for config_info in config_infos:
45
+ configs, compiled_objects = self._compile_class_configs(config_info)
46
+ compile_results[config_info.config_type] = configs
47
+
48
+ # Collect compiled objects for change validation
49
+ all_compiled_objects.extend(compiled_objects)
50
+
51
+ # Validate changes once after all classes have been processed
52
+ self.compile_context.validator.validate_changes(all_compiled_objects)
53
+
54
+ # Show the nice display first
55
+ console.print(
56
+ self.compile_context.compile_status.render(self.compile_context.ignore_python_errors)
57
+ )
58
+
59
+ # Check for confirmation before finalizing files
60
+ self.compile_context.validator.check_pending_changes_confirmation(
61
+ self.compile_context.compile_status
62
+ )
63
+
64
+ # Only proceed with file operations if there are no compilation errors
65
+ if not self._has_compilation_errors() or self.compile_context.ignore_python_errors:
66
+ self._compile_team_metadata()
67
+
68
+ # check if staging_output_dir exists
69
+ staging_dir = self.compile_context.staging_output_dir()
70
+ if os.path.exists(staging_dir):
71
+ # replace staging_output_dir to output_dir
72
+ output_dir = self.compile_context.output_dir()
73
+ if os.path.exists(output_dir):
74
+ shutil.rmtree(output_dir)
75
+ shutil.move(staging_dir, output_dir)
76
+ else:
77
+ print(
78
+ f"Staging directory {staging_dir} does not exist. "
79
+ "Happens when every chronon config fails to compile or when no chronon configs exist."
80
+ )
81
+ else:
82
+ # Clean up staging directory when there are errors (don't move to output)
83
+ staging_dir = self.compile_context.staging_output_dir()
84
+ if os.path.exists(staging_dir):
85
+ shutil.rmtree(staging_dir)
86
+
87
+ return compile_results
88
+
89
+ def _has_compilation_errors(self):
90
+ """Check if there are any compilation errors across all class trackers."""
91
+ for tracker in self.compile_context.compile_status.cls_to_tracker.values():
92
+ if tracker.files_to_errors:
93
+ return True
94
+ return False
95
+
96
+ def _compile_team_metadata(self):
97
+ """
98
+ Compile the team metadata and return the compiled object.
99
+ """
100
+ teams_dict = self.compile_context.teams_dict
101
+ for team in teams_dict:
102
+ m = MetaData()
103
+ merge_team_execution_info(m, teams_dict, team)
104
+
105
+ tjson = serializer.thrift_simple_json(m)
106
+ name = f"{team}.{team}_team_metadata"
107
+ result = CompiledObj(
108
+ name=name,
109
+ obj=m,
110
+ file=name,
111
+ errors=None,
112
+ obj_type=MetaData.__name__,
113
+ tjson=tjson,
114
+ )
115
+ self._write_object(result)
116
+ self.compile_context.compile_status.add_object_update_display(result, MetaData.__name__)
117
+
118
+ # Done writing team metadata, close the class
119
+ self.compile_context.compile_status.close_cls(MetaData.__name__)
120
+
121
+ def _compile_class_configs(
122
+ self, config_info: ConfigInfo
123
+ ) -> Tuple[CompileResult, List[CompiledObj]]:
124
+ compile_result = CompileResult(config_info=config_info, obj_dict={}, error_dict={})
125
+
126
+ input_dir = self.compile_context.input_dir(config_info.cls)
127
+
128
+ compiled_objects = parser.from_folder(config_info.cls, input_dir, self.compile_context)
129
+
130
+ objects, errors = self._write_objects_in_folder(compiled_objects)
131
+
132
+ if objects:
133
+ compile_result.obj_dict.update(objects)
134
+
135
+ if errors:
136
+ compile_result.error_dict.update(errors)
137
+
138
+ self.compile_context.compile_status.close_cls(config_info.cls.__name__)
139
+
140
+ return compile_result, compiled_objects
141
+
142
+ def _write_objects_in_folder(
143
+ self,
144
+ compiled_objects: List[ai.chronon.cli.compile.display.compiled_obj.CompiledObj],
145
+ ) -> Tuple[Dict[str, Any], Dict[str, List[BaseException]]]:
146
+ error_dict = {}
147
+ object_dict = {}
148
+
149
+ for co in compiled_objects:
150
+ if co.obj:
151
+ if co.errors:
152
+ error_dict[co.name] = co.errors
153
+
154
+ for error in co.errors:
155
+ self.compile_context.compile_status.print_live_console(
156
+ f"Error processing conf {co.name}: {error}"
157
+ )
158
+ traceback.print_exception(type(error), error, error.__traceback__)
159
+
160
+ else:
161
+ self._write_object(co)
162
+ object_dict[co.name] = co.obj
163
+ else:
164
+ error_dict[co.file] = co.errors
165
+
166
+ self.compile_context.compile_status.print_live_console(
167
+ f"Error processing file {co.file}: {co.errors}"
168
+ )
169
+ for error in co.errors:
170
+ traceback.print_exception(type(error), error, error.__traceback__)
171
+
172
+ return object_dict, error_dict
173
+
174
+ def _write_object(self, compiled_obj: CompiledObj) -> Optional[List[BaseException]]:
175
+ output_path = self.compile_context.staging_output_path(compiled_obj)
176
+
177
+ folder = os.path.dirname(output_path)
178
+
179
+ if not os.path.exists(folder):
180
+ os.makedirs(folder)
181
+
182
+ with open(output_path, "w") as f:
183
+ f.write(compiled_obj.tjson)