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.
- package/README.md +9 -2
- package/agent-runtime/hostAdapter.ts +53 -0
- package/agent-runtime/index.ts +28 -0
- package/agent-runtime/localLoop.ts +62 -0
- package/agent-runtime/runtimeDecision.ts +70 -0
- package/agent-runtime/types.ts +87 -0
- package/agentRuntimeCommands.ts +139 -22
- package/agentRuntimeLocal.ts +7 -0
- package/ai/agent/_executeModel.ts +118 -0
- package/ai/agent/agentSlice.ts +544 -1
- 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/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/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 +1299 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
- package/ai/agent/types.ts +71 -0
- package/ai/agent/utils/imageOutput.ts +39 -0
- package/ai/agent/utils/publicImageAgentMode.ts +26 -0
- package/ai/agent/utils/sortUtils.ts +250 -0
- package/ai/agent/web/referencePickerUtils.ts +146 -0
- package/ai/ai.locale.ts +1083 -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/inlineImageUrlsForCustomProvider.ts +117 -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 +712 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +512 -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 -1
- package/ai/llm/agentCapabilities.ts +74 -0
- package/ai/llm/calculateGeminiImageTokens.ts +57 -0
- package/ai/llm/deepinfra.ts +28 -0
- package/ai/llm/fireworks.ts +68 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +84 -0
- package/ai/llm/getNoloKey.ts +37 -0
- package/ai/llm/getPricing.ts +232 -0
- package/ai/llm/hooks/useModelPricing.ts +75 -0
- package/ai/llm/imagePricing.ts +66 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/kimi.ts +18 -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 +141 -0
- package/ai/llm/providers.ts +307 -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 +247 -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 +546 -0
- package/ai/token/db.ts +98 -0
- package/ai/token/externalToolCost.ts +321 -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 +147 -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 +1927 -0
- package/ai/tools/listFilesTool.ts +82 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/modelUsageTools.ts +199 -0
- package/ai/tools/olmOcrTool.ts +34 -0
- package/ai/tools/openaiImageTool.ts +267 -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/agentRun.test.ts +240 -0
- package/client/agentRun.ts +182 -19
- package/client/compactDialog.test.ts +238 -0
- package/client/localRuntimeAdapter.test.ts +135 -0
- package/client/localRuntimeAdapter.ts +244 -0
- package/client/profileConfig.test.ts +40 -0
- package/client/streamingOutput.test.ts +22 -0
- package/client/streamingOutput.ts +38 -0
- package/commandRegistry.ts +9 -2
- package/connector-experimental/index.ts +5 -0
- package/database/actions/cacheMergedUserData.ts +64 -0
- package/database/actions/common.ts +242 -0
- package/database/actions/deleteFile.ts +40 -0
- package/database/actions/fetchUserData.ts +16 -0
- package/database/actions/fileContent.ts +125 -0
- package/database/actions/patch.ts +155 -0
- package/database/actions/read.ts +337 -0
- package/database/actions/readAndWait.ts +224 -0
- package/database/actions/readRequestManager.ts +120 -0
- package/database/actions/remove.ts +94 -0
- package/database/actions/replication.ts +366 -0
- package/database/actions/upload.ts +174 -0
- package/database/actions/upsert.ts +56 -0
- package/database/actions/write.ts +126 -0
- package/database/client/db.native.ts +73 -0
- package/database/client/db.ts +51 -0
- package/database/client/fetchUserData.ts +61 -0
- package/database/client/handleError.ts +19 -0
- package/database/client/queryRequest.ts +21 -0
- package/database/config.ts +21 -0
- package/database/dbActionThunks.ts +1 -0
- package/database/dbSlice.ts +149 -0
- package/database/email.ts +42 -0
- package/database/fileRing.ts +51 -0
- package/database/fileSharding.ts +70 -0
- package/database/fileStorage.native.ts +92 -0
- package/database/fileStorage.ts +232 -0
- package/database/fileUrl.ts +34 -0
- package/database/hooks/useUserData.ts +489 -0
- package/database/index.ts +1 -0
- package/database/keys.ts +765 -0
- package/database/queryPrefixes.ts +14 -0
- package/database/requests.ts +443 -0
- package/database/runtimeServerContext.ts +35 -0
- package/database/server/MemoryDB.ts +76 -0
- package/database/server/actorAccess.ts +76 -0
- package/database/server/agentDelegation.ts +124 -0
- package/database/server/coreDataOwnership.ts +13 -0
- package/database/server/coreDataProxy.ts +76 -0
- package/database/server/cybotReadonly.ts +18 -0
- package/database/server/dataHandlers.ts +111 -0
- package/database/server/db.ts +118 -0
- package/database/server/dbPath.ts +20 -0
- package/database/server/delete.ts +499 -0
- package/database/server/emailRepository.ts +1480 -0
- package/database/server/ensureDbOpen.ts +12 -0
- package/database/server/fileRead.ts +337 -0
- package/database/server/fileService.ts +436 -0
- package/database/server/handleTransaction.ts +86 -0
- package/database/server/patch.ts +282 -0
- package/database/server/query.ts +138 -0
- package/database/server/read.ts +325 -0
- package/database/server/resourceAccess.ts +211 -0
- package/database/server/routes.ts +110 -0
- package/database/server/spaceMemberAuthority.ts +67 -0
- package/database/server/upload.ts +159 -0
- package/database/server/write.ts +494 -0
- package/database/server/writeAuthority.ts +133 -0
- package/database/sqliteDb.ts +46 -0
- package/database/table/deleteTable.ts +120 -0
- package/database/tenantPlacement.ts +57 -0
- package/database/tombstones.ts +52 -0
- package/database/userDataLoadDecision.ts +17 -0
- package/database/userDataMerge.ts +95 -0
- package/database/userPreferenceRegister.ts +108 -0
- package/database/utils/dbPath.ts +47 -0
- package/database/utils/ulid.native.ts +6 -0
- package/database/utils/ulid.ts +1 -0
- package/index.ts +25 -15
- package/localRuntimeDb.ts +28 -0
- package/package.json +16 -4
- package/runtimeModeArgs.ts +33 -0
- package/tui/readlineWorkspace.ts +1 -0
- package/tui/session.ts +22 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type MemoryOwnerType = "user" | "space" | "system";
|
|
2
|
+
export type MemoryVisibility = "private" | "shared" | "public";
|
|
3
|
+
export type MemorySubjectType = "user" | "agent" | "space" | "project" | "system";
|
|
4
|
+
export type MemoryKind = "episodic" | "semantic" | "procedural";
|
|
5
|
+
export type MemoryFacet =
|
|
6
|
+
| "preference"
|
|
7
|
+
| "tension"
|
|
8
|
+
| "unfinished"
|
|
9
|
+
| "goal"
|
|
10
|
+
| "style";
|
|
11
|
+
|
|
12
|
+
export interface MemoryItem {
|
|
13
|
+
id: string;
|
|
14
|
+
ownerType: MemoryOwnerType;
|
|
15
|
+
ownerId: string;
|
|
16
|
+
visibility: MemoryVisibility;
|
|
17
|
+
subjectType: MemorySubjectType;
|
|
18
|
+
subjectId: string;
|
|
19
|
+
kind: MemoryKind;
|
|
20
|
+
content: string;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
lastActivatedAt: string;
|
|
23
|
+
activationCount: number;
|
|
24
|
+
importance: number;
|
|
25
|
+
confidence: number;
|
|
26
|
+
tags?: string[];
|
|
27
|
+
facet?: MemoryFacet;
|
|
28
|
+
patternKey?: string;
|
|
29
|
+
sourceDialogId?: string;
|
|
30
|
+
sourceMessageId?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface MemoryOwnerRef {
|
|
34
|
+
ownerType: MemoryOwnerType;
|
|
35
|
+
ownerId: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MemorySubjectRef {
|
|
39
|
+
subjectType: MemorySubjectType;
|
|
40
|
+
subjectId: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MemoryRuntimeResolution {
|
|
44
|
+
selectedItems: MemoryItem[];
|
|
45
|
+
promptBlock: string | null;
|
|
46
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import type { ChatMessage } from "server/handlers/agentRun/types";
|
|
2
|
+
import { createMemoryItem, writeMemoryItemWithIndexesToDb } from "./storeShared";
|
|
3
|
+
import { loadMemoryCandidatesFromDb } from "./queryShared";
|
|
4
|
+
import type {
|
|
5
|
+
MemoryFacet,
|
|
6
|
+
MemoryItem,
|
|
7
|
+
MemoryOwnerRef,
|
|
8
|
+
MemoryVisibility,
|
|
9
|
+
} from "./types";
|
|
10
|
+
|
|
11
|
+
export interface UnderstandingMemoryCandidate {
|
|
12
|
+
facet: MemoryFacet;
|
|
13
|
+
content: string;
|
|
14
|
+
importance: number;
|
|
15
|
+
confidence: number;
|
|
16
|
+
patternKey: string;
|
|
17
|
+
tags: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const UNDERSTANDING_TAG = "understanding-memory";
|
|
21
|
+
|
|
22
|
+
const normalizeClause = (value: string): string =>
|
|
23
|
+
value
|
|
24
|
+
.replace(/[“”"]/g, "")
|
|
25
|
+
.replace(/\s+/g, " ")
|
|
26
|
+
.replace(/[。!?!?;;]+$/u, "")
|
|
27
|
+
.trim();
|
|
28
|
+
|
|
29
|
+
const normalizeSignalLead = (value: string): string =>
|
|
30
|
+
normalizeClause(value.split(/[\n,,。!?!?;;]+/u)[0] ?? "");
|
|
31
|
+
|
|
32
|
+
const stripLead = (value: string): string =>
|
|
33
|
+
value
|
|
34
|
+
.replace(/^(明白|好的|对|是的|所以|其实|我记得|欢迎回来|嗯|那)\s*[,,::\-]?\s*/u, "")
|
|
35
|
+
.trim();
|
|
36
|
+
|
|
37
|
+
const contentToText = (content: ChatMessage["content"]): string => {
|
|
38
|
+
if (typeof content === "string") return content.trim();
|
|
39
|
+
if (!Array.isArray(content)) return "";
|
|
40
|
+
return content
|
|
41
|
+
.map((part) => (part?.type === "text" ? part.text.trim() : ""))
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.join("\n")
|
|
44
|
+
.trim();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const extractPreference = (text: string): string[] => {
|
|
48
|
+
const matches: string[] = [];
|
|
49
|
+
const normalized = stripLead(text);
|
|
50
|
+
|
|
51
|
+
const moreValue = normalized.match(/[你我](?:更)?在意(.+)/u);
|
|
52
|
+
if (moreValue?.[1]) {
|
|
53
|
+
matches.push(`更在意${normalizeSignalLead(moreValue[1])}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const concern = normalized.match(/[你我](?:更)?关心(.+)/u);
|
|
57
|
+
if (concern?.[1]) {
|
|
58
|
+
matches.push(`更关心${normalizeSignalLead(concern[1])}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const fear = normalized.match(/[你我](?:更)?怕(.+)/u);
|
|
62
|
+
if (fear?.[1]) {
|
|
63
|
+
matches.push(`更怕${normalizeSignalLead(fear[1])}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const dontWant = normalized.match(/[你我]不想(.+)/u);
|
|
67
|
+
if (dontWant?.[1]) {
|
|
68
|
+
matches.push(`不想${normalizeSignalLead(dontWant[1])}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const dontHope = normalized.match(/[你我]不希望(.+)/u);
|
|
72
|
+
if (dontHope?.[1]) {
|
|
73
|
+
matches.push(`不希望${normalizeSignalLead(dontHope[1])}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return matches;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const extractStyle = (text: string): string[] => {
|
|
80
|
+
const matches: string[] = [];
|
|
81
|
+
const normalized = stripLead(text);
|
|
82
|
+
|
|
83
|
+
const dislike = normalized.match(/[你我]不喜欢(.+)/u);
|
|
84
|
+
if (dislike?.[1]) {
|
|
85
|
+
matches.push(`不喜欢${normalizeSignalLead(dislike[1])}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const prefer = normalized.match(/[你我](?:更)?喜欢(.+)/u);
|
|
89
|
+
if (prefer?.[1]) {
|
|
90
|
+
matches.push(`更喜欢${normalizeSignalLead(prefer[1])}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return matches;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const extractGoal = (text: string): string[] => {
|
|
97
|
+
const matches: string[] = [];
|
|
98
|
+
const normalized = stripLead(text);
|
|
99
|
+
if (/[??]/u.test(normalized)) {
|
|
100
|
+
return matches;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const wantFirst = normalized.match(/[你我](?:这次)?(?:更)?想先(?:把)?(.+)/u);
|
|
104
|
+
if (wantFirst?.[1]) {
|
|
105
|
+
matches.push(`想先${normalizeSignalLead(wantFirst[1])}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const shouldFirst = normalized.match(/[你我](?:还是)?想要先(?:把)?(.+)/u);
|
|
109
|
+
if (shouldFirst?.[1]) {
|
|
110
|
+
matches.push(`想先${normalizeSignalLead(shouldFirst[1])}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return matches;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const normalizeTensionTail = (tail: string): string =>
|
|
117
|
+
normalizeClause(
|
|
118
|
+
(
|
|
119
|
+
tail.replace(/^在/u, "").replace(/^权衡/u, "").replace(/^比较/u, "")
|
|
120
|
+
.split(/[。!?!?;;\n]/u)[0] ?? ""
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const extractTension = (text: string): string[] => {
|
|
125
|
+
const matches: string[] = [];
|
|
126
|
+
const normalized = stripLead(text);
|
|
127
|
+
|
|
128
|
+
const weigh = normalized.match(/[你我]在权衡(.+)/u);
|
|
129
|
+
if (weigh?.[1]) {
|
|
130
|
+
matches.push(`在权衡${normalizeTensionTail(weigh[1])}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const struggle = normalized.match(/[你我](?:现在)?(?:真正)?纠结(?:的是)?[::,,\s]*(.+)/u);
|
|
134
|
+
if (struggle?.[1] && struggle[1].includes("还是")) {
|
|
135
|
+
matches.push(`在权衡${normalizeTensionTail(struggle[1])}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const stuck = normalized.match(/[你我]卡的点不是.+?而是(.+)/u);
|
|
139
|
+
if (stuck?.[1]) {
|
|
140
|
+
matches.push(`在权衡${normalizeTensionTail(stuck[1])}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return matches;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const extractUnfinished = (text: string): string[] => {
|
|
147
|
+
const matches: string[] = [];
|
|
148
|
+
const normalized = stripLead(text);
|
|
149
|
+
|
|
150
|
+
const direct = normalized.match(/[你我](?:还)?(?:没|尚未)决定(.+)/u);
|
|
151
|
+
if (direct?.[1]) {
|
|
152
|
+
matches.push(`还没决定${normalizeClause(direct[1])}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const uncertain = normalized.match(/[你我](?:还)?不确定(.+)/u);
|
|
156
|
+
if (uncertain?.[1]) {
|
|
157
|
+
matches.push(`还不确定${normalizeClause(uncertain[1])}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const notReady = normalized.match(/[你我](?:还)?没想好(.+)/u);
|
|
161
|
+
if (notReady?.[1]) {
|
|
162
|
+
matches.push(`还没想好${normalizeClause(notReady[1])}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return matches;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const buildPatternKey = (facet: MemoryFacet, content: string): string =>
|
|
169
|
+
`understanding:${facet}:${content.toLowerCase()}`;
|
|
170
|
+
|
|
171
|
+
const buildCandidate = (
|
|
172
|
+
facet: MemoryFacet,
|
|
173
|
+
content: string
|
|
174
|
+
): UnderstandingMemoryCandidate | null => {
|
|
175
|
+
const normalized = normalizeClause(content);
|
|
176
|
+
if (!normalized || normalized.length < 6) return null;
|
|
177
|
+
|
|
178
|
+
const importance =
|
|
179
|
+
facet === "unfinished" ? 0.91 :
|
|
180
|
+
facet === "tension" ? 0.89 :
|
|
181
|
+
facet === "preference" ? 0.84 :
|
|
182
|
+
facet === "style" ? 0.8 :
|
|
183
|
+
0.82;
|
|
184
|
+
const confidence =
|
|
185
|
+
facet === "unfinished" || facet === "tension" ? 0.76 : 0.72;
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
facet,
|
|
189
|
+
content: normalized,
|
|
190
|
+
importance,
|
|
191
|
+
confidence,
|
|
192
|
+
patternKey: buildPatternKey(facet, normalized),
|
|
193
|
+
tags: [UNDERSTANDING_TAG, `memory-facet:${facet}`],
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export const extractUnderstandingMemoryCandidates = (input: {
|
|
198
|
+
userInput: string;
|
|
199
|
+
trace?: ChatMessage[];
|
|
200
|
+
}): UnderstandingMemoryCandidate[] => {
|
|
201
|
+
const texts = [
|
|
202
|
+
input.userInput,
|
|
203
|
+
...(input.trace ?? [])
|
|
204
|
+
.filter((message) => message.role === "assistant" || message.role === "user")
|
|
205
|
+
.map((message) => contentToText(message.content)),
|
|
206
|
+
]
|
|
207
|
+
.map((value) => value.trim())
|
|
208
|
+
.filter(Boolean);
|
|
209
|
+
|
|
210
|
+
const candidates: UnderstandingMemoryCandidate[] = [];
|
|
211
|
+
for (const text of texts) {
|
|
212
|
+
for (const content of extractPreference(text)) {
|
|
213
|
+
const candidate = buildCandidate("preference", content);
|
|
214
|
+
if (candidate) candidates.push(candidate);
|
|
215
|
+
}
|
|
216
|
+
for (const content of extractStyle(text)) {
|
|
217
|
+
const candidate = buildCandidate("style", content);
|
|
218
|
+
if (candidate) candidates.push(candidate);
|
|
219
|
+
}
|
|
220
|
+
for (const content of extractGoal(text)) {
|
|
221
|
+
const candidate = buildCandidate("goal", content);
|
|
222
|
+
if (candidate) candidates.push(candidate);
|
|
223
|
+
}
|
|
224
|
+
for (const content of extractTension(text)) {
|
|
225
|
+
const candidate = buildCandidate("tension", content);
|
|
226
|
+
if (candidate) candidates.push(candidate);
|
|
227
|
+
}
|
|
228
|
+
for (const content of extractUnfinished(text)) {
|
|
229
|
+
const candidate = buildCandidate("unfinished", content);
|
|
230
|
+
if (candidate) candidates.push(candidate);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const seen = new Set<string>();
|
|
235
|
+
return candidates.filter((candidate) => {
|
|
236
|
+
const key = `${candidate.facet}:${candidate.content.toLowerCase()}`;
|
|
237
|
+
if (seen.has(key)) return false;
|
|
238
|
+
seen.add(key);
|
|
239
|
+
return true;
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const buildUnderstandingTarget = (input: {
|
|
244
|
+
userId?: string | null;
|
|
245
|
+
spaceId?: string | null;
|
|
246
|
+
agentKey: string;
|
|
247
|
+
}): {
|
|
248
|
+
owner: MemoryOwnerRef;
|
|
249
|
+
visibility: MemoryVisibility;
|
|
250
|
+
} | null => {
|
|
251
|
+
if (input.userId) {
|
|
252
|
+
return {
|
|
253
|
+
owner: { ownerType: "user", ownerId: input.userId },
|
|
254
|
+
visibility: "private",
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
if (input.spaceId) {
|
|
258
|
+
return {
|
|
259
|
+
owner: { ownerType: "space", ownerId: input.spaceId },
|
|
260
|
+
visibility: "shared",
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const sameUnderstanding = (item: MemoryItem, candidate: UnderstandingMemoryCandidate, agentKey: string) =>
|
|
267
|
+
item.subjectType === "agent" &&
|
|
268
|
+
item.subjectId === agentKey &&
|
|
269
|
+
item.patternKey === candidate.patternKey;
|
|
270
|
+
|
|
271
|
+
export const captureUnderstandingMemoryFromDialog = async (input: {
|
|
272
|
+
db: any;
|
|
273
|
+
userId?: string | null;
|
|
274
|
+
spaceId?: string | null;
|
|
275
|
+
agentKey: string;
|
|
276
|
+
dialogId: string;
|
|
277
|
+
userInput: string;
|
|
278
|
+
trace?: ChatMessage[];
|
|
279
|
+
}): Promise<void> => {
|
|
280
|
+
const target = buildUnderstandingTarget(input);
|
|
281
|
+
if (!target) return;
|
|
282
|
+
|
|
283
|
+
const candidates = extractUnderstandingMemoryCandidates({
|
|
284
|
+
userInput: input.userInput,
|
|
285
|
+
trace: input.trace,
|
|
286
|
+
});
|
|
287
|
+
if (candidates.length === 0) return;
|
|
288
|
+
|
|
289
|
+
const existing = await loadMemoryCandidatesFromDb(input.db, {
|
|
290
|
+
owners: [target.owner],
|
|
291
|
+
subjects: [{ subjectType: "agent", subjectId: input.agentKey }],
|
|
292
|
+
kinds: ["episodic", "semantic"],
|
|
293
|
+
ownerLimit: 100,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
for (const candidate of candidates) {
|
|
297
|
+
const sameItems = existing.filter((item) => sameUnderstanding(item, candidate, input.agentKey));
|
|
298
|
+
const existingSemantic = sameItems.find((item) => item.kind === "semantic");
|
|
299
|
+
if (existingSemantic) continue;
|
|
300
|
+
|
|
301
|
+
const existingEpisode = sameItems.find((item) => item.kind === "episodic");
|
|
302
|
+
if (
|
|
303
|
+
existingEpisode &&
|
|
304
|
+
existingEpisode.sourceDialogId &&
|
|
305
|
+
existingEpisode.sourceDialogId !== input.dialogId
|
|
306
|
+
) {
|
|
307
|
+
const semantic = createMemoryItem({
|
|
308
|
+
ownerType: target.owner.ownerType,
|
|
309
|
+
ownerId: target.owner.ownerId,
|
|
310
|
+
visibility: target.visibility,
|
|
311
|
+
subjectType: "agent",
|
|
312
|
+
subjectId: input.agentKey,
|
|
313
|
+
kind: "semantic",
|
|
314
|
+
content: candidate.content,
|
|
315
|
+
facet: candidate.facet,
|
|
316
|
+
importance: Math.min(0.95, candidate.importance + 0.03),
|
|
317
|
+
confidence: Math.min(0.86, candidate.confidence + 0.06),
|
|
318
|
+
tags: [...candidate.tags, "consolidated-understanding"],
|
|
319
|
+
patternKey: candidate.patternKey,
|
|
320
|
+
sourceDialogId: input.dialogId,
|
|
321
|
+
});
|
|
322
|
+
await writeMemoryItemWithIndexesToDb(input.db, semantic);
|
|
323
|
+
existing.push(semantic);
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (existingEpisode && existingEpisode.sourceDialogId === input.dialogId) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const episodic = createMemoryItem({
|
|
332
|
+
ownerType: target.owner.ownerType,
|
|
333
|
+
ownerId: target.owner.ownerId,
|
|
334
|
+
visibility: target.visibility,
|
|
335
|
+
subjectType: "agent",
|
|
336
|
+
subjectId: input.agentKey,
|
|
337
|
+
kind: "episodic",
|
|
338
|
+
content: candidate.content,
|
|
339
|
+
facet: candidate.facet,
|
|
340
|
+
importance: candidate.importance,
|
|
341
|
+
confidence: candidate.confidence,
|
|
342
|
+
tags: candidate.tags,
|
|
343
|
+
patternKey: candidate.patternKey,
|
|
344
|
+
sourceDialogId: input.dialogId,
|
|
345
|
+
});
|
|
346
|
+
await writeMemoryItemWithIndexesToDb(input.db, episodic);
|
|
347
|
+
existing.push(episodic);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { chooseMemoryOwners, loadMemoryCandidatesFromDb } from "./queryShared";
|
|
2
|
+
import type { MemoryFacet, MemoryItem } from "./types";
|
|
3
|
+
|
|
4
|
+
export interface UnderstandingGreetingResolution {
|
|
5
|
+
item: MemoryItem | null;
|
|
6
|
+
anchorItems: MemoryItem[];
|
|
7
|
+
followUpItem: MemoryItem | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const UNDERSTANDING_TAG = "understanding-memory";
|
|
11
|
+
|
|
12
|
+
const facetPriority: Record<MemoryFacet, number> = {
|
|
13
|
+
unfinished: 5,
|
|
14
|
+
tension: 4,
|
|
15
|
+
preference: 3,
|
|
16
|
+
style: 2,
|
|
17
|
+
goal: 1,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const anchorFacetPriority: Record<MemoryFacet, number> = {
|
|
21
|
+
preference: 3,
|
|
22
|
+
style: 2,
|
|
23
|
+
goal: 1,
|
|
24
|
+
tension: 0,
|
|
25
|
+
unfinished: 0,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const normalizeText = (value: string): string =>
|
|
29
|
+
value.trim().replace(/[。!?!?]+$/u, "").trim();
|
|
30
|
+
|
|
31
|
+
const stripPrefix = (text: string, prefix: string): string =>
|
|
32
|
+
text.startsWith(prefix) ? text.slice(prefix.length).trim() : text;
|
|
33
|
+
|
|
34
|
+
const toTimestamp = (item: MemoryItem): number => {
|
|
35
|
+
const parsed = Date.parse(item.lastActivatedAt || item.createdAt);
|
|
36
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const isUnderstandingItem = (item: MemoryItem): boolean =>
|
|
40
|
+
Array.isArray(item.tags) && item.tags.includes(UNDERSTANDING_TAG);
|
|
41
|
+
|
|
42
|
+
const sortByKindAndTime = (left: MemoryItem, right: MemoryItem): number => {
|
|
43
|
+
if (left.kind !== right.kind) return left.kind === "semantic" ? -1 : 1;
|
|
44
|
+
return toTimestamp(right) - toTimestamp(left);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const sameNormalizedContent = (left: string, right: string): boolean =>
|
|
48
|
+
normalizeText(left).toLowerCase() === normalizeText(right).toLowerCase();
|
|
49
|
+
|
|
50
|
+
const pickAnchorItems = (items: MemoryItem[]): MemoryItem[] => {
|
|
51
|
+
const ranked = [...items]
|
|
52
|
+
.filter((item) => (item.facet ? anchorFacetPriority[item.facet] > 0 : false))
|
|
53
|
+
.sort((left, right) => {
|
|
54
|
+
const leftFacet = left.facet ? anchorFacetPriority[left.facet] ?? 0 : 0;
|
|
55
|
+
const rightFacet = right.facet ? anchorFacetPriority[right.facet] ?? 0 : 0;
|
|
56
|
+
if (leftFacet !== rightFacet) return rightFacet - leftFacet;
|
|
57
|
+
if (left.kind !== right.kind) return left.kind === "semantic" ? -1 : 1;
|
|
58
|
+
if (left.content.length !== right.content.length) {
|
|
59
|
+
return left.content.length - right.content.length;
|
|
60
|
+
}
|
|
61
|
+
return toTimestamp(right) - toTimestamp(left);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const selected: MemoryItem[] = [];
|
|
65
|
+
for (const item of ranked) {
|
|
66
|
+
if (
|
|
67
|
+
selected.some((existing) =>
|
|
68
|
+
sameNormalizedContent(existing.content, item.content) ||
|
|
69
|
+
(existing.facet && item.facet && existing.facet === item.facet)
|
|
70
|
+
)
|
|
71
|
+
) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
selected.push(item);
|
|
75
|
+
if (selected.length >= 2) break;
|
|
76
|
+
}
|
|
77
|
+
return selected;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const pickFollowUpItem = (items: MemoryItem[]): MemoryItem | null =>
|
|
81
|
+
[...items]
|
|
82
|
+
.filter((item) => item.facet === "unfinished" || item.facet === "tension")
|
|
83
|
+
.sort((left, right) => {
|
|
84
|
+
const leftFacet = left.facet ? facetPriority[left.facet] ?? 0 : 0;
|
|
85
|
+
const rightFacet = right.facet ? facetPriority[right.facet] ?? 0 : 0;
|
|
86
|
+
if (leftFacet !== rightFacet) return rightFacet - leftFacet;
|
|
87
|
+
return sortByKindAndTime(left, right);
|
|
88
|
+
})[0] ?? null;
|
|
89
|
+
|
|
90
|
+
export const resolveUnderstandingGreetingMemory = async (input: {
|
|
91
|
+
db: any;
|
|
92
|
+
userId?: string | null;
|
|
93
|
+
spaceId?: string | null;
|
|
94
|
+
agentKey: string;
|
|
95
|
+
}): Promise<UnderstandingGreetingResolution> => {
|
|
96
|
+
const owners = chooseMemoryOwners({
|
|
97
|
+
userId: input.userId,
|
|
98
|
+
spaceId: input.spaceId,
|
|
99
|
+
});
|
|
100
|
+
if (owners.length === 0) {
|
|
101
|
+
return { item: null, anchorItems: [], followUpItem: null };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const items = await loadMemoryCandidatesFromDb(input.db, {
|
|
105
|
+
owners,
|
|
106
|
+
subjects: [{ subjectType: "agent", subjectId: input.agentKey }],
|
|
107
|
+
kinds: ["semantic", "episodic"],
|
|
108
|
+
ownerLimit: 40,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const understandingItems = items.filter(isUnderstandingItem);
|
|
112
|
+
if (understandingItems.length === 0) {
|
|
113
|
+
return { item: null, anchorItems: [], followUpItem: null };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const ranked = [...understandingItems].sort((left, right) => {
|
|
117
|
+
const leftFacet = left.facet ? facetPriority[left.facet] ?? 0 : 0;
|
|
118
|
+
const rightFacet = right.facet ? facetPriority[right.facet] ?? 0 : 0;
|
|
119
|
+
if (leftFacet !== rightFacet) return rightFacet - leftFacet;
|
|
120
|
+
return sortByKindAndTime(left, right);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const anchorItems = pickAnchorItems(understandingItems);
|
|
124
|
+
const followUpItem = pickFollowUpItem(understandingItems);
|
|
125
|
+
const item = followUpItem ?? anchorItems[0] ?? ranked[0] ?? null;
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
item,
|
|
129
|
+
anchorItems,
|
|
130
|
+
followUpItem,
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const renderLeadClause = (item: MemoryItem): string => {
|
|
135
|
+
const content = normalizeText(item.content);
|
|
136
|
+
switch (item.facet) {
|
|
137
|
+
case "unfinished":
|
|
138
|
+
return `我记得你上次还没定下来:${stripPrefix(content, "还没决定")}`;
|
|
139
|
+
case "tension":
|
|
140
|
+
return `我记得你上次还在权衡${stripPrefix(content, "在权衡")}`;
|
|
141
|
+
case "style":
|
|
142
|
+
case "preference":
|
|
143
|
+
return `我记得你上次${content}`;
|
|
144
|
+
case "goal":
|
|
145
|
+
return `我记得你上次想推进的是${content}`;
|
|
146
|
+
default:
|
|
147
|
+
return `我记得你上次提过${content}`;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const renderAnchorFragment = (item: MemoryItem): string => {
|
|
152
|
+
const content = normalizeText(item.content);
|
|
153
|
+
switch (item.facet) {
|
|
154
|
+
case "preference":
|
|
155
|
+
if (content.startsWith("更在意")) {
|
|
156
|
+
return `更在意的是${stripPrefix(content, "更在意")}`;
|
|
157
|
+
}
|
|
158
|
+
if (content.startsWith("更关心")) {
|
|
159
|
+
return `更关心的是${stripPrefix(content, "更关心")}`;
|
|
160
|
+
}
|
|
161
|
+
if (content.startsWith("更怕")) {
|
|
162
|
+
return `更怕${stripPrefix(content, "更怕")}`;
|
|
163
|
+
}
|
|
164
|
+
if (content.startsWith("不想")) {
|
|
165
|
+
return `不想${stripPrefix(content, "不想")}`;
|
|
166
|
+
}
|
|
167
|
+
if (content.startsWith("不希望")) {
|
|
168
|
+
return `不希望${stripPrefix(content, "不希望")}`;
|
|
169
|
+
}
|
|
170
|
+
return content;
|
|
171
|
+
case "style":
|
|
172
|
+
if (content.startsWith("不喜欢")) {
|
|
173
|
+
return `不太喜欢${stripPrefix(content, "不喜欢")}`;
|
|
174
|
+
}
|
|
175
|
+
if (content.startsWith("更喜欢")) {
|
|
176
|
+
return `更喜欢${stripPrefix(content, "更喜欢")}`;
|
|
177
|
+
}
|
|
178
|
+
return content;
|
|
179
|
+
case "goal":
|
|
180
|
+
if (content.startsWith("想先")) {
|
|
181
|
+
return `想先${stripPrefix(content, "想先")}`;
|
|
182
|
+
}
|
|
183
|
+
return `想推进的是${content}`;
|
|
184
|
+
default:
|
|
185
|
+
return content;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const renderAnchorSentence = (items: MemoryItem[]): string | null => {
|
|
190
|
+
if (items.length === 0) return null;
|
|
191
|
+
const fragments = items
|
|
192
|
+
.map(renderAnchorFragment)
|
|
193
|
+
.map((fragment) => fragment.trim())
|
|
194
|
+
.filter(Boolean);
|
|
195
|
+
if (fragments.length === 0) return null;
|
|
196
|
+
if (fragments.length === 1) {
|
|
197
|
+
return `我记得你上次${fragments[0]}。`;
|
|
198
|
+
}
|
|
199
|
+
return `我记得你上次${fragments[0]},也${fragments[1]}。`;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const splitTradeoff = (value: string): [string, string] | null => {
|
|
203
|
+
const normalized = normalizeText(value)
|
|
204
|
+
.replace(/^在权衡/u, "")
|
|
205
|
+
.replace(/^还没决定/u, "")
|
|
206
|
+
.replace(/^还不确定/u, "")
|
|
207
|
+
.replace(/^还没想好/u, "")
|
|
208
|
+
.trim();
|
|
209
|
+
if (!normalized.includes("还是")) return null;
|
|
210
|
+
|
|
211
|
+
const [left, right] = normalized.split(/\s*还是/u, 2);
|
|
212
|
+
const normalizedLeft = normalizeText((left ?? "").replace(/[,,::]+$/u, "").trim());
|
|
213
|
+
const normalizedRight = normalizeText((right ?? "").trim());
|
|
214
|
+
if (!normalizedLeft || !normalizedRight) return null;
|
|
215
|
+
return [normalizedLeft, normalizedRight];
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const renderFollowUpLine = (item: MemoryItem | null): string => {
|
|
219
|
+
if (!item) {
|
|
220
|
+
return "如果你想,我们可以接着上次那个点;如果今天是新问题,也直接说。";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const tradeoff = splitTradeoff(item.content);
|
|
224
|
+
if (tradeoff) {
|
|
225
|
+
return `如果你愿意,我们可以接着看:${tradeoff[0]},还是${tradeoff[1]}。如果今天是新问题,也直接说。`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const content = normalizeText(item.content);
|
|
229
|
+
switch (item.facet) {
|
|
230
|
+
case "unfinished":
|
|
231
|
+
return `如果你愿意,我们可以接着把${stripPrefix(content, "还没决定")}定下来;如果今天是新问题,也直接说。`;
|
|
232
|
+
case "tension":
|
|
233
|
+
return `如果你愿意,我们可以接着看${stripPrefix(content, "在权衡")};如果今天是新问题,也直接说。`;
|
|
234
|
+
case "goal":
|
|
235
|
+
return `如果你愿意,我们可以继续推进${stripPrefix(content, "想先")};如果今天是新问题,也直接说。`;
|
|
236
|
+
default:
|
|
237
|
+
return "如果你想,我们可以接着上次那个点;如果今天是新问题,也直接说。";
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export const mergeGreetingWithUnderstandingMemory = (input: {
|
|
242
|
+
greetingText?: string;
|
|
243
|
+
resolution?: UnderstandingGreetingResolution | null;
|
|
244
|
+
item?: MemoryItem | null;
|
|
245
|
+
}): string | null => {
|
|
246
|
+
const greetingText = typeof input.greetingText === "string"
|
|
247
|
+
? input.greetingText.trim()
|
|
248
|
+
: "";
|
|
249
|
+
const resolution = input.resolution ?? null;
|
|
250
|
+
const item = input.item ?? resolution?.item ?? null;
|
|
251
|
+
if (!greetingText && !item) return null;
|
|
252
|
+
if (!item) return greetingText || null;
|
|
253
|
+
|
|
254
|
+
const anchorItems = resolution?.anchorItems ?? [];
|
|
255
|
+
const followUpItem = resolution?.followUpItem ?? item;
|
|
256
|
+
const leadLine = renderAnchorSentence(anchorItems) ?? `${renderLeadClause(item)}。`;
|
|
257
|
+
const suffix = renderFollowUpLine(followUpItem);
|
|
258
|
+
const memoryBlock = `欢迎回来。${leadLine}\n${suffix}`;
|
|
259
|
+
|
|
260
|
+
if (!greetingText) {
|
|
261
|
+
return memoryBlock;
|
|
262
|
+
}
|
|
263
|
+
return `${greetingText}\n\n${memoryBlock}`;
|
|
264
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ClaudeContent } from "integrations/anthropic/type";
|
|
2
|
+
|
|
3
|
+
export interface InputMessage {
|
|
4
|
+
content:
|
|
5
|
+
| Array<{
|
|
6
|
+
text?: string;
|
|
7
|
+
type: string;
|
|
8
|
+
image_url?: {
|
|
9
|
+
url: string;
|
|
10
|
+
};
|
|
11
|
+
}>
|
|
12
|
+
| string;
|
|
13
|
+
role: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface OutputMessage {
|
|
17
|
+
role: string;
|
|
18
|
+
content: string | ClaudeContent;
|
|
19
|
+
images: string[];
|
|
20
|
+
}
|