nolo-cli 0.1.8 → 0.1.9
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/README.md +32 -0
- package/agentRuntimeCommands.ts +3 -3
- package/ai/agent/_executeModel.ts +118 -0
- package/ai/agent/agentSlice.ts +525 -0
- package/ai/agent/appWorkingMemory.ts +126 -0
- package/ai/agent/avatarUtils.ts +24 -0
- package/ai/agent/buildEditingContext.ts +373 -0
- package/ai/agent/buildSystemPrompt.ts +532 -0
- package/ai/agent/cleanAgentMessages.ts +140 -0
- package/ai/agent/cliChatClient.ts +119 -0
- package/ai/agent/cliExecutor.ts +733 -0
- package/ai/agent/cliPrompt.ts +10 -0
- package/ai/agent/contextCompiler.ts +107 -0
- package/ai/agent/contextLayerContract.ts +44 -0
- package/ai/agent/createAgentSchema.ts +234 -0
- package/ai/agent/executeToolCall.ts +58 -0
- package/ai/agent/fetchAgentContexts.ts +42 -0
- package/ai/agent/generatePrompt.ts +3 -0
- package/ai/agent/getFullChatContextKeys.ts +168 -0
- package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
- package/ai/agent/hooks/useAgentConfig.ts +61 -0
- package/ai/agent/hooks/useAgentDialog.ts +35 -0
- package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
- package/ai/agent/hooks/usePublicAgents.ts +473 -0
- package/ai/agent/machineRunPermissions.ts +95 -0
- package/ai/agent/persistMessageWithFixedId.ts +37 -0
- package/ai/agent/planSlice.ts +259 -0
- package/ai/agent/referenceUtils.ts +229 -0
- package/ai/agent/runAgentBackground.ts +238 -0
- package/ai/agent/runAgentClientLoop.ts +138 -0
- package/ai/agent/runtimeGuidance.ts +97 -0
- package/ai/agent/runtimeServerBase.ts +37 -0
- package/ai/agent/server/fetchPublicAgents.ts +128 -0
- package/ai/agent/startParallelAgentStreams.ts +424 -0
- package/ai/agent/startupProtocol.ts +53 -0
- package/ai/agent/streamAgentChatTurn.ts +1278 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
- package/ai/agent/types.ts +71 -0
- package/ai/agent/utils/imageOutput.ts +33 -0
- package/ai/agent/utils/sortUtils.ts +250 -0
- package/ai/agent/web/referencePickerUtils.ts +146 -0
- package/ai/ai.locale.ts +1075 -0
- package/ai/chat/accumulateToolCallChunks.ts +95 -0
- package/ai/chat/fetchUtils.native.ts +276 -0
- package/ai/chat/fetchUtils.ts +153 -0
- package/ai/chat/parseApiError.ts +64 -0
- package/ai/chat/parseMultilineSSE.ts +95 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +703 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +491 -0
- package/ai/chat/shouldUseServerProxy.ts +18 -0
- package/ai/chat/sseClient.native.ts +91 -0
- package/ai/chat/sseClient.ts +67 -0
- package/ai/chat/streamReader.native.ts +31 -0
- package/ai/chat/streamReader.ts +62 -0
- package/ai/chat/updateTotalUsage.ts +72 -0
- package/ai/context/buildReferenceContext.ts +437 -0
- package/ai/context/calculateContextUsage.ts +133 -0
- package/ai/context/retention.ts +165 -0
- package/ai/context/tokenUtils.ts +78 -0
- package/ai/index.ts +1 -0
- package/ai/llm/calculateGeminiImageTokens.ts +57 -0
- package/ai/llm/deepinfra.ts +28 -0
- package/ai/llm/fireworks.ts +50 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +84 -0
- package/ai/llm/getNoloKey.ts +31 -0
- package/ai/llm/getPricing.ts +199 -0
- package/ai/llm/hooks/useModelPricing.ts +75 -0
- package/ai/llm/imagePricing.ts +40 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/mimo.ts +71 -0
- package/ai/llm/mistral.ts +22 -0
- package/ai/llm/modelAvatar.ts +427 -0
- package/ai/llm/models.ts +45 -0
- package/ai/llm/openrouterModels.ts +269 -0
- package/ai/llm/providers.ts +306 -0
- package/ai/llm/reasoningModels.ts +28 -0
- package/ai/llm/types.ts +59 -0
- package/ai/llm/usageRequestOptions.ts +59 -0
- package/ai/memory/capture.ts +148 -0
- package/ai/memory/consolidate.ts +104 -0
- package/ai/memory/delete.ts +147 -0
- package/ai/memory/overlay.ts +84 -0
- package/ai/memory/query.ts +38 -0
- package/ai/memory/queryShared.ts +160 -0
- package/ai/memory/rank.ts +105 -0
- package/ai/memory/recentRelationshipRecap.ts +249 -0
- package/ai/memory/remember.ts +167 -0
- package/ai/memory/runtime.ts +76 -0
- package/ai/memory/store.ts +20 -0
- package/ai/memory/storeShared.ts +76 -0
- package/ai/memory/types.ts +46 -0
- package/ai/memory/understanding.ts +349 -0
- package/ai/memory/understandingGreeting.ts +264 -0
- package/ai/messages/type.ts +20 -0
- package/ai/policy/personalizationDialog.ts +333 -0
- package/ai/policy/runtimePolicy.ts +440 -0
- package/ai/policy/selfUpdateFields.ts +48 -0
- package/ai/policy/types.ts +64 -0
- package/ai/skills/referenceRuntime.ts +274 -0
- package/ai/skills/skillDiagnostics.ts +251 -0
- package/ai/skills/skillDocBuilder.ts +139 -0
- package/ai/skills/skillDocProtocol.ts +434 -0
- package/ai/skills/skillReferenceSummary.ts +63 -0
- package/ai/skills/skillSummaryMarker.ts +26 -0
- package/ai/token/calculatePrice.ts +544 -0
- package/ai/token/db.ts +98 -0
- package/ai/token/externalToolCost.ts +330 -0
- package/ai/token/hooks/useRecords.ts +65 -0
- package/ai/token/missingUsageEstimate.ts +42 -0
- package/ai/token/modelUsageQuery.ts +252 -0
- package/ai/token/normalizeUsage.ts +84 -0
- package/ai/token/openaiImageGenerationUsage.ts +56 -0
- package/ai/token/prepareTokenUsageData.ts +88 -0
- package/ai/token/query.ts +88 -0
- package/ai/token/queryUserTokens.ts +59 -0
- package/ai/token/resolveBillingTarget.ts +52 -0
- package/ai/token/saveTokenRecord.ts +53 -0
- package/ai/token/serverDialogProjection.ts +78 -0
- package/ai/token/serverTokenWriter.ts +143 -0
- package/ai/token/stats.ts +21 -0
- package/ai/token/tokenThunks.ts +24 -0
- package/ai/token/types.ts +93 -0
- package/ai/tools/agent/agentTools.ts +176 -0
- package/ai/tools/agent/agentUpdateShared.ts +311 -0
- package/ai/tools/agent/callAgentTool.ts +139 -0
- package/ai/tools/agent/createAgentTool.ts +512 -0
- package/ai/tools/agent/createDialogTool.ts +69 -0
- package/ai/tools/agent/createSkillAgentTool.ts +62 -0
- package/ai/tools/agent/parallelBudget.ts +221 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +145 -0
- package/ai/tools/agent/runLlmTool.ts +96 -0
- package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
- package/ai/tools/agent/skillAgentArgs.ts +106 -0
- package/ai/tools/agent/skillAgentPreset.ts +89 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
- package/ai/tools/agent/updateAgentTool.ts +96 -0
- package/ai/tools/agent/updateSelfTool.ts +113 -0
- package/ai/tools/amazonProductScraperTool.ts +86 -0
- package/ai/tools/apifyActorClient.ts +45 -0
- package/ai/tools/appEditGuard.ts +372 -0
- package/ai/tools/appReadSnapshot.ts +153 -0
- package/ai/tools/appTools.ts +1549 -0
- package/ai/tools/applyEditTool.ts +256 -0
- package/ai/tools/applyLineEditsTool.ts +312 -0
- package/ai/tools/browserTools/click.ts +33 -0
- package/ai/tools/browserTools/closeSession.ts +29 -0
- package/ai/tools/browserTools/common.ts +27 -0
- package/ai/tools/browserTools/openSession.ts +48 -0
- package/ai/tools/browserTools/readContent.ts +38 -0
- package/ai/tools/browserTools/selectOption.ts +46 -0
- package/ai/tools/browserTools/typeText.ts +42 -0
- package/ai/tools/category/createCategoryTool.ts +66 -0
- package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
- package/ai/tools/category/updateContentCategoryTool.ts +75 -0
- package/ai/tools/cfBrowserTools.ts +319 -0
- package/ai/tools/cfSpeechToTextTool.ts +49 -0
- package/ai/tools/checkEnvTool.ts +65 -0
- package/ai/tools/cloudflareCrawlTool.ts +289 -0
- package/ai/tools/codeSearchTool.ts +111 -0
- package/ai/tools/codeTools.ts +101 -0
- package/ai/tools/createDocTool.ts +132 -0
- package/ai/tools/createPlanTool.ts +999 -0
- package/ai/tools/createSkillDocTool.ts +155 -0
- package/ai/tools/createWorkflowTool.ts +154 -0
- package/ai/tools/deepseekOcrTool.ts +34 -0
- package/ai/tools/delayTool.ts +31 -0
- package/ai/tools/deleteSpacesTool.ts +325 -0
- package/ai/tools/deleteSpacesToolModel.ts +159 -0
- package/ai/tools/devReloadUtils.ts +29 -0
- package/ai/tools/dialogMessageSearch.ts +137 -0
- package/ai/tools/doctorSkillTool.ts +72 -0
- package/ai/tools/ecommerceScraperTool.ts +86 -0
- package/ai/tools/emailTools.ts +549 -0
- package/ai/tools/evalSkillTool.ts +92 -0
- package/ai/tools/exaSearchTool.ts +64 -0
- package/ai/tools/execBashTool.ts +379 -0
- package/ai/tools/executeSqlTool.ts +192 -0
- package/ai/tools/fetchWebpageSupport.ts +309 -0
- package/ai/tools/fetchWebpageTool.ts +84 -0
- package/ai/tools/geminiImagePreviewTool.ts +361 -0
- package/ai/tools/generateDocxTool.ts +215 -0
- package/ai/tools/googleSearchScraperTool.ts +106 -0
- package/ai/tools/importDataTool.ts +133 -0
- package/ai/tools/importSkillTool.ts +162 -0
- package/ai/tools/index.ts +1858 -0
- package/ai/tools/listFilesTool.ts +82 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/modelUsageTools.ts +142 -0
- package/ai/tools/olmOcrTool.ts +34 -0
- package/ai/tools/openaiImageTool.ts +218 -0
- package/ai/tools/paddleOcrTool.ts +34 -0
- package/ai/tools/prepareTools.ts +23 -0
- package/ai/tools/readDocTool.ts +84 -0
- package/ai/tools/readFileTool.ts +211 -0
- package/ai/tools/readTool.ts +163 -0
- package/ai/tools/readXPostTool.ts +233 -0
- package/ai/tools/rememberMemoryTool.ts +84 -0
- package/ai/tools/remotionVideoTool.ts +151 -0
- package/ai/tools/searchDialogMessagesTool.ts +222 -0
- package/ai/tools/searchRepoTool.ts +115 -0
- package/ai/tools/searchWorkspaceTool.ts +259 -0
- package/ai/tools/skillFollowup.ts +86 -0
- package/ai/tools/surfWeatherTool.ts +169 -0
- package/ai/tools/table/addTableRowTool.ts +217 -0
- package/ai/tools/table/createTableTool.ts +315 -0
- package/ai/tools/table/rowTools.ts +366 -0
- package/ai/tools/table/schemaTools.ts +244 -0
- package/ai/tools/table/shareTableTool.ts +148 -0
- package/ai/tools/table/toolShared.ts +129 -0
- package/ai/tools/toolApiClient.ts +198 -0
- package/ai/tools/toolNameAliases.ts +57 -0
- package/ai/tools/toolResultError.ts +42 -0
- package/ai/tools/toolRunSlice.ts +303 -0
- package/ai/tools/toolSchemaCompatibility.ts +53 -0
- package/ai/tools/toolVisibility.ts +4 -0
- package/ai/tools/types.ts +20 -0
- package/ai/tools/uiAskChoiceTool.ts +104 -0
- package/ai/tools/updateContentTitleTool.ts +84 -0
- package/ai/tools/updateDocTool.ts +105 -0
- package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
- package/ai/tools/whisperTool.ts +77 -0
- package/ai/tools/writeFileTool.ts +210 -0
- package/ai/tools/youtubeScraperTool.ts +116 -0
- package/ai/tools/ziweiChartTool.ts +678 -0
- package/ai/types.ts +55 -0
- package/ai/workflow/workflowExecutor.ts +323 -0
- package/ai/workflow/workflowSlice.ts +73 -0
- package/ai/workflow/workflowTypes.ts +106 -0
- package/client/compactDialog.ts +222 -0
- package/connector-experimental/capabilities.ts +73 -0
- package/connector-experimental/codexBinary.ts +41 -0
- package/connector-experimental/heartbeatLoop.ts +22 -0
- package/connector-experimental/index.ts +5 -0
- package/connector-experimental/machineInfo.ts +46 -0
- package/connector-experimental/protocol.ts +54 -0
- package/machineCommands.ts +4 -4
- package/package.json +8 -6
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { setSettings } from "app/settings/settingSlice";
|
|
2
|
+
import type {
|
|
3
|
+
KnowledgeCaptureLevel,
|
|
4
|
+
SpaceContextLevel,
|
|
5
|
+
TonePreset,
|
|
6
|
+
} from "ai/policy/types";
|
|
7
|
+
import {
|
|
8
|
+
normalizeAgentUpdateFieldList,
|
|
9
|
+
type AgentUpdateField,
|
|
10
|
+
} from "ai/policy/selfUpdateFields";
|
|
11
|
+
|
|
12
|
+
export interface UpdateUserPreferenceProfileArgs {
|
|
13
|
+
userTonePreset?: TonePreset;
|
|
14
|
+
knowledgeCaptureLevel?: KnowledgeCaptureLevel;
|
|
15
|
+
spaceContextLevel?: SpaceContextLevel;
|
|
16
|
+
autoApproveSelfUpdateFields?: AgentUpdateField[];
|
|
17
|
+
globalPrompt?: string;
|
|
18
|
+
defaultAgentId?: string;
|
|
19
|
+
summary?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const updateUserPreferenceProfileFunctionSchema = {
|
|
23
|
+
name: "updateUserPreferenceProfile",
|
|
24
|
+
description: [
|
|
25
|
+
"当你已经明确收集到用户的个性化偏好时,使用本工具把这些偏好保存到用户设置里。",
|
|
26
|
+
"适用于保存 tone、knowledge capture、space context,以及用户的 global prompt / 自我介绍摘要。",
|
|
27
|
+
"只有在用户已经明确表达或确认后才调用;不要在信息不充分时猜测。",
|
|
28
|
+
"调用后,你应当用自然语言简要总结已保存的设置,并说明用户之后可以继续调整。",
|
|
29
|
+
].join("\n"),
|
|
30
|
+
parameters: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
userTonePreset: {
|
|
34
|
+
type: "string",
|
|
35
|
+
enum: ["default", "direct", "pragmatic", "friendly", "professional"],
|
|
36
|
+
description: "用户偏好的交流语气。",
|
|
37
|
+
},
|
|
38
|
+
knowledgeCaptureLevel: {
|
|
39
|
+
type: "integer",
|
|
40
|
+
enum: [1, 2, 3, 4],
|
|
41
|
+
description: "知识沉淀级别:1 不主动创建;2 先问再创建;3 回答后建议;4 高价值结果可自动创建。",
|
|
42
|
+
},
|
|
43
|
+
spaceContextLevel: {
|
|
44
|
+
type: "integer",
|
|
45
|
+
enum: [1, 2, 3, 4],
|
|
46
|
+
description: "空间上下文级别:1 不自动读取;2 只看结构和标题;3 轻量读取;4 自适应读取。",
|
|
47
|
+
},
|
|
48
|
+
autoApproveSelfUpdateFields: {
|
|
49
|
+
type: "array",
|
|
50
|
+
items: {
|
|
51
|
+
type: "string",
|
|
52
|
+
enum: [
|
|
53
|
+
"name",
|
|
54
|
+
"model",
|
|
55
|
+
"provider",
|
|
56
|
+
"prompt",
|
|
57
|
+
"introduction",
|
|
58
|
+
"greeting",
|
|
59
|
+
"isPublic",
|
|
60
|
+
"tags",
|
|
61
|
+
"tools",
|
|
62
|
+
"references",
|
|
63
|
+
"temperature",
|
|
64
|
+
"top_p",
|
|
65
|
+
"frequency_penalty",
|
|
66
|
+
"presence_penalty",
|
|
67
|
+
"max_tokens",
|
|
68
|
+
"reasoning_effort",
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
description: "哪些 updateSelf 字段以后不再询问用户,直接自动通过。",
|
|
72
|
+
},
|
|
73
|
+
globalPrompt: {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "可选:用户希望长期保留的一小段通用偏好说明。未明确确认时不要写。",
|
|
76
|
+
},
|
|
77
|
+
defaultAgentId: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "可选:用户明确要求切换首页默认 agent 时才填写。",
|
|
80
|
+
},
|
|
81
|
+
summary: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "给开发和日志看的简短总结,不会影响设置本身。",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
} as const,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export async function updateUserPreferenceProfileFunc(
|
|
90
|
+
args: UpdateUserPreferenceProfileArgs,
|
|
91
|
+
thunkApi: any
|
|
92
|
+
): Promise<{ rawData: unknown; displayData: string }> {
|
|
93
|
+
const changes: Record<string, unknown> = {};
|
|
94
|
+
|
|
95
|
+
if (args.userTonePreset) {
|
|
96
|
+
changes.userTonePreset = args.userTonePreset;
|
|
97
|
+
}
|
|
98
|
+
if (typeof args.knowledgeCaptureLevel === "number") {
|
|
99
|
+
changes.knowledgeCaptureLevel = args.knowledgeCaptureLevel;
|
|
100
|
+
}
|
|
101
|
+
if (typeof args.spaceContextLevel === "number") {
|
|
102
|
+
changes.spaceContextLevel = args.spaceContextLevel;
|
|
103
|
+
changes.enableReadCurrentSpace = args.spaceContextLevel > 1;
|
|
104
|
+
}
|
|
105
|
+
if (Array.isArray(args.autoApproveSelfUpdateFields)) {
|
|
106
|
+
changes.autoApproveSelfUpdateFields = normalizeAgentUpdateFieldList(
|
|
107
|
+
args.autoApproveSelfUpdateFields,
|
|
108
|
+
[]
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (typeof args.globalPrompt === "string" && args.globalPrompt.trim()) {
|
|
112
|
+
changes.globalPrompt = args.globalPrompt.trim();
|
|
113
|
+
}
|
|
114
|
+
if (typeof args.defaultAgentId === "string" && args.defaultAgentId.trim()) {
|
|
115
|
+
changes.defaultAgentId = args.defaultAgentId.trim();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (Object.keys(changes).length === 0) {
|
|
119
|
+
throw new Error("没有可保存的用户偏好设置。");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await thunkApi.dispatch(setSettings(changes as any)).unwrap();
|
|
123
|
+
|
|
124
|
+
const rawData = {
|
|
125
|
+
success: true,
|
|
126
|
+
updated: changes,
|
|
127
|
+
summary: args.summary?.trim() || null,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const labels: string[] = [];
|
|
131
|
+
if (changes.userTonePreset) labels.push(`语气=${changes.userTonePreset}`);
|
|
132
|
+
if (changes.knowledgeCaptureLevel)
|
|
133
|
+
labels.push(`知识沉淀=${changes.knowledgeCaptureLevel}`);
|
|
134
|
+
if (changes.spaceContextLevel)
|
|
135
|
+
labels.push(`空间读取=${changes.spaceContextLevel}`);
|
|
136
|
+
if (changes.autoApproveSelfUpdateFields)
|
|
137
|
+
labels.push("自我更新免询问字段已更新");
|
|
138
|
+
if (changes.defaultAgentId) labels.push("默认助手已更新");
|
|
139
|
+
if (changes.globalPrompt) labels.push("通用提示词已更新");
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
rawData,
|
|
143
|
+
displayData: `已保存用户偏好:${labels.join(",")}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ToolDefinition } from ".";
|
|
2
|
+
import { callToolApi } from "./toolApiClient";
|
|
3
|
+
|
|
4
|
+
const SHARED_PARAMS = {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
audioUrl: {
|
|
8
|
+
type: "string",
|
|
9
|
+
description:
|
|
10
|
+
"音频来源:内部文件 ID (file-... 或 ULID)、公开 http/https URL,或 base64 data URI(data:audio/...;base64,...)。支持 mp3、wav、m4a、ogg、flac 等常见格式。",
|
|
11
|
+
},
|
|
12
|
+
language: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description:
|
|
15
|
+
"可选,指定音频语言以提升精度(如 'zh'、'en'、'ja')。不指定则自动检测。",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: ["audioUrl"],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// ── whisper-large-v3-turbo(快速、便宜)──────────────────
|
|
22
|
+
|
|
23
|
+
export const whisperTurboSchema = {
|
|
24
|
+
name: "whisper_turbo",
|
|
25
|
+
description:
|
|
26
|
+
"使用 openai/whisper-large-v3-turbo 对音频进行语音转文字(ASR)。速度快、价格低($0.0002/min),适合大多数场景的实时或批量转录。",
|
|
27
|
+
parameters: SHARED_PARAMS,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const whisperTurboFunc: ToolDefinition["executor"] = async (
|
|
31
|
+
input: any,
|
|
32
|
+
thunkApi
|
|
33
|
+
) => {
|
|
34
|
+
const { audioUrl, language } = input;
|
|
35
|
+
const data = await callToolApi(
|
|
36
|
+
thunkApi,
|
|
37
|
+
"/api/whisper-turbo",
|
|
38
|
+
{ audioUrl, language },
|
|
39
|
+
{ withAuth: true }
|
|
40
|
+
);
|
|
41
|
+
return {
|
|
42
|
+
summary: "Transcription completed",
|
|
43
|
+
text: data.text || "",
|
|
44
|
+
language: data.language,
|
|
45
|
+
duration: data.duration,
|
|
46
|
+
rawData: data,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ── whisper-large-v3(高精度)────────────────────────────
|
|
51
|
+
|
|
52
|
+
export const whisperV3Schema = {
|
|
53
|
+
name: "whisper_v3",
|
|
54
|
+
description:
|
|
55
|
+
"使用 openai/whisper-large-v3 对音频进行高精度语音转文字(ASR)。精度更高(尤其多语言/中文),价格稍高($0.00045/min),适合对准确率要求严格的场景。",
|
|
56
|
+
parameters: SHARED_PARAMS,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const whisperV3Func: ToolDefinition["executor"] = async (
|
|
60
|
+
input: any,
|
|
61
|
+
thunkApi
|
|
62
|
+
) => {
|
|
63
|
+
const { audioUrl, language } = input;
|
|
64
|
+
const data = await callToolApi(
|
|
65
|
+
thunkApi,
|
|
66
|
+
"/api/whisper-v3",
|
|
67
|
+
{ audioUrl, language },
|
|
68
|
+
{ withAuth: true }
|
|
69
|
+
);
|
|
70
|
+
return {
|
|
71
|
+
summary: "Transcription completed",
|
|
72
|
+
text: data.text || "",
|
|
73
|
+
language: data.language,
|
|
74
|
+
duration: data.duration,
|
|
75
|
+
rawData: data,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// 文件路径: packages/ai/tools/writeFileTool.ts
|
|
2
|
+
|
|
3
|
+
import { bumpDevReloadSuppressIfSelfEditing } from "./devReloadUtils";
|
|
4
|
+
import { getToolBaseUrl } from "./toolApiClient";
|
|
5
|
+
|
|
6
|
+
// ---- Types ----
|
|
7
|
+
|
|
8
|
+
export type WriteFileArgs = {
|
|
9
|
+
/**
|
|
10
|
+
* 目标文件相对项目根目录(bun-nolo)的路径,例如:
|
|
11
|
+
* - packages/chat/src/messages/web/NewComponent.tsx
|
|
12
|
+
* - packages/server/some-script.ts
|
|
13
|
+
*/
|
|
14
|
+
filePath: string;
|
|
15
|
+
/**
|
|
16
|
+
* 要写入文件的完整内容。
|
|
17
|
+
* - 如果文件不存在:将创建新文件,并写入该内容;
|
|
18
|
+
* - 如果文件已存在:
|
|
19
|
+
* - overwrite = true:覆盖原有内容;
|
|
20
|
+
* - overwrite 未设置或为 false:请求会失败。
|
|
21
|
+
*/
|
|
22
|
+
content: string;
|
|
23
|
+
overwrite?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ---- 工具 Schema,供 LLM 调用 ----
|
|
27
|
+
|
|
28
|
+
export const writeFileFunctionSchema = {
|
|
29
|
+
name: "writeFile",
|
|
30
|
+
description: [
|
|
31
|
+
"在后端服务器上写入一个完整文件的内容,可用于新建文件或覆盖已有文件。",
|
|
32
|
+
"",
|
|
33
|
+
"适用场景:",
|
|
34
|
+
"- 新建一个全新的代码文件(例如新组件、新工具函数、配置文件等)",
|
|
35
|
+
"- 覆盖一个文件的全部内容(当你需要重写整个文件时)",
|
|
36
|
+
"",
|
|
37
|
+
"行为约定:",
|
|
38
|
+
"- 当目标文件不存在:直接创建新文件并写入 content。",
|
|
39
|
+
"- 当目标文件已存在:",
|
|
40
|
+
" - 如果 overwrite=true,则覆盖原文件内容;",
|
|
41
|
+
" - 否则请求会失败,以避免意外覆盖。",
|
|
42
|
+
"",
|
|
43
|
+
"返回数据约定(rawData.response 中):",
|
|
44
|
+
"- created / overwritten:指示本次操作是新建还是覆盖。",
|
|
45
|
+
"- newContent:写入后的完整文件内容(统一使用 \\n 作为换行符),用于 UI 中的代码预览。",
|
|
46
|
+
"- diff:如果文件原本存在,则返回基于旧内容与 newContent 的行级 diff(diffLines 结果),用于 UI 中的 Diff 视图。",
|
|
47
|
+
"",
|
|
48
|
+
"注意:",
|
|
49
|
+
"- filePath 必须是相对项目根目录(bun-nolo)的相对路径,且不能越出项目根目录。",
|
|
50
|
+
"- 建议只在“新建文件/全量重写”时使用本工具;修改部分内容时优先使用 applyEdit。",
|
|
51
|
+
"- 如果用户给了 URL 或网页资料来驱动本次改动,先抓取网页并核对字段,再决定是否需要整文件覆盖。",
|
|
52
|
+
].join("\n"),
|
|
53
|
+
parameters: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
filePath: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description:
|
|
59
|
+
"目标文件相对项目根目录(bun-nolo)的路径,例如: packages/chat/src/messages/web/NewComponent.tsx",
|
|
60
|
+
},
|
|
61
|
+
content: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description:
|
|
64
|
+
"要写入文件的完整内容。对于新建文件,就是文件的全部内容;对于覆盖已有文件,会用该内容替换原内容。",
|
|
65
|
+
},
|
|
66
|
+
overwrite: {
|
|
67
|
+
type: "boolean",
|
|
68
|
+
description:
|
|
69
|
+
"是否允许覆盖已存在的文件。缺省或为 false 时,如果文件已存在将报错;true 时允许覆盖。",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
required: ["filePath", "content"],
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ---- 预览执行:不写文件,只展示信息 ----
|
|
77
|
+
|
|
78
|
+
export async function writeFilePreviewFunc(
|
|
79
|
+
args: WriteFileArgs,
|
|
80
|
+
_thunkApi: any
|
|
81
|
+
): Promise<{ rawData: any; displayData?: string }> {
|
|
82
|
+
const { filePath, content, overwrite } = args;
|
|
83
|
+
|
|
84
|
+
if (!filePath || typeof filePath !== "string") {
|
|
85
|
+
throw new Error("writeFile 预览失败:必须提供有效的 filePath 字符串。");
|
|
86
|
+
}
|
|
87
|
+
if (typeof content !== "string") {
|
|
88
|
+
throw new Error("writeFile 预览失败:content 必须是字符串。");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const mode = overwrite ? "允许覆盖(已存在则覆盖)" : "仅新建(已存在则报错)";
|
|
92
|
+
const lengthInfo = `内容长度: ${content.length} 个字符`;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
rawData: {
|
|
96
|
+
previewOnly: true,
|
|
97
|
+
filePath,
|
|
98
|
+
overwrite: !!overwrite,
|
|
99
|
+
content,
|
|
100
|
+
},
|
|
101
|
+
displayData: `⏸️ 文件写入预览: ${filePath}(模式: ${mode},${lengthInfo})`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---- 真正执行:POST 到后端 /api/write-file ----
|
|
106
|
+
|
|
107
|
+
export async function writeFileFunc(
|
|
108
|
+
args: WriteFileArgs,
|
|
109
|
+
thunkApi: any,
|
|
110
|
+
context?: { parentMessageId?: string; signal?: AbortSignal }
|
|
111
|
+
): Promise<{ rawData: any; displayData?: string }> {
|
|
112
|
+
const { filePath, content, overwrite } = args;
|
|
113
|
+
|
|
114
|
+
if (!filePath || typeof filePath !== "string") {
|
|
115
|
+
throw new Error("写入文件失败:必须提供有效的 filePath 字符串。");
|
|
116
|
+
}
|
|
117
|
+
if (typeof content !== "string") {
|
|
118
|
+
throw new Error("写入文件失败:content 必须是字符串。");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const baseUrl = getToolBaseUrl(thunkApi);
|
|
123
|
+
|
|
124
|
+
if (!baseUrl) {
|
|
125
|
+
throw new Error("写入文件失败:无法获取 writeFile 服务器地址。");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const apiUrl = `${baseUrl.replace(/\/+$/, "")}/api/write-file`;
|
|
129
|
+
|
|
130
|
+
// 自举写入前增加 reload 抑制
|
|
131
|
+
bumpDevReloadSuppressIfSelfEditing(filePath);
|
|
132
|
+
|
|
133
|
+
const response = await fetch(apiUrl, {
|
|
134
|
+
method: "POST",
|
|
135
|
+
headers: { "Content-Type": "application/json" },
|
|
136
|
+
signal: context?.signal,
|
|
137
|
+
body: JSON.stringify({ filePath, content, overwrite }),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const status = response.status;
|
|
141
|
+
const textBody = await response.text();
|
|
142
|
+
let data: any = {};
|
|
143
|
+
try {
|
|
144
|
+
data = textBody ? JSON.parse(textBody) : {};
|
|
145
|
+
} catch {
|
|
146
|
+
// 非 JSON 的情况忽略解析错误
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 409:文件已存在但未允许覆盖 → 视为“写入冲突”,不抛异常
|
|
150
|
+
if (status === 409) {
|
|
151
|
+
const serverMsg =
|
|
152
|
+
data?.error ||
|
|
153
|
+
"写入文件失败:目标文件已存在,且 overwrite 未设置为 true。";
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
rawData: {
|
|
157
|
+
applied: false,
|
|
158
|
+
conflict: true,
|
|
159
|
+
fileExists: true,
|
|
160
|
+
filePath,
|
|
161
|
+
overwrite: !!overwrite,
|
|
162
|
+
serverMessage: serverMsg,
|
|
163
|
+
},
|
|
164
|
+
displayData: [
|
|
165
|
+
"⏸️ 检测到目标文件已存在,暂未写入。",
|
|
166
|
+
`- filePath: ${filePath}`,
|
|
167
|
+
`- 服务器消息: ${serverMsg}`,
|
|
168
|
+
"",
|
|
169
|
+
"如需覆盖,请在下一次调用 writeFile 时显式设置 overwrite: true。",
|
|
170
|
+
].join("\n"),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!response.ok || data?.error) {
|
|
175
|
+
const errMsg =
|
|
176
|
+
data?.error ||
|
|
177
|
+
`writeFile API 请求失败,状态码: ${status}. 响应: ${textBody}`;
|
|
178
|
+
console.error("writeFile API Error:", errMsg);
|
|
179
|
+
throw new Error(errMsg);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const created: boolean | undefined = data?.created;
|
|
183
|
+
const overwrittenFlag: boolean | undefined = data?.overwritten;
|
|
184
|
+
|
|
185
|
+
let actionDesc = "写入";
|
|
186
|
+
if (created === true) {
|
|
187
|
+
actionDesc = "新建";
|
|
188
|
+
} else if (overwrittenFlag === true) {
|
|
189
|
+
actionDesc = "覆盖";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
rawData: {
|
|
194
|
+
applied: true,
|
|
195
|
+
filePath,
|
|
196
|
+
overwrite: !!overwrite,
|
|
197
|
+
// 把这次写入的原始内容也附带上,方便调试
|
|
198
|
+
content,
|
|
199
|
+
// 服务端返回的详细结果(包括 newContent / diff 等)
|
|
200
|
+
response: data,
|
|
201
|
+
},
|
|
202
|
+
displayData: `✅ 已成功${actionDesc}文件: ${filePath}`,
|
|
203
|
+
};
|
|
204
|
+
} catch (error: any) {
|
|
205
|
+
console.error("执行 writeFile 时发生错误:", error);
|
|
206
|
+
throw new Error(
|
|
207
|
+
`写入文件 (${filePath}) 失败:${error?.message || String(error)}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// /ai/tools/youtubeScraperTool.ts
|
|
2
|
+
|
|
3
|
+
import { callApifyActor } from "./apifyActorClient";
|
|
4
|
+
|
|
5
|
+
export const youtubeScraperFunctionSchema = {
|
|
6
|
+
name: "youtubeScraper",
|
|
7
|
+
description:
|
|
8
|
+
"使用 Apify 的 YouTube Scraper 抓取 YouTube 视频、频道、播放列表等信息。支持 Direct URLs 或搜索关键词。",
|
|
9
|
+
parameters: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
directUrls: {
|
|
13
|
+
type: "array",
|
|
14
|
+
description:
|
|
15
|
+
"可选。视频/频道/播放列表等 URL 列表,将映射到 startUrls。",
|
|
16
|
+
items: { type: "string" },
|
|
17
|
+
},
|
|
18
|
+
searchQueries: {
|
|
19
|
+
type: "array",
|
|
20
|
+
description: "可选。搜索关键词数组,将映射到 searchQueries。",
|
|
21
|
+
items: { type: "string" },
|
|
22
|
+
},
|
|
23
|
+
maxResults: { type: "integer", minimum: 0 },
|
|
24
|
+
maxResultsShorts: { type: "integer", minimum: 0 },
|
|
25
|
+
maxResultStreams: { type: "integer", minimum: 0 },
|
|
26
|
+
downloadSubtitles: { type: "boolean" },
|
|
27
|
+
subtitlesLanguage: {
|
|
28
|
+
type: "string",
|
|
29
|
+
enum: [
|
|
30
|
+
"any",
|
|
31
|
+
"en",
|
|
32
|
+
"de",
|
|
33
|
+
"es",
|
|
34
|
+
"fr",
|
|
35
|
+
"it",
|
|
36
|
+
"ja",
|
|
37
|
+
"ko",
|
|
38
|
+
"nl",
|
|
39
|
+
"pt",
|
|
40
|
+
"ru",
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
subtitlesFormat: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: ["srt", "vtt", "xml", "plaintext"],
|
|
46
|
+
},
|
|
47
|
+
extraInput: {
|
|
48
|
+
type: "object",
|
|
49
|
+
description: "可选。直接透传给 Apify 的额外 input 字段,方便扩展。",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: [],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export async function youtubeScraperFunc(
|
|
57
|
+
args: {
|
|
58
|
+
directUrls?: string[];
|
|
59
|
+
searchQueries?: string[];
|
|
60
|
+
maxResults?: number;
|
|
61
|
+
maxResultsShorts?: number;
|
|
62
|
+
maxResultStreams?: number;
|
|
63
|
+
downloadSubtitles?: boolean;
|
|
64
|
+
subtitlesLanguage?: string;
|
|
65
|
+
subtitlesFormat?: string;
|
|
66
|
+
extraInput?: Record<string, any>;
|
|
67
|
+
},
|
|
68
|
+
thunkApi: any
|
|
69
|
+
) {
|
|
70
|
+
const {
|
|
71
|
+
directUrls,
|
|
72
|
+
searchQueries,
|
|
73
|
+
maxResults,
|
|
74
|
+
maxResultsShorts,
|
|
75
|
+
maxResultStreams,
|
|
76
|
+
downloadSubtitles,
|
|
77
|
+
subtitlesLanguage,
|
|
78
|
+
subtitlesFormat,
|
|
79
|
+
extraInput = {},
|
|
80
|
+
} = args;
|
|
81
|
+
|
|
82
|
+
if (
|
|
83
|
+
(!directUrls || directUrls.length === 0) &&
|
|
84
|
+
(!searchQueries || searchQueries.length === 0)
|
|
85
|
+
) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"YouTube 抓取:至少提供 directUrls 或 searchQueries 之一。"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const input: any = {
|
|
92
|
+
...extraInput,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (searchQueries?.length) input.searchQueries = searchQueries;
|
|
96
|
+
if (directUrls?.length) input.startUrls = directUrls.map((url) => ({ url }));
|
|
97
|
+
|
|
98
|
+
if (typeof maxResults === "number") input.maxResults = maxResults;
|
|
99
|
+
if (typeof maxResultsShorts === "number")
|
|
100
|
+
input.maxResultsShorts = maxResultsShorts;
|
|
101
|
+
if (typeof maxResultStreams === "number")
|
|
102
|
+
input.maxResultStreams = maxResultStreams;
|
|
103
|
+
if (typeof downloadSubtitles === "boolean")
|
|
104
|
+
input.downloadSubtitles = downloadSubtitles;
|
|
105
|
+
if (typeof subtitlesLanguage === "string")
|
|
106
|
+
input.subtitlesLanguage = subtitlesLanguage;
|
|
107
|
+
if (typeof subtitlesFormat === "string")
|
|
108
|
+
input.subtitlesFormat = subtitlesFormat;
|
|
109
|
+
|
|
110
|
+
return callApifyActor(thunkApi, {
|
|
111
|
+
actorId: "streamers~youtube-scraper",
|
|
112
|
+
input,
|
|
113
|
+
resultType: "datasetItems",
|
|
114
|
+
displayName: "YouTube Scraper",
|
|
115
|
+
});
|
|
116
|
+
}
|