nolo-cli 0.1.13 → 0.1.14

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.
Files changed (320) hide show
  1. package/README.md +9 -2
  2. package/agent-runtime/hostAdapter.ts +53 -0
  3. package/agent-runtime/index.ts +28 -0
  4. package/agent-runtime/localLoop.ts +62 -0
  5. package/agent-runtime/runtimeDecision.ts +70 -0
  6. package/agent-runtime/types.ts +87 -0
  7. package/agentRuntimeCommands.ts +139 -22
  8. package/agentRuntimeLocal.ts +7 -0
  9. package/ai/agent/_executeModel.ts +118 -0
  10. package/ai/agent/agentSlice.ts +544 -1
  11. package/ai/agent/appWorkingMemory.ts +126 -0
  12. package/ai/agent/avatarUtils.ts +24 -0
  13. package/ai/agent/buildEditingContext.ts +373 -0
  14. package/ai/agent/buildSystemPrompt.ts +532 -0
  15. package/ai/agent/cleanAgentMessages.ts +140 -0
  16. package/ai/agent/cliChatClient.ts +119 -0
  17. package/ai/agent/contextCompiler.ts +107 -0
  18. package/ai/agent/contextLayerContract.ts +44 -0
  19. package/ai/agent/createAgentSchema.ts +234 -0
  20. package/ai/agent/executeToolCall.ts +58 -0
  21. package/ai/agent/fetchAgentContexts.ts +42 -0
  22. package/ai/agent/generatePrompt.ts +3 -0
  23. package/ai/agent/getFullChatContextKeys.ts +168 -0
  24. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  25. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  26. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  27. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  28. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  29. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  30. package/ai/agent/planSlice.ts +259 -0
  31. package/ai/agent/referenceUtils.ts +229 -0
  32. package/ai/agent/runAgentBackground.ts +238 -0
  33. package/ai/agent/runAgentClientLoop.ts +138 -0
  34. package/ai/agent/runtimeGuidance.ts +97 -0
  35. package/ai/agent/runtimeServerBase.ts +37 -0
  36. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  37. package/ai/agent/startParallelAgentStreams.ts +424 -0
  38. package/ai/agent/startupProtocol.ts +53 -0
  39. package/ai/agent/streamAgentChatTurn.ts +1299 -0
  40. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  41. package/ai/agent/types.ts +71 -0
  42. package/ai/agent/utils/imageOutput.ts +39 -0
  43. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  44. package/ai/agent/utils/sortUtils.ts +250 -0
  45. package/ai/agent/web/referencePickerUtils.ts +146 -0
  46. package/ai/ai.locale.ts +1083 -0
  47. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  48. package/ai/chat/fetchUtils.native.ts +276 -0
  49. package/ai/chat/fetchUtils.ts +153 -0
  50. package/ai/chat/inlineImageUrlsForCustomProvider.ts +117 -0
  51. package/ai/chat/parseApiError.ts +64 -0
  52. package/ai/chat/parseMultilineSSE.ts +95 -0
  53. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  54. package/ai/chat/sendOpenAICompletionsRequest.ts +712 -0
  55. package/ai/chat/sendOpenAIResponseRequest.ts +512 -0
  56. package/ai/chat/shouldUseServerProxy.ts +18 -0
  57. package/ai/chat/sseClient.native.ts +91 -0
  58. package/ai/chat/sseClient.ts +67 -0
  59. package/ai/chat/streamReader.native.ts +31 -0
  60. package/ai/chat/streamReader.ts +62 -0
  61. package/ai/chat/updateTotalUsage.ts +72 -0
  62. package/ai/context/buildReferenceContext.ts +437 -0
  63. package/ai/context/calculateContextUsage.ts +133 -0
  64. package/ai/context/retention.ts +165 -0
  65. package/ai/context/tokenUtils.ts +78 -0
  66. package/ai/index.ts +1 -1
  67. package/ai/llm/agentCapabilities.ts +74 -0
  68. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  69. package/ai/llm/deepinfra.ts +28 -0
  70. package/ai/llm/fireworks.ts +68 -0
  71. package/ai/llm/generateRequestBody.ts +165 -0
  72. package/ai/llm/getModelContextWindow.ts +84 -0
  73. package/ai/llm/getNoloKey.ts +37 -0
  74. package/ai/llm/getPricing.ts +232 -0
  75. package/ai/llm/hooks/useModelPricing.ts +75 -0
  76. package/ai/llm/imagePricing.ts +66 -0
  77. package/ai/llm/isResponseAPIModel.ts +13 -0
  78. package/ai/llm/kimi.ts +18 -0
  79. package/ai/llm/mimo.ts +71 -0
  80. package/ai/llm/mistral.ts +22 -0
  81. package/ai/llm/modelAvatar.ts +427 -0
  82. package/ai/llm/models.ts +45 -0
  83. package/ai/llm/openrouterModels.ts +141 -0
  84. package/ai/llm/providers.ts +307 -0
  85. package/ai/llm/reasoningModels.ts +28 -0
  86. package/ai/llm/types.ts +59 -0
  87. package/ai/llm/usageRequestOptions.ts +59 -0
  88. package/ai/memory/capture.ts +148 -0
  89. package/ai/memory/consolidate.ts +104 -0
  90. package/ai/memory/delete.ts +147 -0
  91. package/ai/memory/overlay.ts +84 -0
  92. package/ai/memory/query.ts +38 -0
  93. package/ai/memory/queryShared.ts +160 -0
  94. package/ai/memory/rank.ts +105 -0
  95. package/ai/memory/recentRelationshipRecap.ts +247 -0
  96. package/ai/memory/remember.ts +167 -0
  97. package/ai/memory/runtime.ts +76 -0
  98. package/ai/memory/store.ts +20 -0
  99. package/ai/memory/storeShared.ts +76 -0
  100. package/ai/memory/types.ts +46 -0
  101. package/ai/memory/understanding.ts +349 -0
  102. package/ai/memory/understandingGreeting.ts +264 -0
  103. package/ai/messages/type.ts +20 -0
  104. package/ai/policy/personalizationDialog.ts +333 -0
  105. package/ai/policy/runtimePolicy.ts +440 -0
  106. package/ai/policy/selfUpdateFields.ts +48 -0
  107. package/ai/policy/types.ts +64 -0
  108. package/ai/skills/referenceRuntime.ts +274 -0
  109. package/ai/skills/skillDiagnostics.ts +251 -0
  110. package/ai/skills/skillDocBuilder.ts +139 -0
  111. package/ai/skills/skillDocProtocol.ts +434 -0
  112. package/ai/skills/skillReferenceSummary.ts +63 -0
  113. package/ai/skills/skillSummaryMarker.ts +26 -0
  114. package/ai/token/calculatePrice.ts +546 -0
  115. package/ai/token/db.ts +98 -0
  116. package/ai/token/externalToolCost.ts +321 -0
  117. package/ai/token/hooks/useRecords.ts +65 -0
  118. package/ai/token/missingUsageEstimate.ts +42 -0
  119. package/ai/token/modelUsageQuery.ts +252 -0
  120. package/ai/token/normalizeUsage.ts +84 -0
  121. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  122. package/ai/token/prepareTokenUsageData.ts +88 -0
  123. package/ai/token/query.ts +88 -0
  124. package/ai/token/queryUserTokens.ts +59 -0
  125. package/ai/token/resolveBillingTarget.ts +52 -0
  126. package/ai/token/saveTokenRecord.ts +53 -0
  127. package/ai/token/serverDialogProjection.ts +78 -0
  128. package/ai/token/serverTokenWriter.ts +143 -0
  129. package/ai/token/stats.ts +21 -0
  130. package/ai/token/tokenThunks.ts +24 -0
  131. package/ai/token/types.ts +93 -0
  132. package/ai/tools/agent/agentTools.ts +176 -0
  133. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  134. package/ai/tools/agent/callAgentTool.ts +139 -0
  135. package/ai/tools/agent/createAgentTool.ts +512 -0
  136. package/ai/tools/agent/createDialogTool.ts +69 -0
  137. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  138. package/ai/tools/agent/parallelBudget.ts +221 -0
  139. package/ai/tools/agent/presets/appBuilderPreset.ts +147 -0
  140. package/ai/tools/agent/runLlmTool.ts +96 -0
  141. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  142. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  143. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  144. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  145. package/ai/tools/agent/updateAgentTool.ts +96 -0
  146. package/ai/tools/agent/updateSelfTool.ts +113 -0
  147. package/ai/tools/amazonProductScraperTool.ts +86 -0
  148. package/ai/tools/apifyActorClient.ts +45 -0
  149. package/ai/tools/appEditGuard.ts +372 -0
  150. package/ai/tools/appReadSnapshot.ts +153 -0
  151. package/ai/tools/appTools.ts +1549 -0
  152. package/ai/tools/applyEditTool.ts +256 -0
  153. package/ai/tools/applyLineEditsTool.ts +312 -0
  154. package/ai/tools/browserTools/click.ts +33 -0
  155. package/ai/tools/browserTools/closeSession.ts +29 -0
  156. package/ai/tools/browserTools/common.ts +27 -0
  157. package/ai/tools/browserTools/openSession.ts +48 -0
  158. package/ai/tools/browserTools/readContent.ts +38 -0
  159. package/ai/tools/browserTools/selectOption.ts +46 -0
  160. package/ai/tools/browserTools/typeText.ts +42 -0
  161. package/ai/tools/category/createCategoryTool.ts +66 -0
  162. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  163. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  164. package/ai/tools/cfBrowserTools.ts +319 -0
  165. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  166. package/ai/tools/checkEnvTool.ts +65 -0
  167. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  168. package/ai/tools/codeSearchTool.ts +111 -0
  169. package/ai/tools/codeTools.ts +101 -0
  170. package/ai/tools/createDocTool.ts +132 -0
  171. package/ai/tools/createPlanTool.ts +999 -0
  172. package/ai/tools/createSkillDocTool.ts +155 -0
  173. package/ai/tools/createWorkflowTool.ts +154 -0
  174. package/ai/tools/deepseekOcrTool.ts +34 -0
  175. package/ai/tools/delayTool.ts +31 -0
  176. package/ai/tools/deleteSpacesTool.ts +325 -0
  177. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  178. package/ai/tools/devReloadUtils.ts +29 -0
  179. package/ai/tools/dialogMessageSearch.ts +137 -0
  180. package/ai/tools/doctorSkillTool.ts +72 -0
  181. package/ai/tools/ecommerceScraperTool.ts +86 -0
  182. package/ai/tools/emailTools.ts +549 -0
  183. package/ai/tools/evalSkillTool.ts +92 -0
  184. package/ai/tools/exaSearchTool.ts +64 -0
  185. package/ai/tools/execBashTool.ts +379 -0
  186. package/ai/tools/executeSqlTool.ts +192 -0
  187. package/ai/tools/fetchWebpageSupport.ts +309 -0
  188. package/ai/tools/fetchWebpageTool.ts +84 -0
  189. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  190. package/ai/tools/generateDocxTool.ts +215 -0
  191. package/ai/tools/googleSearchScraperTool.ts +106 -0
  192. package/ai/tools/importDataTool.ts +133 -0
  193. package/ai/tools/importSkillTool.ts +162 -0
  194. package/ai/tools/index.ts +1927 -0
  195. package/ai/tools/listFilesTool.ts +82 -0
  196. package/ai/tools/listUserSpacesTool.ts +113 -0
  197. package/ai/tools/modelUsageTools.ts +199 -0
  198. package/ai/tools/olmOcrTool.ts +34 -0
  199. package/ai/tools/openaiImageTool.ts +267 -0
  200. package/ai/tools/prepareTools.ts +23 -0
  201. package/ai/tools/readDocTool.ts +84 -0
  202. package/ai/tools/readFileTool.ts +211 -0
  203. package/ai/tools/readTool.ts +163 -0
  204. package/ai/tools/readXPostTool.ts +233 -0
  205. package/ai/tools/rememberMemoryTool.ts +84 -0
  206. package/ai/tools/remotionVideoTool.ts +151 -0
  207. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  208. package/ai/tools/searchRepoTool.ts +115 -0
  209. package/ai/tools/searchWorkspaceTool.ts +259 -0
  210. package/ai/tools/skillFollowup.ts +86 -0
  211. package/ai/tools/surfWeatherTool.ts +169 -0
  212. package/ai/tools/table/addTableRowTool.ts +217 -0
  213. package/ai/tools/table/createTableTool.ts +315 -0
  214. package/ai/tools/table/rowTools.ts +366 -0
  215. package/ai/tools/table/schemaTools.ts +244 -0
  216. package/ai/tools/table/shareTableTool.ts +148 -0
  217. package/ai/tools/table/toolShared.ts +129 -0
  218. package/ai/tools/toolApiClient.ts +198 -0
  219. package/ai/tools/toolNameAliases.ts +57 -0
  220. package/ai/tools/toolResultError.ts +42 -0
  221. package/ai/tools/toolRunSlice.ts +303 -0
  222. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  223. package/ai/tools/toolVisibility.ts +4 -0
  224. package/ai/tools/types.ts +20 -0
  225. package/ai/tools/uiAskChoiceTool.ts +104 -0
  226. package/ai/tools/updateContentTitleTool.ts +84 -0
  227. package/ai/tools/updateDocTool.ts +105 -0
  228. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  229. package/ai/tools/whisperTool.ts +77 -0
  230. package/ai/tools/writeFileTool.ts +210 -0
  231. package/ai/tools/youtubeScraperTool.ts +116 -0
  232. package/ai/tools/ziweiChartTool.ts +678 -0
  233. package/ai/types.ts +55 -0
  234. package/ai/workflow/workflowExecutor.ts +323 -0
  235. package/ai/workflow/workflowSlice.ts +73 -0
  236. package/ai/workflow/workflowTypes.ts +106 -0
  237. package/client/agentRun.test.ts +240 -0
  238. package/client/agentRun.ts +182 -19
  239. package/client/compactDialog.test.ts +238 -0
  240. package/client/localRuntimeAdapter.test.ts +135 -0
  241. package/client/localRuntimeAdapter.ts +244 -0
  242. package/client/profileConfig.test.ts +40 -0
  243. package/client/streamingOutput.test.ts +22 -0
  244. package/client/streamingOutput.ts +38 -0
  245. package/commandRegistry.ts +9 -2
  246. package/connector-experimental/index.ts +5 -0
  247. package/database/actions/cacheMergedUserData.ts +64 -0
  248. package/database/actions/common.ts +242 -0
  249. package/database/actions/deleteFile.ts +40 -0
  250. package/database/actions/fetchUserData.ts +16 -0
  251. package/database/actions/fileContent.ts +125 -0
  252. package/database/actions/patch.ts +155 -0
  253. package/database/actions/read.ts +337 -0
  254. package/database/actions/readAndWait.ts +224 -0
  255. package/database/actions/readRequestManager.ts +120 -0
  256. package/database/actions/remove.ts +94 -0
  257. package/database/actions/replication.ts +366 -0
  258. package/database/actions/upload.ts +174 -0
  259. package/database/actions/upsert.ts +56 -0
  260. package/database/actions/write.ts +126 -0
  261. package/database/client/db.native.ts +73 -0
  262. package/database/client/db.ts +51 -0
  263. package/database/client/fetchUserData.ts +61 -0
  264. package/database/client/handleError.ts +19 -0
  265. package/database/client/queryRequest.ts +21 -0
  266. package/database/config.ts +21 -0
  267. package/database/dbActionThunks.ts +1 -0
  268. package/database/dbSlice.ts +149 -0
  269. package/database/email.ts +42 -0
  270. package/database/fileRing.ts +51 -0
  271. package/database/fileSharding.ts +70 -0
  272. package/database/fileStorage.native.ts +92 -0
  273. package/database/fileStorage.ts +232 -0
  274. package/database/fileUrl.ts +34 -0
  275. package/database/hooks/useUserData.ts +489 -0
  276. package/database/index.ts +1 -0
  277. package/database/keys.ts +765 -0
  278. package/database/queryPrefixes.ts +14 -0
  279. package/database/requests.ts +443 -0
  280. package/database/runtimeServerContext.ts +35 -0
  281. package/database/server/MemoryDB.ts +76 -0
  282. package/database/server/actorAccess.ts +76 -0
  283. package/database/server/agentDelegation.ts +124 -0
  284. package/database/server/coreDataOwnership.ts +13 -0
  285. package/database/server/coreDataProxy.ts +76 -0
  286. package/database/server/cybotReadonly.ts +18 -0
  287. package/database/server/dataHandlers.ts +111 -0
  288. package/database/server/db.ts +118 -0
  289. package/database/server/dbPath.ts +20 -0
  290. package/database/server/delete.ts +499 -0
  291. package/database/server/emailRepository.ts +1480 -0
  292. package/database/server/ensureDbOpen.ts +12 -0
  293. package/database/server/fileRead.ts +337 -0
  294. package/database/server/fileService.ts +436 -0
  295. package/database/server/handleTransaction.ts +86 -0
  296. package/database/server/patch.ts +282 -0
  297. package/database/server/query.ts +138 -0
  298. package/database/server/read.ts +325 -0
  299. package/database/server/resourceAccess.ts +211 -0
  300. package/database/server/routes.ts +110 -0
  301. package/database/server/spaceMemberAuthority.ts +67 -0
  302. package/database/server/upload.ts +159 -0
  303. package/database/server/write.ts +494 -0
  304. package/database/server/writeAuthority.ts +133 -0
  305. package/database/sqliteDb.ts +46 -0
  306. package/database/table/deleteTable.ts +120 -0
  307. package/database/tenantPlacement.ts +57 -0
  308. package/database/tombstones.ts +52 -0
  309. package/database/userDataLoadDecision.ts +17 -0
  310. package/database/userDataMerge.ts +95 -0
  311. package/database/userPreferenceRegister.ts +108 -0
  312. package/database/utils/dbPath.ts +47 -0
  313. package/database/utils/ulid.native.ts +6 -0
  314. package/database/utils/ulid.ts +1 -0
  315. package/index.ts +25 -15
  316. package/localRuntimeDb.ts +28 -0
  317. package/package.json +16 -4
  318. package/runtimeModeArgs.ts +33 -0
  319. package/tui/readlineWorkspace.ts +1 -0
  320. package/tui/session.ts +22 -0
