odoo16内置机器人对接chatgpt模块源码分析

news2024/11/25 1:05:49

首先分析 __manifest__.py 代码

# -*- coding: utf-8 -*-
# Copyright (c) 2020-Present InTechual Solutions. (<https://intechualsolutions.com/>)

{
    'name': 'Odoo ChatGPT Integration',
    'version': '16.0.1.0.1',
    'license': 'AGPL-3',
    'summary': 'Odoo ChatGPT Integration',
    'description': 'Allows the application to leverage the capabilities of the GPT language model to generate human-like responses, providing a more natural and intuitive user experience',
    'author': 'InTechual Solutions',
    'company': 'InTechual Solutions',
    'maintainer': 'InTechual Solutions',
    'website': 'https://intechualsolutions.com',
    'depends': ['base', 'base_setup', 'mail'],
    'data': [
        'data/mail_channel_data.xml',
        'data/user_partner_data.xml',
        'views/res_config_settings_views.xml',
    ],
    'external_dependencies': {'python': ['openai']},
    'images': ['static/description/main_screenshot.png'],
    'installable': True,
    'application': False,
    'auto_install': False,
}

发现一段有意思的参数

'external_dependencies': {'python': ['openai']},

这个可以控制在模块安装的时候强制检测有没有安装python的包,如果没有的话,在运行环境手动安装好依赖库后才能安装模块

pip install openai

images 应该是描述页面的主截图?

 接着分析views视图目录,发现里面就一个 res_config_settings_views.xml 文件

<?xml version="1.0"?>
<odoo>
    <record id="is_res_config_settings_view" model="ir.ui.view">
        <field name="name">res.config.settings.view.form.is.chatgpt.inherit</field>
        <field name="model">res.config.settings</field>
        <field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
        <field name="arch" type="xml">
            <xpath expr="//div[@name='integration']" position="after">
                <h2>ChatGPT</h2>
                <div class="col-xs-12 row o_settings_container" id="chatgpt_integraion">
                    <div class="col-xs-12 col-md-6 o_setting_box">
                        <div class="o_setting_right_pane border-start-0">
                            <div class="content-group">
                                <div class="row mt8">
                                    <label class="col-lg-3" string="API Key" for="openapi_api_key"/>
                                    <field name="openapi_api_key" title="OpenAPI API Key"/>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </xpath>
        </field>
    </record>
</odoo>

这个是用于在设置界面增加配置选项的,配置机器人的api_key,界面效果如下

发现字段文本太长了,一行代码写不下,随便改改

<field name="openapi_api_key" title="OpenAPI API Key" style="width: 100% !important;"/>

 

这样就好多了 

  接着分析models视图目录,发现里面就两个文件

res_config_settings.py

# -*- coding: utf-8 -*-
# Copyright (c) 2020-Present InTechual Solutions. (<https://intechualsolutions.com/>)

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
    _inherit = "res.config.settings"

    openapi_api_key = fields.Char(string="API Key", help="Provide the API key here", config_parameter="is_chatgpt_integration.openapi_api_key")

 这个也看的明白,就是给系统配置加一个配置参数

mail_channel.py.py

# -*- coding: utf-8 -*-
# Copyright (c) 2020-Present InTechual Solutions. (<https://intechualsolutions.com/>)

import openai

from odoo import api, fields, models, _
from odoo.exceptions import UserError


