diff --git a/app_chatgpt/models/ai_robot.py b/app_chatgpt/models/ai_robot.py index 49fddbd3..d75f2f3e 100644 --- a/app_chatgpt/models/ai_robot.py +++ b/app_chatgpt/models/ai_robot.py @@ -18,7 +18,7 @@ class AiRobot(models.Model): _name = 'ai.robot' _description = 'Ai Robot' _order = 'sequence, name' - + name = fields.Char(string='Name', translate=True, required=True) provider = fields.Selection(string="AI Provider", selection=[('openai', 'OpenAI'), ('azure', 'Azure')], required=True, default='openai', change_default=True) @@ -116,31 +116,35 @@ GPT-3 A set of models that can understand and generate natural language sequence = fields.Integer('Sequence', help="Determine the display order", default=10) sensitive_words = fields.Text('Sensitive Words Plus', help='Sensitive word filtering. Separate keywords with a carriage return.') is_filtering = fields.Boolean('Filter Sensitive Words', default=False, help='Use base Filter in dir models/lib/sensi_words.txt') - + max_send_char = fields.Integer('Max Send Char', help='Max Send Prompt Length', default=8000) image_avatar = fields.Image('Avatar') partner_ids = fields.One2many('res.partner', 'gpt_id', string='Partner') partner_count = fields.Integer('#Partner', compute='_compute_partner_count', store=False) active = fields.Boolean('Active', default=True) - + def _compute_partner_count(self): for rec in self: rec.partner_count = len(rec.partner_ids) - + def action_disconnect(self): requests.delete('https://chatgpt.com/v1/disconnect') - + def get_ai_pre(self, data, author_id=False, answer_id=False, param={}): # hook,都正常 return False - + + def get_msg_file_content(self, message): + # hook + return False + def get_ai(self, data, author_id=False, answer_id=False, param={}): # 通用方法 # author_id: 请求的 partner_id 对象 # answer_id: 回答的 partner_id 对象 # param,dict 形式的参数 # 调整输出为2个参数:res_post详细内容,is_ai是否ai的响应 - + self.ensure_one() # 前置勾子,一般返回 False,有问题返回响应内容,用于处理敏感词等 res_pre = self.get_ai_pre(data, author_id, answer_id, param) @@ -150,19 +154,19 @@ GPT-3 A set of models that can understand and generate natural language if not hasattr(self, 'get_%s' % self.provider): res = _('No robot provider found') return res, {}, False - + res = getattr(self, 'get_%s' % self.provider)(data, author_id, answer_id, param) # 后置勾子,返回处理后的内容 res_post, usage, is_ai = self.get_ai_post(res, author_id, answer_id, param) return res_post, usage, is_ai - + def get_ai_origin(self, data, author_id=False, answer_id=False, param={}): # 通用方法 # author_id: 请求的 partner_id 对象 # answer_id: 回答的 partner_id 对象 # param,dict 形式的参数 # 调整输出为2个参数:res_post详细内容,is_ai是否ai的响应 - + self.ensure_one() # 前置勾子,一般返回 False,有问题返回响应内容,用于处理敏感词等 res_pre = self.get_ai_pre(data, author_id, answer_id, param) @@ -172,12 +176,12 @@ GPT-3 A set of models that can understand and generate natural language if not hasattr(self, 'get_%s' % self.provider): res = _('No robot provider found') return res, {}, False - + res = getattr(self, 'get_%s' % self.provider)(data, author_id, answer_id, param) # 后置勾子,返回处理后的内容 res_post, usage, is_ai = self.get_ai_post(res, author_id, answer_id, param) return res - + def get_ai_post(self, res, author_id=False, answer_id=False, param=None): # hook,高级版要替代 if param is None: @@ -214,7 +218,7 @@ GPT-3 A set of models that can understand and generate natural language if sys_content: return {"role": "system", "content": sys_content} return {} - + def get_ai_model_info(self): self.ensure_one() headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.openapi_api_key}"} @@ -222,7 +226,7 @@ GPT-3 A set of models that can understand and generate natural language o_url = "https://api.openai.com/v1/models/%s" % self.ai_model if self.endpoint: o_url = self.endpoint.replace("/chat/completions", "") + "/models/%s" % self.ai_model - + response = requests.get(o_url, headers=headers, timeout=R_TIMEOUT) response.close() if response: @@ -231,29 +235,32 @@ GPT-3 A set of models that can understand and generate natural language else: r_text = 'No response.' raise UserError(r_text) - + def get_ai_list_model(self): self.ensure_one() - headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.openapi_api_key}"} - R_TIMEOUT = self.ai_timeout or 120 - o_url = "https://api.openai.com/v1/models" - if self.endpoint: - o_url = self.endpoint.replace("/chat/completions", "") + "/models" - response = requests.get(o_url, headers=headers, timeout=R_TIMEOUT) - response.close() - if response: - res = response.json() - r_text = json.dumps(res, indent=2) + if self.provider == 'openai': + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.openapi_api_key}"} + R_TIMEOUT = self.ai_timeout or 120 + o_url = "https://api.openai.com/v1/models" + if self.endpoint: + o_url = self.endpoint.replace("/chat/completions", "") + "/models" + response = requests.get(o_url, headers=headers, timeout=R_TIMEOUT) + response.close() + if response: + res = response.json() + r_text = json.dumps(res, indent=2) + else: + r_text = 'No response.' else: r_text = 'No response.' raise UserError(r_text) - + def get_openai(self, data, author_id, answer_id, param={}): self.ensure_one() headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.openapi_api_key}"} R_TIMEOUT = self.ai_timeout or 120 o_url = self.endpoint or "https://api.openai.com/v1/chat/completions" - + # 处理传参,传过来的优先于 robot 默认的 max_tokens = param.get('max_tokens') if param.get('max_tokens') else self.max_tokens temperature = param.get('temperature') if param.get('temperature') else self.temperature @@ -261,7 +268,7 @@ GPT-3 A set of models that can understand and generate natural language frequency_penalty = param.get('frequency_penalty') if param.get('frequency_penalty') else self.frequency_penalty presence_penalty = param.get('presence_penalty') if param.get('presence_penalty') else self.presence_penalty request_timeout = param.get('request_timeout') if param.get('request_timeout') else self.ai_timeout - + if self.stop: stop = self.stop.split(',') else: @@ -301,7 +308,7 @@ GPT-3 A set of models that can understand and generate natural language return res else: _logger.warning('=====================openai output data: %s' % response.json()) - + return _("Response Timeout, please speak again.") def get_azure(self, data, author_id, answer_id, param={}): @@ -309,10 +316,10 @@ GPT-3 A set of models that can understand and generate natural language # only for azure if not self.endpoint: raise UserError(_("Please Set your AI robot's endpoint first.")) - + if not self.api_version: raise UserError(_("Please Set your AI robot's API Version first.")) - + if self.stop: stop = self.stop.split(',') else: @@ -321,7 +328,7 @@ GPT-3 A set of models that can understand and generate natural language messages = data else: messages = [{"role": "user", "content": data}] - + # 处理传参,传过来的优先于 robot 默认的 max_tokens = param.get('max_tokens') if param.get('max_tokens') else self.max_tokens temperature = param.get('temperature') if param.get('temperature') else self.temperature @@ -329,14 +336,14 @@ GPT-3 A set of models that can understand and generate natural language frequency_penalty = param.get('frequency_penalty') if param.get('frequency_penalty') else self.frequency_penalty presence_penalty = param.get('presence_penalty') if param.get('presence_penalty') else self.presence_penalty request_timeout = param.get('request_timeout') if param.get('request_timeout') else self.ai_timeout - + # Ai角色设定,如果没设定则再处理 if messages[0].get('role') != 'system': sys_content = self.get_ai_system(param.get('sys_content')) if sys_content: messages.insert(0, sys_content) # 暂时不变 - + client = AzureOpenAI( api_version=self.api_version, azure_endpoint=self.endpoint, @@ -369,14 +376,14 @@ GPT-3 A set of models that can understand and generate natural language else: _logger.warning('=====================azure output data: %s' % response.json()) return _("Response Timeout, please speak again.") - + @api.onchange('provider') def _onchange_provider(self): if self.provider == 'openai': self.endpoint = 'https://api.openai.com/v1/chat/completions' elif self.provider == 'azure': self.endpoint = 'https://odoo.openai.azure.com' - + if self.provider: # 取头像 module_path = modules.get_module_path('app_chatgpt', display_warning=False) @@ -385,7 +392,7 @@ GPT-3 A set of models that can understand and generate natural language if path: image_file = tools.file_open(path, 'rb') self.image_avatar = base64.b64encode(image_file.read()) - + @api.onchange('set_ai_model') def _onchange_set_ai_model(self): if self.set_ai_model: diff --git a/app_chatgpt/models/discuss_channel.py b/app_chatgpt/models/discuss_channel.py index 28d56d3f..6279db14 100644 --- a/app_chatgpt/models/discuss_channel.py +++ b/app_chatgpt/models/discuss_channel.py @@ -71,7 +71,7 @@ class Channel(models.Model): ('-1', '允许较多重复'), ('-2', '更多强调重复'), ], string='Presence penalty', default='1', help="-2~2,值越大越少重复词") - + # todo: 这里用 compute? max_tokens = fields.Integer('最长响应Token', default=600, help="越大返回内容越多,计费也越多") chat_count = fields.Integer(string="上下文数量", default=0, help="0~3,设定后,会将最近n次对话发给Ai,有助于他更好的回答") @@ -79,9 +79,9 @@ class Channel(models.Model): top_p = fields.Float(string="连贯性值", default=0.6, help="0~1,值越大越富有想像力,越小则越保守") frequency_penalty = fields.Float('避免常用词值', default=1, help="-2~2,值越大越少使用常用词") presence_penalty = fields.Float('避免重复词值', default=1, help="-2~2,值越大越少重复词") - + is_current_channel = fields.Boolean('是否当前用户默认频道', compute='_compute_is_current_channel', help='是否当前用户默认微信对话频道') - + # begin 处理Ai对话 is_ai_conversation = fields.Boolean('Ai Conversation', default=False, help='Set active to make conversation between 2+ Ai Employee. You Just say first word, then Ai robots Auto Chat.') @@ -91,9 +91,9 @@ class Channel(models.Model): # 辅助Ai角色设定 ext_ai_sys_content = fields.Char('Extend Robot Role', change_default=True, help='The Role the Second Ai robot play for. This is for Ai Conversation.') - + # end 处理Ai对话 - + def name_get(self): result = [] for c in self: @@ -103,7 +103,7 @@ class Channel(models.Model): pre = '' result.append((c.id, "%s%s" % (pre, c.name or ''))) return result - + def get_openai_context(self, channel_id, author_id, answer_id, minutes=60, chat_count=0): # 上下文处理,要处理群的方式,以及独聊的方式 # azure新api 处理 @@ -113,7 +113,7 @@ class Channel(models.Model): # 处理消息: 取最新问题 + 上 chat_count=1次的交互,将之前的交互按时间顺序拼接。 # 注意: ai 每一次回复都有 parent_id 来处理连续性 # 私聊处理 - + # todo: 更好的处理方式 domain = [('res_id', '=', channel_id), ('model', '=', 'discuss.channel'), @@ -122,7 +122,7 @@ class Channel(models.Model): ('is_ai', '=', True), ('body', '!=', '
%s
' % _('Response Timeout, please speak again.')), ('body', '!=', _('温馨提示:您发送的内容含有敏感词,请修改内容后再向我发送。'))] - + if self.channel_type in ['group', 'channel']: # 群聊增加时间限制,当前找所有人,不限制 author_id domain = expression.AND([domain, [('date', '>=', afterTime)]]) @@ -148,11 +148,11 @@ class Channel(models.Model): 'content': user_content, }) return context_history - + def get_ai_config(self, ai): # 勾子,用于取ai 配置 return {} - + def get_ai_response(self, ai, messages, channel, user_id, message): author_id = message.create_uid.partner_id answer_id = user_id.partner_id @@ -179,10 +179,9 @@ class Channel(models.Model): 'ai_completion_tokens': completion_tokens, 'cost_tokens': total_tokens, }) - + def _notify_thread(self, message, msg_vals=False, **kwargs): rdata = super(Channel, self)._notify_thread(message, msg_vals=msg_vals, **kwargs) - # print(f'rdata:{rdata}') answer_id = self.env['res.partner'] user_id = self.env['res.users'] author_id = msg_vals.get('author_id') @@ -191,13 +190,13 @@ class Channel(models.Model): channel_type = self.channel_type messages = [] add_sys_content = '' - + # 不处理 一般notify,但处理欢迎 if ''): return rdata if 'o_odoobot_command' in message.body: return rdata - + # begin: 找ai,增加 ai二人转功能。 chat类型不用管, 使用其中一个ai登录即可。 author_id 是 res.partner 模型 if channel_type == 'chat': channel_partner_ids = self.channel_partner_ids @@ -209,7 +208,7 @@ class Channel(models.Model): is_allow = message.author_id.id in gpt_wl_partners.ids if gpt_policy == 'all' or (gpt_policy == 'limit' and is_allow): ai = answer_id.sudo().gpt_id - + elif channel_type in ['group', 'channel']: # partner_ids = @ ids partner_ids = list(msg_vals.get('partner_ids')) @@ -266,31 +265,31 @@ class Channel(models.Model): # # 暂时有限用户的Ai # raise UserError(_('此Ai暂时未开放,请联系管理员。')) # end: 找ai,增加 ai二人转功能 - + if hasattr(ai, 'is_translator') and ai.is_translator and ai.ai_model == 'translator': return rdata chatgpt_channel_id = self.env.ref('app_chatgpt.channel_chatgpt') - + if message.body == _('