You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
519 lines
19 KiB
519 lines
19 KiB
# -*- coding: utf-8 -*-
|
|
import uuid
|
|
import sys
|
|
import httpx
|
|
from odoo import models, fields
|
|
|
|
|
|
class AlphaGenerateResearchDirection(models.Model):
|
|
_name = 'alpha.generate.research.direction'
|
|
_description = 'Alpha Generate Research Direction'
|
|
_order = 'id desc'
|
|
|
|
name = fields.Char(string='Name', required=True, default=lambda self: str(uuid.uuid4()))
|
|
|
|
status = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('generated_research', 'Generated Research'),
|
|
('generated_feature', 'Generated Feature'),
|
|
('done', 'Done'),
|
|
('failed', 'Failed'),
|
|
('cancel', 'Cancel')
|
|
], string='Status', default='draft')
|
|
|
|
region = fields.Many2one('alpha.region.settings', string='Region', required=True)
|
|
|
|
universe = fields.Many2one('alpha.universe.settings', string='Universe', required=True)
|
|
|
|
research_direction_prompt = fields.Many2one('alpha.prompt.settings', string='Research Direction Prompt')
|
|
|
|
feature_engineering_prompt = fields.Many2one('alpha.prompt.settings', string='Feature Engineering Prompt') # 用于加上替换的研究方向, 组合新的工程特征提示词
|
|
|
|
results = fields.Text(string='Results')
|
|
|
|
message = fields.Char(string='Message')
|
|
|
|
llm_settings_line_id = fields.Many2one('llm.settings.line', string='Model')
|
|
|
|
models_name = fields.Char(string='Model Name', related='llm_settings_line_id.model_name')
|
|
|
|
provider_name = fields.Char(string='Provider Name', related='llm_settings_line_id.llm_setting_id.name')
|
|
|
|
research_direction = fields.Text(string='Research Direction')
|
|
|
|
prepare_feature_engineering_prompt = fields.Text(string='Feature Engineering Prompt') # 用于已组合完成, 准备生成工程特征的提示词
|
|
|
|
feature_engineering = fields.Text(string='Feature Engineering')
|
|
|
|
use_datasets = fields.Many2one('alpha.datasets', string='Use Datasets')
|
|
|
|
datasets_line_ids = fields.One2many('alpha.research.datasets.line', 'research_direction_id', string='Datasets Lines')
|
|
|
|
def _get_callback_url(self):
|
|
"""根据平台获取回调 URL"""
|
|
platform_info = sys.platform
|
|
if platform_info == "darwin":
|
|
# Mac 开发环境使用本地地址
|
|
base_url_odoo = self.env['ir.config_parameter'].sudo().get_param('web.base.url.local', 'http://127.0.0.1:8069')
|
|
else:
|
|
base_url_odoo = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
|
return f"{base_url_odoo}/api/alpha-generate-research-direction/result"
|
|
|
|
def btn_generate_research(self):
|
|
"""
|
|
生成研究方向按钮
|
|
通过选择的基础提示词,推送到 LLM 微服务生成研究方向
|
|
post 无需等待,LLM 会回写结果到 research_direction 字段
|
|
成功后状态变为 generated_research,失败不修改状态,失败信息写到 message
|
|
"""
|
|
self.ensure_one()
|
|
|
|
# 如果research_direction字段已存在, 则跳过不生成
|
|
if self.research_direction:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Research direction already exists, please delete it first.',
|
|
'sticky': False,
|
|
},
|
|
}
|
|
|
|
# 校验必填字段
|
|
if not self.research_direction_prompt:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Please select a research direction prompt template.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
if not self.llm_settings_line_id:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Please select a model.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
model_name = self.llm_settings_line_id.model_name
|
|
base_url = self.llm_settings_line_id.llm_setting_id.base_url
|
|
api_key = self.llm_settings_line_id.llm_setting_id.api_key
|
|
|
|
final_prompt = self.research_direction_prompt.prompt
|
|
|
|
ms_config = self.get_ms_config()
|
|
ms_url = ms_config.get('url')
|
|
|
|
callback_url = self._get_callback_url()
|
|
|
|
post_data = {
|
|
"record_id": self.id,
|
|
"odoo_model": self._name,
|
|
"action_type": "research_direction",
|
|
"prompt": final_prompt,
|
|
"model_name": model_name,
|
|
"base_url": base_url,
|
|
"api_key": api_key,
|
|
"callback_url": callback_url
|
|
}
|
|
|
|
if not ms_url:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Microservice URL is not available.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
# 发送请求,超短超时,不等待响应
|
|
try:
|
|
httpx.post(f'{ms_url}:32004/api_llm_generate', json=post_data, timeout=0.001)
|
|
except httpx.TimeoutException:
|
|
pass
|
|
except Exception as e:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': f'Failed to send microservices: {e}',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
self.write({
|
|
'status': 'draft',
|
|
'message': 'Request sent to microservice, waiting for research direction...'
|
|
})
|
|
|
|
def btn_generate_prepare_feature_engineering_prompt(self):
|
|
# 手动填入生成好的研究方向, 跳过第一步的 llm 生成
|
|
self.ensure_one()
|
|
|
|
# 如果没有研究方向, 需要生成
|
|
if not self.research_direction or not self.feature_engineering_prompt:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Need to generate research direction and need select feature_engineering_prompt',
|
|
'sticky': False,
|
|
},
|
|
}
|
|
|
|
# 将已有的研究方向, 和基础的feature_engineering_prompt组合
|
|
# feature_engineering_prompt + research_direction
|
|
feature_engineering_prompt = self.feature_engineering_prompt.prompt
|
|
research_direction = self.research_direction
|
|
|
|
replaced_feature_engineering_prompt = feature_engineering_prompt.replace('[#replace#]', research_direction)
|
|
|
|
self.write({
|
|
'status': 'generated_research',
|
|
'prepare_feature_engineering_prompt': replaced_feature_engineering_prompt
|
|
})
|
|
|
|
return True
|
|
|
|
def btn_generate_feature(self):
|
|
"""
|
|
生成特征工程按钮
|
|
通过 feature_engineering_prompt 作为提示词,推送到 LLM 微服务生成特征工程
|
|
post 无需等待,LLM 会回写结果到 feature_engineering 字段
|
|
成功后状态变为 done,失败状态变为 failed,失败信息写到 message
|
|
"""
|
|
self.ensure_one()
|
|
|
|
# 如果未生成prepare_feature_engineering_prompt, 则不允许使用此按钮
|
|
if not self.prepare_feature_engineering_prompt:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Please generate prepare_feature_engineering_prompt first',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
# 如果feature_engineering字段不为空, 则提示需要清空才可以使用这个按钮, 避免重复生成
|
|
if self.feature_engineering:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Please clear feature_engineering field first',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
# 校验状态
|
|
if self.status != 'generated_research':
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Please generate research direction first.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
# 校验必填字段
|
|
if not self.research_direction:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Research direction is empty, please generate it first.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
if not self.llm_settings_line_id:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Please select a model.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
if not self.feature_engineering_prompt:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Please select a feature engineering prompt template.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
model_name = self.llm_settings_line_id.model_name
|
|
base_url = self.llm_settings_line_id.llm_setting_id.base_url
|
|
api_key = self.llm_settings_line_id.llm_setting_id.api_key
|
|
|
|
# 使用 feature_engineering_prompt 作为提示词
|
|
final_prompt = self.feature_engineering_prompt.prompt
|
|
|
|
ms_config = self.get_ms_config()
|
|
ms_url = ms_config.get('url')
|
|
|
|
callback_url = self._get_callback_url()
|
|
|
|
post_data = {
|
|
"record_id": self.id,
|
|
"odoo_model": self._name,
|
|
"action_type": "feature_engineering",
|
|
"prompt": final_prompt,
|
|
"model_name": model_name,
|
|
"base_url": base_url,
|
|
"api_key": api_key,
|
|
"callback_url": callback_url
|
|
}
|
|
|
|
if not ms_url:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Microservice URL is not available.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
# 发送请求,超短超时,不等待响应
|
|
try:
|
|
httpx.post(f'{ms_url}:32004/api_llm_generate', json=post_data, timeout=0.001)
|
|
except httpx.TimeoutException:
|
|
pass
|
|
except Exception as e:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Failed to send microservice.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
self.write({
|
|
'status': 'generated_research',
|
|
'message': 'Request sent to microservice, waiting for feature engineering...'
|
|
})
|
|
|
|
def get_ms_config(self):
|
|
# TODO 先从 nacos 获取微服务 url
|
|
nacos_url = ''
|
|
|
|
platform_info = sys.platform
|
|
if platform_info == "darwin":
|
|
nacos_url = 'http://192.168.31.41:30848/nacos/v1/cs/configs?dataId=microservices_dev&group=quantify'
|
|
elif platform_info.startswith("linux"):
|
|
nacos_url = 'http://192.168.31.41:30848/nacos/v1/cs/configs?dataId=microservices&group=quantify'
|
|
|
|
try:
|
|
ms_config_resp = httpx.get(nacos_url)
|
|
ms_config_resp.raise_for_status()
|
|
except Exception as e:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'Nacos request failed.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
ms_config = ms_config_resp.json()
|
|
|
|
return ms_config
|
|
|
|
def btn_generate_idea(self):
|
|
# 最后一步, 当工程特征已经生成, 并且已选择数据字段, 则生成idea
|
|
# 同一份特征工程, 多个数据字段, 生成与数据字段相同数量的 idea
|
|
self.ensure_one()
|
|
|
|
# if self.status != 'generated_feature':
|
|
# return {
|
|
# 'type': 'ir.actions.client',
|
|
# 'tag': 'display_notification',
|
|
# 'params': {
|
|
# 'title': 'ERROR',
|
|
# 'message': 'Feature engineering not completed.',
|
|
# 'type': 'danger',
|
|
# 'sticky': False
|
|
# }
|
|
# }
|
|
|
|
if not self.datasets_line_ids:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'No data fields selected.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
if not self.feature_engineering:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'ERROR',
|
|
'message': 'No feature engineering selected.',
|
|
'type': 'danger',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
# 开始生成 idea, 先获取到数据集的 name 和 data_sets_type
|
|
for data_field in self.datasets_line_ids:
|
|
data_fields_name = data_field.datasets_id.datasets_id
|
|
data_sets_type = ''
|
|
region_id = self.region.id
|
|
universe_id = self.universe.id
|
|
for data_field_line in data_field.datasets_id.line_ids:
|
|
data_sets_type = data_field_line.data_sets_type
|
|
break
|
|
|
|
# 创建一个alpha.idea记录, 为了防止创建重复的 idea, 当前模型的 name 是 uuid4, 加上 region_id, universe_id, data_fields_name, data_sets_type,这几个字段, 先搜索是否存在相应的记录
|
|
# 如果不存在, 创建, 如果存在, 跳过
|
|
|
|
# 根据 data_sets_type 确定 data_type
|
|
data_type = 'MATRIX' if data_sets_type == 'MATRIX' else 'VECTOR'
|
|
|
|
# 检查是否已存在相同的 idea 记录(特征工程 + 数据集 都相同)
|
|
existing_idea = self.env['alpha.idea'].search([
|
|
('region', '=', region_id),
|
|
('universe', '=', universe_id),
|
|
('data_type', '=', data_type),
|
|
('replace_prompt', '=', self.feature_engineering),
|
|
('needed_data_set_ids.name', '=', data_fields_name)
|
|
], limit=1)
|
|
|
|
if existing_idea:
|
|
continue
|
|
|
|
# 创建新的 alpha.idea 记录
|
|
idea_vals = {
|
|
'region': region_id,
|
|
'universe': universe_id,
|
|
'data_type': data_type,
|
|
'delay': self.use_datasets.delay if self.use_datasets else '1',
|
|
'replace_prompt': self.feature_engineering,
|
|
'needed_data_set_ids': [(0, 0, {'name': data_fields_name})],
|
|
}
|
|
|
|
# 找到最终的 meta_prompt
|
|
meta_prompt = self.env['alpha.prompt.settings'].search([('prompt_type', '=', 'meta_prompt')], limit=1)
|
|
|
|
if meta_prompt:
|
|
idea_vals.update({'meta_prompt': meta_prompt.id})
|
|
|
|
new_record = self.env['alpha.idea'].create(idea_vals)
|
|
|
|
# 创建成功之后, 顺便点一下
|
|
try:
|
|
new_record.btn_check_and_fetch_data()
|
|
new_record.btn_generate_final_prompt()
|
|
except Exception as e:
|
|
print(f'error: {e}')
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'Success',
|
|
'message': 'Ideas generated successfully.',
|
|
'type': 'success',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
def btn_quick_selection_datasets(self):
|
|
self.ensure_one()
|
|
|
|
# 1, 检查是否存在已选择的数据集, 如果存在, 清空self.datasets_line_ids
|
|
if self.datasets_line_ids:
|
|
self.datasets_line_ids.unlink()
|
|
|
|
# 2, 查询数据集, 并创建关联行
|
|
datasets = self.env['alpha.datasets'].search([
|
|
('region', '=', self.region.id),
|
|
('universe', '=', self.universe.id),
|
|
])
|
|
|
|
# 3, 遍历数据, 插入到 datasets_line_ids
|
|
for dataset in datasets:
|
|
self.datasets_line_ids.create({
|
|
'research_direction_id': self.id,
|
|
'datasets_id': dataset.id,
|
|
})
|
|
|
|
return True
|
|
|
|
def btn_action_cancel(self):
|
|
self.ensure_one()
|
|
self.write({
|
|
'status': 'cancel'
|
|
})
|
|
|
|
|
|
class AlphaResearchDatasetsLine(models.Model):
|
|
"""研究方向数据集关联行"""
|
|
_name = 'alpha.research.datasets.line'
|
|
_description = 'Alpha Research Datasets Line'
|
|
_order = 'id desc'
|
|
|
|
research_direction_id = fields.Many2one('alpha.generate.research.direction', string='Research Direction', required=True, ondelete='cascade')
|
|
|
|
datasets_id = fields.Many2one('alpha.datasets', string='Dataset', required=True)
|
|
|
|
data_field_count = fields.Integer(string='Data Field Count', related='datasets_id.data_field_count', readonly=True)
|
|
|
|
datasets_code = fields.Char(string='Dataset Code', related='datasets_id.datasets_id', readonly=True)
|
|
|
|
datasets_name = fields.Char(string='Dataset Name', related='datasets_id.name', readonly=True)
|
|
|
|
region = fields.Char(string='Region', related='datasets_id.region.name', readonly=True)
|
|
|
|
universe = fields.Char(string='Universe', related='datasets_id.universe.name', readonly=True)
|
|
|
|
delay = fields.Selection([
|
|
('1', '1'),
|
|
('0', '0')
|
|
], string='Delay', related='datasets_id.delay', readonly=True)
|
|
|