nolo-cli 0.1.29 → 0.1.31

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 (329) hide show
  1. package/agent-runtime/agentRecordConfig.ts +0 -2
  2. package/agent-runtime/hostAdapter.ts +0 -1
  3. package/agent-runtime/hostRuntimeCapabilities.ts +7 -0
  4. package/agent-runtime/hostRuntimeConfig.ts +86 -0
  5. package/agent-runtime/index.ts +13 -0
  6. package/agent-runtime/localToolPolicy.ts +13 -6
  7. package/agent-runtime/localWorkspaceTools.ts +333 -30
  8. package/agent-runtime/platformChatProvider.ts +4 -1
  9. package/agent-runtime/runtimeToolPolicy.ts +32 -47
  10. package/agent-runtime/shellCommandPolicy.ts +117 -0
  11. package/agent-runtime/toolGate.ts +38 -0
  12. package/agentCreateCustomCommands.ts +9 -4
  13. package/agentDialogsCommands.ts +4 -3
  14. package/agentDoctorCommands.ts +3 -3
  15. package/agentListCommands.ts +3 -3
  16. package/agentMachineCommands.ts +6 -6
  17. package/agentPullCommand.ts +4 -12
  18. package/agentRecordCommands.ts +5 -3
  19. package/agentRecordHelpers.ts +42 -23
  20. package/agentRunCommand.ts +2 -6
  21. package/agentRunParsing.ts +18 -5
  22. package/agentRuntimeLocal.ts +10 -0
  23. package/agentUnpublishCommands.ts +3 -2
  24. package/ai/agent/agentRunDialogLineage.ts +56 -0
  25. package/ai/agent/agentSlice.ts +45 -10
  26. package/ai/agent/avatarUtils.ts +1 -1
  27. package/ai/agent/buildSystemPrompt.ts +1 -2
  28. package/ai/agent/cliExecutor.ts +330 -19
  29. package/ai/agent/createAgentSchema.ts +22 -0
  30. package/ai/agent/hooks/fetchPublicAgents.ts +14 -39
  31. package/ai/agent/hooks/usePublicAgents.ts +90 -192
  32. package/ai/agent/publicAgentCatalog.ts +359 -0
  33. package/ai/agent/publicAgentIdentity.ts +76 -0
  34. package/ai/agent/server/fetchPublicAgents.ts +21 -46
  35. package/ai/agent/serverOrigin.ts +7 -0
  36. package/ai/agent/streamAgentChatTurn.ts +23 -21
  37. package/ai/agent/streamAgentChatTurnUtils.ts +292 -6
  38. package/ai/agent/types.ts +2 -0
  39. package/ai/agent/web/agentDisplayUtils.ts +171 -0
  40. package/ai/agent/web/useAgentModelAvatarComponent.ts +32 -0
  41. package/ai/ai.locale.ts +8 -0
  42. package/ai/chat/sendOpenAICompletionsRequest.native.ts +15 -0
  43. package/ai/chat/sendOpenAICompletionsRequest.ts +93 -1
  44. package/ai/chat/sendOpenAIResponseRequest.ts +86 -9
  45. package/ai/chat/updateTotalUsage.ts +7 -0
  46. package/ai/llm/deepinfra.ts +30 -15
  47. package/ai/llm/mistral.ts +3 -4
  48. package/ai/llm/providers.ts +2 -0
  49. package/ai/llm/types.ts +15 -0
  50. package/ai/memory/capture.ts +3 -3
  51. package/ai/memory/consolidate.ts +2 -2
  52. package/ai/memory/delete.ts +4 -3
  53. package/ai/memory/query.ts +3 -3
  54. package/ai/memory/remember.ts +2 -2
  55. package/ai/memory/store.ts +3 -3
  56. package/ai/memory/storeShared.ts +3 -2
  57. package/ai/policy/commercialMultiplier.ts +16 -0
  58. package/ai/token/billingAdminAuditEvent.ts +68 -0
  59. package/ai/token/billingAdminAuditEventWriter.ts +41 -0
  60. package/ai/token/billingAnomaly.ts +90 -0
  61. package/ai/token/billingAnomalyLifecycle.ts +54 -0
  62. package/ai/token/billingAnomalyLifecycleWriter.ts +44 -0
  63. package/ai/token/billingAnomalyWriter.ts +41 -0
  64. package/ai/token/calculatePrice.ts +3 -2
  65. package/ai/token/deepinfraReconciliation.ts +153 -0
  66. package/ai/token/deepinfraRequestIdQuery.ts +92 -0
  67. package/ai/token/externalToolCost.ts +54 -41
  68. package/ai/token/missingUsageEstimate.ts +12 -3
  69. package/ai/token/normalizeUsage.ts +45 -5
  70. package/ai/token/openaiReconciliation.ts +188 -0
  71. package/ai/token/providerBillableEvent.ts +84 -0
  72. package/ai/token/providerBillableEventWriter.ts +36 -0
  73. package/ai/token/providerBillingAnomalyDrilldown.ts +308 -0
  74. package/ai/token/providerBillingDrilldown.ts +361 -0
  75. package/ai/token/providerBillingHealthReport.ts +366 -0
  76. package/ai/token/providerCall.ts +144 -0
  77. package/ai/token/providerCallSweeper.ts +168 -0
  78. package/ai/token/providerCallWriter.ts +33 -0
  79. package/ai/token/providerCredential.ts +89 -0
  80. package/ai/token/providerCredentialAnomaly.ts +154 -0
  81. package/ai/token/providerCredentialLifecycle.ts +59 -0
  82. package/ai/token/providerCredentialLifecycleWriter.ts +44 -0
  83. package/ai/token/providerCredentialRegistry.ts +83 -0
  84. package/ai/token/providerCredentialRegistryCommand.ts +97 -0
  85. package/ai/token/providerCredentialRegistryList.ts +124 -0
  86. package/ai/token/providerCredentialRegistryResolver.ts +150 -0
  87. package/ai/token/providerCredentialRegistryWriter.ts +41 -0
  88. package/ai/token/providerCredentialRotationCommand.ts +102 -0
  89. package/ai/token/providerCredentialRuntime.ts +56 -0
  90. package/ai/token/providerDispatchIntent.ts +67 -0
  91. package/ai/token/providerDispatchIntentWriter.ts +36 -0
  92. package/ai/token/providerGateway.ts +260 -0
  93. package/ai/token/providerReconciliation.ts +148 -0
  94. package/ai/token/providerReconciliationLocal.ts +97 -0
  95. package/ai/token/providerReconciliationRunner.ts +165 -0
  96. package/ai/token/providerReconciliationWriter.ts +41 -0
  97. package/ai/token/ratingResult.ts +202 -0
  98. package/ai/token/ratingResultWriter.ts +33 -0
  99. package/ai/token/serverDialogProjection.ts +5 -3
  100. package/ai/token/serverTokenWriter.ts +11 -3
  101. package/ai/token/types.ts +10 -0
  102. package/ai/tools/agent/callAgentTool.ts +3 -4
  103. package/ai/tools/agent/presets/appBuilderPreset.ts +5 -7
  104. package/ai/tools/agent/runStreamingAgentTool.ts +1 -2
  105. package/ai/tools/agent/streamParallelAgentsTool.ts +2 -3
  106. package/ai/tools/readXPostTool.ts +1 -5
  107. package/app/admin/adminPages.ts +9 -0
  108. package/app/i18n/i18n.config.ts +5 -0
  109. package/app/i18n/translations/pricing.locale.ts +397 -0
  110. package/app/utils/desktopAgentRuntimeStatusClient.ts +5 -4
  111. package/app/utils/desktopAgentRuntimeTurnClient.ts +4 -1
  112. package/app/utils/imageUtils.ts +64 -34
  113. package/auth/adminPermissions.ts +18 -0
  114. package/auth/authSlice.ts +26 -12
  115. package/auth/hooks/useFetchUsers.ts +3 -0
  116. package/auth/routes.ts +46 -0
  117. package/auth/server/accessActivity.ts +4 -3
  118. package/auth/server/adminPermissions.ts +43 -0
  119. package/auth/server/adminUsageReport.ts +375 -0
  120. package/auth/server/billingAnomalies.ts +235 -0
  121. package/auth/server/billingUsageReport.ts +41 -2
  122. package/auth/server/creatorEarningCreditConversion.ts +10 -2
  123. package/auth/server/deduct.ts +14 -7
  124. package/auth/server/delete.ts +10 -2
  125. package/auth/server/disable.ts +4 -3
  126. package/auth/server/emailAutomation.ts +7 -5
  127. package/auth/server/emailDbConfig.ts +4 -2
  128. package/auth/server/emailDelivery.ts +24 -20
  129. package/auth/server/emailDeliveryGuards.ts +10 -7
  130. package/auth/server/emailPreferences.ts +6 -4
  131. package/auth/server/emailReport.ts +6 -4
  132. package/auth/server/emailRuntimeConfig.ts +3 -3
  133. package/auth/server/emailScenarioNotifications.ts +9 -8
  134. package/auth/server/enable.ts +4 -3
  135. package/auth/server/getRechargeHistory.ts +3 -2
  136. package/auth/server/getUser.ts +3 -2
  137. package/auth/server/gptProAccess.ts +44 -0
  138. package/auth/server/inviteReward.ts +10 -7
  139. package/auth/server/listusers.ts +6 -4
  140. package/auth/server/login.ts +2 -2
  141. package/auth/server/productEntitlements.ts +7 -4
  142. package/auth/server/productPurchase.ts +10 -2
  143. package/auth/server/providerBillingDrilldown.ts +47 -0
  144. package/auth/server/providerBillingHealth.ts +71 -0
  145. package/auth/server/providerCredentials.ts +136 -0
  146. package/auth/server/recharge.ts +14 -8
  147. package/auth/server/revokeSessions.ts +4 -3
  148. package/auth/server/route.ts +77 -0
  149. package/auth/server/sendEmail.ts +5 -4
  150. package/auth/server/signup.ts +10 -3
  151. package/auth/server/token.ts +53 -55
  152. package/auth/server/tokenUsageBilling.ts +18 -7
  153. package/auth/server/usageManagementAccess.ts +43 -0
  154. package/auth/server/usageReport.ts +59 -0
  155. package/auth/types.ts +12 -0
  156. package/auth/utils.ts +24 -22
  157. package/authCommands.ts +36 -2
  158. package/buildPublish.ts +3 -1
  159. package/buildPublishArtifactAssembly.ts +62 -1
  160. package/buildPublishImportAnalysis.ts +21 -16
  161. package/chat/dialog/dialogLaunchScope.ts +32 -0
  162. package/chat/dialog/objectAssistantRegistry.ts +2 -2
  163. package/chat/dialog/useCreateDialog.ts +1 -1
  164. package/chat/hooks/useChatInput.ts +41 -10
  165. package/chat/messages/messageSlice.ts +5 -1
  166. package/chat/messages/pendingAttachmentParts.ts +28 -2
  167. package/chat/messages/sendFirstMessage.ts +76 -79
  168. package/chat/messages/types.ts +9 -0
  169. package/chat/messages/web/streamingMarkdownModel.ts +41 -0
  170. package/chat/messages/web/useStreamingReveal.ts +91 -0
  171. package/chat/web/messageInputAgentUi.ts +94 -6
  172. package/cliEnvHelpers.ts +68 -13
  173. package/cliRuntimeDoctorReport.ts +193 -0
  174. package/client/agentConfigResolver.test.ts +0 -2
  175. package/client/agentRun.test.ts +171 -10
  176. package/client/agentRun.ts +44 -637
  177. package/client/agentRunInput.ts +10 -0
  178. package/client/agentRunLocalExecutor.ts +179 -0
  179. package/client/agentRunServerTransport.ts +394 -0
  180. package/client/agentRunTaskRunRecorder.ts +176 -0
  181. package/client/localRuntimeAdapter.test.ts +236 -9
  182. package/client/localRuntimeAdapter.ts +221 -23
  183. package/client/localToolPolicy.test.ts +28 -4
  184. package/connector-experimental/daemonLauncher.ts +38 -4
  185. package/connectorRunArtifact.ts +34 -10
  186. package/connectorWebSocketTarget.ts +40 -15
  187. package/core/builtinAgents.ts +155 -11
  188. package/create/space/contentLabels.ts +1 -1
  189. package/create/space/contentTypeMeta.ts +3 -2
  190. package/create/space/sidebarVisibleTypes.ts +27 -0
  191. package/create/space/space.locale.ts +76 -4
  192. package/database/actions/agentReadResolution.ts +2 -8
  193. package/database/actions/read.ts +11 -9
  194. package/database/actions/readAndWait.ts +10 -5
  195. package/database/server/agentDelegation.ts +3 -2
  196. package/database/server/authorityStoreConformance.ts +209 -0
  197. package/database/server/authorityStoreTypes.ts +31 -0
  198. package/database/server/coreDataProxy.ts +1 -1
  199. package/database/server/dataHandlers.ts +13 -5
  200. package/database/server/db.ts +15 -34
  201. package/database/server/delete.ts +88 -71
  202. package/database/server/emailRepository.ts +11 -9
  203. package/database/server/fileService.ts +2 -1
  204. package/database/server/legacyServerDb.ts +58 -0
  205. package/database/server/levelAuthorityStore.ts +75 -0
  206. package/database/server/memoryAuthorityStore.ts +133 -0
  207. package/database/server/patch.ts +13 -12
  208. package/database/server/query.ts +3 -2
  209. package/database/server/read.ts +33 -30
  210. package/database/server/resourceAccess.ts +7 -5
  211. package/database/server/serverStoreFactory.ts +124 -0
  212. package/database/server/spaceMemberAuthority.ts +3 -2
  213. package/database/server/storeBatchCompat.ts +37 -0
  214. package/database/server/testAuthorityStore.ts +76 -0
  215. package/database/server/write.ts +29 -27
  216. package/dialogReadCommands.ts +2 -1
  217. package/dialogStatusCommands.ts +2 -1
  218. package/integrations/anthropic/anthropicModels.ts +12 -12
  219. package/integrations/deepseek/models.ts +67 -67
  220. package/integrations/google/models.ts +111 -53
  221. package/integrations/openai/models.ts +22 -22
  222. package/integrations/xai/models.ts +1 -1
  223. package/localRuntimeDb.ts +39 -2
  224. package/machineCommandPrompts.ts +2 -0
  225. package/machineCommands.ts +193 -62
  226. package/machineStatusCommands.ts +4 -12
  227. package/machineWsRunDispatch.ts +80 -15
  228. package/machineWsSession.ts +46 -2
  229. package/noloReadBridgePrompt.ts +16 -0
  230. package/package.json +114 -21
  231. package/pageCreateCommands.ts +2 -2
  232. package/pageDeleteCommands.ts +3 -3
  233. package/pageReadCommands.ts +3 -3
  234. package/pageUpdateCommands.ts +3 -3
  235. package/render/layout/mainLayoutViewMode.ts +25 -0
  236. package/render/page/server/createPage.ts +3 -1
  237. package/render/page/server/handleCreatePage.ts +2 -11
  238. package/runtimeDoctorCommands.ts +18 -69
  239. package/server/api/methods.ts +34 -12
  240. package/server/api/tableHandlers.ts +6 -3
  241. package/server/authenticateToken.ts +47 -2
  242. package/server/billingLedgerRoutes.ts +15 -0
  243. package/server/buildManifest.ts +54 -0
  244. package/server/coreReadiness.ts +34 -25
  245. package/server/coreRuntime.ts +15 -10
  246. package/server/handlers/activityStatsHandler.ts +5 -2
  247. package/server/handlers/agentEmailRegistrationE2EHandler.ts +25 -23
  248. package/server/handlers/agentRun/agentLookup.ts +1 -7
  249. package/server/handlers/agentRun/autonomousRuntime.ts +11 -10
  250. package/server/handlers/agentRun/builtinAgents.ts +12 -88
  251. package/server/handlers/agentRun/continueDialogContext.ts +15 -3
  252. package/server/handlers/agentRun/{inheritedDialogPrompt.ts → conversationReference.ts} +3 -3
  253. package/server/handlers/agentRun/dialogLookup.ts +4 -3
  254. package/server/handlers/agentRun/index.ts +864 -404
  255. package/server/handlers/agentRun/localRuntimeBootstrap.ts +17 -1
  256. package/server/handlers/agentRun/loop.ts +920 -508
  257. package/server/handlers/agentRun/machineConnector.ts +56 -8
  258. package/server/handlers/agentRun/machineRunAudit.ts +3 -2
  259. package/server/handlers/agentRun/modelUsageToolHandlers.ts +13 -10
  260. package/server/handlers/agentRun/requestContext.ts +12 -8
  261. package/server/handlers/agentRun/runtimeExecutionContext.ts +15 -3
  262. package/server/handlers/agentRun/runtimePreparation.ts +26 -24
  263. package/server/handlers/agentRun/runtimeRouting.ts +25 -40
  264. package/server/handlers/agentRun/runtimeSystemMessages.ts +65 -0
  265. package/server/handlers/agentRun/saveDialog.ts +97 -87
  266. package/server/handlers/agentRun/taskRunToolHandler.ts +9 -7
  267. package/server/handlers/agentRun/toolExecutor.ts +42 -78
  268. package/server/handlers/agentRun/types.ts +2 -1
  269. package/server/handlers/appDeployStatusStore.ts +17 -10
  270. package/server/handlers/appDomainHandler.ts +21 -15
  271. package/server/handlers/appLegacyInfraCleanup.ts +5 -2
  272. package/server/handlers/appSandboxHandler.ts +29 -8
  273. package/server/handlers/appSpaceAccess.ts +15 -8
  274. package/server/handlers/chatHandler.ts +22 -2
  275. package/server/handlers/chatUpstreamRetry.ts +225 -2
  276. package/server/handlers/connector/connectorConnectionRegistry.ts +55 -7
  277. package/server/handlers/connector/connectorWebSocket.ts +14 -10
  278. package/server/handlers/desktopAgentRuntimeAdapter.ts +4 -4
  279. package/server/handlers/desktopAgentRuntimeHostCapabilities.ts +25 -0
  280. package/server/handlers/desktopAgentRuntimeReadiness.ts +62 -0
  281. package/server/handlers/desktopAgentRuntimeStatusHandler.ts +38 -30
  282. package/server/handlers/desktopAgentRuntimeTurnHandler.ts +7 -3
  283. package/server/handlers/desktopAgentRuntimeTurnService.ts +39 -45
  284. package/server/handlers/desktopLlamaRuntimeHandler.ts +85 -51
  285. package/server/handlers/desktopUpdaterHandler.ts +132 -24
  286. package/server/handlers/favoriteHandlers.ts +8 -2
  287. package/server/handlers/getDialogBillingHandler.ts +4 -3
  288. package/server/handlers/getTransactionsHandler.ts +5 -2
  289. package/server/handlers/machines/machineBinding.ts +1 -0
  290. package/server/handlers/machines/machineHandlers.ts +0 -14
  291. package/server/handlers/machines/machineInstallAssets.ts +4 -2
  292. package/server/handlers/notificationEmitter.ts +5 -3
  293. package/server/handlers/notificationsHandler.ts +5 -2
  294. package/server/handlers/providerCallsRecentHandler.ts +234 -0
  295. package/server/handlers/providerReconciliationRunHandler.ts +180 -0
  296. package/server/handlers/providerRequestIdsHandler.ts +105 -0
  297. package/server/handlers/tableAccess.ts +6 -2
  298. package/server/handlers/tableToolHandlers.ts +28 -14
  299. package/server/handlers/userSecretsHandler.ts +17 -6
  300. package/server/handlers/versionHandler.ts +9 -4
  301. package/server/handlers/versionStore.ts +18 -10
  302. package/server/html/ssrPreload.ts +5 -3
  303. package/server/payments/crypto/cryptoUsdcBasePaymentProvider.ts +11 -8
  304. package/server/payments/crypto/cryptoUsdcBaseScanner.ts +12 -7
  305. package/server/payments/crypto/cryptoUsdtTronPaymentProvider.ts +11 -8
  306. package/server/payments/crypto/cryptoUsdtTronScanner.ts +9 -7
  307. package/server/payments/waffo/waffoPaymentProvider.ts +12 -9
  308. package/server/runtimeRole.ts +15 -0
  309. package/server/scheduler/scheduledTaskRuntime.ts +4 -2
  310. package/server/scheduler/schedulerService.ts +2 -2
  311. package/server/taskRunBridgeRoutes.ts +8 -5
  312. package/share/server/fetchCommunityShares.ts +1 -1
  313. package/share/server/helpers.ts +19 -17
  314. package/share/server/importShare.ts +9 -13
  315. package/share/server/listCommunityShares.ts +4 -4
  316. package/share/server/purchaseDoc.ts +8 -16
  317. package/share/server/readCommunityShareCover.ts +3 -2
  318. package/share/server/readSharedTable.ts +14 -13
  319. package/share/server/tableReplication.ts +42 -39
  320. package/spaceAttachCommands.ts +135 -0
  321. package/spaceCategoryCommands.ts +6 -6
  322. package/spaceCommands.ts +3 -3
  323. package/spaceDeleteCommands.ts +2 -2
  324. package/spaceInternalCommandEntries.ts +2 -0
  325. package/spaceReadCommands.ts +3 -3
  326. package/tableMetaCommands.ts +2 -2
  327. package/taskRunCommand.ts +11 -0
  328. package/ai/agent/runtimeServerBase.ts +0 -37
  329. package/server/handlers/desktopAgentRuntimeHostFacts.ts +0 -40
