cnhkmcp 2.1.1__py3-none-any.whl → 2.1.3__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 (117) hide show
  1. {cnhkmcp-2.1.1.dist-info → cnhkmcp-2.1.3.dist-info}/METADATA +1 -1
  2. cnhkmcp-2.1.3.dist-info/RECORD +6 -0
  3. cnhkmcp-2.1.3.dist-info/top_level.txt +1 -0
  4. cnhkmcp/__init__.py +0 -125
  5. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/README.md +0 -38
  6. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/ace.log +0 -0
  7. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/config.json +0 -6
  8. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/ace_lib.py +0 -1510
  9. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/fetch_all_datasets.py +0 -157
  10. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/fetch_all_documentation.py +0 -132
  11. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/fetch_all_operators.py +0 -99
  12. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/helpful_functions.py +0 -180
  13. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/icon.ico +0 -0
  14. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/icon.png +0 -0
  15. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/knowledge/test.txt +0 -1
  16. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/main.py +0 -576
  17. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/process_knowledge_base.py +0 -280
  18. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/rag_engine.py +0 -356
  19. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/requirements.txt +0 -7
  20. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/run.bat +0 -3
  21. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/vector_db/_manifest.json +0 -326
  22. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/vector_db/_meta.json +0 -1
  23. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/vector_db/be5d957c-b724-46e3-91d1-999e9f5f7d28/index_metadata.pickle +0 -0
  24. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/vector_db/chroma.sqlite3 +0 -0
  25. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242//321/211/320/266/320/246/321/206/320/274/320/261/321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/231/320/243/321/205/342/225/235/320/220/321/206/320/230/320/241.py +0 -265
  26. cnhkmcp/untracked/APP/.gitignore +0 -32
  27. cnhkmcp/untracked/APP/MODULAR_STRUCTURE.md +0 -112
  28. cnhkmcp/untracked/APP/README.md +0 -309
  29. cnhkmcp/untracked/APP/Tranformer/Transformer.py +0 -4985
  30. cnhkmcp/untracked/APP/Tranformer/ace.log +0 -0
  31. cnhkmcp/untracked/APP/Tranformer/ace_lib.py +0 -1510
  32. cnhkmcp/untracked/APP/Tranformer/helpful_functions.py +0 -180
  33. cnhkmcp/untracked/APP/Tranformer/output/Alpha_candidates.json +0 -2421
  34. cnhkmcp/untracked/APP/Tranformer/output/Alpha_candidates_/321/207/320/264/342/225/221/321/204/342/225/233/320/233.json +0 -654
  35. cnhkmcp/untracked/APP/Tranformer/output/Alpha_generated_expressions_error.json +0 -1034
  36. cnhkmcp/untracked/APP/Tranformer/output/Alpha_generated_expressions_success.json +0 -444
  37. cnhkmcp/untracked/APP/Tranformer/output/Alpha_generated_expressions_/321/207/320/264/342/225/221/321/204/342/225/233/320/233/321/205/320/237/320/277/321/207/320/253/342/224/244/321/206/320/236/320/265/321/210/342/225/234/342/225/234/321/205/320/225/320/265Machine_lib.json +0 -22
  38. cnhkmcp/untracked/APP/Tranformer/parsetab.py +0 -60
  39. cnhkmcp/untracked/APP/Tranformer/template_summary.txt +0 -3182
  40. cnhkmcp/untracked/APP/Tranformer/transformer_config.json +0 -7
  41. cnhkmcp/untracked/APP/Tranformer/validator.py +0 -889
  42. cnhkmcp/untracked/APP/ace.log +0 -69
  43. cnhkmcp/untracked/APP/ace_lib.py +0 -1510
  44. cnhkmcp/untracked/APP/blueprints/__init__.py +0 -6
  45. cnhkmcp/untracked/APP/blueprints/feature_engineering.py +0 -347
  46. cnhkmcp/untracked/APP/blueprints/idea_house.py +0 -221
  47. cnhkmcp/untracked/APP/blueprints/inspiration_house.py +0 -432
  48. cnhkmcp/untracked/APP/blueprints/paper_analysis.py +0 -570
  49. cnhkmcp/untracked/APP/custom_templates/templates.json +0 -1257
  50. cnhkmcp/untracked/APP/give_me_idea/BRAIN_Alpha_Template_Expert_SystemPrompt.md +0 -400
  51. cnhkmcp/untracked/APP/give_me_idea/ace_lib.py +0 -1510
  52. cnhkmcp/untracked/APP/give_me_idea/alpha_data_specific_template_master.py +0 -252
  53. cnhkmcp/untracked/APP/give_me_idea/fetch_all_datasets.py +0 -157
  54. cnhkmcp/untracked/APP/give_me_idea/fetch_all_operators.py +0 -99
  55. cnhkmcp/untracked/APP/give_me_idea/helpful_functions.py +0 -180
  56. cnhkmcp/untracked/APP/give_me_idea/what_is_Alpha_template.md +0 -11
  57. cnhkmcp/untracked/APP/helpful_functions.py +0 -180
  58. cnhkmcp/untracked/APP/hkSimulator/ace_lib.py +0 -1497
  59. cnhkmcp/untracked/APP/hkSimulator/autosimulator.py +0 -447
  60. cnhkmcp/untracked/APP/hkSimulator/helpful_functions.py +0 -180
  61. cnhkmcp/untracked/APP/mirror_config.txt +0 -20
  62. cnhkmcp/untracked/APP/operaters.csv +0 -129
  63. cnhkmcp/untracked/APP/requirements.txt +0 -53
  64. cnhkmcp/untracked/APP/run_app.bat +0 -28
  65. cnhkmcp/untracked/APP/run_app.sh +0 -34
  66. cnhkmcp/untracked/APP/setup_tsinghua.bat +0 -39
  67. cnhkmcp/untracked/APP/setup_tsinghua.sh +0 -43
  68. cnhkmcp/untracked/APP/simulator/alpha_submitter.py +0 -404
  69. cnhkmcp/untracked/APP/simulator/simulator_wqb.py +0 -618
  70. cnhkmcp/untracked/APP/ssrn-3332513.pdf +6 -109201
  71. cnhkmcp/untracked/APP/static/brain.js +0 -589
  72. cnhkmcp/untracked/APP/static/decoder.js +0 -1540
  73. cnhkmcp/untracked/APP/static/feature_engineering.js +0 -1729
  74. cnhkmcp/untracked/APP/static/idea_house.js +0 -937
  75. cnhkmcp/untracked/APP/static/inspiration.js +0 -465
  76. cnhkmcp/untracked/APP/static/inspiration_house.js +0 -868
  77. cnhkmcp/untracked/APP/static/paper_analysis.js +0 -390
  78. cnhkmcp/untracked/APP/static/script.js +0 -3082
  79. cnhkmcp/untracked/APP/static/simulator.js +0 -597
  80. cnhkmcp/untracked/APP/static/styles.css +0 -3127
  81. cnhkmcp/untracked/APP/static/usage_widget.js +0 -508
  82. cnhkmcp/untracked/APP/templates/alpha_inspector.html +0 -511
  83. cnhkmcp/untracked/APP/templates/feature_engineering.html +0 -960
  84. cnhkmcp/untracked/APP/templates/idea_house.html +0 -564
  85. cnhkmcp/untracked/APP/templates/index.html +0 -932
  86. cnhkmcp/untracked/APP/templates/inspiration_house.html +0 -861
  87. cnhkmcp/untracked/APP/templates/paper_analysis.html +0 -91
  88. cnhkmcp/untracked/APP/templates/simulator.html +0 -343
  89. cnhkmcp/untracked/APP/templates/transformer_web.html +0 -580
  90. cnhkmcp/untracked/APP/usage.md +0 -351
  91. cnhkmcp/untracked/APP//321/207/342/225/235/320/250/321/205/320/230/320/226/321/204/342/225/225/320/220/321/211/320/221/320/243/321/206/320/261/320/265/ace_lib.py +0 -1510
  92. cnhkmcp/untracked/APP//321/207/342/225/235/320/250/321/205/320/230/320/226/321/204/342/225/225/320/220/321/211/320/221/320/243/321/206/320/261/320/265/brain_alpha_inspector.py +0 -712
  93. cnhkmcp/untracked/APP//321/207/342/225/235/320/250/321/205/320/230/320/226/321/204/342/225/225/320/220/321/211/320/221/320/243/321/206/320/261/320/265/helpful_functions.py +0 -180
  94. cnhkmcp/untracked/APP//321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/231/320/243/321/205/342/225/235/320/220/321/206/320/230/320/241.py +0 -2456
  95. cnhkmcp/untracked/arXiv_API_Tool_Manual.md +0 -490
  96. cnhkmcp/untracked/arxiv_api.py +0 -229
  97. cnhkmcp/untracked/forum_functions.py +0 -998
  98. cnhkmcp/untracked/mcp/321/206/320/246/320/227/321/204/342/225/227/342/225/242/321/210/320/276/342/225/221/321/205/320/255/320/253/321/207/320/231/320/2302_/321/205/320/266/320/222/321/206/320/256/320/254/321/205/320/236/320/257/321/207/320/231/320/230/321/205/320/240/320/277/321/205/320/232/320/270/321/204/342/225/225/320/235/321/204/342/225/221/320/226/321/206/342/225/241/320/237/321/210/320/267/320/230/321/205/320/251/320/270/321/205/342/226/221/342/226/222/321/210/320/277/320/245/321/210/342/224/220/320/251/321/204/342/225/225/320/272/forum_functions.py +0 -407
  99. cnhkmcp/untracked/mcp/321/206/320/246/320/227/321/204/342/225/227/342/225/242/321/210/320/276/342/225/221/321/205/320/255/320/253/321/207/320/231/320/2302_/321/205/320/266/320/222/321/206/320/256/320/254/321/205/320/236/320/257/321/207/320/231/320/230/321/205/320/240/320/277/321/205/320/232/320/270/321/204/342/225/225/320/235/321/204/342/225/221/320/226/321/206/342/225/241/320/237/321/210/320/267/320/230/321/205/320/251/320/270/321/205/342/226/221/342/226/222/321/210/320/277/320/245/321/210/342/224/220/320/251/321/204/342/225/225/320/272/platform_functions.py +0 -2415
  100. cnhkmcp/untracked/mcp/321/206/320/246/320/227/321/204/342/225/227/342/225/242/321/210/320/276/342/225/221/321/205/320/255/320/253/321/207/320/231/320/2302_/321/205/320/266/320/222/321/206/320/256/320/254/321/205/320/236/320/257/321/207/320/231/320/230/321/205/320/240/320/277/321/205/320/232/320/270/321/204/342/225/225/320/235/321/204/342/225/221/320/226/321/206/342/225/241/320/237/321/210/320/267/320/230/321/205/320/251/320/270/321/205/342/226/221/342/226/222/321/210/320/277/320/245/321/210/342/224/220/320/251/321/204/342/225/225/320/272/user_config.json +0 -31
  101. cnhkmcp/untracked/mcp/321/206/320/246/320/227/321/204/342/225/227/342/225/242/321/210/320/276/342/225/221/321/205/320/255/320/253/321/207/320/231/320/2302_/321/205/320/266/320/222/321/206/320/256/320/254/321/205/320/236/320/257/321/207/320/231/320/230/321/205/320/240/320/277/321/205/320/232/320/270/321/204/342/225/225/320/235/321/204/342/225/221/320/226/321/206/342/225/241/320/237/321/210/320/267/320/230/321/205/320/251/320/270/321/205/342/226/221/342/226/222/321/210/320/277/320/245/321/210/342/224/220/320/251/321/204/342/225/225/320/272//321/210/320/276/320/271AI/321/210/320/277/342/225/227/321/210/342/224/220/320/251/321/204/342/225/225/320/272/321/206/320/246/320/227/321/206/320/261/320/263/321/206/320/255/320/265/321/205/320/275/320/266/321/204/342/225/235/320/252/321/204/342/225/225/320/233/321/210/342/225/234/342/225/234/321/206/342/225/241/320/237/321/210/320/267/320/230/321/205/320/251/320/270.md +0 -101
  102. cnhkmcp/untracked/mcp/321/206/320/246/320/227/321/204/342/225/227/342/225/242/321/210/320/276/342/225/221/321/205/320/255/320/253/321/207/320/231/320/2302_/321/205/320/266/320/222/321/206/320/256/320/254/321/205/320/236/320/257/321/207/320/231/320/230/321/205/320/240/320/277/321/205/320/232/320/270/321/204/342/225/225/320/235/321/204/342/225/221/320/226/321/206/342/225/241/320/237/321/210/320/267/320/230/321/205/320/251/320/270/321/205/342/226/221/342/226/222/321/210/320/277/320/245/321/210/342/224/220/320/251/321/204/342/225/225/320/272//321/211/320/225/320/235/321/207/342/225/234/320/276/321/205/320/231/320/235/321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/230/320/241_/321/205/320/276/320/231/321/210/320/263/320/225/321/205/342/224/220/320/225/321/210/320/266/320/221/321/204/342/225/233/320/255/321/210/342/225/241/320/246/321/205/320/234/320/225.py +0 -190
  103. cnhkmcp/untracked/platform_functions.py +0 -2886
  104. cnhkmcp/untracked/sample_mcp_config.json +0 -11
  105. cnhkmcp/untracked/user_config.json +0 -31
  106. cnhkmcp/untracked//321/207/320/264/342/225/221/321/204/342/225/233/320/233/321/205/320/237/320/222/321/210/320/220/320/223/321/206/320/246/320/227/321/206/320/261/320/263_BRAIN_Alpha_Test_Requirements_and_Tips.md +0 -202
  107. cnhkmcp/untracked//321/207/320/264/342/225/221/321/204/342/225/233/320/233/321/205/342/225/226/320/265/321/204/342/225/234/320/254/321/206/342/225/241/320/221_Alpha_explaination_workflow.md +0 -56
  108. cnhkmcp/untracked//321/207/320/264/342/225/221/321/204/342/225/233/320/233/321/205/342/225/226/320/265/321/204/342/225/234/320/254/321/206/342/225/241/320/221_BRAIN_6_Tips_Datafield_Exploration_Guide.md +0 -194
  109. cnhkmcp/untracked//321/207/320/264/342/225/221/321/204/342/225/233/320/233/321/205/342/225/226/320/265/321/204/342/225/234/320/254/321/206/342/225/241/320/221_BRAIN_Alpha_Improvement_Workflow.md +0 -101
  110. cnhkmcp/untracked//321/207/320/264/342/225/221/321/204/342/225/233/320/233/321/205/342/225/226/320/265/321/204/342/225/234/320/254/321/206/342/225/241/320/221_Dataset_Exploration_Expert_Manual.md +0 -436
  111. cnhkmcp/untracked//321/207/320/264/342/225/221/321/204/342/225/233/320/233/321/205/342/225/226/320/265/321/204/342/225/234/320/254/321/206/342/225/241/320/221_daily_report_workflow.md +0 -128
  112. cnhkmcp/untracked//321/211/320/225/320/235/321/207/342/225/234/320/276/321/205/320/231/320/235/321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/230/320/241_/321/205/320/276/320/231/321/210/320/263/320/225/321/205/342/224/220/320/225/321/210/320/266/320/221/321/204/342/225/233/320/255/321/210/342/225/241/320/246/321/205/320/234/320/225.py +0 -190
  113. cnhkmcp-2.1.1.dist-info/RECORD +0 -115
  114. cnhkmcp-2.1.1.dist-info/top_level.txt +0 -1
  115. {cnhkmcp-2.1.1.dist-info → cnhkmcp-2.1.3.dist-info}/WHEEL +0 -0
  116. {cnhkmcp-2.1.1.dist-info → cnhkmcp-2.1.3.dist-info}/entry_points.txt +0 -0
  117. {cnhkmcp-2.1.1.dist-info → cnhkmcp-2.1.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,1729 +0,0 @@
1
- // Feature Engineering JavaScript
2
-
3
- // Store API key and state in session storage
4
- let apiKey = sessionStorage.getItem('deepseekApiKey');
5
- let modelProvider = sessionStorage.getItem('featureEngProvider') || 'deepseek';
6
- let modelName = sessionStorage.getItem('featureEngModelName') || 'deepseek-chat';
7
- let currentStep = parseInt(sessionStorage.getItem('featureEngCurrentStep')) || 1;
8
- let pipelineSteps = JSON.parse(sessionStorage.getItem('featureEngPipelineSteps')) || [];
9
- let currentOptions = JSON.parse(sessionStorage.getItem('featureEngCurrentOptions')) || [];
10
- let currentDataState = sessionStorage.getItem('featureEngCurrentDataState') || 'raw data';
11
- let conversationHistory = JSON.parse(sessionStorage.getItem('featureEngConversationHistory')) || [];
12
- let customSystemPrompt = sessionStorage.getItem('customSystemPrompt') || null;
13
-
14
- // DOM Elements
15
- const modelProviderSelect = document.getElementById('modelProvider');
16
- const apiKeyInput = document.getElementById('apiKey');
17
- const modelNameInput = document.getElementById('modelName');
18
- const saveApiKeyBtn = document.getElementById('saveApiKey');
19
- const apiConfigSection = document.getElementById('apiConfigSection');
20
- const showApiConfigSection = document.getElementById('showApiConfigSection');
21
- const showApiConfigBtn = document.getElementById('showApiConfig');
22
- const loadQuestionTemplateBtn = document.getElementById('loadQuestionTemplate');
23
- const editSystemPromptBtn = document.getElementById('editSystemPrompt');
24
- const questionTemplateInput = document.getElementById('questionTemplate');
25
- const startPipelineBtn = document.getElementById('startPipeline');
26
- const systemPromptModal = document.getElementById('systemPromptModal');
27
- const systemPromptTextarea = document.getElementById('systemPromptTextarea');
28
- const loadDefaultPromptBtn = document.getElementById('loadDefaultPrompt');
29
- const initialSetupSection = document.getElementById('initialSetup');
30
- const optionsSection = document.getElementById('optionsSection');
31
- const optionsContainer = document.getElementById('optionsContainer');
32
- const clearOptionsBtn = document.getElementById('clearOptions');
33
- const exportPipelineBtn = document.getElementById('exportPipeline');
34
- const pipelineStatus = document.getElementById('pipelineStatus');
35
- const pipelineStepsDiv = document.getElementById('pipelineSteps');
36
- const modalOverlay = document.getElementById('modalOverlay');
37
- const categoryPopup = document.getElementById('categoryPopup');
38
- const categoryPopupTitle = document.getElementById('categoryPopupTitle');
39
- const categoryPopupDescription = document.getElementById('categoryPopupDescription');
40
- const categoryPopupOperators = document.getElementById('categoryPopupOperators');
41
- const categoryPopupOperatorsTitle = document.getElementById('categoryPopupOperatorsTitle');
42
-
43
- // Initialize API key if exists
44
- if (apiKey) {
45
- apiKeyInput.value = apiKey;
46
- }
47
-
48
- // Initialize model provider and name
49
- modelProviderSelect.value = modelProvider;
50
- modelNameInput.value = modelName;
51
-
52
- // Update model name placeholder based on provider
53
- function updateModelNamePlaceholder() {
54
- const provider = modelProviderSelect.value;
55
- if (provider === 'kimi') {
56
- modelNameInput.placeholder = 'e.g., kimi-k2-0711-preview';
57
- if (modelNameInput.value === 'deepseek-chat') {
58
- modelNameInput.value = 'kimi-k2-0711-preview';
59
- }
60
- } else {
61
- modelNameInput.placeholder = 'e.g., deepseek-chat, deepseek-coder';
62
- if (modelNameInput.value === 'kimi-k2-0711-preview') {
63
- modelNameInput.value = 'deepseek-chat';
64
- }
65
- }
66
- }
67
-
68
- // Model provider change handler
69
- modelProviderSelect.addEventListener('change', () => {
70
- modelProvider = modelProviderSelect.value;
71
- sessionStorage.setItem('featureEngProvider', modelProvider);
72
- updateModelNamePlaceholder();
73
- });
74
-
75
- // Model name change handler
76
- modelNameInput.addEventListener('input', () => {
77
- modelName = modelNameInput.value;
78
- sessionStorage.setItem('featureEngModelName', modelName);
79
- });
80
-
81
- // Initialize placeholder on page load
82
- updateModelNamePlaceholder();
83
-
84
- // Check if API is already configured and hide config section if so
85
- function checkApiConfigStatus() {
86
- if (apiKey && modelProvider && modelName) {
87
- apiConfigSection.style.display = 'none';
88
- showApiConfigSection.style.display = 'block';
89
- } else {
90
- apiConfigSection.style.display = 'block';
91
- showApiConfigSection.style.display = 'none';
92
- }
93
- }
94
-
95
- // Show API Config button event listener
96
- showApiConfigBtn.addEventListener('click', () => {
97
- apiConfigSection.style.display = 'block';
98
- showApiConfigSection.style.display = 'none';
99
- });
100
-
101
- // Load existing conversation state on page load
102
- window.addEventListener('DOMContentLoaded', () => {
103
- console.log('Loading conversation state...');
104
- console.log('Conversation history:', conversationHistory);
105
- console.log('Current step:', currentStep);
106
- console.log('Pipeline steps:', pipelineSteps);
107
- console.log('Current options:', currentOptions);
108
-
109
- // Check API config status
110
- checkApiConfigStatus();
111
-
112
- // If we have conversation history, display the current options
113
- if (conversationHistory.length > 0 && currentOptions.length > 0) {
114
- console.log('Restoring conversation state...');
115
- initialSetupSection.style.display = 'none';
116
- optionsSection.style.display = 'block';
117
- displayOptions();
118
- updatePipelineStatus();
119
- } else {
120
- // Ensure we start with a clean state
121
- console.log('Starting with clean state...');
122
- initialSetupSection.style.display = 'block';
123
- optionsSection.style.display = 'none';
124
- }
125
- });
126
-
127
- // Close modal when clicking overlay
128
- modalOverlay.addEventListener('click', (e) => {
129
- if (e.target === modalOverlay) {
130
- modalOverlay.classList.remove('active');
131
- // Find the editing card and cancel edit
132
- const editingCard = document.querySelector('.option-card.editing');
133
- if (editingCard) {
134
- const index = parseInt(editingCard.dataset.optionIndex);
135
- cancelEdit(index);
136
- }
137
- }
138
- });
139
-
140
- // Save API Key and Test Connection
141
- saveApiKeyBtn.addEventListener('click', async () => {
142
- const newApiKey = apiKeyInput.value.trim();
143
- const newProvider = modelProviderSelect.value;
144
- const newModelName = modelNameInput.value.trim();
145
-
146
- if (!newApiKey) {
147
- showNotification('Please enter a valid API key', 'error');
148
- return;
149
- }
150
-
151
- if (!newModelName) {
152
- showNotification('Please enter a model name', 'error');
153
- return;
154
- }
155
-
156
- try {
157
- showLoading('Testing API connection...');
158
-
159
- const response = await fetch('/feature-engineering/api/test-deepseek', {
160
- method: 'POST',
161
- headers: {
162
- 'X-API-Key': newApiKey,
163
- 'Content-Type': 'application/json'
164
- },
165
- body: JSON.stringify({
166
- provider: newProvider,
167
- model_name: newModelName
168
- })
169
- });
170
-
171
- const data = await response.json();
172
-
173
- if (response.ok && data.success) {
174
- sessionStorage.setItem('deepseekApiKey', newApiKey);
175
- sessionStorage.setItem('featureEngProvider', newProvider);
176
- sessionStorage.setItem('featureEngModelName', newModelName);
177
- apiKey = newApiKey;
178
- modelProvider = newProvider;
179
- modelName = newModelName;
180
- showNotification(`${newProvider.charAt(0).toUpperCase() + newProvider.slice(1)} API connection successful`, 'success');
181
-
182
- // Hide API config section after successful configuration
183
- apiConfigSection.style.display = 'none';
184
- showApiConfigSection.style.display = 'block';
185
- } else {
186
- showNotification(`API Error: ${data.error || 'Unknown error'}`, 'error');
187
- console.error('API Error Details:', data);
188
- }
189
- } catch (error) {
190
- showNotification('Error testing API connection: ' + error.message, 'error');
191
- console.error('API Test Error:', error);
192
- } finally {
193
- hideLoading();
194
- }
195
- });
196
-
197
- // Load Question Template
198
- loadQuestionTemplateBtn.addEventListener('click', () => {
199
- const template = `Current step: 0
200
- Current datafield: modify_your_input
201
- Current datafiled description: input_datafield_description
202
- Initial EDA observation: input_datafield_eda_observation
203
- Previous steps and categories used: None
204
- Current data state: this is the first step raw data`;
205
-
206
- questionTemplateInput.value = template;
207
- showNotification('Question template loaded', 'success');
208
- });
209
-
210
- // Edit System Prompt
211
- editSystemPromptBtn.addEventListener('click', () => {
212
- // Load current system prompt or default
213
- if (customSystemPrompt) {
214
- systemPromptTextarea.value = customSystemPrompt;
215
- } else {
216
- loadDefaultSystemPrompt();
217
- }
218
- systemPromptModal.style.display = 'block';
219
- });
220
-
221
- // Load Default System Prompt
222
- loadDefaultPromptBtn.addEventListener('click', loadDefaultSystemPrompt);
223
-
224
- // Hide category popup when clicking outside
225
- document.addEventListener('click', (e) => {
226
- if (!categoryPopup.contains(e.target) && !e.target.classList.contains('clickable-category')) {
227
- hideCategoryPopup();
228
- }
229
- });
230
-
231
- async function loadDefaultSystemPrompt() {
232
- try {
233
- showLoading('Loading default system prompt...');
234
-
235
- const response = await fetch('/feature-engineering/api/get-default-system-prompt', {
236
- method: 'GET',
237
- headers: {
238
- 'Content-Type': 'application/json'
239
- }
240
- });
241
-
242
- const data = await response.json();
243
-
244
- if (response.ok && data.success) {
245
- systemPromptTextarea.value = data.default_system_prompt;
246
- showNotification('Default system prompt loaded from backend', 'success');
247
- } else {
248
- showNotification(`Error loading default prompt: ${data.error || 'Unknown error'}`, 'error');
249
- console.error('Error loading default prompt:', data);
250
- }
251
- } catch (error) {
252
- showNotification('Error loading default system prompt: ' + error.message, 'error');
253
- console.error('Error loading default prompt:', error);
254
- } finally {
255
- hideLoading();
256
- }
257
- }
258
-
259
- // Close System Prompt Modal
260
- function closeSystemPromptModal() {
261
- systemPromptModal.style.display = 'none';
262
- }
263
-
264
- // Save System Prompt
265
- function saveSystemPrompt() {
266
- const prompt = systemPromptTextarea.value.trim();
267
- if (!prompt) {
268
- showNotification('System prompt cannot be empty', 'error');
269
- return;
270
- }
271
-
272
- customSystemPrompt = prompt;
273
- sessionStorage.setItem('customSystemPrompt', prompt);
274
- systemPromptModal.style.display = 'none';
275
- showNotification('System prompt saved successfully', 'success');
276
- }
277
-
278
- // Start Feature Engineering Pipeline
279
- startPipelineBtn.addEventListener('click', async () => {
280
- if (!apiKey) {
281
- showNotification('Please configure your Deepseek API key first', 'error');
282
- return;
283
- }
284
-
285
- const questionTemplate = questionTemplateInput.value.trim();
286
-
287
- if (!questionTemplate) {
288
- showNotification('Please load or enter a question template', 'error');
289
- return;
290
- }
291
-
292
- try {
293
- showLoading('Getting AI recommendations...');
294
-
295
- console.log('=== STARTING NEW PIPELINE ===');
296
- console.log('Current conversation history before start:', conversationHistory);
297
- console.log('Conversation history length:', conversationHistory.length);
298
-
299
- const response = await fetch('/feature-engineering/api/continue-conversation', {
300
- method: 'POST',
301
- headers: {
302
- 'X-API-Key': apiKey,
303
- 'Content-Type': 'application/json'
304
- },
305
- body: JSON.stringify({
306
- conversation_history: [],
307
- user_message: questionTemplate,
308
- custom_system_prompt: customSystemPrompt,
309
- provider: modelProvider,
310
- model_name: modelName
311
- })
312
- });
313
-
314
- const data = await response.json();
315
-
316
- console.log('=== INITIAL PROMPT ===');
317
- console.log('User message:', questionTemplate);
318
- console.log('=== AI RESPONSE ===');
319
- console.log('AI response:', data.response);
320
- console.log('==================');
321
-
322
- if (response.ok && data.success) {
323
- // Clear conversation history and reset state for new pipeline
324
- conversationHistory = [];
325
- currentStep = 1;
326
- pipelineSteps = [];
327
- currentDataState = 'raw data';
328
-
329
- // Add to conversation history
330
- conversationHistory.push({
331
- role: 'user',
332
- content: questionTemplate
333
- });
334
- conversationHistory.push({
335
- role: 'assistant',
336
- content: data.response
337
- });
338
-
339
- console.log('Conversation history after initial:', conversationHistory);
340
- console.log('Conversation history length:', conversationHistory.length);
341
-
342
- // Parse the AI response to extract options
343
- parseAIResponse(data.response);
344
-
345
- // Save conversation state
346
- saveConversationState();
347
-
348
- // Show options section and hide initial setup
349
- initialSetupSection.style.display = 'none';
350
- optionsSection.style.display = 'block';
351
- updatePipelineStatus();
352
-
353
- showNotification('AI recommendations loaded successfully', 'success');
354
- } else {
355
- showNotification(`Error: ${data.error || 'Unknown error'}`, 'error');
356
- console.error('API Error Details:', data);
357
- }
358
- } catch (error) {
359
- showNotification('Error getting recommendations: ' + error.message, 'error');
360
- console.error('Pipeline Start Error:', error);
361
- } finally {
362
- hideLoading();
363
- }
364
- });
365
-
366
- // Parse AI Response to extract options
367
- function parseAIResponse(response) {
368
- console.log('=== PARSING AI RESPONSE ===');
369
- console.log('Raw response:', response);
370
-
371
- currentOptions = [];
372
-
373
- // Dynamic content cleaning - remove various summary sections
374
- let cleanResponse = response;
375
- const summaryPatterns = [
376
- /### \*\*Best Choice\?\*\*[\s\S]*$/i,
377
- /### \*\*Recommended Next Step:\*\*[\s\S]*$/i,
378
- /Most recommended choice:[\s\S]*$/i,
379
- /Rationale:[\s\S]*$/i,
380
- /This maintains[\s\S]*$/i,
381
- /Would you like to proceed[\s\S]*$/i,
382
- /\*Example features to create:\*[\s\S]*$/i
383
- ];
384
-
385
- summaryPatterns.forEach(pattern => {
386
- cleanResponse = cleanResponse.replace(pattern, '');
387
- });
388
-
389
- console.log('Cleaned response:', cleanResponse);
390
-
391
- // Extract top-level context dynamically
392
- let globalContext = null;
393
- const contextPatterns = [
394
- /\*\*Context:\*\*\s*([\s\S]*?)(?=###|####|\*\*Option|\*\*Choose|Option\s+\d+|$)/i,
395
- /Context:\s*([\s\S]*?)(?=###|####|\*\*Option|\*\*Choose|Option\s+\d+|$)/i
396
- ];
397
-
398
- for (const pattern of contextPatterns) {
399
- const match = cleanResponse.match(pattern);
400
- if (match) {
401
- globalContext = match[1].trim();
402
- console.log('Found global context:', globalContext);
403
- break;
404
- }
405
- }
406
-
407
- // Dynamic option pattern matching
408
- const optionPatterns = [
409
- /(?:####\s*)?(?:\*\*)?Option\s+(\d+)\s+for\s+Step\s+(\d+):?\*?\*?\s*([\s\S]*?)(?=(?:####\s*)?(?:\*\*)?Option\s+\d+\s+for\s+Step\s+\d+:|Most recommended|Rationale:|This maintains|$)/gi,
410
- /(?:####\s*)?(?:\*\*)?option\s+(\d+)\s+for\s+Step\s+(\d+):\s*([\s\S]*?)(?=(?:####\s*)?(?:\*\*)?option\s+\d+\s+for\s+Step\s+\d+:|Most recommended|Rationale:|This maintains|$)/gi
411
- ];
412
-
413
- let optionsFound = false;
414
-
415
- for (const optionPattern of optionPatterns) {
416
- let match;
417
- const tempOptions = [];
418
-
419
- while ((match = optionPattern.exec(cleanResponse)) !== null) {
420
- const optionNumber = match[1];
421
- const stepNumber = match[2];
422
- const content = match[3].trim();
423
-
424
- console.log(`Found option ${optionNumber} for step ${stepNumber}:`, content);
425
-
426
- const parsedOption = parseOptionContent(content, globalContext, parseInt(optionNumber), parseInt(stepNumber));
427
- if (parsedOption) {
428
- tempOptions.push(parsedOption);
429
- }
430
- }
431
-
432
- if (tempOptions.length > 0) {
433
- currentOptions = tempOptions;
434
- optionsFound = true;
435
- break;
436
- }
437
-
438
- // Reset regex lastIndex for next pattern
439
- optionPattern.lastIndex = 0;
440
- }
441
-
442
- if (!optionsFound) {
443
- console.log('No options found with standard patterns, trying fallback parsing...');
444
- // Fallback: try to find any numbered options
445
- const fallbackPattern = /(\d+)[.)]\s*([\s\S]*?)(?=\d+[.)]|$)/g;
446
- let match;
447
- while ((match = fallbackPattern.exec(cleanResponse)) !== null) {
448
- const optionNumber = match[1];
449
- const content = match[2].trim();
450
- console.log(`Fallback found option ${optionNumber}:`, content);
451
-
452
- const parsedOption = parseOptionContent(content, globalContext, parseInt(optionNumber), currentStep);
453
- if (parsedOption) {
454
- currentOptions.push(parsedOption);
455
- }
456
- }
457
- }
458
-
459
- // Ensure all options have the same context (copy from first if needed)
460
- if (currentOptions.length > 0 && currentOptions[0].context) {
461
- const sharedContext = currentOptions[0].context;
462
- currentOptions.forEach(option => {
463
- if (!option.context || option.context.includes('Same as above')) {
464
- option.context = sharedContext;
465
- }
466
- });
467
- }
468
-
469
- console.log('Total options parsed:', currentOptions.length);
470
- console.log('Current options:', currentOptions);
471
- console.log('========================');
472
-
473
- displayOptions();
474
-
475
- // Save current options state
476
- saveConversationState();
477
- }
478
-
479
- // Helper function to parse individual option content
480
- function parseOptionContent(content, globalContext, optionNumber, stepNumber) {
481
- console.log('=== PARSING OPTION CONTENT ===');
482
- console.log('Raw content:', content);
483
-
484
- // More precise patterns for the exact format
485
- const contextPatterns = [
486
- /Context:\s*([\s\S]*?)(?=\s+Choose next step:)/i,
487
- /\*\*Context:\*\*\s*([\s\S]*?)(?=\s+\*\*Choose next step:\*\*)/i,
488
- /Context:\s*([\s\S]*?)(?=\s+\*\*Choose next step:\*\*)/i
489
- ];
490
-
491
- // Multiple patterns for next step extraction
492
- const nextStepPatterns = [
493
- /Choose next step:\s*([^\n\r]+?)(?=\s+Reason:)/i,
494
- /\*\*Choose next step:\*\*\s*([^\n\r]+?)(?=\s+\*\*Reason:\*\*)/i,
495
- /Choose next step:\s*([^\n\r]+?)(?=\s+\*\*Reason:\*\*)/i
496
- ];
497
-
498
- // Multiple patterns for reason extraction
499
- const reasonPatterns = [
500
- /Reason:\s*([\s\S]*?)(?=Most recommended|Rationale:|This maintains|$)/i,
501
- /\*\*Reason:\*\*\s*([\s\S]*?)(?=Most recommended|Rationale:|This maintains|$)/i
502
- ];
503
-
504
- let contextMatch = null;
505
- let nextStepMatch = null;
506
- let reasonMatch = null;
507
-
508
- // Try context patterns
509
- for (const pattern of contextPatterns) {
510
- contextMatch = content.match(pattern);
511
- if (contextMatch) {
512
- console.log('Context pattern matched:', pattern);
513
- console.log('Context match:', contextMatch[1].trim());
514
- break;
515
- }
516
- }
517
-
518
- // Try next step patterns
519
- for (const pattern of nextStepPatterns) {
520
- nextStepMatch = content.match(pattern);
521
- if (nextStepMatch) {
522
- console.log('Next step pattern matched:', pattern);
523
- console.log('Next step match:', nextStepMatch[1].trim());
524
- break;
525
- }
526
- }
527
-
528
- // Try reason patterns
529
- for (const pattern of reasonPatterns) {
530
- reasonMatch = content.match(pattern);
531
- if (reasonMatch) {
532
- console.log('Reason pattern matched:', pattern);
533
- console.log('Reason match:', reasonMatch[1].trim());
534
- break;
535
- }
536
- }
537
-
538
- console.log('Parsing results:', {
539
- contextMatch: contextMatch ? contextMatch[1].trim() : null,
540
- nextStepMatch: nextStepMatch ? nextStepMatch[1].trim() : null,
541
- reasonMatch: reasonMatch ? reasonMatch[1].trim() : null,
542
- globalContext: globalContext ? 'available' : 'not available'
543
- });
544
-
545
- // Determine context to use - prioritize individual option context over global context
546
- let context = null;
547
- if (contextMatch) {
548
- context = contextMatch[1].trim().replace(/Same as above/gi, '').trim();
549
- console.log('Using individual option context:', context);
550
- } else if (globalContext) {
551
- context = globalContext;
552
- console.log('Using global context:', context);
553
- }
554
-
555
- if ((context || contextMatch) && nextStepMatch && reasonMatch) {
556
- const result = {
557
- optionNumber: optionNumber,
558
- stepNumber: stepNumber,
559
- context: context,
560
- nextStep: nextStepMatch[1].trim().replace(/\*\*/g, ''),
561
- reason: reasonMatch[1].trim(),
562
- originalContent: content
563
- };
564
- result.reason = "I used xxxxxxx operator" + " in this step, in order to \n" + result.reason;
565
- console.log('Successfully parsed option:', result);
566
- console.log('Final context being stored:', result.context);
567
- console.log('===============================');
568
- return result;
569
- } else {
570
- console.log('Failed to parse option content:', {
571
- hasContext: !!(context || contextMatch),
572
- hasNextStep: !!nextStepMatch,
573
- hasReason: !!reasonMatch
574
- });
575
- console.log('===============================');
576
- return null;
577
- }
578
- }
579
-
580
- // Display options as cards
581
- function displayOptions() {
582
- optionsContainer.innerHTML = '';
583
-
584
- currentOptions.forEach((option, index) => {
585
- const card = createOptionCard(option, index);
586
- optionsContainer.appendChild(card);
587
- });
588
- }
589
-
590
- // Create option card
591
- function createOptionCard(option, index) {
592
- console.log('=== CREATING OPTION CARD ===');
593
- console.log('Option index:', index);
594
- console.log('Option context being displayed:', option.context);
595
- console.log('Option next step:', option.nextStep);
596
- console.log('Option reason:', option.reason);
597
- console.log('============================');
598
-
599
- const card = document.createElement('div');
600
- card.className = 'option-card';
601
- card.dataset.optionIndex = index;
602
-
603
- card.innerHTML = `
604
- <div class="option-header">
605
- <span class="option-number">Option ${option.optionNumber}</span>
606
- <div class="option-actions">
607
- <button class="select-btn" onclick="selectAndEdit(${index})">Select & Edit</button>
608
- <button class="btn btn-secondary" style="background: #9b59b6; margin-left: 5px; padding: 5px 10px; font-size: 12px;" onclick="openOperatorSuggestions(${index})">🎯 Get Operators</button>
609
- </div>
610
- </div>
611
- <div class="option-content">
612
- <div class="option-field readonly">
613
- <label>Context:</label>
614
- <textarea readonly class="auto-resize-textarea">${option.context}</textarea>
615
- </div>
616
- <div class="option-field readonly">
617
- <label>Next Step:</label>
618
- <input type="text" readonly value="${option.nextStep}" style="display: none;">
619
- <div class="readonly-display">
620
- <span class="clickable-category" onclick="showCategoryPopup('${option.nextStep.replace(/'/g, "\\'")}', event)">${option.nextStep}</span>
621
- </div>
622
- </div>
623
- <div class="option-field readonly">
624
- <label>Reason:</label>
625
- <textarea readonly class="auto-resize-textarea">${option.reason}</textarea>
626
- </div>
627
- </div>
628
- `;
629
-
630
- // Auto-resize textareas after creating the card
631
- setTimeout(() => {
632
- const textareas = card.querySelectorAll('.auto-resize-textarea');
633
- textareas.forEach(autoResizeTextarea);
634
- }, 0);
635
-
636
- return card;
637
- }
638
-
639
- // Auto-resize textarea function
640
- function autoResizeTextarea(textarea) {
641
- textarea.style.height = 'auto';
642
- textarea.style.height = Math.max(textarea.scrollHeight, 60) + 'px';
643
- }
644
-
645
- // Select and edit option
646
- function selectAndEdit(index) {
647
- const card = document.querySelector(`[data-option-index="${index}"]`);
648
- const fields = card.querySelectorAll('.option-field');
649
-
650
- // Remove readonly class and make fields editable
651
- fields.forEach(field => {
652
- field.classList.remove('readonly');
653
- const input = field.querySelector('input, textarea');
654
- const readonlyDisplay = field.querySelector('.readonly-display');
655
-
656
- if (input) {
657
- input.removeAttribute('readonly');
658
- // For Next Step field, show input and hide readonly display
659
- if (readonlyDisplay) {
660
- input.style.display = 'block';
661
- readonlyDisplay.style.display = 'none';
662
- }
663
- }
664
-
665
- // Add auto-resize functionality to textareas
666
- if (input && input.tagName === 'TEXTAREA') {
667
- input.addEventListener('input', () => autoResizeTextarea(input));
668
- autoResizeTextarea(input); // Initial resize
669
- }
670
- });
671
-
672
- // Update card state and show modal overlay
673
- card.classList.add('editing');
674
- modalOverlay.classList.add('active');
675
-
676
- // Update action buttons
677
- const actionsDiv = card.querySelector('.option-actions');
678
- actionsDiv.innerHTML = `
679
- <button class="save-btn" onclick="saveOption(${index})">Save Changes</button>
680
- <button class="cancel-btn" onclick="cancelEdit(${index})">Cancel</button>
681
- <button class="send-continue-btn" onclick="sendAndContinue(${index})">Send & Continue</button>
682
- `;
683
- }
684
-
685
- // Save option
686
- function saveOption(index) {
687
- const card = document.querySelector(`[data-option-index="${index}"]`);
688
- const contextTextarea = card.querySelector('.option-field:nth-child(1) textarea');
689
- const nextStepInput = card.querySelector('.option-field:nth-child(2) input');
690
- const reasonTextarea = card.querySelector('.option-field:nth-child(3) textarea');
691
-
692
- // Update the option data
693
- currentOptions[index].context = contextTextarea.value;
694
- currentOptions[index].nextStep = nextStepInput.value;
695
- currentOptions[index].reason = reasonTextarea.value;
696
-
697
- // Save updated options state
698
- saveConversationState();
699
-
700
- // Make fields readonly again
701
- const fields = card.querySelectorAll('.option-field');
702
- fields.forEach(field => {
703
- field.classList.add('readonly');
704
- const input = field.querySelector('input, textarea');
705
- const readonlyDisplay = field.querySelector('.readonly-display');
706
-
707
- if (input) {
708
- input.setAttribute('readonly', 'readonly');
709
- // For Next Step field, hide input and show readonly display
710
- if (readonlyDisplay) {
711
- input.style.display = 'none';
712
- readonlyDisplay.style.display = 'block';
713
- // Update the clickable category text
714
- const categorySpan = readonlyDisplay.querySelector('.clickable-category');
715
- if (categorySpan) {
716
- categorySpan.textContent = input.value;
717
- categorySpan.setAttribute('onclick', `showCategoryPopup('${input.value.replace(/'/g, "\\'")}', event)`);
718
- }
719
- }
720
- }
721
- });
722
-
723
- // Update card state and hide modal overlay
724
- card.classList.remove('editing');
725
- modalOverlay.classList.remove('active');
726
-
727
- // Update action buttons
728
- const actionsDiv = card.querySelector('.option-actions');
729
- actionsDiv.innerHTML = `
730
- <button class="select-btn" onclick="selectAndEdit(${index})">Select & Edit</button>
731
- `;
732
-
733
- showNotification('Option saved successfully', 'success');
734
- }
735
-
736
- // Cancel edit
737
- function cancelEdit(index) {
738
- // Hide modal overlay
739
- modalOverlay.classList.remove('active');
740
-
741
- // Refresh the card with original data
742
- const card = createOptionCard(currentOptions[index], index);
743
- const oldCard = document.querySelector(`[data-option-index="${index}"]`);
744
- oldCard.parentNode.replaceChild(card, oldCard);
745
- }
746
-
747
-
748
-
749
- // Get data state from category
750
- function getDataStateFromCategory(category) {
751
- const stateMap = {
752
- 'Basic Arithmetic & Mathematical Operations': 'mathematically transformed',
753
- 'Logical & Conditional Operations': 'conditionally filtered',
754
- 'Time Series: Change Detection & Value Comparison': 'change-analyzed',
755
- 'Time Series: Statistical Feature Engineering': 'statistically engineered',
756
- 'Time Series: Ranking, Scaling, and Normalization': 'ranked and normalized',
757
- 'Time Series: Decay, Smoothing, and Turnover Control': 'smoothed and controlled',
758
- 'Time Series: Extremes & Position Identification': 'extreme-identified',
759
- 'Cross-Sectional: Ranking, Scaling, and Normalization': 'cross-sectionally normalized',
760
- 'Cross-Sectional: Regression & Neutralization': 'neutralized',
761
- 'Cross-Sectional: Distributional Transformation & Truncation': 'distributionally transformed',
762
- 'Transformational & Filtering Operations': 'transformed and filtered',
763
- 'Group Aggregation & Statistical Summary': 'aggregated',
764
- 'Group Ranking, Scaling, and Normalization': 'group-normalized',
765
- 'Group Regression & Neutralization': 'group-neutralized',
766
- 'Group Imputation & Backfilling': 'imputed and backfilled'
767
- };
768
-
769
- return stateMap[category] || 'processed';
770
- }
771
-
772
- // Clear Options and Start Over
773
- clearOptionsBtn.addEventListener('click', () => {
774
- if (confirm('Are you sure you want to clear all progress and start over?')) {
775
- // Clear all state
776
- conversationHistory = [];
777
- currentStep = 1;
778
- pipelineSteps = [];
779
- currentOptions = [];
780
- currentDataState = 'raw data';
781
-
782
- // Clear session storage
783
- sessionStorage.removeItem('featureEngConversationHistory');
784
- sessionStorage.removeItem('featureEngCurrentStep');
785
- sessionStorage.removeItem('featureEngPipelineSteps');
786
- sessionStorage.removeItem('featureEngCurrentOptions');
787
- sessionStorage.removeItem('featureEngCurrentDataState');
788
-
789
- // Reset UI
790
- optionsSection.style.display = 'none';
791
- initialSetupSection.style.display = 'block';
792
- questionTemplateInput.value = '';
793
-
794
- // Update pipeline status to reflect cleared state
795
- updatePipelineStatus();
796
-
797
- showNotification('Pipeline cleared. You can start a new conversation.', 'success');
798
- }
799
- });
800
-
801
- // Export pipeline
802
- exportPipelineBtn.addEventListener('click', () => {
803
- const exportData = {
804
- timestamp: new Date().toISOString(),
805
- currentStep: currentStep,
806
- pipelineSteps: pipelineSteps,
807
- currentOptions: currentOptions,
808
- conversationHistory: conversationHistory
809
- };
810
-
811
- const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
812
- const url = URL.createObjectURL(blob);
813
- const a = document.createElement('a');
814
- a.href = url;
815
- a.download = `feature_engineering_pipeline_${new Date().toISOString().split('T')[0]}.json`;
816
- document.body.appendChild(a);
817
- a.click();
818
- document.body.removeChild(a);
819
- URL.revokeObjectURL(url);
820
-
821
- showNotification('Pipeline exported successfully', 'success');
822
- });
823
-
824
- // Send edited option and continue
825
- function sendAndContinue(index) {
826
- const card = document.querySelector(`[data-option-index="${index}"]`);
827
- const contextTextarea = card.querySelector('.option-field:nth-child(1) textarea');
828
- const nextStepInput = card.querySelector('.option-field:nth-child(2) input');
829
- const reasonTextarea = card.querySelector('.option-field:nth-child(3) textarea');
830
-
831
- // Get the edited values
832
- const context = contextTextarea.value;
833
- const chosenStep = nextStepInput.value;
834
- const reason = reasonTextarea.value;
835
-
836
- console.log('=== SEND AND CONTINUE DEBUG ===');
837
- console.log('Selected option index:', index);
838
- console.log('Current option:', currentOptions[index]);
839
- console.log('Context:', context);
840
- console.log('Chosen step:', chosenStep);
841
- console.log('Reason:', reason);
842
- console.log('Current step before update:', currentStep);
843
- console.log('Pipeline steps before update:', pipelineSteps);
844
-
845
- // Hide modal overlay
846
- modalOverlay.classList.remove('active');
847
-
848
- // Add to pipeline steps - Fix: Use currentStep instead of stepNumber from option
849
- pipelineSteps.push(`Step ${currentStep}: ${chosenStep}`);
850
- currentStep = currentStep + 1; // Increment from current step
851
- currentDataState = getDataStateFromCategory(chosenStep);
852
-
853
- console.log('Current step after update:', currentStep);
854
- console.log('Pipeline steps after update:', pipelineSteps);
855
- console.log('Current data state:', currentDataState);
856
-
857
- // Update pipeline status
858
- updatePipelineStatus();
859
-
860
- // Save pipeline state
861
- saveConversationState();
862
-
863
- // Prepare message in the proper format for the AI system prompt
864
-
865
- // Build the previous steps list
866
- const previousStepsText = pipelineSteps.length > 0 ? pipelineSteps.join(', ') : 'None';
867
-
868
- // Get the category description for the chosen step
869
- const categoryData = operatorsData.find(cat => cat.name === chosenStep);
870
- const stepDescription = categoryData ? categoryData.description : 'No description available';
871
-
872
- const userMessage = `
873
-
874
- I Chosen next step: ${chosenStep}
875
- The step description: ${stepDescription}
876
- Reason for choice: ${reason}
877
- based on my choice and info, please recommend me some further options`;
878
-
879
- console.log('=== CONSTRUCTED MESSAGE FOR AI ===');
880
- console.log('User message being sent:', userMessage);
881
- console.log('Current step:', currentStep);
882
- console.log('Previous steps:', previousStepsText);
883
- console.log('Current data state:', currentDataState);
884
- console.log('Step description:', stepDescription);
885
- console.log('Chosen next step:', chosenStep);
886
- console.log('Reason for choice:', reason);
887
- console.log('=================================');
888
-
889
- // Get next recommendations
890
- showLoading('Getting next recommendations...');
891
-
892
- fetch('/feature-engineering/api/continue-conversation', {
893
- method: 'POST',
894
- headers: {
895
- 'X-API-Key': apiKey,
896
- 'Content-Type': 'application/json'
897
- },
898
- body: JSON.stringify({
899
- conversation_history: conversationHistory,
900
- user_message: userMessage,
901
- custom_system_prompt: customSystemPrompt,
902
- provider: modelProvider,
903
- model_name: modelName
904
- })
905
- })
906
- .then(response => response.json())
907
- .then(data => {
908
- console.log('=== SEND & CONTINUE PROMPT ===');
909
- console.log('User message:', userMessage);
910
- console.log('Current step before:', currentStep);
911
- console.log('Pipeline steps before:', pipelineSteps);
912
- console.log('Conversation history sent:', conversationHistory);
913
- console.log('=== AI RESPONSE ===');
914
- console.log('AI response:', data.response);
915
- console.log('==================');
916
-
917
- if (data.success) {
918
- // Add to conversation history
919
- conversationHistory.push({
920
- role: 'user',
921
- content: userMessage
922
- });
923
- conversationHistory.push({
924
- role: 'assistant',
925
- content: data.response
926
- });
927
-
928
- console.log('Updated conversation history:', conversationHistory);
929
-
930
- // Parse the new AI response
931
- parseAIResponse(data.response);
932
-
933
- // Save conversation state
934
- saveConversationState();
935
-
936
- showNotification(`Edited option sent successfully. Next step recommendations loaded.`, 'success');
937
- } else {
938
- showNotification(`Error: ${data.error || 'Unknown error'}`, 'error');
939
- console.error('API Error Details:', data);
940
- }
941
- })
942
- .catch(error => {
943
- showNotification('Error getting next recommendations: ' + error.message, 'error');
944
- console.error('Next Step Error:', error);
945
- })
946
- .finally(() => {
947
- hideLoading();
948
- });
949
- }
950
-
951
- // Make functions global for onclick handlers
952
- window.selectAndEdit = selectAndEdit;
953
- window.saveOption = saveOption;
954
- window.cancelEdit = cancelEdit;
955
- window.sendAndContinue = sendAndContinue;
956
- window.closeSystemPromptModal = closeSystemPromptModal;
957
- window.saveSystemPrompt = saveSystemPrompt;
958
-
959
- // Update pipeline status
960
- function updatePipelineStatus() {
961
- console.log('=== UPDATE PIPELINE STATUS ===');
962
- console.log('Pipeline steps:', pipelineSteps);
963
- console.log('Current step:', currentStep);
964
- console.log('Current data state:', currentDataState);
965
-
966
- if (pipelineSteps.length === 0) {
967
- pipelineStatus.style.display = 'none';
968
- return;
969
- }
970
-
971
- pipelineStatus.style.display = 'block';
972
- pipelineStepsDiv.innerHTML = pipelineSteps.map(step =>
973
- `<div class="pipeline-step"><strong>${step}</strong></div>`
974
- ).join('');
975
-
976
- // Add current status
977
- const statusDiv = document.createElement('div');
978
- statusDiv.className = 'pipeline-step';
979
- statusDiv.style.backgroundColor = '#e8f5e8';
980
- statusDiv.innerHTML = `<strong>Current Step:</strong> ${currentStep} | <strong>Data State:</strong> ${currentDataState}`;
981
- pipelineStepsDiv.appendChild(statusDiv);
982
-
983
- console.log('Pipeline status updated');
984
- console.log('==============================');
985
- }
986
-
987
- // Utility Functions
988
- function showNotification(message, type) {
989
- const notification = document.createElement('div');
990
- notification.className = `notification ${type}`;
991
- notification.textContent = message;
992
- document.body.appendChild(notification);
993
-
994
- setTimeout(() => {
995
- notification.remove();
996
- }, 8000);
997
- }
998
-
999
- let loadingElement = null;
1000
-
1001
- function showLoading(message) {
1002
- loadingElement = document.createElement('div');
1003
- loadingElement.className = 'loading-overlay';
1004
- loadingElement.innerHTML = `
1005
- <div class="loading-spinner"></div>
1006
- <div class="loading-message">${message}</div>
1007
- `;
1008
- document.body.appendChild(loadingElement);
1009
- }
1010
-
1011
- function hideLoading() {
1012
- if (loadingElement) {
1013
- loadingElement.remove();
1014
- loadingElement = null;
1015
- }
1016
- }
1017
-
1018
- // Operators Reference Data
1019
- const operatorsData = [
1020
- {
1021
- id: 1,
1022
- name: "Basic Arithmetic & Mathematical Operations",
1023
- description: "Core mathematical and elementwise operations (e.g., add, subtract, multiply, log, exp, abs, power, etc.)",
1024
- operators: ["add", "subtract", "multiply", "divide", "exp", "log", "abs", "power", "sqrt", "round", "round_down", "floor", "ceiling", "inverse", "negate", "signed_power", "sign", "arc_sin", "arc_cos", "arc_tan", "tanh", "sigmoid", "s_log_1p", "fraction", "max", "min", "densify", "pasteurize", "purify", "to_nan", "nan_out", "replace", "reverse"]
1025
- },
1026
- {
1027
- id: 2,
1028
- name: "Logical & Conditional Operations",
1029
- description: "Boolean logic, comparisons, and conditional branching (e.g., and, or, not, if_else, equal, greater, less, etc.)",
1030
- operators: ["and", "or", "not", "if_else", "equal", "not_equal", "less", "less_equal", "greater", "greater_equal", "is_nan", "is_not_nan", "is_finite", "is_not_finite", "nan_mask"]
1031
- },
1032
- {
1033
- id: 3,
1034
- name: "Time Series: Change Detection & Value Comparison",
1035
- description: "Compare values over time, compute differences, detect changes, or count days since change (e.g., ts_delta, ts_returns, days_from_last_change, last_diff_value, etc.)",
1036
- operators: ["ts_delta", "ts_returns", "days_from_last_change", "last_diff_value", "ts_delta_limit", "ts_backfill"]
1037
- },
1038
- {
1039
- id: 4,
1040
- name: "Time Series: Statistical Feature Engineering",
1041
- description: "Calculate rolling statistical properties over time (e.g., ts_mean, ts_std_dev, ts_skewness, ts_kurtosis, ts_entropy, ts_moment, ts_covariance, ts_corr, ts_co_skewness, ts_co_kurtosis, etc.)",
1042
- operators: ["ts_mean", "ts_std_dev", "ts_skewness", "ts_kurtosis", "ts_entropy", "ts_moment", "ts_covariance", "ts_corr", "ts_partial_corr", "ts_triple_corr", "ts_ir", "ts_sum", "ts_product", "ts_median", "ts_count_nans", "ts_av_diff", "ts_regression", "ts_poly_regression", "ts_vector_neut", "ts_vector_proj", "ts_co_skewness", "ts_co_kurtosis", "ts_theilsen", "ts_zscore", "ts_rank_gmean_amean_diff", "ts_step", "ts_delay", "inst_tvr", "generate_stats"]
1043
- },
1044
- {
1045
- id: 5,
1046
- name: "Time Series: Ranking, Scaling, and Normalization",
1047
- description: "Rank, scale, or normalize time series data within a rolling window (e.g., ts_rank, ts_scale, ts_percentage, ts_quantile, etc.)",
1048
- operators: ["ts_rank", "ts_scale", "ts_percentage", "ts_quantile", "ts_rank_gmean_amean_diff", "ts_zscore"]
1049
- },
1050
- {
1051
- id: 6,
1052
- name: "Time Series: Decay, Smoothing, and Turnover Control",
1053
- description: "Apply decay (linear, exponential, weighted), smoothing, or control turnover in time series (e.g., ts_decay_exp_window, ts_decay_linear, ts_weighted_decay, ts_target_tvr_decay, hump, jump_decay, etc.)",
1054
- operators: ["ts_decay_exp_window", "ts_decay_linear", "ts_weighted_decay", "ts_target_tvr_decay", "hump", "jump_decay", "ts_target_tvr_delta_limit", "ts_target_tvr_hump", "hump_decay"]
1055
- },
1056
- {
1057
- id: 7,
1058
- name: "Time Series: Extremes & Position Identification",
1059
- description: "Identify min/max values, their differences, or the position (index) of extremes within a window (e.g., ts_min, ts_max, ts_min_diff, ts_max_diff, ts_arg_min, ts_arg_max, ts_min_max_diff, etc.)",
1060
- operators: ["ts_min", "ts_max", "ts_min_diff", "ts_max_diff", "ts_arg_min", "ts_arg_max", "ts_min_max_diff", "ts_min_max_cps", "kth_element"]
1061
- },
1062
- {
1063
- id: 8,
1064
- name: "Cross-Sectional: Ranking, Scaling, and Normalization",
1065
- description: "Rank, scale, normalize, or standardize data across instruments at a single time point (e.g., rank, zscore, scale_down, normalize, rank_by_side, etc.)",
1066
- operators: ["rank", "zscore", "scale_down", "scale", "normalize", "rank_by_side", "generalized_rank", "one_side", "rank_gmean_amean_diff"]
1067
- },
1068
- {
1069
- id: 9,
1070
- name: "Cross-Sectional: Regression & Neutralization",
1071
- description: "Remove effects of other variables, perform cross-sectional regression, or orthogonalize one vector with respect to another (e.g., regression_neut, vector_neut, regression_proj, vector_proj, multi_regression, etc.)",
1072
- operators: ["regression_neut", "vector_neut", "regression_proj", "vector_proj", "multi_regression"]
1073
- },
1074
- {
1075
- id: 10,
1076
- name: "Cross-Sectional: Distributional Transformation & Truncation",
1077
- description: "Transform distributions or truncate outliers across instruments (e.g., quantile, winsorize, truncate, bucket, generalized_rank, etc.)",
1078
- operators: ["quantile", "winsorize", "truncate", "bucket", "right_tail", "left_tail", "tail"]
1079
- },
1080
- {
1081
- id: 11,
1082
- name: "Transformational & Filtering Operations",
1083
- description: "General data transformation, filtering, clamping, masking, or conditional value assignment (e.g., filter, clamp, keep, tail, left_tail, right_tail, trade_when, etc.)",
1084
- operators: ["filter", "clamp", "keep", "tail", "left_tail", "right_tail", "trade_when"]
1085
- },
1086
- {
1087
- id: 12,
1088
- name: "Group Aggregation & Statistical Summary",
1089
- description: "Aggregate or summarize (e.g., mean, sum, std, min, max, median) within each group (such as industry, sector, country). Each stock receives the group-level value based on its group membership.",
1090
- operators: ["group_mean", "group_sum", "group_std_dev", "group_min", "group_max", "group_median", "group_count", "group_percentage", "group_extra"]
1091
- },
1092
- {
1093
- id: 13,
1094
- name: "Group Ranking, Scaling, and Normalization",
1095
- description: "Rank, scale, or normalize within each group (e.g., industry rank for each stock, scale values within sector). Each stock is ranked or scaled among its group peers.",
1096
- operators: ["group_rank", "group_scale", "group_zscore", "group_normalize"]
1097
- },
1098
- {
1099
- id: 14,
1100
- name: "Group Regression & Neutralization",
1101
- description: "Remove group-level effects, perform regression, or orthogonalize within each group (e.g., industry-neutralization, group-wise regression). Each group is treated independently.",
1102
- operators: ["group_vector_neut", "group_vector_proj", "group_neutralize", "group_multi_regression"]
1103
- },
1104
- {
1105
- id: 15,
1106
- name: "Group Imputation & Backfilling",
1107
- description: "Impute missing values or backfill using data from other stocks in the same group (e.g., fill NaN with group mean or median, group_backfill).",
1108
- operators: ["group_backfill"]
1109
- }
1110
- ];
1111
-
1112
- // Show category popup
1113
- function showCategoryPopup(categoryName, event) {
1114
- event.stopPropagation();
1115
-
1116
- // Find the category data
1117
- const categoryData = operatorsData.find(cat => cat.name === categoryName);
1118
-
1119
- if (!categoryData) {
1120
- console.log('Category not found:', categoryName);
1121
- return;
1122
- }
1123
-
1124
- // Populate popup content
1125
- categoryPopupTitle.textContent = categoryData.name;
1126
- categoryPopupDescription.textContent = categoryData.description;
1127
- categoryPopupOperatorsTitle.textContent = `Available Operators (${categoryData.operators.length}):`;
1128
-
1129
- const operatorsHtml = categoryData.operators.map(op =>
1130
- `<span class="popup-operator-tag">${op}</span>`
1131
- ).join('');
1132
- categoryPopupOperators.innerHTML = operatorsHtml;
1133
-
1134
- // Position the popup near the clicked element
1135
- const rect = event.target.getBoundingClientRect();
1136
- const popup = categoryPopup;
1137
-
1138
- popup.style.display = 'block';
1139
-
1140
- // Calculate position
1141
- let left = rect.left + window.scrollX;
1142
- let top = rect.bottom + window.scrollY + 5;
1143
-
1144
- // Adjust if popup would go off-screen
1145
- const popupRect = popup.getBoundingClientRect();
1146
- if (left + popupRect.width > window.innerWidth) {
1147
- left = window.innerWidth - popupRect.width - 20;
1148
- }
1149
- if (top + popupRect.height > window.innerHeight + window.scrollY) {
1150
- top = rect.top + window.scrollY - popupRect.height - 5;
1151
- }
1152
-
1153
- popup.style.left = left + 'px';
1154
- popup.style.top = top + 'px';
1155
- }
1156
-
1157
- // Hide category popup
1158
- function hideCategoryPopup() {
1159
- categoryPopup.style.display = 'none';
1160
- }
1161
-
1162
- // Make functions global for onclick handlers
1163
- window.showCategoryPopup = showCategoryPopup;
1164
- window.hideCategoryPopup = hideCategoryPopup;
1165
-
1166
- // Function to save conversation state
1167
- function saveConversationState() {
1168
- sessionStorage.setItem('featureEngConversationHistory', JSON.stringify(conversationHistory));
1169
- sessionStorage.setItem('featureEngCurrentStep', currentStep.toString());
1170
- sessionStorage.setItem('featureEngPipelineSteps', JSON.stringify(pipelineSteps));
1171
- sessionStorage.setItem('featureEngCurrentOptions', JSON.stringify(currentOptions));
1172
- sessionStorage.setItem('featureEngCurrentDataState', currentDataState);
1173
- console.log('Conversation state saved to sessionStorage');
1174
- }
1175
-
1176
- // Operator Suggestions Modal Functions
1177
- let currentOperatorModalIndex = -1;
1178
- let modalEvaluationResults = [];
1179
-
1180
- // Open operator suggestions modal
1181
- function openOperatorSuggestions(index) {
1182
- currentOperatorModalIndex = index;
1183
- const option = currentOptions[index];
1184
-
1185
- // Set the target text from the option's reason instead of context
1186
- let targetText = option.reason || 'No target specified';
1187
-
1188
- // Replace "I used xxxxxxx operator in this step, in order to " with "I want to"
1189
- targetText = targetText.replace(/I used .*? operator in this step, in order to /gi, 'I want to ');
1190
-
1191
- document.getElementById('modalTargetText').textContent = targetText;
1192
-
1193
- // Clear previous results
1194
- document.getElementById('modalCurrentExpression').value = '';
1195
- document.getElementById('modalEvaluationSection').style.display = 'none';
1196
- document.getElementById('modalProgressSection').style.display = 'none';
1197
- document.getElementById('modalExportResults').style.display = 'none';
1198
-
1199
- // Check BRAIN connection status
1200
- const sessionId = localStorage.getItem('brain_session_id');
1201
- const storedOperators = sessionStorage.getItem('brainOperators');
1202
-
1203
- if (!sessionId) {
1204
- // No BRAIN session, show connection notice
1205
- document.getElementById('modalBrainNotice').style.display = 'block';
1206
- } else if (!storedOperators) {
1207
- // Has session but no operators, try to load them
1208
- loadOperatorsFromBRAIN().then(operators => {
1209
- if (operators.length > 0) {
1210
- document.getElementById('modalBrainNotice').style.display = 'none';
1211
- } else {
1212
- document.getElementById('modalBrainNotice').style.display = 'block';
1213
- }
1214
- });
1215
- } else {
1216
- // Has operators, hide notice
1217
- document.getElementById('modalBrainNotice').style.display = 'none';
1218
- }
1219
-
1220
- // Show the modal
1221
- document.getElementById('operatorSuggestionsModal').style.display = 'block';
1222
-
1223
- // Add event listeners for modal controls
1224
- setupModalEventListeners();
1225
-
1226
- // Add click-outside-to-close functionality
1227
- const modal = document.getElementById('operatorSuggestionsModal');
1228
- modal.addEventListener('click', (e) => {
1229
- if (e.target === modal) {
1230
- closeOperatorSuggestionsModal();
1231
- }
1232
- });
1233
- }
1234
-
1235
- // Setup modal event listeners
1236
- function setupModalEventListeners() {
1237
- // Remove existing listeners to prevent duplicates
1238
- const startBtn = document.getElementById('modalStartEvaluation');
1239
- const clearBtn = document.getElementById('modalClearExpression');
1240
- const minScoreFilter = document.getElementById('modalMinScoreFilter');
1241
- const showHighScores = document.getElementById('modalShowHighScores');
1242
- const showMediumScores = document.getElementById('modalShowMediumScores');
1243
- const showLowScores = document.getElementById('modalShowLowScores');
1244
- const exportBtn = document.getElementById('modalExportResults');
1245
- const editTargetBtn = document.getElementById('modalEditTarget');
1246
- const saveTargetBtn = document.getElementById('modalSaveTarget');
1247
- const cancelTargetBtn = document.getElementById('modalCancelTarget');
1248
-
1249
- // Clone elements to remove old listeners
1250
- startBtn.replaceWith(startBtn.cloneNode(true));
1251
- clearBtn.replaceWith(clearBtn.cloneNode(true));
1252
- minScoreFilter.replaceWith(minScoreFilter.cloneNode(true));
1253
- showHighScores.replaceWith(showHighScores.cloneNode(true));
1254
- showMediumScores.replaceWith(showMediumScores.cloneNode(true));
1255
- showLowScores.replaceWith(showLowScores.cloneNode(true));
1256
- exportBtn.replaceWith(exportBtn.cloneNode(true));
1257
- editTargetBtn.replaceWith(editTargetBtn.cloneNode(true));
1258
- saveTargetBtn.replaceWith(saveTargetBtn.cloneNode(true));
1259
- cancelTargetBtn.replaceWith(cancelTargetBtn.cloneNode(true));
1260
-
1261
- // Get fresh references
1262
- const newStartBtn = document.getElementById('modalStartEvaluation');
1263
- const newClearBtn = document.getElementById('modalClearExpression');
1264
- const newMinScoreFilter = document.getElementById('modalMinScoreFilter');
1265
- const newShowHighScores = document.getElementById('modalShowHighScores');
1266
- const newShowMediumScores = document.getElementById('modalShowMediumScores');
1267
- const newShowLowScores = document.getElementById('modalShowLowScores');
1268
- const newExportBtn = document.getElementById('modalExportResults');
1269
- const newEditTargetBtn = document.getElementById('modalEditTarget');
1270
- const newSaveTargetBtn = document.getElementById('modalSaveTarget');
1271
- const newCancelTargetBtn = document.getElementById('modalCancelTarget');
1272
-
1273
- // Add new listeners
1274
- newStartBtn.addEventListener('click', startModalEvaluation);
1275
-
1276
- newClearBtn.addEventListener('click', () => {
1277
- document.getElementById('modalCurrentExpression').value = '';
1278
- });
1279
-
1280
- newMinScoreFilter.addEventListener('input', (e) => {
1281
- document.getElementById('modalMinScoreValue').textContent = e.target.value;
1282
- filterModalResults();
1283
- });
1284
-
1285
- newShowHighScores.addEventListener('change', filterModalResults);
1286
- newShowMediumScores.addEventListener('change', filterModalResults);
1287
- newShowLowScores.addEventListener('change', filterModalResults);
1288
-
1289
- newExportBtn.addEventListener('click', exportModalResults);
1290
-
1291
- // Target editing listeners
1292
- newEditTargetBtn.addEventListener('click', () => {
1293
- const targetText = document.getElementById('modalTargetText').textContent;
1294
- document.getElementById('modalTargetInput').value = targetText;
1295
- document.getElementById('modalTargetDisplay').style.display = 'none';
1296
- document.getElementById('modalTargetInputGroup').style.display = 'block';
1297
- newEditTargetBtn.style.display = 'none';
1298
- });
1299
-
1300
- newSaveTargetBtn.addEventListener('click', () => {
1301
- const newTarget = document.getElementById('modalTargetInput').value.trim();
1302
- if (newTarget) {
1303
- document.getElementById('modalTargetText').textContent = newTarget;
1304
- document.getElementById('modalTargetDisplay').style.display = 'block';
1305
- document.getElementById('modalTargetInputGroup').style.display = 'none';
1306
- newEditTargetBtn.style.display = 'inline-block';
1307
- showNotification('Target updated successfully', 'success');
1308
- } else {
1309
- showNotification('Please enter a target description', 'error');
1310
- }
1311
- });
1312
-
1313
- newCancelTargetBtn.addEventListener('click', () => {
1314
- document.getElementById('modalTargetDisplay').style.display = 'block';
1315
- document.getElementById('modalTargetInputGroup').style.display = 'none';
1316
- newEditTargetBtn.style.display = 'inline-block';
1317
- });
1318
- }
1319
-
1320
- // Start operator evaluation in modal
1321
- async function startModalEvaluation() {
1322
- const expression = document.getElementById('modalCurrentExpression').value.trim();
1323
- const target = document.getElementById('modalTargetText').textContent;
1324
-
1325
- if (!expression) {
1326
- showNotification('Please enter an expression to evaluate', 'error');
1327
- return;
1328
- }
1329
-
1330
- if (!apiKey) {
1331
- showNotification('Please configure your API key first', 'error');
1332
- return;
1333
- }
1334
-
1335
- // Get operators list
1336
- let operators = [];
1337
- const storedOperators = sessionStorage.getItem('brainOperators');
1338
-
1339
- if (storedOperators) {
1340
- try {
1341
- operators = JSON.parse(storedOperators);
1342
- } catch (error) {
1343
- console.error('Error parsing stored operators:', error);
1344
- }
1345
- }
1346
-
1347
- // If no operators from BRAIN, ask user for choice
1348
- if (operators.length === 0) {
1349
- const userChoice = confirm(
1350
- 'No BRAIN operators available. Would you like to:\n\n' +
1351
- '• Click "OK" to connect to BRAIN and get 400+ operators\n' +
1352
- '• Click "Cancel" to use fallback operators (40 operators)'
1353
- );
1354
-
1355
- if (userChoice) {
1356
- // User wants to connect to BRAIN
1357
- closeOperatorSuggestionsModal();
1358
- openBrainLoginModal();
1359
- return;
1360
- } else {
1361
- // User chooses fallback operators
1362
- operators = getFallbackOperators();
1363
- showNotification('Using fallback operator list (40 operators).', 'info');
1364
- document.getElementById('modalBrainNotice').style.display = 'block';
1365
- }
1366
- } else {
1367
- // Hide BRAIN connection notice if operators are available
1368
- document.getElementById('modalBrainNotice').style.display = 'none';
1369
- }
1370
-
1371
- try {
1372
- // Show progress section
1373
- document.getElementById('modalProgressSection').style.display = 'block';
1374
- document.getElementById('modalEvaluationSection').style.display = 'none';
1375
-
1376
- // Get operators from inspiration house API
1377
- const response = await fetch('/inspiration-house/api/batch-evaluate', {
1378
- method: 'POST',
1379
- headers: {
1380
- 'X-API-Key': apiKey,
1381
- 'Content-Type': 'application/json'
1382
- },
1383
- body: JSON.stringify({
1384
- operators: operators,
1385
- research_target: target,
1386
- current_expression: expression,
1387
- expression_context: `Feature engineering step ${currentStep}: ${currentOptions[currentOperatorModalIndex].nextStep}`,
1388
- provider: modelProvider,
1389
- model_name: modelName,
1390
- batch_size: 100
1391
- })
1392
- });
1393
-
1394
- const data = await response.json();
1395
-
1396
- if (response.ok && data.success) {
1397
- modalEvaluationResults = data.results || [];
1398
- displayModalResults();
1399
- showNotification(`Evaluated ${modalEvaluationResults.length} operators`, 'success');
1400
- } else {
1401
- showNotification(`Evaluation failed: ${data.error || 'Unknown error'}`, 'error');
1402
- }
1403
- } catch (error) {
1404
- showNotification('Error evaluating operators: ' + error.message, 'error');
1405
- console.error('Modal evaluation error:', error);
1406
- } finally {
1407
- document.getElementById('modalProgressSection').style.display = 'none';
1408
- }
1409
- }
1410
-
1411
- // Display modal results
1412
- function displayModalResults() {
1413
- const tableBody = document.getElementById('modalEvaluationTableBody');
1414
- const summaryStats = document.getElementById('modalSummaryStats');
1415
-
1416
- if (modalEvaluationResults.length === 0) {
1417
- tableBody.innerHTML = `
1418
- <tr>
1419
- <td colspan="3" style="text-align: center; color: #7f8c8d; font-style: italic; padding: 20px;">
1420
- No operators found. Try a different expression.
1421
- </td>
1422
- </tr>
1423
- `;
1424
- summaryStats.style.display = 'none';
1425
- return;
1426
- }
1427
-
1428
- // Calculate summary stats
1429
- const highScores = modalEvaluationResults.filter(r => (r.score || 0) >= 8).length;
1430
- const mediumScores = modalEvaluationResults.filter(r => (r.score || 0) >= 4 && (r.score || 0) < 8).length;
1431
- const lowScores = modalEvaluationResults.filter(r => (r.score || 0) < 4).length;
1432
-
1433
- document.getElementById('modalHighScoreCount').textContent = highScores;
1434
- document.getElementById('modalMediumScoreCount').textContent = mediumScores;
1435
- document.getElementById('modalLowScoreCount').textContent = lowScores;
1436
- summaryStats.style.display = 'grid';
1437
-
1438
- // Display results
1439
- filterModalResults();
1440
-
1441
- // Show action buttons
1442
- document.getElementById('modalExportResults').style.display = 'inline-block';
1443
-
1444
- // Show evaluation section
1445
- document.getElementById('modalEvaluationSection').style.display = 'block';
1446
- }
1447
-
1448
- // Filter modal results based on score and checkboxes
1449
- function filterModalResults() {
1450
- const minScore = parseInt(document.getElementById('modalMinScoreFilter').value);
1451
- const showHigh = document.getElementById('modalShowHighScores').checked;
1452
- const showMedium = document.getElementById('modalShowMediumScores').checked;
1453
- const showLow = document.getElementById('modalShowLowScores').checked;
1454
-
1455
- const filteredResults = modalEvaluationResults.filter(result => {
1456
- const score = result.score || 0;
1457
- if (score < minScore) return false;
1458
-
1459
- if (score >= 8 && !showHigh) return false;
1460
- if (score >= 4 && score < 8 && !showMedium) return false;
1461
- if (score < 4 && !showLow) return false;
1462
-
1463
- return true;
1464
- });
1465
-
1466
- const tableBody = document.getElementById('modalEvaluationTableBody');
1467
-
1468
- if (filteredResults.length === 0) {
1469
- tableBody.innerHTML = `
1470
- <tr>
1471
- <td colspan="3" style="text-align: center; color: #7f8c8d; font-style: italic; padding: 20px;">
1472
- No operators match the current filters.
1473
- </td>
1474
- </tr>
1475
- `;
1476
- return;
1477
- }
1478
-
1479
- // Get operators list from sessionStorage (like inspiration_house.js does)
1480
- let operatorsList = [];
1481
- const storedOperators = sessionStorage.getItem('brainOperators');
1482
- if (storedOperators) {
1483
- try {
1484
- operatorsList = JSON.parse(storedOperators);
1485
- } catch (error) {
1486
- console.error('Error parsing stored operators:', error);
1487
- }
1488
- }
1489
-
1490
- tableBody.innerHTML = filteredResults.map(result => {
1491
- const operatorName = result.operator_name || result.operator || 'Unknown';
1492
- const category = result.category || 'Unknown';
1493
- const reason = result.reason || '';
1494
- const score = result.score || 0;
1495
-
1496
- // Find the operator details from the operators list (like inspiration_house.js)
1497
- const operatorDetails = operatorsList.find(op => op.name === operatorName);
1498
- const description = operatorDetails ? operatorDetails.description || '' : '';
1499
- const definition = operatorDetails ? operatorDetails.definition || '' : '';
1500
-
1501
- return `
1502
- <tr>
1503
- <td style="border: 1px solid #dee2e6; padding: 8px; vertical-align: top;">
1504
- <div class="operator-name">${operatorName}</div>
1505
- <div class="operator-category">${category}</div>
1506
- ${description ? `<div class="operator-description">${convertMarkdownToHTML(description)}</div>` : ''}
1507
- ${definition ? `<div class="operator-definition"><strong>Definition:</strong> ${convertMarkdownToHTML(definition)}</div>` : ''}
1508
- </td>
1509
- <td class="reason-text" style="border: 1px solid #dee2e6; padding: 8px; vertical-align: top;">${convertMarkdownToHTML(reason)}</td>
1510
- <td class="score-cell score-${score >= 8 ? 'high' : score >= 4 ? 'medium' : 'low'}" style="border: 1px solid #dee2e6; padding: 8px; text-align: center; vertical-align: top;">${score}</td>
1511
- </tr>
1512
- `;
1513
- }).join('');
1514
- }
1515
-
1516
- // Copy operator to clipboard
1517
- function copyOperatorToClipboard(operatorName) {
1518
- navigator.clipboard.writeText(operatorName).then(() => {
1519
- showNotification(`Copied "${operatorName}" to clipboard`, 'success');
1520
- }).catch(() => {
1521
- showNotification('Failed to copy to clipboard', 'error');
1522
- });
1523
- }
1524
-
1525
- // Export modal results
1526
- function exportModalResults() {
1527
- const data = modalEvaluationResults.map(result => ({
1528
- operator: result.operator_name,
1529
- category: result.category,
1530
- score: result.score,
1531
- reason: result.reason,
1532
- description: result.description
1533
- }));
1534
-
1535
- const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
1536
- const url = URL.createObjectURL(blob);
1537
- const a = document.createElement('a');
1538
- a.href = url;
1539
- a.download = `operator_suggestions_step_${currentStep}.json`;
1540
- a.click();
1541
- URL.revokeObjectURL(url);
1542
-
1543
- showNotification('Results exported successfully', 'success');
1544
- }
1545
-
1546
-
1547
-
1548
- // Close operator suggestions modal
1549
- function closeOperatorSuggestionsModal() {
1550
- document.getElementById('operatorSuggestionsModal').style.display = 'none';
1551
- currentOperatorModalIndex = -1;
1552
- modalEvaluationResults = [];
1553
- }
1554
-
1555
- // Convert markdown to HTML for better display (like inspiration_house.js)
1556
- function convertMarkdownToHTML(text) {
1557
- if (!text) return '';
1558
-
1559
- return text
1560
- // Bold text: **text** -> <strong>text</strong>
1561
- .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
1562
- // Italic text: *text* -> <em>text</em>
1563
- .replace(/\*(.*?)\*/g, '<em>$1</em>')
1564
- // Code: `text` -> <code>text</code>
1565
- .replace(/`(.*?)`/g, '<code>$1</code>')
1566
- // Headers: ### text -> <h4>text</h4>
1567
- .replace(/^### (.+)$/gm, '<h4>$1</h4>')
1568
- .replace(/^## (.+)$/gm, '<h3>$1</h3>')
1569
- .replace(/^# (.+)$/gm, '<h2>$1</h2>')
1570
- // Lists: - item -> <li>item</li>
1571
- .replace(/^- (.+)$/gm, '<li>$1</li>')
1572
- // Numbered lists: 1. item -> <li>item</li>
1573
- .replace(/^(\d+)\. (.+)$/gm, '<li>$2</li>')
1574
- // Line breaks: \n -> <br>
1575
- .replace(/\n/g, '<br>')
1576
- // Escape HTML characters
1577
- .replace(/&/g, '&amp;')
1578
- .replace(/</g, '&lt;')
1579
- .replace(/>/g, '&gt;')
1580
- // Restore our HTML tags
1581
- .replace(/&lt;strong&gt;/g, '<strong>')
1582
- .replace(/&lt;\/strong&gt;/g, '</strong>')
1583
- .replace(/&lt;em&gt;/g, '<em>')
1584
- .replace(/&lt;\/em&gt;/g, '</em>')
1585
- .replace(/&lt;code&gt;/g, '<code>')
1586
- .replace(/&lt;\/code&gt;/g, '</code>')
1587
- .replace(/&lt;br&gt;/g, '<br>')
1588
- .replace(/&lt;h[234]&gt;/g, '<$1>')
1589
- .replace(/&lt;\/h[234]&gt;/g, '</$1>')
1590
- .replace(/&lt;li&gt;/g, '<li>')
1591
- .replace(/&lt;\/li&gt;/g, '</li>');
1592
- }
1593
-
1594
- // Get fallback operators list (when BRAIN is not connected)
1595
- function getFallbackOperators() {
1596
- return [
1597
- // Basic Arithmetic & Mathematical Operations
1598
- { name: 'add', category: 'Basic Arithmetic & Mathematical Operations', description: 'Add two values' },
1599
- { name: 'subtract', category: 'Basic Arithmetic & Mathematical Operations', description: 'Subtract two values' },
1600
- { name: 'multiply', category: 'Basic Arithmetic & Mathematical Operations', description: 'Multiply two values' },
1601
- { name: 'divide', category: 'Basic Arithmetic & Mathematical Operations', description: 'Divide two values' },
1602
- { name: 'exp', category: 'Basic Arithmetic & Mathematical Operations', description: 'Exponential function' },
1603
- { name: 'log', category: 'Basic Arithmetic & Mathematical Operations', description: 'Natural logarithm' },
1604
- { name: 'abs', category: 'Basic Arithmetic & Mathematical Operations', description: 'Absolute value' },
1605
- { name: 'power', category: 'Basic Arithmetic & Mathematical Operations', description: 'Raise to power' },
1606
- { name: 'sqrt', category: 'Basic Arithmetic & Mathematical Operations', description: 'Square root' },
1607
-
1608
- // Time Series Operations
1609
- { name: 'ts_mean', category: 'Time Series: Statistical Feature Engineering', description: 'Rolling mean over time' },
1610
- { name: 'ts_std_dev', category: 'Time Series: Statistical Feature Engineering', description: 'Rolling standard deviation' },
1611
- { name: 'ts_rank', category: 'Time Series: Ranking, Scaling, and Normalization', description: 'Rolling rank over time' },
1612
- { name: 'ts_scale', category: 'Time Series: Ranking, Scaling, and Normalization', description: 'Rolling scaling over time' },
1613
- { name: 'ts_delta', category: 'Time Series: Change Detection & Value Comparison', description: 'Time series difference' },
1614
- { name: 'ts_returns', category: 'Time Series: Change Detection & Value Comparison', description: 'Time series returns' },
1615
- { name: 'ts_min', category: 'Time Series: Extremes & Position Identification', description: 'Rolling minimum' },
1616
- { name: 'ts_max', category: 'Time Series: Extremes & Position Identification', description: 'Rolling maximum' },
1617
- { name: 'ts_decay_exp_window', category: 'Time Series: Decay, Smoothing, and Turnover Control', description: 'Exponential decay' },
1618
-
1619
- // Cross-Sectional Operations
1620
- { name: 'rank', category: 'Cross-Sectional: Ranking, Scaling, and Normalization', description: 'Cross-sectional rank' },
1621
- { name: 'zscore', category: 'Cross-Sectional: Ranking, Scaling, and Normalization', description: 'Cross-sectional z-score' },
1622
- { name: 'scale', category: 'Cross-Sectional: Ranking, Scaling, and Normalization', description: 'Cross-sectional scaling' },
1623
- { name: 'normalize', category: 'Cross-Sectional: Ranking, Scaling, and Normalization', description: 'Cross-sectional normalization' },
1624
- { name: 'regression_neut', category: 'Cross-Sectional: Regression & Neutralization', description: 'Regression neutralization' },
1625
- { name: 'vector_neut', category: 'Cross-Sectional: Regression & Neutralization', description: 'Vector neutralization' },
1626
- { name: 'quantile', category: 'Cross-Sectional: Distributional Transformation & Truncation', description: 'Quantile transformation' },
1627
- { name: 'winsorize', category: 'Cross-Sectional: Distributional Transformation & Truncation', description: 'Winsorize outliers' },
1628
-
1629
- // Logical & Conditional Operations
1630
- { name: 'if_else', category: 'Logical & Conditional Operations', description: 'Conditional value assignment' },
1631
- { name: 'equal', category: 'Logical & Conditional Operations', description: 'Equality comparison' },
1632
- { name: 'greater', category: 'Logical & Conditional Operations', description: 'Greater than comparison' },
1633
- { name: 'less', category: 'Logical & Conditional Operations', description: 'Less than comparison' },
1634
-
1635
- // Group Operations
1636
- { name: 'group_mean', category: 'Group Aggregation & Statistical Summary', description: 'Group mean aggregation' },
1637
- { name: 'group_rank', category: 'Group Ranking, Scaling, and Normalization', description: 'Group ranking' },
1638
- { name: 'group_scale', category: 'Group Ranking, Scaling, and Normalization', description: 'Group scaling' },
1639
- { name: 'group_vector_neut', category: 'Group Regression & Neutralization', description: 'Group vector neutralization' },
1640
-
1641
- // Transformational & Filtering Operations
1642
- { name: 'filter', category: 'Transformational & Filtering Operations', description: 'Filter data' },
1643
- { name: 'clamp', category: 'Transformational & Filtering Operations', description: 'Clamp values to range' },
1644
- { name: 'keep', category: 'Transformational & Filtering Operations', description: 'Keep specific values' }
1645
- ];
1646
- }
1647
-
1648
- // Function to refresh operators after BRAIN connection
1649
- function refreshOperatorsAfterBrainLogin() {
1650
- const storedOperators = sessionStorage.getItem('brainOperators');
1651
- if (storedOperators) {
1652
- try {
1653
- const operators = JSON.parse(storedOperators);
1654
- if (operators.length > 0) {
1655
- // Hide BRAIN connection notice
1656
- document.getElementById('modalBrainNotice').style.display = 'none';
1657
- showNotification(`Successfully loaded ${operators.length} operators from BRAIN`, 'success');
1658
-
1659
- // Reopen the operator suggestions modal with BRAIN operators
1660
- setTimeout(() => {
1661
- openOperatorSuggestions(currentOperatorModalIndex);
1662
- }, 1000);
1663
- }
1664
- } catch (error) {
1665
- console.error('Error parsing stored operators:', error);
1666
- }
1667
- }
1668
- }
1669
-
1670
- // Override the original authenticateBrain function to refresh operators
1671
- const originalAuthenticateBrain = window.authenticateBrain;
1672
- window.authenticateBrain = async function() {
1673
- if (originalAuthenticateBrain) {
1674
- await originalAuthenticateBrain();
1675
- // Load operators after successful authentication
1676
- setTimeout(async () => {
1677
- const operators = await loadOperatorsFromBRAIN();
1678
- if (operators.length > 0) {
1679
- refreshOperatorsAfterBrainLogin();
1680
- }
1681
- }, 2000);
1682
- }
1683
- };
1684
-
1685
- // Function to load operators from BRAIN (similar to inspiration_house.js)
1686
- async function loadOperatorsFromBRAIN() {
1687
- try {
1688
- // Get session ID from localStorage
1689
- const sessionId = localStorage.getItem('brain_session_id');
1690
- if (!sessionId) {
1691
- showNotification('Please connect to BRAIN first to load operators', 'warning');
1692
- return [];
1693
- }
1694
-
1695
- const response = await fetch('/api/operators', {
1696
- headers: {
1697
- 'Session-ID': sessionId
1698
- }
1699
- });
1700
- const data = await response.json();
1701
-
1702
- if (response.ok && Array.isArray(data)) {
1703
- const operators = data;
1704
- sessionStorage.setItem('brainOperators', JSON.stringify(operators));
1705
- console.log(`Loaded ${operators.length} operators from BRAIN`);
1706
- showNotification(`Loaded ${operators.length} operators from BRAIN`, 'success');
1707
- return operators;
1708
- } else {
1709
- console.error('Failed to load operators:', data.error);
1710
- if (data.error && data.error.includes('Invalid or expired session')) {
1711
- showNotification('Please connect to BRAIN first to load operators', 'warning');
1712
- } else {
1713
- showNotification('Failed to load operators from BRAIN', 'error');
1714
- }
1715
- return [];
1716
- }
1717
- } catch (error) {
1718
- console.error('Error loading operators:', error);
1719
- showNotification('Error connecting to BRAIN API', 'error');
1720
- return [];
1721
- }
1722
- }
1723
-
1724
- // Make functions global for onclick handlers
1725
- window.openOperatorSuggestions = openOperatorSuggestions;
1726
- window.closeOperatorSuggestionsModal = closeOperatorSuggestionsModal;
1727
- window.copyOperatorToClipboard = copyOperatorToClipboard;
1728
- window.openBrainLoginModal = openBrainLoginModal;
1729
- window.closeBrainLoginModal = closeBrainLoginModal;