class Channel(models.Model):
    _inherit = 'mail.channel'

    def _notify_thread(self, message, msg_vals=False, **kwargs):
        rdata = super(Channel, self)._notify_thread(message, msg_vals=msg_vals, **kwargs)
        chatgpt_channel_id = self.env.ref('is_chatgpt_integration.channel_chatgpt')
        user_chatgpt = self.env.ref("is_chatgpt_integration.user_chatgpt")
        partner_chatgpt = self.env.ref("is_chatgpt_integration.partner_chatgpt")
        author_id = msg_vals.get('author_id')
        chatgpt_name = str(partner_chatgpt.name or '') + ', '
        prompt = msg_vals.get('body')
        if not prompt:
            return rdata
        openai.api_key = self.env['ir.config_parameter'].sudo().get_param('is_chatgpt_integration.openapi_api_key')
        Partner = self.env['res.partner']
        partner_name = ''
        if author_id:
            partner_id = Partner.browse(author_id)
            if partner_id:
                partner_name = partner_id.name
        if author_id != partner_chatgpt.id and chatgpt_name in msg_vals.get('record_name', '') or 'ChatGPT,' in msg_vals.get('record_name', '') and self.channel_type == 'chat':
            try:
                response = openai.Completion.create(
                    model="text-davinci-003",
                    prompt=prompt,
                    temperature=0.6,
                    max_tokens=3000,
                    top_p=1,
                    frequency_penalty=0,
                    presence_penalty=0,
                    user = partner_name,
                )
                res = response['choices'][0]['text']
                self.with_user(user_chatgpt).message_post(body=res, message_type='comment', subtype_xmlid='mail.mt_comment')
            except Exception as e:
                raise UserError(_(e))

        elif author_id != partner_chatgpt.id and msg_vals.get('model', '') == 'mail.channel' and msg_vals.get('res_id', 0) == chatgpt_channel_id.id:
            try:
                response = openai.Completion.create(
                    model="text-davinci-003",
                    prompt=prompt,
                    temperature=0.6,
                    max_tokens=3000,
                    top_p=1,
                    frequency_penalty=0,
                    presence_penalty=0,
                    user = partner_name,
                )
                res = response['choices'][0]['text']
                chatgpt_channel_id.with_user(user_chatgpt).message_post(body=res, message_type='comment', subtype_xmlid='mail.mt_comment')
            except Exception as e:
                raise UserError(_(e))

        return rdata

这个文件就是机器人响应的后端核心逻辑了,大致说一下代码思路:

1.继承修改聊天通道模型 mail.channel 的  _notify_thread方法

可以看到代码利去获取了发送消息的用户的联系人id  author_id  和 用户输入的消息 prompt

 观察了一下代码,如果第一次用户输入的消息prompt是空,那么返回的rdata是个空列表

不然的话输了内容,感觉这段 _notify_thread 代码应该是运行了两次,第一次是用户的联系人id,第2次是chatgpt的联系人id,根据这个69我找到的

接下来去获取了全局参数配置里的chatgpt对应的api_key

还去获取了发送消息的联系人的名称

然后判断是群聊还是私聊

if author_id != partner_chatgpt.id and chatgpt_name in msg_vals.get('record_name', '') or 'ChatGPT,' in msg_vals.get('record_name', '') and self.channel_type == 'chat':
         

这句代码判断发送消息的联系人不等于chatgpt的联系人,并且chatgpt的联系人名称在聊天包括的联系人列表里,或者判断 ChatGPT, 逗号出现在record_name也代表不止一个,表示是私聊

elif author_id != partner_chatgpt.id and msg_vals.get('model', '') == 'mail.channel' and msg_vals.get('res_id', 0) == chatgpt_channel_id.id:

这句代码判断不是自己给自己发消息,并且所在的聊天通道id就是chatgpt的通道,属于判断频道聊天也就是群聊

然后不管是私聊还是群聊,拿到参数去调用openapi请求回复,以下是通用代码

response = openai.Completion.create(
                    model="text-davinci-003",
                    prompt=prompt,
                    temperature=0.6,
                    max_tokens=3000,
                    top_p=1,
                    frequency_penalty=0,
                    presence_penalty=0,
                    user = partner_name,
                )
res = response['choices'][0]['text']

 当然,代码是需要加try的,防止请求chatgpt发送问题。这个经常出问题的

res就是chatgpt正常返回的文本结果

如果是频道聊天,就拿到chatgpt所在的频道并且用chatgpt的联系人来发送消息就行了

chatgpt_channel_id.with_user(user_chatgpt).message_post(body=res, message_type='comment', subtype_xmlid='mail.mt_comment')

如果是私聊,就拿 user_chatgpt这个用户调用消息发送即可