@@ -4,7 +4,7 @@
4
4
  * 外部 API 工具(OCR、搜索、浏览器等)的统一计费抽象。
5
5
  *
6
6
  * 价格单位:credits / 1M tokens(token 类),或 credits / 次调用(perCall 类)。
7
- * 换算关系:1 USD = 8 credits(与 fireworks/openrouter 一致)。
7
+ * 换算关系按模型/服务家族维护:默认 *8,DeepInfra 托管的 Claude *9。
8
8
  *
9
9
  * 如何添加新工具:
10
10
  * 在 EXTERNAL_TOOL_PRICING 中增加一条记录,然后在 handler 里调用
@@ -13,14 +13,17 @@
13
13
 
14
14
  import { ulid } from "ulid";
15
15
  import { format } from "date-fns";
16
- import serverDb from "../../database/server/db";
16
+ import { getServerAuthorityStore } from "../../database/server/db";
17
17
  import { createKey, createTokenKey, createTokenStatsKey } from "../../database/keys";
18
18
  import { chargeTokenUsageWithLedger } from "../../auth/server/tokenUsageBilling";
19
19
  import { DataType } from "../../create/types";
20
20
  import pino from "pino";
21
- import { applyServerDialogProjectionDelta } from "./serverDialogProjection";
21
+ import { applyServerDialogProjectionDelta } from "./serverDialogProjection";
22
+ import { DEFAULT_COMMERCIAL_MULTIPLIER } from "../policy/commercialMultiplier";
22
23
 
