@vibe-lark/larkpal 0.1.23 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +396 -72
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -221,7 +221,7 @@ function ensureLarkCliConfig() {
|
|
|
221
221
|
* 首次启动时检测并生成 ~/.claude/settings.json 和 ~/.claude/CLAUDE.md,
|
|
222
222
|
* 以及确保会话工作目录结构完整。
|
|
223
223
|
*/
|
|
224
|
-
const logger$
|
|
224
|
+
const logger$8 = larkLogger("config/defaults");
|
|
225
225
|
const DEFAULT_SETTINGS = {
|
|
226
226
|
permissions: {
|
|
227
227
|
allow: [
|
|
@@ -344,13 +344,13 @@ async function ensureDefaults() {
|
|
|
344
344
|
const settingsPath = join(claudeDir, "settings.json");
|
|
345
345
|
const claudeMdPath = join(claudeDir, "CLAUDE.md");
|
|
346
346
|
await mkdir(claudeDir, { recursive: true });
|
|
347
|
-
logger$
|
|
347
|
+
logger$8.info("确保 ~/.claude 目录存在", { path: claudeDir });
|
|
348
348
|
await writeFile(settingsPath, JSON.stringify(DEFAULT_SETTINGS, null, 2), "utf-8");
|
|
349
|
-
logger$
|
|
350
|
-
if (await fileExists(claudeMdPath)) logger$
|
|
349
|
+
logger$8.info("settings.json 已同步(强制覆盖)", { path: settingsPath });
|
|
350
|
+
if (await fileExists(claudeMdPath)) logger$8.info("CLAUDE.md 已存在,跳过生成", { path: claudeMdPath });
|
|
351
351
|
else {
|
|
352
352
|
await writeFile(claudeMdPath, DEFAULT_CLAUDE_MD, "utf-8");
|
|
353
|
-
logger$
|
|
353
|
+
logger$8.info("已生成默认 CLAUDE.md", { path: claudeMdPath });
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
//#endregion
|
|
@@ -435,7 +435,10 @@ var CCStreamParser = class extends EventEmitter {
|
|
|
435
435
|
this.handleResult(msg);
|
|
436
436
|
break;
|
|
437
437
|
case "system":
|
|
438
|
-
log$28.info("收到 system 消息", {
|
|
438
|
+
log$28.info("收到 system 消息", {
|
|
439
|
+
subtype: msg.subtype,
|
|
440
|
+
detail: JSON.stringify(msg).slice(0, 500)
|
|
441
|
+
});
|
|
439
442
|
this.emit("system", msg);
|
|
440
443
|
break;
|
|
441
444
|
default:
|
|
@@ -1654,7 +1657,7 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
|
|
|
1654
1657
|
* 群聊 → /workspace/chats/group_{chat_id}/
|
|
1655
1658
|
* 话题 → /workspace/chats/group_{chat_id}/topics/{thread_id}/
|
|
1656
1659
|
*/
|
|
1657
|
-
const logger$
|
|
1660
|
+
const logger$7 = larkLogger("routing/session-router");
|
|
1658
1661
|
const SESSION_OUTPUT_RULES = [
|
|
1659
1662
|
`## 内容输出优先级`,
|
|
1660
1663
|
"",
|
|
@@ -1693,7 +1696,7 @@ var SessionRouter = class {
|
|
|
1693
1696
|
cwd: path.join(this.workspaceRoot, "chats", `p2p_${safeId}`),
|
|
1694
1697
|
type: "p2p"
|
|
1695
1698
|
};
|
|
1696
|
-
logger$
|
|
1699
|
+
logger$7.debug("解析私聊路由", {
|
|
1697
1700
|
chatId,
|
|
1698
1701
|
userId: safeId,
|
|
1699
1702
|
sessionId: route.sessionId,
|
|
@@ -1709,7 +1712,7 @@ var SessionRouter = class {
|
|
|
1709
1712
|
cwd: path.join(groupCwd, "topics", threadId),
|
|
1710
1713
|
type: "topic"
|
|
1711
1714
|
};
|
|
1712
|
-
logger$
|
|
1715
|
+
logger$7.debug("解析话题路由", {
|
|
1713
1716
|
chatId,
|
|
1714
1717
|
threadId,
|
|
1715
1718
|
sessionId: route.sessionId,
|
|
@@ -1722,7 +1725,7 @@ var SessionRouter = class {
|
|
|
1722
1725
|
cwd: groupCwd,
|
|
1723
1726
|
type: "group"
|
|
1724
1727
|
};
|
|
1725
|
-
logger$
|
|
1728
|
+
logger$7.debug("解析群聊路由", {
|
|
1726
1729
|
chatId,
|
|
1727
1730
|
sessionId: route.sessionId,
|
|
1728
1731
|
cwd: route.cwd
|
|
@@ -1736,7 +1739,7 @@ var SessionRouter = class {
|
|
|
1736
1739
|
* 此方法是幂等的,多次调用不会报错也不会覆盖已有文件。
|
|
1737
1740
|
*/
|
|
1738
1741
|
async ensureSessionDirectory(route) {
|
|
1739
|
-
logger$
|
|
1742
|
+
logger$7.info("确保会话目录存在", {
|
|
1740
1743
|
sessionId: route.sessionId,
|
|
1741
1744
|
cwd: route.cwd
|
|
1742
1745
|
});
|
|
@@ -1746,7 +1749,7 @@ var SessionRouter = class {
|
|
|
1746
1749
|
const claudeMdPath = path.join(route.cwd, "CLAUDE.md");
|
|
1747
1750
|
if (!existsSync$1(claudeMdPath)) {
|
|
1748
1751
|
await writeFile$1(claudeMdPath, this.generateClaudeMd(route), "utf-8");
|
|
1749
|
-
logger$
|
|
1752
|
+
logger$7.info("创建会话级 CLAUDE.md", {
|
|
1750
1753
|
path: claudeMdPath,
|
|
1751
1754
|
type: route.type
|
|
1752
1755
|
});
|
|
@@ -1810,7 +1813,7 @@ var SessionRouter = class {
|
|
|
1810
1813
|
*
|
|
1811
1814
|
* 内部通过 taskStore 维护所有任务的生命周期状态。
|
|
1812
1815
|
*/
|
|
1813
|
-
const logger$
|
|
1816
|
+
const logger$6 = larkLogger("gateway/execute");
|
|
1814
1817
|
/** 全局任务存储:taskId → TaskInfo */
|
|
1815
1818
|
const taskStore = /* @__PURE__ */ new Map();
|
|
1816
1819
|
/**
|
|
@@ -1844,7 +1847,7 @@ function createExecuteRouter(processManager) {
|
|
|
1844
1847
|
*/
|
|
1845
1848
|
async function handleExecute(req, res, processManager) {
|
|
1846
1849
|
const body = req.body;
|
|
1847
|
-
logger$
|
|
1850
|
+
logger$6.info("收到执行请求", {
|
|
1848
1851
|
session_id: body.session_id,
|
|
1849
1852
|
cwd: body.cwd,
|
|
1850
1853
|
prompt: body.prompt?.slice(0, 200),
|
|
@@ -1853,7 +1856,7 @@ async function handleExecute(req, res, processManager) {
|
|
|
1853
1856
|
max_budget_usd: body.max_budget_usd
|
|
1854
1857
|
});
|
|
1855
1858
|
if (!body.session_id || !body.cwd || !body.prompt) {
|
|
1856
|
-
logger$
|
|
1859
|
+
logger$6.warn("执行请求参数缺失", {
|
|
1857
1860
|
hasSessionId: !!body.session_id,
|
|
1858
1861
|
hasCwd: !!body.cwd,
|
|
1859
1862
|
hasPrompt: !!body.prompt
|
|
@@ -1873,7 +1876,7 @@ async function handleExecute(req, res, processManager) {
|
|
|
1873
1876
|
createdAt: /* @__PURE__ */ new Date()
|
|
1874
1877
|
};
|
|
1875
1878
|
taskStore.set(taskId, taskInfo);
|
|
1876
|
-
logger$
|
|
1879
|
+
logger$6.info("任务已创建", {
|
|
1877
1880
|
taskId,
|
|
1878
1881
|
sessionId: body.session_id,
|
|
1879
1882
|
mode
|
|
@@ -1889,7 +1892,7 @@ async function handleExecute(req, res, processManager) {
|
|
|
1889
1892
|
const callbacks = createTaskCallbacks(taskId);
|
|
1890
1893
|
processManager.executePrompt(config, callbacks).catch((err) => {
|
|
1891
1894
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1892
|
-
logger$
|
|
1895
|
+
logger$6.error("异步任务执行异常", {
|
|
1893
1896
|
taskId,
|
|
1894
1897
|
error: errorMsg
|
|
1895
1898
|
});
|
|
@@ -1897,7 +1900,7 @@ async function handleExecute(req, res, processManager) {
|
|
|
1897
1900
|
taskInfo.error = errorMsg;
|
|
1898
1901
|
taskInfo.completedAt = /* @__PURE__ */ new Date();
|
|
1899
1902
|
});
|
|
1900
|
-
logger$
|
|
1903
|
+
logger$6.info("异步任务已启动,立即返回", { taskId });
|
|
1901
1904
|
res.status(202).json({
|
|
1902
1905
|
task_id: taskId,
|
|
1903
1906
|
status: "running"
|
|
@@ -1905,8 +1908,8 @@ async function handleExecute(req, res, processManager) {
|
|
|
1905
1908
|
return;
|
|
1906
1909
|
}
|
|
1907
1910
|
try {
|
|
1908
|
-
const result = await executeAndWaitResult(taskId, config, processManager);
|
|
1909
|
-
logger$
|
|
1911
|
+
const result = await executeAndWaitResult$1(taskId, config, processManager);
|
|
1912
|
+
logger$6.info("同步任务执行完成", {
|
|
1910
1913
|
taskId,
|
|
1911
1914
|
resultSubtype: result?.subtype
|
|
1912
1915
|
});
|
|
@@ -1917,7 +1920,7 @@ async function handleExecute(req, res, processManager) {
|
|
|
1917
1920
|
});
|
|
1918
1921
|
} catch (err) {
|
|
1919
1922
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1920
|
-
logger$
|
|
1923
|
+
logger$6.error("同步任务执行失败", {
|
|
1921
1924
|
taskId,
|
|
1922
1925
|
error: errorMsg
|
|
1923
1926
|
});
|
|
@@ -1935,12 +1938,12 @@ async function handleExecute(req, res, processManager) {
|
|
|
1935
1938
|
*/
|
|
1936
1939
|
async function handleBatchExecute(req, res, processManager) {
|
|
1937
1940
|
const body = req.body;
|
|
1938
|
-
logger$
|
|
1941
|
+
logger$6.info("收到批量执行请求", {
|
|
1939
1942
|
taskCount: body.tasks?.length,
|
|
1940
1943
|
concurrency: body.concurrency
|
|
1941
1944
|
});
|
|
1942
1945
|
if (!body.tasks || !Array.isArray(body.tasks) || body.tasks.length === 0) {
|
|
1943
|
-
logger$
|
|
1946
|
+
logger$6.warn("批量执行请求参数缺失: tasks 为空或格式错误");
|
|
1944
1947
|
res.status(400).json({
|
|
1945
1948
|
error: "Bad Request",
|
|
1946
1949
|
message: "Missing or empty required field: tasks"
|
|
@@ -1950,7 +1953,7 @@ async function handleBatchExecute(req, res, processManager) {
|
|
|
1950
1953
|
for (let i = 0; i < body.tasks.length; i++) {
|
|
1951
1954
|
const task = body.tasks[i];
|
|
1952
1955
|
if (!task?.session_id || !task?.cwd || !task?.prompt) {
|
|
1953
|
-
logger$
|
|
1956
|
+
logger$6.warn("批量执行子任务参数缺失", { index: i });
|
|
1954
1957
|
res.status(400).json({
|
|
1955
1958
|
error: "Bad Request",
|
|
1956
1959
|
message: `Task at index ${i} is missing required fields: session_id, cwd, prompt`
|
|
@@ -1981,7 +1984,7 @@ async function handleBatchExecute(req, res, processManager) {
|
|
|
1981
1984
|
}
|
|
1982
1985
|
};
|
|
1983
1986
|
});
|
|
1984
|
-
logger$
|
|
1987
|
+
logger$6.info("批量任务已创建", {
|
|
1985
1988
|
batchId,
|
|
1986
1989
|
taskIds,
|
|
1987
1990
|
concurrency
|
|
@@ -1998,17 +2001,17 @@ async function handleBatchExecute(req, res, processManager) {
|
|
|
1998
2001
|
*/
|
|
1999
2002
|
function handleGetTask(req, res) {
|
|
2000
2003
|
const taskId = req.params.taskId;
|
|
2001
|
-
logger$
|
|
2004
|
+
logger$6.info("查询任务状态", { taskId });
|
|
2002
2005
|
const taskInfo = taskStore.get(taskId);
|
|
2003
2006
|
if (!taskInfo) {
|
|
2004
|
-
logger$
|
|
2007
|
+
logger$6.warn("任务不存在", { taskId });
|
|
2005
2008
|
res.status(404).json({
|
|
2006
2009
|
error: "Not Found",
|
|
2007
2010
|
message: `Task ${taskId} not found`
|
|
2008
2011
|
});
|
|
2009
2012
|
return;
|
|
2010
2013
|
}
|
|
2011
|
-
logger$
|
|
2014
|
+
logger$6.info("返回任务状态", {
|
|
2012
2015
|
taskId,
|
|
2013
2016
|
status: taskInfo.status
|
|
2014
2017
|
});
|
|
@@ -2025,10 +2028,10 @@ function handleGetTask(req, res) {
|
|
|
2025
2028
|
*/
|
|
2026
2029
|
async function handleCancelTask(req, res, processManager) {
|
|
2027
2030
|
const taskId = req.params.taskId;
|
|
2028
|
-
logger$
|
|
2031
|
+
logger$6.info("收到取消任务请求", { taskId });
|
|
2029
2032
|
const taskInfo = taskStore.get(taskId);
|
|
2030
2033
|
if (!taskInfo) {
|
|
2031
|
-
logger$
|
|
2034
|
+
logger$6.warn("取消失败:任务不存在", { taskId });
|
|
2032
2035
|
res.status(404).json({
|
|
2033
2036
|
error: "Not Found",
|
|
2034
2037
|
message: `Task ${taskId} not found`
|
|
@@ -2036,7 +2039,7 @@ async function handleCancelTask(req, res, processManager) {
|
|
|
2036
2039
|
return;
|
|
2037
2040
|
}
|
|
2038
2041
|
if (taskInfo.status !== "running") {
|
|
2039
|
-
logger$
|
|
2042
|
+
logger$6.warn("取消失败:任务不在运行状态", {
|
|
2040
2043
|
taskId,
|
|
2041
2044
|
status: taskInfo.status
|
|
2042
2045
|
});
|
|
@@ -2050,7 +2053,7 @@ async function handleCancelTask(req, res, processManager) {
|
|
|
2050
2053
|
await processManager.stopProcess(taskInfo.sessionId);
|
|
2051
2054
|
taskInfo.status = "cancelled";
|
|
2052
2055
|
taskInfo.completedAt = /* @__PURE__ */ new Date();
|
|
2053
|
-
logger$
|
|
2056
|
+
logger$6.info("任务已取消", {
|
|
2054
2057
|
taskId,
|
|
2055
2058
|
sessionId: taskInfo.sessionId
|
|
2056
2059
|
});
|
|
@@ -2060,7 +2063,7 @@ async function handleCancelTask(req, res, processManager) {
|
|
|
2060
2063
|
});
|
|
2061
2064
|
} catch (err) {
|
|
2062
2065
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2063
|
-
logger$
|
|
2066
|
+
logger$6.error("取消任务时发生错误", {
|
|
2064
2067
|
taskId,
|
|
2065
2068
|
error: errorMsg
|
|
2066
2069
|
});
|
|
@@ -2083,7 +2086,7 @@ function createTaskCallbacks(taskId) {
|
|
|
2083
2086
|
taskInfo.result = result;
|
|
2084
2087
|
taskInfo.status = result.isError ? "failed" : "completed";
|
|
2085
2088
|
taskInfo.completedAt = /* @__PURE__ */ new Date();
|
|
2086
|
-
logger$
|
|
2089
|
+
logger$6.info("任务收到结果", {
|
|
2087
2090
|
taskId,
|
|
2088
2091
|
status: taskInfo.status,
|
|
2089
2092
|
subtype: result.subtype,
|
|
@@ -2097,7 +2100,7 @@ function createTaskCallbacks(taskId) {
|
|
|
2097
2100
|
taskInfo.status = "failed";
|
|
2098
2101
|
taskInfo.error = error.message;
|
|
2099
2102
|
taskInfo.completedAt = /* @__PURE__ */ new Date();
|
|
2100
|
-
logger$
|
|
2103
|
+
logger$6.error("任务执行出错", {
|
|
2101
2104
|
taskId,
|
|
2102
2105
|
error: error.message
|
|
2103
2106
|
});
|
|
@@ -2109,7 +2112,7 @@ function createTaskCallbacks(taskId) {
|
|
|
2109
2112
|
*
|
|
2110
2113
|
* 通过 Promise 包装回调机制,在收到 onResult 或 onError 时 resolve/reject。
|
|
2111
2114
|
*/
|
|
2112
|
-
function executeAndWaitResult(taskId, config, processManager) {
|
|
2115
|
+
function executeAndWaitResult$1(taskId, config, processManager) {
|
|
2113
2116
|
return new Promise((resolve, reject) => {
|
|
2114
2117
|
processManager.executePrompt(config, {
|
|
2115
2118
|
onResult: (result) => {
|
|
@@ -2119,7 +2122,7 @@ function executeAndWaitResult(taskId, config, processManager) {
|
|
|
2119
2122
|
taskInfo.status = result.isError ? "failed" : "completed";
|
|
2120
2123
|
taskInfo.completedAt = /* @__PURE__ */ new Date();
|
|
2121
2124
|
}
|
|
2122
|
-
logger$
|
|
2125
|
+
logger$6.info("同步任务收到结果", {
|
|
2123
2126
|
taskId,
|
|
2124
2127
|
subtype: result.subtype,
|
|
2125
2128
|
durationMs: result.durationMs,
|
|
@@ -2134,7 +2137,7 @@ function executeAndWaitResult(taskId, config, processManager) {
|
|
|
2134
2137
|
taskInfo.error = error.message;
|
|
2135
2138
|
taskInfo.completedAt = /* @__PURE__ */ new Date();
|
|
2136
2139
|
}
|
|
2137
|
-
logger$
|
|
2140
|
+
logger$6.error("同步任务执行出错", {
|
|
2138
2141
|
taskId,
|
|
2139
2142
|
error: error.message
|
|
2140
2143
|
});
|
|
@@ -2142,7 +2145,7 @@ function executeAndWaitResult(taskId, config, processManager) {
|
|
|
2142
2145
|
}
|
|
2143
2146
|
}).catch((err) => {
|
|
2144
2147
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2145
|
-
logger$
|
|
2148
|
+
logger$6.error("同步任务 executePrompt 调用失败", {
|
|
2146
2149
|
taskId,
|
|
2147
2150
|
error: errorMsg
|
|
2148
2151
|
});
|
|
@@ -2162,7 +2165,7 @@ function executeAndWaitResult(taskId, config, processManager) {
|
|
|
2162
2165
|
* 使用简单的信号量模式控制并发数,逐个启动任务直到所有任务完成。
|
|
2163
2166
|
*/
|
|
2164
2167
|
async function runBatchTasks(taskConfigs, concurrency, processManager) {
|
|
2165
|
-
logger$
|
|
2168
|
+
logger$6.info("开始批量任务执行", {
|
|
2166
2169
|
totalTasks: taskConfigs.length,
|
|
2167
2170
|
concurrency
|
|
2168
2171
|
});
|
|
@@ -2174,7 +2177,7 @@ async function runBatchTasks(taskConfigs, concurrency, processManager) {
|
|
|
2174
2177
|
await processManager.executePrompt(config, callbacks);
|
|
2175
2178
|
} catch (err) {
|
|
2176
2179
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2177
|
-
logger$
|
|
2180
|
+
logger$6.error("批量子任务执行异常", {
|
|
2178
2181
|
taskId,
|
|
2179
2182
|
error: errorMsg
|
|
2180
2183
|
});
|
|
@@ -2191,7 +2194,311 @@ async function runBatchTasks(taskConfigs, concurrency, processManager) {
|
|
|
2191
2194
|
if (executing.size >= concurrency) await Promise.race(executing);
|
|
2192
2195
|
}
|
|
2193
2196
|
await Promise.all(executing);
|
|
2194
|
-
logger$
|
|
2197
|
+
logger$6.info("批量任务全部完成", { totalTasks: taskConfigs.length });
|
|
2198
|
+
}
|
|
2199
|
+
//#endregion
|
|
2200
|
+
//#region src/gateway/ai-extract-handler.ts
|
|
2201
|
+
/**
|
|
2202
|
+
* AI 数据提取路由 — 统一 AI 能力网关
|
|
2203
|
+
*
|
|
2204
|
+
* 提供 POST /api/ai/extract 接口,接收外部服务(如 rd-assistant-api)的结构化数据提取请求。
|
|
2205
|
+
* 内部通过 CC(Claude Code)RuntimeAdapter 执行 AI 提取任务。
|
|
2206
|
+
*
|
|
2207
|
+
* 支持两种模式:
|
|
2208
|
+
* - 同步模式(默认):等待 CC 完成后直接返回提取结果
|
|
2209
|
+
* - 异步模式:立即返回,完成后通过 X-Callback-Url 回调通知
|
|
2210
|
+
*
|
|
2211
|
+
* 鉴权:通过 X-Internal-Secret 头验证服务间调用身份
|
|
2212
|
+
*/
|
|
2213
|
+
const logger$5 = larkLogger("gateway/ai-extract");
|
|
2214
|
+
/** 内部通信密钥,从环境变量读取 */
|
|
2215
|
+
const INTERNAL_SECRET = process.env.LARKPAL_API_SECRET || "dev-internal-secret";
|
|
2216
|
+
/** CC 执行时的工作目录(使用临时目录,不需要真正的项目上下文) */
|
|
2217
|
+
const AI_EXTRACT_CWD = process.env.LARKPAL_AI_EXTRACT_CWD || "/tmp/larkpal-ai-extract";
|
|
2218
|
+
/**
|
|
2219
|
+
* 创建 AI 提取路由
|
|
2220
|
+
*
|
|
2221
|
+
* @param runtimeAdapter - CC 运行时适配器实例,用于执行 AI 提取 prompt
|
|
2222
|
+
*/
|
|
2223
|
+
function createAiExtractRouter(runtimeAdapter) {
|
|
2224
|
+
const router = Router();
|
|
2225
|
+
router.post("/api/ai/extract", (req, res) => {
|
|
2226
|
+
handleExtract(req, res, runtimeAdapter);
|
|
2227
|
+
});
|
|
2228
|
+
return router;
|
|
2229
|
+
}
|
|
2230
|
+
async function handleExtract(req, res, runtimeAdapter) {
|
|
2231
|
+
const secret = req.headers["x-internal-secret"];
|
|
2232
|
+
if (secret !== INTERNAL_SECRET) {
|
|
2233
|
+
logger$5.warn("AI 提取请求鉴权失败", { providedSecret: secret?.slice(0, 8) + "..." });
|
|
2234
|
+
res.status(401).json({
|
|
2235
|
+
code: 1,
|
|
2236
|
+
message: "鉴权失败"
|
|
2237
|
+
});
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
const callbackUrl = req.headers["x-callback-url"];
|
|
2241
|
+
const body = req.body;
|
|
2242
|
+
if (!body.taskId || !body.fileName || !body.fileBase64 || !body.moduleName) {
|
|
2243
|
+
logger$5.warn("AI 提取请求参数不完整", {
|
|
2244
|
+
hasTaskId: !!body.taskId,
|
|
2245
|
+
hasFileName: !!body.fileName,
|
|
2246
|
+
hasFileBase64: !!body.fileBase64,
|
|
2247
|
+
hasModuleName: !!body.moduleName
|
|
2248
|
+
});
|
|
2249
|
+
res.status(400).json({
|
|
2250
|
+
code: 1,
|
|
2251
|
+
message: "缺少必要参数: taskId, fileName, fileBase64, moduleName"
|
|
2252
|
+
});
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
logger$5.info("收到 AI 提取请求", {
|
|
2256
|
+
taskId: body.taskId,
|
|
2257
|
+
fileName: body.fileName,
|
|
2258
|
+
moduleName: body.moduleName,
|
|
2259
|
+
extractionType: body.extractionType,
|
|
2260
|
+
indicatorCount: body.indicators?.length || 0,
|
|
2261
|
+
fileSize: body.fileBase64.length,
|
|
2262
|
+
hasCallback: !!callbackUrl
|
|
2263
|
+
});
|
|
2264
|
+
const prompt = buildExtractionPrompt(body);
|
|
2265
|
+
try {
|
|
2266
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
2267
|
+
const { join } = await import("node:path");
|
|
2268
|
+
await mkdir(AI_EXTRACT_CWD, { recursive: true });
|
|
2269
|
+
const fileExt = body.fileName.split(".").pop() || "bin";
|
|
2270
|
+
const tempFilePath = join(AI_EXTRACT_CWD, `${body.taskId}.${fileExt}`);
|
|
2271
|
+
const fileBuffer = Buffer.from(body.fileBase64, "base64");
|
|
2272
|
+
await writeFile(tempFilePath, fileBuffer);
|
|
2273
|
+
logger$5.info("临时文件已写入", {
|
|
2274
|
+
tempFilePath,
|
|
2275
|
+
sizeBytes: fileBuffer.length
|
|
2276
|
+
});
|
|
2277
|
+
const sessionId = `ai-extract-${body.taskId}`;
|
|
2278
|
+
const outputFilePath = join(AI_EXTRACT_CWD, `${body.taskId}-result.json`);
|
|
2279
|
+
const result = await executeAndWaitResult(runtimeAdapter, {
|
|
2280
|
+
sessionId,
|
|
2281
|
+
cwd: AI_EXTRACT_CWD,
|
|
2282
|
+
prompt,
|
|
2283
|
+
maxTurns: 10
|
|
2284
|
+
});
|
|
2285
|
+
let extractedData = [];
|
|
2286
|
+
try {
|
|
2287
|
+
const { readFile } = await import("node:fs/promises");
|
|
2288
|
+
const fileContent = await readFile(outputFilePath, "utf-8");
|
|
2289
|
+
logger$5.info("从结果文件读取提取数据", {
|
|
2290
|
+
outputFilePath,
|
|
2291
|
+
contentLength: fileContent.length
|
|
2292
|
+
});
|
|
2293
|
+
extractedData = parseExtractionResult(fileContent);
|
|
2294
|
+
} catch {
|
|
2295
|
+
logger$5.info("结果文件不存在,从 CC 文本输出解析");
|
|
2296
|
+
extractedData = parseExtractionResult(result);
|
|
2297
|
+
}
|
|
2298
|
+
try {
|
|
2299
|
+
const { unlink } = await import("node:fs/promises");
|
|
2300
|
+
await unlink(tempFilePath);
|
|
2301
|
+
await unlink(outputFilePath).catch(() => {});
|
|
2302
|
+
} catch {}
|
|
2303
|
+
logger$5.info("AI 提取完成", {
|
|
2304
|
+
taskId: body.taskId,
|
|
2305
|
+
recordCount: extractedData.length
|
|
2306
|
+
});
|
|
2307
|
+
if (callbackUrl) {
|
|
2308
|
+
res.status(200).json({
|
|
2309
|
+
code: 0,
|
|
2310
|
+
message: "accepted",
|
|
2311
|
+
mode: "async"
|
|
2312
|
+
});
|
|
2313
|
+
await sendCallback(callbackUrl, body.taskId, "success", extractedData);
|
|
2314
|
+
} else res.status(200).json({
|
|
2315
|
+
code: 0,
|
|
2316
|
+
message: "ok",
|
|
2317
|
+
mode: "sync",
|
|
2318
|
+
data: extractedData
|
|
2319
|
+
});
|
|
2320
|
+
} catch (err) {
|
|
2321
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2322
|
+
logger$5.error("AI 提取执行失败", {
|
|
2323
|
+
taskId: body.taskId,
|
|
2324
|
+
error: errorMsg
|
|
2325
|
+
});
|
|
2326
|
+
if (callbackUrl) {
|
|
2327
|
+
res.status(200).json({
|
|
2328
|
+
code: 0,
|
|
2329
|
+
message: "accepted",
|
|
2330
|
+
mode: "async"
|
|
2331
|
+
});
|
|
2332
|
+
await sendCallback(callbackUrl, body.taskId, "failed", void 0, errorMsg);
|
|
2333
|
+
} else res.status(500).json({
|
|
2334
|
+
code: 1,
|
|
2335
|
+
message: `提取失败: ${errorMsg}`
|
|
2336
|
+
});
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
/**
|
|
2340
|
+
* 构造用于数据提取的结构化 Prompt
|
|
2341
|
+
*/
|
|
2342
|
+
function buildExtractionPrompt(body) {
|
|
2343
|
+
const indicatorsList = (body.indicators || []).map((ind, i) => ` ${i + 1}. ${ind.name}${ind.unit ? ` (单位: ${ind.unit})` : ""}${ind.format ? ` [格式: ${ind.format}]` : ""}`).join("\n");
|
|
2344
|
+
const fileExt = body.fileName.split(".").pop() || "";
|
|
2345
|
+
return `你是一个精确的数据提取助手。请从提供的文件中提取结构化性能测试数据。
|
|
2346
|
+
|
|
2347
|
+
## 任务说明
|
|
2348
|
+
- 文件名: ${body.fileName}
|
|
2349
|
+
- 性能模块: ${body.moduleName}
|
|
2350
|
+
- 提取方式: ${body.extractionType}
|
|
2351
|
+
|
|
2352
|
+
## 需要提取的指标
|
|
2353
|
+
${indicatorsList}
|
|
2354
|
+
|
|
2355
|
+
## 文件路径
|
|
2356
|
+
文件已保存在当前目录: ./${body.taskId}.${fileExt}
|
|
2357
|
+
|
|
2358
|
+
## 输出要求
|
|
2359
|
+
请读取文件内容,提取出所有样品/配方的性能数据。
|
|
2360
|
+
|
|
2361
|
+
**必须以严格 JSON 格式输出**,不要包含任何其他文字。输出格式如下:
|
|
2362
|
+
\`\`\`json
|
|
2363
|
+
[
|
|
2364
|
+
{
|
|
2365
|
+
"formulaNo": "样品/配方编号(如果文件中有)",
|
|
2366
|
+
"indicators": {
|
|
2367
|
+
"指标名称1": 数值或字符串,
|
|
2368
|
+
"指标名称2": 数值或字符串
|
|
2369
|
+
},
|
|
2370
|
+
"testConditions": {
|
|
2371
|
+
"测试温度": "23°C",
|
|
2372
|
+
"测试标准": "相关标准号"
|
|
2373
|
+
},
|
|
2374
|
+
"confidence": 0.95,
|
|
2375
|
+
"notes": "备注信息(如有异常值或不确定项)"
|
|
2376
|
+
}
|
|
2377
|
+
]
|
|
2378
|
+
\`\`\`
|
|
2379
|
+
|
|
2380
|
+
## 注意事项
|
|
2381
|
+
1. confidence 为 0-1 之间的浮点数,表示对该条数据提取准确性的置信度
|
|
2382
|
+
2. 如果文件中某个指标的值模糊或无法确定,将 confidence 降低并在 notes 中说明
|
|
2383
|
+
3. 如果文件是图片(OCR),先描述图片内容再提取数据
|
|
2384
|
+
4. 每个独立的样品/配方/实验组应作为数组中的一个元素
|
|
2385
|
+
5. 数值类指标请直接输出数字类型(不要带单位),单位已在指标定义中给出
|
|
2386
|
+
|
|
2387
|
+
请直接读取文件并输出 JSON 结果。
|
|
2388
|
+
|
|
2389
|
+
**重要:你必须将最终的 JSON 结果写入文件 \`./${body.taskId}-result.json\`(只写纯 JSON 数组,不要包含 markdown 代码块标记)。**`;
|
|
2390
|
+
}
|
|
2391
|
+
/**
|
|
2392
|
+
* 调用 CC 执行 prompt 并等待完成
|
|
2393
|
+
*/
|
|
2394
|
+
async function executeAndWaitResult(runtimeAdapter, config) {
|
|
2395
|
+
return new Promise((resolve, reject) => {
|
|
2396
|
+
let resultText = "";
|
|
2397
|
+
runtimeAdapter.executePrompt(config, {
|
|
2398
|
+
onTextDelta(text) {
|
|
2399
|
+
resultText += text;
|
|
2400
|
+
},
|
|
2401
|
+
onResult(result) {
|
|
2402
|
+
logger$5.info("CC 执行返回结果", {
|
|
2403
|
+
sessionId: config.sessionId,
|
|
2404
|
+
subtype: result.subtype,
|
|
2405
|
+
resultLength: resultText.length
|
|
2406
|
+
});
|
|
2407
|
+
resolve(resultText || result.result || "");
|
|
2408
|
+
},
|
|
2409
|
+
onError(error) {
|
|
2410
|
+
logger$5.error("CC 执行出错", {
|
|
2411
|
+
sessionId: config.sessionId,
|
|
2412
|
+
error: error.message
|
|
2413
|
+
});
|
|
2414
|
+
reject(error);
|
|
2415
|
+
}
|
|
2416
|
+
}).catch(reject);
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* 从 CC 输出中解析结构化数据
|
|
2421
|
+
*/
|
|
2422
|
+
function parseExtractionResult(rawOutput) {
|
|
2423
|
+
logger$5.info("解析 CC 输出", {
|
|
2424
|
+
outputLength: rawOutput.length,
|
|
2425
|
+
preview: rawOutput.slice(0, 200)
|
|
2426
|
+
});
|
|
2427
|
+
const codeBlockMatch = rawOutput.match(/```json\s*\n?([\s\S]*?)```/);
|
|
2428
|
+
if (codeBlockMatch) try {
|
|
2429
|
+
return parseAndValidateJson(codeBlockMatch[1].trim());
|
|
2430
|
+
} catch {}
|
|
2431
|
+
const arrayMatch = rawOutput.match(/\[[\s\S]*\]/);
|
|
2432
|
+
if (arrayMatch) try {
|
|
2433
|
+
return parseAndValidateJson(arrayMatch[0]);
|
|
2434
|
+
} catch {}
|
|
2435
|
+
const objMatch = rawOutput.match(/\{[\s\S]*\}/);
|
|
2436
|
+
if (objMatch) try {
|
|
2437
|
+
return parseAndValidateJson(`[${objMatch[0]}]`);
|
|
2438
|
+
} catch {}
|
|
2439
|
+
try {
|
|
2440
|
+
return parseAndValidateJson(rawOutput.trim());
|
|
2441
|
+
} catch {
|
|
2442
|
+
logger$5.warn("无法从 CC 输出中解析 JSON,返回空结果", { output: rawOutput.slice(0, 500) });
|
|
2443
|
+
return [];
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* 解析并校验 JSON 格式的提取结果
|
|
2448
|
+
*/
|
|
2449
|
+
function parseAndValidateJson(jsonStr) {
|
|
2450
|
+
const parsed = JSON.parse(jsonStr);
|
|
2451
|
+
return (Array.isArray(parsed) ? parsed : [parsed]).map((item) => ({
|
|
2452
|
+
formulaNo: item.formulaNo || item.formula_no || item.sampleId || item.sample_id,
|
|
2453
|
+
indicators: item.indicators || item.values || {},
|
|
2454
|
+
testConditions: item.testConditions || item.test_conditions || item.conditions,
|
|
2455
|
+
confidence: typeof item.confidence === "number" ? Math.max(0, Math.min(1, item.confidence)) : .8,
|
|
2456
|
+
notes: item.notes || item.remarks
|
|
2457
|
+
}));
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* 向调用方发送回调通知
|
|
2461
|
+
*/
|
|
2462
|
+
async function sendCallback(callbackUrl, taskId, status, data, error) {
|
|
2463
|
+
try {
|
|
2464
|
+
logger$5.info("发送回调通知", {
|
|
2465
|
+
callbackUrl,
|
|
2466
|
+
taskId,
|
|
2467
|
+
status,
|
|
2468
|
+
recordCount: data?.length
|
|
2469
|
+
});
|
|
2470
|
+
const response = await fetch(callbackUrl, {
|
|
2471
|
+
method: "POST",
|
|
2472
|
+
headers: {
|
|
2473
|
+
"Content-Type": "application/json",
|
|
2474
|
+
"X-Internal-Secret": INTERNAL_SECRET
|
|
2475
|
+
},
|
|
2476
|
+
body: JSON.stringify({
|
|
2477
|
+
taskId,
|
|
2478
|
+
status,
|
|
2479
|
+
data,
|
|
2480
|
+
error
|
|
2481
|
+
})
|
|
2482
|
+
});
|
|
2483
|
+
if (!response.ok) {
|
|
2484
|
+
const errText = await response.text();
|
|
2485
|
+
logger$5.warn("回调通知失败", {
|
|
2486
|
+
callbackUrl,
|
|
2487
|
+
status: response.status,
|
|
2488
|
+
body: errText
|
|
2489
|
+
});
|
|
2490
|
+
} else logger$5.info("回调通知成功", {
|
|
2491
|
+
callbackUrl,
|
|
2492
|
+
taskId
|
|
2493
|
+
});
|
|
2494
|
+
} catch (err) {
|
|
2495
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2496
|
+
logger$5.error("回调通知异常", {
|
|
2497
|
+
callbackUrl,
|
|
2498
|
+
taskId,
|
|
2499
|
+
error: errorMsg
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2195
2502
|
}
|
|
2196
2503
|
//#endregion
|
|
2197
2504
|
//#region src/gateway/skills-handler.ts
|
|
@@ -3296,8 +3603,10 @@ function notImplemented(_req, res) {
|
|
|
3296
3603
|
* 其余路由当前仍为占位 handler。
|
|
3297
3604
|
*/
|
|
3298
3605
|
function registerRoutes(app, processManager, scheduledTaskManager, appCredentials) {
|
|
3299
|
-
if (processManager)
|
|
3300
|
-
|
|
3606
|
+
if (processManager) {
|
|
3607
|
+
app.use(createExecuteRouter(processManager));
|
|
3608
|
+
app.use(createAiExtractRouter(processManager));
|
|
3609
|
+
} else {
|
|
3301
3610
|
logger$1.warn("processManager 未注入,执行路由使用占位 handler");
|
|
3302
3611
|
app.post("/api/execute", notImplemented);
|
|
3303
3612
|
app.post("/api/execute/batch", notImplemented);
|
|
@@ -4615,6 +4924,7 @@ function sortTraceValue(value) {
|
|
|
4615
4924
|
//#endregion
|
|
4616
4925
|
//#region src/card/cc-stream-bridge.ts
|
|
4617
4926
|
const log$18 = larkLogger("card/cc-stream-bridge");
|
|
4927
|
+
const CC_INTERNAL_PLACEHOLDER = "No response requested.";
|
|
4618
4928
|
/**
|
|
4619
4929
|
* CCStreamBridge — 将 CC 流事件桥接到 StreamingCardController
|
|
4620
4930
|
*
|
|
@@ -4733,6 +5043,15 @@ var CCStreamBridge = class {
|
|
|
4733
5043
|
accumulatedThinkingTextLen: this.accumulatedThinkingText.length
|
|
4734
5044
|
});
|
|
4735
5045
|
if (this.options.autoCompleteOnTurnEnd && stopReason === "end_turn") {
|
|
5046
|
+
const trimmedText = this.accumulatedText.trim();
|
|
5047
|
+
if (trimmedText === CC_INTERNAL_PLACEHOLDER || trimmedText === "") {
|
|
5048
|
+
log$18.info("检测到 CC 内部占位消息,静默丢弃", {
|
|
5049
|
+
text: trimmedText.slice(0, 50),
|
|
5050
|
+
sessionKey: this.options.sessionKey
|
|
5051
|
+
});
|
|
5052
|
+
this.controller.abortCard();
|
|
5053
|
+
return;
|
|
5054
|
+
}
|
|
4736
5055
|
this.controller.markFullyComplete();
|
|
4737
5056
|
this.controller.onIdle();
|
|
4738
5057
|
}
|
|
@@ -8761,7 +9080,8 @@ async function dispatchToCC(params) {
|
|
|
8761
9080
|
const isGroup = chatType === "group";
|
|
8762
9081
|
const replyInThread = !!threadId;
|
|
8763
9082
|
const replyToMessageId = threadId ? void 0 : ctx.messageId;
|
|
8764
|
-
|
|
9083
|
+
let textPrompt = formatForCC(ctx, isGroup);
|
|
9084
|
+
if (isGroup) textPrompt = `[直接@你的消息,请正常回复] ${textPrompt}`;
|
|
8765
9085
|
const imageResources = ctx.resources.filter((r) => r.type === "image");
|
|
8766
9086
|
let prompt = textPrompt;
|
|
8767
9087
|
if (imageResources.length > 0) {
|
|
@@ -9007,6 +9327,9 @@ async function dispatchToCC(params) {
|
|
|
9007
9327
|
chatId,
|
|
9008
9328
|
replyToMessageId: cardDeps.replyToMessageId
|
|
9009
9329
|
});
|
|
9330
|
+
cardController.ensureCardCreated().catch((err) => {
|
|
9331
|
+
log$11.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
|
|
9332
|
+
});
|
|
9010
9333
|
const bridge = new CCStreamBridge(cardController, {
|
|
9011
9334
|
autoCompleteOnTurnEnd: true,
|
|
9012
9335
|
sessionKey: route.sessionId
|
|
@@ -9133,12 +9456,12 @@ async function dispatchTeammateEval(params) {
|
|
|
9133
9456
|
const teammateSessionKey = `teammate_${route.sessionId}`;
|
|
9134
9457
|
startToolUseTraceRun(teammateSessionKey);
|
|
9135
9458
|
const NO_REPLY_TOKEN = SILENT_REPLY_TOKEN;
|
|
9136
|
-
const
|
|
9459
|
+
const CC_INTERNAL_PLACEHOLDER = "No response requested.";
|
|
9137
9460
|
/** 缓冲的 thinking 内容 */
|
|
9138
9461
|
let thinkingBuffer = "";
|
|
9139
|
-
/** text
|
|
9140
|
-
let
|
|
9141
|
-
/** 是否已确认 CC
|
|
9462
|
+
/** 全部 text 输出缓冲(end_turn 时统一判断是否含 NO_REPLY) */
|
|
9463
|
+
let fullTextBuffer = "";
|
|
9464
|
+
/** 是否已确认 CC 决定回复(仅在 end_turn 时才确认) */
|
|
9142
9465
|
let confirmed = false;
|
|
9143
9466
|
/** 是否已确认 CC 决定静默 */
|
|
9144
9467
|
let silenced = false;
|
|
@@ -9151,7 +9474,7 @@ async function dispatchTeammateEval(params) {
|
|
|
9151
9474
|
const pendingToolEvents = [];
|
|
9152
9475
|
let finalStopReason = "";
|
|
9153
9476
|
/**
|
|
9154
|
-
* 确认 CC 要回复 — 创建卡片 + bridge,灌入缓冲的 thinking + text
|
|
9477
|
+
* 确认 CC 要回复 — 创建卡片 + bridge,灌入缓冲的 thinking + text
|
|
9155
9478
|
*/
|
|
9156
9479
|
const confirmReply = () => {
|
|
9157
9480
|
if (confirmed) return;
|
|
@@ -9159,7 +9482,7 @@ async function dispatchTeammateEval(params) {
|
|
|
9159
9482
|
log$11.info("teammate 确认回复,创建流式卡片", {
|
|
9160
9483
|
chatId,
|
|
9161
9484
|
thinkingLen: thinkingBuffer.length,
|
|
9162
|
-
|
|
9485
|
+
textLen: fullTextBuffer.length
|
|
9163
9486
|
});
|
|
9164
9487
|
cardController = new StreamingCardController({
|
|
9165
9488
|
cfg: {},
|
|
@@ -9190,13 +9513,18 @@ async function dispatchTeammateEval(params) {
|
|
|
9190
9513
|
sessionKey: teammateSessionKey
|
|
9191
9514
|
});
|
|
9192
9515
|
if (thinkingBuffer) bridge.onThinkingDelta(thinkingBuffer);
|
|
9193
|
-
|
|
9516
|
+
const cleanedText = fullTextBuffer.replace(/\s*NO_REPLY\s*$/, "").trim();
|
|
9517
|
+
if (cleanedText) bridge.onTextDelta(cleanedText);
|
|
9194
9518
|
for (const evt of pendingToolEvents) if (evt.type === "start") bridge.onToolUseStart(evt.name, evt.input);
|
|
9195
9519
|
else bridge.onToolResult(evt.id);
|
|
9196
9520
|
pendingToolEvents.length = 0;
|
|
9197
9521
|
};
|
|
9198
9522
|
/**
|
|
9199
|
-
* 处理 text delta —
|
|
9523
|
+
* 处理 text delta — 全量缓冲,直到 end_turn 时统一判断
|
|
9524
|
+
*
|
|
9525
|
+
* 策略:CC 在 teammate 模式下可能先执行工具再输出文本,且文本中可能正文在前、
|
|
9526
|
+
* NO_REPLY 在末尾。因此不做前缀检测,而是缓冲所有 text,在 onTurnEnd(end_turn)
|
|
9527
|
+
* 时统一判断完整输出是否包含 NO_REPLY。
|
|
9200
9528
|
*/
|
|
9201
9529
|
const handleTextDelta = (text) => {
|
|
9202
9530
|
if (silenced) return;
|
|
@@ -9204,17 +9532,7 @@ async function dispatchTeammateEval(params) {
|
|
|
9204
9532
|
bridge.onTextDelta(text);
|
|
9205
9533
|
return;
|
|
9206
9534
|
}
|
|
9207
|
-
|
|
9208
|
-
if (textPrefixBuffer.length >= PREFIX_LEN) {
|
|
9209
|
-
if (textPrefixBuffer.trim() === NO_REPLY_TOKEN) {
|
|
9210
|
-
silenced = true;
|
|
9211
|
-
log$11.info("teammate 前缀检测: NO_REPLY", { chatId });
|
|
9212
|
-
return;
|
|
9213
|
-
}
|
|
9214
|
-
confirmReply();
|
|
9215
|
-
return;
|
|
9216
|
-
}
|
|
9217
|
-
if (!NO_REPLY_TOKEN.startsWith(textPrefixBuffer.trim())) confirmReply();
|
|
9535
|
+
fullTextBuffer += text;
|
|
9218
9536
|
};
|
|
9219
9537
|
try {
|
|
9220
9538
|
await new Promise((resolve) => {
|
|
@@ -9260,12 +9578,12 @@ async function dispatchTeammateEval(params) {
|
|
|
9260
9578
|
return;
|
|
9261
9579
|
}
|
|
9262
9580
|
if (!confirmed && !silenced) {
|
|
9263
|
-
const trimmed =
|
|
9264
|
-
if (trimmed === NO_REPLY_TOKEN || trimmed === "") {
|
|
9581
|
+
const trimmed = fullTextBuffer.trim();
|
|
9582
|
+
if (trimmed === NO_REPLY_TOKEN || trimmed === CC_INTERNAL_PLACEHOLDER || trimmed.endsWith(NO_REPLY_TOKEN) || trimmed === "") {
|
|
9265
9583
|
silenced = true;
|
|
9266
|
-
log$11.info("teammate turnEnd 最终判断:
|
|
9584
|
+
log$11.info("teammate turnEnd 最终判断: 静默", {
|
|
9267
9585
|
chatId,
|
|
9268
|
-
trimmed
|
|
9586
|
+
trimmed: trimmed.slice(0, 60)
|
|
9269
9587
|
});
|
|
9270
9588
|
} else {
|
|
9271
9589
|
confirmReply();
|
|
@@ -9302,7 +9620,7 @@ async function dispatchTeammateEval(params) {
|
|
|
9302
9620
|
chatId,
|
|
9303
9621
|
confirmed,
|
|
9304
9622
|
silenced,
|
|
9305
|
-
|
|
9623
|
+
fullTextBufferLen: fullTextBuffer.length,
|
|
9306
9624
|
thinkingBufferLen: thinkingBuffer.length,
|
|
9307
9625
|
stopReason: finalStopReason
|
|
9308
9626
|
});
|
|
@@ -11976,9 +12294,15 @@ var TeammateBuffer = class {
|
|
|
11976
12294
|
*/
|
|
11977
12295
|
buildEvalPrompt(messages) {
|
|
11978
12296
|
const header = [
|
|
11979
|
-
"[
|
|
11980
|
-
"
|
|
11981
|
-
"
|
|
12297
|
+
"[旁听评估任务] 以下是群聊中最近的对话,你作为团队成员正在旁听。",
|
|
12298
|
+
"请判断是否需要主动参与讨论。如果不需要,只输出 NO_REPLY。",
|
|
12299
|
+
"如果需要参与,直接输出你的回复内容(不要加 NO_REPLY)。",
|
|
12300
|
+
"",
|
|
12301
|
+
"重要规则:",
|
|
12302
|
+
"- 这是一次旁听评估,仅在本次评估中适用 NO_REPLY 规则。后续如果用户直接 @你 则必须正常回复。",
|
|
12303
|
+
"- 你无法查看图片/文件的实际内容。如果消息只包含图片或文件且没有文字上下文,直接输出 NO_REPLY。",
|
|
12304
|
+
"- 不要使用任何工具(Bash/Glob/Read 等)来尝试查找或分析图片文件。",
|
|
12305
|
+
"- 只基于文字内容判断是否参与。",
|
|
11982
12306
|
"",
|
|
11983
12307
|
"---"
|
|
11984
12308
|
].join("\n");
|