self.with_user(user_chatgpt).message_post(body=res, message_type='comment', subtype_xmlid='mail.mt_comment')

但是实际测试过程中发现bug,这个嗲吗基于无法生效,所以小改了一下

判断私聊的代码改为如下:

if author_id != partner_chatgpt.id and (chatgpt_name in msg_vals.get('record_name', '') or 'ChatGPT,' in msg_vals.get('record_name', '') ) and self.channel_type == 'chat':
        

私聊里发送消息的代码改为如下:

channel = self.env[msg_vals.get('model')].browse(msg_vals.get('res_id'))
channel.with_user(user_chatgpt).message_post(body=res, message_type='comment',subtype_xmlid='mail.mt_comment')

然后私聊就正常了

 接着分析data数据目录,发现里面就两个文件

mail_channel_data.xml

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data noupdate="1">
        <record model="mail.channel" id="channel_chatgpt">
            <field name="name">ChatGPT</field>
            <field name="description">ChatGPT Integration</field>
            <field name="image_128" type="base64" file="is_chatgpt_integration/static/description/chatgpt.png"/>
        </record>

        <record model="mail.message" id="module_install_notification">
            <field name="model">mail.channel</field>
            <field name="res_id" ref="is_chatgpt_integration.channel_chatgpt"/>
            <field name="message_type">email</field>
            <field name="subtype_id" ref="mail.mt_comment"/>
            <field name="subject">Welcome to ChatGPT Channel!</field>
            <field name="body"><![CDATA[<p>Welcome to the #ChatGPT channel.</p>
            <p>Ask your questions to ChatGPT</b>.</p>]]></field>
        </record>

        <record model="mail.channel.member" id="channel_member_chatgtp_channel_for_admin">
            <field name="partner_id" ref="base.partner_admin"/>
            <field name="channel_id" ref="is_chatgpt_integration.channel_chatgpt"/>
            <field name="fetched_message_id" ref="is_chatgpt_integration.module_install_notification"/>
            <field name="seen_message_id" ref="is_chatgpt_integration.module_install_notification"/>
        </record>

        <record model="mail.channel" id="is_chatgpt_integration.channel_chatgpt">
            <field name="group_ids" eval="[Command.link(ref('base.group_user'))]"/>
        </record>
    </data>
</odoo>

 这个初始化了一个聊天频道,把所有内部用户拉进去了。是chatgpt给内部用户进行群聊的

这个不是很清楚

 

 这个初始化了频道创建后的第一条消息

 

 

这个初始化了频道的基本信息,名称,图标,描述等

 

 user_partner_data.xml

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data noupdate="1">
        <record id="partner_chatgpt" model="res.partner">
            <field name="name">ChatGPT</field>
            <field name="image_1920" type="base64" file="is_chatgpt_integration/static/description/chatgpt.png"/>
        </record>
            <record id="user_chatgpt" model="res.users">
            <field name="login">chatgpt</field>
            <field name="password">chatgpt</field>
            <field name="partner_id" ref="is_chatgpt_integration.partner_chatgpt"/>
            <field name="company_id" ref="base.main_company"/>
            <field name="company_ids" eval="[Command.link(ref('base.main_company'))]"/>
            <field name="groups_id" eval="[Command.link(ref('base.group_user'))]"/>
        </record>
    </data>
</odoo>

这个初始化chatgpt的用户登录账号密码,联系人id 公司id,群组id,和对应的联系人名称,头像

接着分析controllers路由接口目录,发现里面就一个文件

main.py

# -*- coding: utf-8 -*-
# Copyright (c) 2020-Present InTechual Solutions. (<https://intechualsolutions.com/>)

from odoo import http


class ChatgptController(http.Controller):
    @http.route(['/chatgpt_form'], type='http', auth="public", csrf=False,
                website=True)
    def question_submit(self):
        return http.request.render('is_chatgpt_integration.connector')

这个不知道干嘛用的,貌似没有这个页面,访问这个接口也找到不到

好了,总的分析就这点东西,剩下的一些静态图片和模块描述页面 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/333596.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Boom 3D最新2023电脑版音效增强软件