23
- const logger = pino({ name: "external-tool-cost" });
24
+ const logger = pino({ name: "external-tool-cost" });
25
+ const DEFAULT_TOOL_MULTIPLIER = DEFAULT_COMMERCIAL_MULTIPLIER;
26
+ const getExternalToolCostStore = () => getServerAuthorityStore();
24
27
 
25
28
  // ─────────────────────────────────────────
26
29
  // 价格注册表(所有外部工具)
@@ -28,9 +31,9 @@ const logger = pino({ name: "external-tool-cost" });
28
31
 
29
32
  type TokenPrice = {
30
33
  type: "token";
31
- /** credits / 1M input tokens (USD/1M × 8) */
34
+ /** credits / 1M input tokens */
32
35
  input: number;
33
- /** credits / 1M output tokens (USD/1M × 8) */
36
+ /** credits / 1M output tokens */
34
37
  output: number;
35
38
  provider: string;
36
39
  };
@@ -58,34 +61,40 @@ type ToolPrice = TokenPrice | PerCallPrice | PerMinutePrice;
58
61
  * OCR 价格来自 DeepInfra 官网(截图,2026-03):
59
62
  * olmOCR $0.09/$0.19 /1M → ×8
60
63
  * DeepSeek $0.03/$0.10 /1M → ×8
