背景
NeoAI
(https://github.com/Bryley/neoai.nvim)是一个Neovim
插件,它将OpenAI
的GPT-4
的强大功能直接引入Neovim
编辑器。它可以帮助你生成代码、重写文本,甚至在与你的代码上下文相符的情况下提供建议。该插件采用了用户友好的界面,使你可以轻松地与人工智能进行交互,获取所需的帮助。该插件的主要动机是在你的Neovim
编程工作流中提供无缝集成的AI
聊天助手,如ChatGPT
。而作者目前的实现也仅支持ChatGPT
。对于中国用户来说,多少有点不便。
在一段时间前,我向作者提了一个issue
(https://github.com/Bryley/neoai.nvim/issues/50),并且实现了基于科大讯飞Spark
的集成实现(https://github.com/Bryley/neoai.nvim/pull/52)。不过仓库的作者截至目前也没有评审代码和回复(可能比较忙),因此,我fork
了一份到自己的仓库(https://github.com/skyfireitdiy/neoai.nvim),并将其他的一些pr
合入。
现在既然千帆大模型开放了,那么,今天就将千帆大模型接入NeoAI
。
测试
创建应用
千帆大模型的配置参数主要有API Key/Secret Key
,由此通过调用远端API
获取access_token
,后续根据access_token
调用服务。
接下来获取API Key/Secret Key
。
在百度云控制台“应用接入”创建应用,即可拿到API Key/Secret Key
。
开通付费
在“计费管理”页面开通想要使用的模型的付费,才能从API
调用。
测试接口
在“人工智能 AI”可以直接进行测试,并生成对应的接口代码。
编写一个命令行工具
接下来,将接口代码下载下来,加以改造,使其接受命令行参数。
代码如下:
#!/usr/bin/env python3
import requests
import json
import argparse
urls = {
"ErnieBot": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant",
"ErnieBot-turbo": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant",
}
def chat(api_key, secret_key, messages, m):
url = urls[m] + "?access_token=" + get_access_token(api_key, secret_key)
payload = json.dumps({
"messages": messages
})
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
def get_access_token(api_key, secret_key):
url = "https://aip.baidubce.com/oauth/2.0/token"
params = {"grant_type": "client_credentials",
"client_id": api_key, "client_secret": secret_key}
return str(requests.post(url, params=params).json().get("access_token"))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('api_key')
parser.add_argument('secret_key')
parser.add_argument('messages')
parser.add_argument('model')
args = parser.parse_args()
chat(args.api_key, args.secret_key, args.messages, args.model)
复制代码
测试如下:
集成进 NeoAI
之前集成过讯飞Spark
,因此本次集成千帆就比较简单了。
集成配置
AI
配置位于:neoai.nvim/lua/neoai/config.lua
在模型配置中增加千帆配置:
models = {
{
name = "openai",
model = "gpt-3.5-turbo",
params = nil,
},
{
name = "spark",
model = "v1",
params = nil,
},
{
name = "qianfan",
model = "ErnieBot-turbo",
params = nil,
}
},
复制代码
增加千帆认证信息配置
qianfan = {
api_key = {
secret_env = "QIANFAN_SECRET",
apikey_env = "QIANFAN_APIKEY",
secret = nil,
apikey = nil,
get = function()
if not M.options.qianfan.api_key.secret then
M.options.qianfan.api_key.secret = os.getenv(M.options.qianfan.api_key.secret_env)
end
if not M.options.qianfan.api_key.apikey then
M.options.qianfan.api_key.apikey = os.getenv(M.options.qianfan.api_key.apikey_env)
end
if M.options.qianfan.api_key.secret and M.options.qianfan.api_key.apikey then
return M.options.qianfan.api_key.secret, M.options.qianfan.api_key.apikey
end
local msg = M.options.qianfan.api_key.secret_env .. "/"
.. M.options.qianfan.api_key.apikey_env .. " environment variable is not set"
logger.error(msg)
error(msg)
end
},
},
复制代码
本质上是从环境变量 QIANFAN_SECRET
和QIANFAN_APIKEY
获取认证信息。
使用认证信息,调用脚本
在neoai.nvim/lua/neoai/chat/models/
中创建qianfan.lua
:
local utils = require("neoai.utils")
local config = require("neoai.config")
---@type ModelModule
local M = {}
M.name = "Qianfan"
M._chunks = {}
local raw_chunks = {}
M.get_current_output = function()
return table.concat(M._chunks, "")
end
---@param chunk string
---@param on_stdout_chunk fun(chunk: string) Function to call whenever a stdout chunk occurs
M._recieve_chunk = function(chunk, on_stdout_chunk)
local raw_json = chunk
table.insert(raw_chunks, raw_json)
local ok, path = pcall(vim.json.decode, raw_json)
if not ok then
on_stdout_chunk("111")
return
end
path = path.result
if path == nil then
on_stdout_chunk("222")
return
end
on_stdout_chunk(path)
-- append_to_output(path, 0)
table.insert(M._chunks, path)
end
---@param chat_history ChatHistory
---@param on_stdout_chunk fun(chunk: string) Function to call whenever a stdout chunk occurs
---@param on_complete fun(err?: string, output?: string) Function to call when model has finished
M.send_to_model = function(chat_history, on_stdout_chunk, on_complete)
local secret, apikey = config.options.qianfan.api_key.get()
local model = config.options.models[config.options.selected_model_index+1].model
local get_script_dir = function()
local info = debug.getinfo(1, "S")
local script_path = info.source:sub(2)
return script_path:match("(.*/)")
end
local py_script_path = get_script_dir() .. "qianfan.py"
os.execute("chmod +x "..py_script_path)
chunks = {}
raw_chunks = {}
on_stdout_chunk(apikey)
on_stdout_chunk(secret)
on_stdout_chunk(vim.json.encode(chat_history.messages))
on_stdout_chunk(model)
utils.exec(py_script_path, {
apikey,
secret,
vim.json.encode(chat_history.messages),
model,
}, function(chunk)
M._recieve_chunk(chunk, on_stdout_chunk)
end, function(err, _)
local total_message = table.concat(raw_chunks, "")
local ok, json = pcall(vim.json.decode, total_message)
if ok then
if json.error ~= nil then
on_complete(json.error.message, nil)
return
end
end
on_complete(err, M.get_current_output())
end)
end
return M
复制代码
此lua
脚本会调用相同目录下的qianfan.py
脚本,与千帆对话交互。
在初始化中配置使用千帆大模型
let $QIANFAN_APIKEY = "xxxxxxxxxx"
let $QIANFAN_SECRET = "yyyyyyyyyyyyyyyyyyyyyyy"
lua<<EOF
require("neoai").setup({
selected_model_index = 2,
})
EOF
复制代码
上述配置在插件配置中配置。
效果
整体还是很不错的。
欢迎大家试用:https://github.com/skyfireitdiy/neoai.nvim
评论