easy_ml 0.1.4 → 0.2.0.pre.rc1

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 (239) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +234 -26
  3. data/Rakefile +45 -0
  4. data/app/controllers/easy_ml/application_controller.rb +67 -0
  5. data/app/controllers/easy_ml/columns_controller.rb +38 -0
  6. data/app/controllers/easy_ml/datasets_controller.rb +156 -0
  7. data/app/controllers/easy_ml/datasources_controller.rb +88 -0
  8. data/app/controllers/easy_ml/deploys_controller.rb +20 -0
  9. data/app/controllers/easy_ml/models_controller.rb +151 -0
  10. data/app/controllers/easy_ml/retraining_runs_controller.rb +19 -0
  11. data/app/controllers/easy_ml/settings_controller.rb +59 -0
  12. data/app/frontend/components/AlertProvider.tsx +108 -0
  13. data/app/frontend/components/DatasetPreview.tsx +161 -0
  14. data/app/frontend/components/EmptyState.tsx +28 -0
  15. data/app/frontend/components/ModelCard.tsx +255 -0
  16. data/app/frontend/components/ModelDetails.tsx +334 -0
  17. data/app/frontend/components/ModelForm.tsx +384 -0
  18. data/app/frontend/components/Navigation.tsx +300 -0
  19. data/app/frontend/components/Pagination.tsx +72 -0
  20. data/app/frontend/components/Popover.tsx +55 -0
  21. data/app/frontend/components/PredictionStream.tsx +105 -0
  22. data/app/frontend/components/ScheduleModal.tsx +726 -0
  23. data/app/frontend/components/SearchInput.tsx +23 -0
  24. data/app/frontend/components/SearchableSelect.tsx +132 -0
  25. data/app/frontend/components/dataset/AutosaveIndicator.tsx +39 -0
  26. data/app/frontend/components/dataset/ColumnConfigModal.tsx +431 -0
  27. data/app/frontend/components/dataset/ColumnFilters.tsx +256 -0
  28. data/app/frontend/components/dataset/ColumnList.tsx +101 -0
  29. data/app/frontend/components/dataset/FeatureConfigPopover.tsx +57 -0
  30. data/app/frontend/components/dataset/FeaturePicker.tsx +205 -0
  31. data/app/frontend/components/dataset/PreprocessingConfig.tsx +704 -0
  32. data/app/frontend/components/dataset/SplitConfigurator.tsx +120 -0
  33. data/app/frontend/components/dataset/splitters/DateSplitter.tsx +58 -0
  34. data/app/frontend/components/dataset/splitters/KFoldSplitter.tsx +68 -0
  35. data/app/frontend/components/dataset/splitters/LeavePOutSplitter.tsx +29 -0
  36. data/app/frontend/components/dataset/splitters/PredefinedSplitter.tsx +146 -0
  37. data/app/frontend/components/dataset/splitters/RandomSplitter.tsx +85 -0
  38. data/app/frontend/components/dataset/splitters/StratifiedSplitter.tsx +79 -0
  39. data/app/frontend/components/dataset/splitters/constants.ts +77 -0
  40. data/app/frontend/components/dataset/splitters/types.ts +168 -0
  41. data/app/frontend/components/dataset/splitters/utils.ts +53 -0
  42. data/app/frontend/components/features/CodeEditor.tsx +46 -0
  43. data/app/frontend/components/features/DataPreview.tsx +150 -0
  44. data/app/frontend/components/features/FeatureCard.tsx +88 -0
  45. data/app/frontend/components/features/FeatureForm.tsx +235 -0
  46. data/app/frontend/components/features/FeatureGroupCard.tsx +54 -0
  47. data/app/frontend/components/settings/PluginSettings.tsx +81 -0
  48. data/app/frontend/components/ui/badge.tsx +44 -0
  49. data/app/frontend/components/ui/collapsible.tsx +9 -0
  50. data/app/frontend/components/ui/scroll-area.tsx +46 -0
  51. data/app/frontend/components/ui/separator.tsx +29 -0
  52. data/app/frontend/entrypoints/App.tsx +40 -0
  53. data/app/frontend/entrypoints/Application.tsx +24 -0
  54. data/app/frontend/hooks/useAutosave.ts +61 -0
  55. data/app/frontend/layouts/Layout.tsx +38 -0
  56. data/app/frontend/lib/utils.ts +6 -0
  57. data/app/frontend/mockData.ts +272 -0
  58. data/app/frontend/pages/DatasetDetailsPage.tsx +103 -0
  59. data/app/frontend/pages/DatasetsPage.tsx +261 -0
  60. data/app/frontend/pages/DatasourceFormPage.tsx +147 -0
  61. data/app/frontend/pages/DatasourcesPage.tsx +261 -0
  62. data/app/frontend/pages/EditModelPage.tsx +45 -0
  63. data/app/frontend/pages/EditTransformationPage.tsx +56 -0
  64. data/app/frontend/pages/ModelsPage.tsx +115 -0
  65. data/app/frontend/pages/NewDatasetPage.tsx +366 -0
  66. data/app/frontend/pages/NewModelPage.tsx +45 -0
  67. data/app/frontend/pages/NewTransformationPage.tsx +43 -0
  68. data/app/frontend/pages/SettingsPage.tsx +272 -0
  69. data/app/frontend/pages/ShowModelPage.tsx +30 -0
  70. data/app/frontend/pages/TransformationsPage.tsx +95 -0
  71. data/app/frontend/styles/application.css +100 -0
  72. data/app/frontend/types/dataset.ts +146 -0
  73. data/app/frontend/types/datasource.ts +33 -0
  74. data/app/frontend/types/preprocessing.ts +1 -0
  75. data/app/frontend/types.ts +113 -0
  76. data/app/helpers/easy_ml/application_helper.rb +10 -0
  77. data/app/jobs/easy_ml/application_job.rb +21 -0
  78. data/app/jobs/easy_ml/batch_job.rb +46 -0
  79. data/app/jobs/easy_ml/compute_feature_job.rb +19 -0
  80. data/app/jobs/easy_ml/deploy_job.rb +13 -0
  81. data/app/jobs/easy_ml/finalize_feature_job.rb +15 -0
  82. data/app/jobs/easy_ml/refresh_dataset_job.rb +32 -0
  83. data/app/jobs/easy_ml/schedule_retraining_job.rb +11 -0
  84. data/app/jobs/easy_ml/sync_datasource_job.rb +17 -0
  85. data/app/jobs/easy_ml/training_job.rb +62 -0
  86. data/app/models/easy_ml/adapters/base_adapter.rb +45 -0
  87. data/app/models/easy_ml/adapters/polars_adapter.rb +77 -0
  88. data/app/models/easy_ml/cleaner.rb +82 -0
  89. data/app/models/easy_ml/column.rb +124 -0
  90. data/app/models/easy_ml/column_history.rb +30 -0
  91. data/app/models/easy_ml/column_list.rb +122 -0
  92. data/app/models/easy_ml/concerns/configurable.rb +61 -0
  93. data/app/models/easy_ml/concerns/versionable.rb +19 -0
  94. data/app/models/easy_ml/dataset.rb +767 -0
  95. data/app/models/easy_ml/dataset_history.rb +56 -0
  96. data/app/models/easy_ml/datasource.rb +182 -0
  97. data/app/models/easy_ml/datasource_history.rb +24 -0
  98. data/app/models/easy_ml/datasources/base_datasource.rb +54 -0
  99. data/app/models/easy_ml/datasources/file_datasource.rb +58 -0
  100. data/app/models/easy_ml/datasources/polars_datasource.rb +89 -0
  101. data/app/models/easy_ml/datasources/s3_datasource.rb +97 -0
  102. data/app/models/easy_ml/deploy.rb +114 -0
  103. data/app/models/easy_ml/event.rb +79 -0
  104. data/app/models/easy_ml/feature.rb +437 -0
  105. data/app/models/easy_ml/feature_history.rb +38 -0
  106. data/app/models/easy_ml/model.rb +575 -41
  107. data/app/models/easy_ml/model_file.rb +133 -0
  108. data/app/models/easy_ml/model_file_history.rb +24 -0
  109. data/app/models/easy_ml/model_history.rb +51 -0
  110. data/app/models/easy_ml/models/base_model.rb +58 -0
  111. data/app/models/easy_ml/models/hyperparameters/base.rb +99 -0
  112. data/app/models/easy_ml/models/hyperparameters/xgboost/dart.rb +82 -0
  113. data/app/models/easy_ml/models/hyperparameters/xgboost/gblinear.rb +82 -0
  114. data/app/models/easy_ml/models/hyperparameters/xgboost/gbtree.rb +97 -0
  115. data/app/models/easy_ml/models/hyperparameters/xgboost.rb +71 -0
  116. data/app/models/easy_ml/models/xgboost/evals_callback.rb +138 -0
  117. data/app/models/easy_ml/models/xgboost/progress_callback.rb +39 -0
  118. data/app/models/easy_ml/models/xgboost.rb +544 -5
  119. data/app/models/easy_ml/prediction.rb +44 -0
  120. data/app/models/easy_ml/retraining_job.rb +278 -0
  121. data/app/models/easy_ml/retraining_run.rb +184 -0
  122. data/app/models/easy_ml/settings.rb +37 -0
  123. data/app/models/easy_ml/splitter.rb +90 -0
  124. data/app/models/easy_ml/splitters/base_splitter.rb +28 -0
  125. data/app/models/easy_ml/splitters/date_splitter.rb +91 -0
  126. data/app/models/easy_ml/splitters/predefined_splitter.rb +74 -0
  127. data/app/models/easy_ml/splitters/random_splitter.rb +82 -0
  128. data/app/models/easy_ml/tuner_job.rb +56 -0
  129. data/app/models/easy_ml/tuner_run.rb +31 -0
  130. data/app/models/splitter_history.rb +6 -0
  131. data/app/serializers/easy_ml/column_serializer.rb +27 -0
  132. data/app/serializers/easy_ml/dataset_serializer.rb +73 -0
  133. data/app/serializers/easy_ml/datasource_serializer.rb +64 -0
  134. data/app/serializers/easy_ml/feature_serializer.rb +27 -0
  135. data/app/serializers/easy_ml/model_serializer.rb +90 -0
  136. data/app/serializers/easy_ml/retraining_job_serializer.rb +22 -0
  137. data/app/serializers/easy_ml/retraining_run_serializer.rb +39 -0
  138. data/app/serializers/easy_ml/settings_serializer.rb +9 -0
  139. data/app/views/layouts/easy_ml/application.html.erb +15 -0
  140. data/config/initializers/resque.rb +3 -0
  141. data/config/resque-pool.yml +6 -0
  142. data/config/routes.rb +39 -0
  143. data/config/spring.rb +1 -0
  144. data/config/vite.json +15 -0
  145. data/lib/easy_ml/configuration.rb +64 -0
  146. data/lib/easy_ml/core/evaluators/base_evaluator.rb +53 -0
  147. data/lib/easy_ml/core/evaluators/classification_evaluators.rb +126 -0
  148. data/lib/easy_ml/core/evaluators/regression_evaluators.rb +66 -0
  149. data/lib/easy_ml/core/model_evaluator.rb +161 -89
  150. data/lib/easy_ml/core/tuner/adapters/base_adapter.rb +28 -18
  151. data/lib/easy_ml/core/tuner/adapters/xgboost_adapter.rb +4 -25
  152. data/lib/easy_ml/core/tuner.rb +123 -62
  153. data/lib/easy_ml/core.rb +0 -3
  154. data/lib/easy_ml/core_ext/hash.rb +24 -0
  155. data/lib/easy_ml/core_ext/pathname.rb +11 -5
  156. data/lib/easy_ml/data/date_converter.rb +90 -0
  157. data/lib/easy_ml/data/filter_extensions.rb +31 -0
  158. data/lib/easy_ml/data/polars_column.rb +126 -0
  159. data/lib/easy_ml/data/polars_reader.rb +297 -0
  160. data/lib/easy_ml/data/preprocessor.rb +280 -142
  161. data/lib/easy_ml/data/simple_imputer.rb +255 -0
  162. data/lib/easy_ml/data/splits/file_split.rb +252 -0
  163. data/lib/easy_ml/data/splits/in_memory_split.rb +54 -0
  164. data/lib/easy_ml/data/splits/split.rb +95 -0
  165. data/lib/easy_ml/data/splits.rb +9 -0
  166. data/lib/easy_ml/data/statistics_learner.rb +93 -0
  167. data/lib/easy_ml/data/synced_directory.rb +341 -0
  168. data/lib/easy_ml/data.rb +6 -2
  169. data/lib/easy_ml/engine.rb +105 -6
  170. data/lib/easy_ml/feature_store.rb +227 -0
  171. data/lib/easy_ml/features.rb +61 -0
  172. data/lib/easy_ml/initializers/inflections.rb +17 -3
  173. data/lib/easy_ml/logging.rb +2 -2
  174. data/lib/easy_ml/predict.rb +74 -0
  175. data/lib/easy_ml/railtie/generators/migration/migration_generator.rb +192 -36
  176. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_column_histories.rb.tt +9 -0
  177. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_columns.rb.tt +25 -0
  178. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_dataset_histories.rb.tt +9 -0
  179. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_datasets.rb.tt +31 -0
  180. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_datasource_histories.rb.tt +9 -0
  181. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_datasources.rb.tt +16 -0
  182. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_deploys.rb.tt +24 -0
  183. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_events.rb.tt +20 -0
  184. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_feature_histories.rb.tt +14 -0
  185. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_features.rb.tt +32 -0
  186. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_model_file_histories.rb.tt +9 -0
  187. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_model_files.rb.tt +17 -0
  188. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_model_histories.rb.tt +9 -0
  189. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_models.rb.tt +20 -9
  190. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_predictions.rb.tt +17 -0
  191. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_retraining_jobs.rb.tt +77 -0
  192. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_settings.rb.tt +9 -0
  193. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_splitter_histories.rb.tt +9 -0
  194. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_splitters.rb.tt +15 -0
  195. data/lib/easy_ml/railtie/templates/migration/create_easy_ml_tuner_jobs.rb.tt +40 -0
  196. data/lib/easy_ml/support/est.rb +5 -1
  197. data/lib/easy_ml/support/file_rotate.rb +79 -15
  198. data/lib/easy_ml/support/file_support.rb +9 -0
  199. data/lib/easy_ml/support/local_file.rb +24 -0
  200. data/lib/easy_ml/support/lockable.rb +62 -0
  201. data/lib/easy_ml/support/synced_file.rb +103 -0
  202. data/lib/easy_ml/support/utc.rb +5 -1
  203. data/lib/easy_ml/support.rb +6 -3
  204. data/lib/easy_ml/version.rb +4 -1
  205. data/lib/easy_ml.rb +7 -2
  206. metadata +355 -72
  207. data/app/models/easy_ml/models.rb +0 -5
  208. data/lib/easy_ml/core/model.rb +0 -30
  209. data/lib/easy_ml/core/model_core.rb +0 -181
  210. data/lib/easy_ml/core/models/hyperparameters/base.rb +0 -34
  211. data/lib/easy_ml/core/models/hyperparameters/xgboost.rb +0 -19
  212. data/lib/easy_ml/core/models/xgboost.rb +0 -10
  213. data/lib/easy_ml/core/models/xgboost_core.rb +0 -220
  214. data/lib/easy_ml/core/models.rb +0 -10
  215. data/lib/easy_ml/core/uploaders/model_uploader.rb +0 -24
  216. data/lib/easy_ml/core/uploaders.rb +0 -7
  217. data/lib/easy_ml/data/dataloader.rb +0 -6
  218. data/lib/easy_ml/data/dataset/data/preprocessor/statistics.json +0 -31
  219. data/lib/easy_ml/data/dataset/data/sample_info.json +0 -1
  220. data/lib/easy_ml/data/dataset/dataset/files/sample_info.json +0 -1
  221. data/lib/easy_ml/data/dataset/splits/file_split.rb +0 -140
  222. data/lib/easy_ml/data/dataset/splits/in_memory_split.rb +0 -49
  223. data/lib/easy_ml/data/dataset/splits/split.rb +0 -98
  224. data/lib/easy_ml/data/dataset/splits.rb +0 -11
  225. data/lib/easy_ml/data/dataset/splitters/date_splitter.rb +0 -43
  226. data/lib/easy_ml/data/dataset/splitters.rb +0 -9
  227. data/lib/easy_ml/data/dataset.rb +0 -430
  228. data/lib/easy_ml/data/datasource/datasource_factory.rb +0 -60
  229. data/lib/easy_ml/data/datasource/file_datasource.rb +0 -40
  230. data/lib/easy_ml/data/datasource/merged_datasource.rb +0 -64
  231. data/lib/easy_ml/data/datasource/polars_datasource.rb +0 -41
  232. data/lib/easy_ml/data/datasource/s3_datasource.rb +0 -89
  233. data/lib/easy_ml/data/datasource.rb +0 -33
  234. data/lib/easy_ml/data/preprocessor/preprocessor.rb +0 -205
  235. data/lib/easy_ml/data/preprocessor/simple_imputer.rb +0 -402
  236. data/lib/easy_ml/deployment.rb +0 -5
  237. data/lib/easy_ml/support/synced_directory.rb +0 -134
  238. data/lib/easy_ml/transforms.rb +0 -29
  239. /data/{lib/easy_ml/core → app/models/easy_ml}/models/hyperparameters.rb +0 -0