61
- *
62
- * Exa 价格:$0.01/次(含 contents)× 8
64
+ *
65
+ * Exa 价格:$0.01/次(含 contents)× 8
63
66
  */
64
67
  export const EXTERNAL_TOOL_PRICING: Record<string, ToolPrice> = {
65
68
  // ── OCR (DeepInfra, token-based) ──────────────────────
66
69
  "allenai/olmOCR-2-7B-1025": {
67
70
  type: "token",
68
- input: 0.09 * 8, // 0.72
69
- output: 0.19 * 8, // 1.52
70
- provider: "deepinfra",
71
- },
72
-
73
- // ── Web Search ────────────────────────────────────────
74
- "exa_search": {
71
+ input: 0.09 * DEFAULT_TOOL_MULTIPLIER,
72
+ output: 0.19 * DEFAULT_TOOL_MULTIPLIER,
73
+ provider: "deepinfra",
74
+ },
75
+ "deepseek-ai/DeepSeek-OCR": {
76
+ type: "token",
77
+ input: 0.03 * DEFAULT_TOOL_MULTIPLIER,
78
+ output: 0.10 * DEFAULT_TOOL_MULTIPLIER,
79
+ provider: "deepinfra",
80
+ },
81
+
82
+ // ── Web Search ────────────────────────────────────────
83
+ "exa_search": {
75
84
  type: "perCall",
76
- cost: 0.01 * 8, // 0.08
85
+ cost: 0.01 * DEFAULT_TOOL_MULTIPLIER,
77
86
  provider: "exa",
78
87
  },
79
88
 
80
89
  // ── ASR / Whisper (DeepInfra, per-minute) ─────────────
81
90
  "openai/whisper-large-v3-turbo": {
82
91
  type: "perMinute",
83
- cost: 0.00020 * 8, // 0.0016 credits/min
92
+ cost: 0.00020 * DEFAULT_TOOL_MULTIPLIER,
84
93
  provider: "deepinfra",
85
94
  },
86
95
  "openai/whisper-large-v3": {
87
96
  type: "perMinute",
88
- cost: 0.00045 * 8, // 0.0036 credits/min
97
+ cost: 0.00045 * DEFAULT_TOOL_MULTIPLIER,
89
98
  provider: "deepinfra",
90
99
  },
91
100
 
@@ -94,7 +103,7 @@ export const EXTERNAL_TOOL_PRICING: Record<string, ToolPrice> = {
94
103
  // 保守取 $0.25/run 作为默认基准,建议高消耗 Actor 单独定价
95
104
  "apify_actor_run": {
96
105
  type: "perCall",
97
- cost: 0.25 * 8, // 2.0 credits/run
106
+ cost: 0.25 * DEFAULT_TOOL_MULTIPLIER,
98
107
  provider: "apify",
99
108
  },
100
109
 
@@ -103,36 +112,36 @@ export const EXTERNAL_TOOL_PRICING: Record<string, ToolPrice> = {
103
112
  // 3 Pro Image Preview: ~$0.15/image(Pro 系列,多图编辑)
104
113
  "gemini-2.5-flash-image": {
105
114
  type: "perCall",
106
- cost: 0.04 * 8, // 0.32 credits/image
115
+ cost: 0.04 * DEFAULT_TOOL_MULTIPLIER,
107
116
  provider: "google",
108
117
  },
109
118
  // OpenAI image generation currently goes through the images API instead of
110
119
  // token accounting, so register a per-image fallback price here.
111
120
  "gpt-image-1.5": {
112
121
  type: "perCall",
113
- cost: 0.04 * 8, // 0.32 credits/image
122
+ cost: 0.04 * DEFAULT_TOOL_MULTIPLIER, // 0.32 credits/image
114
123
  provider: "openai",
115
124
  },
116
125
  "gpt-image-2": {
117
126
  type: "perCall",
118
- cost: 0.04 * 8, // 0.32 credits/image placeholder
127
+ cost: 0.04 * DEFAULT_TOOL_MULTIPLIER, // 0.32 credits/image placeholder
119
128
  provider: "openai",
120
129
  },
121
130
  "gemini-3-pro-image-preview": {
122
131
  type: "perCall",
123
- cost: 0.15 * 8, // 1.2 credits/image
132
+ cost: 0.15 * DEFAULT_TOOL_MULTIPLIER,
124
133
  provider: "google",
125
134
  },
126
135
  "gemini-3.1-flash-image-preview": {
127
136
  type: "perCall",
128
- cost: 0.067 * 8, // fallback only; Gemini handler overrides by image size
137
+ cost: 0.067 * DEFAULT_TOOL_MULTIPLIER, // fallback only; Gemini handler overrides by image size
129
138
  provider: "google",
130
139
  },
131
140
 
132
141
  // ── Cloudflare Browser Rendering (per-minute) ─────────
133
142
  "cf_browser_rendering": {
134
143
  type: "perMinute",
135
- cost: 0.001 * 8, // $0.008/min
144
+ cost: 0.001 * DEFAULT_TOOL_MULTIPLIER,
136
145
  provider: "cloudflare",
137
146
  },
138
147
 
@@ -140,12 +149,12 @@ export const EXTERNAL_TOOL_PRICING: Record<string, ToolPrice> = {
140
149
  // @cf/openai/whisper,价格与 DeepInfra whisper-large-v3 相近
141
150
  "@cf/openai/whisper": {
142
151
  type: "perMinute",
143
- cost: 0.00045 * 8, // $0.0036 credits/min
152
+ cost: 0.00045 * DEFAULT_TOOL_MULTIPLIER,
144
153
  provider: "cloudflare",
145
154
  },
146
155
  "@cf/myshell-ai/melotts": {
147
156
  type: "perMinute",
148
- cost: 0.00020 * 8, // $0.0016 credits/min
157
+ cost: 0.00020 * DEFAULT_TOOL_MULTIPLIER,
149
158
  provider: "cloudflare",
150
159
  },
151
160
  };
@@ -183,11 +192,12 @@ export interface ChargeResult {
183
192
  * 计算费用、扣余额、写 usage 记录,三步合一。
184
193
  * 若 toolId 未注册,跳过扣费并打 warn 日志。
185
194
  */
186
- export async function chargeExternalTool(
187
- opts: ChargeExternalToolOpts
188
- ): Promise<ChargeResult> {
189
- const {
190
- userId,
195
+ export async function chargeExternalTool(
196
+ opts: ChargeExternalToolOpts
197
+ ): Promise<ChargeResult> {
198
+ const store = getExternalToolCostStore();
199
+ const {
200
+ userId,
191
201
  toolId,
192
202
  dialogId,
193
203
  inputTokens = 0,
@@ -223,6 +233,7 @@ export async function chargeExternalTool(
223
233
  const deductReason = reason ?? toolId;
224
234
  // 写 usage 记录(与 LLM token 记录同格式,显示在 Usage 页面)
225
235
  const usageRecord = await writeUsageRecord({
236
+ store,
226
237
  userId,
227
238
  toolId,
228
239
  dialogId,
@@ -233,7 +244,7 @@ export async function chargeExternalTool(
233
244
  });
234
245
 
235
246
  const chargeResult = await chargeTokenUsageWithLedger({
236
- store: serverDb,
247
+ store,
237
248
  userId,
238
249
  tokenKey: usageRecord.recordKey,
239
250
  tokenRecord: usageRecord.tokenRecord,
@@ -280,15 +291,17 @@ export async function chargeExternalTool(
280
291
  // ─────────────────────────────────────────
281
292
 
282
293
  async function writeUsageRecord(opts: {
283
- userId: string;
284
- toolId: string;
285
- dialogId?: string;
294
+ store: ReturnType<typeof getServerAuthorityStore>;
295
+ userId: string;
296
+ toolId: string;
297
+ dialogId?: string;
286
298
  inputTokens: number;
287
299
  outputTokens: number;
288
300
  cost: number;
289
301
  provider: string;
290
302
  }): Promise<{ recordKey: string; tokenRecord: any }> {
291
- const { userId, toolId, dialogId, inputTokens, outputTokens, cost, provider } = opts;
303
+ const { store, userId, toolId, dialogId, inputTokens, outputTokens, cost, provider } =
304
+ opts;
292
305
  const timestamp = Date.now();
293
306
  const dateKey = format(timestamp, "yyyy-MM-dd");
294
307
  const recordKey = createTokenKey.record(userId, timestamp);
@@ -314,9 +327,9 @@ async function writeUsageRecord(opts: {
314
327
  dateKey,
315
328
  };
316
329
 
317
- const statsKey = createTokenStatsKey(userId, dateKey);
318
- let stats: any = null;
319
- try { stats = await serverDb.get(statsKey); } catch { /* init below */ }
330
+ const statsKey = createTokenStatsKey(userId, dateKey);
331
+ let stats: any = null;
332
+ try { stats = await store.get(statsKey); } catch { /* init below */ }
320
333
 
321
334
  const inc = (s: any = { count: 0, tokens: { input: 0, output: 0 }, cost: 0 }) => ({
322
335
  count: s.count + 1,
@@ -333,7 +346,7 @@ async function writeUsageRecord(opts: {
333
346
  models: { ...base.models, [toolId]: inc(base.models?.[toolId]) },
334
347
  providers: { ...base.providers, [provider]: inc(base.providers?.[provider]) },
335
348
  };
336
- await serverDb.batch([
349
+ await store.batchWrite([
337
350
  { type: "put", key: recordKey, value: record },
338
351
  { type: "put", key: statsKey, value: newStats },
339
352
  ]);
@@ -21,19 +21,28 @@ const stringifyContent = (content: unknown): string => {
21
21
 
22
22
  export const estimateMissingUsage = ({
23
23
  content,
24
+ inputContent,
25
+ minimumInputTokens = 1,
24
26
  minimumOutputTokens = 1,
25
27
  }: {
26
28
  content: unknown;
29
+ inputContent?: unknown;
30
+ minimumInputTokens?: number;
27
31
  minimumOutputTokens?: number;
28
32
  }): RawUsage => {
29
- const text = stringifyContent(content);
33
+ const outputText = stringifyContent(content);
34
+ const inputText = stringifyContent(inputContent);
35
+ const estimatedInputTokens = Math.max(
36
+ minimumInputTokens,
37
+ Math.ceil(inputText.length / APPROX_CHARS_PER_TOKEN)
38
+ );
30
39
  const estimatedOutputTokens = Math.max(
31
40
  minimumOutputTokens,
32
- Math.ceil(text.length / APPROX_CHARS_PER_TOKEN)
41
+ Math.ceil(outputText.length / APPROX_CHARS_PER_TOKEN)
33
42
  );
34
43
 
35
44
  return {
36
- input_tokens: 1,
45
+ input_tokens: estimatedInputTokens,
37
46
  output_tokens: estimatedOutputTokens,
38
47
  cache_creation_input_tokens: 0,
39
48
  cache_read_input_tokens: 0,
@@ -2,6 +2,36 @@
2
2
 
3
3
  import { RawUsage, NormalizedUsage } from "./types";
4
4
 
5
+ const normalizeStringArray = (value: unknown): string[] | undefined => {
6
+ if (!Array.isArray(value)) return undefined;
7
+ const normalized = Array.from(
8
+ new Set(
9
+ value
10
+ .filter((item): item is string => typeof item === "string")
11
+ .map((item) => item.trim())
12
+ .filter(Boolean)
13
+ )
14
+ );
15
+ return normalized.length > 0 ? normalized : undefined;
16
+ };
17
+
18
+ const finiteTokenCount = (value: unknown): number | undefined => {
19
+ if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
20
+ return Math.max(0, Math.floor(value));
21
+ };
22
+
23
+ const readNestedTokenCount = (
24
+ value: unknown,
25
+ path: readonly string[]
26
+ ): number | undefined => {
27
+ let cursor = value;
28
+ for (const key of path) {
29
+ if (!cursor || typeof cursor !== "object") return undefined;
30
+ cursor = (cursor as Record<string, unknown>)[key];
31
+ }
32
+ return finiteTokenCount(cursor);
33
+ };
34
+
5
35
  /**
6
36
  * 规范化 usage 数据
7
37
  */
@@ -28,11 +58,11 @@ export const normalizeUsage = (usage: RawUsage): NormalizedUsage => {
28
58
  : 0;
29
59
 
30
60
  const cacheReadInputTokens =
31
- "cache_read_input_tokens" in usage
32
- ? usage.cache_read_input_tokens
33
- : "prompt_cache_hit_tokens" in usage
34
- ? usage.prompt_cache_hit_tokens
35
- : 0;
61
+ finiteTokenCount((usage as any).cache_read_input_tokens) ??
62
+ finiteTokenCount((usage as any).prompt_cache_hit_tokens) ??
63
+ readNestedTokenCount(usage, ["input_tokens_details", "cached_tokens"]) ??
64
+ readNestedTokenCount(usage, ["prompt_tokens_details", "cached_tokens"]) ??
65
+ 0;
36
66
 
37
67
  // ✅ 如果 provider 返回 usage.cost,则先原样透传;
38
68
  // 后续由 calculatePrice 按各 provider 的账单单位再做换算。
@@ -64,6 +94,12 @@ export const normalizeUsage = (usage: RawUsage): NormalizedUsage => {
64
94
  Number.isFinite(usage.image_generation_count)
65
95
  ? usage.image_generation_count
66
96
  : undefined;
97
+ const providerResponseIds = normalizeStringArray(
98
+ (usage as any).provider_response_ids
99
+ );
100
+ const providerRequestIds = normalizeStringArray(
101
+ (usage as any).provider_request_ids
102
+ );
67
103
 
68
104
  return {
69
105
  input_tokens: inputTokens,
@@ -74,6 +110,10 @@ export const normalizeUsage = (usage: RawUsage): NormalizedUsage => {
74
110
  ...(typeof imageGenerationCount === "number"
75
111
  ? { image_generation_count: imageGenerationCount }
76
112
  : {}),
113
+ ...(providerResponseIds
114
+ ? { provider_response_ids: providerResponseIds }
115
+ : {}),
116
+ ...(providerRequestIds ? { provider_request_ids: providerRequestIds } : {}),
77
117
  ...(billingProvider ? { billing_provider: billingProvider } : {}),
78
118
  ...(billingModel ? { billing_model: billingModel } : {}),
79
119
  ...(billingServiceTier
@@ -0,0 +1,188 @@
1
+ import type { NormalizedOfficialProviderBucket } from "./deepinfraReconciliation";
2
+
3
+ type OpenAICompletionsUsageResponse = {
4
+ data?: Array<{
5
+ start_time?: number;
6
+ end_time?: number;
7
+ results?: Array<{
8
+ model?: string;
9
+ num_model_requests?: number;
10
+ input_tokens?: number;
11
+ output_tokens?: number;
12
+ input_cached_tokens?: number;
13
+ }>;
14
+ }>;
15
+ };
16
+
17
+ type OpenAICostsResponse = {
18
+ data?: Array<{
19
+ start_time?: number;
20
+ end_time?: number;
21
+ results?: Array<{
22
+ amount?: {
23
+ value?: number | string;
24
+ currency?: string;
25
+ };
26
+ line_item?: string;
27
+ project_id?: string;
28
+ organization_id?: string;
29
+ }>;
30
+ }>;
31
+ };
32
+
33
+ const unixSecondsToIso = (value: unknown) => {
34
+ const seconds = typeof value === "number" ? value : NaN;
35
+ if (!Number.isFinite(seconds)) return undefined;
36
+ return new Date(seconds * 1000).toISOString();
37
+ };
38
+
39
+ const numberOrZero = (value: unknown) =>
40
+ typeof value === "number" && Number.isFinite(value) ? value : 0;
41
+
42
+ const decimalNumberOrZero = (value: unknown) => {
43
+ if (typeof value === "number" && Number.isFinite(value)) return value;
44
+ if (typeof value !== "string") return 0;
45
+ const parsed = Number(value);
46
+ return Number.isFinite(parsed) ? parsed : 0;
47
+ };
48
+
49
+ const normalizeCostLineItemModel = (lineItem: string | undefined) =>
50
+ lineItem?.replace(/,\s*(input|output)$/i, "");
51
+
52
+ export function normalizeOpenAICompletionsUsageResponse(
53
+ response: OpenAICompletionsUsageResponse
54
+ ): NormalizedOfficialProviderBucket[] {
55
+ const buckets: NormalizedOfficialProviderBucket[] = [];
56
+ for (const bucket of response.data ?? []) {
57
+ const bucketStart = unixSecondsToIso(bucket.start_time);
58
+ const bucketEnd = unixSecondsToIso(bucket.end_time);
59
+ if (!bucketStart || !bucketEnd) continue;
60
+ for (const result of bucket.results ?? []) {
61
+ buckets.push({
62
+ provider: "openai",
63
+ model: result.model,
64
+ bucketStart,
65
+ bucketEnd,
66
+ official: {
67
+ requestCount: numberOrZero(result.num_model_requests),
68
+ inputTokens: numberOrZero(result.input_tokens),
69
+ outputTokens: numberOrZero(result.output_tokens),
70
+ rawProviderCostUsd: 0,
71
+ },
72
+ evidence: {
73
+ inputCachedTokens: numberOrZero(result.input_cached_tokens),
74
+ source: "openai_completions_usage",
75
+ },
76
+ });
77
+ }
78
+ }
79
+ return buckets;
80
+ }
81
+
82
+ export function normalizeOpenAICostsResponse(
83
+ response: OpenAICostsResponse
84
+ ): NormalizedOfficialProviderBucket[] {
85
+ const buckets: NormalizedOfficialProviderBucket[] = [];
86
+ for (const bucket of response.data ?? []) {
87
+ const bucketStart = unixSecondsToIso(bucket.start_time);
88
+ const bucketEnd = unixSecondsToIso(bucket.end_time);
89
+ if (!bucketStart || !bucketEnd) continue;
90
+ for (const result of bucket.results ?? []) {
91
+ const currency = result.amount?.currency;
92
+ buckets.push({
93
+ provider: "openai",
94
+ model: normalizeCostLineItemModel(result.line_item),
95
+ bucketStart,
96
+ bucketEnd,
97
+ official: {
98
+ requestCount: 0,
99
+ inputTokens: 0,
100
+ outputTokens: 0,
101
+ rawProviderCostUsd:
102
+ currency?.toLowerCase() === "usd"
103
+ ? decimalNumberOrZero(result.amount?.value)
104
+ : 0,
105
+ },
106
+ evidence: {
107
+ source: "openai_costs",
108
+ currency,
109
+ lineItem: result.line_item,
110
+ },
111
+ });
112
+ }
113
+ }
114
+ return buckets;
115
+ }
116
+
117
+ const mergeKey = (bucket: NormalizedOfficialProviderBucket) =>
118
+ [
119
+ bucket.provider,
120
+ bucket.model ?? "",
121
+ bucket.bucketStart,
122
+ bucket.bucketEnd,
123
+ ].join("\u0000");
124
+
125
+ export function mergeOpenAIUsageAndCostBuckets(
126
+ usageBuckets: NormalizedOfficialProviderBucket[],
127
+ costBuckets: NormalizedOfficialProviderBucket[]
128
+ ): NormalizedOfficialProviderBucket[] {
129
+ const costByKey = new Map<string, NormalizedOfficialProviderBucket>();
130
+ for (const bucket of costBuckets) {
131
+ const key = mergeKey(bucket);
132
+ const existing = costByKey.get(key);
133
+ if (!existing) {
134
+ costByKey.set(key, bucket);
135
+ continue;
136
+ }
137
+ costByKey.set(key, {
138
+ ...existing,
139
+ official: {
140
+ ...existing.official,
141
+ rawProviderCostUsd:
142
+ existing.official.rawProviderCostUsd +
143
+ bucket.official.rawProviderCostUsd,
144
+ },
145
+ evidence: {
146
+ ...existing.evidence,
147
+ costEvidenceItems: [
148
+ ...(Array.isArray(existing.evidence.costEvidenceItems)
149
+ ? existing.evidence.costEvidenceItems
150
+ : [existing.evidence]),
151
+ bucket.evidence,
152
+ ],
153
+ },
154
+ });
155
+ }
156
+ const merged: NormalizedOfficialProviderBucket[] = [];
157
+ const usedCostKeys = new Set<string>();
158
+
159
+ for (const usageBucket of usageBuckets) {
160
+ const key = mergeKey(usageBucket);
161
+ const costBucket = costByKey.get(key);
162
+ if (!costBucket) {
163
+ merged.push(usageBucket);
164
+ continue;
165
+ }
166
+ usedCostKeys.add(key);
167
+ merged.push({
168
+ ...usageBucket,
169
+ official: {
170
+ ...usageBucket.official,
171
+ rawProviderCostUsd: costBucket.official.rawProviderCostUsd,
172
+ },
173
+ evidence: {
174
+ ...usageBucket.evidence,
175
+ source: `${usageBucket.evidence.source}+openai_costs`,
176
+ costEvidence: costBucket.evidence,
177
+ },
178
+ });
179
+ }
180
+
181
+ for (const costBucket of costBuckets) {
182
+ if (!usedCostKeys.has(mergeKey(costBucket))) {
183
+ merged.push(costBucket);
184
+ }
185
+ }
186
+
187
+ return merged;
188
+ }
@@ -0,0 +1,84 @@
1
+ export type ProviderBillableEventKind =
2
+ | "llm_tokens"
3
+ | "external_tool_tokens"
4
+ | "image_generation"
5
+ | "hosted_tool"
6
+ | "storage";
7
+
8
+ export type ProviderBillableEventStatus = "unrated" | "rated" | "void";
9
+
10
+ export type ProviderBillableUsage = {
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ cacheCreationInputTokens: number;
14
+ cacheReadInputTokens: number;
15
+ imageCount?: number;
16
+ toolUnits?: number;
17
+ };
18
+
19
+ export type ProviderBillableEvent = {
20
+ id: string;
21
+ operationId: string;
22
+ sourceProviderCallIds: string[];
23
+ userId: string;
24
+ dialogId?: string;
25
+ agentId?: string;
26
+ provider: string;
27
+ model: string;
28
+ endpoint?: string;
29
+ kind: ProviderBillableEventKind;
30
+ toolId?: string;
31
+ usage: ProviderBillableUsage;
32
+ estimated: boolean;
33
+ estimateReason?: string;
34
+ needsReconciliation: boolean;
35
+ status: ProviderBillableEventStatus;
36
+ createdAt: string;
37
+ };
38
+
39
+ export type CreateProviderBillableEventInput = {
40
+ eventId: string;
41
+ operationId: string;
42
+ sourceProviderCallIds: string[];
43
+ userId: string;
44
+ dialogId?: string;
45
+ agentId?: string;
46
+ provider: string;
47
+ model: string;
48
+ endpoint?: string;
49
+ kind: ProviderBillableEventKind;
50
+ toolId?: string;
51
+ usage: ProviderBillableUsage;
52
+ estimated?: boolean;
53
+ estimateReason?: string;
54
+ needsReconciliation?: boolean;
55
+ createdAt: string;
56
+ };
57
+
58
+ export const buildProviderBillableEventKey = (eventId: string) =>
59
+ `provider-billable-event-${eventId}`;
60
+
61
+ export const createProviderBillableEvent = (
62
+ input: CreateProviderBillableEventInput
63
+ ): ProviderBillableEvent => {
64
+ const estimated = input.estimated === true;
65
+ return {
66
+ id: input.eventId,
67
+ operationId: input.operationId,
68
+ sourceProviderCallIds: [...input.sourceProviderCallIds],
69
+ userId: input.userId,
70
+ dialogId: input.dialogId,
71
+ agentId: input.agentId,
72
+ provider: input.provider,
73
+ model: input.model,
74
+ endpoint: input.endpoint,
75
+ kind: input.kind,
76
+ toolId: input.toolId,
77
+ usage: { ...input.usage },
78
+ estimated,
79
+ estimateReason: input.estimateReason,
80
+ needsReconciliation: estimated || input.needsReconciliation === true,
81
+ status: "unrated",
82
+ createdAt: input.createdAt,
83
+ };
84
+ };
@@ -0,0 +1,36 @@
1
+ import {
2
+ buildProviderBillableEventKey,
3
+ type ProviderBillableEvent,
4
+ } from "./providerBillableEvent";
5
+
6
+ export type ProviderBillableEventStore = {
7
+ get(key: string): Promise<unknown>;
8
+ put(key: string, value: unknown): Promise<void>;
9
+ };
10
+
11
+ const isMissingRecordError = (error: unknown) => {
12
+ if (!error || typeof error !== "object") return false;
13
+ const code = (error as { code?: unknown }).code;
14
+ return code === "LEVEL_NOT_FOUND" || code === "NOT_FOUND";
15
+ };
16
+
17
+ export async function writeProviderBillableEvent({
18
+ store,
19
+ event,
20
+ }: {
21
+ store: ProviderBillableEventStore;
22
+ event: ProviderBillableEvent;
23
+ }): Promise<{ key: string }> {
24
+ const key = buildProviderBillableEventKey(event.id);
25
+ let existing: unknown;
26
+ try {
27
+ existing = await store.get(key);
28
+ } catch (error) {
29
+ if (!isMissingRecordError(error)) throw error;
30
+ }
31
+ if (existing) {
32
+ throw new Error(`provider billable event already exists: ${key}`);
33
+ }
34
+ await store.put(key, event);
35
+ return { key };
36
+ }