前段时间通义千问大降价,趁机入手了api,结果没找到相似的攻略,不会用,求助。
遇到的问题
我尝试在Copilot插件中使用,在GPT菜单下填入了我的api和官方推荐的网址,如下
官方推荐如上
之后使用插件发现没有反应,是我设置的有问题吗?求大佬指点
这个一直没有反应
前段时间通义千问大降价,趁机入手了api,结果没找到相似的攻略,不会用,求助。
我尝试在Copilot插件中使用,在GPT菜单下填入了我的api和官方推荐的网址,如下
官方推荐如上
之后使用插件发现没有反应,是我设置的有问题吗?求大佬指点
这个一直没有反应
可能比较复杂, 希望别是跨域的事
还是先了解问题出在哪吧
Copilot 的选项最后那里, 可以启用 debug 模式,
启用后, 打开 Ob 控制台 Ctrl+Shift+i 的状态下, 发送简单一条对话, 看看报的是啥错?
Access to fetch at ‘https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions’ from origin ‘app://obsidian.md’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.
plugin:copilot:83303
POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions net::ERR_FAILED
显示了这个问题,问了AI说是CORS(跨源资源共享)相关的,大佬,这个能解决吗?
实测成功在 Copilot 里使用 OpenAI 风格 API 的 qwen-turbo
简单说, 确实得先解决跨域问题
这方面我也是现学的, 也理解的不太好,
希望熟悉的朋友帮忙提些建议, 指出错误
解决 Copilot 以代理 endpoint 调通义千问时的跨域问题
我目前找到的最简单方案是 cors-anywhere
原理是自造中转服务器, 它既是服务端又是客户端, 得以居中协调两侧都动不了的那些设置
建议先拿这项目搭在 heroku 的 demo 试试
试跑时要注意:
https://dashscope.aliyuncs.com/compatible-mode/v1
https://cors-anywhere.herokuapp.com/https://dashscope.aliyuncs.com/compatible-mode/v1
注意这个就是测试用的: 安全, 速度, 便捷都可疑
如果跑通了, 打算长期就这么用了, 才建议去本地搭建 cors-anywhere 自己用
完后, 还有个坑我也踩过了, 也建议一并改掉:
通义千问要求 top_p 不得为 1
, 可以给改成 0.9
代码在 .obsidian/plugins/copilot/main.js
第 86027 行
Object.defineProperty(this, "topP", {
enumerable: true,
configurable: true,
writable: true,
value: 1 // 通义千问不允许是 1, 给改成 0.5~0.9
});
改掉这个值, 完后重启 Obsidian (找个好点的文本编辑器, 这文件有 4mb)
PS. 这细节也见 如何在 Obsidian 中使用 AI 国产大模型?(KIMI,通义千问) 搜 “报错处理” 那一节
顺便记录一些查这问题时的信息…
1
OpenAI Proxy base url CORS errors · Issue #360 · logancyang/obsidian-copilot 已经有人给 Copilot 报 bug 了
2
CORS problem with library - Developers: Plugin & API - Obsidian Forum
Make HTTP requests from plugins - Developers: Plugin & API - Obsidian Forum 提到了自建中间件服务器, 提到了 node-fetch, 提到了可以用 Ob 提供的 request()
以及 requestUrl()
替代 fetch()
理由是 Ob 给的这俩自带跨域?
3
据说可以加 mode: 'no-cors'
就能解决
fetch('https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', {
mode: 'no-cors'
});
但是上述几个办法, 要么没太理解, 要么试了没搞出来
应该找找比较全面的讲跨域的资料
补充跨域原理的资料, 清楚讲了来龙去脉 为什么给你设置重重障碍?讲一讲Web开发中的跨域
此外对于 Obsidian Copilot 的部分, 也可能有疏漏:
你好,您提到了pkmer这篇文章,这里通义千问的部分是我写的,但是我只测试了text-gen插件,如果您有完整的Copilot解决方案,欢迎您来补充细节
感谢您的文章! 如果我找到了便捷办法, 我一定会帮助补充的
目前情况是, 对于 Copilot 插件我只跑通了 cors-anywhere 方案, 这我知道是挺麻烦的
所以目前我打算看看这两个:
CORS Bypass
开关就能解决跨域? 然后设法运用到 Copilot 里面但这些我一时半会弄不出来, 大家如果能有啥好办法, 也欢迎提提建议
我找到一个很邪门的方法,写了一个插件把原生 fetch 换成了 node-fetch 的 fetch
结果它真的成功了,但是原生 fetch 和 node-fetch 总归是有点小区别,不敢保证没有其他副作用
感谢指路! 我顺着这个 node-fetch 学习一下
这个方法目前来看还挺好用的,甚至连流式都没问题,我之前尝试过一个更邪门的方法来绕过 cors 的问题,绕是绕过了,但是不支持流式传输。
感谢两位大佬的研究,欢迎你们来pkmer文章补充和贡献
可以给作者提PR,虽然现在作者消失了
这两天, 在 Copilot 使用通义千问的问题上取得了一些进展
感谢楼里大家的帮忙!
我把目前知道办法总结为三种方案:
1 修改 fetch 方案
来自 lazyloong 的研究
该思路是临时替换掉 window.fetch, 改成可以跨域的替代品 node-fetch
以下为插件的具体代码
首先 clone sample-plugin 完后需要调整三个文件
// 调整 manifest.json 的描述
{
"id": "alternative-fetch-replacer",
"name": "Alternative Fetch Replacer",
"version": "1.0.0",
"minAppVersion": "0.15.0",
"description": "临时替换 window.fetch 为 node-fetch 以绕开同源策略限制",
"author": "Obsidian",
"authorUrl": "https://obsidian.md",
"fundingUrl": "https://obsidian.md/pricing",
"isDesktopOnly": false
}
// 调整 esbuild.config.mjs 增加一行
......
external: [
......
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"node:*", // <-- 增加这一行, 其余不变
"@lezer/lr",
...builtins],
......
// 完全修改掉 main.ts
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, requestUrl } from 'obsidian';
import fetch from "node-fetch";
type FetchFunction = typeof window.fetch;
// 这个 as unknown 啥意思?
// 目前已熟练掌握了 "类型体操" 四个字的拼写, 其余都不懂
const node_fetch: FetchFunction = fetch as unknown as FetchFunction;
export default class NoProblemsFetch extends Plugin {
originalFetch: any;
async onload () {
this.originalFetch = window.fetch;
console.log(`预备使用 node-fetch 替代 window.fetch`);
window.fetch = node_fetch;
const fetch_funcs = [this.originalFetch, node_fetch, window.fetch];
const current_fetch_desc = fetch_funcs[2] === fetch_funcs[0] ? '原有的fetch':'插件提供的fetch';
console.log(fetch_funcs);
console.log(`当前fetch为: ${current_fetch_desc} \n以上三个函数分别是 [原始fetch, 新node-fetch, 目前采用fetch]`);
new Notice(`使用 node-fetch 替代 window.fetch\n现在是: ${current_fetch_desc}`);
}
onunload () {
console.log(`预备撤销对 window.fetch 的更改`);
window.fetch = this.originalFetch;
const fetch_funcs = [this.originalFetch, node_fetch, window.fetch];
const current_fetch_desc = fetch_funcs[2] === fetch_funcs[0] ? '原有的fetch':'插件提供的fetch';
console.log(fetch_funcs);
console.log(`当前fetch为: ${current_fetch_desc} \n以上三个函数分别是 [原始fetch, 新node-fetch, 目前采用fetch]`);
new Notice(`撤销对 window.fetch 的更改\n现在是: ${current_fetch_desc}`);
}
}
编译方法是
npm install
npm run build # 之后会拿到 main.js 文件
如果不熟悉 nodejs 弄这些可能会有困难
所以也把插件打包下载放在这里 download alter-fetch-replacer
(尽量还是自己编译, 尽量别用莫名来源的插件, 尤其这种改了基础函数的)
使用注意:
.obsidian/plugins/
下面仍存疑问
为啥 TextGen 直接一个
CORS Bypass
开关就能解决跨域? 然后设法运用到 Copilot 里面
因为 TextGen 自造了服务器来解决, 见代码 base.tsx#L165 proxy-service.ts
那么 Copilot 有没有做这个事? 有 (端口为 55301)
但是它只为了 Claude 系列做了代理, 我想给千问也改改, 没弄出来
2 绕行方案
思路是设法代理这个 “通义千问 endpoint”
目前找到了 songquanpeng/one-api 适合干这事, 安装简单, releases 里有本地单文件直接运行
对 One API 的极简介绍: 聚合各平台大模型, 统一以 OpenAI 的格式呈现, 本地部署, 自己给自己当中间商
使用起来大致是
原先 Obsidian Copilot 配置为:
1 在 Copilot 里面填自定义代理 GPT 地址
https://dashscope.aliyuncs.com/compatible-mode/v1
2 在 Copilot 里面填通义千问 apikey (sk-xxxxxx 阿里大模型那个key)
结果: 配完后应该不能用, 有跨域问题
现在要改为:
1
在本地运行 One API
并访问界面地址 http://localhost:3000 (默认密码 root 123456)
在其中配置 渠道 和 令牌
渠道: 添加一个通义千问模型 (通义千问 apikey 要填, 完后点测试并跑通)
令牌: 生成一个, 复制下来
2
在 Copilot 里面 "OpenAI Proxy Base URL" 那里, 改填 One API 给出的地址
http://localhost:3000/v1
在 Copilot 里面填 One API 的令牌 (sk-xxxxxx 注意不是通义的 key)
完后就可以正常用 Obsidian Copilot 了
附 "渠道"的设置图为
优点:
3 cors anywhere 方案
之前已经说过, 不再详述
这个方案确实麻烦, 硬找两个优点:
https://cdnfile.sspai.com
批量给替换成 http://localhost:8080/https://cdnfile.sspai.com
4 总结
再次感谢楼里大家的帮忙!
以上难免有自己理解不对, 试验不周全之处, 遇到了再完善吧
Obsidian, 年轻人的第一款前端实战训练营…
现在 copilot 修复了 CORS 的 bug,现在可以支持所有的 OpenAI 兼容 API,可以接入 Qwen 了