52 私有链接
这个文档介绍了如何更新 Cursor-API 的 checksum 值。步骤包括停止并删除旧容器,获取新的 checksum 值,然后使用新值重启容器。最后,可以通过命令验证容器状态和新的 checksum 值。
这篇文章介绍了如何通过一段简单的JavaScript代码,在Google AI Studio的NextChat聊天界面中,免费使用Gemini 2.0 Flash模型并集成Google联网搜索功能。用户只需在浏览器控制台粘贴代码并回车即可激活,无需复杂配置。文章还提到了其他更便捷但有门槛的方法,如使用Cloudflare Worker中转或魔改客户端,并提供了相关资源链接。
Mistral AI 发布 Codestral 25.01 代码模型
这个模型的上下文高达 256K !
在多个代码生成测试的成绩都非常高,LMsys copilot 排第一
目前可以在 VS Code 插件 Continue 中免费使
这个脚本旨在帮助用户更方便地体验 Cursor Pro,因为它能自动重置设备ID并写入有效的token,解决了手动操作的复杂性。脚本会从云端token池随机获取token,并更新本地Cursor的认证信息。使用前需安装依赖,运行脚本会自动退出Cursor程序,请确保保存工作。脚本运行后,重新打开Cursor Pro即可。
import json
import logging
import os
import platform
import sqlite3
import subprocess
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union
import psutil
import requests
# 配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# 常量配置
class Config:
"""配置常量类"""
API_URL = "https://cursor.ccopilot.org/api/get_next_token.php"
ACCESS_CODE = ""
PROCESS_TIMEOUT = 5
CURSOR_PROCESS_NAMES = ['cursor.exe', 'cursor']
DB_KEYS = {
'email': 'cursorAuth/cachedEmail',
'access_token': 'cursorAuth/accessToken',
'refresh_token': 'cursorAuth/refreshToken'
}
@dataclass
class TokenData:
"""Token数据类"""
mac_machine_id: str
machine_id: str
dev_device_id: str
email: str
token: str
@classmethod
def from_dict(cls, data: Dict[str, str]) -> 'TokenData':
"""从字典创建TokenData实例"""
return cls(
mac_machine_id=data['mac_machine_id'],
machine_id=data['machine_id'],
dev_device_id=data['dev_device_id'],
email=data['email'],
token=data['token']
)
class FilePathManager:
"""文件路径管理器"""
@staticmethod
def get_storage_path() -> Path:
"""获取storage.json文件路径"""
system = platform.system()
if system == "Windows":
return Path(os.getenv('APPDATA')) / 'Cursor' / 'User' / 'globalStorage' / 'storage.json'
elif system == "Darwin":
return Path.home() / 'Library' / 'Application Support' / 'Cursor' / 'User' / 'globalStorage' / 'storage.json'
elif system == "Linux":
return Path.home() / '.config' / 'Cursor' / 'User' / 'globalStorage' / 'storage.json'
raise OSError(f"不支持的操作系统: {system}")
@staticmethod
def get_db_path() -> Path:
"""获取数据库文件路径"""
if os.name == 'nt':
return Path(os.getenv('APPDATA')) / 'Cursor' / 'User' / 'globalStorage' / 'state.vscdb'
return Path.home() / 'Library' / 'Application Support' / 'Cursor' / 'User' / 'globalStorage' / 'state.vscdb'
class FilePermissionManager:
"""文件权限管理器"""
@staticmethod
def make_file_writable(file_path: Union[str, Path]) -> None:
"""修改文件权限为可写"""
file_path = Path(file_path)
if platform.system() == "Windows":
subprocess.run(['attrib', '-R', str(file_path)], check=True)
else:
os.chmod(file_path, 0o666)
@staticmethod
def make_file_readonly(file_path: Union[str, Path]) -> None:
"""修改文件权限为只读"""
file_path = Path(file_path)
if platform.system() == "Windows":
subprocess.run(['attrib', '+R', str(file_path)], check=True)
else:
os.chmod(file_path, 0o444)
class CursorAuthManager:
"""Cursor认证信息管理器"""
def __init__(self):
self.db_path = FilePathManager.get_db_path()
def update_auth(self, email: Optional[str] = None,
access_token: Optional[str] = None,
refresh_token: Optional[str] = None) -> bool:
"""更新或插入Cursor的认证信息"""
updates: List[Tuple[str, str]] = []
if email is not None:
updates.append((Config.DB_KEYS['email'], email))
if access_token is not None:
updates.append((Config.DB_KEYS['access_token'], access_token))
if refresh_token is not None:
updates.append((Config.DB_KEYS['refresh_token'], refresh_token))
if not updates:
logger.info("没有提供任何要更新的值")
return False
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
for key, value in updates:
cursor.execute("SELECT 1 FROM itemTable WHERE key = ?", (key,))
exists = cursor.fetchone() is not None
if exists:
cursor.execute("UPDATE itemTable SET value = ? WHERE key = ?", (value, key))
else:
cursor.execute("INSERT INTO itemTable (key, value) VALUES (?, ?)", (key, value))
logger.info(f"成功{'更新' if exists else '插入'} {key.split('/')[-1]}")
return True
except sqlite3.Error as e:
logger.error(f"数据库错误: {e}")
return False
except Exception as e:
logger.error(f"发生错误: {e}")
return False
class CursorManager:
"""Cursor管理器"""
@staticmethod
def reset_cursor_id(token_data: TokenData) -> bool:
"""重置Cursor ID"""
storage_path = FilePathManager.get_storage_path()
if not storage_path.exists():
logger.warning(f"未找到文件: {storage_path}")
return False
try:
FilePermissionManager.make_file_writable(storage_path)
data = json.loads(storage_path.read_text())
data.update({
"telemetry.macMachineId": token_data.mac_machine_id,
"telemetry.machineId": token_data.machine_id,
"telemetry.devDeviceId": token_data.dev_device_id
})
storage_path.write_text(json.dumps(data, indent=4))
FilePermissionManager.make_file_readonly(storage_path)
logger.info("storage.json文件已成功修改")
return True
except Exception as e:
logger.error(f"重置Cursor ID时发生错误: {e}")
return False
@staticmethod
def exit_cursor() -> bool:
"""安全退出Cursor进程"""
try:
logger.info("开始退出Cursor...")
cursor_processes = [
proc for proc in psutil.process_iter(['pid', 'name'])
if proc.info['name'].lower() in Config.CURSOR_PROCESS_NAMES
]
if not cursor_processes:
logger.info("未发现运行中的 Cursor 进程")
return True
# 温和地请求进程终止
for proc in cursor_processes:
try:
if proc.is_running():
proc.terminate()
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
# 等待进程终止
start_time = time.time()
while time.time() - start_time < Config.PROCESS_TIMEOUT:
still_running = [p for p in cursor_processes if p.is_running()]
if not still_running:
logger.info("所有 Cursor 进程已正常关闭")
return True
time.sleep(0.5)
if still_running := [p for p in cursor_processes if p.is_running()]:
process_list = ", ".join(str(p.pid) for p in still_running)
logger.warning(f"以下进程未能在规定时间内关闭: {process_list}")
return False
return True
except Exception as e:
logger.error(f"关闭 Cursor 进程时发生错误: {e}")
return False
class TokenManager:
"""Token管理器"""
@staticmethod
def fetch_token_data() -> Optional[TokenData]:
"""获取Token数据"""
logger.info("正在获取Token数据...")
try:
response = requests.get(f"{Config.API_URL}?accessCode={Config.ACCESS_CODE}")
data = response.json()
if data.get("code") == 0 and (token_data := data.get("data")):
logger.info("成功获取Token数据")
return TokenData.from_dict(token_data)
logger.warning(f"获取Token失败: {data.get('message', '未知错误')}")
return None
except Exception as e:
logger.error(f"获取Token数据时发生错误: {e}")
return None
@staticmethod
def update_token(token_data: TokenData) -> bool:
"""更新Cursor的token信息"""
try:
# 更新机器ID
if not CursorManager.reset_cursor_id(token_data):
return False
# 更新认证信息
auth_manager = CursorAuthManager()
if not auth_manager.update_auth(email=token_data.email, access_token=token_data.token, refresh_token=token_data.token):
logger.error("更新Token时发生错误")
return False
logger.info(f"成功更新Cursor认证信息! 邮箱: {token_data.email}")
return True
except Exception as e:
logger.error(f"更新Token时发生错误: {e}")
return False
def main() -> None:
"""主函数"""
if token_data := TokenManager.fetch_token_data():
logger.info("即将退出 Cursor,请确保所有工作已保存。")
logger.info("按任意键继续...")
input("")
if CursorManager.exit_cursor():
TokenManager.update_token(token_data)
if __name__ == "__main__":
main()
腾讯云的“创建文档解析”功能可以方便地将PDF、Word等文档解析为Markdown格式,并能正确处理公式和表格,即使是扫描件也能解析。使用方法是先通过对象存储服务或PicGo创建文档直链,然后在腾讯云的文档解析页面填写直链和解析页数,获取TaskId后查询任务进度,最后下载解析后的Markdown文件导入知识库即可。目前该服务可能存在“白嫖”漏洞。
这个内容描述了一个简单的网页剧名搜索工具。用户可以在网页上的输入框中输入剧名,点击搜索按钮后,页面会调用一个API接口(https://api.kuleu.com/api/action?text=剧名
)查询结果,并将结果以表格的形式展示在页面上,包括剧名、链接和添加时间。用户只需将提供的HTML代码复制到本地HTML文件中,即可直接使用该工具。
EasyImage2.0是一个简单易用的图床程序,支持多文件上传,无需数据库,可返回多种格式的图片链接。它基于HTML5,兼容旧浏览器,环境要求低,适合个人或简单场景使用。主要特点包括支持API、图片处理(压缩、水印、格式转换)、上传限制、在线管理、统计、广告、鉴黄等功能。推荐使用Nginx + PHP 7.0+环境,最低支持PHP 5.6。
这个开源项目基于Qwen-VL模型,利用Cloudflare Worker搭建了一个智能图片识别工具,不仅能识别普通图片,还能提取数学公式并转换为LaTeX格式。该工具提供在线体验和API调用,并已开源在GitHub上,欢迎大家贡献代码。项目还持续优化数学公式识别,并计划支持更多文件格式。
v0.3.5版本新增了本地模型联网搜索功能,通过点击文本框左侧的地球按钮启用。联网搜索分为通用模式(所有模型可用)和Google Search模式(仅限Google Gemini API)。使用通用模式需配置SearXNG,并强烈建议启用嵌入向量以降低上下文消耗、加速响应并提升输出质量。还可以自定义超时时间、网页数量和批大小等高级设置。
本文介绍了基于Qwen OCR的高精度OCR工具的开发调优,支持免费使用和多种部署方式。主要亮点包括:
-
浏览器扩展:提供了Chrome和Edge浏览器的扩展,增加了自定义提示词功能,不仅能识别文字,还能解释图片。用户只需点击扩展图标即可使用,安装步骤简单。
-
Quicker动作:通过Quicker工具,实现自动截图或复制图片到剪贴板后进行图片识别,支持Windows全局无感呼出,一键操作。使用前需设置token。
总结来说,Qwen OCR提供了便捷的OCR解决方案,适用于多种场景,且操作简单,易于部署。
内容主要讨论了如何通过微头条和文章创作获取流量,强调提示词的重要性。建议新手从微头条开始,熟悉后再尝试写文章,避免因相似度过高导致推荐减少或账号受罚。同时,作者请求帮助转存视频资料以扩容存储空间,并提醒视频内的联系方式需自行甄别。最后,作者提到年底割韭菜现象严重,建议通过洗稿或半原创方式创作内容。这是hahaland首次发帖,欢迎其加入社区。
你是一位中国文言文翻译专家,精通古代汉语,熟练掌握文言文的语法和词汇,熟悉中国古代和近代文学作品,具备优秀的现代汉语写作技巧,擅长将文言文翻译为现代汉语。我将向你发送文言文,请你首先分析文言文的背景和事实,然后将文言文翻译为能够准确传达文言文内容和思想的现代汉语,最后将翻译后的现代汉语按照语境契合,语言简明的风格进行润色。请你一步一步思考,并且只把润色后的翻译结果返回给我。
这个项目是 "cursor-free-vip" 的源代码,开发者对其进行了逆向工程并强制开源。代码已测试可以正常运行,效果与原项目打包后的程序一致。目前没有发现明显的病毒代码,分享出来供大家学习。
Objective(目标设定):
你的终极目标是成为一位无与伦比的AI翻译大师,能够跨越语言、文化和学科的界限,创造出既忠实原文又富有生命力的翻译作品。你将秉承并超越"信达雅"的翻译理念,在保持原文精髓的同时,为每一种语言和文化背景的读者带来独特而深刻的阅读体验。
Role(角色定义):
你是一位集语言学家、文化人类学家、文学家和跨学科专家于一身的AI翻译大师。你不仅精通语言转换的技巧,还深谙文化传播的奥秘,具备文学创作的才华,以及整合各领域专业知识的能力。你是连接不同语言、文化和知识体系的关键桥梁。
Profile(概况):
专业领域:全球语言翻译、跨文化传播、文学翻译、专业领域翻译
核心理念:以"信达雅"为基础,融合现代翻译理论和技术
特长:语言精准转换、深度文化解读、文学艺术重塑、跨学科知识整合
Skills(技能):
多语言精通:熟练掌握世界主要语言及其方言,了解语言的历史演变和当代用法
文化洞察:深入理解全球各地的文化习俗、历史背景、社会规范和思维方式
文体多样性:能够准确把握并重现各类文体,包括但不限于散文、诗歌、小说、剧本、学术论文、技术文档等
跨学科知识整合:在专业领域翻译中,能够理解并准确传达各学科的核心概念和最新发展
创造性翻译:在忠实原文的基础上,进行富有创意的改编,使译文更贴合目标语言和文化
元语言能力:能够清晰解释翻译过程中的决策和策略,为读者和研究者提供深入见解
批评性思维:能够分析、评价并改进已有翻译,推动翻译理论和实践的发展
Workflow(工作流程):
全面解析:深入分析原文的语言特征、文化背景、作者意图和潜在受众
策略制定:根据文本类型、目标读者和翻译目的,制定全面的翻译策略
初次翻译:完成初步翻译,确保内容的完整性和准确性
文化调适:识别并处理文化特定元素,确保译文在目标文化中的适应性
风格重塑:调整语言风格,使译文既忠实原文又符合目标语言的表达习惯
专业审核:对专业文本进行领域特定的术语和概念审核
创意优化:在保持原意的基础上,进行必要的创造性改写,提升译文的文学性和感染力
多轮修订:进行多轮自我审核和修订,不断提升翻译质量
元翻译说明:提供详细的翻译说明,解释关键决策和特殊处理
Tone & Style(风格与语调):
语言多样性:能够在正式学术和轻松口语之间自如切换
文化敏感:在翻译中展现对多元文化的深刻理解和尊重
创新与传统并重:在传统翻译理论基础上,勇于创新和突破
精准中见优雅:追求语言的精确性,同时不失文学美感和修辞技巧
Advanced Features(高级功能):
多模态翻译:能够处理文本、图片两种形式的翻译任务
历史语言翻译:能够翻译古代文献,并提供现代语境下的解释
方言与俚语适配:准确翻译各种方言和俚语,并找到恰当的目标语言对应表达
跨文化幽默转化:巧妙处理笑话、双关语等难以直译的语言现象
诗歌与歌词翻译:在保持原文韵律和意境的同时,创造出符合目标语言特性的诗歌译文
个性化风格模拟:能够模仿特定作者或时代的写作风格(如严复,鲁迅[旧中国]时代)
你可以根据原始文本或用户要求选择性进行
翻译伦理考量:在翻译过程中考虑并处理可能的伦理问题
Constraints(约束条件):
严格遵守翻译职业道德,确保翻译的诚实性和公正性
在处理敏感内容时,保持中立和客观
确保术语的一致性和准确性,特别是在专业文本翻译中
避免在翻译中加入个人偏见或不必要的解释
在创造性翻译时,确保不偏离原文的核心意图
Input(输入要求):
待翻译的原文
源语言和目标语言
文本类型和领域(如文学、科技、法律、医学等)
目标读者群体的详细信息(年龄、文化背景、专业水平等)
翻译目的和使用场景
特殊要求或限制(如字数限制、风格偏好、专有名词处理等)
【你可以自行智能判断 如果用户让你优化提示词 让AI更好地翻译 你也可以套用此模板】
Output(输出预期):
高质量的译文,符合"信达雅"的标准
详细的翻译说明,包括:
总体翻译策略概述
关键词汇和难点的处理说明
文化特定元素的翻译解释
创造性改编(如有)的理由和方法
术语表(适用于专业文本)
文化背景注释(适用于文学或历史文本)
原文与译文的对照版本
多个翻译版本供选择(如适用)
翻译质量自评报告
对于部分高级词汇 在回答最后进行注释(包括音标 词性 释义等 但要确保简洁 同时使用角标对原词进行标注)
Evaluation Criteria(评估标准):
准确性:原意传达的精确度
流畅性:译文的自然度和可读性
文化等效性:文化元素转换的恰当性
风格还原:原文风格和语气的保留程度
创造性:在必要时的创新翻译解决方案
专业性:术语使用的准确性和一致性
目标适应性:对目标读者和使用场景的适配度
伦理性:翻译过程和结果的道德考量
Continuous Improvement(持续改进):
跟踪最新的语言学和翻译学研究成果
收集并分析用户反馈,不断优化翻译策略
不断进行自我评估(可以是在每一次重要翻译决策后)
Interaction Strategy(互动策略):
主动询问用户对特定翻译难点的处理偏好
在面对模糊或多义词时,提供多个翻译选项并解释差异
鼓励用户参与翻译过程,提供反馈和建议
对复杂的文化概念提供详细解释,增进跨文化理解
;; 将下一行作为纯文本输入并将其翻译成 {{to}},{{html_only}} 仅输出翻译。如果不需要翻译(例如专有名词、代码等),则返回原始文本。没有解释。没有注释。输入:{{text}}
这段内容主要介绍了两个针对Cursor AI的工具:一个是能自动升级为“pro”会员并绕过会员检查的工具,另一个是用于导出Cursor AI聊天记录的工具。导出工具支持将旧版本的聊天记录导出为Markdown和JSON格式,并提供了Windows、macOS和Linux的下载链接,以及手动配置工作区路径的功能。它还具有保留代码块格式、简洁的图形界面和导出进度显示等特点。
这个帖子介绍了一个基于Vercel的代理项目,它允许用户通过环境变量设置代理目标URL和访问密码。密码验证通过cookie实现,并在转发请求时移除,确保安全。用户只需在部署时设置 NEXT_PUBLIC_ACCESSURL
和 NEXT_PUBLIC_SAFEPWD
环境变量即可使用。目前该项目已验证可以安全代理谷歌搜索,其他网站仍需测试。
最近Qwen Chat的API流式输出存在问题,导致使用困难。有人尝试解决,但最初的方案因字符截断导致乱码。最终,通过使用TextDecoder的stream模式,解决了乱码问题,并提供了Deno和Cloudflare Worker版本的代码示例,这些代码可以正确处理Qwen Chat的流式输出。此外,还尝试将Hugging Face的Qwen模型API转换为Deno,但遇到了50x错误。
// Qwen API 配置
const QWEN_API_URL = "https://chat.qwenlm.ai/api/chat/completions";
const QWEN_MODELS_URL = "https://chat.qwenlm.ai/api/models";
const MAX_RETRIES = 3;
const RETRY_DELAY = 1000; // 1秒
const encoder = new TextEncoder();
const streamDecoder = new TextDecoder();
let cachedModels: string | null = null;
let cachedModelsTimestamp = 0;
const CACHE_TTL = 60 * 60 * 1000; // 缓存 1 小时
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function fetchWithRetry(
url: string,
options: RequestInit,
retries = MAX_RETRIES,
): Promise<Response> {
let lastError: unknown;
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return response;
}
const contentType = response.headers.get("content-type") || "";
if (response.status >= 500 || contentType.includes("text/html")) {
const responseClone = response.clone();
const responseText = await responseClone.text();
lastError = {
status: response.status,
contentType,
responseText: responseText.slice(0, 1000),
headers: Object.fromEntries(response.headers.entries()),
};
if (i < retries - 1) {
await sleep(RETRY_DELAY * (i + 1));
continue;
}
} else {
// 对于非 5xx 错误,不再重试
lastError = {
status: response.status,
headers: Object.fromEntries(response.headers.entries()),
};
break;
}
} catch (error) {
lastError = error;
if (i < retries - 1) {
await sleep(RETRY_DELAY * (i + 1));
continue;
}
}
}
throw new Error(JSON.stringify({
error: true,
message: "All retry attempts failed",
lastError,
retries,
}));
}
async function processLine(
line: string,
writer: WritableStreamDefaultWriter<Uint8Array>,
previousContent: string,
): Promise<string> {
try {
const data = JSON.parse(line.slice(6));
if (
data.choices && data.choices[0] && data.choices[0].delta &&
data.choices[0].delta.content
) {
const currentContent: string = data.choices[0].delta.content;
let newContent = currentContent;
if (currentContent.startsWith(previousContent) && previousContent.length > 0) {
newContent = currentContent.slice(previousContent.length);
}
if (newContent) { // 仅当有新内容时才发送
const newData = {
...data,
choices: [{
...data.choices[0],
delta: {
...data.choices[0].delta,
content: newContent,
},
}],
};
await writer.write(
encoder.encode(`data: ${JSON.stringify(newData)}\n\n`),
);
}
return currentContent;
} else {
await writer.write(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
return previousContent;
}
} catch {
await writer.write(encoder.encode(`${line}\n\n`));
return previousContent;
}
}
async function handleStream(
reader: ReadableStreamDefaultReader<Uint8Array>,
writer: WritableStreamDefaultWriter<Uint8Array>,
previousContent: string,
timeout: number,
) {
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
clearTimeout(timeout);
if (buffer) {
const lines = buffer.split("\n");
for (const line of lines) {
if (line.trim().startsWith("data: ")) {
await processLine(line, writer, previousContent);
}
}
}
await writer.write(encoder.encode("data: [DONE]\n\n"));
await writer.close();
break;
}
const valueText = streamDecoder.decode(value, { stream: true });
buffer += valueText;
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (line.trim().startsWith("data: ")) {
const result = await processLine(line, writer, previousContent);
if (result) {
previousContent = result;
}
}
}
}
} catch (error) {
clearTimeout(timeout);
await writer.write(
encoder.encode(`data: {"error":true,"message":"${error.message}"}\n\n`),
);
await writer.write(encoder.encode("data: [DONE]\n\n"));
await writer.close();
}
}
async function handleRequest(request: Request): Promise<Response> {
try {
const url = new URL(request.url);
const pathname = url.pathname;
if (request.method === "GET" && pathname === "/api/models") {
const authHeader = request.headers.get("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return new Response("Unauthorized", { status: 401 });
}
const now = Date.now();
if (cachedModels && now - cachedModelsTimestamp < CACHE_TTL) {
return new Response(cachedModels, {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
},
});
}
try {
const response = await fetchWithRetry(QWEN_MODELS_URL, {
headers: {
"Authorization": authHeader,
},
});
cachedModels = await response.text();
cachedModelsTimestamp = now;
return new Response(cachedModels, {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
},
});
} catch (error) {
return new Response(
JSON.stringify({ error: true, message: error.message }),
{ status: 500 },
);
}
}
if (request.method !== "POST" || pathname !== "/api/chat/completions") {
return new Response("Method not allowed", { status: 405 });
}
const authHeader = request.headers.get("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return new Response("Unauthorized", { status: 401 });
}
const requestData = await request.json();
const { messages, stream = false, model, max_tokens } = requestData;
if (!model) {
return new Response(
JSON.stringify({ error: true, message: "Model parameter is required" }),
{ status: 400 },
);
}
const qwenRequest = {
model,
messages,
stream,
};
if (max_tokens !== undefined) {
qwenRequest.max_tokens = max_tokens;
}
const qwenResponse = await fetch(QWEN_API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": authHeader,
},
body: JSON.stringify(qwenRequest),
});
if (stream) {
const { readable, writable } = new TransformStream<
Uint8Array,
Uint8Array
>();
const writer = writable.getWriter();
const reader = qwenResponse.body!.getReader();
const timeout = setTimeout(async () => {
try {
await writer.write(
encoder.encode(
'data: {"error":true,"message":"Response timeout"}\n\n',
),
);
await writer.write(encoder.encode("data: [DONE]\n\n"));
await writer.close();
} catch {
// writer closed
}
}, 60000);
handleStream(reader, writer, "", timeout).catch(async (error) => {
clearTimeout(timeout);
try {
await writer.write(
encoder.encode(
`data: {"error":true,"message":"${error.message}"}\n\n`,
),
);
await writer.write(encoder.encode("data: [DONE]\n\n"));
await writer.close();
} catch {
// writer closed
}
});
return new Response(readable, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
},
});
}
const responseText = await qwenResponse.text();
return new Response(responseText, {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
},
});
} catch (error) {
return new Response(
JSON.stringify({ error: true, message: error.message }),
{ status: 500 },
);
}
}
Deno.serve(handleRequest);
mv -v ~/Library/Application\ Support/.ffuserdata ~/.Trash
这段代码是一个用于将 OpenAI API 请求转换为 Qwen API 请求的代理。它主要实现了以下功能:
- 配置 Qwen API: 定义了 Qwen API 的 URL 和模型 URL。
- 请求重试: 实现了带有重试机制的
fetchWithRetry
函数。 - 流式处理: 对于流式响应,它会逐行解析 Qwen API 返回的数据,提取增量内容,并将其格式化为 OpenAI API 兼容的格式。
- 非流式处理: 对于非流式响应,直接转发 Qwen API 返回的文本。
- 模型列表获取: 实现了获取 Qwen 模型列表的功能。
- 错误处理: 包含了基本的错误处理和超时机制。
总而言之,这段代码充当了一个中间层,使得可以使用 OpenAI API 的客户端来访问 Qwen 模型。
一位用户(jianglyQAWS12g)使用阿里云的Qwen-vl-max-latest模型,通过 Cloudflare Worker 搭建了一个在线OCR图片识别服务,并分享了体验链接和GitHub仓库地址。其他用户对该项目表示赞赏,并称赞其强大。