@@ -0,0 +1,165 @@
1
+ // ai/llm/generateRequestBody.ts
2
+ import { Agent, Message } from "app/types";
3
+ import { generateOpenAIRequestBody } from "integrations/openai/generateOpenAIRequestBody";
4
+ import { generateResponseRequestBody } from "integrations/openai/generateResponseRequestBody";
5
+
6
+ import { isResponseAPIModel } from "./isResponseAPIModel";
7
+ import { Contexts } from "../types";
8
+ import { getModelConfig } from "./providers";
9
+ import type { Model } from "./types";
10
+
11
+ export interface GenerateRequestBodyArgs {
12
+ agentConfig: Agent;
13
+ messages: Message[]; // 动态历史消息/本轮新增
14
+ userInput: string; // 本次用户输入
15
+ contexts?: Contexts; // 可选上下文
16
+ stableMessages?: Message[]; // 稳定的、已截断的历史快照(用于缓存)
17
+ prependSystemPrompt?: boolean;
18
+ }
19
+
20
+ const IMAGE_OUTPUT_EXECUTION_PROMPT = [
21
+ "When the conversation already includes an input image and the user asks to change any visual attribute such as hairstyle, glasses, outfit, makeup, background, composition, or style, default to returning the edited image in this turn.",
22
+ "When the user asks you to generate, edit, redraw, or return an image, you must return the image in this turn.",
23
+ "Do not answer with only a textual description.",
24
+ "Do not claim that you uploaded, attached, or sent an image unless the response actually includes image output.",
25
+ "Only return text without an image when the user explicitly asks for analysis, recommendations, or explanation without generating an image.",
26
+ ].join(" ");
27
+
28
+ type ImageOutputSettings = {
29
+ shouldEnableImage: boolean;
30
+ modelConfig: Model | null;
31
+ };
32
+
33
+ const getImageOutputSettings = (
34
+ agentConfig: Agent,
35
+ provider: string
36
+ ): ImageOutputSettings => {
37
+ const modelName = agentConfig.model;
38
+ if (!modelName) {
39
+ return { shouldEnableImage: false, modelConfig: null };
40
+ }
41
+
42
+ try {
43
+ const modelConfig = getModelConfig(provider as any, modelName);
44
+ const hasImageOutput = !!modelConfig.hasImageOutput;
45
+ const isExplicitlyDisabled = agentConfig.imageConfig?.enabled === false;
46
+ return {
47
+ shouldEnableImage: hasImageOutput && !isExplicitlyDisabled,
48
+ modelConfig,
49
+ };
50
+ } catch {
51
+ return { shouldEnableImage: false, modelConfig: null };
52
+ }
53
+ };
54
+
55
+ const withImageOutputPromptGuard = (
56
+ agentConfig: Agent,
57
+ shouldEnableImage: boolean
58
+ ): Agent => {
59
+ if (!shouldEnableImage) return agentConfig;
60
+
61
+ const basePrompt = typeof agentConfig.prompt === "string" ? agentConfig.prompt.trim() : "";
62
+ if (basePrompt.includes(IMAGE_OUTPUT_EXECUTION_PROMPT)) {
63
+ return agentConfig;
64
+ }
65
+
66
+ return {
67
+ ...agentConfig,
68
+ prompt: basePrompt
69
+ ? `${basePrompt}\n\n${IMAGE_OUTPUT_EXECUTION_PROMPT}`
70
+ : IMAGE_OUTPUT_EXECUTION_PROMPT,
71
+ };
72
+ };
73
+
74
+ /**
75
+ * 根据模型元数据 + Agent 配置,为 chat/completions 请求体注入
76
+ * - modalities: ["image", "text"]
77
+ * - image_config: { aspect_ratio, image_size }
78
+ */
79
+ const applyImageConfigIfNeeded = (
80
+ body: any,
81
+ agentConfig: Agent,
82
+ provider: string
83
+ ): any => {
84
+ const { shouldEnableImage, modelConfig } = getImageOutputSettings(
85
+ agentConfig,
86
+ provider
87
+ );
88
+ const agentImageCfg = agentConfig.imageConfig;
89
+
90
+ if (!shouldEnableImage || !modelConfig) {
91
+ return body;
92
+ }
93
+
94
+ // 1) 处理 modalities
95
+ // 优先级:Agent.forceModalities > Model.defaultModalities > requiresImageModalities ? ["image","text"] : 不强制
96
+ const modalities: Array<"text" | "image"> | undefined =
97
+ agentImageCfg?.forceModalities ??
98
+ modelConfig.defaultModalities ??
99
+ (modelConfig.requiresImageModalities
100
+ ? (["image", "text"] as Array<"text" | "image">)
101
+ : undefined);
102
+
103
+ if (modalities && !body.modalities) {
104
+ body.modalities = modalities;
105
+ }
106
+
107
+ // 2) 处理 image_config(仅在模型声明 supportsImageConfig 时生效)
108
+ if (!modelConfig.supportsImageConfig) {
109
+ return body;
110
+ }
111
+
112
+ const aspectRatio = agentImageCfg?.aspectRatio;
113
+ const imageSize = agentImageCfg?.imageSize;
114
+
115
+ if (!aspectRatio && !imageSize) {
116
+ // Agent 未指定任何 image_config 参数,则沿用模型默认
117
+ return body;
118
+ }
119
+
120
+ body.image_config = {
121
+ ...(body.image_config ?? {}),
122
+ ...(aspectRatio ? { aspect_ratio: aspectRatio } : {}),
123
+ ...(imageSize ? { image_size: imageSize } : {}),
124
+ };
125
+
126
+ return body;
127
+ };
128
+
129
+ export const generateRequestBody = ({
130
+ agentConfig,
131
+ messages,
132
+ contexts,
133
+ stableMessages,
134
+ prependSystemPrompt = true,
135
+ }: GenerateRequestBodyArgs) => {
136
+ const provider = (agentConfig.provider || "").toLowerCase();
137
+ const imageSettings = getImageOutputSettings(agentConfig, provider);
138
+ const agentConfigForRequest = withImageOutputPromptGuard(
139
+ agentConfig,
140
+ imageSettings.shouldEnableImage
141
+ );
142
+
143
+ // 1) Response API 走新版 /v1/responses(目前主要是 OpenAI)
144
+ if (isResponseAPIModel(agentConfigForRequest)) {
145
+ return generateResponseRequestBody(
146
+ agentConfigForRequest,
147
+ [...(stableMessages ?? []), ...messages],
148
+ contexts,
149
+ prependSystemPrompt
150
+ );
151
+ }
152
+
153
+ // 2) 其余走老版 chat/completions
154
+ const baseBody = generateOpenAIRequestBody(
155
+ agentConfigForRequest,
156
+ provider,
157
+ messages,
158
+ contexts,
159
+ stableMessages,
160
+ prependSystemPrompt
161
+ );
162
+
163
+ // 在基础请求体上注入图像相关配置(如果模型 + Agent 都支持/需要)
164
+ return applyImageConfigIfNeeded(baseBody, agentConfigForRequest, provider);
165
+ };
@@ -0,0 +1,84 @@
1
+ // ai/llm/getModelContextWindow.ts
2
+
3
+ /**
4
+ * 获取指定模型的 Context Window 大小。
5
+ *
6
+ * 复用 ALL_MODELS 数据源,保持与模型选择器的一致性。
7
+ */
8
+
9
+ import { ALL_MODELS, type ModelWithProvider } from "./models";
10
+
11
+ // 默认 Context Window(用于未知模型)
12
+ const DEFAULT_CONTEXT_WINDOW = 128000;
13
+
14
+ // 缓存模型映射表
15
+ let modelMap: Map<string, ModelWithProvider> | null = null;
16
+
17
+ const getModelMap = (): Map<string, ModelWithProvider> => {
18
+ if (!modelMap) {
19
+ modelMap = new Map();
20
+ for (const model of ALL_MODELS) {
21
+ modelMap.set(model.name, model);
22
+ // 也按 displayName 索引(如果有)
23
+ if (model.displayName) {
24
+ modelMap.set(model.displayName.toLowerCase(), model);
25
+ }
26
+ }
27
+ }
28
+ return modelMap;
29
+ };
30
+
31
+ /**
32
+ * 根据模型名称获取 Context Window 大小
33
+ * @param modelName 模型名称(如 "gpt-4o", "claude-3-5-sonnet")
34
+ * @returns Context Window 大小(tokens)
35
+ */
36
+ export const getModelContextWindow = (modelName: string): number => {
37
+ if (!modelName) return DEFAULT_CONTEXT_WINDOW;
38
+
39
+ const map = getModelMap();
40
+ const model =
41
+ map.get(modelName) ||
42
+ map.get(modelName.toLowerCase());
43
+
44
+ if (model?.contextWindow) {
45
+ return typeof model.contextWindow === "number"
46
+ ? model.contextWindow
47
+ : DEFAULT_CONTEXT_WINDOW;
48
+ }
49
+
50
+ return DEFAULT_CONTEXT_WINDOW;
51
+ };
52
+
53
+ /**
54
+ * 获取模型的完整信息
55
+ */
56
+ export const getModelInfo = (modelName: string): ModelWithProvider | null => {
57
+ if (!modelName) return null;
58
+
59
+ const map = getModelMap();
60
+ return (
61
+ map.get(modelName) ||
62
+ map.get(modelName.toLowerCase()) ||
63
+ null
64
+ );
65
+ };
66
+
67
+ /**
68
+ * 获取模型的 Max Output Tokens
69
+ */
70
+ export const getModelMaxOutputTokens = (modelName: string): number => {
71
+ const model = getModelInfo(modelName);
72
+ if (model?.maxOutputTokens && typeof model.maxOutputTokens === "number") {
73
+ return model.maxOutputTokens;
74
+ }
75
+ return 4096; // 保守默认值
76
+ };
77
+
78
+ /**
79
+ * 获取模型是否支持视觉
80
+ */
81
+ export const getModelHasVision = (modelName: string): boolean => {
82
+ const model = getModelInfo(modelName);
83
+ return model?.hasVision ?? false;
84
+ };
@@ -0,0 +1,37 @@
1
+ import { availableProviderOptions } from "./providers";
2
+
3
+ const firstConfiguredKey = (...keys: Array<string | undefined>) =>
4
+ keys.find((key) => key?.trim())?.trim();
5
+
6
+ export const getNoloKey = (
7
+ provider: (typeof availableProviderOptions)[number] | "deepinfra"
8
+ ) => {
9
+ switch (provider) {
10
+ case "anthropic":
11
+ return process.env.ANTHROPIC_API_KEY;
12
+ case "google":
13
+ return process.env.GOOGLE_API_KEY;
14
+ case "deepseek":
15
+ return process.env.DEEPSEEK_API_KEY;
16
+ case "openrouter":
17
+ return process.env.OPENROUTER_API_KEY;
18
+ case "fireworks":
19
+ return process.env.FIREWORKS_API_KEY;
20
+ case "openai":
21
+ return firstConfiguredKey(
22
+ process.env.OPENAI_KEY,
23
+ process.env.OPENAI_API_KEY
24
+ );
25
+ case "mistral":
26
+ return process.env.MISTRAL_KEY;
27
+ case "deepinfra":
28
+ return process.env.DEEPINFRA_API_KEY;
29
+ case "mimo":
30
+ return process.env.MIMO_API_KEY || process.env.OPENCODE_API_KEY;
31
+
32
+ // case "xai":
33
+ // return process.env.XAI_API_KEY;
34
+ default:
35
+ return null;
36
+ }
37
+ };
@@ -0,0 +1,232 @@
1
+ import type { Agent } from "app/types";
2
+ import { getModelConfig } from "ai/llm/providers";
3
+ import {
4
+ isOpenAIFlexPricedModel,
5
+ resolveOpenAIServiceTier,
6
+ } from "integrations/openai/flexTier";
7
+ import { getPublicImageAgentDefaultProfile } from "ai/agent/utils/publicImageAgentMode";
8
+ import { getApproxPricePerImage } from "./imagePricing";
9
+
10
+ interface ModelPricing {
11
+ inputPrice: number;
12
+ inputCacheHitPrice: number;
13
+ outputPrice: number;
14
+ }
15
+
16
+ interface Prices {
17
+ cybotInput: number;
18
+ cybotOutput: number;
19
+ serverInput: number;
20
+ serverOutput: number;
21
+ }
22
+
23
+ const MAX_OUTPUT_TOKENS = 8192; // 单次返回最大 token 数
24
+
25
+ export const getModelPricing = (
26
+ provider: string,
27
+ modelName: string,
28
+ requestedServiceTier?: string
29
+ ): ModelPricing | null => {
30
+ let model;
31
+ try {
32
+ model = getModelConfig(provider as any, modelName);
33
+ } catch {
34
+ return null;
35
+ }
36
+
37
+ if (!model?.price) return null;
38
+
39
+ return getModelPricingForModel(provider, modelName, model, requestedServiceTier);
40
+ };
41
+
42
+ export const getModelPricingForModel = (
43
+ provider: string,
44
+ modelName: string,
45
+ model: { price?: { input: number; inputCacheHit?: number; output: number } | null },
46
+ requestedServiceTier?: string
47
+ ): ModelPricing | null => {
48
+ if (!model.price) return null;
49
+ const normalizedRequestedServiceTier =
50
+ requestedServiceTier === "flex" || requestedServiceTier === "default"
51
+ ? requestedServiceTier
52
+ : undefined;
53
+ const shouldApplyFlexDiscount =
54
+ provider === "openai" &&
55
+ (normalizedRequestedServiceTier === "flex" ||
56
+ resolveOpenAIServiceTier({
57
+ providerName: provider,
58
+ model: modelName,
59
+ }) === "flex") &&
60
+ !isOpenAIFlexPricedModel(modelName);
61
+ const priceMultiplier = shouldApplyFlexDiscount ? 0.5 : 1;
62
+
63
+ return {
64
+ inputPrice: model.price.input * priceMultiplier,
65
+ inputCacheHitPrice:
66
+ typeof model.price.inputCacheHit === "number"
67
+ ? model.price.inputCacheHit * priceMultiplier
68
+ : 0,
69
+ outputPrice: model.price.output * priceMultiplier,
70
+ };
71
+ };
72
+
73
+ export const getPrices = (config: any, serverPrices: any): Prices => ({
74
+ cybotInput: Number(config?.inputPrice ?? 0),
75
+ cybotOutput: Number(config?.outputPrice ?? 0),
76
+ serverInput: Number(serverPrices?.inputPrice ?? 0),
77
+ serverOutput: Number(serverPrices?.outputPrice ?? 0),
78
+ });
79
+
80
+ /**
81
+ * 计算最终价格:
82
+ * - 从所有价格中取「每百万 tokens 价格」的最大值
83
+ * - 再换算成单 token 价格
84
+ * - 再乘以最大输出 token 数
85
+ */
86
+ export const getFinalPrice = (prices: Prices): number => {
87
+ // 1. 取出所有价格字段
88
+ const rawValues = Object.values(prices);
89
+
90
+ // 2. 过滤出合法数字(去掉 NaN、Infinity、null/undefined 等)
91
+ const validValues = rawValues.filter(
92
+ (value) => typeof value === "number" && Number.isFinite(value)
93
+ );
94
+
95
+ // 3. 没有合法值时返回 0,避免 Math.max(...[]) 抛错
96
+ if (validValues.length === 0) {
97
+ return 0;
98
+ }
99
+
100
+ // 4. 找到每百万 token 的最高单价
101
+ const maxPricePerMillion = Math.max(...validValues);
102
+
103
+ // 5. 换算成单 token 价格
104
+ const maxPricePerToken = maxPricePerMillion / 1_000_000;
105
+
106
+ // 6. 计算 8192 个 token 的费用
107
+ return maxPricePerToken * MAX_OUTPUT_TOKENS;
108
+ };
109
+
110
+ /**
111
+ * 每次对话估算的典型 token 用量(输入含历史上下文,输出为单次回复)
112
+ * 仅用于 UI 展示的估算,并非计费依据。
113
+ */
114
+ const TYPICAL_INPUT_TOKENS = 500;
115
+ const TYPICAL_OUTPUT_TOKENS = 300;
116
+
117
+ export type AgentPriceHint =
118
+ | {
119
+ type: "per_image";
120
+ amount: number;
121
+ labelKey?: "defaultImageProfileEstimate";
122
+ profileLabel?: string;
123
+ }
124
+ | { type: "per_turn"; amount: number };
125
+
126
+ export interface CompactTurnPriceDisplay {
127
+ amountText: string;
128
+ unitCount: number;
129
+ }
130
+
131
+ const trimTrailingZeros = (value: string): string => {
132
+ if (!value.includes(".")) return value;
133
+ return value.replace(/\.?0+$/, "");
134
+ };
135
+
136
+ /**
137
+ * 根据 agent 配置返回用户可读的价格提示:
138
+ * - 图像 agent(imageConfig.enabled):从 model config 查 pricePerImage,以每张为单位
139
+ * - 文字 agent:用典型 token 用量估算每次对话费用
140
+ * - 无价格信息则返回 null
141
+ */
142
+ export const getAgentPriceHint = (
143
+ agent: Pick<
144
+ Agent,
145
+ | "inputPrice"
146
+ | "outputPrice"
147
+ | "model"
148
+ | "imageModel"
149
+ | "provider"
150
+ | "imageConfig"
151
+ | "imageWorkflow"
152
+ >
153
+ ): AgentPriceHint | null => {
154
+ const imagePriceModel = agent.imageModel ?? agent.model;
155
+
156
+ if (agent.imageConfig?.enabled && agent.provider && imagePriceModel) {
157
+ try {
158
+ const model = getModelConfig(agent.provider as any, imagePriceModel);
159
+ const mode = agent.imageWorkflow;
160
+
161
+ if (mode === "generate") {
162
+ // For generator agents, always use the default profile price
163
+ const defaultProfile = getPublicImageAgentDefaultProfile("generate");
164
+ const pricePerImage = getApproxPricePerImage(model, "1K");
165
+ if (typeof pricePerImage === "number") {
166
+ return {
167
+ type: "per_image",
168
+ amount: pricePerImage,
169
+ labelKey: "defaultImageProfileEstimate",
170
+ profileLabel: `${defaultProfile.quality} · ${defaultProfile.size}`,
171
+ };
172
+ }
173
+ } else {
174
+ const pricePerImage = getApproxPricePerImage(
175
+ model,
176
+ agent.imageConfig?.imageSize
177
+ );
178
+ if (typeof pricePerImage === "number") {
179
+ return { type: "per_image", amount: pricePerImage };
180
+ }
181
+ }
182
+ } catch {
183
+ // Fall through to token-based pricing or null.
184
+ }
185
+ }
186
+
187
+ const inputPrice = agent.inputPrice ?? 0;
188
+ const outputPrice = agent.outputPrice ?? 0;
189
+ if (inputPrice === 0 && outputPrice === 0) return null;
190
+
191
+ const amount =
192
+ (inputPrice * TYPICAL_INPUT_TOKENS + outputPrice * TYPICAL_OUTPUT_TOKENS) /
193
+ 1_000_000;
194
+ return { type: "per_turn", amount };
195
+ };
196
+
197
+ /** 将金额格式化为用户可读字符串(2 位有效数字,不使用科学计数法)*/
198
+ export const formatPriceAmount = (amount: number): string => {
199
+ if (amount >= 0.1) return amount.toFixed(2);
200
+ if (amount >= 0.01) return amount.toFixed(3);
201
+ if (amount >= 0.001) return amount.toFixed(4);
202
+ if (amount >= 0.0001) return amount.toFixed(5);
203
+ return amount.toFixed(6);
204
+ };
205
+
206
+ export const formatCompactTurnPrice = (
207
+ amount: number
208
+ ): CompactTurnPriceDisplay => {
209
+ if (amount <= 0) {
210
+ return { amountText: "0", unitCount: 1 };
211
+ }
212
+
213
+ if (amount >= 0.01) {
214
+ return {
215
+ amountText: trimTrailingZeros(formatPriceAmount(amount)),
216
+ unitCount: 1,
217
+ };
218
+ }
219
+
220
+ const perHundred = amount * 100;
221
+ if (perHundred >= 0.01) {
222
+ return {
223
+ amountText: trimTrailingZeros(formatPriceAmount(perHundred)),
224
+ unitCount: 100,
225
+ };
226
+ }
227
+
228
+ return {
229
+ amountText: trimTrailingZeros(formatPriceAmount(amount * 1000)),
230
+ unitCount: 1000,
231
+ };
232
+ };
@@ -0,0 +1,75 @@
1
+ import { useState, useEffect, useRef } from "react";
2
+ import { getModelsByProvider, getProviderByModelName } from "ai/llm/providers";
3
+ import type { Model, ModelPrice } from "ai/llm/types";
4
+
5
+ type ModelWithLegacyPricing = Partial<Model> & {
6
+ pricing?: ModelPrice | null;
7
+ };
8
+
9
+ export const resolveModelPrice = (
10
+ model?: ModelWithLegacyPricing | null
11
+ ): ModelPrice => {
12
+ const price = model?.price ?? model?.pricing;
13
+ return {
14
+ input: typeof price?.input === "number" ? price.input : 0,
15
+ output: typeof price?.output === "number" ? price.output : 0,
16
+ cachingWrite: price?.cachingWrite,
17
+ cachingRead: price?.cachingRead,
18
+ inputCacheHit: price?.inputCacheHit,
19
+ };
20
+ };
21
+
22
+ const useModelPricing = (
23
+ provider: string,
24
+ modelName: string,
25
+ setValue?: (name: string, value: number) => void
26
+ ) => {
27
+ const [models, setModels] = useState<Model[]>([]);
28
+ const [inputPrice, setInputPrice] = useState<number>(0);
29
+ const [outputPrice, setOutputPrice] = useState<number>(0);
30
+ const setValueRef = useRef(setValue);
31
+
32
+ useEffect(() => {
33
+ setValueRef.current = setValue;
34
+ }, [setValue]);
35
+
36
+ useEffect(() => {
37
+ // 若 provider 为空,从模型名反查(防止 provider 漏传导致定价缺失)
38
+ const resolvedProvider = provider || getProviderByModelName(modelName);
39
+ if (!resolvedProvider) return;
40
+ setModels(getModelsByProvider(resolvedProvider));
41
+ }, [provider, modelName]);
42
+
43
+ useEffect(() => {
44
+ const selectedModel = models.find((model) => model.name === modelName);
45
+ if (selectedModel) {
46
+ const price = resolveModelPrice(selectedModel as ModelWithLegacyPricing);
47
+ setInputPrice((current) => current === price.input ? current : price.input);
48
+ setOutputPrice((current) => current === price.output ? current : price.output);
49
+
50
+ if (setValueRef.current) {
51
+ setValueRef.current("inputPrice", price.input);
52
+ setValueRef.current("outputPrice", price.output);
53
+ }
54
+ }
55
+ }, [models, modelName]);
56
+
57
+ const updateInputPrice = (value: number) => {
58
+ setInputPrice(value);
59
+ if (setValue) setValue("inputPrice", value);
60
+ };
61
+
62
+ const updateOutputPrice = (value: number) => {
63
+ setOutputPrice(value);
64
+ if (setValue) setValue("outputPrice", value);
65
+ };
66
+
67
+ return {
68
+ inputPrice,
69
+ outputPrice,
70
+ setInputPrice: updateInputPrice,
71
+ setOutputPrice: updateOutputPrice,
72
+ };
73
+ };
74
+
75
+ export default useModelPricing;
@@ -0,0 +1,66 @@
1
+ import type { Agent } from "app/types";
2
+ import { findModelConfig } from "./providers";
3
+ import type { Model } from "./types";
4
+
5
+ export type ImageSizeKey = "1K" | "2K" | "4K";
6
+
7
+ type ImagePricingModel = Pick<
8
+ Model,
9
+ "pricePerImage" | "imageTokenPricePerMillion" | "imageOutputTokenEstimateBySize"
10
+ >;
11
+
12
+ const DEFAULT_IMAGE_SIZE: ImageSizeKey = "1K";
13
+
14
+ export const getApproxPricePerImage = (
15
+ model: ImagePricingModel | null | undefined,
16
+ requestedSize?: ImageSizeKey
17
+ ): number | undefined => {
18
+ if (!model) return undefined;
19
+
20
+ if (typeof model.pricePerImage === "number") {
21
+ return model.pricePerImage;
22
+ }
23
+
24
+ if (typeof model.imageTokenPricePerMillion !== "number") {
25
+ return undefined;
26
+ }
27
+
28
+ const tokenEstimates = model.imageOutputTokenEstimateBySize;
29
+ if (!tokenEstimates) return undefined;
30
+
31
+ const resolvedSize =
32
+ requestedSize && typeof tokenEstimates[requestedSize] === "number"
33
+ ? requestedSize
34
+ : DEFAULT_IMAGE_SIZE;
35
+ const outputTokens = tokenEstimates[resolvedSize];
36
+
37
+ if (typeof outputTokens !== "number") {
38
+ return undefined;
39
+ }
40
+
41
+ return (model.imageTokenPricePerMillion * outputTokens) / 1_000_000;
42
+ };
43
+
44
+ const OPENAI_IMAGE_FALLBACK_MODEL = "gpt-image-2";
45
+
46
+ export const getApproxAgentPricePerImage = (
47
+ agent: Pick<Agent, "provider" | "model" | "imageConfig">
48
+ ): number | undefined => {
49
+ if (!agent.imageConfig?.enabled || !agent.provider || !agent.model) {
50
+ return undefined;
51
+ }
52
+
53
+ const requestedSize = agent.imageConfig.imageSize as ImageSizeKey | undefined;
54
+ const directModel = findModelConfig(agent.provider, agent.model);
55
+ const directPrice = getApproxPricePerImage(directModel, requestedSize);
56
+ if (typeof directPrice === "number") {
57
+ return directPrice;
58
+ }
59
+
60
+ if (String(agent.provider).toLowerCase() !== "openai") {
61
+ return undefined;
62
+ }
63
+
64
+ const fallbackModel = findModelConfig("openai", OPENAI_IMAGE_FALLBACK_MODEL);
65
+ return getApproxPricePerImage(fallbackModel, requestedSize);
66
+ };
@@ -0,0 +1,13 @@
1
+ import { getModelConfig } from "./providers";
2
+
3
+ export const isResponseAPIModel = (agentConfig) => {
4
+ if (agentConfig.provider !== "openai") return false;
5
+ if (agentConfig.endpointKey === "responses") return true;
6
+ if (!agentConfig.model) return false;
7
+
8
+ try {
9
+ return getModelConfig("openai", agentConfig.model).endpointKey === "responses";
10
+ } catch {
11
+ return false;
12
+ }
13
+ };
package/ai/llm/kimi.ts ADDED
@@ -0,0 +1,18 @@
1
+ export const FIREWORKS_KIMI_LATEST_MODEL = "accounts/fireworks/models/kimi-latest";
2
+ export const FIREWORKS_KIMI_CURRENT_MODEL = "accounts/fireworks/models/kimi-k2p6";
3
+ export const OPENROUTER_KIMI_FALLBACK_MODEL = "moonshotai/kimi-k2.6";
4
+
5
+ export const isFireworksKimiModel = (model?: string | null): boolean =>
6
+ model === FIREWORKS_KIMI_LATEST_MODEL || model === FIREWORKS_KIMI_CURRENT_MODEL;
7
+
8
+ export const resolveFireworksKimiModel = (model?: string | null): string => {
9
+ if (model === FIREWORKS_KIMI_LATEST_MODEL) {
10
+ return FIREWORKS_KIMI_CURRENT_MODEL;
11
+ }
12
+ return model ?? "";
13
+ };
14
+
15
+ export const shouldHideKimiAliasFromPricing = (
16
+ provider?: string | null,
17
+ model?: string | null
18
+ ): boolean => provider === "fireworks" && model === FIREWORKS_KIMI_LATEST_MODEL;