Boom 3D是适用于Mac和Windows系统的专业音效增强软件&#xff0c;旨在通过播放器&#xff0c;媒体或流媒体服务等介质&#xff0c;在不同类型的耳机上以3D环绕效果播放媒体内容。您无需使用昂贵的耳机或其他附加环绕音效增强器即可感受3D环绕音乐。 Boom 3D专业音效增强软件&am…

CHI一致性概述

硬件保证一致性使得系统功能部件能够在不需要软件编程参与的情况下共享内存。如果任意两个组件对相同地址单元进行写访问&#xff0c;且系统内所有组件看到的这两个写访问顺序是相同&#xff0c;那么这个地址具有一致性属性。 一致性模型 参见下图&#xff0c;每个请求部件RN…

@LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析

背景 最近引入了 Nacos Config 配置管理能力&#xff0c;说起来用法很简单&#xff0c;还是踩了三个坑。 Nacos Config 的 nacos 的帐号密码加密配置后&#xff0c;怎么解密而且在 NacosConfigBootstrapConfiguration 真正注入 Nacos Config 注入之前&#xff0c;而且不能触发…

十分钟利用环信WebIM-vue3-Demo,打包上线一个即时通讯项目【含音视频通话】

这篇文章无废话&#xff0c;只教你如果接到即时通讯功能需求&#xff0c;十分钟利用环信WebIM-vue3-Demo&#xff0c;打包上线一个即时通讯项目【包含音视频通话功能】。 写这篇文章是因为&#xff0c;结合自身情况&#xff0c;以及所遇到的有同样情况的开发者在接到即时通讯&a…

VS+QT项目创建及配置设置

1.创建QT项目 选择MSV2015 32 (与VS一致即可) 2.在VS中 Qt VS Tools-OpenQtProjectFiles(.pro) 打开QT项目。 3.VS属性页中设置配置 &#xff08;1&#xff09;修改SDK版本和 平台工具集 &#xff08;2&#xff09;更改输出目录 &#xff08;3&#xff09;确认Qt Project …

设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)

工厂模式 Factory Pattern&#xff08;简单工厂、工厂方法、抽象工厂&#xff09; 工厂模式-创建型模式-提供了创建对象的最佳方式。 在工厂模式中&#xff0c;创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通过一个共同的接口来创建新的对象。 简单工厂 简单工厂…

微服务负载均衡器Ribbon

目录 什么是Ribbon 客户端的负载均衡 服务端的负载均衡 常见负载均衡算法 Nacos使用Ribbon 添加LoadBalanced注解 修改controller Ribbon负载均衡策略 IRule AbstractLoadBalancerRule 修改默认负载均衡策略 自定义负载均衡策略 配置自定义的策略 饥饿加载 Ribbo…

Codeforces Round #699 (Div. 2)

E. 题意:n本书,每本书有颜色a[i],一次操作可以将其中一本书放在末尾,求满足:相同颜色的书都是相邻的 的最小操作次数. 显然最多只需要n次,考虑能节省多少次.倒着考虑,记f[i]为i~n最多能节约的次数.先预处理出每种颜色的出现的位置范围l[i],r[i]. 1.不节约这本书f[i] f[i 1]…

Linux文件,目录IO类系统调用总结与示例

tags: C Syscall Linux 写在前面 无论是做网络编程还是系统编程, 逃不开的一个内容就是C系统调用的学习, 正如C的STL一样, 学习OS也有如下的三步骤: 会使用: 熟悉API懂原理: 分析源码写扩展: 实际开发 现在就来熟悉一下系统调用吧. 环境Ubuntu x86_64. 源码部分也参考了apu…

2023抓住这些风口,让你的服装生意一路狂飙!

如今正是消费回暖的大好时机&#xff0c;想要趁着行情回升大展身手的服装商户们&#xff0c;抓住2023的这些风口&#xff0c;生意一路狂飙不是梦&#xff01;风口1&#xff1a; T 恤和运动衫全球纺织信息透露&#xff0c;在全球范围内&#xff0c;T 恤和运动衫的市场规模将在 2…

