nolo-cli 0.1.8 → 0.1.10
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 +1079 -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 +22 -6
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface UsageRequestOptions {
|
|
2
|
+
stream_options?: {
|
|
3
|
+
include_usage: true;
|
|
4
|
+
};
|
|
5
|
+
usage?: {
|
|
6
|
+
include: true;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type UsageRequestApi = "chat-completions" | "responses";
|
|
11
|
+
|
|
12
|
+
const STREAM_USAGE_PROVIDERS = new Set([
|
|
13
|
+
"google",
|
|
14
|
+
"openrouter",
|
|
15
|
+
"xai",
|
|
16
|
+
"openai",
|
|
17
|
+
"fireworks",
|
|
18
|
+
"mistral",
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const EXTRA_USAGE_FIELD_PROVIDERS = new Set(["openrouter"]);
|
|
22
|
+
|
|
23
|
+
const normalizeProviderName = (providerName?: string | null) =>
|
|
24
|
+
typeof providerName === "string" ? providerName.trim().toLowerCase() : "";
|
|
25
|
+
|
|
26
|
+
export const getUsageRequestOptions = (
|
|
27
|
+
providerName?: string | null,
|
|
28
|
+
options?: { api?: UsageRequestApi }
|
|
29
|
+
): UsageRequestOptions => {
|
|
30
|
+
const normalizedProvider = normalizeProviderName(providerName);
|
|
31
|
+
const api = options?.api ?? "chat-completions";
|
|
32
|
+
|
|
33
|
+
if (api === "responses") {
|
|
34
|
+
return EXTRA_USAGE_FIELD_PROVIDERS.has(normalizedProvider)
|
|
35
|
+
? {
|
|
36
|
+
usage: {
|
|
37
|
+
include: true as const,
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
: {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
...(STREAM_USAGE_PROVIDERS.has(normalizedProvider)
|
|
45
|
+
? {
|
|
46
|
+
stream_options: {
|
|
47
|
+
include_usage: true as const,
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
: {}),
|
|
51
|
+
...(EXTRA_USAGE_FIELD_PROVIDERS.has(normalizedProvider)
|
|
52
|
+
? {
|
|
53
|
+
usage: {
|
|
54
|
+
include: true as const,
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
: {}),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import serverDb from "database/server/db";
|
|
2
|
+
import { maybeConsolidateExplicitRemember } from "./consolidate";
|
|
3
|
+
import { createMemoryItem, writeMemoryItemWithIndexesToDb } from "./store";
|
|
4
|
+
import type { MemoryOwnerType, MemorySubjectType, MemoryVisibility } from "./types";
|
|
5
|
+
|
|
6
|
+
const EXPLICIT_REMEMBER_PATTERNS = [
|
|
7
|
+
"记住",
|
|
8
|
+
"你要记住",
|
|
9
|
+
"请记住",
|
|
10
|
+
"以后记住",
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export const shouldCaptureExplicitMemory = (userInput: string): boolean => {
|
|
14
|
+
const text = userInput.trim();
|
|
15
|
+
if (!text) return false;
|
|
16
|
+
return EXPLICIT_REMEMBER_PATTERNS.some((pattern) => {
|
|
17
|
+
const index = text.indexOf(pattern);
|
|
18
|
+
if (index < 0) return false;
|
|
19
|
+
|
|
20
|
+
const prefix = text.slice(0, index).trim();
|
|
21
|
+
const suffix = text.slice(index + pattern.length).trim();
|
|
22
|
+
if (!suffix) return false;
|
|
23
|
+
if (!prefix) return true;
|
|
24
|
+
|
|
25
|
+
return /^(这个|这件事|这一点|这点|请你|你)?$/u.test(prefix);
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const buildExplicitMemoryTargets = (input: {
|
|
30
|
+
userId?: string | null;
|
|
31
|
+
spaceId?: string | null;
|
|
32
|
+
}): Array<{
|
|
33
|
+
ownerType: MemoryOwnerType;
|
|
34
|
+
ownerId: string;
|
|
35
|
+
visibility: MemoryVisibility;
|
|
36
|
+
subjectType: MemorySubjectType;
|
|
37
|
+
subjectId: string;
|
|
38
|
+
importance: number;
|
|
39
|
+
confidence: number;
|
|
40
|
+
tags: string[];
|
|
41
|
+
}> => {
|
|
42
|
+
if (input.userId) {
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
ownerType: "user",
|
|
46
|
+
ownerId: input.userId,
|
|
47
|
+
visibility: "private",
|
|
48
|
+
subjectType: "user",
|
|
49
|
+
subjectId: input.userId,
|
|
50
|
+
importance: 0.95,
|
|
51
|
+
confidence: 0.95,
|
|
52
|
+
tags: ["explicit-memory"],
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (input.spaceId) {
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
ownerType: "space",
|
|
61
|
+
ownerId: input.spaceId,
|
|
62
|
+
visibility: "shared",
|
|
63
|
+
subjectType: "space",
|
|
64
|
+
subjectId: input.spaceId,
|
|
65
|
+
importance: 0.85,
|
|
66
|
+
confidence: 0.9,
|
|
67
|
+
tags: ["explicit-memory", "space-context"],
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return [];
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const captureExplicitMemoryEpisode = async (input: {
|
|
76
|
+
db?: any;
|
|
77
|
+
userId?: string | null;
|
|
78
|
+
spaceId?: string | null;
|
|
79
|
+
agentKey: string;
|
|
80
|
+
dialogId: string;
|
|
81
|
+
userInput: string;
|
|
82
|
+
}): Promise<void> => {
|
|
83
|
+
if (!shouldCaptureExplicitMemory(input.userInput)) return;
|
|
84
|
+
const db = input.db ?? serverDb;
|
|
85
|
+
const targets = buildExplicitMemoryTargets(input);
|
|
86
|
+
|
|
87
|
+
for (const target of targets) {
|
|
88
|
+
const item = createMemoryItem({
|
|
89
|
+
ownerType: target.ownerType,
|
|
90
|
+
ownerId: target.ownerId,
|
|
91
|
+
visibility: target.visibility,
|
|
92
|
+
subjectType: target.subjectType,
|
|
93
|
+
subjectId: target.subjectId,
|
|
94
|
+
kind: "episodic",
|
|
95
|
+
content: input.userInput.trim(),
|
|
96
|
+
importance: target.importance,
|
|
97
|
+
confidence: target.confidence,
|
|
98
|
+
tags: target.tags,
|
|
99
|
+
patternKey: "explicit-remember",
|
|
100
|
+
sourceDialogId: input.dialogId,
|
|
101
|
+
});
|
|
102
|
+
await writeMemoryItemWithIndexesToDb(db, item);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const consolidateExplicitMemoryAfterDialog = async (input: {
|
|
107
|
+
db?: any;
|
|
108
|
+
userId?: string | null;
|
|
109
|
+
spaceId?: string | null;
|
|
110
|
+
agentKey: string;
|
|
111
|
+
dialogId: string;
|
|
112
|
+
userInput: string;
|
|
113
|
+
}): Promise<void> => {
|
|
114
|
+
if (!shouldCaptureExplicitMemory(input.userInput)) return;
|
|
115
|
+
const db = input.db ?? serverDb;
|
|
116
|
+
const targets = buildExplicitMemoryTargets(input);
|
|
117
|
+
|
|
118
|
+
for (const target of targets) {
|
|
119
|
+
await maybeConsolidateExplicitRemember({
|
|
120
|
+
db,
|
|
121
|
+
owner: { ownerType: target.ownerType, ownerId: target.ownerId },
|
|
122
|
+
subjectType: target.subjectType,
|
|
123
|
+
subjectId: target.subjectId,
|
|
124
|
+
visibility: target.visibility,
|
|
125
|
+
content: input.userInput.trim(),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const scheduleExplicitMemoryConsolidation = (input: {
|
|
131
|
+
db?: any;
|
|
132
|
+
userId?: string | null;
|
|
133
|
+
spaceId?: string | null;
|
|
134
|
+
agentKey: string;
|
|
135
|
+
dialogId: string;
|
|
136
|
+
userInput: string;
|
|
137
|
+
}): void => {
|
|
138
|
+
queueMicrotask(() => {
|
|
139
|
+
void consolidateExplicitMemoryAfterDialog(input).catch((error) => {
|
|
140
|
+
console.warn("[memory] explicit post-dialog consolidation failed", {
|
|
141
|
+
dialogId: input.dialogId,
|
|
142
|
+
userId: input.userId ?? null,
|
|
143
|
+
spaceId: input.spaceId ?? null,
|
|
144
|
+
error,
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import serverDb from "database/server/db";
|
|
2
|
+
import { loadMemoryCandidatesFromDb } from "./query";
|
|
3
|
+
import { createMemoryItem, writeMemoryItemWithIndexesToDb } from "./store";
|
|
4
|
+
import type { MemoryItem, MemoryOwnerRef, MemorySubjectType } from "./types";
|
|
5
|
+
|
|
6
|
+
const EXPLICIT_REMEMBER_PREFIXES = [
|
|
7
|
+
"你要记住",
|
|
8
|
+
"请记住",
|
|
9
|
+
"以后记住",
|
|
10
|
+
"记住",
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const normalizeExplicitRememberContent = (text: string): string => {
|
|
14
|
+
let normalized = text.trim();
|
|
15
|
+
for (const prefix of EXPLICIT_REMEMBER_PREFIXES) {
|
|
16
|
+
if (normalized.startsWith(prefix)) {
|
|
17
|
+
normalized = normalized.slice(prefix.length).trim();
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
normalized = normalized
|
|
23
|
+
.replace(/^[,,。.\s::]+/, "")
|
|
24
|
+
.replace(/[。!?!?]+$/g, "")
|
|
25
|
+
.trim();
|
|
26
|
+
|
|
27
|
+
return normalized;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const buildSemanticPatternKey = (normalizedContent: string): string =>
|
|
31
|
+
`semantic-explicit:${normalizedContent.toLowerCase()}`;
|
|
32
|
+
|
|
33
|
+
const shouldKeepAsSemanticCandidate = (normalizedContent: string): boolean =>
|
|
34
|
+
normalizedContent.length >= 6;
|
|
35
|
+
|
|
36
|
+
const findMatchingExplicitEpisodes = (
|
|
37
|
+
items: MemoryItem[],
|
|
38
|
+
normalizedContent: string,
|
|
39
|
+
subjectId: string
|
|
40
|
+
): MemoryItem[] =>
|
|
41
|
+
items.filter((item) => {
|
|
42
|
+
if (item.kind !== "episodic") return false;
|
|
43
|
+
if (item.patternKey !== "explicit-remember") return false;
|
|
44
|
+
if (item.subjectId !== subjectId) return false;
|
|
45
|
+
return normalizeExplicitRememberContent(item.content) === normalizedContent;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const maybeConsolidateExplicitRemember = async (input: {
|
|
49
|
+
db?: any;
|
|
50
|
+
owner: MemoryOwnerRef;
|
|
51
|
+
subjectType: MemorySubjectType;
|
|
52
|
+
subjectId: string;
|
|
53
|
+
visibility: "private" | "shared" | "public";
|
|
54
|
+
content: string;
|
|
55
|
+
}): Promise<void> => {
|
|
56
|
+
const normalizedContent = normalizeExplicitRememberContent(input.content);
|
|
57
|
+
if (!shouldKeepAsSemanticCandidate(normalizedContent)) return;
|
|
58
|
+
|
|
59
|
+
const db = input.db ?? serverDb;
|
|
60
|
+
const items = await loadMemoryCandidatesFromDb(db, {
|
|
61
|
+
owners: [input.owner],
|
|
62
|
+
subjects: [{ subjectType: input.subjectType, subjectId: input.subjectId }],
|
|
63
|
+
kinds: ["episodic", "semantic"],
|
|
64
|
+
ownerLimit: 50,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const matchingEpisodes = findMatchingExplicitEpisodes(
|
|
68
|
+
items,
|
|
69
|
+
normalizedContent,
|
|
70
|
+
input.subjectId
|
|
71
|
+
);
|
|
72
|
+
if (matchingEpisodes.length < 2) return;
|
|
73
|
+
|
|
74
|
+
const semanticPatternKey = buildSemanticPatternKey(normalizedContent);
|
|
75
|
+
const existingSemantic = items.find(
|
|
76
|
+
(item) =>
|
|
77
|
+
item.kind === "semantic" &&
|
|
78
|
+
item.subjectId === input.subjectId &&
|
|
79
|
+
item.patternKey === semanticPatternKey
|
|
80
|
+
);
|
|
81
|
+
if (existingSemantic) return;
|
|
82
|
+
|
|
83
|
+
const semantic = createMemoryItem({
|
|
84
|
+
ownerType: input.owner.ownerType,
|
|
85
|
+
ownerId: input.owner.ownerId,
|
|
86
|
+
visibility: input.visibility,
|
|
87
|
+
subjectType: input.subjectType,
|
|
88
|
+
subjectId: input.subjectId,
|
|
89
|
+
kind: "semantic",
|
|
90
|
+
content: normalizedContent,
|
|
91
|
+
importance: 0.92,
|
|
92
|
+
confidence: 0.72,
|
|
93
|
+
tags: ["explicit-memory", "consolidated"],
|
|
94
|
+
patternKey: semanticPatternKey,
|
|
95
|
+
sourceDialogId: matchingEpisodes[matchingEpisodes.length - 1]?.sourceDialogId,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await writeMemoryItemWithIndexesToDb(db, semantic);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const __test__ = {
|
|
102
|
+
normalizeExplicitRememberContent,
|
|
103
|
+
buildSemanticPatternKey,
|
|
104
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import serverDb from "database/server/db";
|
|
2
|
+
import {
|
|
3
|
+
createMemoryKey,
|
|
4
|
+
createMemoryOwnerIndexKey,
|
|
5
|
+
createMemorySubjectKindIndexKey,
|
|
6
|
+
memoryOwnerRange,
|
|
7
|
+
} from "database/keys";
|
|
8
|
+
import type {
|
|
9
|
+
MemoryFacet,
|
|
10
|
+
MemoryItem,
|
|
11
|
+
MemoryKind,
|
|
12
|
+
MemoryOwnerRef,
|
|
13
|
+
MemorySubjectType,
|
|
14
|
+
} from "./types";
|
|
15
|
+
|
|
16
|
+
interface DeleteMemoryFilters {
|
|
17
|
+
ids?: string[];
|
|
18
|
+
kinds?: MemoryKind[];
|
|
19
|
+
facets?: MemoryFacet[];
|
|
20
|
+
subjectType?: MemorySubjectType;
|
|
21
|
+
subjectId?: string;
|
|
22
|
+
patternKeyPrefix?: string;
|
|
23
|
+
sourceDialogId?: string;
|
|
24
|
+
tags?: string[];
|
|
25
|
+
limit?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DeleteMemoryResult {
|
|
29
|
+
deletedCount: number;
|
|
30
|
+
deletedIds: string[];
|
|
31
|
+
matchedItems: MemoryItem[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const normalizeSet = (values?: string[]): Set<string> | null => {
|
|
35
|
+
if (!Array.isArray(values) || values.length === 0) return null;
|
|
36
|
+
const normalized = values
|
|
37
|
+
.map((value) => value.trim())
|
|
38
|
+
.filter(Boolean);
|
|
39
|
+
return normalized.length > 0 ? new Set(normalized) : null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const matchesFilters = (item: MemoryItem, filters: DeleteMemoryFilters): boolean => {
|
|
43
|
+
const idSet = normalizeSet(filters.ids);
|
|
44
|
+
if (idSet && !idSet.has(item.id)) return false;
|
|
45
|
+
|
|
46
|
+
const kindSet = normalizeSet(filters.kinds);
|
|
47
|
+
if (kindSet && !kindSet.has(item.kind)) return false;
|
|
48
|
+
|
|
49
|
+
const facetSet = normalizeSet(filters.facets);
|
|
50
|
+
if (facetSet && (!item.facet || !facetSet.has(item.facet))) return false;
|
|
51
|
+
|
|
52
|
+
if (filters.subjectType && item.subjectType !== filters.subjectType) return false;
|
|
53
|
+
if (filters.subjectId && item.subjectId !== filters.subjectId) return false;
|
|
54
|
+
|
|
55
|
+
const prefix = typeof filters.patternKeyPrefix === "string"
|
|
56
|
+
? filters.patternKeyPrefix.trim()
|
|
57
|
+
: "";
|
|
58
|
+
if (prefix && !(item.patternKey ?? "").startsWith(prefix)) return false;
|
|
59
|
+
|
|
60
|
+
if (filters.sourceDialogId && item.sourceDialogId !== filters.sourceDialogId) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const tagSet = normalizeSet(filters.tags);
|
|
65
|
+
if (tagSet) {
|
|
66
|
+
const itemTags = new Set((item.tags ?? []).map((tag) => tag.trim()).filter(Boolean));
|
|
67
|
+
for (const tag of tagSet) {
|
|
68
|
+
if (!itemTags.has(tag)) return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const deleteMemoryItemWithIndexesInBatch = (
|
|
76
|
+
batch: any,
|
|
77
|
+
item: MemoryItem
|
|
78
|
+
) => {
|
|
79
|
+
batch.del(createMemoryKey(item.ownerType, item.ownerId, item.id));
|
|
80
|
+
batch.del(
|
|
81
|
+
createMemoryOwnerIndexKey(
|
|
82
|
+
item.ownerType,
|
|
83
|
+
item.ownerId,
|
|
84
|
+
item.createdAt,
|
|
85
|
+
item.id
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
batch.del(
|
|
89
|
+
createMemorySubjectKindIndexKey(
|
|
90
|
+
item.subjectType,
|
|
91
|
+
item.subjectId,
|
|
92
|
+
item.kind,
|
|
93
|
+
item.createdAt,
|
|
94
|
+
item.id
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const deleteMemoriesForOwnerFromDb = async (
|
|
100
|
+
db: any,
|
|
101
|
+
owner: MemoryOwnerRef,
|
|
102
|
+
filters: DeleteMemoryFilters = {}
|
|
103
|
+
): Promise<DeleteMemoryResult> => {
|
|
104
|
+
const range = memoryOwnerRange(owner.ownerType, owner.ownerId);
|
|
105
|
+
const matchedItems: MemoryItem[] = [];
|
|
106
|
+
const limit = typeof filters.limit === "number" && filters.limit > 0
|
|
107
|
+
? filters.limit
|
|
108
|
+
: Number.POSITIVE_INFINITY;
|
|
109
|
+
|
|
110
|
+
for await (const [, value] of db.iterator({
|
|
111
|
+
gte: range.start,
|
|
112
|
+
lte: range.end,
|
|
113
|
+
reverse: true,
|
|
114
|
+
})) {
|
|
115
|
+
const memoryId = typeof value?.memoryId === "string" ? value.memoryId : "";
|
|
116
|
+
if (!memoryId) continue;
|
|
117
|
+
const item = (await db
|
|
118
|
+
.get(createMemoryKey(owner.ownerType, owner.ownerId, memoryId))
|
|
119
|
+
.catch(() => null)) as MemoryItem | null;
|
|
120
|
+
if (!item) continue;
|
|
121
|
+
if (!matchesFilters(item, filters)) continue;
|
|
122
|
+
matchedItems.push(item);
|
|
123
|
+
if (matchedItems.length >= limit) break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (matchedItems.length === 0) {
|
|
127
|
+
return { deletedCount: 0, deletedIds: [], matchedItems: [] };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const batch = db.batch();
|
|
131
|
+
for (const item of matchedItems) {
|
|
132
|
+
deleteMemoryItemWithIndexesInBatch(batch, item);
|
|
133
|
+
}
|
|
134
|
+
await batch.write();
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
deletedCount: matchedItems.length,
|
|
138
|
+
deletedIds: matchedItems.map((item) => item.id),
|
|
139
|
+
matchedItems,
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const deleteMemoriesForOwner = async (
|
|
144
|
+
owner: MemoryOwnerRef,
|
|
145
|
+
filters: DeleteMemoryFilters = {}
|
|
146
|
+
): Promise<DeleteMemoryResult> =>
|
|
147
|
+
deleteMemoriesForOwnerFromDb(serverDb, owner, filters);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { MemoryItem } from "./types";
|
|
2
|
+
|
|
3
|
+
const KIND_TITLES: Record<MemoryItem["kind"], string> = {
|
|
4
|
+
episodic: "Episodic",
|
|
5
|
+
semantic: "Semantic",
|
|
6
|
+
procedural: "Procedural",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const normalizeDisplayContent = (item: MemoryItem): string => {
|
|
10
|
+
const normalized = item.content
|
|
11
|
+
.trim()
|
|
12
|
+
.replace(/^(你要记住|请记住|以后记住|记住)[,,。.\s::]*/u, "")
|
|
13
|
+
.replace(/[。!?!?]+$/u, "")
|
|
14
|
+
.trim();
|
|
15
|
+
const stripPrefix = (prefix: string) =>
|
|
16
|
+
normalized.startsWith(prefix) ? normalized.slice(prefix.length).trim() : normalized;
|
|
17
|
+
|
|
18
|
+
if (item.kind === "semantic") {
|
|
19
|
+
if (item.tags?.includes("understanding-memory")) {
|
|
20
|
+
switch (item.facet) {
|
|
21
|
+
case "unfinished":
|
|
22
|
+
return `用户仍未定下:${stripPrefix("还没决定")}`;
|
|
23
|
+
case "tension":
|
|
24
|
+
return `用户当前在权衡:${stripPrefix("在权衡")}`;
|
|
25
|
+
case "preference":
|
|
26
|
+
return `用户当前更在意:${stripPrefix("更在意")}`;
|
|
27
|
+
case "style":
|
|
28
|
+
return `用户互动偏好:${stripPrefix("更喜欢")}`;
|
|
29
|
+
case "goal":
|
|
30
|
+
return `用户当前目标:${stripPrefix("想推进")}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return `用户长期偏好/事实:${normalized}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (item.kind === "episodic" && item.patternKey === "explicit-remember") {
|
|
37
|
+
return `用户明确要求你记住:${normalized}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (item.kind === "episodic" && item.tags?.includes("understanding-memory")) {
|
|
41
|
+
switch (item.facet) {
|
|
42
|
+
case "unfinished":
|
|
43
|
+
return `最近一次对话显示:用户还没定下 ${stripPrefix("还没决定")}`;
|
|
44
|
+
case "tension":
|
|
45
|
+
return `最近一次对话显示:用户还在权衡 ${stripPrefix("在权衡")}`;
|
|
46
|
+
case "preference":
|
|
47
|
+
return `最近一次对话显示:用户更在意 ${stripPrefix("更在意")}`;
|
|
48
|
+
case "style":
|
|
49
|
+
return `最近一次对话显示:用户更偏好 ${stripPrefix("更喜欢")}`;
|
|
50
|
+
case "goal":
|
|
51
|
+
return `最近一次对话显示:用户想推进 ${stripPrefix("想推进")}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return normalized;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const buildMemoryOverlay = (items: MemoryItem[]): string | null => {
|
|
59
|
+
if (items.length === 0) return null;
|
|
60
|
+
|
|
61
|
+
const byKind: Record<string, MemoryItem[]> = {
|
|
62
|
+
episodic: [],
|
|
63
|
+
semantic: [],
|
|
64
|
+
procedural: [],
|
|
65
|
+
};
|
|
66
|
+
for (const item of items) {
|
|
67
|
+
byKind[item.kind].push(item);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const sections = Object.entries(byKind)
|
|
71
|
+
.filter(([, list]) => list.length > 0)
|
|
72
|
+
.map(([kind, list]) => {
|
|
73
|
+
const lines = list.slice(0, 3).map((item) => `- ${normalizeDisplayContent(item)}`);
|
|
74
|
+
return [`[${KIND_TITLES[kind as MemoryItem["kind"]]}]`, ...lines].join("\n");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return [
|
|
78
|
+
"--- Memory Overlay ---",
|
|
79
|
+
"以下是当前请求可用的记忆事实;相关时直接基于这些记忆回答。",
|
|
80
|
+
"用户当前输入优先于记忆;记忆只辅助理解,不要求迎合。",
|
|
81
|
+
"",
|
|
82
|
+
...sections,
|
|
83
|
+
].join("\n");
|
|
84
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import serverDb from "database/server/db";
|
|
2
|
+
import type {
|
|
3
|
+
MemoryItem,
|
|
4
|
+
MemoryKind,
|
|
5
|
+
MemoryOwnerRef,
|
|
6
|
+
MemorySubjectRef,
|
|
7
|
+
} from "./types";
|
|
8
|
+
import {
|
|
9
|
+
buildDefaultSubjects,
|
|
10
|
+
chooseMemoryOwners,
|
|
11
|
+
loadMemoryCandidatesFromDb,
|
|
12
|
+
loadOwnerItemsFromDb,
|
|
13
|
+
loadSubjectKindItemsFromDb,
|
|
14
|
+
} from "./queryShared";
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
buildDefaultSubjects,
|
|
18
|
+
chooseMemoryOwners,
|
|
19
|
+
loadMemoryCandidatesFromDb,
|
|
20
|
+
loadOwnerItemsFromDb,
|
|
21
|
+
loadSubjectKindItemsFromDb,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const loadOwnerItems = async (
|
|
25
|
+
owner: MemoryOwnerRef,
|
|
26
|
+
limit: number
|
|
27
|
+
): Promise<MemoryItem[]> => {
|
|
28
|
+
return loadOwnerItemsFromDb(serverDb, owner, limit);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const loadMemoryCandidates = async (input: {
|
|
32
|
+
owners: MemoryOwnerRef[];
|
|
33
|
+
subjects: MemorySubjectRef[];
|
|
34
|
+
kinds?: MemoryKind[];
|
|
35
|
+
ownerLimit?: number;
|
|
36
|
+
}): Promise<MemoryItem[]> => {
|
|
37
|
+
return loadMemoryCandidatesFromDb(serverDb, input);
|
|
38
|
+
};
|