背景
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 python3import requestsimport jsonimport 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 ModelModulelocal 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 occursM._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 finishedM.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<<EOFrequire("neoai").setup({ selected_model_index = 2,})EOF
复制代码
上述配置在插件配置中配置。
效果
整体还是很不错的。
欢迎大家试用:https://github.com/skyfireitdiy/neoai.nvim
评论