骑车不戴头盔识别检测系统 Tesnorflow

骑车不戴头盔识别检测系统通过GPU深度学习技术&#xff0c;骑车不戴头盔识别检测对行驶在马路上的骑电动摩托车等未戴头盔的行为进行抓拍&#xff0c;不经过人为干预自动对上述违规行为进行自动抓拍识别。骑车不戴头盔识别检测系统技术上采用 TesnorflowTensorRT推理组合&#…

Qt学习笔记

文章目录一、C指针函数驼峰命名法、下划线命名法编程报错二、C三、Qt语法Qt历史、Qt应用Qt特色快捷键Qt类的族谱QWidgetQPushButtonQDebug对象树Qt窗口坐标信号和槽Qt自带的信号的槽自定义的信号和槽Qt4版本 vs Qt5版本 的connect写法函数指针解决重载问题拓展类型转换QString …

Minikube vs. kind vs. k3s vs k3d vs MicroK8s

文章目录1. minikube2. k3s3. k3d4. Kind5. MicroK8s1. minikube minikube 是一个 Kubernetes SIG 项目&#xff0c;已经启动三年多了。它采用生成虚拟机的方法&#xff0c;该虚拟机本质上是一个单节点 K8s 集群。由于支持大量管理程序&#xff0c;它可以在所有主要操作系统上…

项目管理基础

项目的特点 项目是为提供独特产品、服务或成果所做的临时性努力 临时性&#xff08;一次性&#xff0c;指项目有明确的开始时间和结束时间&#xff09;独特性逐步完善-渐进明细资源约束目的性 项目的三重制约&#xff1a; 成本 质量 时间 其次还有范围 目标&#xff1a;多快…

FPGA时序约束与分析 --- 实例教程(1)

注意&#xff1a; 时序约束辅助工具或者相关的TCL命令&#xff0c;都必须在 open synthesis design / open implemention design 后才能有效运行。 1、时序约束辅助工具 2、查看相关时序信息 3、一般的时序约束顺序 1、 时序约束辅助工具&#xff08;1&#xff09;时序约束编辑…

操作系统的奋斗(三)内存管理

第三章 内存管理3.1内存管理概念3.1.1 内存管理的基本原理和要求&#xff08;1&#xff09;内存管理的主要功能3.1.2 覆盖和交换&#xff08;1&#xff09;覆盖&#xff08;2&#xff09;交换3.1.3 连续分配管理方式&#xff08;1&#xff09;单一连续分配&#xff08;2&#x…

【Spring源码】23. 执行初始化逻辑:initializeBean()

进入initializeBean()先检查是否有安全管理器&#xff0c;如果有就以特权方式执行回调bean中Aware接口方法invokeAwareMethods()invokeAwareMethods()这个方法处理了3个Aware&#xff08;更多关于Aware的内容可移步至那些Aware们&#xff09;BeanNameAwareBeanClassLoaderAware…

json文件在faster_rcnn中从测试到训练 可行性

1.确认任务 经过mydataset文件处理后 - > 在train_res50_fpn文件内应用 # load train data set # VOCdevkit -> VOC2012 -> ImageSets -> Main -> train.txt train_dataset VOCDataSet(VOC_root, "2012", data_transform["train"], &…

Python 异步: 使用和查询任务(8)

任务是异步程序的货币。在本节中&#xff0c;我们将仔细研究如何在我们的程序中与它们交互。 1. 任务生命周期 异步任务具有生命周期。首先&#xff0c;任务是从协程创建的。然后安排在事件循环中独立执行。在某个时候&#xff0c;它会运行。 在运行时它可能会被挂起&#xff0…

舆情监测方案主体需求,TOOM舆情监测预警应对处置方案

舆情监测预警是一种通过预先设定的告警机制&#xff0c;在发生重要事件或异常情况时及时通知相关人员的舆情监测方式。它旨在帮助企业和组织及时了解舆情变化&#xff0c;并采取必要的应对措施&#xff0c;舆情监测方案主体需求&#xff0c;TOOM舆情监测预警应对处置方案。 一…