@@ -0,0 +1,272 @@
1
+ import React, { useState } from 'react';
2
+ import { usePage } from '@inertiajs/react'
3
+ import { useInertiaForm } from 'use-inertia-form';
4
+ import { Settings2, Save, AlertCircle, Key, Database, Globe2 } from 'lucide-react';
5
+ import { PluginSettings } from '../components/settings/PluginSettings';
6
+
7
+ interface Settings {
8
+ settings: {
9
+ timezone: string;
10
+ s3_bucket: string;
11
+ s3_region: string;
12
+ s3_access_key_id: string;
13
+ s3_secret_access_key: string;
14
+ wandb_api_key: string;
15
+ }
16
+ }
17
+
18
+ const TIMEZONES = [
19
+ { value: 'America/New_York', label: 'Eastern Time' },
20
+ { value: 'America/Chicago', label: 'Central Time' },
21
+ { value: 'America/Denver', label: 'Mountain Time' },
22
+ { value: 'America/Los_Angeles', label: 'Pacific Time' }
23
+ ];
24
+
25
+ export default function SettingsPage({ settings: initialSettings }: { settings: Settings }) {
26
+ const { rootPath } = usePage().props;
27
+
28
+ const form = useInertiaForm<Settings>({
29
+ settings: {
30
+ timezone: initialSettings?.settings?.timezone || 'America/New_York',
31
+ s3_bucket: initialSettings?.settings?.s3_bucket || '',
32
+ s3_region: initialSettings?.settings?.s3_region || 'us-east-1',
33
+ s3_access_key_id: initialSettings?.settings?.s3_access_key_id || '',
34
+ s3_secret_access_key: initialSettings?.settings?.s3_secret_access_key || '',
35
+ wandb_api_key: initialSettings?.settings?.wandb_api_key || ''
36
+ }
37
+ });
38
+
39
+ const { data: formData, setData: setFormData, patch, processing } = form;
40
+
41
+ const [showSecretKey, setShowSecretKey] = useState(false);
42
+ const [saved, setSaved] = useState(false);
43
+ const [error, setError] = useState<string | null>(null);
44
+
45
+ const handleSubmit = (e: React.FormEvent) => {
46
+ e.preventDefault();
47
+ setSaved(false);
48
+ setError(null);
49
+
50
+ const timeoutId = setTimeout(() => {
51
+ setError('Request timed out. Please try again.');
52
+ }, 3000);
53
+
54
+ patch(`${rootPath}/settings`, {
55
+ onSuccess: () => {
56
+ clearTimeout(timeoutId);
57
+ setSaved(true);
58
+ },
59
+ onError: () => {
60
+ clearTimeout(timeoutId);
61
+ setError('Failed to save settings. Please try again.');
62
+ }
63
+ });
64
+ };
65
+
66
+ return (
67
+ <div className="max-w-4xl mx-auto p-8">
68
+ <div className="bg-white rounded-lg shadow-lg">
69
+ <div className="px-6 py-4 border-b border-gray-200">
70
+ <div className="flex items-center gap-3">
71
+ <Settings2 className="w-6 h-6 text-blue-600" />
72
+ <h2 className="text-xl font-semibold text-gray-900">Settings</h2>
73
+ </div>
74
+ </div>
75
+
76
+ <form onSubmit={handleSubmit} className="p-6 space-y-8">
77
+ {/* General Settings */}
78
+ <div className="space-y-4">
79
+ <div className="flex items-center gap-2 mb-4">
80
+ <Globe2 className="w-5 h-5 text-gray-500" />
81
+ <h3 className="text-lg font-medium text-gray-900">General Settings</h3>
82
+ </div>
83
+
84
+ <div>
85
+ <label htmlFor="timezone" className="block text-sm font-medium text-gray-700 mb-1">
86
+ Timezone
87
+ </label>
88
+ <select
89
+ id="timezone"
90
+ value={formData.settings.timezone}
91
+
92
+ onChange={(e) => setFormData({
93
+ ...formData,
94
+ settings: {
95
+ ...formData.settings,
96
+ timezone: e.target.value
97
+ }
98
+ })}
99
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
100
+ >
101
+ {TIMEZONES.map((tz) => (
102
+ <option key={tz.value} value={tz.value}>
103
+ {tz.label}
104
+ </option>
105
+ ))}
106
+ </select>
107
+ <p className="mt-1 text-sm text-gray-500">
108
+ All dates and times will be displayed in this timezone
109
+ </p>
110
+ </div>
111
+ </div>
112
+
113
+ {/* S3 Configuration */}
114
+ <div className="space-y-4">
115
+ <div className="flex items-center gap-2 mb-4">
116
+
117
+ <Database className="w-5 h-5 text-gray-500" />
118
+ <h3 className="text-lg font-medium text-gray-900">S3 Configuration</h3>
119
+ </div>
120
+
121
+ <div className="grid grid-cols-2 gap-6">
122
+ <div>
123
+ <label htmlFor="bucket" className="block text-sm font-medium text-gray-700 mb-1">
124
+ Default S3 Bucket
125
+ </label>
126
+ <input
127
+ type="text"
128
+ id="bucket"
129
+ value={formData.settings.s3_bucket}
130
+ onChange={(e) => setFormData({
131
+ ...formData,
132
+ settings: {
133
+ ...formData.settings,
134
+ s3_bucket: e.target.value
135
+ }
136
+ })}
137
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
138
+ placeholder="my-bucket"
139
+ />
140
+ </div>
141
+
142
+ <div>
143
+ <label htmlFor="region" className="block text-sm font-medium text-gray-700 mb-1">
144
+ AWS Region
145
+ </label>
146
+ <select
147
+ id="region"
148
+ value={formData.settings.s3_region}
149
+ onChange={(e) => setFormData({
150
+ ...formData,
151
+ settings: {
152
+ ...formData.settings,
153
+ s3_region: e.target.value
154
+ }
155
+ })}
156
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
157
+ >
158
+ <option value="us-east-1">US East (N. Virginia)</option>
159
+ <option value="us-east-2">US East (Ohio)</option>
160
+ <option value="us-west-1">US West (N. California)</option>
161
+ <option value="us-west-2">US West (Oregon)</option>
162
+ </select>
163
+ </div>
164
+ </div>
165
+
166
+ <div className="bg-blue-50 rounded-lg p-4">
167
+ <div className="flex gap-2">
168
+ <AlertCircle className="w-5 h-5 text-blue-500 mt-0.5" />
169
+ <div>
170
+ <h4 className="text-sm font-medium text-blue-900">AWS Credentials</h4>
171
+ <p className="mt-1 text-sm text-blue-700">
172
+ These credentials will be used as default for all S3 operations. You can override them per datasource.
173
+ </p>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ <div className="grid grid-cols-2 gap-6">
179
+ <div>
180
+ <label htmlFor="accessKeyId" className="block text-sm font-medium text-gray-700 mb-1">
181
+ Access Key ID
182
+ </label>
183
+ <div className="relative">
184
+ <Key className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
185
+ <input
186
+ type="text"
187
+ id="accessKeyId"
188
+ value={formData.settings.s3_access_key_id}
189
+ onChange={(e) => setFormData({
190
+ ...formData,
191
+ settings: {
192
+ ...formData.settings,
193
+ s3_access_key_id: e.target.value
194
+ }
195
+ })}
196
+ className="mt-1 block w-full pl-9 rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
197
+ placeholder="AKIA..."
198
+ />
199
+ </div>
200
+ </div>
201
+
202
+ <div>
203
+ <label htmlFor="secretAccessKey" className="block text-sm font-medium text-gray-700 mb-1">
204
+ Secret Access Key
205
+ </label>
206
+ <div className="relative">
207
+ <Key className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
208
+ <input
209
+ type={showSecretKey ? 'text' : 'password'}
210
+ id="secretAccessKey"
211
+ value={formData.settings.s3_secret_access_key}
212
+ onChange={(e) => setFormData({
213
+ ...formData,
214
+ settings: {
215
+ ...formData.settings,
216
+ s3_secret_access_key: e.target.value
217
+ }
218
+ })}
219
+ className="mt-1 block w-full pl-9 pr-24 rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
220
+ placeholder="Your secret key"
221
+ />
222
+ <button
223
+ type="button"
224
+ onClick={() => setShowSecretKey(!showSecretKey)}
225
+ className="absolute right-2 top-1/2 transform -translate-y-1/2 text-sm text-gray-500 hover:text-gray-700"
226
+ >
227
+ {showSecretKey ? 'Hide' : 'Show'}
228
+ </button>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ <div className="border-t border-gray-200 pt-8">
235
+ <PluginSettings
236
+ settings={formData.settings}
237
+ setData={(settings) => setFormData({ ...settings })}
238
+ />
239
+ </div>
240
+
241
+ <div className="pt-6 border-t flex items-center justify-between">
242
+ {saved && (
243
+ <div className="flex items-center gap-2 text-green-600">
244
+ <Save className="w-4 h-4" />
245
+ <span className="text-sm font-medium">Settings saved successfully</span>
246
+ </div>
247
+ )}
248
+ {error && (
249
+ <div className="flex items-center gap-2 text-red-600">
250
+ <AlertCircle className="w-4 h-4" />
251
+ <span className="text-sm font-medium">{error}</span>
252
+ </div>
253
+ )}
254
+ <div className="flex gap-3">
255
+ <button
256
+ type="submit"
257
+ disabled={processing}
258
+ className={`px-4 py-2 text-white text-sm font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${
259
+ processing
260
+ ? 'bg-blue-400 cursor-not-allowed'
261
+ : 'bg-blue-600 hover:bg-blue-700'
262
+ }`}
263
+ >
264
+ {processing ? 'Saving...' : 'Save Settings'}
265
+ </button>
266
+ </div>
267
+ </div>
268
+ </form>
269
+ </div>
270
+ </div>
271
+ );
272
+ }
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { ModelDetails } from '../components/ModelDetails';
3
+ import { router, Link, usePage } from "@inertiajs/react";
4
+ import { useInertiaForm } from "use-inertia-form";
5
+ import { ArrowLeft, Brain } from 'lucide-react';
6
+ import { ModelForm } from '../components/ModelForm';
7
+ import { Model, Dataset } from '../types';
8
+
9
+ interface ModelFormData {
10
+ model: Model;
11
+ }
12
+
13
+ interface PageProps {
14
+ model: Model;
15
+ datasets: Dataset[];
16
+ constants: {
17
+ modelTypes: string[];
18
+ tasks: string[];
19
+ objectives: string[];
20
+ metrics: string[];
21
+ };
22
+ }
23
+
24
+ export default function ShowModelPage({ model, rootPath }: PageProps) {
25
+ return (
26
+ <div className="max-w-3xl mx-auto py-8">
27
+ <ModelDetails model={model} rootPath={rootPath} />
28
+ </div>
29
+ );
30
+ }
@@ -0,0 +1,95 @@
1
+ import React, { useState } from 'react';
2
+ import { Plus, FolderPlus } from 'lucide-react';
3
+ // import { Link } from 'react-router-dom';
4
+ import { mockFeatureGroups } from '../mockData';
5
+ import { FeatureCard } from '../components/features/FeatureCard';
6
+ import { FeatureGroupCard } from '../components/features/FeatureGroupCard';
7
+ import { EmptyState } from '../components/EmptyState';
8
+
9
+ export default function FeaturesPage() {
10
+ const [view, setView] = useState<'groups' | 'all'>('groups');
11
+
12
+ if (mockFeatureGroups.length === 0) {
13
+ return (
14
+ <div className="p-8">
15
+ <EmptyState
16
+ icon={FolderPlus}
17
+ title="Create your first feature group"
18
+ description="Create a group to organize your column features"
19
+ actionLabel="Create Group"
20
+ onAction={() => {/* Handle group creation */}}
21
+ />
22
+ </div>
23
+ );
24
+ }
25
+
26
+ const allFeatures = mockFeatureGroups.flatMap(g => g.features);
27
+
28
+ return (
29
+ <div className="p-8">
30
+ <div className="space-y-6">
31
+ <div className="flex justify-between items-center">
32
+ <div className="flex items-center gap-4">
33
+ <h2 className="text-xl font-semibold text-gray-900">Features</h2>
34
+ <div className="flex rounded-md shadow-sm">
35
+ <button
36
+ onClick={() => setView('groups')}
37
+ className={`px-4 py-2 text-sm font-medium rounded-l-md ${
38
+ view === 'groups'
39
+ ? 'bg-blue-600 text-white'
40
+ : 'bg-white text-gray-700 hover:text-gray-900 border border-gray-300'
41
+ }`}
42
+ >
43
+ Groups
44
+ </button>
45
+ <button
46
+ onClick={() => setView('all')}
47
+ className={`px-4 py-2 text-sm font-medium rounded-r-md ${
48
+ view === 'all'
49
+ ? 'bg-blue-600 text-white'
50
+ : 'bg-white text-gray-700 hover:text-gray-900 border border-l-0 border-gray-300'
51
+ }`}
52
+ >
53
+ All Features
54
+ </button>
55
+ </div>
56
+ </div>
57
+ <div className="flex gap-3">
58
+ <Link
59
+ to="/features/groups/new"
60
+ className="inline-flex items-center gap-2 px-4 py-2 bg-white border border-gray-300 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-50"
61
+ >
62
+ <FolderPlus className="w-4 h-4" />
63
+ New Group
64
+ </Link>
65
+ <Link
66
+ to="/features/new"
67
+ className="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700"
68
+ >
69
+ <Plus className="w-4 h-4" />
70
+ New Feature
71
+ </Link>
72
+ </div>
73
+ </div>
74
+
75
+ {view === 'groups' ? (
76
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
77
+ {mockFeatureGroups.map((group) => (
78
+ <FeatureGroupCard key={group.id} group={group} />
79
+ ))}
80
+ </div>
81
+ ) : (
82
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
83
+ {allFeatures.map((feature) => (
84
+ <FeatureCard
85
+ key={feature.id}
86
+ feature={feature}
87
+ group={mockFeatureGroups.find(g => g.id === feature.groupId)!}
88
+ />
89
+ ))}
90
+ </div>
91
+ )}
92
+ </div>
93
+ </div>
94
+ );
95
+ }
@@ -0,0 +1,100 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+ --background: 0 0% 100%;
7
+ --foreground: 222.2 84% 4.9%;
8
+ --card: 0 0% 100%;
9
+ --card-foreground: 222.2 84% 4.9%;
10
+ --popover: 0 0% 100%;
11
+ --popover-foreground: 222.2 84% 4.9%;
12
+ --primary: 221.2 83.2% 53.3%;
13
+ --primary-foreground: 210 40% 98%;
14
+ --secondary: 210 40% 96.1%;
15
+ --secondary-foreground: 222.2 47.4% 11.2%;
16
+ --muted: 210 40% 96.1%;
17
+ --muted-foreground: 215.4 16.3% 46.9%;
18
+ --accent: 210 40% 96.1%;
19
+ --accent-foreground: 222.2 47.4% 11.2%;
20
+ --destructive: 0 84.2% 60.2%;
21
+ --destructive-foreground: 210 40% 98%;
22
+ --border: 214.3 31.8% 91.4%;
23
+ --input: 214.3 31.8% 91.4%;
24
+ --ring: 221.2 83.2% 53.3%;
25
+ --radius: 0.5rem;
26
+ }
27
+
28
+ * {
29
+ @apply border-border;
30
+ }
31
+
32
+ body {
33
+ @apply bg-background text-foreground;
34
+ font-feature-settings: "rlig" 1, "calt" 1;
35
+ }
36
+
37
+ @layer base {
38
+ :root {
39
+ --background: 0 0% 100%;
40
+ --foreground: 0 0% 3.9%;
41
+ --card: 0 0% 100%;
42
+ --card-foreground: 0 0% 3.9%;
43
+ --popover: 0 0% 100%;
44
+ --popover-foreground: 0 0% 3.9%;
45
+ --primary: 0 0% 9%;
46
+ --primary-foreground: 0 0% 98%;
47
+ --secondary: 0 0% 96.1%;
48
+ --secondary-foreground: 0 0% 9%;
49
+ --muted: 0 0% 96.1%;
50
+ --muted-foreground: 0 0% 45.1%;
51
+ --accent: 0 0% 96.1%;
52
+ --accent-foreground: 0 0% 9%;
53
+ --destructive: 0 84.2% 60.2%;
54
+ --destructive-foreground: 0 0% 98%;
55
+ --border: 0 0% 89.8%;
56
+ --input: 0 0% 89.8%;
57
+ --ring: 0 0% 3.9%;
58
+ --chart-1: 12 76% 61%;
59
+ --chart-2: 173 58% 39%;
60
+ --chart-3: 197 37% 24%;
61
+ --chart-4: 43 74% 66%;
62
+ --chart-5: 27 87% 67%;
63
+ --radius: 0.5rem;
64
+ }
65
+ .dark {
66
+ --background: 0 0% 3.9%;
67
+ --foreground: 0 0% 98%;
68
+ --card: 0 0% 3.9%;
69
+ --card-foreground: 0 0% 98%;
70
+ --popover: 0 0% 3.9%;
71
+ --popover-foreground: 0 0% 98%;
72
+ --primary: 0 0% 98%;
73
+ --primary-foreground: 0 0% 9%;
74
+ --secondary: 0 0% 14.9%;
75
+ --secondary-foreground: 0 0% 98%;
76
+ --muted: 0 0% 14.9%;
77
+ --muted-foreground: 0 0% 63.9%;
78
+ --accent: 0 0% 14.9%;
79
+ --accent-foreground: 0 0% 98%;
80
+ --destructive: 0 62.8% 30.6%;
81
+ --destructive-foreground: 0 0% 98%;
82
+ --border: 0 0% 14.9%;
83
+ --input: 0 0% 14.9%;
84
+ --ring: 0 0% 83.1%;
85
+ --chart-1: 220 70% 50%;
86
+ --chart-2: 160 60% 45%;
87
+ --chart-3: 30 80% 55%;
88
+ --chart-4: 280 65% 60%;
89
+ --chart-5: 340 75% 55%;
90
+ }
91
+ }
92
+
93
+ @layer base {
94
+ * {
95
+ @apply border-border;
96
+ }
97
+ body {
98
+ @apply bg-background text-foreground;
99
+ }
100
+ }
@@ -0,0 +1,146 @@
1
+ import type { Datasource } from "./datasource";
2
+ import type { SplitterConfig } from '../components/dataset/splitters/types';
3
+
4
+ export type DatasetWorkflowStatus = "analyzing" | "ready" | "failed" | "locked";
5
+ export type DatasetStatus = "training" | "inference" | "retired";
6
+ export type ColumnType =
7
+ | "float"
8
+ | "integer"
9
+ | "boolean"
10
+ | "categorical"
11
+ | "datetime"
12
+ | "text"
13
+ | "string";
14
+
15
+ export type PreprocessingSteps = {
16
+ training?: PreprocessingStep;
17
+ inference?: PreprocessingStep;
18
+ };
19
+
20
+ export type Feature = {
21
+ id?: number;
22
+ name: string;
23
+ feature_class: string;
24
+ feature_position: number;
25
+ dataset_id?: number;
26
+ description?: string;
27
+ feature_type?: "calculation" | "lookup" | "other";
28
+ _destroy?: boolean;
29
+ };
30
+
31
+ export type PreprocessingStep = {
32
+ method:
33
+ | "none"
34
+ | "mean"
35
+ | "median"
36
+ | "ffill"
37
+ | "most_frequent"
38
+ | "categorical"
39
+ | "constant"
40
+ | "today";
41
+ params: {
42
+ value?: number;
43
+ constant?: string;
44
+ categorical_min?: number;
45
+ clip?: {
46
+ min?: number;
47
+ max?: number;
48
+ };
49
+ one_hot?: boolean;
50
+ ordinal_encoding?: boolean;
51
+ };
52
+ };
53
+
54
+ export interface StatisticSet {
55
+ mean?: number;
56
+ median?: number;
57
+ min?: number;
58
+ max?: number;
59
+ last_value?: string;
60
+ count?: number;
61
+ null_count?: number;
62
+ unique_count?: number;
63
+ most_frequent_value?: string;
64
+ counts: object;
65
+ num_rows?: number;
66
+ sample_data?: any[];
67
+ }
68
+
69
+ export interface Statistics {
70
+ raw: StatisticSet;
71
+ processed: StatisticSet;
72
+ }
73
+ export interface Column {
74
+ id: number;
75
+ name: string;
76
+ type: ColumnType;
77
+ description?: string;
78
+ dataset_id: number;
79
+ datatype: string;
80
+ polars_datatype: string;
81
+ is_target: boolean;
82
+ hidden: boolean;
83
+ drop_if_null: boolean;
84
+ sample_values: {};
85
+ statistics?: Statistics;
86
+ preprocessing_steps?: PreprocessingSteps;
87
+ }
88
+
89
+ export interface Dataset {
90
+ id: number;
91
+ name: string;
92
+ description?: string;
93
+ status: DatasetStatus;
94
+ needs_refresh: boolean;
95
+ workflow_status: DatasetWorkflowStatus;
96
+ target?: string;
97
+ num_rows?: number;
98
+ drop_cols?: string[];
99
+ datasource_id: number;
100
+ columns: Array<Column>;
101
+ sample_data: Record<string, any>[];
102
+ features?: Array<Feature>;
103
+ preprocessing_steps: {
104
+ training: Record<string, any>;
105
+ };
106
+ splitter_attributes?: {
107
+ splitter_type: string;
108
+ date_col: string;
109
+ months_test: number;
110
+ months_valid: number;
111
+ };
112
+ stacktrace?: string;
113
+ }
114
+
115
+ export interface NewDatasetForm {
116
+ dataset: {
117
+ name: string;
118
+ datasource_id: string;
119
+ splitter_attributes: SplitterConfig;
120
+ };
121
+ }
122
+
123
+ export interface NewDatasetFormProps {
124
+ constants: {
125
+ splitter_constants: {
126
+ SPLITTER_TYPES: Array<{
127
+ value: string;
128
+ label: string;
129
+ description: string;
130
+ }>;
131
+ DEFAULT_CONFIGS: Record<string, any>;
132
+ };
133
+ };
134
+ datasources: Array<{
135
+ id: number;
136
+ name: string;
137
+ columns: string[];
138
+ }>;
139
+ }
140
+
141
+ export interface PreprocessingConstants {
142
+ column_types: Array<{ value: ColumnType; label: string }>;
143
+ preprocessing_strategies: {
144
+ [K in ColumnType]: Array<{ value: string; label: string }>;
145
+ };
146
+ }
@@ -0,0 +1,33 @@
1
+ export type ColumnType =
2
+ | "float"
3
+ | "integer"
4
+ | "boolean"
5
+ | "categorical"
6
+ | "datetime"
7
+ | "text"
8
+ | "string";
9
+
10
+ export interface Schema {
11
+ [key: string]: ColumnType;
12
+ }
13
+
14
+ export interface Datasource {
15
+ id: number;
16
+ name: string;
17
+ datasource_type: string;
18
+ is_syncing: boolean;
19
+ last_synced_at: string | null;
20
+ columns: string[];
21
+ schema: Schema;
22
+ error?: string;
23
+ }
24
+
25
+ export interface DatasourceFormProps {
26
+ datasource?: Datasource;
27
+ constants: {
28
+ DATASOURCE_TYPES: Array<{ value: string; label: string; description: string }>;
29
+ s3: {
30
+ S3_REGIONS: Array<{ value: string; label: string }>;
31
+ };
32
+ };
33
+ }
@@ -0,0 +1 @@
1
+