mirror of
https://github.com/7836246/cursor2api.git
synced 2026-06-08 02:13:04 +08:00
Merge branch '7836246:main' into main
This commit is contained in:
@@ -27,6 +27,19 @@ timeout: 120
|
||||
# Cursor 使用的模型
|
||||
cursor_model: "anthropic/claude-sonnet-4.6"
|
||||
|
||||
# ==================== 自动续写配置 ====================
|
||||
# 当模型输出被截断时,自动发起续写请求的最大次数
|
||||
# 设为 0 可完全禁用自动续写(由用户在对话中手动续写)
|
||||
# 环境变量: MAX_AUTO_CONTINUE=3
|
||||
max_auto_continue: 3
|
||||
|
||||
# ==================== 历史消息条数硬限制 ====================
|
||||
# 输入消息条数上限,超出时删除最早的消息(保留工具 few-shot 示例)
|
||||
# 防止超长对话(800+ 条)导致请求体积过大、响应变慢
|
||||
# 设为 -1 不限制消息条数
|
||||
# 环境变量: MAX_HISTORY_MESSAGES=100
|
||||
max_history_messages: 100
|
||||
|
||||
# ==================== Thinking 开关(最高优先级) ====================
|
||||
# 控制是否向 Cursor 发送 thinking 请求,优先级高于客户端传入的 thinking 参数
|
||||
# 设为 true: 强制启用 thinking(即使客户端没请求也注入)
|
||||
|
||||
@@ -36,6 +36,10 @@ services:
|
||||
# - COMPRESSION_ENABLED=true
|
||||
# - COMPRESSION_LEVEL=2
|
||||
|
||||
# ── 自动续写 & 历史消息限制 ──
|
||||
# - MAX_AUTO_CONTINUE=3 # 截断后自动续写次数,0=禁用
|
||||
# - MAX_HISTORY_MESSAGES=100 # 历史消息条数上限,-1=不限制
|
||||
|
||||
# ── 日志持久化 ──
|
||||
# - LOG_FILE_ENABLED=true
|
||||
# - LOG_DIR=./logs
|
||||
|
||||
@@ -12,6 +12,8 @@ export function getConfig(): AppConfig {
|
||||
port: 3010,
|
||||
timeout: 120,
|
||||
cursorModel: 'anthropic/claude-sonnet-4.6',
|
||||
maxAutoContinue: 3,
|
||||
maxHistoryMessages: 100,
|
||||
fingerprint: {
|
||||
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
|
||||
},
|
||||
@@ -26,6 +28,8 @@ export function getConfig(): AppConfig {
|
||||
if (yaml.timeout) config.timeout = yaml.timeout;
|
||||
if (yaml.proxy) config.proxy = yaml.proxy;
|
||||
if (yaml.cursor_model) config.cursorModel = yaml.cursor_model;
|
||||
if (typeof yaml.max_auto_continue === 'number') config.maxAutoContinue = yaml.max_auto_continue;
|
||||
if (typeof yaml.max_history_messages === 'number') config.maxHistoryMessages = yaml.max_history_messages;
|
||||
if (yaml.fingerprint) {
|
||||
if (yaml.fingerprint.user_agent) config.fingerprint.userAgent = yaml.fingerprint.user_agent;
|
||||
}
|
||||
@@ -90,6 +94,8 @@ export function getConfig(): AppConfig {
|
||||
if (process.env.TIMEOUT) config.timeout = parseInt(process.env.TIMEOUT);
|
||||
if (process.env.PROXY) config.proxy = process.env.PROXY;
|
||||
if (process.env.CURSOR_MODEL) config.cursorModel = process.env.CURSOR_MODEL;
|
||||
if (process.env.MAX_AUTO_CONTINUE !== undefined) config.maxAutoContinue = parseInt(process.env.MAX_AUTO_CONTINUE);
|
||||
if (process.env.MAX_HISTORY_MESSAGES !== undefined) config.maxHistoryMessages = parseInt(process.env.MAX_HISTORY_MESSAGES);
|
||||
if (process.env.AUTH_TOKEN) {
|
||||
config.authTokens = process.env.AUTH_TOKEN.split(',').map(s => s.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
@@ -399,6 +399,19 @@ export async function convertToCursorRequest(req: AnthropicRequest): Promise<Cur
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 历史消息条数硬限制
|
||||
// 超出 max_history_messages 时,删除最早的消息(保留 few-shot 示例)
|
||||
const maxHistoryMessages = config.maxHistoryMessages;
|
||||
if (maxHistoryMessages >= 0) {
|
||||
const fewShotOffset = hasTools ? 2 : 0; // 工具模式有2条 few-shot 消息需跳过
|
||||
const userMessages = messages.length - fewShotOffset;
|
||||
if (userMessages > maxHistoryMessages) {
|
||||
const toRemove = userMessages - maxHistoryMessages;
|
||||
messages.splice(fewShotOffset, toRemove);
|
||||
console.log(`[Converter] 历史消息裁剪: ${userMessages} → ${maxHistoryMessages} 条 (移除了最早的 ${toRemove} 条)`);
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 渐进式历史压缩(智能压缩,不破坏结构)
|
||||
// 可通过 config.yaml 的 compression 配置控制开关和级别
|
||||
// 策略:保留最近 KEEP_RECENT 条消息完整,对早期消息进行结构感知压缩
|
||||
|
||||
@@ -693,12 +693,12 @@ export async function autoContinueCursorToolResponseStream(
|
||||
hasTools: boolean,
|
||||
): Promise<string> {
|
||||
let fullResponse = initialResponse;
|
||||
const MAX_AUTO_CONTINUE = 3;
|
||||
const MAX_AUTO_CONTINUE = getConfig().maxAutoContinue;
|
||||
let continueCount = 0;
|
||||
let consecutiveSmallAdds = 0;
|
||||
const originalMessages = [...cursorReq.messages];
|
||||
|
||||
while (shouldAutoContinueTruncatedToolResponse(fullResponse, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
while (MAX_AUTO_CONTINUE > 0 && shouldAutoContinueTruncatedToolResponse(fullResponse, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
continueCount++;
|
||||
|
||||
const anchorLength = Math.min(300, fullResponse.length);
|
||||
@@ -760,12 +760,12 @@ export async function autoContinueCursorToolResponseFull(
|
||||
hasTools: boolean,
|
||||
): Promise<string> {
|
||||
let fullText = initialText;
|
||||
const MAX_AUTO_CONTINUE = 3;
|
||||
const MAX_AUTO_CONTINUE = getConfig().maxAutoContinue;
|
||||
let continueCount = 0;
|
||||
let consecutiveSmallAdds = 0;
|
||||
const originalMessages = [...cursorReq.messages];
|
||||
|
||||
while (shouldAutoContinueTruncatedToolResponse(fullText, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
while (MAX_AUTO_CONTINUE > 0 && shouldAutoContinueTruncatedToolResponse(fullText, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
continueCount++;
|
||||
|
||||
const anchorLength = Math.min(300, fullText.length);
|
||||
@@ -1279,14 +1279,14 @@ async function handleStream(res: Response, cursorReq: CursorChatRequest, body: A
|
||||
// 流完成后,处理完整响应
|
||||
// ★ 内部截断续写:如果模型输出过长被截断(常见于写大文件),Proxy 内部分段续写,然后拼接成完整响应
|
||||
// 这样可以确保工具调用(如 Write)不会横跨两次 API 响应而退化为纯文本
|
||||
const MAX_AUTO_CONTINUE = 3;
|
||||
const MAX_AUTO_CONTINUE = getConfig().maxAutoContinue;
|
||||
let continueCount = 0;
|
||||
let consecutiveSmallAdds = 0; // 连续小增量计数
|
||||
|
||||
// 保存原始请求的消息快照(不含续写追加的消息)
|
||||
const originalMessages = [...activeCursorReq.messages];
|
||||
|
||||
while (shouldAutoContinueTruncatedToolResponse(fullResponse, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
while (MAX_AUTO_CONTINUE > 0 && shouldAutoContinueTruncatedToolResponse(fullResponse, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
continueCount++;
|
||||
const prevLength = fullResponse.length;
|
||||
log.warn('Handler', 'continuation', `内部检测到截断 (${fullResponse.length} chars),隐式续写 (第${continueCount}次)`);
|
||||
@@ -1671,12 +1671,12 @@ async function handleNonStream(res: Response, cursorReq: CursorChatRequest, body
|
||||
// ★ 内部截断续写(与流式路径对齐)
|
||||
// Claude CLI 使用非流式模式时,写大文件最容易被截断
|
||||
// 在 proxy 内部完成续写,确保工具调用参数完整
|
||||
const MAX_AUTO_CONTINUE = 3;
|
||||
const MAX_AUTO_CONTINUE = getConfig().maxAutoContinue;
|
||||
let continueCount = 0;
|
||||
let consecutiveSmallAdds = 0; // 连续小增量计数
|
||||
const originalMessages = [...activeCursorReq.messages];
|
||||
|
||||
while (shouldAutoContinueTruncatedToolResponse(fullText, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
while (MAX_AUTO_CONTINUE > 0 && shouldAutoContinueTruncatedToolResponse(fullText, hasTools) && continueCount < MAX_AUTO_CONTINUE) {
|
||||
continueCount++;
|
||||
const prevLength = fullText.length;
|
||||
log.warn('Handler', 'continuation', `非流式检测到截断 (${fullText.length} chars),隐式续写 (第${continueCount}次)`);
|
||||
|
||||
@@ -106,6 +106,8 @@ export interface AppConfig {
|
||||
proxy?: string;
|
||||
cursorModel: string;
|
||||
authTokens?: string[]; // API 鉴权 token 列表,为空则不鉴权
|
||||
maxAutoContinue: number; // 自动续写最大次数,默认 3,设 0 禁用
|
||||
maxHistoryMessages: number; // 历史消息条数硬限制,默认 100,-1 不限制
|
||||
vision?: {
|
||||
enabled: boolean;
|
||||
mode: 'ocr' | 'api';
|
||||
|
||||
Reference in New Issue
Block a user